Code review focused on new node types added by partitioning support.

Fix failure to check that we got a plain Const from const-simplification of
a coercion request.  This is the cause of bug #14666 from Tian Bing: there
is an int4 to money cast, but it's only stable not immutable (because of
dependence on lc_monetary), resulting in a FuncExpr that the code was
miserably unequipped to deal with, or indeed even to notice that it was
failing to deal with.  Add test cases around this coercion behavior.

In view of the above, sprinkle the code liberally with castNode() macros,
in hope of catching the next such bug a bit sooner.  Also, change some
functions that were randomly declared to take Node* to take more specific
pointer types.  And change some struct fields that were declared Node*
but could be given more specific types, allowing removal of assorted
explicit casts.

Place PARTITION_MAX_KEYS check a bit closer to the code it's protecting.
Likewise check only-one-key-for-list-partitioning restriction in a less
random place.

Avoid not-per-project-style usages like !strcmp(...).

Fix assorted failures to avoid scribbling on the input of parse
transformation.  I'm not sure how necessary this is, but it's entirely
silly for these functions to be expending cycles to avoid that and not
getting it right.

Add guards against partitioning on system columns.

Put backend/nodes/ support code into an order that matches handling
of these node types elsewhere.

Annotate the fact that somebody added location fields to PartitionBoundSpec
and PartitionRangeDatum but forgot to handle them in
outfuncs.c/readfuncs.c.  This is fairly harmless for production purposes
(since readfuncs.c would just substitute -1 anyway) but it's still bogus.
It's not worth forcing a post-beta1 initdb just to fix this, but if we
have another reason to force initdb before 10.0, we should go back and
clean this up.

Contrariwise, somebody added location fields to PartitionElem and
PartitionSpec but forgot to teach exprLocation() about them.

Consolidate duplicative code in transformPartitionBound().

Improve a couple of error messages.

Improve assorted commentary.

Re-pgindent the files touched by this patch; this affects a few comment
blocks that must have been added quite recently.

Report: https://postgr.es/m/20170524024550.29935.14396@wrigleys.postgresql.org
This commit is contained in:
Tom Lane 2017-05-28 23:20:28 -04:00
parent 54bb322ec7
commit 76a3df6e5e
18 changed files with 362 additions and 271 deletions

View File

