diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/gram.y | 60 | ||||
-rw-r--r-- | src/backend/parser/parse_agg.c | 10 | ||||
-rw-r--r-- | src/backend/parser/parse_expr.c | 5 | ||||
-rw-r--r-- | src/backend/parser/parse_func.c | 3 | ||||
-rw-r--r-- | src/backend/parser/parse_utilcmd.c | 213 |
5 files changed, 164 insertions, 127 deletions
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 6535550c72e..c1faf4152ca 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -581,8 +581,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <partelem> part_elem %type <list> part_params %type <partboundspec> PartitionBoundSpec -%type <node> partbound_datum PartitionRangeDatum -%type <list> hash_partbound partbound_datum_list range_datum_list +%type <list> hash_partbound %type <defelt> hash_partbound_elem /* @@ -2731,7 +2730,7 @@ PartitionBoundSpec: } /* a LIST partition */ - | FOR VALUES IN_P '(' partbound_datum_list ')' + | FOR VALUES IN_P '(' expr_list ')' { PartitionBoundSpec *n = makeNode(PartitionBoundSpec); @@ -2744,7 +2743,7 @@ PartitionBoundSpec: } /* a RANGE partition */ - | FOR VALUES FROM '(' range_datum_list ')' TO '(' range_datum_list ')' + | FOR VALUES FROM '(' expr_list ')' TO '(' expr_list ')' { PartitionBoundSpec *n = makeNode(PartitionBoundSpec); @@ -2787,59 +2786,6 @@ hash_partbound: } ; -partbound_datum: - Sconst { $$ = makeStringConst($1, @1); } - | NumericOnly { $$ = makeAConst($1, @1); } - | TRUE_P { $$ = makeStringConst(pstrdup("true"), @1); } - | FALSE_P { $$ = makeStringConst(pstrdup("false"), @1); } - | NULL_P { $$ = makeNullAConst(@1); } - ; - -partbound_datum_list: - partbound_datum { $$ = list_make1($1); } - | partbound_datum_list ',' partbound_datum - { $$ = lappend($1, $3); } - ; - -range_datum_list: - PartitionRangeDatum { $$ = list_make1($1); } - | range_datum_list ',' PartitionRangeDatum - { $$ = lappend($1, $3); } - ; - -PartitionRangeDatum: - MINVALUE - { - PartitionRangeDatum *n = makeNode(PartitionRangeDatum); - - n->kind = PARTITION_RANGE_DATUM_MINVALUE; - n->value = NULL; - n->location = @1; - - $$ = (Node *) n; - } - | MAXVALUE - { - PartitionRangeDatum *n = makeNode(PartitionRangeDatum); - - n->kind = PARTITION_RANGE_DATUM_MAXVALUE; - n->value = NULL; - n->location = @1; - - $$ = (Node *) n; - } - | partbound_datum - { - PartitionRangeDatum *n = makeNode(PartitionRangeDatum); - - n->kind = PARTITION_RANGE_DATUM_VALUE; - n->value = $1; - n->location = @1; - - $$ = (Node *) n; - } - ; - /***************************************************************************** * * ALTER TYPE diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 669fe82c48a..8ed38168667 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -507,6 +507,13 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr) err = _("grouping operations are not allowed in trigger WHEN conditions"); break; + case EXPR_KIND_PARTITION_BOUND: + if (isAgg) + err = _("aggregate functions are not allowed in partition bound"); + else + err = _("grouping operations are not allowed in partition bound"); + + break; case EXPR_KIND_PARTITION_EXPRESSION: if (isAgg) err = _("aggregate functions are not allowed in partition key expressions"); @@ -904,6 +911,9 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc, case EXPR_KIND_TRIGGER_WHEN: err = _("window functions are not allowed in trigger WHEN conditions"); break; + case EXPR_KIND_PARTITION_BOUND: + err = _("window functions are not allowed in partition bound"); + break; case EXPR_KIND_PARTITION_EXPRESSION: err = _("window functions are not allowed in partition key expressions"); break; diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index a47697a4215..3e0a762c41e 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -1843,6 +1843,9 @@ transformSubLink(ParseState *pstate, SubLink *sublink) case EXPR_KIND_TRIGGER_WHEN: err = _("cannot use subquery in trigger WHEN condition"); break; + case EXPR_KIND_PARTITION_BOUND: + err = _("cannot use subquery in partition bound"); + break; case EXPR_KIND_PARTITION_EXPRESSION: err = _("cannot use subquery in partition key expression"); break; @@ -3474,6 +3477,8 @@ ParseExprKindName(ParseExprKind exprKind) return "EXECUTE"; case EXPR_KIND_TRIGGER_WHEN: return "WHEN"; + case EXPR_KIND_PARTITION_BOUND: + return "partition bound"; case EXPR_KIND_PARTITION_EXPRESSION: return "PARTITION BY"; case EXPR_KIND_CALL_ARGUMENT: diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 7213f5be17a..5222231b511 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -2364,6 +2364,9 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location) case EXPR_KIND_TRIGGER_WHEN: err = _("set-returning functions are not allowed in trigger WHEN conditions"); break; + case EXPR_KIND_PARTITION_BOUND: + err = _("set-returning functions are not allowed in partition bound"); + break; case EXPR_KIND_PARTITION_EXPRESSION: err = _("set-returning functions are not allowed in partition key expressions"); break; 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; } |