]> granicus.if.org Git - postgresql/commitdiff
Code review focused on new node types added by partitioning support.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 29 May 2017 03:20:28 +0000 (23:20 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 29 May 2017 03:20:28 +0000 (23:20 -0400)
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

18 files changed:
src/backend/catalog/heap.c
src/backend/catalog/partition.c
src/backend/commands/tablecmds.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/nodeFuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/parser/gram.y
src/backend/parser/parse_utilcmd.c
src/backend/utils/adt/ruleutils.c
src/include/catalog/heap.h
src/include/catalog/partition.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/parser/parse_utilcmd.h
src/test/regress/expected/create_table.out
src/test/regress/sql/create_table.sql

index fa926048e1102d3c658885090daf7834c4abce72..0ce94f346f56d856937ba629ba70d215d7b24680 100644 (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,
index 7f2fd58462daa4ec4fdb55d0bb02251b70bdaa9c..097f191f08017ab7d6898070980f8cb0b2fd372f 100644 (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");
index fb961e46c4ac90707e892e74c0fb14f53edc753d..7959120f53eb3a17210a55a124f2f301982f3eee 100644 (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")));
                        }
                }
 
index 7811ad5d526b698cda749036c729adf672ede366..36bf1dc92bbe3c90de58741c1a1d9c6c69d3c884 100644 (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;
index c9a8c34892755561471a65e2176929eb5e2c1f28..5bcf0317dc8fb01000634d2dbf9e56f669df3b9d 100644 (file)
@@ -2813,22 +2813,22 @@ _equalTriggerTransition(const TriggerTransition *a, const TriggerTransition *b)
 }
 
 static bool
-_equalPartitionSpec(const PartitionSpec *a, const PartitionSpec *b)
+_equalPartitionElem(const PartitionElem *a, const PartitionElem *b)
 {
-       COMPARE_STRING_FIELD(strategy);
-       COMPARE_NODE_FIELD(partParams);
+       COMPARE_STRING_FIELD(name);
+       COMPARE_NODE_FIELD(expr);
+       COMPARE_NODE_FIELD(collation);
+       COMPARE_NODE_FIELD(opclass);
        COMPARE_LOCATION_FIELD(location);
 
        return true;
 }
 
 static bool
-_equalPartitionElem(const PartitionElem *a, const PartitionElem *b)
+_equalPartitionSpec(const PartitionSpec *a, const PartitionSpec *b)
 {
-       COMPARE_STRING_FIELD(name);
-       COMPARE_NODE_FIELD(expr);
-       COMPARE_NODE_FIELD(collation);
-       COMPARE_NODE_FIELD(opclass);
+       COMPARE_STRING_FIELD(strategy);
+       COMPARE_NODE_FIELD(partParams);
        COMPARE_LOCATION_FIELD(location);
 
        return true;
@@ -3660,12 +3660,12 @@ equal(const void *a, const void *b)
                case T_TriggerTransition:
                        retval = _equalTriggerTransition(a, b);
                        break;
-               case T_PartitionSpec:
-                       retval = _equalPartitionSpec(a, b);
-                       break;
                case T_PartitionElem:
                        retval = _equalPartitionElem(a, b);
                        break;
+               case T_PartitionSpec:
+                       retval = _equalPartitionSpec(a, b);
+                       break;
                case T_PartitionBoundSpec:
                        retval = _equalPartitionBoundSpec(a, b);
                        break;
index 95c1d3efbb57619a02bcc93e01dce1ce6fb18fd8..41f3408cfcf9bf2de4baaf51558fe70bd9fc89e4 100644 (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;
index 4949d58864d5b5f7fa1f600dc3078b313ed94cb6..9189c8d43f8716eac6398b79f1976cf34e3195c5 100644 (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;
index e24f5d672637f3698b545df0d58a15bfba840654..b59ebd63ecb379bd16bb0b10bbb5b8ffd214c6a1 100644 (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();
 }
index 28223311e6168f5d6dd1e756fb550abc048b8704..ff5b1becf2d139e386c2b3a8ddc76d93a05c5e70 100644 (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;
index beb099569bae989956be7e69877b718f1b27a3c3..9134fb9d63c1c5f06f2ff097e9c573889c44e690 100644 (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 partition bound specification
  */
-Node *
-transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
+PartitionBoundSpec *
+transformPartitionBound(ParseState *pstate, Relation parent,
+                                               PartitionBoundSpec *spec)
 {
-       PartitionBoundSpec *spec = (PartitionBoundSpec *) bound,
-                          *result_spec;
+       PartitionBoundSpec *result_spec;
        PartitionKey key = RelationGetPartitionKey(parent);
        char            strategy = get_partition_strategy(key);
        int                     partnatts = get_partition_natts(key);
        List       *partexprs = get_partition_exprs(key);
 
+       /* Avoid scribbling on input */
        result_spec = copyObject(spec);
 
        if (strategy == PARTITION_STRATEGY_LIST)
        {
                ListCell   *cell;
                char       *colname;
+               Oid                     coltype;
+               int32           coltypmod;
+
+               if (spec->strategy != PARTITION_STRATEGY_LIST)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                                 errmsg("invalid bound specification for a list partition"),
+                                  parser_errposition(pstate, exprLocation((Node *) spec))));
 
                /* Get the only column's name in case we need to output an error */
                if (key->partattrs[0] != 0)
@@ -3308,47 +3305,26 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
                                                 deparse_context_for(RelationGetRelationName(parent),
                                                                                         RelationGetRelid(parent)),
                                                                                 false, false);
-
-               if (spec->strategy != PARTITION_STRATEGY_LIST)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
-                                 errmsg("invalid bound specification for a list partition"),
-                                        parser_errposition(pstate, exprLocation(bound))));
+               /* Need its type data too */
+               coltype = get_partition_col_typid(key, 0);
+               coltypmod = get_partition_col_typmod(key, 0);
 
                result_spec->listdatums = NIL;
                foreach(cell, spec->listdatums)
                {
-                       A_Const    *con = (A_Const *) lfirst(cell);
-                       Node       *value;
+                       A_Const    *con = castNode(A_Const, lfirst(cell));
+                       Const      *value;
                        ListCell   *cell2;
                        bool            duplicate;
 
-                       value = (Node *) make_const(pstate, &con->val, con->location);
-                       value = coerce_to_target_type(pstate,
-                                                                                 value, exprType(value),
-                                                                                 get_partition_col_typid(key, 0),
-                                                                                 get_partition_col_typmod(key, 0),
-                                                                                 COERCION_ASSIGNMENT,
-                                                                                 COERCE_IMPLICIT_CAST,
-                                                                                 -1);
-
-                       if (value == NULL)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                                errmsg("specified value cannot be cast to type \"%s\" of column \"%s\"",
-                                                        format_type_be(get_partition_col_typid(key, 0)),
-                                                               colname),
-                                                parser_errposition(pstate,
-                                                                                       exprLocation((Node *) con))));
-
-                       /* Simplify the expression */
-                       value = (Node *) expression_planner((Expr *) value);
+                       value = transformPartitionBoundValue(pstate, con,
+                                                                                                colname, coltype, coltypmod);
 
                        /* Don't add to the result if the value is a duplicate */
                        duplicate = false;
                        foreach(cell2, result_spec->listdatums)
                        {
-                               Const      *value2 = (Const *) lfirst(cell2);
+                               Const      *value2 = castNode(Const, lfirst(cell2));
 
                                if (equal(value, value2))
                                {
@@ -3369,16 +3345,13 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
                                   *cell2;
                int                     i,
                                        j;
-               char       *colname;
                bool            seen_unbounded;
 
                if (spec->strategy != PARTITION_STRATEGY_RANGE)
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                                 errmsg("invalid bound specification for a range partition"),
-                                        parser_errposition(pstate, exprLocation(bound))));
-
-               Assert(spec->lowerdatums != NIL && spec->upperdatums != NIL);
+                                  parser_errposition(pstate, exprLocation((Node *) spec))));
 
                if (list_length(spec->lowerdatums) != partnatts)
                        ereport(ERROR,
@@ -3390,15 +3363,15 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
                                         errmsg("TO must specify exactly one value per partitioning column")));
 
                /*
-                * Check that no finite value follows a UNBOUNDED literal in either of
+                * Check that no finite value follows an UNBOUNDED item in either of
                 * lower and upper bound lists.
                 */
                seen_unbounded = false;
                foreach(cell1, spec->lowerdatums)
                {
-                       PartitionRangeDatum *ldatum;
+                       PartitionRangeDatum *ldatum = castNode(PartitionRangeDatum,
+                                                                                                  lfirst(cell1));
 
-                       ldatum = (PartitionRangeDatum *) lfirst(cell1);
                        if (ldatum->infinite)
                                seen_unbounded = true;
                        else if (seen_unbounded)
@@ -3410,9 +3383,9 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
                seen_unbounded = false;
                foreach(cell1, spec->upperdatums)
                {
-                       PartitionRangeDatum *rdatum;
+                       PartitionRangeDatum *rdatum = castNode(PartitionRangeDatum,
+                                                                                                  lfirst(cell1));
 
-                       rdatum = (PartitionRangeDatum *) lfirst(cell1);
                        if (rdatum->infinite)
                                seen_unbounded = true;
                        else if (seen_unbounded)
@@ -3422,18 +3395,19 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
                                 parser_errposition(pstate, exprLocation((Node *) rdatum))));
                }
 
