From 8acdb8bf9cebc42cee5aa96a2d594756b44173c9 Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Fri, 11 Mar 2011 16:27:51 -0500 Subject: [PATCH] Split CollateClause into separate raw and analyzed node types. CollateClause is now used only in raw grammar output, and CollateExpr after parse analysis. This is for clarity and to avoid carrying collation names in post-analysis parse trees: that's both wasteful and possibly misleading, since the collation's name could be changed while the parsetree still exists. Also, clean up assorted infelicities and omissions in processing of the node type. --- src/backend/catalog/dependency.c | 43 ++++++++----- src/backend/commands/typecmds.c | 2 +- src/backend/executor/execQual.c | 54 ++++++++--------- src/backend/nodes/copyfuncs.c | 21 ++++++- src/backend/nodes/equalfuncs.c | 16 ++++- src/backend/nodes/nodeFuncs.c | 45 ++++++++------ src/backend/nodes/outfuncs.c | 24 ++++++-- src/backend/nodes/readfuncs.c | 35 ++++++----- src/backend/optimizer/util/clauses.c | 48 +++++++++++++++ src/backend/parser/gram.y | 10 ++-- src/backend/parser/parse_coerce.c | 16 +++-- src/backend/parser/parse_expr.c | 10 ++-- src/backend/parser/parse_target.c | 2 +- src/backend/parser/parse_type.c | 2 +- src/backend/parser/parse_utilcmd.c | 2 +- src/backend/utils/adt/ruleutils.c | 60 ++++++++++++++----- src/include/catalog/catversion.h | 2 +- src/include/nodes/nodes.h | 3 +- src/include/nodes/parsenodes.h | 11 ++++ src/include/nodes/primnodes.h | 24 ++++---- src/pl/plpgsql/src/pl_exec.c | 3 + .../regress/expected/collate.linux.utf8.out | 4 +- 22 files changed, 298 insertions(+), 139 deletions(-) diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index bce0e076837..9e2028a64ff 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -1357,6 +1357,9 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender, * on the datatype, and OpExpr nodes depend on the operator which depends on * the datatype. However we do need a type dependency if there is no such * indirect dependency, as for example in Const and CoerceToDomain nodes. + * + * Similarly, we don't need to create dependencies on collations except where + * the collation is being freshly introduced to the expression. */ static bool find_expr_references_walker(Node *node, @@ -1425,7 +1428,15 @@ find_expr_references_walker(Node *node, /* A constant must depend on the constant's datatype */ add_object_address(OCLASS_TYPE, con->consttype, 0, context->addrs); - if (OidIsValid(con->constcollid)) + + /* + * We must also depend on the constant's collation: it could be + * different from the datatype's, if a CollateExpr was const-folded + * to a simple constant. However we can save work in the most common + * case where the collation is "default", since we know that's pinned. + */ + if (OidIsValid(con->constcollid) && + con->constcollid != DEFAULT_COLLATION_OID) add_object_address(OCLASS_COLLATION, con->constcollid, 0, context->addrs); @@ -1494,7 +1505,9 @@ find_expr_references_walker(Node *node, /* A parameter must depend on the parameter's datatype */ add_object_address(OCLASS_TYPE, param->paramtype, 0, context->addrs); - if (OidIsValid(param->paramcollation)) + /* and its collation, just as for Consts */ + if (OidIsValid(param->paramcollation) && + param->paramcollation != DEFAULT_COLLATION_OID) add_object_address(OCLASS_COLLATION, param->paramcollation, 0, context->addrs); } @@ -1567,13 +1580,6 @@ find_expr_references_walker(Node *node, add_object_address(OCLASS_TYPE, relab->resulttype, 0, context->addrs); } - else if (IsA(node, CollateClause)) - { - CollateClause *coll = (CollateClause *) node; - - add_object_address(OCLASS_COLLATION, coll->collOid, 0, - context->addrs); - } else if (IsA(node, CoerceViaIO)) { CoerceViaIO *iocoerce = (CoerceViaIO *) node; @@ -1601,6 +1607,13 @@ find_expr_references_walker(Node *node, add_object_address(OCLASS_TYPE, cvt->resulttype, 0, context->addrs); } + else if (IsA(node, CollateExpr)) + { + CollateExpr *coll = (CollateExpr *) node; + + add_object_address(OCLASS_COLLATION, coll->collOid, 0, + context->addrs); + } else if (IsA(node, RowExpr)) { RowExpr *rowexpr = (RowExpr *) node; @@ -1652,10 +1665,11 @@ find_expr_references_walker(Node *node, /* * Add whole-relation refs for each plain relation mentioned in the - * subquery's rtable, as well as datatype refs for any datatypes used - * as a RECORD function's output. (Note: query_tree_walker takes care - * of recursing into RTE_FUNCTION RTEs, subqueries, etc, so no need to - * do that here. But keep it from looking at join alias lists.) + * subquery's rtable, as well as refs for any datatypes and collations + * used in a RECORD function's output. (Note: query_tree_walker takes + * care of recursing into RTE_FUNCTION RTEs, subqueries, etc, so no + * need to do that here. But keep it from looking at join alias + * lists.) */ foreach(lc, query->rtable) { @@ -1678,7 +1692,8 @@ find_expr_references_walker(Node *node, { Oid collid = lfirst_oid(ct); - if (OidIsValid(collid)) + if (OidIsValid(collid) && + collid != DEFAULT_COLLATION_OID) add_object_address(OCLASS_COLLATION, collid, 0, context->addrs); } diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 3513256b9a5..ee3bca17d17 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -831,7 +831,7 @@ DefineDomain(CreateDomainStmt *stmt) */ baseColl = baseType->typcollation; if (stmt->collClause) - domaincoll = get_collation_oid(stmt->collClause->collnames, false); + domaincoll = get_collation_oid(stmt->collClause->collname, false); else domaincoll = baseColl; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 2b5dd2dbf85..0faf52dfd79 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -120,6 +120,9 @@ static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, static Datum ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); +static Datum ExecEvalCollateExpr(GenericExprState *exprstate, + ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalCaseTestExpr(ExprState *exprstate, @@ -166,9 +169,6 @@ static Datum ExecEvalFieldStore(FieldStoreState *fstate, static Datum ExecEvalRelabelType(GenericExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); -static Datum ExecEvalCollateClause(GenericExprState *exprstate, - ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalCoerceViaIO(CoerceViaIOState *iostate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); @@ -2753,6 +2753,20 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate, return HeapTupleGetDatum(result); } +/* ---------------------------------------------------------------- + * ExecEvalCollateExpr + * + * Evaluate a CollateExpr node. + * ---------------------------------------------------------------- + */ +static Datum +ExecEvalCollateExpr(GenericExprState *exprstate, + ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone) +{ + return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone); +} + /* ---------------------------------------------------------------- * ExecEvalCase * @@ -4028,20 +4042,6 @@ ExecEvalRelabelType(GenericExprState *exprstate, return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone); } -/* ---------------------------------------------------------------- - * ExecEvalCollateClause - * - * Evaluate a CollateClause node. - * ---------------------------------------------------------------- - */ -static Datum -ExecEvalCollateClause(GenericExprState *exprstate, - ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) -{ - return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone); -} - /* ---------------------------------------------------------------- * ExecEvalCoerceViaIO * @@ -4501,16 +4501,6 @@ ExecInitExpr(Expr *node, PlanState *parent) state = (ExprState *) gstate; } break; - case T_CollateClause: - { - CollateClause *collate = (CollateClause *) node; - GenericExprState *gstate = makeNode(GenericExprState); - - gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCollateClause; - gstate->arg = ExecInitExpr(collate->arg, parent); - state = (ExprState *) gstate; - } - break; case T_CoerceViaIO: { CoerceViaIO *iocoerce = (CoerceViaIO *) node; @@ -4561,6 +4551,16 @@ ExecInitExpr(Expr *node, PlanState *parent) state = (ExprState *) cstate; } break; + case T_CollateExpr: + { + CollateExpr *collate = (CollateExpr *) node; + GenericExprState *gstate = makeNode(GenericExprState); + + gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCollateExpr; + gstate->arg = ExecInitExpr(collate->arg, parent); + state = (ExprState *) gstate; + } + break; case T_CaseExpr: { CaseExpr *caseexpr = (CaseExpr *) node; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index b948af604d8..c0490e93ea5 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -1434,6 +1434,21 @@ _copyConvertRowtypeExpr(ConvertRowtypeExpr *from) return newnode; } +/* + * _copyCollateExpr + */ +static CollateExpr * +_copyCollateExpr(CollateExpr *from) +{ + CollateExpr *newnode = makeNode(CollateExpr); + + COPY_NODE_FIELD(arg); + COPY_SCALAR_FIELD(collOid); + COPY_LOCATION_FIELD(location); + + return newnode; +} + /* * _copyCaseExpr */ @@ -2260,8 +2275,7 @@ _copyCollateClause(CollateClause *from) CollateClause *newnode = makeNode(CollateClause); COPY_NODE_FIELD(arg); - COPY_NODE_FIELD(collnames); - COPY_SCALAR_FIELD(collOid); + COPY_NODE_FIELD(collname); COPY_LOCATION_FIELD(location); return newnode; @@ -4017,6 +4031,9 @@ copyObject(void *from) case T_ConvertRowtypeExpr: retval = _copyConvertRowtypeExpr(from); break; + case T_CollateExpr: + retval = _copyCollateExpr(from); + break; case T_CaseExpr: retval = _copyCaseExpr(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index c8ee4744364..3726006f1d2 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -493,6 +493,16 @@ _equalConvertRowtypeExpr(ConvertRowtypeExpr *a, ConvertRowtypeExpr *b) return true; } +static bool +_equalCollateExpr(CollateExpr *a, CollateExpr *b) +{ + COMPARE_NODE_FIELD(arg); + COMPARE_SCALAR_FIELD(collOid); + COMPARE_LOCATION_FIELD(location); + + return true; +} + static bool _equalCaseExpr(CaseExpr *a, CaseExpr *b) { @@ -2149,8 +2159,7 @@ static bool _equalCollateClause(CollateClause *a, CollateClause *b) { COMPARE_NODE_FIELD(arg); - COMPARE_NODE_FIELD(collnames); - COMPARE_SCALAR_FIELD(collOid); + COMPARE_NODE_FIELD(collname); COMPARE_LOCATION_FIELD(location); return true; @@ -2583,6 +2592,9 @@ equal(void *a, void *b) case T_ConvertRowtypeExpr: retval = _equalConvertRowtypeExpr(a, b); break; + case T_CollateExpr: + retval = _equalCollateExpr(a, b); + break; case T_CaseExpr: retval = _equalCaseExpr(a, b); break; diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index c3c5d8e6e5c..5394851a1f5 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -162,9 +162,6 @@ exprType(Node *expr) case T_RelabelType: type = ((RelabelType *) expr)->resulttype; break; - case T_CollateClause: - type = exprType((Node *) ((CollateClause *) expr)->arg); - break; case T_CoerceViaIO: type = ((CoerceViaIO *) expr)->resulttype; break; @@ -174,6 +171,9 @@ exprType(Node *expr) case T_ConvertRowtypeExpr: type = ((ConvertRowtypeExpr *) expr)->resulttype; break; + case T_CollateExpr: + type = exprType((Node *) ((CollateExpr *) expr)->arg); + break; case T_CaseExpr: type = ((CaseExpr *) expr)->casetype; break; @@ -321,6 +321,8 @@ exprTypmod(Node *expr) return ((RelabelType *) expr)->resulttypmod; case T_ArrayCoerceExpr: return ((ArrayCoerceExpr *) expr)->resulttypmod; + case T_CollateExpr: + return exprTypmod((Node *) ((CollateExpr *) expr)->arg); case T_CaseExpr: { /* @@ -571,9 +573,6 @@ exprCollation(Node *expr) case T_RelabelType: coll = exprCollation((Node *) ((RelabelType *) expr)->arg); break; - case T_CollateClause: - coll = ((CollateClause *) expr)->collOid; - break; case T_CoerceViaIO: { CoerceViaIO *cvio = (CoerceViaIO *) expr; @@ -592,6 +591,9 @@ exprCollation(Node *expr) coll = coercion_expression_result_collation(cre->resulttype, (Node *) cre->arg); break; } + case T_CollateExpr: + coll = ((CollateExpr *) expr)->collOid; + break; case T_CaseExpr: coll = ((CaseExpr *) expr)->casecollation; break; @@ -989,6 +991,10 @@ exprLocation(Node *expr) exprLocation((Node *) cexpr->arg)); } break; + case T_CollateExpr: + /* just use argument's location */ + loc = exprLocation((Node *) ((CollateExpr *) expr)->arg); + break; case T_CaseExpr: /* CASE keyword should always be the first thing */ loc = ((CaseExpr *) expr)->location; @@ -1122,7 +1128,8 @@ exprLocation(Node *expr) } break; case T_CollateClause: - loc = ((CollateClause *) expr)->location; + /* just use argument's location */ + loc = exprLocation(((CollateClause *) expr)->arg); break; case T_SortBy: /* just use argument's location (ignore operator, if any) */ @@ -1436,14 +1443,14 @@ expression_tree_walker(Node *node, break; case T_RelabelType: return walker(((RelabelType *) node)->arg, context); - case T_CollateClause: - return walker(((CollateClause *) node)->arg, context); case T_CoerceViaIO: return walker(((CoerceViaIO *) node)->arg, context); case T_ArrayCoerceExpr: return walker(((ArrayCoerceExpr *) node)->arg, context); case T_ConvertRowtypeExpr: return walker(((ConvertRowtypeExpr *) node)->arg, context); + case T_CollateExpr: + return walker(((CollateExpr *) node)->arg, context); case T_CaseExpr: { CaseExpr *caseexpr = (CaseExpr *) node; @@ -1993,16 +2000,6 @@ expression_tree_mutator(Node *node, return (Node *) newnode; } break; - case T_CollateClause: - { - CollateClause *collate = (CollateClause *) node; - CollateClause *newnode; - - FLATCOPY(newnode, collate, CollateClause); - MUTATE(newnode->arg, collate->arg, Expr *); - return (Node *) newnode; - } - break; case T_CoerceViaIO: { CoerceViaIO *iocoerce = (CoerceViaIO *) node; @@ -2033,6 +2030,16 @@ expression_tree_mutator(Node *node, return (Node *) newnode; } break; + case T_CollateExpr: + { + CollateExpr *collate = (CollateExpr *) node; + CollateExpr *newnode; + + FLATCOPY(newnode, collate, CollateExpr); + MUTATE(newnode->arg, collate->arg, Expr *); + return (Node *) newnode; + } + break; case T_CaseExpr: { CaseExpr *caseexpr = (CaseExpr *) node; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 06fd7ff818e..d56e4dac011 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1195,6 +1195,16 @@ _outConvertRowtypeExpr(StringInfo str, ConvertRowtypeExpr *node) WRITE_LOCATION_FIELD(location); } +static void +_outCollateExpr(StringInfo str, CollateExpr *node) +{ + WRITE_NODE_TYPE("COLLATE"); + + WRITE_NODE_FIELD(arg); + WRITE_OID_FIELD(collOid); + WRITE_LOCATION_FIELD(location); +} + static void _outCaseExpr(StringInfo str, CaseExpr *node) { @@ -2104,11 +2114,10 @@ _outTypeCast(StringInfo str, TypeCast *node) static void _outCollateClause(StringInfo str, CollateClause *node) { - WRITE_NODE_TYPE("COLLATE"); + WRITE_NODE_TYPE("COLLATECLAUSE"); WRITE_NODE_FIELD(arg); - WRITE_NODE_FIELD(collnames); - WRITE_OID_FIELD(collOid); + WRITE_NODE_FIELD(collname); WRITE_LOCATION_FIELD(location); } @@ -2829,9 +2838,6 @@ _outNode(StringInfo str, void *obj) case T_RelabelType: _outRelabelType(str, obj); break; - case T_CollateClause: - _outCollateClause(str, obj); - break; case T_CoerceViaIO: _outCoerceViaIO(str, obj); break; @@ -2841,6 +2847,9 @@ _outNode(StringInfo str, void *obj) case T_ConvertRowtypeExpr: _outConvertRowtypeExpr(str, obj); break; + case T_CollateExpr: + _outCollateExpr(str, obj); + break; case T_CaseExpr: _outCaseExpr(str, obj); break; @@ -3020,6 +3029,9 @@ _outNode(StringInfo str, void *obj) case T_TypeCast: _outTypeCast(str, obj); break; + case T_CollateClause: + _outCollateClause(str, obj); + break; case T_IndexElem: _outIndexElem(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 09c5e25012c..6da61285b00 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -743,22 +743,6 @@ _readRelabelType(void) READ_DONE(); } -/* - * _readCollateClause - */ -static CollateClause * -_readCollateClause(void) -{ - READ_LOCALS(CollateClause); - - READ_NODE_FIELD(arg); - READ_NODE_FIELD(collnames); - READ_OID_FIELD(collOid); - READ_LOCATION_FIELD(location); - - READ_DONE(); -} - /* * _readCoerceViaIO */ @@ -810,6 +794,21 @@ _readConvertRowtypeExpr(void) READ_DONE(); } +/* + * _readCollateExpr + */ +static CollateExpr * +_readCollateExpr(void) +{ + READ_LOCALS(CollateExpr); + + READ_NODE_FIELD(arg); + READ_OID_FIELD(collOid); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + /* * _readCaseExpr */ @@ -1286,14 +1285,14 @@ parseNodeString(void) return_value = _readFieldStore(); else if (MATCH("RELABELTYPE", 11)) return_value = _readRelabelType(); - else if (MATCH("COLLATE", 7)) - return_value = _readCollateClause(); else if (MATCH("COERCEVIAIO", 11)) return_value = _readCoerceViaIO(); else if (MATCH("ARRAYCOERCEEXPR", 15)) return_value = _readArrayCoerceExpr(); else if (MATCH("CONVERTROWTYPEEXPR", 18)) return_value = _readConvertRowtypeExpr(); + else if (MATCH("COLLATE", 7)) + return_value = _readCollateExpr(); else if (MATCH("CASE", 4)) return_value = _readCaseExpr(); else if (MATCH("WHEN", 4)) diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index fa0952618b1..8503792df44 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -1308,6 +1308,12 @@ find_nonnullable_rels_walker(Node *node, bool top_level) result = find_nonnullable_rels_walker((Node *) expr->arg, top_level); } + else if (IsA(node, CollateExpr)) + { + CollateExpr *expr = (CollateExpr *) node; + + result = find_nonnullable_rels_walker((Node *) expr->arg, top_level); + } else if (IsA(node, NullTest)) { /* IS NOT NULL can be considered strict, but only at top level */ @@ -1510,6 +1516,12 @@ find_nonnullable_vars_walker(Node *node, bool top_level) result = find_nonnullable_vars_walker((Node *) expr->arg, top_level); } + else if (IsA(node, CollateExpr)) + { + CollateExpr *expr = (CollateExpr *) node; + + result = find_nonnullable_vars_walker((Node *) expr->arg, top_level); + } else if (IsA(node, NullTest)) { /* IS NOT NULL can be considered strict, but only at top level */ @@ -2580,6 +2592,42 @@ eval_const_expressions_mutator(Node *node, /* Else we must return the partially-simplified node */ return (Node *) newexpr; } + if (IsA(node, CollateExpr)) + { + /* + * If we can simplify the input to a constant, then we don't need the + * CollateExpr node anymore: just change the constcollid field of the + * Const node. Otherwise, must copy the CollateExpr node. + */ + CollateExpr *collate = (CollateExpr *) node; + Node *arg; + + arg = eval_const_expressions_mutator((Node *) collate->arg, + context); + + /* + * If we find stacked CollateExprs, we can discard all but the top one. + */ + while (arg && IsA(arg, CollateExpr)) + arg = (Node *) ((CollateExpr *) arg)->arg; + + if (arg && IsA(arg, Const)) + { + Const *con = (Const *) arg; + + con->constcollid = collate->collOid; + return (Node *) con; + } + else + { + CollateExpr *newcollate = makeNode(CollateExpr); + + newcollate->arg = (Expr *) arg; + newcollate->collOid = collate->collOid; + newcollate->location = collate->location; + return (Node *) newcollate; + } + } if (IsA(node, CaseExpr)) { /*---------- diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 373d2adc71c..1633499f939 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -1993,8 +1993,7 @@ opt_collate_clause: { CollateClause *n = makeNode(CollateClause); n->arg = NULL; - n->collnames = $2; - n->collOid = InvalidOid; + n->collname = $2; n->location = @1; $$ = (Node *) n; } @@ -2537,8 +2536,7 @@ ColConstraint: */ CollateClause *n = makeNode(CollateClause); n->arg = NULL; - n->collnames = $2; - n->collOid = InvalidOid; + n->collname = $2; n->location = @1; $$ = (Node *) n; } @@ -9690,8 +9688,8 @@ a_expr: c_expr { $$ = $1; } | a_expr COLLATE any_name { CollateClause *n = makeNode(CollateClause); - n->arg = (Expr *) $1; - n->collnames = $3; + n->arg = $1; + n->collname = $3; n->location = @2; $$ = (Node *) n; } diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 2fd808d26b2..6aff34dd90d 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -279,11 +279,17 @@ coerce_type(ParseState *pstate, Node *node, if (result) return result; } - if (IsA(node, CollateClause)) + if (IsA(node, CollateExpr)) { - CollateClause *cc = (CollateClause *) node; + /* + * XXX very ugly kluge to push the coercion underneath the CollateExpr. + * This needs to be rethought, as it almost certainly doesn't cover + * all cases. + */ + CollateExpr *cc = (CollateExpr *) node; - cc->arg = (Expr *) coerce_type(pstate, (Node *) cc->arg, inputTypeId, targetTypeId, targetTypeMod, + cc->arg = (Expr *) coerce_type(pstate, (Node *) cc->arg, + inputTypeId, targetTypeId, targetTypeMod, ccontext, cformat, location); return (Node *) cc; } @@ -2121,7 +2127,7 @@ select_common_collation(ParseState *pstate, List *exprs, bool none_ok) { Node *pexpr = (Node *) lfirst(lc); Oid pcoll = exprCollation(pexpr); - bool pexplicit = IsA(pexpr, CollateClause); + bool pexplicit = IsA(pexpr, CollateExpr); if (pcoll && pexplicit) { @@ -2130,7 +2136,7 @@ select_common_collation(ParseState *pstate, List *exprs, bool none_ok) { Node *nexpr = (Node *) lfirst(lc2); Oid ncoll = exprCollation(nexpr); - bool nexplicit = IsA(nexpr, CollateClause); + bool nexplicit = IsA(nexpr, CollateExpr); if (!ncoll || !nexplicit) continue; diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 7a4f8cc2497..17bd2bf50ae 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -318,6 +318,7 @@ transformExpr(ParseState *pstate, Node *expr) case T_CoerceViaIO: case T_ArrayCoerceExpr: case T_ConvertRowtypeExpr: + case T_CollateExpr: case T_CaseTestExpr: case T_ArrayExpr: case T_CoerceToDomain: @@ -2103,11 +2104,11 @@ transformTypeCast(ParseState *pstate, TypeCast *tc) static Node * transformCollateClause(ParseState *pstate, CollateClause *c) { - CollateClause *newc; + CollateExpr *newc; Oid argtype; - newc = makeNode(CollateClause); - newc->arg = (Expr *) transformExpr(pstate, (Node *) c->arg); + newc = makeNode(CollateExpr); + newc->arg = (Expr *) transformExpr(pstate, c->arg); argtype = exprType((Node *) newc->arg); /* @@ -2121,8 +2122,7 @@ transformCollateClause(ParseState *pstate, CollateClause *c) format_type_be(argtype)), parser_errposition(pstate, c->location))); - newc->collOid = LookupCollation(pstate, c->collnames, c->location); - newc->collnames = c->collnames; + newc->collOid = LookupCollation(pstate, c->collname, c->location); newc->location = c->location; return (Node *) newc; diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index c0eaea71a66..fd1529fb3f9 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1583,7 +1583,7 @@ FigureColnameInternal(Node *node, char **name) } break; case T_CollateClause: - return FigureColnameInternal((Node *) ((CollateClause *) node)->arg, name); + return FigureColnameInternal(((CollateClause *) node)->arg, name); case T_CaseExpr: strength = FigureColnameInternal((Node *) ((CaseExpr *) node)->defresult, name); diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 2ba9bf51816..f413593f602 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -471,7 +471,7 @@ GetColumnDefCollation(ParseState *pstate, ColumnDef *coldef, Oid typeOid) { /* We have a raw COLLATE clause, so look up the collation */ location = coldef->collClause->location; - result = LookupCollation(pstate, coldef->collClause->collnames, + result = LookupCollation(pstate, coldef->collClause->collname, location); } else if (OidIsValid(coldef->collOid)) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index e876853af02..06baf89886a 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -2467,7 +2467,7 @@ transformColumnType(CreateStmtContext *cxt, ColumnDef *column) Oid collOid; collOid = LookupCollation(cxt->pstate, - column->collClause->collnames, + column->collClause->collname, column->collClause->location); /* Complain if COLLATE is applied to an uncollatable type */ if (!OidIsValid(typtup->typcollation)) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 7cbd0222cb2..ac0c53a7f32 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -226,6 +226,7 @@ static void get_coercion_expr(Node *arg, deparse_context *context, Node *parentNode); static void get_const_expr(Const *constval, deparse_context *context, int showtype); +static void get_const_collation(Const *constval, deparse_context *context); static void simple_quote_literal(StringInfo buf, const char *val); static void get_sublink_expr(SubLink *sublink, deparse_context *context); static void get_from_clause(Query *query, const char *prefix, @@ -5075,21 +5076,6 @@ get_rule_expr(Node *node, deparse_context *context, } break; - case T_CollateClause: - { - CollateClause *collate = (CollateClause *) node; - Node *arg = (Node *) collate->arg; - - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren(arg, context, showimplicit, node); - appendStringInfo(buf, " COLLATE %s", - generate_collation_name(collate->collOid)); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - } - break; - case T_CoerceViaIO: { CoerceViaIO *iocoerce = (CoerceViaIO *) node; @@ -5152,6 +5138,21 @@ get_rule_expr(Node *node, deparse_context *context, } break; + case T_CollateExpr: + { + CollateExpr *collate = (CollateExpr *) node; + Node *arg = (Node *) collate->arg; + + if (!PRETTY_PAREN(context)) + appendStringInfoChar(buf, '('); + get_rule_expr_paren(arg, context, showimplicit, node); + appendStringInfo(buf, " COLLATE %s", + generate_collation_name(collate->collOid)); + if (!PRETTY_PAREN(context)) + appendStringInfoChar(buf, ')'); + } + break; + case T_CaseExpr: { CaseExpr *caseexpr = (CaseExpr *) node; @@ -5974,6 +5975,10 @@ get_coercion_expr(Node *arg, deparse_context *context, * showtype can be -1 to never show "::typename" decoration, or +1 to always * show it, or 0 to show it only if the constant wouldn't be assumed to be * the right type by default. + * + * If the Const's collation isn't default for its type, show that too. + * This can only happen in trees that have been through constant-folding. + * We assume we don't need to do this when showtype is -1. * ---------- */ static void @@ -5994,9 +5999,12 @@ get_const_expr(Const *constval, deparse_context *context, int showtype) */ appendStringInfo(buf, "NULL"); if (showtype >= 0) + { appendStringInfo(buf, "::%s", format_type_with_typemod(constval->consttype, constval->consttypmod)); + get_const_collation(constval, context); + } return; } @@ -6097,6 +6105,28 @@ get_const_expr(Const *constval, deparse_context *context, int showtype) appendStringInfo(buf, "::%s", format_type_with_typemod(constval->consttype, constval->consttypmod)); + + get_const_collation(constval, context); +} + +/* + * helper for get_const_expr: append COLLATE if needed + */ +static void +get_const_collation(Const *constval, deparse_context *context) +{ + StringInfo buf = context->buf; + + if (OidIsValid(constval->constcollid)) + { + Oid typcollation = get_typcollation(constval->consttype); + + if (constval->constcollid != typcollation) + { + appendStringInfo(buf, " COLLATE %s", + generate_collation_name(constval->constcollid)); + } + } } /* diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 27ce7955772..657015616cd 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201103111 +#define CATALOG_VERSION_NO 201103112 #endif diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index cbaf123ee9d..8ed819b4dd4 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -148,6 +148,7 @@ typedef enum NodeTag T_CoerceViaIO, T_ArrayCoerceExpr, T_ConvertRowtypeExpr, + T_CollateExpr, T_CaseExpr, T_CaseWhen, T_CaseTestExpr, @@ -169,7 +170,6 @@ typedef enum NodeTag T_JoinExpr, T_FromExpr, T_IntoClause, - T_CollateClause, /* * TAGS FOR EXPRESSION STATE NODES (execnodes.h) @@ -377,6 +377,7 @@ typedef enum NodeTag T_A_ArrayExpr, T_ResTarget, T_TypeCast, + T_CollateClause, T_SortBy, T_WindowDef, T_RangeSubselect, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 9d4515cb27f..904bd5e9e18 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -265,6 +265,17 @@ typedef struct TypeCast int location; /* token location, or -1 if unknown */ } TypeCast; +/* + * CollateClause - a COLLATE expression + */ +typedef struct CollateClause +{ + NodeTag type; + Node *arg; /* input expression */ + List *collname; /* possibly-qualified collation name */ + int location; /* token location, or -1 if unknown */ +} CollateClause; + /* * FuncCall - a function or aggregate invocation * diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 8c366df5f56..41fd56e1bf1 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -653,18 +653,6 @@ typedef struct RelabelType int location; /* token location, or -1 if unknown */ } RelabelType; -/* - * CollateClause - COLLATE - */ -typedef struct CollateClause -{ - Expr xpr; - Expr *arg; /* original expression */ - List *collnames; /* assigned collation */ - Oid collOid; /* resolved collation OID */ - int location; /* token location, or -1 if unknown */ -} CollateClause; - /* ---------------- * CoerceViaIO * @@ -730,6 +718,18 @@ typedef struct ConvertRowtypeExpr int location; /* token location, or -1 if unknown */ } ConvertRowtypeExpr; +/*---------- + * CollateExpr - COLLATE + *---------- + */ +typedef struct CollateExpr +{ + Expr xpr; + Expr *arg; /* input expression */ + Oid collOid; /* collation's OID */ + int location; /* token location, or -1 if unknown */ +} CollateExpr; + /*---------- * CaseExpr - a CASE expression * diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 7af6eee088e..689686b7825 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -5350,6 +5350,9 @@ exec_simple_check_node(Node *node) case T_ConvertRowtypeExpr: return exec_simple_check_node((Node *) ((ConvertRowtypeExpr *) node)->arg); + case T_CollateExpr: + return exec_simple_check_node((Node *) ((CollateExpr *) node)->arg); + case T_CaseExpr: { CaseExpr *expr = (CaseExpr *) node; diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index 2102298abaa..53595b6e10c 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -107,11 +107,11 @@ SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US.utf8"; ERROR: collation mismatch between explicit collations "C" and "en_US.utf8" -LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e... +LINE 1: ...ELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLL... ^ SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; ERROR: collation mismatch between explicit collations "C" and "en_US" -LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e... +LINE 1: ...ELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLL... ^ CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE.utf8"; CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE.utf8"; -- fails -- GitLab