On 24.09.2020 06:27, Michael Paquier wrote:
On Mon, Sep 14, 2020 at 02:38:56PM +0300, Anastasia Lubennikova wrote:
Fixed. This was also caught by cfbot. This version should pass it clean.
Please note that regression tests are failing, because of 6b2c4e59.
--
Michael
Thank you. Updated patch is attached.
Open issues for review:
- new syntax;
- generation of partition names;
- overall patch review and testing, especially with complex partitioning
clauses.
--
Anastasia Lubennikova
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
commit faa5805b839effd9d8220eff787fb0995276c370
Author: anastasia <a.lubennik...@postgrespro.ru>
Date: Mon Sep 14 11:34:42 2020 +0300
Auto generated HASH and LIST partitions.
New syntax:
CREATE TABLE tbl_hash (i int) PARTITION BY HASH (i)
CONFIGURATION (modulus 3);
CREATE TABLE tbl_list (i int) PARTITION BY LIST (i)
CONFIGURATION (values in (1, 2), (3, 4) DEFAULT PARTITION tbl_default);
With documentation draft.
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 087cad184c..ff9a7eda09 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -29,6 +29,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
] )
[ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ]
[ PARTITION BY { RANGE | LIST | HASH } ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [, ... ] ) ]
+[ CONFIGURATION ( <replaceable class="parameter">partition_bound_auto_spec</replaceable> ) ]
[ USING <replaceable class="parameter">method</replaceable> ]
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
@@ -41,6 +42,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
[, ... ]
) ]
[ PARTITION BY { RANGE | LIST | HASH } ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [, ... ] ) ]
+[ CONFIGURATION ( <replaceable class="parameter">partition_bound_auto_spec</replaceable> ) ]
[ USING <replaceable class="parameter">method</replaceable> ]
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
@@ -53,6 +55,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
[, ... ]
) ] { FOR VALUES <replaceable class="parameter">partition_bound_spec</replaceable> | DEFAULT }
[ PARTITION BY { RANGE | LIST | HASH } ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [, ... ] ) ]
+[ CONFIGURATION ( <replaceable class="parameter">partition_bound_auto_spec</replaceable> ) ]
[ USING <replaceable class="parameter">method</replaceable> ]
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
@@ -96,6 +99,11 @@ FROM ( { <replaceable class="parameter">partition_bound_expr</replaceable> | MIN
TO ( { <replaceable class="parameter">partition_bound_expr</replaceable> | MINVALUE | MAXVALUE } [, ...] ) |
WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REMAINDER <replaceable class="parameter">numeric_literal</replaceable> )
+<phrase>and <replaceable class="parameter">partition_bound_auto_spec</replaceable> is:</phrase>
+
+VALUES IN ( <replaceable class="parameter">partition_bound_expr</replaceable> [, ...] ), [( <replaceable class="parameter">partition_bound_expr</replaceable> [, ...] )] [, ...] [DEFAULT PARTITION <replaceable class="parameter">defailt_part_name</replaceable>]
+MODULUS <replaceable class="parameter">numeric_literal</replaceable>
+
<phrase><replaceable class="parameter">index_parameters</replaceable> in <literal>UNIQUE</literal>, <literal>PRIMARY KEY</literal>, and <literal>EXCLUDE</literal> constraints are:</phrase>
[ INCLUDE ( <replaceable class="parameter">column_name</replaceable> [, ... ] ) ]
@@ -383,6 +391,11 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
however, you can define these constraints on individual partitions.
</para>
+ <para>
+ Range and list partitioning also support automatic creation of partitions
+ with an optional <literal>CONFIGURATION</literal> clause.
+ </para>
+
<para>
See <xref linkend="ddl-partitioning"/> for more discussion on table
partitioning.
@@ -391,6 +404,38 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>CONFIGURATION ( <replaceable class="parameter">partition_bound_auto_spec</replaceable> ) ] </literal></term>
+ <listitem>
+ <para>
+ The optional <literal>CONFIGURATION</literal> clause used together
+ with <literal>PARTITION BY</literal> specifies a rule of generating bounds
+ for partitions of the partitioned table. All partitions are created automatically
+ along with the parent table.
+
+ Any indexes, constraints and user-defined row-level triggers that exist
+ in the parent table are cloned on the new partitions.
+ </para>
+
+ <para>
+ The <replaceable class="parameter">partition_bound_auto_spec</replaceable>
+ must correspond to the partitioning method and partition key of the
+ parent table, and must not overlap with any existing partition of that
+ parent. The form with <literal>VALUES IN</literal> is used for list partitioning
+ and the form with <literal>MODULUS</literal> is used for hash partitioning.
+ List partitioning can also provide a default partition using
+ <literal>DEFAULT PARTITION</literal>.
+ </para>
+
+ <para>
+ Automatic range partitioning is not supported yet.
+ </para>
+
+
+
+ </listitem>
+ </varlistentry>
+
<varlistentry id="sql-createtable-partition">
<term><literal>PARTITION OF <replaceable class="parameter">parent_table</replaceable> { FOR VALUES <replaceable class="parameter">partition_bound_spec</replaceable> | DEFAULT }</literal></term>
<listitem>
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 0409a40b82..6893fa5495 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -4628,6 +4628,7 @@ _copyPartitionSpec(const PartitionSpec *from)
COPY_STRING_FIELD(strategy);
COPY_NODE_FIELD(partParams);
+ COPY_NODE_FIELD(autopart);
COPY_LOCATION_FIELD(location);
return newnode;
@@ -4650,6 +4651,19 @@ _copyPartitionBoundSpec(const PartitionBoundSpec *from)
return newnode;
}
+static PartitionBoundAutoSpec *
+_copyPartitionBoundAutoSpec(const PartitionBoundAutoSpec *from)
+{
+ PartitionBoundAutoSpec *newnode = makeNode(PartitionBoundAutoSpec);
+
+ COPY_SCALAR_FIELD(strategy);
+ COPY_SCALAR_FIELD(modulus);
+ COPY_NODE_FIELD(listdatumsList);
+ COPY_NODE_FIELD(default_partition_rv);
+
+ return newnode;
+}
+
static PartitionRangeDatum *
_copyPartitionRangeDatum(const PartitionRangeDatum *from)
{
@@ -5699,6 +5713,9 @@ copyObjectImpl(const void *from)
case T_PartitionBoundSpec:
retval = _copyPartitionBoundSpec(from);
break;
+ case T_PartitionBoundAutoSpec:
+ retval = _copyPartitionBoundAutoSpec(from);
+ break;
case T_PartitionRangeDatum:
retval = _copyPartitionRangeDatum(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index e2d1b987bf..ccba2471de 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2897,6 +2897,7 @@ _equalPartitionSpec(const PartitionSpec *a, const PartitionSpec *b)
COMPARE_STRING_FIELD(strategy);
COMPARE_NODE_FIELD(partParams);
COMPARE_LOCATION_FIELD(location);
+ COMPARE_NODE_FIELD(autopart);
return true;
}
@@ -2916,6 +2917,19 @@ _equalPartitionBoundSpec(const PartitionBoundSpec *a, const PartitionBoundSpec *
return true;
}
+static bool
+_equalPartitionBoundAutoSpec(const PartitionBoundAutoSpec *a,
+ const PartitionBoundAutoSpec *b)
+{
+ COMPARE_SCALAR_FIELD(strategy);
+ COMPARE_SCALAR_FIELD(modulus);
+ COMPARE_NODE_FIELD(listdatumsList);
+ COMPARE_NODE_FIELD(default_partition_rv);
+
+ return true;
+}
+
+
static bool
_equalPartitionRangeDatum(const PartitionRangeDatum *a, const PartitionRangeDatum *b)
{
@@ -3751,6 +3765,9 @@ equal(const void *a, const void *b)
case T_PartitionBoundSpec:
retval = _equalPartitionBoundSpec(a, b);
break;
+ case T_PartitionBoundAutoSpec:
+ retval = _equalPartitionBoundAutoSpec(a, b);
+ break;
case T_PartitionRangeDatum:
retval = _equalPartitionRangeDatum(a, b);
break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e2f177515d..4fd12523d8 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3643,6 +3643,7 @@ _outPartitionSpec(StringInfo str, const PartitionSpec *node)
WRITE_STRING_FIELD(strategy);
WRITE_NODE_FIELD(partParams);
+ WRITE_NODE_FIELD(autopart);
WRITE_LOCATION_FIELD(location);
}
@@ -3661,6 +3662,18 @@ _outPartitionBoundSpec(StringInfo str, const PartitionBoundSpec *node)
WRITE_LOCATION_FIELD(location);
}
+static void
+_outPartitionBoundAutoSpec(StringInfo str, const PartitionBoundAutoSpec *node)
+{
+ WRITE_NODE_TYPE("PARTITIONBOUNDAUTOSPEC");
+
+ WRITE_CHAR_FIELD(strategy);
+ WRITE_INT_FIELD(modulus);
+ WRITE_NODE_FIELD(listdatumsList);
+ WRITE_NODE_FIELD(default_partition_rv);
+
+}
+
static void
_outPartitionRangeDatum(StringInfo str, const PartitionRangeDatum *node)
{
@@ -4334,6 +4347,9 @@ outNode(StringInfo str, const void *obj)
case T_PartitionBoundSpec:
_outPartitionBoundSpec(str, obj);
break;
+ case T_PartitionBoundAutoSpec:
+ _outPartitionBoundAutoSpec(str, obj);
+ break;
case T_PartitionRangeDatum:
_outPartitionRangeDatum(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 42050ab719..703b413f93 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -2602,6 +2602,19 @@ _readPartitionBoundSpec(void)
READ_DONE();
}
+static PartitionBoundAutoSpec *
+_readPartitionBoundAutoSpec(void)
+{
+ READ_LOCALS(PartitionBoundAutoSpec);
+
+ READ_CHAR_FIELD(strategy);
+ READ_INT_FIELD(modulus);
+ READ_NODE_FIELD(listdatumsList);
+ READ_NODE_FIELD(default_partition_rv);
+
+ READ_DONE();
+}
+
/*
* _readPartitionRangeDatum
*/
@@ -2880,6 +2893,8 @@ parseNodeString(void)
return_value = _readPartitionBoundSpec();
else if (MATCH("PARTITIONRANGEDATUM", 19))
return_value = _readPartitionRangeDatum();
+ else if (MATCH("PARTITIONBOUNDAUTOSPEC", 22))
+ return_value = _readPartitionBoundAutoSpec();
else
{
elog(ERROR, "badly formatted node string \"%.32s\"...", token);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 17653ef3a7..aca93b8c96 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -249,6 +249,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
PartitionElem *partelem;
PartitionSpec *partspec;
PartitionBoundSpec *partboundspec;
+ PartitionBoundAutoSpec *partboundautospec;
RoleSpec *rolespec;
struct SelectLimit *selectlimit;
}
@@ -601,6 +602,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <list> hash_partbound
%type <defelt> hash_partbound_elem
+%type <partboundautospec> OptPartitionBoundAutoSpec values_in_clause p_desc
+%type <range> opt_default_partition_clause
+
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
* They must be listed first so that their numeric codes do not depend on
@@ -3904,14 +3908,14 @@ OptPartitionSpec: PartitionSpec { $$ = $1; }
| /*EMPTY*/ { $$ = NULL; }
;
-PartitionSpec: PARTITION BY ColId '(' part_params ')'
+PartitionSpec: PARTITION BY ColId '(' part_params ')' OptPartitionBoundAutoSpec
{
PartitionSpec *n = makeNode(PartitionSpec);
n->strategy = $3;
n->partParams = $5;
n->location = @1;
-
+ n->autopart = (Node *) $7;
$$ = n;
}
;
@@ -3955,6 +3959,80 @@ part_elem: ColId opt_collate opt_class
}
;
+OptPartitionBoundAutoSpec:
+ CONFIGURATION '(' p_desc ')'
+ {
+ $$ = $3;
+ }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+p_desc:
+ hash_partbound
+ {
+ ListCell *lc;
+ PartitionBoundAutoSpec *n = makeNode(PartitionBoundAutoSpec);
+
+ n->modulus = -1;
+
+ foreach (lc, $1)
+ {
+ DefElem *opt = lfirst_node(DefElem, lc);
+
+ if (strcmp(opt->defname, "modulus") == 0)
+ {
+ n->strategy = PARTITION_STRATEGY_HASH;
+ if (n->modulus != -1)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("modulus for hash partition provided more than once"),
+ parser_errposition(opt->location)));
+ n->modulus = defGetInt32(opt);
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unrecognized auto partition bound specification \"%s\"",
+ opt->defname),
+ parser_errposition(opt->location)));
+ }
+
+ $$ = (PartitionBoundAutoSpec *) n;
+ }
+ | values_in_clause opt_default_partition_clause
+ {
+ PartitionBoundAutoSpec *n = $1;
+ n->default_partition_rv = $2;
+ $$ = (PartitionBoundAutoSpec *) n;
+ }
+ ;
+
+values_in_clause:
+ VALUES IN_P '(' expr_list ')'
+ {
+ PartitionBoundAutoSpec *n = makeNode(PartitionBoundAutoSpec);
+ n->strategy = PARTITION_STRATEGY_LIST;
+ n->listdatumsList = list_make1($4);
+ $$ = (PartitionBoundAutoSpec *) n;
+ }
+ | values_in_clause ',' '(' expr_list ')'
+ {
+ PartitionBoundAutoSpec *n = (PartitionBoundAutoSpec *) $1;
+ n->strategy = PARTITION_STRATEGY_LIST;
+ n->listdatumsList = lappend(n->listdatumsList, $4);
+ $$ = (PartitionBoundAutoSpec *) n;
+ }
+ ;
+
+opt_default_partition_clause:
+ DEFAULT PARTITION qualified_name
+ {
+ $$ = $3;
+ }
+ | /* EMPTY */
+ { $$ = NULL; }
+ ;
+
table_access_method_clause:
USING name { $$ = $2; }
| /*EMPTY*/ { $$ = NULL; }
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 164312d60e..b9ba712e36 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -75,6 +75,7 @@
/* State shared by transformCreateStmt and its subroutines */
typedef struct
{
+ CreateStmt *stmt; /* initial statement */
ParseState *pstate; /* overall parser state */
const char *stmtType; /* "CREATE [FOREIGN] TABLE" or "ALTER TABLE" */
RangeVar *relation; /* relation to create */
@@ -145,6 +146,7 @@ static Const *transformPartitionBoundValue(ParseState *pstate, Node *con,
const char *colName, Oid colType, int32 colTypmod,
Oid partCollation);
+static void transformPartitionAutoCreate(CreateStmtContext *cxt, PartitionSpec* partspec);
/*
* transformCreateStmt -
@@ -235,6 +237,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
cxt.stmtType = "CREATE TABLE";
cxt.isforeign = false;
}
+ cxt.stmt = stmt;
cxt.relation = stmt->relation;
cxt.rel = NULL;
cxt.inhRelations = stmt->inhRelations;
@@ -324,6 +327,10 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
*/
transformExtendedStatistics(&cxt);
+ /* Process partition definitions */
+ if (stmt->partspec && stmt->partspec->autopart)
+ transformPartitionAutoCreate(&cxt, stmt->partspec);
+
/*
* Output results.
*/
@@ -4279,3 +4286,133 @@ transformPartitionBoundValue(ParseState *pstate, Node *val,
return (Const *) value;
}
+
+
+/*
+ * Transform configuration into a set of partition bounds.
+ * Generate extra statements to create partition tables.
+ */
+static void
+transformPartitionAutoCreate(CreateStmtContext *cxt, PartitionSpec* partspec)
+{
+ CreateStmt *part;
+ List *partlist = NIL;
+ int i = 0;
+ PartitionBoundAutoSpec *bound = (PartitionBoundAutoSpec *) partspec->autopart;
+
+ elog(DEBUG1, "transformPartitionAutoCreate \n %s \n ", nodeToString(bound));
+
+ /*
+ * Generate regular partbounds based on autopart rule.
+ * and form create table statements from these partbounds
+ */
+ if (pg_strcasecmp(partspec->strategy, "hash") == 0)
+ {
+ if (bound->strategy != PARTITION_STRATEGY_HASH)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("invalid bound specification for a hash partition"),
+ parser_errposition(cxt->pstate, exprLocation((Node *) partspec))));
+
+ for (i = 0; i < bound->modulus; i++)
+ {
+ char *part_relname;
+
+ /*
+ * Generate partition name in the format:
+ * $relname_$partnum
+ *
+ * TODO: Add checks on relname length.
+ */
+ part_relname = psprintf("%s_%d", cxt->relation->relname, i);
+
+ part = copyObject(cxt->stmt);
+
+ part->relation = makeRangeVar(cxt->relation->schemaname,
+ part_relname, cxt->relation->location);
+ /* set table as a parent */
+ part->inhRelations = lappend(part->inhRelations, cxt->relation);
+
+ /* child table is not partitioned */
+ part->partspec = NULL;
+
+ /* Actual partbound generation is here */
+ part->partbound = makeNode(PartitionBoundSpec);
+ part->partbound->strategy = PARTITION_STRATEGY_HASH;
+ part->partbound->modulus = bound->modulus;
+ part->partbound->remainder = i;
+ part->partbound->is_default = false;
+
+ elog(DEBUG1,"stransformPartitionAutoCreate HASH i %d MODULUS %d \n %s\n",
+ i, bound->modulus, nodeToString(part));
+
+ partlist = lappend(partlist, part);
+ }
+ }
+ else if (pg_strcasecmp(partspec->strategy, "list") == 0)
+ {
+
+ int n_list_parts = list_length(bound->listdatumsList);
+
+ if (bound->strategy != PARTITION_STRATEGY_LIST)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("invalid bound specification for a list partition"),
+ parser_errposition(cxt->pstate, exprLocation((Node *) partspec))));
+
+ for (i = 0; i < n_list_parts; i++)
+ {
+ char *part_relname;
+ List *listdatums = (List *)
+ list_nth(bound->listdatumsList, i);
+
+ part_relname = psprintf("%s_%d", cxt->relation->relname, i);
+
+ part = copyObject(cxt->stmt);
+
+ part->relation = makeRangeVar(cxt->relation->schemaname,
+ part_relname, cxt->relation->location);
+ /* set table as a parent */
+ part->inhRelations = lappend(part->inhRelations, cxt->relation);
+
+ /* child table is not partitioned */
+ part->partspec = NULL;
+
+ /* Actual partbound generation is here */
+ part->partbound = makeNode(PartitionBoundSpec);
+ part->partbound->strategy = PARTITION_STRATEGY_LIST;
+ part->partbound->listdatums = list_copy(listdatums);
+ part->partbound->is_default = false;
+
+ elog(DEBUG1,"Debug transformPartitionAutoCreate LIST i %d \n %s\n",
+ i, nodeToString(part));
+
+ partlist = lappend(partlist, part);
+ }
+
+ if (bound->default_partition_rv)
+ {
+ part = copyObject(cxt->stmt);
+
+ part->relation = bound->default_partition_rv;
+ /* set table as a parent */
+ part->inhRelations = lappend(part->inhRelations, cxt->relation);
+
+ /* child table is not partitioned */
+ part->partspec = NULL;
+
+ part->partbound = makeNode(PartitionBoundSpec);
+ part->partbound->strategy = PARTITION_STRATEGY_LIST;
+ part->partbound->listdatums = NULL;
+ part->partbound->is_default = true;
+
+ elog(DEBUG1,"Debug transformPartitionAutoCreate LIST default partition \n %s\n",
+ nodeToString(part));
+
+ partlist = lappend(partlist, part);
+ }
+ }
+
+ /* Add statements to create each partition after we create parent table */
+ cxt->alist = list_concat(cxt->alist, partlist);
+}
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 381d84b4e4..ac6fcc029e 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -478,6 +478,7 @@ typedef enum NodeTag
T_PartitionElem,
T_PartitionSpec,
T_PartitionBoundSpec,
+ T_PartitionBoundAutoSpec,
T_PartitionRangeDatum,
T_PartitionCmd,
T_VacuumRelation,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 60c2f45466..ff7903a0dd 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -795,6 +795,9 @@ typedef struct PartitionSpec
* 'range') */
List *partParams; /* List of PartitionElems */
int location; /* token location, or -1 if unknown */
+
+ Node *autopart; /* PartitionBoundAutoSpec -
+ * spec to generate bounds automatically */
} PartitionSpec;
/* Internal codes for partitioning strategies */
@@ -829,6 +832,26 @@ struct PartitionBoundSpec
int location; /* token location, or -1 if unknown */
};
+/*
+ * PartitionBoundAutoSpec - a partition bound specification
+ * for auto generated partitions.
+ *
+ * This represents the rule of generating partition bounds
+ */
+struct PartitionBoundAutoSpec
+{
+ NodeTag type;
+
+ char strategy; /* see PARTITION_STRATEGY codes above */
+
+ /* Partitioning info for HASH strategy: */
+ int modulus;
+
+ /* Partitioning info for LIST strategy: */
+ List *listdatumsList; /* List of lists of Consts (or A_Consts in raw tree) */
+ RangeVar *default_partition_rv; /* Name of default list partition */
+};
+
/*
* PartitionRangeDatum - one of the values in a range partition bound
*
diff --git a/src/include/partitioning/partdefs.h b/src/include/partitioning/partdefs.h
index 6414e2c116..25ecfbd1de 100644
--- a/src/include/partitioning/partdefs.h
+++ b/src/include/partitioning/partdefs.h
@@ -19,6 +19,8 @@ typedef struct PartitionKeyData *PartitionKey;
typedef struct PartitionBoundSpec PartitionBoundSpec;
+typedef struct PartitionBoundAutoSpec PartitionBoundAutoSpec;
+
typedef struct PartitionDescData *PartitionDesc;
typedef struct PartitionDirectoryData *PartitionDirectory;
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index 41dce69cc4..1776721b7b 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -1313,3 +1313,42 @@ Indexes:
"part_column_drop_1_10_expr_idx1" btree ((d = 2))
drop table part_column_drop;
+-- Auto generated partitions
+-- must fail because of wrong configuration
+CREATE TABLE tbl_hash_fail (i int) PARTITION BY HASH (i)
+CONFIGURATION (values in (1, 2), (3, 4) default partition tbl_default);
+ERROR: invalid bound specification for a hash partition
+LINE 1: CREATE TABLE tbl_hash_fail (i int) PARTITION BY HASH (i)
+ ^
+-- must fail because of wrong configuration
+CREATE TABLE tbl_list_fail (i int) PARTITION BY LIST (i)
+CONFIGURATION (values in (1, 2), (1, 3));
+ERROR: partition "tbl_list_fail_1" would overlap partition "tbl_list_fail_0"
+LINE 2: CONFIGURATION (values in (1, 2), (1, 3));
+ ^
+CREATE TABLE tbl_list (i int) PARTITION BY LIST (i)
+CONFIGURATION (values in (1, 2), (3, 4) default partition tbl_default);
+\d+ tbl_list
+ Partitioned table "public.tbl_list"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ i | integer | | | | plain | |
+Partition key: LIST (i)
+Partitions: tbl_list_0 FOR VALUES IN (1, 2),
+ tbl_list_1 FOR VALUES IN (3, 4),
+ tbl_default DEFAULT
+
+CREATE TABLE tbl_hash (i int) PARTITION BY HASH (i)
+CONFIGURATION (modulus 3);
+\d+ tbl_hash
+ Partitioned table "public.tbl_hash"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ i | integer | | | | plain | |
+Partition key: HASH (i)
+Partitions: tbl_hash_0 FOR VALUES WITH (modulus 3, remainder 0),
+ tbl_hash_1 FOR VALUES WITH (modulus 3, remainder 1),
+ tbl_hash_2 FOR VALUES WITH (modulus 3, remainder 2)
+
+DROP TABLE tbl_list;
+DROP TABLE tbl_hash;
diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql
index 9b1adcb8ad..c82fca0a9a 100644
--- a/src/test/regress/sql/create_table.sql
+++ b/src/test/regress/sql/create_table.sql
@@ -971,3 +971,26 @@ create table part_column_drop_1_10 partition of
\d part_column_drop
\d part_column_drop_1_10
drop table part_column_drop;
+
+-- Auto generated partitions
+
+-- must fail because of wrong configuration
+CREATE TABLE tbl_hash_fail (i int) PARTITION BY HASH (i)
+CONFIGURATION (values in (1, 2), (3, 4) default partition tbl_default);
+
+-- must fail because of wrong configuration
+CREATE TABLE tbl_list_fail (i int) PARTITION BY LIST (i)
+CONFIGURATION (values in (1, 2), (1, 3));
+
+CREATE TABLE tbl_list (i int) PARTITION BY LIST (i)
+CONFIGURATION (values in (1, 2), (3, 4) default partition tbl_default);
+
+\d+ tbl_list
+
+CREATE TABLE tbl_hash (i int) PARTITION BY HASH (i)
+CONFIGURATION (modulus 3);
+
+\d+ tbl_hash
+
+DROP TABLE tbl_list;
+DROP TABLE tbl_hash;