+               /* Transform all the constants */
                i = j = 0;
                result_spec->lowerdatums = result_spec->upperdatums = NIL;
                forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums)
                {
-                       PartitionRangeDatum *ldatum,
-                                          *rdatum;
-                       Node       *value;
-                       A_Const    *lcon = NULL,
-                                          *rcon = NULL;
-
-                       ldatum = (PartitionRangeDatum *) lfirst(cell1);
-                       rdatum = (PartitionRangeDatum *) lfirst(cell2);
+                       PartitionRangeDatum *ldatum = (PartitionRangeDatum *) lfirst(cell1);
+                       PartitionRangeDatum *rdatum = (PartitionRangeDatum *) lfirst(cell2);
+                       char       *colname;
+                       Oid                     coltype;
+                       int32           coltypmod;
+                       A_Const    *con;
+                       Const      *value;
+
                        /* Get the column's name in case we need to output an error */
                        if (key->partattrs[i] != 0)
                                colname = get_relid_attribute_name(RelationGetRelid(parent),
@@ -3446,70 +3420,42 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
                                                                                         false, false);
                                ++j;
                        }
+                       /* Need its type data too */
+                       coltype = get_partition_col_typid(key, i);
+                       coltypmod = get_partition_col_typmod(key, i);
 
-                       if (!ldatum->infinite)
-                               lcon = (A_Const *) ldatum->value;
-                       if (!rdatum->infinite)
-                               rcon = (A_Const *) rdatum->value;
-
-                       if (lcon)
+                       if (ldatum->value)
                        {
-                               value = (Node *) make_const(pstate, &lcon->val, lcon->location);
-                               if (((Const *) value)->constisnull)
+                               con = castNode(A_Const, ldatum->value);
+                               value = transformPartitionBoundValue(pstate, con,
+                                                                                                        colname,
+                                                                                                        coltype, coltypmod);
+                               if (value->constisnull)
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                                                         errmsg("cannot specify NULL in range bound")));
-                               value = coerce_to_target_type(pstate,
-                                                                                         value, exprType(value),
-                                                                                         get_partition_col_typid(key, i),
-                                                                                       get_partition_col_typmod(key, i),
-                                                                                         COERCION_ASSIGNMENT,
-                                                                                         COERCE_IMPLICIT_CAST,
-                                                                                         -1);
-                               if (value == NULL)
-                                       ereport(ERROR,
-                                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                                        errmsg("specified value cannot be cast to type \"%s\" of column \"%s\"",
-                                                        format_type_be(get_partition_col_typid(key, i)),
-                                                                       colname),
-                                                        parser_errposition(pstate, exprLocation((Node *) ldatum))));
-
-                               /* Simplify the expression */
-                               value = (Node *) expression_planner((Expr *) value);
-                               ldatum->value = value;
+                               ldatum = copyObject(ldatum);    /* don't scribble on input */
+                               ldatum->value = (Node *) value;
                        }
 
-                       if (rcon)
+                       if (rdatum->value)
                        {
-                               value = (Node *) make_const(pstate, &rcon->val, rcon->location);
-                               if (((Const *) value)->constisnull)
+                               con = castNode(A_Const, rdatum->value);
+                               value = transformPartitionBoundValue(pstate, con,
+                                                                                                        colname,
+                                                                                                        coltype, coltypmod);
+                               if (value->constisnull)
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                                                         errmsg("cannot specify NULL in range bound")));
-                               value = coerce_to_target_type(pstate,
-                                                                                         value, exprType(value),
-                                                                                         get_partition_col_typid(key, i),
-                                                                                       get_partition_col_typmod(key, i),
-                                                                                         COERCION_ASSIGNMENT,
-                                                                                         COERCE_IMPLICIT_CAST,
-                                                                                         -1);
-                               if (value == NULL)
-                                       ereport(ERROR,
-                                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                                        errmsg("specified value cannot be cast to type \"%s\" of column \"%s\"",
-                                                        format_type_be(get_partition_col_typid(key, i)),
-                                                                       colname),
-                                                        parser_errposition(pstate, exprLocation((Node *) rdatum))));
-
-                               /* Simplify the expression */
-                               value = (Node *) expression_planner((Expr *) value);
-                               rdatum->value = value;
+                               rdatum = copyObject(rdatum);    /* don't scribble on input */
+                               rdatum->value = (Node *) value;
                        }
 
                        result_spec->lowerdatums = lappend(result_spec->lowerdatums,
-                                                                                          copyObject(ldatum));
+                                                                                          ldatum);
                        result_spec->upperdatums = lappend(result_spec->upperdatums,
-                                                                                          copyObject(rdatum));
+                                                                                          rdatum);
 
                        ++i;
                }
