aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_coerce.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_coerce.c')
-rw-r--r--src/backend/parser/parse_coerce.c132
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;
+}