Re: Boolean partitions syntax - Mailing list pgsql-hackers
From | Kyotaro HORIGUCHI |
---|---|
Subject | Re: Boolean partitions syntax |
Date | |
Msg-id | 20180416.161740.51264437.horiguchi.kyotaro@lab.ntt.co.jp Whole thread Raw |
In response to | Re: Boolean partitions syntax (Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>) |
Responses |
Re: Boolean partitions syntax
Re: Boolean partitions syntax |
List | pgsql-hackers |
Hello. Thank you for the comment. the attached v6 patch differs only in gram.y since v5. At Fri, 13 Apr 2018 18:55:30 +0900, Amit Langote <Langote_Amit_f8@lab.ntt.co.jp> wrote in <ca37d679-014e-1895-e4bc-89b7129ce58b@lab.ntt.co.jp> > Horiguchi-san, > > Thanks for the latest patch. > > On 2018/04/12 13:12, Kyotaro HORIGUCHI wrote: > > Thank you for verification and the revised patch. The format is > > fine and the fix is correct but I noticed that I forgot to remove > > plural S's from error messages. The attached is the version with > > the fix. > > Looking at the gram.y changes in the latest patch, I think there needs to > be some explanatory comments about about the new productions -- u_expr, > b0_expr, and c0_expr. I think I did that. And refactord the rules. It was a bother that some rules used c_expr directly but I managed to replace all of them with a_expr by lowering precedence of some ordinary keywords (PASSING, BY, COLUMNS and ROW). c_expr is no loger used elsewhere so we can just remove columnref from c_expr. Finally [abc]0_expr was eliminated and we have only a_expr, b_expr, u_expr and c_expr. This seems simple enough. The relationship among the rules after this change is as follows. a_expr --+-- columnref +-- u_expr ---+-- c_expr -- (all old c_expr stuff except columnref) +-- (all old a_expr stuff) b_expr --+-- columnref +-- c_expr -- (ditto) +-- (all old b_expr stuff) On the way fixing this, I noticed that the precedence of some keywords (PRESERVE, STRIP_P) that was more than necessary and corrected it. regards, -- Kyotaro Horiguchi NTT Open Source Software Center diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index ed6b680ed8..cfb0984100 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -152,8 +152,6 @@ static Node *substitute_actual_parameters(Node *expr, int nargs, List *args, static Node *substitute_actual_parameters_mutator(Node *node, substitute_actual_parameters_context *context); static void sql_inline_error_callback(void *arg); -static Expr *evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, - Oid result_collation); static Query *substitute_actual_srf_parameters(Query *expr, int nargs, List *args); static Node *substitute_actual_srf_parameters_mutator(Node *node, @@ -4842,7 +4840,7 @@ sql_inline_error_callback(void *arg) * We use the executor's routine ExecEvalExpr() to avoid duplication of * code and ensure we get the same result as the executor would get. */ -static Expr * +Expr * evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, Oid result_collation) { diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index e548476623..ebdb0f7b18 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -180,6 +180,8 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, static List *mergeTableFuncParameters(List *func_args, List *columns); static TypeName *TableFuncTypeName(List *columns); static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_t yyscanner); +static Node *makePartRangeDatum(PartitionRangeDatumKind kind, Node *value, + int location); static void SplitColQualList(List *qualList, List **constraintList, CollateClause **collClause, core_yyscan_t yyscanner); @@ -468,7 +470,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <node> columnDef columnOptions %type <defelt> def_elem reloption_elem old_aggr_elem operator_def_elem %type <node> def_arg columnElem where_clause where_or_current_clause - a_expr b_expr c_expr AexprConst indirection_el opt_slice_bound + a_expr u_expr b_expr c_expr + AexprConst indirection_el opt_slice_bound columnref in_expr having_clause func_table xmltable array_expr ExclusionWhereClause operator_def_arg %type <list> rowsfrom_item rowsfrom_list opt_col_def_list @@ -581,7 +584,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 <node> PartitionRangeDatum %type <list> hash_partbound partbound_datum_list range_datum_list %type <defelt> hash_partbound_elem @@ -734,8 +737,12 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); * for RANGE, ROWS, GROUPS so that they can follow a_expr without creating * postfix-operator problems; * for GENERATED so that it can follow b_expr; - * and for NULL so that it can follow b_expr in ColQualList without creating - * postfix-operator problems. + * for NULL so that it can follow b_expr in ColQualList without creating + * postfix-operator problems; + * for ROWS to support opt_existing_window_frame; + * for ROW to support row; + * for PASSING, BY and COLUMNS to support xmltable; + * for PRESERVE STRIP_P to support xml_whitespace_option. * * To support CUBE and ROLLUP in GROUP BY without reserving them, we give them * an explicit priority lower than '(', so that a rule with CUBE '(' will shift @@ -752,7 +759,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); * blame any funny behavior of UNBOUNDED on the SQL standard, though. */ %nonassoc UNBOUNDED /* ideally should have same precedence as IDENT */ -%nonassoc IDENT GENERATED NULL_P PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP +%nonassoc IDENT GENERATED NULL_P PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP PASSING BY COLUMNS ROW PRESERVESTRIP_P %left Op OPERATOR /* multi-character ops and user-defined operators */ %left '+' '-' %left '*' '/' '%' @@ -773,9 +780,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); * left-associativity among the JOIN rules themselves. */ %left JOIN CROSS LEFT FULL RIGHT INNER_P NATURAL -/* kluge to keep xml_whitespace_option from causing shift/reduce conflicts */ -%right PRESERVE STRIP_P - %% /* @@ -2795,15 +2799,9 @@ hash_partbound: } ; -partbound_datum: - Sconst { $$ = makeStringConst($1, @1); } - | NumericOnly { $$ = makeAConst($1, @1); } - | NULL_P { $$ = makeNullAConst(@1); } - ; - partbound_datum_list: - partbound_datum { $$ = list_make1($1); } - | partbound_datum_list ',' partbound_datum + u_expr { $$ = list_make1($1); } + | partbound_datum_list ',' u_expr { $$ = lappend($1, $3); } ; @@ -2816,33 +2814,18 @@ range_datum_list: PartitionRangeDatum: MINVALUE { - PartitionRangeDatum *n = makeNode(PartitionRangeDatum); - - n->kind = PARTITION_RANGE_DATUM_MINVALUE; - n->value = NULL; - n->location = @1; - - $$ = (Node *) n; + $$ = makePartRangeDatum(PARTITION_RANGE_DATUM_MINVALUE, + NULL, @1); } | MAXVALUE { - PartitionRangeDatum *n = makeNode(PartitionRangeDatum); - - n->kind = PARTITION_RANGE_DATUM_MAXVALUE; - n->value = NULL; - n->location = @1; - - $$ = (Node *) n; + $$ = makePartRangeDatum(PARTITION_RANGE_DATUM_MAXVALUE, + NULL, @1); } - | partbound_datum + | u_expr { - PartitionRangeDatum *n = makeNode(PartitionRangeDatum); - - n->kind = PARTITION_RANGE_DATUM_VALUE; - n->value = $1; - n->location = @1; - - $$ = (Node *) n; + $$ = makePartRangeDatum(PARTITION_RANGE_DATUM_VALUE, + $1, @1); } ; @@ -11605,12 +11588,8 @@ opt_select_fetch_first_value: | /*EMPTY*/ { $$ = makeIntConst(1, -1); } ; -/* - * Again, the trailing ROW/ROWS in this case prevent the full expression - * syntax. c_expr is the best we can do. - */ select_offset_value2: - c_expr { $$ = $1; } + a_expr { $$ = $1; } ; /* noise words */ @@ -12264,9 +12243,16 @@ TableFuncElement: ColId Typename opt_collate_clause /* * XMLTABLE + * + * If we see PASSING after the a_expr, it cannot be a postfix operator but a + * part of xmlexists_arguent but it can be taken as a part of the + * a_expr. Similary xmlexists_argument can end with a_expr then the succeeding + * COLUMNS can be taken as an postfix operator. We fix this by making them + * have higher precedence than POSTFIXOP, so that the shift/reduce conflict is + * resolved in favor of shifting the rule. */ xmltable: - XMLTABLE '(' c_expr xmlexists_argument COLUMNS xmltable_column_list ')' + XMLTABLE '(' a_expr xmlexists_argument COLUMNS xmltable_column_list ')' { RangeTableFunc *n = makeNode(RangeTableFunc); n->rowexpr = $3; @@ -12277,7 +12263,7 @@ xmltable: $$ = (Node *)n; } | XMLTABLE '(' XMLNAMESPACES '(' xml_namespace_list ')' ',' - c_expr xmlexists_argument COLUMNS xmltable_column_list ')' + a_expr xmlexists_argument COLUMNS xmltable_column_list ')' { RangeTableFunc *n = makeNode(RangeTableFunc); n->rowexpr = $8; @@ -12891,16 +12877,19 @@ interval_second: * General expressions * This is the heart of the expression syntax. * - * We have two expression types: a_expr is the unrestricted kind, and - * b_expr is a subset that must be used in some places to avoid shift/reduce + * We have three expression types: a_expr is the unrestricted kind, b_expr and + * u_expr are subsets that must be used in some places to avoid shift/reduce * conflicts. For example, we can't do BETWEEN as "BETWEEN a_expr AND a_expr" * because that use of AND conflicts with AND as a boolean operator. So, * b_expr is used in BETWEEN and we remove boolean keywords from b_expr. + * Likewise we can't use MAXVALUE as an alternative rule of a_expr because + * columnref conflicts. So, u_expr is used in such places and we remove + * columnref from a_expr. * - * Note that '(' a_expr ')' is a b_expr, so an unrestricted expression can - * always be used by surrounding it with parens. + * Note that '(' a_expr ')' is both a b_expr and a u_expr, so an unrestricted + * expression can always be used by surrounding it with parens. * - * c_expr is all the productions that are common to a_expr and b_expr; + * c_expr is all the productions that are common to a_expr, u_expr and b_expr; * it's factored out just to eliminate redundant coding. * * Be careful of productions involving more than one terminal token. @@ -12909,7 +12898,19 @@ interval_second: * of the first terminal instead; otherwise you will not get the behavior * you expect! So we use %prec annotations freely to set precedences. */ -a_expr: c_expr { $$ = $1; } +a_expr: columnref { $$ = $1; } + | u_expr { $$ = $1; } + ; + +/* + * u_expr is a subset of a_expr usable as an altertive rule with unreserved + * keywords + * + * a_expr causes trouble since columnref conflicts with unreserved_keywords. + * This syntax is used for expressions appears with alternatives of + * unreserved_keywords. + */ +u_expr: c_expr { $$ = $1; } | a_expr TYPECAST Typename { $$ = makeTypeCast($1, $3, @2); } | a_expr COLLATE any_name @@ -13331,8 +13332,8 @@ a_expr: c_expr { $$ = $1; } * cause trouble in the places where b_expr is used. For simplicity, we * just eliminate all the boolean-keyword-operator productions from b_expr. */ -b_expr: c_expr - { $$ = $1; } +b_expr: columnref { $$ = $1; } + | c_expr { $$ = $1; } | b_expr TYPECAST Typename { $$ = makeTypeCast($1, $3, @2); } | '+' b_expr %prec UMINUS @@ -13399,15 +13400,14 @@ b_expr: c_expr ; /* - * Productions that can be used in both a_expr and b_expr. + * Productions that can be used in both a_expr, u_expr and b_expr. * - * Note: productions that refer recursively to a_expr or b_expr mostly - * cannot appear here. However, it's OK to refer to a_exprs that occur - * inside parentheses, such as function arguments; that cannot introduce - * ambiguity to the b_expr syntax. + * Note: productions that refer recursively to a_expr, u_expr or b_expr mostly + * cannot appear here. However, it's OK to refer to a_exprs that occur inside + * parentheses, such as function arguments; that cannot introduce ambiguity to + * the b_expr syntax. */ -c_expr: columnref { $$ = $1; } - | AexprConst { $$ = $1; } +c_expr: AexprConst { $$ = $1; } | PARAM opt_indirection { ParamRef *p = makeNode(ParamRef); @@ -13851,7 +13851,7 @@ func_expr_common_subexpr: { $$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8, @1); } - | XMLEXISTS '(' c_expr xmlexists_argument ')' + | XMLEXISTS '(' a_expr xmlexists_argument ')' { /* xmlexists(A PASSING [BY REF] B [BY REF]) is * converted to xmlexists(A, B)*/ @@ -13947,21 +13947,28 @@ xml_whitespace_option: PRESERVE WHITESPACE_P { $$ = true; } | /*EMPTY*/ { $$ = false; } ; -/* We allow several variants for SQL and other compatibility. */ +/* + * We allow several variants for SQL and other compatibility. + * + * If we see BY just aftert a_expr, it is not a part of preceding a_expr but + * it can be taken as a postfix operator. We fix this by making BY have + * higher precedence than POSTFIXOP, so that the shift/reduce confict is + * resolved in favor of reducing the rule. + */ xmlexists_argument: - PASSING c_expr + PASSING a_expr { $$ = $2; } - | PASSING c_expr BY REF + | PASSING a_expr BY REF { $$ = $2; } - | PASSING BY REF c_expr + | PASSING BY REF a_expr { $$ = $4; } - | PASSING BY REF c_expr BY REF + | PASSING BY REF a_expr BY REF { $$ = $4; } @@ -16126,6 +16133,18 @@ makeRangeVarFromAnyName(List *names, int position, core_yyscan_t yyscanner) return r; } +static Node * +makePartRangeDatum(PartitionRangeDatumKind kind, Node *value, int location) +{ + PartitionRangeDatum *n = makeNode(PartitionRangeDatum); + + n->kind = kind; + n->value = value; + n->location = location; + + return (Node *) n; +} + /* Separate Constraint nodes from COLLATE clauses in a ColQualList */ static void SplitColQualList(List *qualList, diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 61727e1d71..55a5304a38 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -499,6 +499,13 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr) else err = _("grouping operations are not allowed in EXECUTE parameters"); + 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_TRIGGER_WHEN: if (isAgg) @@ -899,6 +906,9 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc, case EXPR_KIND_PARTITION_EXPRESSION: err = _("window functions are not allowed in partition key expressions"); break; + case EXPR_KIND_PARTITION_BOUND: + err = _("window functions are not allowed in partition bound"); + break; case EXPR_KIND_CALL_ARGUMENT: err = _("window functions are not allowed in CALL arguments"); break; diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 385e54a9b6..f8759f185b 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -1849,6 +1849,9 @@ transformSubLink(ParseState *pstate, SubLink *sublink) case EXPR_KIND_CALL_ARGUMENT: err = _("cannot use subquery in CALL argument"); break; + case EXPR_KIND_PARTITION_BOUND: + err = _("cannot use subquery in partition bound"); + break; /* * There is intentionally no default: case here, so that the @@ -3473,6 +3476,8 @@ ParseExprKindName(ParseExprKind exprKind) return "WHEN"; case EXPR_KIND_PARTITION_EXPRESSION: return "PARTITION BY"; + case EXPR_KIND_PARTITION_BOUND: + return "partition bound"; case EXPR_KIND_CALL_ARGUMENT: return "CALL"; diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index ea5d5212b4..570ae860ae 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -2303,6 +2303,9 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location) case EXPR_KIND_PARTITION_EXPRESSION: err = _("set-returning functions are not allowed in partition key expressions"); break; + case EXPR_KIND_PARTITION_BOUND: + err = _("set-returning functions are not allowed in partition bound"); + break; case EXPR_KIND_CALL_ARGUMENT: err = _("set-returning functions are not allowed in CALL arguments"); break; diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 9178139912..950421286c 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -48,6 +48,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "optimizer/clauses.h" #include "optimizer/planner.h" #include "parser/analyze.h" #include "parser/parse_clause.h" @@ -138,8 +139,9 @@ 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 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 colCollation); /* @@ -3648,6 +3650,7 @@ transformPartitionBound(ParseState *pstate, Relation parent, char *colname; Oid coltype; int32 coltypmod; + Oid colcollation; if (spec->strategy != PARTITION_STRATEGY_LIST) ereport(ERROR, @@ -3667,17 +3670,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); + colcollation = get_partition_col_collation(key, 0); result_spec->listdatums = NIL; foreach(cell, spec->listdatums) { - A_Const *con = castNode(A_Const, lfirst(cell)); + Node *expr = (Node *)lfirst (cell); Const *value; ListCell *cell2; bool duplicate; - value = transformPartitionBoundValue(pstate, con, - colname, coltype, coltypmod); + value = transformPartitionBoundValue(pstate, expr, + colname, coltype, coltypmod, + colcollation); /* Don't add to the result if the value is a duplicate */ duplicate = false; @@ -3737,7 +3742,7 @@ transformPartitionBound(ParseState *pstate, Relation parent, char *colname; Oid coltype; int32 coltypmod; - A_Const *con; + Oid colcollation; Const *value; /* Get the column's name in case we need to output an error */ @@ -3755,13 +3760,15 @@ transformPartitionBound(ParseState *pstate, Relation parent, /* Need its type data too */ coltype = get_partition_col_typid(key, i); coltypmod = get_partition_col_typmod(key, i); + colcollation = get_partition_col_collation(key, i); if (ldatum->value) { - con = castNode(A_Const, ldatum->value); - value = transformPartitionBoundValue(pstate, con, + value = transformPartitionBoundValue(pstate, + ldatum->value, colname, - coltype, coltypmod); + coltype, coltypmod, + colcollation); if (value->constisnull) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), @@ -3772,10 +3779,11 @@ transformPartitionBound(ParseState *pstate, Relation parent, if (rdatum->value) { - con = castNode(A_Const, rdatum->value); - value = transformPartitionBoundValue(pstate, con, + value = transformPartitionBoundValue(pstate, + rdatum->value, colname, - coltype, coltypmod); + coltype, coltypmod, + colcollation); if (value->constisnull) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), @@ -3842,13 +3850,14 @@ 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 colCollation) { 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); /* Coerce to correct type */ value = coerce_to_target_type(pstate, @@ -3864,21 +3873,32 @@ 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)))); + + /* Fix collations after all else */ + assign_expr_collations(pstate, value); + + /* + * Check for conflict between explict collations. Partition key expression + * has precedence over partition bound value. + */ + if (exprCollation(value) != DEFAULT_COLLATION_OID && + colCollation != exprCollation(value)) + ereport(ERROR, + (errcode(ERRCODE_COLLATION_MISMATCH), + errmsg("collation mismatch between partition key expression (%d) and partition bound value (%d)", colCollation,exprCollation(value)), + 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) */ + /* Eval if we still 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))); - + value = (Node *)evaluate_expr((Expr *) value, colType, colTypmod, + colCollation); + + Assert(IsA(value, Const)); return (Const *) value; } diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index ba4fa4b68b..4b1a5b96f8 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -85,4 +85,6 @@ extern Node *estimate_expression_value(PlannerInfo *root, Node *node); extern Query *inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte); +extern Expr *evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod, + Oid result_collation); #endif /* CLAUSES_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 0230543810..68bec4b932 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -69,6 +69,7 @@ typedef enum ParseExprKind EXPR_KIND_TRIGGER_WHEN, /* WHEN condition in CREATE TRIGGER */ EXPR_KIND_POLICY, /* USING or WITH CHECK expr in policy */ EXPR_KIND_PARTITION_EXPRESSION, /* PARTITION BY expression */ + EXPR_KIND_PARTITION_BOUND, /* partition bounds value */ EXPR_KIND_CALL_ARGUMENT /* procedure argument in CALL */ } ParseExprKind; diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index ffffde01da..215f5fa06e 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -660,6 +660,12 @@ get_partition_col_typmod(PartitionKey key, int col) return key->parttypmod[col]; } +static inline Oid +get_partition_col_collation(PartitionKey key, int col) +{ + return key->partcollation[col]; +} + /* * RelationGetPartitionDesc * Returns partition descriptor for a relation. diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 654464c631..b4918802ed 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -449,14 +449,6 @@ CREATE TABLE list_parted ( CREATE TABLE part_1 PARTITION OF list_parted FOR VALUES IN ('1'); CREATE TABLE part_2 PARTITION OF list_parted FOR VALUES IN (2); CREATE TABLE part_null PARTITION OF list_parted FOR VALUES IN (null); -CREATE TABLE fail_part PARTITION OF list_parted FOR VALUES IN (int '1'); -ERROR: syntax error at or near "int" -LINE 1: ... fail_part PARTITION OF list_parted FOR VALUES IN (int '1'); - ^ -CREATE TABLE fail_part PARTITION OF list_parted FOR VALUES IN ('1'::int); -ERROR: syntax error at or near "::" -LINE 1: ...fail_part PARTITION OF list_parted FOR VALUES IN ('1'::int); - ^ -- syntax does not allow empty list of values for list partitions CREATE TABLE fail_part PARTITION OF list_parted FOR VALUES IN (); ERROR: syntax error at or near ")" @@ -490,12 +482,8 @@ 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'); +CREATE TABLE moneyp_11 PARTITION OF moneyp FOR VALUES IN ('11'); +CREATE TABLE moneyp_12 PARTITION OF moneyp FOR VALUES IN (to_char(12, '99')::int); DROP TABLE moneyp; -- immutable cast should work, though CREATE TABLE bigintp ( @@ -683,6 +671,26 @@ ERROR: modulus for hash partition must be a positive integer -- remainder must be greater than or equal to zero and less than modulus CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 8, REMAINDER 8); ERROR: remainder for hash partition must be less than modulus +-- check for collation handling +CREATE TABLE col_parted ( + a varchar +) PARTITION BY LIST (a); +CREATE TABLE fail_part PARTITION OF col_parted FOR VALUES IN (('a' collate "en_US")); +ERROR: collation mismatch between partition key expression (100) and partition bound value (12638) +LINE 1: ...fail_part PARTITION OF col_parted FOR VALUES IN (('a' collat... + ^ +CREATE TABLE success_part PARTITION OF col_parted FOR VALUES IN ('a'); +DROP TABLE col_parted; +CREATE TABLE col_parted ( + a varchar collate "en_US" +) PARTITION BY LIST (a); +CREATE TABLE fail_part PARTITION OF col_parted FOR VALUES IN (('a' collate "en_GB")); +ERROR: collation mismatch between partition key expression (12638) and partition bound value (12631) +LINE 1: ...fail_part PARTITION OF col_parted FOR VALUES IN (('a' collat... + ^ +CREATE TABLE success_part PARTITION OF col_parted FOR VALUES IN ('a'); +CREATE TABLE success_part2 PARTITION OF col_parted FOR VALUES IN (('b' collate "en_US")); +DROP TABLE col_parted; -- check schema propagation from parent CREATE TABLE parted ( a text, diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index 54694347ae..e979b6866e 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -432,8 +432,6 @@ CREATE TABLE list_parted ( CREATE TABLE part_1 PARTITION OF list_parted FOR VALUES IN ('1'); CREATE TABLE part_2 PARTITION OF list_parted FOR VALUES IN (2); CREATE TABLE part_null PARTITION OF list_parted FOR VALUES IN (null); -CREATE TABLE fail_part PARTITION OF list_parted FOR VALUES IN (int '1'); -CREATE TABLE fail_part PARTITION OF list_parted FOR VALUES IN ('1'::int); -- syntax does not allow empty list of values for list partitions CREATE TABLE fail_part PARTITION OF list_parted FOR VALUES IN (); @@ -458,7 +456,8 @@ 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'); +CREATE TABLE moneyp_11 PARTITION OF moneyp FOR VALUES IN ('11'); +CREATE TABLE moneyp_12 PARTITION OF moneyp FOR VALUES IN (to_char(12, '99')::int); DROP TABLE moneyp; -- immutable cast should work, though @@ -620,6 +619,22 @@ CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 0, REM -- remainder must be greater than or equal to zero and less than modulus CREATE TABLE fail_part PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 8, REMAINDER 8); +-- check for collation handling +CREATE TABLE col_parted ( + a varchar +) PARTITION BY LIST (a); +CREATE TABLE fail_part PARTITION OF col_parted FOR VALUES IN (('a' collate "en_US")); +CREATE TABLE success_part PARTITION OF col_parted FOR VALUES IN ('a'); +DROP TABLE col_parted; + +CREATE TABLE col_parted ( + a varchar collate "en_US" +) PARTITION BY LIST (a); +CREATE TABLE fail_part PARTITION OF col_parted FOR VALUES IN (('a' collate "en_GB")); +CREATE TABLE success_part PARTITION OF col_parted FOR VALUES IN ('a'); +CREATE TABLE success_part2 PARTITION OF col_parted FOR VALUES IN (('b' collate "en_US")); +DROP TABLE col_parted; + -- check schema propagation from parent CREATE TABLE parted (
pgsql-hackers by date: