From 5808c5e843cac1e1383366a5cbff116eaa433f90 Mon Sep 17 00:00:00 2001 From: anastasia Date: Fri, 3 Jul 2020 03:34:24 +0300 Subject: [PATCH] WIP create partitions automatically Implement new syntax to generate bounds for HASH, LIST and RANGE partitions. Implement automatic partition creation for HASH and LIST. Check new regression test 'auto_partitions.sql' for syntax examples --- src/backend/commands/tablecmds.c | 7 + src/backend/nodes/copyfuncs.c | 33 ++++ src/backend/nodes/equalfuncs.c | 29 +++ src/backend/nodes/outfuncs.c | 28 +++ src/backend/nodes/readfuncs.c | 33 ++++ src/backend/parser/gram.y | 160 ++++++++++++++--- src/backend/parser/parse_utilcmd.c | 166 ++++++++++++++++++ src/include/nodes/nodes.h | 2 + src/include/nodes/parsenodes.h | 43 +++++ src/include/parser/kwlist.h | 1 + src/include/partitioning/partdefs.h | 4 + src/test/regress/expected/auto_partitions.out | 0 src/test/regress/parallel_schedule | 2 + src/test/regress/serial_schedule | 1 + src/test/regress/sql/auto_partitions.sql | 43 +++++ 15 files changed, 523 insertions(+), 29 deletions(-) create mode 100644 src/test/regress/expected/auto_partitions.out create mode 100644 src/test/regress/sql/auto_partitions.sql diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index f79044f39f..7b2c651952 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -628,6 +628,13 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, else partitioned = false; + if (!partitioned && stmt->partautocreate) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("PARTITION bounds can only be used on partitioned tables"))); + } + /* * Look up the namespace in which we are supposed to create the relation, * check we have permission to create there, lock it against concurrent diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index d8cf87e6d0..74a305c5d1 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -3393,6 +3393,7 @@ CopyCreateStmtFields(const CreateStmt *from, CreateStmt *newnode) COPY_NODE_FIELD(inhRelations); COPY_NODE_FIELD(partspec); COPY_NODE_FIELD(partbound); + COPY_NODE_FIELD(partautocreate); COPY_NODE_FIELD(ofTypename); COPY_NODE_FIELD(constraints); COPY_NODE_FIELD(options); @@ -4651,6 +4652,32 @@ _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(interval); + COPY_NODE_FIELD(lowerdatums); + COPY_NODE_FIELD(upperdatums); + + return newnode; +} + +static PartitionAutoCreate * +_copyPartitionAutoCreate(const PartitionAutoCreate *from) +{ + PartitionAutoCreate *newnode = makeNode(PartitionAutoCreate); + + COPY_SCALAR_FIELD(is_deferred); + COPY_NODE_FIELD(bound); + + return newnode; +} + static PartitionRangeDatum * _copyPartitionRangeDatum(const PartitionRangeDatum *from) { @@ -5700,6 +5727,12 @@ copyObjectImpl(const void *from) case T_PartitionBoundSpec: retval = _copyPartitionBoundSpec(from); break; + case T_PartitionBoundAutoSpec: + retval = _copyPartitionBoundAutoSpec(from); + break; + case T_PartitionAutoCreate: + retval = _copyPartitionAutoCreate(from); + break; case T_PartitionRangeDatum: retval = _copyPartitionRangeDatum(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 627b026b19..5ca9ed6a90 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -2917,6 +2917,29 @@ _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(interval); + COMPARE_NODE_FIELD(lowerdatums); + COMPARE_NODE_FIELD(upperdatums); + + return true; +} + +static bool +_equalPartitionAutoCreate(const PartitionAutoCreate *a, const PartitionAutoCreate *b) +{ + COMPARE_SCALAR_FIELD(is_deferred); + COMPARE_NODE_FIELD(bound); + + return true; +} + static bool _equalPartitionRangeDatum(const PartitionRangeDatum *a, const PartitionRangeDatum *b) { @@ -3752,6 +3775,12 @@ 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_PartitionAutoCreate: + retval = _equalPartitionAutoCreate(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..35b1438197 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -3661,6 +3661,28 @@ _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(interval); + WRITE_NODE_FIELD(lowerdatums); + WRITE_NODE_FIELD(upperdatums); +} + +static void +_outPartitionAutoCreate(StringInfo str, const PartitionAutoCreate *node) +{ + WRITE_NODE_TYPE("PartitionAutoCreate"); + + WRITE_BOOL_FIELD(is_deferred); + WRITE_NODE_FIELD(bound); +} + static void _outPartitionRangeDatum(StringInfo str, const PartitionRangeDatum *node) { @@ -4334,6 +4356,12 @@ outNode(StringInfo str, const void *obj) case T_PartitionBoundSpec: _outPartitionBoundSpec(str, obj); break; + case T_PartitionBoundAutoSpec: + _outPartitionBoundAutoSpec(str, obj); + break; + case T_PartitionAutoCreate: + _outPartitionAutoCreate(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..fa2b08316b 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -2602,6 +2602,35 @@ _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(interval); + READ_NODE_FIELD(lowerdatums); + READ_NODE_FIELD(upperdatums); + + READ_DONE(); +} + +/* + * _readPartitionAutoCreate + */ +static PartitionAutoCreate * +_readPartitionAutoCreate(void) +{ + READ_LOCALS(PartitionAutoCreate); + + READ_BOOL_FIELD(is_deferred); + READ_NODE_FIELD(bound); + + READ_DONE(); +} + /* * _readPartitionRangeDatum */ @@ -2880,6 +2909,10 @@ parseNodeString(void) return_value = _readPartitionBoundSpec(); else if (MATCH("PARTITIONRANGEDATUM", 19)) return_value = _readPartitionRangeDatum(); + else if (MATCH("PARTITIONAUTPBOUNDSPEC", 21)) + return_value = _readPartitionAutoCreate(); + 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 4ff35095b8..3c62837763 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -249,6 +249,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); PartitionElem *partelem; PartitionSpec *partspec; PartitionBoundSpec *partboundspec; + PartitionBoundAutoSpec *partboundautospec; + PartitionAutoCreate *partautocreate; RoleSpec *rolespec; struct SelectLimit *selectlimit; } @@ -599,6 +601,11 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type hash_partbound %type hash_partbound_elem +%type PartitionBoundAutoSpec values_in_clause +%type PartitionAutoCreate OptPartitionAutoCreate +%type opt_part_deferred +%type range_interval + /* * 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 @@ -669,7 +676,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED - MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE + MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MODULUS MONTH_P MOVE NAME_P NAMES NATIONAL NATURAL NCHAR NEW NEXT NFC NFD NFKC NFKD NO NONE NORMALIZE NORMALIZED @@ -3179,7 +3186,8 @@ copy_generic_opt_arg_list_item: *****************************************************************************/ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' - OptInherit OptPartitionSpec table_access_method_clause OptWith + OptInherit OptPartitionSpec OptPartitionAutoCreate + table_access_method_clause OptWith OnCommitOption OptTableSpace { CreateStmt *n = makeNode(CreateStmt); @@ -3188,17 +3196,19 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->tableElts = $6; n->inhRelations = $8; n->partspec = $9; + n->partautocreate = $10; n->ofTypename = NULL; n->constraints = NIL; - n->accessMethod = $10; - n->options = $11; - n->oncommit = $12; - n->tablespacename = $13; + n->accessMethod = $11; + n->options = $12; + n->oncommit = $13; + n->tablespacename = $14; n->if_not_exists = false; $$ = (Node *)n; } | CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name '(' - OptTableElementList ')' OptInherit OptPartitionSpec table_access_method_clause + OptTableElementList ')' OptInherit OptPartitionSpec + OptPartitionAutoCreate table_access_method_clause OptWith OnCommitOption OptTableSpace { CreateStmt *n = makeNode(CreateStmt); @@ -3207,17 +3217,19 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->tableElts = $9; n->inhRelations = $11; n->partspec = $12; + n->partautocreate = $13; n->ofTypename = NULL; n->constraints = NIL; - n->accessMethod = $13; - n->options = $14; - n->oncommit = $15; - n->tablespacename = $16; + n->accessMethod = $14; + n->options = $15; + n->oncommit = $16; + n->tablespacename = $17; n->if_not_exists = true; $$ = (Node *)n; } | CREATE OptTemp TABLE qualified_name OF any_name - OptTypedTableElementList OptPartitionSpec table_access_method_clause + OptTypedTableElementList OptPartitionSpec + OptPartitionAutoCreate table_access_method_clause OptWith OnCommitOption OptTableSpace { CreateStmt *n = makeNode(CreateStmt); @@ -3226,18 +3238,20 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->tableElts = $7; n->inhRelations = NIL; n->partspec = $8; + n->partautocreate = $9; n->ofTypename = makeTypeNameFromNameList($6); n->ofTypename->location = @6; n->constraints = NIL; - n->accessMethod = $9; - n->options = $10; - n->oncommit = $11; - n->tablespacename = $12; + n->accessMethod = $10; + n->options = $11; + n->oncommit = $12; + n->tablespacename = $13; n->if_not_exists = false; $$ = (Node *)n; } | CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name OF any_name - OptTypedTableElementList OptPartitionSpec table_access_method_clause + OptTypedTableElementList OptPartitionSpec + OptPartitionAutoCreate table_access_method_clause OptWith OnCommitOption OptTableSpace { CreateStmt *n = makeNode(CreateStmt); @@ -3246,18 +3260,20 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->tableElts = $10; n->inhRelations = NIL; n->partspec = $11; + n->partautocreate = $12; n->ofTypename = makeTypeNameFromNameList($9); n->ofTypename->location = @9; n->constraints = NIL; - n->accessMethod = $12; - n->options = $13; - n->oncommit = $14; - n->tablespacename = $15; + n->accessMethod = $13; + n->options = $14; + n->oncommit = $15; + n->tablespacename = $16; n->if_not_exists = true; $$ = (Node *)n; } | CREATE OptTemp TABLE qualified_name PARTITION OF qualified_name OptTypedTableElementList PartitionBoundSpec OptPartitionSpec + OptPartitionAutoCreate table_access_method_clause OptWith OnCommitOption OptTableSpace { CreateStmt *n = makeNode(CreateStmt); @@ -3267,17 +3283,19 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->inhRelations = list_make1($7); n->partbound = $9; n->partspec = $10; + n->partautocreate = $11; n->ofTypename = NULL; n->constraints = NIL; - n->accessMethod = $11; - n->options = $12; - n->oncommit = $13; - n->tablespacename = $14; + n->accessMethod = $12; + n->options = $13; + n->oncommit = $14; + n->tablespacename = $15; n->if_not_exists = false; $$ = (Node *)n; } | CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name PARTITION OF qualified_name OptTypedTableElementList PartitionBoundSpec OptPartitionSpec + OptPartitionAutoCreate table_access_method_clause OptWith OnCommitOption OptTableSpace { CreateStmt *n = makeNode(CreateStmt); @@ -3287,12 +3305,13 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->inhRelations = list_make1($10); n->partbound = $12; n->partspec = $13; + n->partautocreate = $14; n->ofTypename = NULL; n->constraints = NIL; - n->accessMethod = $14; - n->options = $15; - n->oncommit = $16; - n->tablespacename = $17; + n->accessMethod = $15; + n->options = $16; + n->oncommit = $17; + n->tablespacename = $18; n->if_not_exists = true; $$ = (Node *)n; } @@ -3958,6 +3977,88 @@ part_elem: ColId opt_collate opt_class } ; +/* Optional partition automatic creation specification */ +OptPartitionAutoCreate: PartitionAutoCreate { $$ = $1; } + | /*EMPTY*/ { $$ = NULL; } + ; +/* XXX + * CONFIGURATION is just a random keyword that exists already and fits here. + * Any ideas on better wording? + */ +PartitionAutoCreate: CONFIGURATION opt_part_deferred USING PartitionBoundAutoSpec + { + PartitionAutoCreate *n = makeNode(PartitionAutoCreate); + + n->is_deferred = $2; + n->bound = $4; + + $$ = n; + } + ; + +opt_part_deferred: + DEFERRED { $$ = true; } + | IMMEDIATE { $$ = false; } + | /* EMPTY*/ { $$ = false;} + ; + +PartitionBoundAutoSpec: + /* a HASH partition */ + MODULUS Iconst + { + PartitionBoundAutoSpec *n = makeNode(PartitionBoundAutoSpec); + + n->strategy = PARTITION_STRATEGY_HASH; + n->modulus = (int16) $2; + + $$ = n; + } + + /* a LIST partition */ + | values_in_clause { $$ = $1; } + + /* a RANGE partition */ + | range_interval FROM '(' expr_list ')' TO '(' expr_list ')' + { + PartitionBoundAutoSpec *n = makeNode(PartitionBoundAutoSpec); + + n->strategy = PARTITION_STRATEGY_RANGE; + n->interval = $1; + n->lowerdatums = $4; + n->upperdatums = $8; + + $$ = n; + } + ; + +/* TODO allow not only interval, but also other types of Const values */ +range_interval: + ConstInterval Sconst opt_interval + { + TypeName *t = $1; + t->typmods = $3; + $$ = makeStringConstCast($2, @2, t); + } + ; + +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; + } + ; + + table_access_method_clause: USING name { $$ = $2; } | /*EMPTY*/ { $$ = NULL; } @@ -15166,6 +15267,7 @@ unreserved_keyword: | MINUTE_P | MINVALUE | MODE + | MODULUS | MONTH_P | MOVE | NAME_P diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 0e4caa6ad4..e4d638aa04 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 */ @@ -95,6 +96,7 @@ typedef struct IndexStmt *pkey; /* PRIMARY KEY index, if any */ bool ispartitioned; /* true if table is partitioned */ PartitionBoundSpec *partbound; /* transformed FOR VALUES */ + PartitionAutoCreate *partautocreate; /* transformed PartitionAutoCreate CONFIGURATION */ bool ofType; /* true if statement contains OF typename */ } CreateStmtContext; @@ -146,6 +148,7 @@ static Const *transformPartitionBoundValue(ParseState *pstate, Node *con, const char *colName, Oid colType, int32 colTypmod, Oid partCollation); +static void transformPartitionAutoCreate(CreateStmtContext *cxt); /* * transformCreateStmt - @@ -233,6 +236,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; @@ -248,6 +252,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) cxt.pkey = NULL; cxt.ispartitioned = stmt->partspec != NULL; cxt.partbound = stmt->partbound; + cxt.partautocreate = stmt->partautocreate; cxt.ofType = (stmt->ofTypename != NULL); Assert(!stmt->ofTypename || !stmt->inhRelations); /* grammar enforces */ @@ -323,6 +328,10 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) */ transformExtendedStatistics(&cxt); + /* Process partition definitions */ + if (cxt.partautocreate) + transformPartitionAutoCreate(&cxt); + /* * Output results. */ @@ -4172,3 +4181,160 @@ transformPartitionBoundValue(ParseState *pstate, Node *val, return (Const *) value; } + + +/* + * Transform configuration into a set of partition bounds. + * Generate statements to create partition tables. + */ +static void +transformPartitionAutoCreate(CreateStmtContext *cxt) +{ + CreateStmt *part; + List *partlist = NIL; + ListCell *lc; + int i = 0; + PartitionBoundAutoSpec *bound = cxt->partautocreate->bound; + + if (cxt->partautocreate->is_deferred) + elog(ERROR, "Dynamic generation of partitions is not implemented yet"); + + /* Generate regular partbounds based on partautocreate. + * Generate create table statements from these partbounds/ + */ + if (bound->strategy == PARTITION_STRATEGY_HASH) + { + for (i = 0; i < bound->modulus; i++) + { + char *part_relname; + + /* + * sGenerate partition name in the format: + * $relname_$partnum + * + * TODO: Add checks on relname length. + */ + part_relname = psprintf("%s_%d", cxt->relation->relname, i); + + part = makeNode(CreateStmt); + + part->relation = makeRangeVar(cxt->relation->schemaname, + part_relname, cxt->relation->location); + part->tableElts = list_copy(cxt->columns); + /* set table as a parent */ + part->inhRelations = lappend(part->inhRelations, cxt->relation); + + /* 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; + + part->partspec = NULL; + part->partautocreate = NULL; + part->ofTypename = cxt->stmt->ofTypename; //TODO + part->constraints = list_copy(cxt->stmt->constraints); //TODO + part->options = cxt->stmt->options; //TODO + part->oncommit = cxt->stmt->oncommit; //TODO + part->tablespacename = cxt->stmt->tablespacename; + part->accessMethod = cxt->stmt->accessMethod; + + elog(DEBUG1,"Debug transformPartitionAutoCreate HASH i %d MODULUS %d \n %s\n", + i, bound->modulus, nodeToString(part)); + + partlist = lappend(partlist, part); + } + } + else if (bound->strategy == PARTITION_STRATEGY_LIST) + { + + int n_list_parts = list_length(bound->listdatumsList); + + 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 = makeNode(CreateStmt); + + part->relation = makeRangeVar(cxt->relation->schemaname, + part_relname, cxt->relation->location); + part->tableElts = list_copy(cxt->columns); + /* set table as a parent */ + part->inhRelations = lappend(part->inhRelations, cxt->relation); + + /* 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; + + part->partspec = NULL; + part->partautocreate = NULL; + part->ofTypename = cxt->stmt->ofTypename; //TODO + part->constraints = list_copy(cxt->stmt->constraints); //TODO + part->options = cxt->stmt->options; //TODO + part->oncommit = cxt->stmt->oncommit; //TODO + part->tablespacename = cxt->stmt->tablespacename; + part->accessMethod = cxt->stmt->accessMethod; + + elog(DEBUG1,"Debug transformPartitionAutoCreate LIST i %d \n %s\n",i, nodeToString(part)); + + partlist = lappend(partlist, part); + } + + } + else if (bound->strategy == PARTITION_STRATEGY_RANGE) + { + + elog(WARNING, "Automatic generation of RANGE partition bounds is not implemented yet.\n" + "This command will only create one partition"); + for (i = 0; i < 1; i++) + { + char *part_relname; + + part_relname = psprintf("%s_%d", cxt->relation->relname, i); + + part = makeNode(CreateStmt); + + part->relation = makeRangeVar(cxt->relation->schemaname, + part_relname, cxt->relation->location); + part->tableElts = list_copy(cxt->columns); + /* set table as a parent */ + part->inhRelations = lappend(part->inhRelations, cxt->relation); + + /* Actual partbound generation is here */ + part->partbound = makeNode(PartitionBoundSpec); + part->partbound->strategy = PARTITION_STRATEGY_RANGE; + /* + * TODO Implement partition bound generation:s + * add bound->interval to lowerbound, while upperbound is not reached. + * Can we use SPI here to simplify operations with different data types + * adn their operators? + */ + part->partbound->lowerdatums = list_copy(bound->lowerdatums); + part->partbound->upperdatums = list_copy(bound->upperdatums); + part->partbound->is_default = false; + + part->partspec = NULL; + part->partautocreate = NULL; + part->ofTypename = cxt->stmt->ofTypename; //TODO + part->constraints = list_copy(cxt->stmt->constraints); //TODO + part->options = cxt->stmt->options; //TODO + part->oncommit = cxt->stmt->oncommit; //TODO + part->tablespacename = cxt->stmt->tablespacename; + part->accessMethod = cxt->stmt->accessMethod; + + elog(DEBUG1,"transformPartitionAutoCreate RANGE i %d \n %s\n",i, nodeToString(part)); + + partlist = lappend(partlist, part); + } + } + + /* Add statemets to create each partition */ + cxt->alist = list_concat(cxt->alist, partlist); +} diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 381d84b4e4..cad0e5e10c 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -478,6 +478,8 @@ typedef enum NodeTag T_PartitionElem, T_PartitionSpec, T_PartitionBoundSpec, + T_PartitionBoundAutoSpec, + T_PartitionAutoCreate, T_PartitionRangeDatum, T_PartitionCmd, T_VacuumRelation, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 5e1ffafb91..95fc993cb4 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -828,6 +828,48 @@ struct PartitionBoundSpec int location; /* token location, or -1 if unknown */ }; +/* + * PartitionBoundAutoSpec - a partition bound specification + * + * 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) */ + + /* Partitioning info for RANGE strategy: */ + Node *interval; /* TODO */ + List *lowerdatums; /* List of PartitionRangeDatums */ + List *upperdatums; /* List of PartitionRangeDatums */ + +}; + +/* + * PartitionAutoCreate - a partition bound specification for automatic creation + * + * This represents the information needed automatically calculate partition bounds. + * Now only HASH strategy is implemented + */ +struct PartitionAutoCreate +{ + NodeTag type; + + bool is_deferred; /* create partitions statically (on create statement) + * or create them dynamically (when first insertion happens) + * DEFERRED creation is not supported yet. + */ + /* The rule of generating partition bounds */ + PartitionBoundAutoSpec *bound; +}; + /* * PartitionRangeDatum - one of the values in a range partition bound * @@ -2075,6 +2117,7 @@ typedef struct CreateStmt * inhRelation) */ PartitionBoundSpec *partbound; /* FOR VALUES clause */ PartitionSpec *partspec; /* PARTITION BY clause */ + PartitionAutoCreate *partautocreate; /* CONFIGURATION clause */ TypeName *ofTypename; /* OF typename */ List *constraints; /* constraints (list of Constraint nodes) */ List *options; /* options from WITH clause */ diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index 08f22ce211..497c58266c 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -251,6 +251,7 @@ PG_KEYWORD("method", METHOD, UNRESERVED_KEYWORD) PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD) PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD) PG_KEYWORD("mode", MODE, UNRESERVED_KEYWORD) +PG_KEYWORD("modulus", MODULUS, UNRESERVED_KEYWORD) PG_KEYWORD("month", MONTH_P, UNRESERVED_KEYWORD) PG_KEYWORD("move", MOVE, UNRESERVED_KEYWORD) PG_KEYWORD("name", NAME_P, UNRESERVED_KEYWORD) diff --git a/src/include/partitioning/partdefs.h b/src/include/partitioning/partdefs.h index 6414e2c116..160b1c66ff 100644 --- a/src/include/partitioning/partdefs.h +++ b/src/include/partitioning/partdefs.h @@ -19,6 +19,10 @@ typedef struct PartitionKeyData *PartitionKey; typedef struct PartitionBoundSpec PartitionBoundSpec; +typedef struct PartitionBoundAutoSpec PartitionBoundAutoSpec; + +typedef struct PartitionAutoCreate PartitionAutoCreate; + typedef struct PartitionDescData *PartitionDesc; typedef struct PartitionDirectoryData *PartitionDirectory; diff --git a/src/test/regress/expected/auto_partitions.out b/src/test/regress/expected/auto_partitions.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 026ea880cd..6ca08bf544 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -119,5 +119,7 @@ test: event_trigger # this test also uses event triggers, so likewise run it by itself test: fast_default +test: auto_partitions + # run stats by itself because its delay may be insufficient under heavy load test: stats diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 979d926119..94da936c84 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -200,4 +200,5 @@ test: tuplesort test: explain test: event_trigger test: fast_default +test: auto_partitions test: stats diff --git a/src/test/regress/sql/auto_partitions.sql b/src/test/regress/sql/auto_partitions.sql new file mode 100644 index 0000000000..6df1bbaf48 --- /dev/null +++ b/src/test/regress/sql/auto_partitions.sql @@ -0,0 +1,43 @@ +/* Dynamic generation of partitions is not implemented yet */ +CREATE TABLE tbl_hash (i int) PARTITION BY HASH (i) +CONFIGURATION DEFERRED USING MODULUS 3; + +/* Hash */ +CREATE TABLE tbl_hash (i int) PARTITION BY HASH (i) +CONFIGURATION IMMEDIATE USING MODULUS 3; + +\d+ tbl_hash + +INSERT INTO tbl_hash select * from generate_series(0,10); + +SELECT i from tbl_hash_0; +SELECT i from tbl_hash_1; +SELECT i from tbl_hash_2; + +DROP TABLE tbl_hash; + +/* List */ +CREATE TABLE tbl_list (i char) PARTITION BY LIST (i) +CONFIGURATION IMMEDIATE USING VALUES IN ('a', 'b'), ('c', 'd'), ('e','f'); + +\d+ tbl_list + +INSERT INTO tbl_list values ('a'), ('b'), ('c'), ('d'), ('e'), ('f'); + +SELECT i from tbl_list_0; +SELECT i from tbl_list_1; +SELECT i from tbl_list_2; + +/* Must fail. No default partition */ +INSERT INTO tbl_list values ('q'); + +DROP TABLE tbl_list; + +/* Range. + * Automatic generation of RANGE partition bounds is not implemented yet. + * This command will only create one partition + */ +CREATE TABLE tbl_range (i timestamptz) PARTITION BY RANGE (i) + CONFIGURATION IMMEDIATE USING INTERVAL '1 month 2 days' FROM ('2020-01-01') TO ('2020-12-31'); + +\d+ tbl_range \ No newline at end of file -- 2.17.1