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.
*/
void
StorePartitionBound(Relation rel, Relation parent, Node *bound)
StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
{
Relation classRel;
HeapTuple tuple,

View File

@ -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");

View File

@ -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.
@ -13273,28 +13302,39 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
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")));
}
}

View File

@ -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;

View File

@ -2812,16 +2812,6 @@ _equalTriggerTransition(const TriggerTransition *a, const TriggerTransition *b)
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
_equalPartitionElem(const PartitionElem *a, const PartitionElem *b)
{
@ -2834,6 +2824,16 @@ _equalPartitionElem(const PartitionElem *a, const PartitionElem *b)
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
_equalPartitionBoundSpec(const PartitionBoundSpec *a, const PartitionBoundSpec *b)
{
@ -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;

View File

@ -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;

View File

@ -3515,16 +3515,6 @@ _outForeignKeyCacheInfo(StringInfo str, const ForeignKeyCacheInfo *node)
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
_outPartitionElem(StringInfo str, const PartitionElem *node)
{
@ -3537,6 +3527,16 @@ _outPartitionElem(StringInfo str, const PartitionElem *node)
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
_outPartitionBoundSpec(StringInfo str, const PartitionBoundSpec *node)
{
@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -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;
PartitionRangeDatum *ldatum = (PartitionRangeDatum *) lfirst(cell1);
PartitionRangeDatum *rdatum = (PartitionRangeDatum *) lfirst(cell2);
char *colname;
Oid coltype;
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 */
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;
}

View File

@ -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 = ", ";

View File

@ -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 */

View File

@ -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);

View File

@ -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)

View File

@ -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) */

View File

@ -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 */

View File

@ -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);

View File

@ -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);