aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_utilcmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_utilcmd.c')
-rw-r--r--src/backend/parser/parse_utilcmd.c223
1 files changed, 107 insertions, 116 deletions
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index beb099569ba..9134fb9d63c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -91,7 +91,7 @@ typedef struct
* the table */
IndexStmt *pkey; /* PRIMARY KEY index, if any */
bool ispartitioned; /* true if table is partitioned */
- Node *partbound; /* transformed FOR VALUES */
+ PartitionBoundSpec *partbound; /* transformed FOR VALUES */
} CreateStmtContext;
/* State shared by transformCreateSchemaStmt and its subroutines */
@@ -135,6 +135,8 @@ static void transformConstraintAttrs(CreateStmtContext *cxt,
static void transformColumnType(CreateStmtContext *cxt, ColumnDef *column);
static void setSchemaName(char *context_schema, char **stmt_schema_name);
static void transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd);
+static Const *transformPartitionBoundValue(ParseState *pstate, A_Const *con,
+ const char *colName, Oid colType, int32 colTypmod);
/*
@@ -256,24 +258,10 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
if (stmt->partspec)
{
- int partnatts = list_length(stmt->partspec->partParams);
-
if (stmt->inhRelations && !stmt->partbound)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("cannot create partitioned table as inheritance child")));
-
- if (partnatts > PARTITION_MAX_KEYS)
- ereport(ERROR,
- (errcode(ERRCODE_TOO_MANY_COLUMNS),
- errmsg("cannot partition using more than %d columns",
- PARTITION_MAX_KEYS)));
-
- if (!pg_strcasecmp(stmt->partspec->strategy, "list") &&
- partnatts > 1)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("cannot list partition using more than one column")));
}
/*
@@ -3280,24 +3268,33 @@ transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd)
/*
* transformPartitionBound
*
- * Transform partition bound specification
+ * Transform a partition bound specification
*/
-Node *
-transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
+PartitionBoundSpec *
+transformPartitionBound(ParseState *pstate, Relation parent,
+ PartitionBoundSpec *spec)
{
- PartitionBoundSpec *spec = (PartitionBoundSpec *) bound,
- *result_spec;
+ PartitionBoundSpec *result_spec;
PartitionKey key = RelationGetPartitionKey(parent);
char strategy = get_partition_strategy(key);
int partnatts = get_partition_natts(key);
List *partexprs = get_partition_exprs(key);
+ /* Avoid scribbling on input */
result_spec = copyObject(spec);
if (strategy == PARTITION_STRATEGY_LIST)
{
ListCell *cell;
char *colname;
+ Oid coltype;
+ int32 coltypmod;
+
+ if (spec->strategy != PARTITION_STRATEGY_LIST)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("invalid bound specification for a list partition"),
+ parser_errposition(pstate, exprLocation((Node *) spec))));
/* Get the only column's name in case we need to output an error */
if (key->partattrs[0] != 0)
@@ -3308,47 +3305,26 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
deparse_context_for(RelationGetRelationName(parent),
RelationGetRelid(parent)),
false, false);
-
- if (spec->strategy != PARTITION_STRATEGY_LIST)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
- errmsg("invalid bound specification for a list partition"),
- parser_errposition(pstate, exprLocation(bound))));
+ /* Need its type data too */
+ coltype = get_partition_col_typid(key, 0);
+ coltypmod = get_partition_col_typmod(key, 0);
result_spec->listdatums = NIL;
foreach(cell, spec->listdatums)
{
- A_Const *con = (A_Const *) lfirst(cell);
- Node *value;
+ A_Const *con = castNode(A_Const, lfirst(cell));
+ Const *value;
ListCell *cell2;
bool duplicate;
- value = (Node *) make_const(pstate, &con->val, con->location);
- value = coerce_to_target_type(pstate,
- value, exprType(value),
- get_partition_col_typid(key, 0),
- get_partition_col_typmod(key, 0),
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
-
- if (value == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("specified value cannot be cast to type \"%s\" of column \"%s\"",
- format_type_be(get_partition_col_typid(key, 0)),
- colname),
- parser_errposition(pstate,
- exprLocation((Node *) con))));
-
- /* Simplify the expression */
- value = (Node *) expression_planner((Expr *) value);
+ value = transformPartitionBoundValue(pstate, con,
+ colname, coltype, coltypmod);
/* Don't add to the result if the value is a duplicate */
duplicate = false;
foreach(cell2, result_spec->listdatums)
{
- Const *value2 = (Const *) lfirst(cell2);
+ Const *value2 = castNode(Const, lfirst(cell2));
if (equal(value, value2))
{
@@ -3369,16 +3345,13 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
*cell2;
int i,
j;
- char *colname;
bool seen_unbounded;
if (spec->strategy != PARTITION_STRATEGY_RANGE)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("invalid bound specification for a range partition"),
- parser_errposition(pstate, exprLocation(bound))));
-
- Assert(spec->lowerdatums != NIL && spec->upperdatums != NIL);
+ parser_errposition(pstate, exprLocation((Node *) spec))));
if (list_length(spec->lowerdatums) != partnatts)
ereport(ERROR,
@@ -3390,15 +3363,15 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
errmsg("TO must specify exactly one value per partitioning column")));
/*
- * Check that no finite value follows a UNBOUNDED literal in either of
+ * Check that no finite value follows an UNBOUNDED item in either of
* lower and upper bound lists.
*/
seen_unbounded = false;
foreach(cell1, spec->lowerdatums)
{
- PartitionRangeDatum *ldatum;
+ PartitionRangeDatum *ldatum = castNode(PartitionRangeDatum,
+ lfirst(cell1));
- ldatum = (PartitionRangeDatum *) lfirst(cell1);
if (ldatum->infinite)
seen_unbounded = true;
else if (seen_unbounded)
@@ -3410,9 +3383,9 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
seen_unbounded = false;
foreach(cell1, spec->upperdatums)
{
- PartitionRangeDatum *rdatum;
+ PartitionRangeDatum *rdatum = castNode(PartitionRangeDatum,
+ lfirst(cell1));
- rdatum = (PartitionRangeDatum *) lfirst(cell1);
if (rdatum->infinite)
seen_unbounded = true;
else if (seen_unbounded)
@@ -3422,18 +3395,19 @@ transformPartitionBound(ParseState *pstate, Relation parent, Node *bound)
parser_errposition(pstate, exprLocation((Node *) rdatum))));
}
+ /* Transform all the constants */
i = j = 0;
result_spec->lowerdatums = result_spec->upperdatums = NIL;
forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums)
{
- PartitionRangeDatum *ldatum,
- *rdatum;
- Node *value;
- A_Const *lcon = NULL,
- *rcon = NULL;
-
- 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;
}