aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/catalog/dependency.c43
-rw-r--r--src/backend/commands/typecmds.c2
-rw-r--r--src/backend/executor/execQual.c54
-rw-r--r--src/backend/nodes/copyfuncs.c21
-rw-r--r--src/backend/nodes/equalfuncs.c16
-rw-r--r--src/backend/nodes/nodeFuncs.c45
-rw-r--r--src/backend/nodes/outfuncs.c24
-rw-r--r--src/backend/nodes/readfuncs.c35
-rw-r--r--src/backend/optimizer/util/clauses.c48
-rw-r--r--src/backend/parser/gram.y10
-rw-r--r--src/backend/parser/parse_coerce.c16
-rw-r--r--src/backend/parser/parse_expr.c10
-rw-r--r--src/backend/parser/parse_target.c2
-rw-r--r--src/backend/parser/parse_type.c2
-rw-r--r--src/backend/parser/parse_utilcmd.c2
-rw-r--r--src/backend/utils/adt/ruleutils.c60
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/nodes/nodes.h3
-rw-r--r--src/include/nodes/parsenodes.h11
-rw-r--r--src/include/nodes/primnodes.h24
-rw-r--r--src/pl/plpgsql/src/pl_exec.c3
-rw-r--r--src/test/regress/expected/collate.linux.utf8.out4
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);
@@ -2754,6 +2754,20 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
}
/* ----------------------------------------------------------------
+ * ExecEvalCollateExpr
+ *
+ * Evaluate a CollateExpr node.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalCollateExpr(GenericExprState *exprstate,
+ ExprContext *econtext,
+ bool *isNull, ExprDoneCond *isDone)
+{
+ return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone);
+}
+
+/* ----------------------------------------------------------------
* ExecEvalCase
*
* Evaluate a CASE clause. Will have boolean expressions
@@ -4029,20 +4043,6 @@ ExecEvalRelabelType(GenericExprState *exprstate,
}
/* ----------------------------------------------------------------
- * ExecEvalCollateClause
- *
- * Evaluate a CollateClause node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCollateClause(GenericExprState *exprstate,
- ExprContext *econtext,
- bool *isNull, ExprDoneCond *isDone)
-{
- return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone);
-}
-
-/* ----------------------------------------------------------------
* ExecEvalCoerceViaIO
*
* Evaluate a CoerceViaIO node.
@@ -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
@@ -1435,6 +1435,21 @@ _copyConvertRowtypeExpr(ConvertRowtypeExpr *from)
}
/*
+ * _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
*/
static CaseExpr *
@@ -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
@@ -494,6 +494,16 @@ _equalConvertRowtypeExpr(ConvertRowtypeExpr *a, ConvertRowtypeExpr *b)
}
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)
{
COMPARE_SCALAR_FIELD(casetype);
@@ -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
@@ -1196,6 +1196,16 @@ _outConvertRowtypeExpr(StringInfo str, ConvertRowtypeExpr *node)
}
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)
{
WRITE_NODE_TYPE("CASE");
@@ -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
@@ -744,22 +744,6 @@ _readRelabelType(void)
}
/*
- * _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
*/
static CoerceViaIO *
@@ -811,6 +795,21 @@ _readConvertRowtypeExpr(void)
}
/*
+ * _readCollateExpr
+ */
+static CollateExpr *
+_readCollateExpr(void)
+{
+ READ_LOCALS(CollateExpr);
+
+ READ_NODE_FIELD(arg);
+ READ_OID_FIELD(collOid);
+ READ_LOCATION_FIELD(location);
+
+ READ_DONE();
+}
+
+/*
* _readCaseExpr
*/
static CaseExpr *
@@ -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
@@ -266,6 +266,17 @@ typedef struct TypeCast
} 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
*
* agg_order (if not NIL) indicates we saw 'foo(... ORDER BY ...)'.
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
*
@@ -731,6 +719,18 @@ typedef struct ConvertRowtypeExpr
} 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
*
* We support two distinct forms of 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