aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/tablecmds.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/tablecmds.c')
-rw-r--r--src/backend/commands/tablecmds.c90
1 files changed, 65 insertions, 25 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index fb961e46c4a..7959120f53e 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -756,7 +756,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
/* Process and store partition bound, if any. */
if (stmt->partbound)
{
- Node *bound;
+ PartitionBoundSpec *bound;
ParseState *pstate;
Oid parentId = linitial_oid(inheritOids);
Relation parent;
@@ -777,6 +777,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
/* Tranform the bound values */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
+
bound = transformPartitionBound(pstate, parent, stmt->partbound);
/*
@@ -812,6 +813,15 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
Oid partcollation[PARTITION_MAX_KEYS];
List *partexprs = NIL;
+ partnatts = list_length(stmt->partspec->partParams);
+
+ /* Protect fixed-size arrays here and in executor */
+ if (partnatts > PARTITION_MAX_KEYS)
+ ereport(ERROR,
+ (errcode(ERRCODE_TOO_MANY_COLUMNS),
+ errmsg("cannot partition using more than %d columns",
+ PARTITION_MAX_KEYS)));
+
/*
* We need to transform the raw parsetrees corresponding to partition
* expressions into executable expression trees. Like column defaults
@@ -820,11 +830,11 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
*/
stmt->partspec = transformPartitionSpec(rel, stmt->partspec,
&strategy);
+
ComputePartitionAttrs(rel, stmt->partspec->partParams,
partattrs, &partexprs, partopclass,
partcollation);
- partnatts = list_length(stmt->partspec->partParams);
StorePartitionKey(rel, strategy, partnatts, partattrs, partexprs,
partopclass, partcollation);
}
@@ -13109,6 +13119,8 @@ RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
/*
* Transform any expressions present in the partition key
+ *
+ * Returns a transformed PartitionSpec, as well as the strategy code
*/
static PartitionSpec *
transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
@@ -13121,13 +13133,13 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
newspec = makeNode(PartitionSpec);
newspec->strategy = partspec->strategy;
- newspec->location = partspec->location;
newspec->partParams = NIL;
+ newspec->location = partspec->location;
/* Parse partitioning strategy name */
- if (!pg_strcasecmp(partspec->strategy, "list"))
+ if (pg_strcasecmp(partspec->strategy, "list") == 0)
*strategy = PARTITION_STRATEGY_LIST;
- else if (!pg_strcasecmp(partspec->strategy, "range"))
+ else if (pg_strcasecmp(partspec->strategy, "range") == 0)
*strategy = PARTITION_STRATEGY_RANGE;
else
ereport(ERROR,
@@ -13135,6 +13147,13 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
errmsg("unrecognized partitioning strategy \"%s\"",
partspec->strategy)));
+ /* Check valid number of columns for strategy */
+ if (*strategy == PARTITION_STRATEGY_LIST &&
+ list_length(partspec->partParams) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("cannot use \"list\" partition strategy with more than one column")));
+
/*
* Create a dummy ParseState and insert the target relation as its sole
* rangetable entry. We need a ParseState for transformExpr.
@@ -13146,16 +13165,16 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
/* take care of any partition expressions */
foreach(l, partspec->partParams)
{
+ PartitionElem *pelem = castNode(PartitionElem, lfirst(l));
ListCell *lc;
- PartitionElem *pelem = (PartitionElem *) lfirst(l);
/* Check for PARTITION BY ... (foo, foo) */
foreach(lc, newspec->partParams)
{
- PartitionElem *pparam = (PartitionElem *) lfirst(lc);
+ PartitionElem *pparam = castNode(PartitionElem, lfirst(lc));
if (pelem->name && pparam->name &&
- !strcmp(pelem->name, pparam->name))
+ strcmp(pelem->name, pparam->name) == 0)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN),
errmsg("column \"%s\" appears more than once in partition key",
@@ -13165,6 +13184,9 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
if (pelem->expr)
{
+ /* Copy, to avoid scribbling on the input */
+ pelem = copyObject(pelem);
+
/* Now do parse transformation of the expression */
pelem->expr = transformExpr(pstate, pelem->expr,
EXPR_KIND_PARTITION_EXPRESSION);
@@ -13180,7 +13202,8 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
}
/*
- * Compute per-partition-column information from a list of PartitionElem's
+ * Compute per-partition-column information from a list of PartitionElems.
+ * Expressions in the PartitionElems must be parse-analyzed already.
*/
static void
ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
@@ -13192,7 +13215,7 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
attn = 0;
foreach(lc, partParams)
{
- PartitionElem *pelem = (PartitionElem *) lfirst(lc);
+ PartitionElem *pelem = castNode(PartitionElem, lfirst(lc));
Oid atttype;
Oid attcollation;
@@ -13202,7 +13225,8 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
HeapTuple atttuple;
Form_pg_attribute attform;
- atttuple = SearchSysCacheAttName(RelationGetRelid(rel), pelem->name);
+ atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
+ pelem->name);
if (!HeapTupleIsValid(atttuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
@@ -13212,7 +13236,7 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
if (attform->attnum <= 0)
ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_COLUMN),
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("cannot use system column \"%s\" in partition key",
pelem->name)));
@@ -13220,8 +13244,6 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
atttype = attform->atttypid;
attcollation = attform->attcollation;
ReleaseSysCache(atttuple);
-
- /* Note that whole-row references can't happen here; see below */
}
else
{
@@ -13240,7 +13262,7 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
expr = (Node *) ((CollateExpr *) expr)->arg;
if (IsA(expr, Var) &&
- ((Var *) expr)->varattno != InvalidAttrNumber)
+ ((Var *) expr)->varattno > 0)
{
/*
* User wrote "(column)" or "(column COLLATE something)".
@@ -13251,11 +13273,18 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
else
{
Bitmapset *expr_attrs = NULL;
+ int i;
partattrs[attn] = 0; /* marks the column as expression */
*partexprs = lappend(*partexprs, expr);
/*
+ * Try to simplify the expression before checking for
+ * mutability. The main practical value of doing it in this
+ * order is that an inline-able SQL-language function will be
+ * accepted if its expansion is immutable, whether or not the
+ * function itself is marked immutable.
+ *
* Note that expression_planner does not change the passed in
* expression destructively and we have already saved the
* expression to be stored into the catalog above.
@@ -13274,27 +13303,38 @@ ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
errmsg("functions in partition key expression must be marked IMMUTABLE")));
/*
- * While it is not exactly *wrong* for an expression to be a
- * constant value, it seems better to prevent such input.
- */
- if (IsA(expr, Const))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("cannot use constant expression as partition key")));
-
- /*
* transformPartitionSpec() should have already rejected
* subqueries, aggregates, window functions, and SRFs, based
* on the EXPR_KIND_ for partition expressions.
*/
- /* Cannot have expressions containing whole-row references */
+ /*
+ * Cannot have expressions containing whole-row references or
+ * system column references.
+ */
pull_varattnos(expr, 1, &expr_attrs);
if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber,
expr_attrs))
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("partition key expressions cannot contain whole-row references")));
+ for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
+ {
+ if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
+ expr_attrs))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("partition key expressions cannot contain system column references")));
+ }
+
+ /*
+ * While it is not exactly *wrong* for a partition expression
+ * to be a constant, it seems better to reject such keys.
+ */
+ if (IsA(expr, Const))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("cannot use constant expression as partition key")));
}
}