diff options
-rw-r--r-- | src/backend/catalog/heap.c | 2 | ||||
-rw-r--r-- | src/backend/catalog/partition.c | 64 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 90 | ||||
-rw-r--r-- | src/backend/nodes/copyfuncs.c | 30 | ||||
-rw-r--r-- | src/backend/nodes/equalfuncs.c | 22 | ||||
-rw-r--r-- | src/backend/nodes/nodeFuncs.c | 6 | ||||
-rw-r--r-- | src/backend/nodes/outfuncs.c | 28 | ||||
-rw-r--r-- | src/backend/nodes/readfuncs.c | 4 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 29 | ||||
-rw-r--r-- | src/backend/parser/parse_utilcmd.c | 223 | ||||
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 28 | ||||
-rw-r--r-- | src/include/catalog/heap.h | 3 | ||||
-rw-r--r-- | src/include/catalog/partition.h | 6 | ||||
-rw-r--r-- | src/include/nodes/nodes.h | 2 | ||||
-rw-r--r-- | src/include/nodes/parsenodes.h | 48 | ||||
-rw-r--r-- | src/include/parser/parse_utilcmd.h | 4 | ||||
-rw-r--r-- | src/test/regress/expected/create_table.out | 25 | ||||
-rw-r--r-- | src/test/regress/sql/create_table.sql | 17 |
18 files changed, 361 insertions, 270 deletions
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index fa926048e11..0ce94f346f5 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -3219,7 +3219,7 @@ RemovePartitionKeyByRelId(Oid relid) * the new partition's info into its partition descriptor. */ void -StorePartitionBound(Relation rel, Relation parent, Node *bound) +StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound) { Relation classRel; HeapTuple tuple, diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 7f2fd58462d..097f191f080 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -247,15 +247,16 @@ RelationBuildPartitionDesc(Relation rel) null_index = -1; foreach(cell, boundspecs) { + PartitionBoundSpec *spec = castNode(PartitionBoundSpec, + lfirst(cell)); ListCell *c; - PartitionBoundSpec *spec = lfirst(cell); if (spec->strategy != PARTITION_STRATEGY_LIST) elog(ERROR, "invalid strategy in partition bound spec"); foreach(c, spec->listdatums) { - Const *val = lfirst(c); + Const *val = castNode(Const, lfirst(c)); PartitionListValue *list_value = NULL; if (!val->constisnull) @@ -327,7 +328,8 @@ RelationBuildPartitionDesc(Relation rel) i = j = 0; foreach(cell, boundspecs) { - PartitionBoundSpec *spec = lfirst(cell); + PartitionBoundSpec *spec = castNode(PartitionBoundSpec, + lfirst(cell)); PartitionRangeBound *lower, *upper; @@ -665,9 +667,9 @@ partition_bounds_equal(PartitionKey key, * of parent. Also performs additional checks as necessary per strategy. */ void -check_new_partition_bound(char *relname, Relation parent, Node *bound) +check_new_partition_bound(char *relname, Relation parent, + PartitionBoundSpec *spec) { - PartitionBoundSpec *spec = (PartitionBoundSpec *) bound; PartitionKey key = RelationGetPartitionKey(parent); PartitionDesc partdesc = RelationGetPartitionDesc(parent); ParseState *pstate = make_parsestate(NULL); @@ -692,7 +694,7 @@ check_new_partition_bound(char *relname, Relation parent, Node *bound) foreach(cell, spec->listdatums) { - Const *val = lfirst(cell); + Const *val = castNode(Const, lfirst(cell)); if (!val->constisnull) { @@ -889,9 +891,9 @@ get_partition_parent(Oid relid) * expressions as partition constraint */ List * -get_qual_from_partbound(Relation rel, Relation parent, Node *bound) +get_qual_from_partbound(Relation rel, Relation parent, + PartitionBoundSpec *spec) { - PartitionBoundSpec *spec = (PartitionBoundSpec *) bound; PartitionKey key = RelationGetPartitionKey(parent); List *my_qual = NIL; @@ -1328,7 +1330,7 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec) prev = NULL; for (cell = list_head(spec->listdatums); cell; cell = next) { - Const *val = (Const *) lfirst(cell); + Const *val = castNode(Const, lfirst(cell)); next = lnext(cell); @@ -1427,12 +1429,12 @@ get_range_key_properties(PartitionKey key, int keynum, } if (!ldatum->infinite) - *lower_val = (Const *) ldatum->value; + *lower_val = castNode(Const, ldatum->value); else *lower_val = NULL; if (!udatum->infinite) - *upper_val = (Const *) udatum->value; + *upper_val = castNode(Const, udatum->value); else *upper_val = NULL; } @@ -1448,7 +1450,7 @@ get_range_key_properties(PartitionKey key, int keynum, * as the lower bound tuple and (au, bu, cu) as the upper bound tuple, we * generate an expression tree of the following form: * - * (a IS NOT NULL) and (b IS NOT NULL) and (c IS NOT NULL) + * (a IS NOT NULL) and (b IS NOT NULL) and (c IS NOT NULL) * AND * (a > al OR (a = al AND b > bl) OR (a = al AND b = bl AND c >= cl)) * AND @@ -1458,7 +1460,7 @@ get_range_key_properties(PartitionKey key, int keynum, * the same values, for example, (al = au), in which case, we will emit an * expression tree of the following form: * - * (a IS NOT NULL) and (b IS NOT NULL) and (c IS NOT NULL) + * (a IS NOT NULL) and (b IS NOT NULL) and (c IS NOT NULL) * AND * (a = al) * AND @@ -1512,8 +1514,8 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec) num_or_arms = key->partnatts; /* - * A range-partitioned table does not currently allow partition keys to - * be null, so emit an IS NOT NULL expression for each key column. + * A range-partitioned table does not currently allow partition keys to be + * null, so emit an IS NOT NULL expression for each key column. */ partexprs_item = list_head(key->partexprs); for (i = 0; i < key->partnatts; i++) @@ -1565,8 +1567,8 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec) Datum test_result; bool isNull; - ldatum = lfirst(cell1); - udatum = lfirst(cell2); + ldatum = castNode(PartitionRangeDatum, lfirst(cell1)); + udatum = castNode(PartitionRangeDatum, lfirst(cell2)); /* * Since get_range_key_properties() modifies partexprs_item, and we @@ -1644,12 +1646,14 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec) PartitionRangeDatum *ldatum_next = NULL, *udatum_next = NULL; - ldatum = lfirst(cell1); + ldatum = castNode(PartitionRangeDatum, lfirst(cell1)); if (lnext(cell1)) - ldatum_next = lfirst(lnext(cell1)); - udatum = lfirst(cell2); + ldatum_next = castNode(PartitionRangeDatum, + lfirst(lnext(cell1))); + udatum = castNode(PartitionRangeDatum, lfirst(cell2)); if (lnext(cell2)) - udatum_next = lfirst(lnext(cell2)); + udatum_next = castNode(PartitionRangeDatum, + lfirst(lnext(cell2))); get_range_key_properties(key, j, ldatum, udatum, &partexprs_item, &keyCol, @@ -1779,7 +1783,7 @@ generate_partition_qual(Relation rel) MemoryContext oldcxt; Datum boundDatum; bool isnull; - Node *bound; + PartitionBoundSpec *bound; List *my_qual = NIL, *result = NIL; Relation parent; @@ -1807,7 +1811,8 @@ generate_partition_qual(Relation rel) if (isnull) /* should not happen */ elog(ERROR, "relation \"%s\" has relpartbound = null", RelationGetRelationName(rel)); - bound = stringToNode(TextDatumGetCString(boundDatum)); + bound = castNode(PartitionBoundSpec, + stringToNode(TextDatumGetCString(boundDatum))); ReleaseSysCache(tuple); my_qual = get_qual_from_partbound(rel, parent, bound); @@ -1971,9 +1976,8 @@ get_partition_for_tuple(PartitionDispatch *pd, if (key->strategy == PARTITION_STRATEGY_RANGE) { /* - * Since we cannot route tuples with NULL partition keys through - * a range-partitioned table, simply return that no partition - * exists + * Since we cannot route tuples with NULL partition keys through a + * range-partitioned table, simply return that no partition exists */ for (i = 0; i < key->partnatts; i++) { @@ -2080,7 +2084,7 @@ static PartitionRangeBound * make_one_range_bound(PartitionKey key, int index, List *datums, bool lower) { PartitionRangeBound *bound; - ListCell *cell; + ListCell *lc; int i; bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound)); @@ -2091,9 +2095,9 @@ make_one_range_bound(PartitionKey key, int index, List *datums, bool lower) bound->lower = lower; i = 0; - foreach(cell, datums) + foreach(lc, datums) { - PartitionRangeDatum *datum = lfirst(cell); + PartitionRangeDatum *datum = castNode(PartitionRangeDatum, lfirst(lc)); /* What's contained in this range datum? */ bound->content[i] = !datum->infinite @@ -2103,7 +2107,7 @@ make_one_range_bound(PartitionKey key, int index, List *datums, bool lower) if (bound->content[i] == RANGE_DATUM_FINITE) { - Const *val = (Const *) datum->value; + Const *val = castNode(Const, datum->value); if (val->constisnull) elog(ERROR, "invalid range bound datum"); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index fb961e46c4a..7959120f53e 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -756,7 +756,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, /* Process and store partition bound, if any. */ if (stmt->partbound) { - Node *bound; + PartitionBoundSpec *bound; ParseState *pstate; Oid parentId = linitial_oid(inheritOids); Relation parent; @@ -777,6 +777,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, /* Tranform the bound values */ pstate = make_parsestate(NULL); pstate->p_sourcetext = queryString; + bound = transformPartitionBound(pstate, parent, stmt->partbound); /* @@ -812,6 +813,15 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, Oid partcollation[PARTITION_MAX_KEYS]; List *partexprs = NIL; + partnatts = list_length(stmt->partspec->partParams); + + /* Protect fixed-size arrays here and in executor */ + if (partnatts > PARTITION_MAX_KEYS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_COLUMNS), + errmsg("cannot partition using more than %d columns", + PARTITION_MAX_KEYS))); + /* * We need to transform the raw parsetrees corresponding to partition * expressions into executable expression trees. Like column defaults @@ -820,11 +830,11 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, */ stmt->partspec = transformPartitionSpec(rel, stmt->partspec, &strategy); + ComputePartitionAttrs(rel, stmt->partspec->partParams, partattrs, &partexprs, partopclass, partcollation); - partnatts = list_length(stmt->partspec->partParams); StorePartitionKey(rel, strategy, partnatts, partattrs, partexprs, partopclass, partcollation); } @@ -13109,6 +13119,8 @@ RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid, /* * Transform any expressions present in the partition key + * + * Returns a transformed PartitionSpec, as well as the strategy code */ static PartitionSpec * transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy) @@ -13121,13 +13133,13 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy) newspec = makeNode(PartitionSpec); newspec->strategy = partspec->strategy; - newspec->location = partspec->location; newspec->partParams = NIL; + newspec->location = partspec->location; /* Parse partitioning strategy name */ - if (!pg_strcasecmp(partspec->strategy, "list")) + if (pg_strcasecmp(partspec->strategy, "list") == 0) *strategy = PARTITION_STRATEGY_LIST; - else if (!pg_strcasecmp(partspec->strategy, "range")) + else if (pg_strcasecmp(partspec->strategy, "range") == 0) *strategy = PARTITION_STRATEGY_RANGE; else ereport(ERROR, @@ -13135,6 +13147,13 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy) errmsg("unrecognized partitioning strategy \"%s\"", partspec->strategy))); + /* Check valid number of columns for strategy */ + if (*strategy == PARTITION_STRATEGY_LIST && + list_length(partspec->partParams) != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("cannot use \"list\" partition strategy with more than one column"))); + /* * Create a dummy ParseState and insert the target relation as its sole * rangetable entry. We need a ParseState for transformExpr. @@ -13146,16 +13165,16 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy) /* take care of any partition expressions */ foreach(l, partspec->partParams) { + PartitionElem *pelem = castNode(PartitionElem, lfirst(l)); ListCell *lc; - PartitionElem *pelem = (PartitionElem *) lfirst(l); /* Check for PARTITION BY ... (foo, foo) */ foreach(lc, newspec->partParams) { - PartitionElem *pparam = (PartitionElem *) lfirst(lc); + PartitionElem *pparam = castNode(PartitionElem, lfirst(lc)); if (pelem->name && pparam->name && - !strcmp(pelem->name, pparam->name)) + strcmp(pelem->name, pparam->name) == 0) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_COLUMN), errmsg("column \"%s\" appears more than once in partition key", @@ -13165,6 +13184,9 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy) if (pelem->expr) { + /* Copy, to avoid scribbling on the input */ + pelem = copyObject(pelem); + /* Now do parse transformation of the expression */ pelem->expr = transformExpr(pstate, pelem->expr, EXPR_KIND_PARTITION_EXPRESSION); @@ -13180,7 +13202,8 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy) } /* - * Compute per-partition-column information from a list of PartitionElem's + * Compute per-partition-column information from a list of PartitionElems. + * Expressions in the PartitionElems must be parse-analyzed already. */ static void ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs, @@ -13192,7 +13215,7 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs, attn = 0; foreach(lc, partParams) { - PartitionElem *pelem = (PartitionElem *) lfirst(lc); + PartitionElem *pelem = castNode(PartitionElem, lfirst(lc)); Oid atttype; Oid attcollation; @@ -13202,7 +13225,8 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs, HeapTuple atttuple; Form_pg_attribute attform; - atttuple = SearchSysCacheAttName(RelationGetRelid(rel), pelem->name); + atttuple = SearchSysCacheAttName(RelationGetRelid(rel), + pelem->name); if (!HeapTupleIsValid(atttuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), @@ -13212,7 +13236,7 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs, if (attform->attnum <= 0) ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("cannot use system column \"%s\" in partition key", pelem->name))); @@ -13220,8 +13244,6 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs, atttype = attform->atttypid; attcollation = attform->attcollation; ReleaseSysCache(atttuple); - - /* Note that whole-row references can't happen here; see below */ } else { @@ -13240,7 +13262,7 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs, expr = (Node *) ((CollateExpr *) expr)->arg; if (IsA(expr, Var) && - ((Var *) expr)->varattno != InvalidAttrNumber) + ((Var *) expr)->varattno > 0) { /* * User wrote "(column)" or "(column COLLATE something)". @@ -13251,11 +13273,18 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs, else { Bitmapset *expr_attrs = NULL; + int i; partattrs[attn] = 0; /* marks the column as expression */ *partexprs = lappend(*partexprs, expr); /* + * Try to simplify the expression before checking for + * mutability. The main practical value of doing it in this + * order is that an inline-able SQL-language function will be + * accepted if its expansion is immutable, whether or not the + * function itself is marked immutable. + * * Note that expression_planner does not change the passed in * expression destructively and we have already saved the * expression to be stored into the catalog above. @@ -13274,27 +13303,38 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs, errmsg("functions in partition key expression must be marked IMMUTABLE"))); /* - * While it is not exactly *wrong* for an expression to be a - * constant value, it seems better to prevent such input. - */ - if (IsA(expr, Const)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("cannot use constant expression as partition key"))); - - /* * transformPartitionSpec() should have already rejected * subqueries, aggregates, window functions, and SRFs, based * on the EXPR_KIND_ for partition expressions. */ - /* Cannot have expressions containing whole-row references */ + /* + * Cannot have expressions containing whole-row references or + * system column references. + */ pull_varattnos(expr, 1, &expr_attrs); if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, expr_attrs)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("partition key expressions cannot contain whole-row references"))); + for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++) + { + if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber, + expr_attrs)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("partition key expressions cannot contain system column references"))); + } + + /* + * While it is not exactly *wrong* for a partition expression + * to be a constant, it seems better to reject such keys. + */ + if (IsA(expr, Const)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("cannot use constant expression as partition key"))); } } diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 7811ad5d526..36bf1dc92bb 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -4412,18 +4412,6 @@ _copyAlterPolicyStmt(const AlterPolicyStmt *from) return newnode; } -static PartitionSpec * -_copyPartitionSpec(const PartitionSpec *from) -{ - PartitionSpec *newnode = makeNode(PartitionSpec); - - COPY_STRING_FIELD(strategy); - COPY_NODE_FIELD(partParams); - COPY_LOCATION_FIELD(location); - - return newnode; -} - static PartitionElem * _copyPartitionElem(const PartitionElem *from) { @@ -4438,6 +4426,18 @@ _copyPartitionElem(const PartitionElem *from) return newnode; } +static PartitionSpec * +_copyPartitionSpec(const PartitionSpec *from) +{ + PartitionSpec *newnode = makeNode(PartitionSpec); + + COPY_STRING_FIELD(strategy); + COPY_NODE_FIELD(partParams); + COPY_LOCATION_FIELD(location); + + return newnode; +} + static PartitionBoundSpec * _copyPartitionBoundSpec(const PartitionBoundSpec *from) { @@ -5509,12 +5509,12 @@ copyObjectImpl(const void *from) case T_TriggerTransition: retval = _copyTriggerTransition(from); break; - case T_PartitionSpec: - retval = _copyPartitionSpec(from); - break; case T_PartitionElem: retval = _copyPartitionElem(from); break; + case T_PartitionSpec: + retval = _copyPartitionSpec(from); + break; case T_PartitionBoundSpec: retval = _copyPartitionBoundSpec(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index c9a8c348927..5bcf0317dc8 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -2813,22 +2813,22 @@ _equalTriggerTransition(const TriggerTransition *a, const TriggerTransition *b) } static bool -_equalPartitionSpec(const PartitionSpec *a, const PartitionSpec *b) +_equalPartitionElem(const PartitionElem *a, const PartitionElem *b) { - COMPARE_STRING_FIELD(strategy); - COMPARE_NODE_FIELD(partParams); + COMPARE_STRING_FIELD(name); + COMPARE_NODE_FIELD(expr); + COMPARE_NODE_FIELD(collation); + COMPARE_NODE_FIELD(opclass); COMPARE_LOCATION_FIELD(location); return true; } static bool -_equalPartitionElem(const PartitionElem *a, const PartitionElem *b) +_equalPartitionSpec(const PartitionSpec *a, const PartitionSpec *b) { - COMPARE_STRING_FIELD(name); - COMPARE_NODE_FIELD(expr); - COMPARE_NODE_FIELD(collation); - COMPARE_NODE_FIELD(opclass); + COMPARE_STRING_FIELD(strategy); + COMPARE_NODE_FIELD(partParams); COMPARE_LOCATION_FIELD(location); return true; @@ -3660,12 +3660,12 @@ equal(const void *a, const void *b) case T_TriggerTransition: retval = _equalTriggerTransition(a, b); break; - case T_PartitionSpec: - retval = _equalPartitionSpec(a, b); - break; case T_PartitionElem: retval = _equalPartitionElem(a, b); break; + case T_PartitionSpec: + retval = _equalPartitionSpec(a, b); + break; case T_PartitionBoundSpec: retval = _equalPartitionBoundSpec(a, b); break; diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index 95c1d3efbb5..41f3408cfcf 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -1560,6 +1560,12 @@ exprLocation(const Node *expr) /* just use nested expr's location */ loc = exprLocation((Node *) ((const InferenceElem *) expr)->expr); break; + case T_PartitionElem: + loc = ((const PartitionElem *) expr)->location; + break; + case T_PartitionSpec: + loc = ((const PartitionSpec *) expr)->location; + break; case T_PartitionBoundSpec: loc = ((const PartitionBoundSpec *) expr)->location; break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 4949d58864d..9189c8d43f8 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -3516,16 +3516,6 @@ _outForeignKeyCacheInfo(StringInfo str, const ForeignKeyCacheInfo *node) } static void -_outPartitionSpec(StringInfo str, const PartitionSpec *node) -{ - WRITE_NODE_TYPE("PARTITIONBY"); - - WRITE_STRING_FIELD(strategy); - WRITE_NODE_FIELD(partParams); - WRITE_LOCATION_FIELD(location); -} - -static void _outPartitionElem(StringInfo str, const PartitionElem *node) { WRITE_NODE_TYPE("PARTITIONELEM"); @@ -3538,6 +3528,16 @@ _outPartitionElem(StringInfo str, const PartitionElem *node) } static void +_outPartitionSpec(StringInfo str, const PartitionSpec *node) +{ + WRITE_NODE_TYPE("PARTITIONBY"); + + WRITE_STRING_FIELD(strategy); + WRITE_NODE_FIELD(partParams); + WRITE_LOCATION_FIELD(location); +} + +static void _outPartitionBoundSpec(StringInfo str, const PartitionBoundSpec *node) { WRITE_NODE_TYPE("PARTITIONBOUND"); @@ -3546,6 +3546,7 @@ _outPartitionBoundSpec(StringInfo str, const PartitionBoundSpec *node) WRITE_NODE_FIELD(listdatums); WRITE_NODE_FIELD(lowerdatums); WRITE_NODE_FIELD(upperdatums); + /* XXX somebody forgot location field; too late to change for v10 */ } static void @@ -3555,6 +3556,7 @@ _outPartitionRangeDatum(StringInfo str, const PartitionRangeDatum *node) WRITE_BOOL_FIELD(infinite); WRITE_NODE_FIELD(value); + /* XXX somebody forgot location field; too late to change for v10 */ } /* @@ -4184,12 +4186,12 @@ outNode(StringInfo str, const void *obj) case T_TriggerTransition: _outTriggerTransition(str, obj); break; - case T_PartitionSpec: - _outPartitionSpec(str, obj); - break; case T_PartitionElem: _outPartitionElem(str, obj); break; + case T_PartitionSpec: + _outPartitionSpec(str, obj); + break; case T_PartitionBoundSpec: _outPartitionBoundSpec(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index e24f5d67263..b59ebd63ecb 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -2376,6 +2376,8 @@ _readPartitionBoundSpec(void) READ_NODE_FIELD(listdatums); READ_NODE_FIELD(lowerdatums); READ_NODE_FIELD(upperdatums); + /* XXX somebody forgot location field; too late to change for v10 */ + local_node->location = -1; READ_DONE(); } @@ -2390,6 +2392,8 @@ _readPartitionRangeDatum(void) READ_BOOL_FIELD(infinite); READ_NODE_FIELD(value); + /* XXX somebody forgot location field; too late to change for v10 */ + local_node->location = -1; READ_DONE(); } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 28223311e61..ff5b1becf2d 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -239,7 +239,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); VariableSetStmt *vsetstmt; PartitionElem *partelem; PartitionSpec *partspec; - PartitionRangeDatum *partrange_datum; + PartitionBoundSpec *partboundspec; RoleSpec *rolespec; } @@ -575,11 +575,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <str> part_strategy %type <partelem> part_elem %type <list> part_params -%type <node> ForValues -%type <node> partbound_datum -%type <list> partbound_datum_list -%type <partrange_datum> PartitionRangeDatum -%type <list> range_datum_list +%type <partboundspec> ForValues +%type <node> partbound_datum PartitionRangeDatum +%type <list> partbound_datum_list range_datum_list /* * Non-keyword token types. These are hard-wired into the "flex" lexer. @@ -2020,7 +2018,7 @@ partition_cmd: n->subtype = AT_AttachPartition; cmd->name = $3; - cmd->bound = (Node *) $4; + cmd->bound = $4; n->def = (Node *) cmd; $$ = (Node *) n; @@ -2033,6 +2031,7 @@ partition_cmd: n->subtype = AT_DetachPartition; cmd->name = $3; + cmd->bound = NULL; n->def = (Node *) cmd; $$ = (Node *) n; @@ -2661,7 +2660,7 @@ ForValues: n->listdatums = $5; n->location = @3; - $$ = (Node *) n; + $$ = n; } /* a RANGE partition */ @@ -2674,7 +2673,7 @@ ForValues: n->upperdatums = $9; n->location = @3; - $$ = (Node *) n; + $$ = n; } ; @@ -2705,7 +2704,7 @@ PartitionRangeDatum: n->value = NULL; n->location = @1; - $$ = n; + $$ = (Node *) n; } | partbound_datum { @@ -2715,7 +2714,7 @@ PartitionRangeDatum: n->value = $1; n->location = @1; - $$ = n; + $$ = (Node *) n; } ; @@ -3144,7 +3143,7 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->relation = $4; n->tableElts = $8; n->inhRelations = list_make1($7); - n->partbound = (Node *) $9; + n->partbound = $9; n->partspec = $10; n->ofTypename = NULL; n->constraints = NIL; @@ -3163,7 +3162,7 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->relation = $7; n->tableElts = $11; n->inhRelations = list_make1($10); - n->partbound = (Node *) $12; + n->partbound = $12; n->partspec = $13; n->ofTypename = NULL; n->constraints = NIL; @@ -4866,7 +4865,7 @@ CreateForeignTableStmt: n->base.relation = $4; n->base.inhRelations = list_make1($7); n->base.tableElts = $8; - n->base.partbound = (Node *) $9; + n->base.partbound = $9; n->base.ofTypename = NULL; n->base.constraints = NIL; n->base.options = NIL; @@ -4887,7 +4886,7 @@ CreateForeignTableStmt: n->base.relation = $7; n->base.inhRelations = list_make1($10); n->base.tableElts = $11; - n->base.partbound = (Node *) $12; + n->base.partbound = $12; n->base.ofTypename = NULL; n->base.constraints = NIL; n->base.options = NIL; diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index beb099569ba..9134fb9d63c 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -91,7 +91,7 @@ typedef struct * the table */ IndexStmt *pkey; /* PRIMARY KEY index, if any */ bool ispartitioned; /* true if table is partitioned */ - Node *partbound; /* transformed FOR VALUES */ + PartitionBoundSpec *partbound; /* transformed FOR VALUES */ } CreateStmtContext; /* State shared by transformCreateSchemaStmt and its subroutines */ @@ -135,6 +135,8 @@ static void transformConstraintAttrs(CreateStmtContext *cxt, static void transformColumnType(CreateStmtContext *cxt, ColumnDef *column); static void setSchemaName(char *context_schema, char **stmt_schema_name); static void transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd); +static Const *transformPartitionBoundValue(ParseState *pstate, A_Const *con, + const char *colName, Oid colType, int32 colTypmod); /* @@ -256,24 +258,10 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) if (stmt->partspec) { - int partnatts = list_length(stmt->partspec->partParams); - if (stmt->inhRelations && !stmt->partbound) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("cannot create partitioned table as inheritance child"))); - - if (partnatts > PARTITION_MAX_KEYS) - ereport(ERROR, - (errcode(ERRCODE_TOO_MANY_COLUMNS), - errmsg("cannot partition using more than %d columns", - PARTITION_MAX_KEYS))); - - if (!pg_strcasecmp(stmt->partspec->strategy, "list") && - partnatts > 1) - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("cannot list partition using more than one column"))); } /* @@ -3280,24 +3268,33 @@ transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd) /* * transformPartitionBound * - * Transform partition bound specification + * Transform a partition bound specification */ -Node * -transformPartitionBound(ParseState *pstate, Relation parent, Node *bound) +PartitionBoundSpec * +transformPartitionBound(ParseState *pstate, Relation parent, + PartitionBoundSpec *spec) { - PartitionBoundSpec *spec = (PartitionBoundSpec *) bound, - *result_spec; + PartitionBoundSpec *result_spec; PartitionKey key = RelationGetPartitionKey(parent); char strategy = get_partition_strategy(key); int partnatts = get_partition_natts(key); List *partexprs = get_partition_exprs(key); + /* Avoid scribbling on input */ result_spec = copyObject(spec); if (strategy == PARTITION_STRATEGY_LIST) { ListCell *cell; char *colname; + Oid coltype; + int32 coltypmod; + + if (spec->strategy != PARTITION_STRATEGY_LIST) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("invalid bound specification for a list partition"), + parser_errposition(pstate, exprLocation((Node *) spec)))); /* Get the only column's name in case we need to output an error */ if (key->partattrs[0] != 0) @@ -3308,47 +3305,26 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound) deparse_context_for(RelationGetRelationName(parent), RelationGetRelid(parent)), false, false); - - if (spec->strategy != PARTITION_STRATEGY_LIST) - ereport(ERROR, - (errcode(ERRCODE_INVALID_TABLE_DEFINITION), - errmsg("invalid bound specification for a list partition"), - parser_errposition(pstate, exprLocation(bound)))); + /* Need its type data too */ + coltype = get_partition_col_typid(key, 0); + coltypmod = get_partition_col_typmod(key, 0); result_spec->listdatums = NIL; foreach(cell, spec->listdatums) { - A_Const *con = (A_Const *) lfirst(cell); - Node *value; + A_Const *con = castNode(A_Const, lfirst(cell)); + Const *value; ListCell *cell2; bool duplicate; - value = (Node *) make_const(pstate, &con->val, con->location); - value = coerce_to_target_type(pstate, - value, exprType(value), - get_partition_col_typid(key, 0), - get_partition_col_typmod(key, 0), - COERCION_ASSIGNMENT, - COERCE_IMPLICIT_CAST, - -1); - - if (value == NULL) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("specified value cannot be cast to type \"%s\" of column \"%s\"", - format_type_be(get_partition_col_typid(key, 0)), - colname), - parser_errposition(pstate, - exprLocation((Node *) con)))); - - /* Simplify the expression */ - value = (Node *) expression_planner((Expr *) value); + value = transformPartitionBoundValue(pstate, con, + colname, coltype, coltypmod); /* Don't add to the result if the value is a duplicate */ duplicate = false; foreach(cell2, result_spec->listdatums) { - Const *value2 = (Const *) lfirst(cell2); + Const *value2 = castNode(Const, lfirst(cell2)); if (equal(value, value2)) { @@ -3369,16 +3345,13 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound) *cell2; int i, j; - char *colname; bool seen_unbounded; if (spec->strategy != PARTITION_STRATEGY_RANGE) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("invalid bound specification for a range partition"), - parser_errposition(pstate, exprLocation(bound)))); - - Assert(spec->lowerdatums != NIL && spec->upperdatums != NIL); + parser_errposition(pstate, exprLocation((Node *) spec)))); if (list_length(spec->lowerdatums) != partnatts) ereport(ERROR, @@ -3390,15 +3363,15 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound) errmsg("TO must specify exactly one value per partitioning column"))); /* - * Check that no finite value follows a UNBOUNDED literal in either of + * Check that no finite value follows an UNBOUNDED item in either of * lower and upper bound lists. */ seen_unbounded = false; foreach(cell1, spec->lowerdatums) { - PartitionRangeDatum *ldatum; + PartitionRangeDatum *ldatum = castNode(PartitionRangeDatum, + lfirst(cell1)); - ldatum = (PartitionRangeDatum *) lfirst(cell1); if (ldatum->infinite) seen_unbounded = true; else if (seen_unbounded) @@ -3410,9 +3383,9 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound) seen_unbounded = false; foreach(cell1, spec->upperdatums) { - PartitionRangeDatum *rdatum; + PartitionRangeDatum *rdatum = castNode(PartitionRangeDatum, + lfirst(cell1)); - rdatum = (PartitionRangeDatum *) lfirst(cell1); if (rdatum->infinite) seen_unbounded = true; else if (seen_unbounded) @@ -3422,18 +3395,19 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound) parser_errposition(pstate, exprLocation((Node *) rdatum)))); } + /* Transform all the constants */ i = j = 0; result_spec->lowerdatums = result_spec->upperdatums = NIL; forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums) { - PartitionRangeDatum *ldatum, - *rdatum; - Node *value; - A_Const *lcon = NULL, - *rcon = NULL; - - ldatum = (PartitionRangeDatum *) lfirst(cell1); - rdatum = (PartitionRangeDatum *) lfirst(cell2); + PartitionRangeDatum *ldatum = (PartitionRangeDatum *) lfirst(cell1); + PartitionRangeDatum *rdatum = (PartitionRangeDatum *) lfirst(cell2); + char *colname; + Oid coltype; + int32 coltypmod; + A_Const *con; + Const *value; + /* Get the column's name in case we need to output an error */ if (key->partattrs[i] != 0) colname = get_relid_attribute_name(RelationGetRelid(parent), @@ -3446,70 +3420,42 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound) false, false); ++j; } + /* Need its type data too */ + coltype = get_partition_col_typid(key, i); + coltypmod = get_partition_col_typmod(key, i); - if (!ldatum->infinite) - lcon = (A_Const *) ldatum->value; - if (!rdatum->infinite) - rcon = (A_Const *) rdatum->value; - - if (lcon) + if (ldatum->value) { - value = (Node *) make_const(pstate, &lcon->val, lcon->location); - if (((Const *) value)->constisnull) + con = castNode(A_Const, ldatum->value); + value = transformPartitionBoundValue(pstate, con, + colname, + coltype, coltypmod); + if (value->constisnull) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("cannot specify NULL in range bound"))); - value = coerce_to_target_type(pstate, - value, exprType(value), - get_partition_col_typid(key, i), - get_partition_col_typmod(key, i), - COERCION_ASSIGNMENT, - COERCE_IMPLICIT_CAST, - -1); - if (value == NULL) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("specified value cannot be cast to type \"%s\" of column \"%s\"", - format_type_be(get_partition_col_typid(key, i)), - colname), - parser_errposition(pstate, exprLocation((Node *) ldatum)))); - - /* Simplify the expression */ - value = (Node *) expression_planner((Expr *) value); - ldatum->value = value; + ldatum = copyObject(ldatum); /* don't scribble on input */ + ldatum->value = (Node *) value; } - if (rcon) + if (rdatum->value) { - value = (Node *) make_const(pstate, &rcon->val, rcon->location); - if (((Const *) value)->constisnull) + con = castNode(A_Const, rdatum->value); + value = transformPartitionBoundValue(pstate, con, + colname, + coltype, coltypmod); + if (value->constisnull) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("cannot specify NULL in range bound"))); - value = coerce_to_target_type(pstate, - value, exprType(value), - get_partition_col_typid(key, i), - get_partition_col_typmod(key, i), - COERCION_ASSIGNMENT, - COERCE_IMPLICIT_CAST, - -1); - if (value == NULL) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("specified value cannot be cast to type \"%s\" of column \"%s\"", - format_type_be(get_partition_col_typid(key, i)), - colname), - parser_errposition(pstate, exprLocation((Node *) rdatum)))); - - /* Simplify the expression */ - value = (Node *) expression_planner((Expr *) value); - rdatum->value = value; + rdatum = copyObject(rdatum); /* don't scribble on input */ + rdatum->value = (Node *) value; } result_spec->lowerdatums = lappend(result_spec->lowerdatums, - copyObject(ldatum)); + ldatum); result_spec->upperdatums = lappend(result_spec->upperdatums, - copyObject(rdatum)); + rdatum); ++i; } @@ -3517,5 +3463,50 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound) else elog(ERROR, "unexpected partition strategy: %d", (int) strategy); - return (Node *) result_spec; + return result_spec; +} + +/* + * Transform one constant in a partition bound spec + */ +static Const * +transformPartitionBoundValue(ParseState *pstate, A_Const *con, + const char *colName, Oid colType, int32 colTypmod) +{ + Node *value; + + /* Make it into a Const */ + value = (Node *) make_const(pstate, &con->val, con->location); + + /* Coerce to correct type */ + value = coerce_to_target_type(pstate, + value, exprType(value), + colType, + colTypmod, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST, + -1); + + if (value == NULL) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("specified value cannot be cast to type %s for column \"%s\"", + format_type_be(colType), colName), + parser_errposition(pstate, con->location))); + + /* Simplify the expression, in case we had a coercion */ + if (!IsA(value, Const)) + value = (Node *) expression_planner((Expr *) value); + + /* Fail if we don't have a constant (i.e., non-immutable coercion) */ + if (!IsA(value, Const)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("specified value cannot be cast to type %s for column \"%s\"", + format_type_be(colType), colName), + errdetail("The cast requires a non-immutable conversion."), + errhint("Try putting the literal value in single quotes."), + parser_errposition(pstate, con->location))); + + return (Const *) value; } diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 9234bc2a971..824d7572faf 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -8652,12 +8652,11 @@ get_rule_expr(Node *node, deparse_context *context, case PARTITION_STRATEGY_LIST: Assert(spec->listdatums != NIL); - appendStringInfoString(buf, "FOR VALUES"); - appendStringInfoString(buf, " IN ("); + appendStringInfoString(buf, "FOR VALUES IN ("); sep = ""; foreach(cell, spec->listdatums) { - Const *val = lfirst(cell); + Const *val = castNode(Const, lfirst(cell)); appendStringInfoString(buf, sep); get_const_expr(val, context, -1); @@ -8673,41 +8672,38 @@ get_rule_expr(Node *node, deparse_context *context, list_length(spec->lowerdatums) == list_length(spec->upperdatums)); - appendStringInfoString(buf, "FOR VALUES"); - appendStringInfoString(buf, " FROM"); - appendStringInfoString(buf, " ("); + appendStringInfoString(buf, "FOR VALUES FROM ("); sep = ""; foreach(cell, spec->lowerdatums) { - PartitionRangeDatum *datum = lfirst(cell); - Const *val; + PartitionRangeDatum *datum = + castNode(PartitionRangeDatum, lfirst(cell)); appendStringInfoString(buf, sep); if (datum->infinite) appendStringInfoString(buf, "UNBOUNDED"); else { - val = (Const *) datum->value; + Const *val = castNode(Const, datum->value); + get_const_expr(val, context, -1); } sep = ", "; } - appendStringInfoString(buf, ")"); - - appendStringInfoString(buf, " TO"); - appendStringInfoString(buf, " ("); + appendStringInfoString(buf, ") TO ("); sep = ""; foreach(cell, spec->upperdatums) { - PartitionRangeDatum *datum = lfirst(cell); - Const *val; + PartitionRangeDatum *datum = + castNode(PartitionRangeDatum, lfirst(cell)); appendStringInfoString(buf, sep); if (datum->infinite) appendStringInfoString(buf, "UNBOUNDED"); else { - val = (Const *) datum->value; + Const *val = castNode(Const, datum->value); + get_const_expr(val, context, -1); } sep = ", "; diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index 1187797fd9e..aa494528364 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -143,6 +143,7 @@ extern void StorePartitionKey(Relation rel, Oid *partopclass, Oid *partcollation); extern void RemovePartitionKeyByRelId(Oid relid); -extern void StorePartitionBound(Relation rel, Relation parent, Node *bound); +extern void StorePartitionBound(Relation rel, Relation parent, + PartitionBoundSpec *bound); #endif /* HEAP_H */ diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h index 25fb0a04401..0a1e4688986 100644 --- a/src/include/catalog/partition.h +++ b/src/include/catalog/partition.h @@ -74,9 +74,11 @@ extern void RelationBuildPartitionDesc(Relation relation); extern bool partition_bounds_equal(PartitionKey key, PartitionBoundInfo p1, PartitionBoundInfo p2); -extern void check_new_partition_bound(char *relname, Relation parent, Node *bound); +extern void check_new_partition_bound(char *relname, Relation parent, + PartitionBoundSpec *spec); extern Oid get_partition_parent(Oid relid); -extern List *get_qual_from_partbound(Relation rel, Relation parent, Node *bound); +extern List *get_qual_from_partbound(Relation rel, Relation parent, + PartitionBoundSpec *spec); extern List *map_partition_varattnos(List *expr, int target_varno, Relation partrel, Relation parent); extern List *RelationGetPartitionQual(Relation rel); diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index f59d719923e..15de9363557 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -406,7 +406,6 @@ typedef enum NodeTag T_AlterPolicyStmt, T_CreateTransformStmt, T_CreateAmStmt, - T_PartitionCmd, T_CreatePublicationStmt, T_AlterPublicationStmt, T_CreateSubscriptionStmt, @@ -468,6 +467,7 @@ typedef enum NodeTag T_PartitionSpec, T_PartitionBoundSpec, T_PartitionRangeDatum, + T_PartitionCmd, /* * TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h) diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 4b8727e9193..8720e713c42 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -755,7 +755,10 @@ typedef struct XmlSerialize /* Partitioning related definitions */ /* - * PartitionElem - a column in the partition key + * PartitionElem - parse-time representation of a single partition key + * + * expr can be either a raw expression tree or a parse-analyzed expression. + * We don't store these on-disk, though. */ typedef struct PartitionElem { @@ -768,7 +771,9 @@ typedef struct PartitionElem } PartitionElem; /* - * PartitionSpec - partition key specification + * PartitionSpec - parse-time representation of a partition key specification + * + * This represents the key space we will be partitioning on. */ typedef struct PartitionSpec { @@ -778,52 +783,55 @@ typedef struct PartitionSpec int location; /* token location, or -1 if unknown */ } PartitionSpec; +/* Internal codes for partitioning strategies */ #define PARTITION_STRATEGY_LIST 'l' #define PARTITION_STRATEGY_RANGE 'r' /* * PartitionBoundSpec - a partition bound specification + * + * This represents the portion of the partition key space assigned to a + * particular partition. These are stored on disk in pg_class.relpartbound. */ typedef struct PartitionBoundSpec { NodeTag type; - char strategy; + char strategy; /* see PARTITION_STRATEGY codes above */ - /* List partition values */ - List *listdatums; + /* Partitioning info for LIST strategy: */ + List *listdatums; /* List of Consts (or A_Consts in raw tree) */ - /* - * Range partition lower and upper bounds; each member of the lists is a - * PartitionRangeDatum (see below). - */ - List *lowerdatums; - List *upperdatums; + /* Partitioning info for RANGE strategy: */ + List *lowerdatums; /* List of PartitionRangeDatums */ + List *upperdatums; /* List of PartitionRangeDatums */ - int location; + int location; /* token location, or -1 if unknown */ } PartitionBoundSpec; /* - * PartitionRangeDatum + * PartitionRangeDatum - can be either a value or UNBOUNDED + * + * "value" is an A_Const in raw grammar output, a Const after analysis */ typedef struct PartitionRangeDatum { NodeTag type; - bool infinite; - Node *value; + bool infinite; /* true if UNBOUNDED */ + Node *value; /* null if UNBOUNDED */ - int location; + int location; /* token location, or -1 if unknown */ } PartitionRangeDatum; /* - * PartitionCmd - ALTER TABLE partition commands + * PartitionCmd - info for ALTER TABLE ATTACH/DETACH PARTITION commands */ typedef struct PartitionCmd { NodeTag type; - RangeVar *name; - Node *bound; + RangeVar *name; /* name of partition to attach/detach */ + PartitionBoundSpec *bound; /* FOR VALUES, if attaching */ } PartitionCmd; /**************************************************************************** @@ -1969,7 +1977,7 @@ typedef struct CreateStmt List *tableElts; /* column definitions (list of ColumnDef) */ List *inhRelations; /* relations to inherit from (list of * inhRelation) */ - Node *partbound; /* FOR VALUES clause */ + PartitionBoundSpec *partbound; /* FOR VALUES clause */ PartitionSpec *partspec; /* PARTITION BY clause */ TypeName *ofTypename; /* OF typename */ List *constraints; /* constraints (list of Constraint nodes) */ diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h index 41ab44e5c7b..8d0d17f8577 100644 --- a/src/include/parser/parse_utilcmd.h +++ b/src/include/parser/parse_utilcmd.h @@ -25,7 +25,7 @@ extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt, extern void transformRuleStmt(RuleStmt *stmt, const char *queryString, List **actions, Node **whereClause); extern List *transformCreateSchemaStmt(CreateSchemaStmt *stmt); -extern Node *transformPartitionBound(ParseState *pstate, Relation parent, - Node *bound); +extern PartitionBoundSpec *transformPartitionBound(ParseState *pstate, Relation parent, + PartitionBoundSpec *spec); #endif /* PARSE_UTILCMD_H */ diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 39edf04cb4e..5136506dff6 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -274,7 +274,7 @@ CREATE TABLE partitioned ( a1 int, a2 int ) PARTITION BY LIST (a1, a2); -- fail -ERROR: cannot list partition using more than one column +ERROR: cannot use "list" partition strategy with more than one column -- unsupported constraint type for partitioned tables CREATE TABLE partitioned ( a int PRIMARY KEY @@ -472,10 +472,31 @@ CREATE TABLE bools ( a bool ) PARTITION BY LIST (a); CREATE TABLE bools_true PARTITION OF bools FOR VALUES IN (1); -ERROR: specified value cannot be cast to type "boolean" of column "a" +ERROR: specified value cannot be cast to type boolean for column "a" LINE 1: ...REATE TABLE bools_true PARTITION OF bools FOR VALUES IN (1); ^ DROP TABLE bools; +-- specified literal can be cast, but cast isn't immutable +CREATE TABLE moneyp ( + a money +) PARTITION BY LIST (a); +CREATE TABLE moneyp_10 PARTITION OF moneyp FOR VALUES IN (10); +ERROR: specified value cannot be cast to type money for column "a" +LINE 1: ...EATE TABLE moneyp_10 PARTITION OF moneyp FOR VALUES IN (10); + ^ +DETAIL: The cast requires a non-immutable conversion. +HINT: Try putting the literal value in single quotes. +CREATE TABLE moneyp_10 PARTITION OF moneyp FOR VALUES IN ('10'); +DROP TABLE moneyp; +-- immutable cast should work, though +CREATE TABLE bigintp ( + a bigint +) PARTITION BY LIST (a); +CREATE TABLE bigintp_10 PARTITION OF bigintp FOR VALUES IN (10); +-- fails due to overlap: +CREATE TABLE bigintp_10_2 PARTITION OF bigintp FOR VALUES IN ('10'); +ERROR: partition "bigintp_10_2" would overlap partition "bigintp_10" +DROP TABLE bigintp; CREATE TABLE range_parted ( a date ) PARTITION BY RANGE (a); diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 5a2774395e5..cb7aa5bbc60 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -454,6 +454,23 @@ CREATE TABLE bools ( CREATE TABLE bools_true PARTITION OF bools FOR VALUES IN (1); DROP TABLE bools; +-- specified literal can be cast, but cast isn't immutable +CREATE TABLE moneyp ( + a money +) PARTITION BY LIST (a); +CREATE TABLE moneyp_10 PARTITION OF moneyp FOR VALUES IN (10); +CREATE TABLE moneyp_10 PARTITION OF moneyp FOR VALUES IN ('10'); +DROP TABLE moneyp; + +-- immutable cast should work, though +CREATE TABLE bigintp ( + a bigint +) PARTITION BY LIST (a); +CREATE TABLE bigintp_10 PARTITION OF bigintp FOR VALUES IN (10); +-- fails due to overlap: +CREATE TABLE bigintp_10_2 PARTITION OF bigintp FOR VALUES IN ('10'); +DROP TABLE bigintp; + CREATE TABLE range_parted ( a date ) PARTITION BY RANGE (a); |