diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index bce0e076837f57857c8c19d82b08f5cb51b1f766..9e2028a64ff6d2d5c67e5f493ba8271966d70d48 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 3513256b9a54874b9c42f1489aff67c1bbcb01ae..ee3bca17d17a3419b2eb8fdf22fed98e22cfce02 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 2b5dd2dbf85e46efd7f48db684256656d91983f4..0faf52dfd793b1167624f3951bdc6d33330720ed 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 b948af604d8149b9d95f9c923cbb983b2df3eb40..c0490e93ea50c045aff08da3ed6a16bcca5b545b 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 c8ee4744364e7b925415c66c23d0bc2d78b68ffa..3726006f1d2fd5e0eaf77521777a749dd39bf363 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 c3c5d8e6e5c3e910e6caf953d228e5bf13d60a1b..5394851a1f55bfcb744b0680da4883f16a8bdef1 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 06fd7ff818e4ed45c7cbb42ab2ef1f38848839bb..d56e4dac011cd613eaa152df59282cf72ac1890d 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 09c5e25012c21e73fa103afcd2d17f0482e35dbe..6da61285b004570cd91ebf36c1799ded9bfcacff 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 fa0952618b13367029be0ea21712c972d58237b8..8503792df448ccde6b03d728e61d28f6fb81c6a6 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 373d2adc71c90a58318e9c46e0c55024ab7caede..1633499f93974b2e0c1cf634f8ef2738cd292c02 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 2fd808d26b200acaace440b464b5cfee4c975c1b..6aff34dd90d5b2e6124787e242bcc76a476aa318 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 7a4f8cc2497aaf9f669dc3b1d65016a383f425da..17bd2bf50aee9d5322659b94621d4762b6348120 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 c0eaea71a6649cea58ef1a922e8ea7dd94df0e88..fd1529fb3f9e5da9ab2e8975ea8b54cec48c888d 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 2ba9bf5181642d3f4896c25e14f58ab72800dc08..f413593f60220a6cc0e0080ded0836c6f310f513 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 e876853af02c9a37b74e4031ea46a753898b4876..06baf89886a9dd08813f7a0e12571a48d1661809 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 7cbd0222cb2e77b59ec3264c8ffabb68addeb1a5..ac0c53a7f320ed3522e8eb3dca75f6cf3e07dfd3 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 27ce7955772e92038dd8b4bf0cac80864e297e7f..657015616cdd3bd1d86d49dd76d434b8e625fc13 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 cbaf123ee9d11a9914bd0df39cfeb86880e73778..8ed819b4dd40412e0447640457921f6813b021fc 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 9d4515cb27f39f5cae6165eb7385982a67aeb397..904bd5e9e18757a843bec97e275511fe1dbd88ec 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 8c366df5f5619d27e80fb99011fc448425d202aa..41fd56e1bf13e78d9af3ca62712490a1f25160d2 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 7af6eee088e3d7868f7a0e05a3a747eabf539f72..689686b7825656fa3078eb1676d24216d7b94e40 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 2102298abaa10e929c5b5da76d1cefcc9bdd5bc7..53595b6e10cfcad895f7bb1ec323f9e8f112b759 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