@ -3219,7 +3219,7 @@ RemovePartitionKeyByRelId(Oid relid)
* the new partition's info into its partition descriptor. * the new partition's info into its partition descriptor.
*/ */
void void
StorePartitionBound(Relation rel, Relation parent, Node *bound) StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
{ {
Relation classRel; Relation classRel;
HeapTuple tuple, HeapTuple tuple,

View File

@ -247,15 +247,16 @@ RelationBuildPartitionDesc(Relation rel)
null_index = -1; null_index = -1;
foreach(cell, boundspecs) foreach(cell, boundspecs)
{ {
PartitionBoundSpec *spec = castNode(PartitionBoundSpec,
lfirst(cell));
ListCell *c; ListCell *c;
PartitionBoundSpec *spec = lfirst(cell);
if (spec->strategy != PARTITION_STRATEGY_LIST) if (spec->strategy != PARTITION_STRATEGY_LIST)
elog(ERROR, "invalid strategy in partition bound spec"); elog(ERROR, "invalid strategy in partition bound spec");
foreach(c, spec->listdatums) foreach(c, spec->listdatums)
{ {
Const *val = lfirst(c); Const *val = castNode(Const, lfirst(c));
PartitionListValue *list_value = NULL; PartitionListValue *list_value = NULL;
if (!val->constisnull) if (!val->constisnull)
@ -327,7 +328,8 @@ RelationBuildPartitionDesc(Relation rel)
i = j = 0; i = j = 0;
foreach(cell, boundspecs) foreach(cell, boundspecs)
{ {
PartitionBoundSpec *spec = lfirst(cell); PartitionBoundSpec *spec = castNode(PartitionBoundSpec,
lfirst(cell));
PartitionRangeBound *lower, PartitionRangeBound *lower,
*upper; *upper;
@ -665,9 +667,9 @@ partition_bounds_equal(PartitionKey key,
* of parent. Also performs additional checks as necessary per strategy. * of parent. Also performs additional checks as necessary per strategy.
*/ */
void 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); PartitionKey key = RelationGetPartitionKey(parent);
PartitionDesc partdesc = RelationGetPartitionDesc(parent); PartitionDesc partdesc = RelationGetPartitionDesc(parent);
ParseState *pstate = make_parsestate(NULL); ParseState *pstate = make_parsestate(NULL);
@ -692,7 +694,7 @@ check_new_partition_bound(char *relname, Relation parent, Node *bound)
foreach(cell, spec->listdatums) foreach(cell, spec->listdatums)
{ {
Const *val = lfirst(cell); Const *val = castNode(Const, lfirst(cell));
if (!val->constisnull) if (!val->constisnull)
{ {
@ -889,9 +891,9 @@ get_partition_parent(Oid relid)
* expressions as partition constraint * expressions as partition constraint
*/ */
List * 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); PartitionKey key = RelationGetPartitionKey(parent);
List *my_qual = NIL; List *my_qual = NIL;
@ -1328,7 +1330,7 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
prev = NULL; prev = NULL;
for (cell = list_head(spec->listdatums); cell; cell = next) for (cell = list_head(spec->listdatums); cell; cell = next)
{ {
Const *val = (Const *) lfirst(cell); Const *val = castNode(Const, lfirst(cell));
next = lnext(cell); next = lnext(cell);
@ -1427,12 +1429,12 @@ get_range_key_properties(PartitionKey key, int keynum,
} }
if (!ldatum->infinite) if (!ldatum->infinite)
*lower_val = (Const *) ldatum->value; *lower_val = castNode(Const, ldatum->value);
else else
*lower_val = NULL; *lower_val = NULL;
if (!udatum->infinite) if (!udatum->infinite)
*upper_val = (Const *) udatum->value; *upper_val = castNode(Const, udatum->value);
else else
*upper_val = NULL; *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 * as the lower bound tuple and (au, bu, cu) as the upper bound tuple, we
* generate an expression tree of the following form: * 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 * AND
* (a > al OR (a = al AND b > bl) OR (a = al AND b = bl AND c >= cl)) * (a > al OR (a = al AND b > bl) OR (a = al AND b = bl AND c >= cl))
* AND * 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 * the same values, for example, (al = au), in which case, we will emit an
* expression tree of the following form: * 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 * AND
* (a = al) * (a = al)
* AND * AND
@ -1512,8 +1514,8 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
num_or_arms = key->partnatts; num_or_arms = key->partnatts;
/* /*
* A range-partitioned table does not currently allow partition keys to * A range-partitioned table does not currently allow partition keys to be
* be null, so emit an IS NOT NULL expression for each key column. * null, so emit an IS NOT NULL expression for each key column.
*/ */
partexprs_item = list_head(key->partexprs); partexprs_item = list_head(key->partexprs);
for (i = 0; i < key->partnatts; i++) for (i = 0; i < key->partnatts; i++)
@ -1565,8 +1567,8 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
Datum test_result; Datum test_result;
bool isNull; bool isNull;
ldatum = lfirst(cell1); ldatum = castNode(PartitionRangeDatum, lfirst(cell1));
udatum = lfirst(cell2); udatum = castNode(PartitionRangeDatum, lfirst(cell2));
/* /*
* Since get_range_key_properties() modifies partexprs_item, and we * 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, PartitionRangeDatum *ldatum_next = NULL,
*udatum_next = NULL; *udatum_next = NULL;
ldatum = lfirst(cell1); ldatum = castNode(PartitionRangeDatum, lfirst(cell1));
if (lnext(cell1)) if (lnext(cell1))
ldatum_next = lfirst(lnext(cell1)); ldatum_next = castNode(PartitionRangeDatum,
udatum = lfirst(cell2); lfirst(lnext(cell1)));
udatum = castNode(PartitionRangeDatum, lfirst(cell2));
if (lnext(cell2)) if (lnext(cell2))
udatum_next = lfirst(lnext(cell2)); udatum_next = castNode(PartitionRangeDatum,
lfirst(lnext(cell2)));
get_range_key_properties(key, j, ldatum, udatum, get_range_key_properties(key, j, ldatum, udatum,
&partexprs_item, &partexprs_item,
&keyCol, &keyCol,
@ -1779,7 +1783,7 @@ generate_partition_qual(Relation rel)
MemoryContext oldcxt; MemoryContext oldcxt;
Datum boundDatum; Datum boundDatum;
bool isnull; bool isnull;
Node *bound; PartitionBoundSpec *bound;
List *my_qual = NIL, List *my_qual = NIL,
*result = NIL; *result = NIL;
Relation parent; Relation parent;
@ -1807,7 +1811,8 @@ generate_partition_qual(Relation rel)
if (isnull) /* should not happen */ if (isnull) /* should not happen */
elog(ERROR, "relation \"%s\" has relpartbound = null", elog(ERROR, "relation \"%s\" has relpartbound = null",
RelationGetRelationName(rel)); RelationGetRelationName(rel));
bound = stringToNode(TextDatumGetCString(boundDatum)); bound = castNode(PartitionBoundSpec,
stringToNode(TextDatumGetCString(boundDatum)));
ReleaseSysCache(tuple); ReleaseSysCache(tuple);
my_qual = get_qual_from_partbound(rel, parent, bound); 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) if (key->strategy == PARTITION_STRATEGY_RANGE)
{ {
/* /*
* Since we cannot route tuples with NULL partition keys through * Since we cannot route tuples with NULL partition keys through a
* a range-partitioned table, simply return that no partition * range-partitioned table, simply return that no partition exists
* exists
*/ */
for (i = 0; i < key->partnatts; i++) 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) make_one_range_bound(PartitionKey key, int index, List *datums, bool lower)
{ {
PartitionRangeBound *bound; PartitionRangeBound *bound;
ListCell *cell; ListCell *lc;
int i; int i;
bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound)); bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
@ -2091,9 +2095,9 @@ make_one_range_bound(PartitionKey key, int index, List *datums, bool lower)
bound->lower = lower; bound->lower = lower;
i = 0; 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? */ /* What's contained in this range datum? */
bound->content[i] = !datum->infinite 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) if (bound->content[i] == RANGE_DATUM_FINITE)
{ {
Const *val = (Const *) datum->value; Const *val = castNode(Const, datum->value);
if (val->constisnull) if (val->constisnull)
elog(ERROR, "invalid range bound datum"); elog(ERROR, "invalid range bound datum");

View File

@ -756,7 +756,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
/* Process and store partition bound, if any. */ /* Process and store partition bound, if any. */
if (stmt->partbound) if (stmt->partbound)
{ {
Node *bound; PartitionBoundSpec *bound;
ParseState *pstate; ParseState *pstate;
Oid parentId = linitial_oid(inheritOids); Oid parentId = linitial_oid(inheritOids);
Relation parent; Relation parent;
@ -777,6 +777,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
/* Tranform the bound values */ /* Tranform the bound values */
pstate = make_parsestate(NULL); pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString; pstate->p_sourcetext = queryString;
bound = transformPartitionBound(pstate, parent, stmt->partbound); bound = transformPartitionBound(pstate, parent, stmt->partbound);
/* /*
@ -812,6 +813,15 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
Oid partcollation[PARTITION_MAX_KEYS]; Oid partcollation[PARTITION_MAX_KEYS];
List *partexprs = NIL; 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 * We need to transform the raw parsetrees corresponding to partition
* expressions into executable expression trees. Like column defaults * 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, stmt->partspec = transformPartitionSpec(rel, stmt->partspec,
&strategy); &strategy);
ComputePartitionAttrs(rel, stmt->partspec->partParams, ComputePartitionAttrs(rel, stmt->partspec->partParams,
partattrs, &partexprs, partopclass, partattrs, &partexprs, partopclass,
partcollation); partcollation);
partnatts = list_length(stmt->partspec->partParams);
StorePartitionKey(rel, strategy, partnatts, partattrs, partexprs, StorePartitionKey(rel, strategy, partnatts, partattrs, partexprs,
partopclass, partcollation); partopclass, partcollation);
} }
@ -13109,6 +13119,8 @@ RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
/* /*
* Transform any expressions present in the partition key * Transform any expressions present in the partition key
*
* Returns a transformed PartitionSpec, as well as the strategy code
*/ */
static PartitionSpec * static PartitionSpec *
transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy) transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
@ -13121,13 +13133,13 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
newspec = makeNode(PartitionSpec); newspec = makeNode(PartitionSpec);
newspec->strategy = partspec->strategy; newspec->strategy = partspec->strategy;
newspec->location = partspec->location;
newspec->partParams = NIL; newspec->partParams = NIL;
newspec->location = partspec->location;
/* Parse partitioning strategy name */ /* Parse partitioning strategy name */
if (!pg_strcasecmp(partspec->strategy, "list")) if (pg_strcasecmp(partspec->strategy, "list") == 0)
*strategy = PARTITION_STRATEGY_LIST; *strategy = PARTITION_STRATEGY_LIST;
else if (!pg_strcasecmp(partspec->strategy, "range")) else if (pg_strcasecmp(partspec->strategy, "range") == 0)
*strategy = PARTITION_STRATEGY_RANGE; *strategy = PARTITION_STRATEGY_RANGE;
else else
ereport(ERROR, ereport(ERROR,
@ -13135,6 +13147,13 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
errmsg("unrecognized partitioning strategy \"%s\"", errmsg("unrecognized partitioning strategy \"%s\"",
partspec->strategy))); 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 * Create a dummy ParseState and insert the target relation as its sole
* rangetable entry. We need a ParseState for transformExpr. * 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 */ /* take care of any partition expressions */
foreach(l, partspec->partParams) foreach(l, partspec->partParams)
{ {
PartitionElem *pelem = castNode(PartitionElem, lfirst(l));
ListCell *lc; ListCell *lc;
PartitionElem *pelem = (PartitionElem *) lfirst(l);
/* Check for PARTITION BY ... (foo, foo) */ /* Check for PARTITION BY ... (foo, foo) */
foreach(lc, newspec->partParams) foreach(lc, newspec->partParams)
{ {
PartitionElem *pparam = (PartitionElem *) lfirst(lc); PartitionElem *pparam = castNode(PartitionElem, lfirst(lc));
if (pelem->name && pparam->name && if (pelem->name && pparam->name &&
!strcmp(pelem->name, pparam->name)) strcmp(pelem->name, pparam->name) == 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN), (errcode(ERRCODE_DUPLICATE_COLUMN),
errmsg("column \"%s\" appears more than once in partition key", errmsg("column \"%s\" appears more than once in partition key",
@ -13165,6 +13184,9 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
if (pelem->expr) if (pelem->expr)
{ {
/* Copy, to avoid scribbling on the input */
pelem = copyObject(pelem);
/* Now do parse transformation of the expression */ /* Now do parse transformation of the expression */
pelem->expr = transformExpr(pstate, pelem->expr, pelem->expr = transformExpr(pstate, pelem->expr,
EXPR_KIND_PARTITION_EXPRESSION); 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 static void
ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs, ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
@ -13192,7 +13215,7 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
attn = 0; attn = 0;
foreach(lc, partParams) foreach(lc, partParams)
{ {
PartitionElem *pelem = (PartitionElem *) lfirst(lc); PartitionElem *pelem = castNode(PartitionElem, lfirst(lc));
Oid atttype; Oid atttype;
Oid attcollation; Oid attcollation;
@ -13202,7 +13225,8 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
HeapTuple atttuple; HeapTuple atttuple;
Form_pg_attribute attform; Form_pg_attribute attform;
atttuple = SearchSysCacheAttName(RelationGetRelid(rel), pelem->name); atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
pelem->name);
if (!HeapTupleIsValid(atttuple)) if (!HeapTupleIsValid(atttuple))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN), (errcode(ERRCODE_UNDEFINED_COLUMN),
@ -13212,7 +13236,7 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
if (attform->attnum <= 0) if (attform->attnum <= 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("cannot use system column \"%s\" in partition key", errmsg("cannot use system column \"%s\" in partition key",
pelem->name))); pelem->name)));
@ -13220,8 +13244,6 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
atttype = attform->atttypid; atttype = attform->atttypid;
attcollation = attform->attcollation; attcollation = attform->attcollation;
ReleaseSysCache(atttuple); ReleaseSysCache(atttuple);
/* Note that whole-row references can't happen here; see below */
} }
else else
{ {
@ -13240,7 +13262,7 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
expr = (Node *) ((CollateExpr *) expr)->arg; expr = (Node *) ((CollateExpr *) expr)->arg;
if (IsA(expr, Var) && if (IsA(expr, Var) &&
((Var *) expr)->varattno != InvalidAttrNumber) ((Var *) expr)->varattno > 0)
{ {
/* /*
* User wrote "(column)" or "(column COLLATE something)". * User wrote "(column)" or "(column COLLATE something)".
@ -13251,11 +13273,18 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
else else
{ {
Bitmapset *expr_attrs = NULL; Bitmapset *expr_attrs = NULL;
int i;
partattrs[attn] = 0; /* marks the column as expression */ partattrs[attn] = 0; /* marks the column as expression */
*partexprs = lappend(*partexprs, expr); *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 * Note that expression_planner does not change the passed in
* expression destructively and we have already saved the * expression destructively and we have already saved the
* expression to be stored into the catalog above. * expression to be stored into the catalog above.
@ -13273,28 +13302,39 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("functions in partition key expression must be marked IMMUTABLE"))); 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 * transformPartitionSpec() should have already rejected
* subqueries, aggregates, window functions, and SRFs, based * subqueries, aggregates, window functions, and SRFs, based
* on the EXPR_KIND_ for partition expressions. * 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); pull_varattnos(expr, 1, &expr_attrs);
if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber,
expr_attrs)) expr_attrs))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("partition key expressions cannot contain whole-row references"))); 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")));
} }
} }

View File

@ -4412,18 +4412,6 @@ _copyAlterPolicyStmt(const AlterPolicyStmt *from)
return newnode; 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 * static PartitionElem *
_copyPartitionElem(const PartitionElem *from) _copyPartitionElem(const PartitionElem *from)
{ {
@ -4438,6 +4426,18 @@ _copyPartitionElem(const PartitionElem *from)
return newnode; 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 * static PartitionBoundSpec *
_copyPartitionBoundSpec(const PartitionBoundSpec *from) _copyPartitionBoundSpec(const PartitionBoundSpec *from)
{ {
@ -5509,12 +5509,12 @@ copyObjectImpl(const void *from)
case T_TriggerTransition: case T_TriggerTransition:
retval = _copyTriggerTransition(from); retval = _copyTriggerTransition(from);
break; break;
case T_PartitionSpec:
retval = _copyPartitionSpec(from);
break;
case T_PartitionElem: case T_PartitionElem:
retval = _copyPartitionElem(from); retval = _copyPartitionElem(from);
break; break;
case T_PartitionSpec:
retval = _copyPartitionSpec(from);
break;
case T_PartitionBoundSpec: case T_PartitionBoundSpec:
retval = _copyPartitionBoundSpec(from); retval = _copyPartitionBoundSpec(from);
break; break;

View File

@ -2812,16 +2812,6 @@ _equalTriggerTransition(const TriggerTransition *a, const TriggerTransition *b)
return true; return true;
} }
static bool
_equalPartitionSpec(const PartitionSpec *a, const PartitionSpec *b)
{
COMPARE_STRING_FIELD(strategy);
COMPARE_NODE_FIELD(partParams);
COMPARE_LOCATION_FIELD(location);
return true;
}
static bool static bool
_equalPartitionElem(const PartitionElem *a, const PartitionElem *b) _equalPartitionElem(const PartitionElem *a, const PartitionElem *b)
{ {
@ -2834,6 +2824,16 @@ _equalPartitionElem(const PartitionElem *a, const PartitionElem *b)
return true; return true;
} }
static bool
_equalPartitionSpec(const PartitionSpec *a, const PartitionSpec *b)
{
COMPARE_STRING_FIELD(strategy);
COMPARE_NODE_FIELD(partParams);
COMPARE_LOCATION_FIELD(location);
return true;
}
static bool static bool
_equalPartitionBoundSpec(const PartitionBoundSpec *a, const PartitionBoundSpec *b) _equalPartitionBoundSpec(const PartitionBoundSpec *a, const PartitionBoundSpec *b)
{ {
@ -3660,12 +3660,12 @@ equal(const void *a, const void *b)
case T_TriggerTransition: case T_TriggerTransition:
retval = _equalTriggerTransition(a, b); retval = _equalTriggerTransition(a, b);
break; break;
case T_PartitionSpec:
retval = _equalPartitionSpec(a, b);
break;
case T_PartitionElem: case T_PartitionElem:
retval = _equalPartitionElem(a, b); retval = _equalPartitionElem(a, b);
break; break;
case T_PartitionSpec:
retval = _equalPartitionSpec(a, b);
break;
case T_PartitionBoundSpec: case T_PartitionBoundSpec:
retval = _equalPartitionBoundSpec(a, b); retval = _equalPartitionBoundSpec(a, b);
break; break;

View File

@ -1560,6 +1560,12 @@ exprLocation(const Node *expr)
/* just use nested expr's location */ /* just use nested expr's location */
loc = exprLocation((Node *) ((const InferenceElem *) expr)->expr); loc = exprLocation((Node *) ((const InferenceElem *) expr)->expr);
break; break;
case T_PartitionElem:
loc = ((const PartitionElem *) expr)->location;
break;
case T_PartitionSpec:
loc = ((const PartitionSpec *) expr)->location;
break;
case T_PartitionBoundSpec: case T_PartitionBoundSpec:
loc = ((const PartitionBoundSpec *) expr)->location; loc = ((const PartitionBoundSpec *) expr)->location;
break; break;

View File

@ -3515,16 +3515,6 @@ _outForeignKeyCacheInfo(StringInfo str, const ForeignKeyCacheInfo *node)
appendStringInfo(str, " %u", node->conpfeqop[i]); appendStringInfo(str, " %u", node->conpfeqop[i]);
} }
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 static void
_outPartitionElem(StringInfo str, const PartitionElem *node) _outPartitionElem(StringInfo str, const PartitionElem *node)
{ {
@ -3537,6 +3527,16 @@ _outPartitionElem(StringInfo str, const PartitionElem *node)
WRITE_LOCATION_FIELD(location); WRITE_LOCATION_FIELD(location);
} }
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 static void
_outPartitionBoundSpec(StringInfo str, const PartitionBoundSpec *node) _outPartitionBoundSpec(StringInfo str, const PartitionBoundSpec *node)
{ {
@ -3546,6 +3546,7 @@ _outPartitionBoundSpec(StringInfo str, const PartitionBoundSpec *node)
WRITE_NODE_FIELD(listdatums); WRITE_NODE_FIELD(listdatums);
WRITE_NODE_FIELD(lowerdatums); WRITE_NODE_FIELD(lowerdatums);
WRITE_NODE_FIELD(upperdatums); WRITE_NODE_FIELD(upperdatums);
/* XXX somebody forgot location field; too late to change for v10 */
} }
static void static void
@ -3555,6 +3556,7 @@ _outPartitionRangeDatum(StringInfo str, const PartitionRangeDatum *node)
WRITE_BOOL_FIELD(infinite); WRITE_BOOL_FIELD(infinite);
WRITE_NODE_FIELD(value); 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: case T_TriggerTransition:
_outTriggerTransition(str, obj); _outTriggerTransition(str, obj);
break; break;
case T_PartitionSpec:
_outPartitionSpec(str, obj);
break;
case T_PartitionElem: case T_PartitionElem:
_outPartitionElem(str, obj); _outPartitionElem(str, obj);
break; break;
case T_PartitionSpec:
_outPartitionSpec(str, obj);
break;
case T_PartitionBoundSpec: case T_PartitionBoundSpec:
_outPartitionBoundSpec(str, obj); _outPartitionBoundSpec(str, obj);
break; break;

View File

@ -2376,6 +2376,8 @@ _readPartitionBoundSpec(void)
READ_NODE_FIELD(listdatums); READ_NODE_FIELD(listdatums);
READ_NODE_FIELD(lowerdatums); READ_NODE_FIELD(lowerdatums);
READ_NODE_FIELD(upperdatums); READ_NODE_FIELD(upperdatums);
/* XXX somebody forgot location field; too late to change for v10 */
local_node->location = -1;
READ_DONE(); READ_DONE();
} }
@ -2390,6 +2392,8 @@ _readPartitionRangeDatum(void)
READ_BOOL_FIELD(infinite); READ_BOOL_FIELD(infinite);
READ_NODE_FIELD(value); READ_NODE_FIELD(value);
/* XXX somebody forgot location field; too late to change for v10 */
local_node->location = -1;
READ_DONE(); READ_DONE();
} }

View File

@ -239,7 +239,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
VariableSetStmt *vsetstmt; VariableSetStmt *vsetstmt;
PartitionElem *partelem; PartitionElem *partelem;
PartitionSpec *partspec; PartitionSpec *partspec;
PartitionRangeDatum *partrange_datum; PartitionBoundSpec *partboundspec;
RoleSpec *rolespec; RoleSpec *rolespec;
} }
@ -575,11 +575,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> part_strategy %type <str> part_strategy
%type <partelem> part_elem %type <partelem> part_elem
%type <list> part_params %type <list> part_params
%type <node> ForValues %type <partboundspec> ForValues
%type <node> partbound_datum %type <node> partbound_datum PartitionRangeDatum
%type <list> partbound_datum_list %type <list> partbound_datum_list range_datum_list
%type <partrange_datum> PartitionRangeDatum
%type <list> range_datum_list
/* /*
* Non-keyword token types. These are hard-wired into the "flex" lexer. * Non-keyword token types. These are hard-wired into the "flex" lexer.
@ -2020,7 +2018,7 @@ partition_cmd:
n->subtype = AT_AttachPartition; n->subtype = AT_AttachPartition;
cmd->name = $3; cmd->name = $3;
cmd->bound = (Node *) $4; cmd->bound = $4;
n->def = (Node *) cmd; n->def = (Node *) cmd;
$$ = (Node *) n; $$ = (Node *) n;
@ -2033,6 +2031,7 @@ partition_cmd:
n->subtype = AT_DetachPartition; n->subtype = AT_DetachPartition;
cmd->name = $3; cmd->name = $3;
cmd->bound = NULL;
n->def = (Node *) cmd; n->def = (Node *) cmd;
$$ = (Node *) n; $$ = (Node *) n;
@ -2661,7 +2660,7 @@ ForValues:
n->listdatums = $5; n->listdatums = $5;
n->location = @3; n->location = @3;
$$ = (Node *) n; $$ = n;
} }
/* a RANGE partition */ /* a RANGE partition */
@ -2674,7 +2673,7 @@ ForValues:
n->upperdatums = $9; n->upperdatums = $9;
n->location = @3; n->location = @3;
$$ = (Node *) n; $$ = n;
} }
; ;
@ -2705,7 +2704,7 @@ PartitionRangeDatum:
n->value = NULL; n->value = NULL;
n->location = @1; n->location = @1;
$$ = n; $$ = (Node *) n;
} }
| partbound_datum | partbound_datum
{ {
@ -2715,7 +2714,7 @@ PartitionRangeDatum:
n->value = $1; n->value = $1;
n->location = @1; n->location = @1;
$$ = n; $$ = (Node *) n;
} }
; ;
@ -3144,7 +3143,7 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
n->relation = $4; n->relation = $4;
n->tableElts = $8; n->tableElts = $8;
n->inhRelations = list_make1($7); n->inhRelations = list_make1($7);
n->partbound = (Node *) $9; n->partbound = $9;
n->partspec = $10; n->partspec = $10;
n->ofTypename = NULL; n->ofTypename = NULL;
n->constraints = NIL; n->constraints = NIL;
@ -3163,7 +3162,7 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
n->relation = $7; n->relation = $7;
n->tableElts = $11; n->tableElts = $11;
n->inhRelations = list_make1($10); n->inhRelations = list_make1($10);
n->partbound = (Node *) $12; n->partbound = $12;
n->partspec = $13; n->partspec = $13;
n->ofTypename = NULL; n->ofTypename = NULL;
n->constraints = NIL; n->constraints = NIL;
@ -4866,7 +4865,7 @@ CreateForeignTableStmt:
n->base.relation = $4; n->base.relation = $4;
n->base.inhRelations = list_make1($7); n->base.inhRelations = list_make1($7);
n->base.tableElts = $8; n->base.tableElts = $8;
n->base.partbound = (Node *) $9; n->base.partbound = $9;
n->base.ofTypename = NULL; n->base.ofTypename = NULL;
n->base.constraints = NIL; n->base.constraints = NIL;
n->base.options = NIL; n->base.options = NIL;
@ -4887,7 +4886,7 @@ CreateForeignTableStmt:
n->base.relation = $7; n->base.relation = $7;
n->base.inhRelations = list_make1($10); n->base.inhRelations = list_make1($10);
n->base.tableElts = $11; n->base.tableElts = $11;
n->base.partbound = (Node *) $12; n->base.partbound = $12;
n->base.ofTypename = NULL; n->base.ofTypename = NULL;
n->base.constraints = NIL; n->base.constraints = NIL;
n->base.options = NIL; n->base.options = NIL;

View File

@ -91,7 +91,7 @@ typedef struct
* the table */ * the table */
IndexStmt *pkey; /* PRIMARY KEY index, if any */ IndexStmt *pkey; /* PRIMARY KEY index, if any */
bool ispartitioned; /* true if table is partitioned */ bool ispartitioned; /* true if table is partitioned */
Node *partbound; /* transformed FOR VALUES */ PartitionBoundSpec *partbound; /* transformed FOR VALUES */
} CreateStmtContext; } CreateStmtContext;
/* State shared by transformCreateSchemaStmt and its subroutines */ /* 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 transformColumnType(CreateStmtContext *cxt, ColumnDef *column);
static void setSchemaName(char *context_schema, char **stmt_schema_name); static void setSchemaName(char *context_schema, char **stmt_schema_name);
static void transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd); 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) if (stmt->partspec)
{ {
int partnatts = list_length(stmt->partspec->partParams);
if (stmt->inhRelations && !stmt->partbound) if (stmt->inhRelations && !stmt->partbound)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("cannot create partitioned table as inheritance child"))); 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 * transformPartitionBound
* *
* Transform partition bound specification * Transform a partition bound specification
*/ */
Node * PartitionBoundSpec *
transformPartitionBound(ParseState *pstate, Relation parent, Node *bound) transformPartitionBound(ParseState *pstate, Relation parent,
PartitionBoundSpec *spec)
{ {
PartitionBoundSpec *spec = (PartitionBoundSpec *) bound, PartitionBoundSpec *result_spec;
*result_spec;
PartitionKey key = RelationGetPartitionKey(parent); PartitionKey key = RelationGetPartitionKey(parent);
char strategy = get_partition_strategy(key); char strategy = get_partition_strategy(key);
int partnatts = get_partition_natts(key); int partnatts = get_partition_natts(key);
List *partexprs = get_partition_exprs(key); List *partexprs = get_partition_exprs(key);
/* Avoid scribbling on input */
result_spec = copyObject(spec); result_spec = copyObject(spec);
if (strategy == PARTITION_STRATEGY_LIST) if (strategy == PARTITION_STRATEGY_LIST)
{ {
ListCell *cell; ListCell *cell;
char *colname; 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 */ /* Get the only column's name in case we need to output an error */
if (key->partattrs[0] != 0) if (key->partattrs[0] != 0)
@ -3308,47 +3305,26 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
deparse_context_for(RelationGetRelationName(parent), deparse_context_for(RelationGetRelationName(parent),
RelationGetRelid(parent)), RelationGetRelid(parent)),
false, false); false, false);
/* Need its type data too */
if (spec->strategy != PARTITION_STRATEGY_LIST) coltype = get_partition_col_typid(key, 0);
ereport(ERROR, coltypmod = get_partition_col_typmod(key, 0);
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("invalid bound specification for a list partition"),
parser_errposition(pstate, exprLocation(bound))));
result_spec->listdatums = NIL; result_spec->listdatums = NIL;
foreach(cell, spec->listdatums) foreach(cell, spec->listdatums)
{ {
A_Const *con = (A_Const *) lfirst(cell); A_Const *con = castNode(A_Const, lfirst(cell));
Node *value; Const *value;
ListCell *cell2; ListCell *cell2;
bool duplicate; bool duplicate;
value = (Node *) make_const(pstate, &con->val, con->location); value = transformPartitionBoundValue(pstate, con,
value = coerce_to_target_type(pstate, colname, coltype, coltypmod);
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);
/* Don't add to the result if the value is a duplicate */ /* Don't add to the result if the value is a duplicate */
duplicate = false; duplicate = false;
foreach(cell2, result_spec->listdatums) foreach(cell2, result_spec->listdatums)
{ {
Const *value2 = (Const *) lfirst(cell2); Const *value2 = castNode(Const, lfirst(cell2));
if (equal(value, value2)) if (equal(value, value2))
{ {
@ -3369,16 +3345,13 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
*cell2; *cell2;
int i, int i,
j; j;
char *colname;
bool seen_unbounded; bool seen_unbounded;
if (spec->strategy != PARTITION_STRATEGY_RANGE) if (spec->strategy != PARTITION_STRATEGY_RANGE)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION), (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("invalid bound specification for a range partition"), errmsg("invalid bound specification for a range partition"),
parser_errposition(pstate, exprLocation(bound)))); parser_errposition(pstate, exprLocation((Node *) spec))));
Assert(spec->lowerdatums != NIL && spec->upperdatums != NIL);
if (list_length(spec->lowerdatums) != partnatts) if (list_length(spec->lowerdatums) != partnatts)
ereport(ERROR, ereport(ERROR,
@ -3390,15 +3363,15 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
errmsg("TO must specify exactly one value per partitioning column"))); 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. * lower and upper bound lists.
*/ */
seen_unbounded = false; seen_unbounded = false;
foreach(cell1, spec->lowerdatums) foreach(cell1, spec->lowerdatums)
{ {
PartitionRangeDatum *ldatum; PartitionRangeDatum *ldatum = castNode(PartitionRangeDatum,
lfirst(cell1));
ldatum = (PartitionRangeDatum *) lfirst(cell1);
if (ldatum->infinite) if (ldatum->infinite)
seen_unbounded = true; seen_unbounded = true;
else if (seen_unbounded) else if (seen_unbounded)
@ -3410,9 +3383,9 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
seen_unbounded = false; seen_unbounded = false;
foreach(cell1, spec->upperdatums) foreach(cell1, spec->upperdatums)
{ {
PartitionRangeDatum *rdatum; PartitionRangeDatum *rdatum = castNode(PartitionRangeDatum,
lfirst(cell1));
rdatum = (PartitionRangeDatum *) lfirst(cell1);
if (rdatum->infinite) if (rdatum->infinite)
seen_unbounded = true; seen_unbounded = true;
else if (seen_unbounded) else if (seen_unbounded)
@ -3422,18 +3395,19 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
parser_errposition(pstate, exprLocation((Node *) rdatum)))); parser_errposition(pstate, exprLocation((Node *) rdatum))));
} }
/* Transform all the constants */
i = j = 0; i = j = 0;
result_spec->lowerdatums = result_spec->upperdatums = NIL; result_spec->lowerdatums = result_spec->upperdatums = NIL;
forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums) forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums)
{ {
PartitionRangeDatum *ldatum, PartitionRangeDatum *ldatum = (PartitionRangeDatum *) lfirst(cell1);
*rdatum; PartitionRangeDatum *rdatum = (PartitionRangeDatum *) lfirst(cell2);
Node *value; char *colname;
A_Const *lcon = NULL, Oid coltype;
*rcon = NULL; int32 coltypmod;
A_Const *con;
Const *value;
ldatum = (PartitionRangeDatum *) lfirst(cell1);
rdatum = (PartitionRangeDatum *) lfirst(cell2);
/* Get the column's name in case we need to output an error */ /* Get the column's name in case we need to output an error */
if (key->partattrs[i] != 0) if (key->partattrs[i] != 0)
colname = get_relid_attribute_name(RelationGetRelid(parent), colname = get_relid_attribute_name(RelationGetRelid(parent),
@ -3446,70 +3420,42 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
false, false); false, false);
++j; ++j;
} }
/* Need its type data too */
coltype = get_partition_col_typid(key, i);
coltypmod = get_partition_col_typmod(key, i);
if (!ldatum->infinite) if (ldatum->value)
lcon = (A_Const *) ldatum->value;
if (!rdatum->infinite)
rcon = (A_Const *) rdatum->value;
if (lcon)
{ {
value = (Node *) make_const(pstate, &lcon->val, lcon->location); con = castNode(A_Const, ldatum->value);
if (((Const *) value)->constisnull) value = transformPartitionBoundValue(pstate, con,
colname,
coltype, coltypmod);
if (value->constisnull)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("cannot specify NULL in range bound"))); errmsg("cannot specify NULL in range bound")));
value = coerce_to_target_type(pstate, ldatum = copyObject(ldatum); /* don't scribble on input */
value, exprType(value), ldatum->value = (Node *) 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;
} }
if (rcon) if (rdatum->value)
{ {
value = (Node *) make_const(pstate, &rcon->val, rcon->location); con = castNode(A_Const, rdatum->value);
if (((Const *) value)->constisnull) value = transformPartitionBoundValue(pstate, con,
colname,
coltype, coltypmod);
if (value->constisnull)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("cannot specify NULL in range bound"))); errmsg("cannot specify NULL in range bound")));
value = coerce_to_target_type(pstate, rdatum = copyObject(rdatum); /* don't scribble on input */
value, exprType(value), rdatum->value = (Node *) 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;
} }
result_spec->lowerdatums = lappend(result_spec->lowerdatums, result_spec->lowerdatums = lappend(result_spec->lowerdatums,
copyObject(ldatum)); ldatum);
result_spec->upperdatums = lappend(result_spec->upperdatums, result_spec->upperdatums = lappend(result_spec->upperdatums,
copyObject(rdatum)); rdatum);
++i; ++i;
} }
@ -3517,5 +3463,50 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
else else
elog(ERROR, "unexpected partition strategy: %d", (int) strategy); 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;
} }

View File

@ -8652,12 +8652,11 @@ get_rule_expr(Node *node, deparse_context *context,
case PARTITION_STRATEGY_LIST: case PARTITION_STRATEGY_LIST:
Assert(spec->listdatums != NIL); Assert(spec->listdatums != NIL);
appendStringInfoString(buf, "FOR VALUES"); appendStringInfoString(buf, "FOR VALUES IN (");
appendStringInfoString(buf, " IN (");
sep = ""; sep = "";
foreach(cell, spec->listdatums) foreach(cell, spec->listdatums)
{ {
Const *val = lfirst(cell); Const *val = castNode(Const, lfirst(cell));
appendStringInfoString(buf, sep); appendStringInfoString(buf, sep);
get_const_expr(val, context, -1); 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->lowerdatums) ==
list_length(spec->upperdatums)); list_length(spec->upperdatums));
appendStringInfoString(buf, "FOR VALUES"); appendStringInfoString(buf, "FOR VALUES FROM (");
appendStringInfoString(buf, " FROM");
appendStringInfoString(buf, " (");
sep = ""; sep = "";
foreach(cell, spec->lowerdatums) foreach(cell, spec->lowerdatums)
{ {
PartitionRangeDatum *datum = lfirst(cell); PartitionRangeDatum *datum =
Const *val; castNode(PartitionRangeDatum, lfirst(cell));
appendStringInfoString(buf, sep); appendStringInfoString(buf, sep);
if (datum->infinite) if (datum->infinite)
appendStringInfoString(buf, "UNBOUNDED"); appendStringInfoString(buf, "UNBOUNDED");
else else
{ {
val = (Const *) datum->value; Const *val = castNode(Const, datum->value);
get_const_expr(val, context, -1); get_const_expr(val, context, -1);
} }
sep = ", "; sep = ", ";
} }
appendStringInfoString(buf, ")"); appendStringInfoString(buf, ") TO (");
appendStringInfoString(buf, " TO");
appendStringInfoString(buf, " (");
sep = ""; sep = "";
foreach(cell, spec->upperdatums) foreach(cell, spec->upperdatums)
{ {
PartitionRangeDatum *datum = lfirst(cell); PartitionRangeDatum *datum =
Const *val; castNode(PartitionRangeDatum, lfirst(cell));
appendStringInfoString(buf, sep); appendStringInfoString(buf, sep);
if (datum->infinite) if (datum->infinite)
appendStringInfoString(buf, "UNBOUNDED"); appendStringInfoString(buf, "UNBOUNDED");
else else
{ {
val = (Const *) datum->value; Const *val = castNode(Const, datum->value);
get_const_expr(val, context, -1); get_const_expr(val, context, -1);
} }
sep = ", "; sep = ", ";

View File

@ -143,6 +143,7 @@ extern void StorePartitionKey(Relation rel,
Oid *partopclass, Oid *partopclass,
Oid *partcollation); Oid *partcollation);
extern void RemovePartitionKeyByRelId(Oid relid); 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 */ #endif /* HEAP_H */

View File

@ -74,9 +74,11 @@ extern void RelationBuildPartitionDesc(Relation relation);
extern bool partition_bounds_equal(PartitionKey key, extern bool partition_bounds_equal(PartitionKey key,
PartitionBoundInfo p1, PartitionBoundInfo p2); 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 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, extern List *map_partition_varattnos(List *expr, int target_varno,
Relation partrel, Relation parent); Relation partrel, Relation parent);
extern List *RelationGetPartitionQual(Relation rel); extern List *RelationGetPartitionQual(Relation rel);

View File

@ -406,7 +406,6 @@ typedef enum NodeTag
T_AlterPolicyStmt, T_AlterPolicyStmt,
T_CreateTransformStmt, T_CreateTransformStmt,
T_CreateAmStmt, T_CreateAmStmt,
T_PartitionCmd,
T_CreatePublicationStmt, T_CreatePublicationStmt,
T_AlterPublicationStmt, T_AlterPublicationStmt,
T_CreateSubscriptionStmt, T_CreateSubscriptionStmt,
@ -468,6 +467,7 @@ typedef enum NodeTag
T_PartitionSpec, T_PartitionSpec,
T_PartitionBoundSpec, T_PartitionBoundSpec,
T_PartitionRangeDatum, T_PartitionRangeDatum,
T_PartitionCmd,
/* /*
* TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h) * TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)

View File

@ -755,7 +755,10 @@ typedef struct XmlSerialize
/* Partitioning related definitions */ /* 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 typedef struct PartitionElem
{ {
@ -768,7 +771,9 @@ typedef struct PartitionElem
} 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 typedef struct PartitionSpec
{ {
@ -778,52 +783,55 @@ typedef struct PartitionSpec
int location; /* token location, or -1 if unknown */ int location; /* token location, or -1 if unknown */
} PartitionSpec; } PartitionSpec;
/* Internal codes for partitioning strategies */
#define PARTITION_STRATEGY_LIST 'l' #define PARTITION_STRATEGY_LIST 'l'
#define PARTITION_STRATEGY_RANGE 'r' #define PARTITION_STRATEGY_RANGE 'r'
/* /*
* PartitionBoundSpec - a partition bound specification * 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 typedef struct PartitionBoundSpec
{ {
NodeTag type; NodeTag type;
char strategy; char strategy; /* see PARTITION_STRATEGY codes above */
/* List partition values */ /* Partitioning info for LIST strategy: */
List *listdatums; List *listdatums; /* List of Consts (or A_Consts in raw tree) */
/* /* Partitioning info for RANGE strategy: */
* Range partition lower and upper bounds; each member of the lists is a List *lowerdatums; /* List of PartitionRangeDatums */
* PartitionRangeDatum (see below). List *upperdatums; /* List of PartitionRangeDatums */
*/
List *lowerdatums;
List *upperdatums;
int location; int location; /* token location, or -1 if unknown */
} PartitionBoundSpec; } 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 typedef struct PartitionRangeDatum
{ {
NodeTag type; NodeTag type;
bool infinite; bool infinite; /* true if UNBOUNDED */
Node *value; Node *value; /* null if UNBOUNDED */
int location; int location; /* token location, or -1 if unknown */
} PartitionRangeDatum; } PartitionRangeDatum;
/* /*
* PartitionCmd - ALTER TABLE partition commands * PartitionCmd - info for ALTER TABLE ATTACH/DETACH PARTITION commands
*/ */
typedef struct PartitionCmd typedef struct PartitionCmd
{ {
NodeTag type; NodeTag type;
RangeVar *name; RangeVar *name; /* name of partition to attach/detach */
Node *bound; PartitionBoundSpec *bound; /* FOR VALUES, if attaching */
} PartitionCmd; } PartitionCmd;
/**************************************************************************** /****************************************************************************
@ -1969,7 +1977,7 @@ typedef struct CreateStmt
List *tableElts; /* column definitions (list of ColumnDef) */ List *tableElts; /* column definitions (list of ColumnDef) */
List *inhRelations; /* relations to inherit from (list of List *inhRelations; /* relations to inherit from (list of
* inhRelation) */ * inhRelation) */
Node *partbound; /* FOR VALUES clause */ PartitionBoundSpec *partbound; /* FOR VALUES clause */
PartitionSpec *partspec; /* PARTITION BY clause */ PartitionSpec *partspec; /* PARTITION BY clause */
TypeName *ofTypename; /* OF typename */ TypeName *ofTypename; /* OF typename */
List *constraints; /* constraints (list of Constraint nodes) */ List *constraints; /* constraints (list of Constraint nodes) */

