diff options
Diffstat (limited to 'src/backend/parser/parse_coerce.c')
-rw-r--r-- | src/backend/parser/parse_coerce.c | 132 |
1 files changed, 131 insertions, 1 deletions
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 5b0dc1420d0..2fd808d26b2 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -16,6 +16,7 @@ #include "catalog/pg_cast.h" #include "catalog/pg_class.h" +#include "catalog/pg_collation.h" #include "catalog/pg_inherits_fn.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" @@ -216,6 +217,7 @@ coerce_type(ParseState *pstate, Node *node, newcon->consttype = baseTypeId; newcon->consttypmod = inputTypeMod; + newcon->constcollid = get_typcollation(newcon->consttype); newcon->constlen = typeLen(targetType); newcon->constbyval = typeByVal(targetType); newcon->constisnull = con->constisnull; @@ -277,6 +279,14 @@ coerce_type(ParseState *pstate, Node *node, if (result) return result; } + if (IsA(node, CollateClause)) + { + CollateClause *cc = (CollateClause *) node; + + cc->arg = (Expr *) coerce_type(pstate, (Node *) cc->arg, inputTypeId, targetTypeId, targetTypeMod, + ccontext, cformat, location); + return (Node *) cc; + } pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext, &funcId); if (pathtype != COERCION_PATH_NONE) @@ -718,6 +728,7 @@ build_coercion_expression(Node *node, FuncExpr *fexpr; List *args; Const *cons; + Oid collation; Assert(OidIsValid(funcId)); @@ -749,7 +760,9 @@ build_coercion_expression(Node *node, args = lappend(args, cons); } - fexpr = makeFuncExpr(funcId, targetTypeId, args, cformat); + collation = coercion_expression_result_collation(targetTypeId, node); + + fexpr = makeFuncExpr(funcId, targetTypeId, args, collation, cformat); fexpr->location = location; return (Node *) fexpr; } @@ -2081,3 +2094,120 @@ typeIsOfTypedTable(Oid reltypeId, Oid reloftypeId) return result; } + + +/* + * select_common_collation() -- determine one collation to apply for + * an expression node, for evaluating the expression itself or to + * label the result of the expression node. + * + * none_ok means that it is permitted to return "no" collation. It is + * then not possible to sort the result value of whatever expression + * is applying this. none_ok = true reflects the rules of SQL + * standard clause "Result of data type combinations", none_ok = false + * reflects the rules of clause "Collation determination" (in some + * cases invoked via "Grouping operations"). + */ +Oid +select_common_collation(ParseState *pstate, List *exprs, bool none_ok) +{ + ListCell *lc; + + /* + * Check if there are any explicit collation derivations. If so, + * they must all be the same. + */ + foreach(lc, exprs) + { + Node *pexpr = (Node *) lfirst(lc); + Oid pcoll = exprCollation(pexpr); + bool pexplicit = IsA(pexpr, CollateClause); + + if (pcoll && pexplicit) + { + ListCell *lc2; + for_each_cell(lc2, lnext(lc)) + { + Node *nexpr = (Node *) lfirst(lc2); + Oid ncoll = exprCollation(nexpr); + bool nexplicit = IsA(nexpr, CollateClause); + + if (!ncoll || !nexplicit) + continue; + + if (ncoll != pcoll) + ereport(ERROR, + (errcode(ERRCODE_COLLATION_MISMATCH), + errmsg("collation mismatch between explicit collations \"%s\" and \"%s\"", + get_collation_name(pcoll), + get_collation_name(ncoll)), + parser_errposition(pstate, exprLocation(nexpr)))); + } + + return pcoll; + } + } + + /* + * Check if there are any implicit collation derivations. + */ + foreach(lc, exprs) + { + Node *pexpr = (Node *) lfirst(lc); + Oid pcoll = exprCollation(pexpr); + + if (pcoll && pcoll != DEFAULT_COLLATION_OID) + { + ListCell *lc2; + for_each_cell(lc2, lnext(lc)) + { + Node *nexpr = (Node *) lfirst(lc2); + Oid ncoll = exprCollation(nexpr); + + if (!ncoll || ncoll == DEFAULT_COLLATION_OID) + continue; + + if (ncoll != pcoll) + { + if (none_ok) + return InvalidOid; + ereport(ERROR, + (errcode(ERRCODE_COLLATION_MISMATCH), + errmsg("collation mismatch between implicit collations \"%s\" and \"%s\"", + get_collation_name(pcoll), + get_collation_name(ncoll)), + errhint("You can override the collation by applying the COLLATE clause to one or both expressions."), + parser_errposition(pstate, exprLocation(nexpr)))); + } + } + + return pcoll; + } + } + + foreach(lc, exprs) + { + Node *pexpr = (Node *) lfirst(lc); + Oid pcoll = exprCollation(pexpr); + + if (pcoll == DEFAULT_COLLATION_OID) + { + ListCell *lc2; + for_each_cell(lc2, lnext(lc)) + { + Node *nexpr = (Node *) lfirst(lc2); + Oid ncoll = exprCollation(nexpr); + + if (ncoll != pcoll) + break; + } + + return pcoll; + } + } + + /* + * Else use default + */ + return InvalidOid; +} |