From 8acdb8bf9cebc42cee5aa96a2d594756b44173c9 Mon Sep 17 00:00:00 2001 From: Tom Lane 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 bce0e07683..9e2028a64f 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 3513256b9a..ee3bca17d1 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 2b5dd2dbf8..0faf52dfd7 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 b948af604d..c0490e93ea 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 c8ee474436..3726006f1d 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 c3c5d8e6e5..5394851a1f 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 06fd7ff818..d56e4dac01 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 09c5e25012..6da61285b0 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 fa0952618b..8503792df4 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 373d2adc71..1633499f93 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 2fd808d26b..6aff34dd90 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 7a4f8cc249..17bd2bf50a 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 c0eaea71a6..fd1529fb3f 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 2ba9bf5181..f413593f60 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 e876853af0..06baf89886 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 7cbd0222cb..ac0c53a7f3 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 27ce795577..657015616c 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 cbaf123ee9..8ed819b4dd 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 9d4515cb27..904bd5e9e1 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 8c366df5f5..41fd56e1bf 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 7af6eee088..689686b782 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 2102298aba..53595b6e10 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 -- 2.50.0