* 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,
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)
i = j = 0;
foreach(cell, boundspecs)
{
- PartitionBoundSpec *spec = lfirst(cell);
+ PartitionBoundSpec *spec = castNode(PartitionBoundSpec,
+ lfirst(cell));
PartitionRangeBound *lower,
*upper;
* 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);
foreach(cell, spec->listdatums)
{
- Const *val = lfirst(cell);
+ Const *val = castNode(Const, lfirst(cell));
if (!val->constisnull)
{
* 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;
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);
}
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;
}
* 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
* 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
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++)
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
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,
MemoryContext oldcxt;
Datum boundDatum;
bool isnull;
- Node *bound;
+ PartitionBoundSpec *bound;
List *my_qual = NIL,
*result = NIL;
Relation parent;
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);
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++)
{
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));
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
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");
/* Process and store partition bound, if any. */
if (stmt->partbound)
{
- Node *bound;
+ PartitionBoundSpec *bound;
ParseState *pstate;
Oid parentId = linitial_oid(inheritOids);
Relation parent;
/* Tranform the bound values */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
+
bound = transformPartitionBound(pstate, parent, stmt->partbound);
/*
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
*/
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);
}
/*
* 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)
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,
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.
/* 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",
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);
}
/*
- * 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,
attn = 0;
foreach(lc, partParams)
{
- PartitionElem *pelem = (PartitionElem *) lfirst(lc);
+ PartitionElem *pelem = castNode(PartitionElem, lfirst(lc));
Oid atttype;
Oid attcollation;
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),
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)));
atttype = attform->atttypid;
attcollation = attform->attcollation;
ReleaseSysCache(atttuple);
-
- /* Note that whole-row references can't happen here; see below */
}
else
{
expr = (Node *) ((CollateExpr *) expr)->arg;
if (IsA(expr, Var) &&
- ((Var *) expr)->varattno != InvalidAttrNumber)
+ ((Var *) expr)->varattno > 0)
{
/*
* User wrote "(column)" or "(column COLLATE something)".
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.
(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")));
}
}
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)
{
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)
{
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;
}
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;
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;
/* 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;
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)
{
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)
{
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
WRITE_BOOL_FIELD(infinite);
WRITE_NODE_FIELD(value);
+ /* XXX somebody forgot location field; too late to change for v10 */
}
/*
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;
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();
}
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();
}
VariableSetStmt *vsetstmt;
PartitionElem *partelem;
PartitionSpec *partspec;
- PartitionRangeDatum *partrange_datum;
+ PartitionBoundSpec *partboundspec;
RoleSpec *rolespec;
}
%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.
n->subtype = AT_AttachPartition;
cmd->name = $3;
- cmd->bound = (Node *) $4;
+ cmd->bound = $4;
n->def = (Node *) cmd;
$$ = (Node *) n;
n->subtype = AT_DetachPartition;
cmd->name = $3;
+ cmd->bound = NULL;
n->def = (Node *) cmd;
$$ = (Node *) n;
n->listdatums = $5;
n->location = @3;
- $$ = (Node *) n;
+ $$ = n;
}
/* a RANGE partition */
n->upperdatums = $9;
n->location = @3;
- $$ = (Node *) n;
+ $$ = n;
}
;
n->value = NULL;
n->location = @1;
- $$ = n;
+ $$ = (Node *) n;
}
| partbound_datum
{
n->value = $1;
n->location = @1;
- $$ = n;
+ $$ = (Node *) n;
}
;
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;
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;
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;
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;
* 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 */
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);
/*
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")));
}
/*
/*
* 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)
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))
{
*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,
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)
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)
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),
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;
}
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;
}
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);
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 = ", ";
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 */
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);
T_AlterPolicyStmt,
T_CreateTransformStmt,
T_CreateAmStmt,
- T_PartitionCmd,
T_CreatePublicationStmt,
T_AlterPublicationStmt,
T_CreateSubscriptionStmt,
T_PartitionSpec,
T_PartitionBoundSpec,
T_PartitionRangeDatum,
+ T_PartitionCmd,
/*
* TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)
/* 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
{
} 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
{
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;
/****************************************************************************
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) */
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 */
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
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);
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);