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.c213
1 files changed, 143 insertions, 70 deletions
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 1084af2eedb..445e81a1afb 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -50,7 +50,9 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
+#include "optimizer/clauses.h"
#include "optimizer/planner.h"
+#include "optimizer/var.h"
#include "parser/analyze.h"
#include "parser/parse_clause.h"
#include "parser/parse_coerce.h"
@@ -139,9 +141,12 @@ 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 List *transformPartitionRangeBounds(ParseState *pstate, List *blist,
+ Relation parent);
static void validateInfiniteBounds(ParseState *pstate, List *blist);
-static Const *transformPartitionBoundValue(ParseState *pstate, A_Const *con,
- const char *colName, Oid colType, int32 colTypmod);
+static Const *transformPartitionBoundValue(ParseState *pstate, Node *con,
+ const char *colName, Oid colType, int32 colTypmod,
+ Oid partCollation);
/*
@@ -3625,6 +3630,7 @@ transformPartitionBound(ParseState *pstate, Relation parent,
char *colname;
Oid coltype;
int32 coltypmod;
+ Oid partcollation;
if (spec->strategy != PARTITION_STRATEGY_LIST)
ereport(ERROR,
@@ -3644,17 +3650,19 @@ transformPartitionBound(ParseState *pstate, Relation parent,
/* Need its type data too */
coltype = get_partition_col_typid(key, 0);
coltypmod = get_partition_col_typmod(key, 0);
+ partcollation = get_partition_col_collation(key, 0);
result_spec->listdatums = NIL;
foreach(cell, spec->listdatums)
{
- A_Const *con = castNode(A_Const, lfirst(cell));
+ Node *expr = lfirst(cell);
Const *value;
ListCell *cell2;
bool duplicate;
- value = transformPartitionBoundValue(pstate, con,
- colname, coltype, coltypmod);
+ value = transformPartitionBoundValue(pstate, expr,
+ colname, coltype, coltypmod,
+ partcollation);
/* Don't add to the result if the value is a duplicate */
duplicate = false;
@@ -3677,11 +3685,6 @@ transformPartitionBound(ParseState *pstate, Relation parent,
}
else if (strategy == PARTITION_STRATEGY_RANGE)
{
- ListCell *cell1,
- *cell2;
- int i,
- j;
-
if (spec->strategy != PARTITION_STRATEGY_RANGE)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
@@ -3698,23 +3701,78 @@ transformPartitionBound(ParseState *pstate, Relation parent,
errmsg("TO must specify exactly one value per partitioning column")));
/*
- * Once we see MINVALUE or MAXVALUE for one column, the remaining
- * columns must be the same.
+ * Convert raw parse nodes into PartitionRangeDatum nodes and perform
+ * any necessary validation.
+ */
+ result_spec->lowerdatums =
+ transformPartitionRangeBounds(pstate, spec->lowerdatums,
+ parent);
+ result_spec->upperdatums =
+ transformPartitionRangeBounds(pstate, spec->upperdatums,
+ parent);
+ }
+ else
+ elog(ERROR, "unexpected partition strategy: %d", (int) strategy);
+
+ return result_spec;
+}
+
+/*
+ * transformPartitionRangeBounds
+ * This converts the expressions for range partition bounds from the raw
+ * grammar representation to PartitionRangeDatum structs
+ */
+static List *
+transformPartitionRangeBounds(ParseState *pstate, List *blist,
+ Relation parent)
+{
+ List *result = NIL;
+ PartitionKey key = RelationGetPartitionKey(parent);
+ List *partexprs = get_partition_exprs(key);
+ ListCell *lc;
+ int i,
+ j;
+
+ i = j = 0;
+ foreach(lc, blist)
+ {
+ Node *expr = lfirst(lc);
+ PartitionRangeDatum *prd = NULL;
+
+ /*
+ * Infinite range bounds -- "minvalue" and "maxvalue" -- get passed
+ * in as ColumnRefs.
*/
- validateInfiniteBounds(pstate, spec->lowerdatums);
- validateInfiniteBounds(pstate, spec->upperdatums);
+ if (IsA(expr, ColumnRef))
+ {
+ ColumnRef *cref = (ColumnRef *) expr;
+ char *cname = NULL;
+
+ if (list_length(cref->fields) == 1 &&
+ IsA(linitial(cref->fields), String))
+ cname = strVal(linitial(cref->fields));
- /* Transform all the constants */
- i = j = 0;
- result_spec->lowerdatums = result_spec->upperdatums = NIL;
- forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums)
+ Assert(cname != NULL);
+ if (strcmp("minvalue", cname) == 0)
+ {
+ prd = makeNode(PartitionRangeDatum);
+ prd->kind = PARTITION_RANGE_DATUM_MINVALUE;
+ prd->value = NULL;
+ }
+ else if (strcmp("maxvalue", cname) == 0)
+ {
+ prd = makeNode(PartitionRangeDatum);
+ prd->kind = PARTITION_RANGE_DATUM_MAXVALUE;
+ prd->value = NULL;
+ }
+ }
+
+ if (prd == NULL)
{
- PartitionRangeDatum *ldatum = (PartitionRangeDatum *) lfirst(cell1);
- PartitionRangeDatum *rdatum = (PartitionRangeDatum *) lfirst(cell2);
char *colname;
Oid coltype;
int32 coltypmod;
- A_Const *con;
+ Oid partcollation;
Const *value;
/* Get the column's name in case we need to output an error */
@@ -3729,50 +3787,38 @@ transformPartitionBound(ParseState *pstate, Relation parent,
false, false);
++j;
}
+
/* Need its type data too */
coltype = get_partition_col_typid(key, i);
coltypmod = get_partition_col_typmod(key, i);
+ partcollation = get_partition_col_collation(key, i);
- if (ldatum->value)
- {
- 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")));
- ldatum = copyObject(ldatum); /* don't scribble on input */
- ldatum->value = (Node *) value;
- }
-
- if (rdatum->value)
- {
- 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")));
- rdatum = copyObject(rdatum); /* don't scribble on input */
- rdatum->value = (Node *) value;
- }
-
- result_spec->lowerdatums = lappend(result_spec->lowerdatums,
- ldatum);
- result_spec->upperdatums = lappend(result_spec->upperdatums,
- rdatum);
-
+ value = transformPartitionBoundValue(pstate, expr,
+ colname,
+ coltype, coltypmod,
+ partcollation);
+ if (value->constisnull)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("cannot specify NULL in range bound")));
+ prd = makeNode(PartitionRangeDatum);
+ prd->kind = PARTITION_RANGE_DATUM_VALUE;
+ prd->value = (Node *) value;
++i;
}
+
+ prd->location = exprLocation(expr);
+
+ result = lappend(result, prd);
}
- else
- elog(ERROR, "unexpected partition strategy: %d", (int) strategy);
- return result_spec;
+ /*
+ * Once we see MINVALUE or MAXVALUE for one column, the remaining
+ * columns must be the same.
+ */
+ validateInfiniteBounds(pstate, result);
+
+ return result;
}
/*
@@ -3821,13 +3867,34 @@ validateInfiniteBounds(ParseState *pstate, List *blist)
* Transform one constant in a partition bound spec
*/
static Const *
-transformPartitionBoundValue(ParseState *pstate, A_Const *con,
- const char *colName, Oid colType, int32 colTypmod)
+transformPartitionBoundValue(ParseState *pstate, Node *val,
+ const char *colName, Oid colType, int32 colTypmod,
+ Oid partCollation)
{
Node *value;
- /* Make it into a Const */
- value = (Node *) make_const(pstate, &con->val, con->location);
+ /* Transform raw parsetree */
+ value = transformExpr(pstate, val, EXPR_KIND_PARTITION_BOUND);
+
+ /*
+ * Check that the input expression's collation is compatible with one
+ * specified for the parent's partition key (partcollation). Don't
+ * throw an error if it's the default collation which we'll replace with
+ * the parent's collation anyway.
+ */
+ if (IsA(value, CollateExpr))
+ {
+ Oid exprCollOid = exprCollation(value);
+
+ if (OidIsValid(exprCollOid) &&
+ exprCollOid != DEFAULT_COLLATION_OID &&
+ exprCollOid != partCollation)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("collation of partition bound value for column \"%s\" does not match partition key collation \"%s\"",
+ colName, get_collation_name(partCollation)),
+ parser_errposition(pstate, exprLocation(value))));
+ }
/* Coerce to correct type */
value = coerce_to_target_type(pstate,
@@ -3843,21 +3910,27 @@ transformPartitionBoundValue(ParseState *pstate, A_Const *con,
(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)));
+ parser_errposition(pstate, exprLocation(val))));
/* 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))
+ /* Make sure the expression does not refer to any vars. */
+ if (contain_var_clause(value))
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)));
+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+ errmsg("cannot use column references in partition bound expression"),
+ parser_errposition(pstate, exprLocation(value))));
+
+ /*
+ * Evaluate the expression, assigning the partition key's collation to the
+ * resulting Const expression.
+ */
+ value = (Node *) evaluate_expr((Expr *) value, colType, colTypmod,
+ partCollation);
+ if (!IsA(value, Const))
+ elog(ERROR, "could not evaluate partition bound expression");
return (Const *) value;
}