@@ -3517,5 +3463,50 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
        else
                elog(ERROR, "unexpected partition strategy: %d", (int) strategy);
 
-       return (Node *) result_spec;
+       return result_spec;
+}
+
+/*
+ * Transform one constant in a partition bound spec
+ */
+static Const *
+transformPartitionBoundValue(ParseState *pstate, A_Const *con,
+                                                  const char *colName, Oid colType, int32 colTypmod)
+{
+       Node       *value;
+
+       /* Make it into a Const */
+       value = (Node *) make_const(pstate, &con->val, con->location);
+
+       /* Coerce to correct type */
+       value = coerce_to_target_type(pstate,
+                                                                 value, exprType(value),
+                                                                 colType,
+                                                                 colTypmod,
+                                                                 COERCION_ASSIGNMENT,
+                                                                 COERCE_IMPLICIT_CAST,
+                                                                 -1);
+
+       if (value == NULL)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+               errmsg("specified value cannot be cast to type %s for column \"%s\"",
+                          format_type_be(colType), colName),
+                                parser_errposition(pstate, con->location)));
+
+       /* Simplify the expression, in case we had a coercion */
+       if (!IsA(value, Const))
+               value = (Node *) expression_planner((Expr *) value);
+
+       /* Fail if we don't have a constant (i.e., non-immutable coercion) */
+       if (!IsA(value, Const))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+               errmsg("specified value cannot be cast to type %s for column \"%s\"",
+                          format_type_be(colType), colName),
+                                errdetail("The cast requires a non-immutable conversion."),
+                                errhint("Try putting the literal value in single quotes."),
+                                parser_errposition(pstate, con->location)));
+
+       return (Const *) value;
 }
index 9234bc2a971ee84649a4fd25c1c8a9de904fb08d..824d7572faf43139b12e68fd2c8981290226d75b 100644 (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 = ", ";
index 1187797fd9ef3a90e951432ee1475053b9496a46..aa494528364367c72c36b97c8344d69d54050671 100644 (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 */
index 25fb0a044018756aa196d6b49138ab74d0f861f5..0a1e468898612a514102966753d26b4371dffee8 100644 (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);
index f59d719923ee6dfb394f2297b0073af9204ae49c..15de93635573affce3919fdab596bcf464368d78 100644 (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)
index 4b8727e9193172c1ec0df55dd7d406abd13069dc..8720e713c42cd83abd762398be7884496b5e1e98 100644 (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) */
index 41ab44e5c7be991b6d3b13f54accd2f66351e94f..8d0d17f8577a972824141279dcf920d7f8b088de 100644 (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 */
index 39edf04cb4e0c687b8000c7a5d33467ff531f170..5136506dff68c33aea0696c6f1267f38b554bee2 100644 (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);
index 5a2774395e5493fe096237366dcbc159f3e02cee..cb7aa5bbc6032de84cf2147b3ef4f6e4e4a0240d 100644 (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);