View File

@ -25,7 +25,7 @@ extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString, extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause); List **actions, Node **whereClause);
extern List *transformCreateSchemaStmt(CreateSchemaStmt *stmt); extern List *transformCreateSchemaStmt(CreateSchemaStmt *stmt);
extern Node *transformPartitionBound(ParseState *pstate, Relation parent, extern PartitionBoundSpec *transformPartitionBound(ParseState *pstate, Relation parent,
Node *bound); PartitionBoundSpec *spec);
#endif /* PARSE_UTILCMD_H */ #endif /* PARSE_UTILCMD_H */

View File

@ -274,7 +274,7 @@ CREATE TABLE partitioned (
a1 int, a1 int,
a2 int a2 int
) PARTITION BY LIST (a1, a2); -- fail ) 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 -- unsupported constraint type for partitioned tables
CREATE TABLE partitioned ( CREATE TABLE partitioned (
a int PRIMARY KEY a int PRIMARY KEY
@ -472,10 +472,31 @@ CREATE TABLE bools (
a bool a bool
) PARTITION BY LIST (a); ) PARTITION BY LIST (a);
CREATE TABLE bools_true PARTITION OF bools FOR VALUES IN (1); 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); LINE 1: ...REATE TABLE bools_true PARTITION OF bools FOR VALUES IN (1);
^ ^
DROP TABLE bools; 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 ( CREATE TABLE range_parted (
a date a date
) PARTITION BY RANGE (a); ) PARTITION BY RANGE (a);

View File

@ -454,6 +454,23 @@ CREATE TABLE bools (
CREATE TABLE bools_true PARTITION OF bools FOR VALUES IN (1); CREATE TABLE bools_true PARTITION OF bools FOR VALUES IN (1);
DROP TABLE bools; 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 ( CREATE TABLE range_parted (
a date a date
) PARTITION BY RANGE (a); ) PARTITION BY RANGE (a);