aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/gin/ginutil.c2
-rw-r--r--src/backend/access/index/indexam.c2
-rw-r--r--src/backend/catalog/dependency.c23
-rw-r--r--src/backend/catalog/heap.c11
-rw-r--r--src/backend/commands/analyze.c4
-rw-r--r--src/backend/commands/functioncmds.c2
-rw-r--r--src/backend/commands/indexcmds.c25
-rw-r--r--src/backend/commands/prepare.c4
-rw-r--r--src/backend/commands/tablecmds.c4
-rw-r--r--src/backend/commands/typecmds.c7
-rw-r--r--src/backend/commands/view.c16
-rw-r--r--src/backend/executor/execQual.c86
-rw-r--r--src/backend/executor/functions.c3
-rw-r--r--src/backend/executor/nodeAgg.c10
-rw-r--r--src/backend/executor/nodeIndexscan.c12
-rw-r--r--src/backend/executor/nodeMergejoin.c2
-rw-r--r--src/backend/executor/nodeSubplan.c6
-rw-r--r--src/backend/executor/nodeWindowAgg.c12
-rw-r--r--src/backend/nodes/copyfuncs.c83
-rw-r--r--src/backend/nodes/equalfuncs.c95
-rw-r--r--src/backend/nodes/makefuncs.c10
-rw-r--r--src/backend/nodes/nodeFuncs.c656
-rw-r--r--src/backend/nodes/outfuncs.c76
-rw-r--r--src/backend/nodes/readfuncs.c104
-rw-r--r--src/backend/optimizer/path/costsize.c6
-rw-r--r--src/backend/optimizer/path/equivclass.c147
-rw-r--r--src/backend/optimizer/path/indxpath.c60
-rw-r--r--src/backend/optimizer/path/pathkeys.c140
-rw-r--r--src/backend/optimizer/plan/createplan.c6
-rw-r--r--src/backend/optimizer/plan/initsplan.c10
-rw-r--r--src/backend/optimizer/plan/subselect.c48
-rw-r--r--src/backend/optimizer/util/clauses.c176
-rw-r--r--src/backend/optimizer/util/predtest.c14
-rw-r--r--src/backend/parser/Makefile6
-rw-r--r--src/backend/parser/README1
-rw-r--r--src/backend/parser/analyze.c89
-rw-r--r--src/backend/parser/parse_agg.c15
-rw-r--r--src/backend/parser/parse_clause.c23
-rw-r--r--src/backend/parser/parse_coerce.c152
-rw-r--r--src/backend/parser/parse_collate.c763
-rw-r--r--src/backend/parser/parse_cte.c6
-rw-r--r--src/backend/parser/parse_expr.c54
-rw-r--r--src/backend/parser/parse_func.c17
-rw-r--r--src/backend/parser/parse_node.c4
-rw-r--r--src/backend/parser/parse_oper.c18
-rw-r--r--src/backend/parser/parse_param.c6
-rw-r--r--src/backend/parser/parse_target.c6
-rw-r--r--src/backend/parser/parse_utilcmd.c4
-rw-r--r--src/backend/utils/adt/ruleutils.c25
-rw-r--r--src/backend/utils/adt/selfuncs.c19
-rw-r--r--src/backend/utils/fmgr/README13
-rw-r--r--src/backend/utils/fmgr/fmgr.c36
-rw-r--r--src/backend/utils/fmgr/funcapi.c25
-rw-r--r--src/backend/utils/sort/tuplesort.c2
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/fmgr.h30
-rw-r--r--src/include/nodes/makefuncs.h6
-rw-r--r--src/include/nodes/nodeFuncs.h12
-rw-r--r--src/include/nodes/nodes.h2
-rw-r--r--src/include/nodes/parsenodes.h7
-rw-r--r--src/include/nodes/primnodes.h75
-rw-r--r--src/include/nodes/relation.h22
-rw-r--r--src/include/optimizer/clauses.h3
-rw-r--r--src/include/optimizer/paths.h5
-rw-r--r--src/include/optimizer/planmain.h2
-rw-r--r--src/include/parser/parse_agg.h2
-rw-r--r--src/include/parser/parse_coerce.h2
-rw-r--r--src/include/parser/parse_collate.h27
-rw-r--r--src/include/parser/parse_node.h1
-rw-r--r--src/pl/plpgsql/src/pl_comp.c1
-rw-r--r--src/pl/plpgsql/src/pl_exec.c27
-rw-r--r--src/test/regress/expected/collate.linux.utf8.out25
-rw-r--r--src/test/regress/sql/collate.linux.utf8.sql4
73 files changed, 2341 insertions, 1060 deletions
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index 35f71c843e1..23965449df2 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -61,7 +61,7 @@ initGinState(GinState *state, Relation index)
fmgr_info_copy(&(state->compareFn[i]),
index_getprocinfo(index, i + 1, GIN_COMPARE_PROC),
CurrentMemoryContext);
- fmgr_info_collation(get_typcollation(index->rd_att->attrs[i]->atttypid),
+ fmgr_info_set_collation(get_typcollation(index->rd_att->attrs[i]->atttypid),
&(state->compareFn[i]));
fmgr_info_copy(&(state->extractValueFn[i]),
index_getprocinfo(index, i + 1, GIN_EXTRACTVALUE_PROC),
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 803bb063985..88f73e8241e 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -872,7 +872,7 @@ index_getprocinfo(Relation irel,
procnum, attnum, RelationGetRelationName(irel));
fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt);
- fmgr_info_collation(irel->rd_indcollation[attnum-1], locinfo);
+ fmgr_info_set_collation(irel->rd_indcollation[attnum-1], locinfo);
}
return locinfo;
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 9e2028a64ff..de24ef7a094 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -1506,9 +1506,9 @@ find_expr_references_walker(Node *node,
add_object_address(OCLASS_TYPE, param->paramtype, 0,
context->addrs);
/* and its collation, just as for Consts */
- if (OidIsValid(param->paramcollation) &&
- param->paramcollation != DEFAULT_COLLATION_OID)
- add_object_address(OCLASS_COLLATION, param->paramcollation, 0,
+ if (OidIsValid(param->paramcollid) &&
+ param->paramcollid != DEFAULT_COLLATION_OID)
+ add_object_address(OCLASS_COLLATION, param->paramcollid, 0,
context->addrs);
}
else if (IsA(node, FuncExpr))
@@ -1535,19 +1535,19 @@ find_expr_references_walker(Node *node,
context->addrs);
/* fall through to examine arguments */
}
- else if (IsA(node, ScalarArrayOpExpr))
+ else if (IsA(node, NullIfExpr))
{
- ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
+ NullIfExpr *nullifexpr = (NullIfExpr *) node;
- add_object_address(OCLASS_OPERATOR, opexpr->opno, 0,
+ add_object_address(OCLASS_OPERATOR, nullifexpr->opno, 0,
context->addrs);
/* fall through to examine arguments */
}
- else if (IsA(node, NullIfExpr))
+ else if (IsA(node, ScalarArrayOpExpr))
{
- NullIfExpr *nullifexpr = (NullIfExpr *) node;
+ ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
- add_object_address(OCLASS_OPERATOR, nullifexpr->opno, 0,
+ add_object_address(OCLASS_OPERATOR, opexpr->opno, 0,
context->addrs);
/* fall through to examine arguments */
}
@@ -1579,6 +1579,11 @@ find_expr_references_walker(Node *node,
/* since there is no function dependency, need to depend on type */
add_object_address(OCLASS_TYPE, relab->resulttype, 0,
context->addrs);
+ /* the collation might not be referenced anywhere else, either */
+ if (OidIsValid(relab->resultcollid) &&
+ relab->resultcollid != DEFAULT_COLLATION_OID)
+ add_object_address(OCLASS_COLLATION, relab->resultcollid, 0,
+ context->addrs);
}
else if (IsA(node, CoerceViaIO))
{
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 50efa4782d6..567eb7fd6eb 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -58,6 +58,7 @@
#include "nodes/nodeFuncs.h"
#include "optimizer/var.h"
#include "parser/parse_coerce.h"
+#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "storage/bufmgr.h"
@@ -2387,6 +2388,11 @@ cookDefault(ParseState *pstate,
errhint("You will need to rewrite or cast the expression.")));
}
+ /*
+ * Finally, take care of collations in the finished expression.
+ */
+ assign_expr_collations(pstate, expr);
+
return expr;
}
@@ -2415,6 +2421,11 @@ cookConstraint(ParseState *pstate,
expr = coerce_to_boolean(pstate, expr, "CHECK");
/*
+ * Take care of collations.
+ */
+ assign_expr_collations(pstate, expr);
+
+ /*
* Make sure no outside relations are referred to.
*/
if (list_length(pstate->p_rtable) != 1)
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index a9acc7c3036..774bb044715 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1931,7 +1931,7 @@ compute_minimal_stats(VacAttrStatsP stats,
fmgr_info(mystats->eqfunc, &f_cmpeq);
/* We always use the default collation for statistics */
- fmgr_info_collation(DEFAULT_COLLATION_OID, &f_cmpeq);
+ fmgr_info_set_collation(DEFAULT_COLLATION_OID, &f_cmpeq);
for (i = 0; i < samplerows; i++)
{
@@ -2254,7 +2254,7 @@ compute_scalar_stats(VacAttrStatsP stats,
SelectSortFunction(mystats->ltopr, false, &cmpFn, &cmpFlags);
fmgr_info(cmpFn, &f_cmpfn);
/* We always use the default collation for statistics */
- fmgr_info_collation(DEFAULT_COLLATION_OID, &f_cmpfn);
+ fmgr_info_set_collation(DEFAULT_COLLATION_OID, &f_cmpfn);
/* Initial scan to find sortable values */
for (i = 0; i < samplerows; i++)
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index a8ef947240e..c8cbe035f05 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -51,6 +51,7 @@
#include "miscadmin.h"
#include "optimizer/var.h"
#include "parser/parse_coerce.h"
+#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
@@ -334,6 +335,7 @@ examine_parameter_list(List *parameters, Oid languageOid,
def = transformExpr(pstate, fp->defexpr);
def = coerce_to_specific_type(pstate, def, toid, "DEFAULT");
+ assign_expr_collations(pstate, def);
/*
* Make sure no variables are referred to.
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index c8e21b68f58..163980bbfa3 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -883,17 +883,34 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
}
/*
- * Collation override
+ * Apply collation override if any
*/
if (attribute->collation)
+ attcollation = get_collation_oid(attribute->collation, false);
+
+ /*
+ * Check we have a collation iff it's a collatable type. The only
+ * expected failures here are (1) COLLATE applied to a noncollatable
+ * type, or (2) index expression had an unresolved collation. But
+ * we might as well code this to be a complete consistency check.
+ */
+ if (type_is_collatable(atttype))
+ {
+ if (!OidIsValid(attcollation))
+ ereport(ERROR,
+ (errcode(ERRCODE_INDETERMINATE_COLLATION),
+ errmsg("no collation was derived for the index expression"),
+ errhint("Use the COLLATE clause to set the collation explicitly.")));
+ }
+ else
{
- if (!type_is_collatable(atttype))
+ if (OidIsValid(attcollation))
ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("collations are not supported by type %s",
format_type_be(atttype))));
- attcollation = get_collation_oid(attribute->collation, false);
}
+
collationOidP[attn] = attcollation;
/*
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 6115e8678cd..adbf5872f38 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -23,6 +23,7 @@
#include "nodes/nodeFuncs.h"
#include "parser/analyze.h"
#include "parser/parse_coerce.h"
+#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_type.h"
#include "rewrite/rewriteHandler.h"
@@ -368,6 +369,9 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
format_type_be(expected_type_id)),
errhint("You will need to rewrite or cast the expression.")));
+ /* Take care of collations in the finished expression. */
+ assign_expr_collations(pstate, expr);
+
lfirst(l) = expr;
i++;
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index ee34cfa97e1..f677a8e8bd0 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -58,6 +58,7 @@
#include "optimizer/clauses.h"
#include "parser/parse_clause.h"
#include "parser/parse_coerce.h"
+#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_oper.h"
#include "parser/parse_relation.h"
@@ -6598,6 +6599,9 @@ ATPrepAlterColumnType(List **wqueue,
errmsg("column \"%s\" cannot be cast to type %s",
colName, format_type_be(targettype))));
+ /* Fix collations after all else */
+ assign_expr_collations(pstate, transform);
+
/*
* Add a work queue item to make ATRewriteTable update the column
* contents.
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index ee3bca17d17..4c06d898a88 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -54,6 +54,7 @@
#include "optimizer/planner.h"
#include "optimizer/var.h"
#include "parser/parse_coerce.h"
+#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
@@ -2341,6 +2342,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
domVal = makeNode(CoerceToDomainValue);
domVal->typeId = baseTypeOid;
domVal->typeMod = typMod;
+ domVal->collation = get_typcollation(baseTypeOid);
domVal->location = -1; /* will be set when/if used */
pstate->p_value_substitute = (Node *) domVal;
@@ -2353,6 +2355,11 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
expr = coerce_to_boolean(pstate, expr, "CHECK");
/*
+ * Fix up collation information.
+ */
+ assign_expr_collations(pstate, expr);
+
+ /*
* Make sure no outside relations are referred to.
*/
if (list_length(pstate->p_rtable) != 0)
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 794a56e84de..250d02605cf 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -129,14 +129,22 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
def->raw_default = NULL;
def->cooked_default = NULL;
def->collClause = NULL;
+ def->collOid = exprCollation((Node *) tle->expr);
/*
- * XXX Temporary kluge to make regression tests pass. We should
- * be able to trust the result of exprCollation more than this.
+ * It's possible that the column is of a collatable type but the
+ * collation could not be resolved, so double-check.
*/
if (type_is_collatable(exprType((Node *) tle->expr)))
- def->collOid = exprCollation((Node *) tle->expr);
+ {
+ if (!OidIsValid(def->collOid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INDETERMINATE_COLLATION),
+ errmsg("no collation was derived for view column \"%s\"",
+ def->colname),
+ errhint("Use the COLLATE clause to set the collation explicitly.")));
+ }
else
- def->collOid = InvalidOid;
+ Assert(!OidIsValid(def->collOid));
def->constraints = NIL;
attrList = lappend(attrList, def);
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 0faf52dfd79..e410818900d 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -82,7 +82,7 @@ static Datum ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalParamExtern(ExprState *exprstate, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
-static void init_fcache(Oid foid, FuncExprState *fcache,
+static void init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
MemoryContext fcacheCxt, bool needDescForSets);
static void ShutdownFuncExpr(Datum arg);
static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod,
@@ -120,9 +120,6 @@ 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,
@@ -1179,7 +1176,7 @@ GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
* init_fcache - initialize a FuncExprState node during first use
*/
static void
-init_fcache(Oid foid, FuncExprState *fcache,
+init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
MemoryContext fcacheCxt, bool needDescForSets)
{
AclResult aclresult;
@@ -1205,7 +1202,8 @@ init_fcache(Oid foid, FuncExprState *fcache,
/* Set up the primary fmgr lookup information */
fmgr_info_cxt(foid, &(fcache->func), fcacheCxt);
- fmgr_info_expr((Node *) fcache->xprstate.expr, &(fcache->func));
+ fmgr_info_set_collation(input_collation, &(fcache->func));
+ fmgr_info_set_expr((Node *) fcache->xprstate.expr, &(fcache->func));
/* Initialize the function call parameter struct as well */
InitFunctionCallInfoData(fcache->fcinfo_data, &(fcache->func),
@@ -1976,7 +1974,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
{
FuncExpr *func = (FuncExpr *) fcache->xprstate.expr;
- init_fcache(func->funcid, fcache,
+ init_fcache(func->funcid, func->inputcollid, fcache,
econtext->ecxt_per_query_memory, false);
}
returnsSet = fcache->func.fn_retset;
@@ -2255,7 +2253,8 @@ ExecEvalFunc(FuncExprState *fcache,
FuncExpr *func = (FuncExpr *) fcache->xprstate.expr;
/* Initialize function lookup info */
- init_fcache(func->funcid, fcache, econtext->ecxt_per_query_memory, true);
+ init_fcache(func->funcid, func->inputcollid, fcache,
+ econtext->ecxt_per_query_memory, true);
/* Go directly to ExecMakeFunctionResult on subsequent uses */
fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResult;
@@ -2277,7 +2276,8 @@ ExecEvalOper(FuncExprState *fcache,
OpExpr *op = (OpExpr *) fcache->xprstate.expr;
/* Initialize function lookup info */
- init_fcache(op->opfuncid, fcache, econtext->ecxt_per_query_memory, true);
+ init_fcache(op->opfuncid, op->inputcollid, fcache,
+ econtext->ecxt_per_query_memory, true);
/* Go directly to ExecMakeFunctionResult on subsequent uses */
fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResult;
@@ -2318,7 +2318,7 @@ ExecEvalDistinct(FuncExprState *fcache,
{
DistinctExpr *op = (DistinctExpr *) fcache->xprstate.expr;
- init_fcache(op->opfuncid, fcache,
+ init_fcache(op->opfuncid, op->inputcollid, fcache,
econtext->ecxt_per_query_memory, true);
Assert(!fcache->func.fn_retset);
}
@@ -2395,7 +2395,7 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
*/
if (sstate->fxprstate.func.fn_oid == InvalidOid)
{
- init_fcache(opexpr->opfuncid, &sstate->fxprstate,
+ init_fcache(opexpr->opfuncid, opexpr->inputcollid, &sstate->fxprstate,
econtext->ecxt_per_query_memory, true);
Assert(!sstate->fxprstate.func.fn_retset);
}
@@ -2754,20 +2754,6 @@ 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
@@ -3542,7 +3528,7 @@ ExecEvalNullIf(FuncExprState *nullIfExpr,
{
NullIfExpr *op = (NullIfExpr *) nullIfExpr->xprstate.expr;
- init_fcache(op->opfuncid, nullIfExpr,
+ init_fcache(op->opfuncid, op->inputcollid, nullIfExpr,
econtext->ecxt_per_query_memory, true);
Assert(!nullIfExpr->func.fn_retset);
}
@@ -4129,9 +4115,8 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
/* Set up the primary fmgr lookup information */
fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc),
econtext->ecxt_per_query_memory);
-
- /* Initialize additional info */
- fmgr_info_expr((Node *) acoerce, &(astate->elemfunc));
+ /* Note: coercion functions are assumed to not use collation */
+ fmgr_info_set_expr((Node *) acoerce, &(astate->elemfunc));
}
/*
@@ -4400,6 +4385,18 @@ ExecInitExpr(Expr *node, PlanState *parent)
state = (ExprState *) fstate;
}
break;
+ case T_NullIfExpr:
+ {
+ NullIfExpr *nullifexpr = (NullIfExpr *) node;
+ FuncExprState *fstate = makeNode(FuncExprState);
+
+ fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullIf;
+ fstate->args = (List *)
+ ExecInitExpr((Expr *) nullifexpr->args, parent);
+ fstate->func.fn_oid = InvalidOid; /* not initialized */
+ state = (ExprState *) fstate;
+ }
+ break;
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
@@ -4551,16 +4548,6 @@ 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;
@@ -4713,11 +4700,11 @@ ExecInitExpr(Expr *node, PlanState *parent)
Assert(list_length(rcexpr->opfamilies) == nopers);
rstate->funcs = (FmgrInfo *) palloc(nopers * sizeof(FmgrInfo));
i = 0;
- forthree(l, rcexpr->opnos, l2, rcexpr->opfamilies, l3, rcexpr->collids)
+ forthree(l, rcexpr->opnos, l2, rcexpr->opfamilies, l3, rcexpr->inputcollids)
{
Oid opno = lfirst_oid(l);
Oid opfamily = lfirst_oid(l2);
- Oid collid = lfirst_oid(l3);
+ Oid inputcollid = lfirst_oid(l3);
int strategy;
Oid lefttype;
Oid righttype;
@@ -4739,7 +4726,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
* does this code.
*/
fmgr_info(proc, &(rstate->funcs[i]));
- fmgr_info_collation(collid, &(rstate->funcs[i]));
+ fmgr_info_set_collation(inputcollid, &(rstate->funcs[i]));
i++;
}
state = (ExprState *) rstate;
@@ -4799,7 +4786,8 @@ ExecInitExpr(Expr *node, PlanState *parent)
* code.
*/
fmgr_info(typentry->cmp_proc, &(mstate->cfunc));
- fmgr_info_collation(minmaxexpr->collid, &(mstate->cfunc));
+ fmgr_info_set_collation(minmaxexpr->inputcollid,
+ &(mstate->cfunc));
state = (ExprState *) mstate;
}
break;
@@ -4836,18 +4824,6 @@ ExecInitExpr(Expr *node, PlanState *parent)
state = (ExprState *) xstate;
}
break;
- case T_NullIfExpr:
- {
- NullIfExpr *nullifexpr = (NullIfExpr *) node;
- FuncExprState *fstate = makeNode(FuncExprState);
-
- fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullIf;
- fstate->args = (List *)
- ExecInitExpr((Expr *) nullifexpr->args, parent);
- fstate->func.fn_oid = InvalidOid; /* not initialized */
- state = (ExprState *) fstate;
- }
- break;
case T_NullTest:
{
NullTest *ntest = (NullTest *) node;
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index a2f4fac74cb..0421be57a41 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -1290,6 +1290,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
tle->expr = (Expr *) makeRelabelType(tle->expr,
rettype,
-1,
+ get_typcollation(rettype),
COERCE_DONTCARE);
/* Relabel is dangerous if TLE is a sort/group or setop column */
if (tle->ressortgroupref != 0 || parse->setOperations)
@@ -1335,6 +1336,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
tle->expr = (Expr *) makeRelabelType(tle->expr,
rettype,
-1,
+ get_typcollation(rettype),
COERCE_DONTCARE);
/* Relabel is dangerous if sort/group or setop column */
if (tle->ressortgroupref != 0 || parse->setOperations)
@@ -1437,6 +1439,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
tle->expr = (Expr *) makeRelabelType(tle->expr,
atttype,
-1,
+ get_typcollation(atttype),
COERCE_DONTCARE);
/* Relabel is dangerous if sort/group or setop column */
if (tle->ressortgroupref != 0 || parse->setOperations)
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index d9bed220e4a..51b1228c26f 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1669,19 +1669,21 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
numArguments,
aggtranstype,
aggref->aggtype,
+ aggref->inputcollid,
transfn_oid,
finalfn_oid,
- aggref->collid,
&transfnexpr,
&finalfnexpr);
fmgr_info(transfn_oid, &peraggstate->transfn);
- fmgr_info_expr((Node *) transfnexpr, &peraggstate->transfn);
+ fmgr_info_set_collation(aggref->inputcollid, &peraggstate->transfn);
+ fmgr_info_set_expr((Node *) transfnexpr, &peraggstate->transfn);
if (OidIsValid(finalfn_oid))
{
fmgr_info(finalfn_oid, &peraggstate->finalfn);
- fmgr_info_expr((Node *) finalfnexpr, &peraggstate->finalfn);
+ fmgr_info_set_collation(aggref->inputcollid, &peraggstate->finalfn);
+ fmgr_info_set_expr((Node *) finalfnexpr, &peraggstate->finalfn);
}
get_typlenbyval(aggref->aggtype,
@@ -1831,6 +1833,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc);
fmgr_info(get_opcode(sortcl->eqop), &peraggstate->equalfns[i]);
+ fmgr_info_set_collation(aggref->inputcollid,
+ &peraggstate->equalfns[i]);
i++;
}
Assert(i == numDistinctCols);
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 55fce94b321..e60db7813a4 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -732,7 +732,6 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
int op_strategy; /* operator's strategy number */
Oid op_lefttype; /* operator's declared input types */
Oid op_righttype;
- Oid collation;
Expr *leftop; /* expr on lhs of operator */
Expr *rightop; /* expr on rhs ... */
AttrNumber varattno; /* att number used in scan */
@@ -833,7 +832,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
opfuncid, /* reg proc to use */
scanvalue); /* constant */
ScanKeyEntryInitializeCollation(this_scan_key,
- ((OpExpr *) clause)->collid);
+ ((OpExpr *) clause)->inputcollid);
}
else if (IsA(clause, RowCompareExpr))
{
@@ -842,7 +841,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
ListCell *largs_cell = list_head(rc->largs);
ListCell *rargs_cell = list_head(rc->rargs);
ListCell *opnos_cell = list_head(rc->opnos);
- ListCell *collids_cell = list_head(rc->collids);
+ ListCell *collids_cell = list_head(rc->inputcollids);
ScanKey first_sub_key;
int n_sub_key;
@@ -858,6 +857,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
ScanKey this_sub_key = &first_sub_key[n_sub_key];
int flags = SK_ROW_MEMBER;
Datum scanvalue;
+ Oid inputcollation;
/*
* leftop should be the index key Var, possibly relabeled
@@ -901,7 +901,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
op_righttype,
BTORDER_PROC);
- collation = lfirst_oid(collids_cell);
+ inputcollation = lfirst_oid(collids_cell);
collids_cell = lnext(collids_cell);
/*
@@ -960,7 +960,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
opfuncid, /* reg proc to use */
scanvalue); /* constant */
ScanKeyEntryInitializeCollation(this_sub_key,
- collation);
+ inputcollation);
n_sub_key++;
}
@@ -1045,7 +1045,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
opfuncid, /* reg proc to use */
(Datum) 0); /* constant */
ScanKeyEntryInitializeCollation(this_scan_key,
- saop->collid);
+ saop->inputcollid);
}
else if (IsA(clause, NullTest))
{
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index c0b9f230855..75c3a645359 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -242,7 +242,7 @@ MJExamineQuals(List *mergeclauses,
/* Set up the fmgr lookup information */
fmgr_info(cmpproc, &(clause->cmpfinfo));
- fmgr_info_collation(collation, &(clause->cmpfinfo));
+ fmgr_info_set_collation(collation, &(clause->cmpfinfo));
/* Fill the additional comparison-strategy flags */
if (opstrategy == BTLessStrategyNumber)
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e9b3d76df1c..08a3017e614 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -831,7 +831,9 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
/* Lookup the equality function (potentially cross-type) */
fmgr_info(opexpr->opfuncid, &sstate->cur_eq_funcs[i - 1]);
- fmgr_info_expr((Node *) opexpr, &sstate->cur_eq_funcs[i - 1]);
+ fmgr_info_set_collation(opexpr->inputcollid,
+ &sstate->cur_eq_funcs[i - 1]);
+ fmgr_info_set_expr((Node *) opexpr, &sstate->cur_eq_funcs[i - 1]);
/* Look up the equality function for the RHS type */
if (!get_compatible_hash_operators(opexpr->opno,
@@ -839,6 +841,8 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
elog(ERROR, "could not find compatible hash operator for operator %u",
opexpr->opno);
fmgr_info(get_opcode(rhs_eq_oper), &sstate->tab_eq_funcs[i - 1]);
+ fmgr_info_set_collation(opexpr->inputcollid,
+ &sstate->tab_eq_funcs[i - 1]);
/* Lookup the associated hash functions */
if (!get_op_hash_functions(opexpr->opno,
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 372262ad7f6..5680efeb69e 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1561,7 +1561,9 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo,
econtext->ecxt_per_query_memory);
- fmgr_info_expr((Node *) wfunc, &perfuncstate->flinfo);
+ fmgr_info_set_collation(wfunc->inputcollid, &perfuncstate->flinfo);
+ fmgr_info_set_expr((Node *) wfunc, &perfuncstate->flinfo);
+
get_typlenbyval(wfunc->wintype,
&perfuncstate->resulttypeLen,
&perfuncstate->resulttypeByVal);
@@ -1792,19 +1794,21 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
numArguments,
aggtranstype,
wfunc->wintype,
+ wfunc->inputcollid,
transfn_oid,
finalfn_oid,
- wfunc->collid,
&transfnexpr,
&finalfnexpr);
fmgr_info(transfn_oid, &peraggstate->transfn);
- fmgr_info_expr((Node *) transfnexpr, &peraggstate->transfn);
+ fmgr_info_set_collation(wfunc->inputcollid, &peraggstate->transfn);
+ fmgr_info_set_expr((Node *) transfnexpr, &peraggstate->transfn);
if (OidIsValid(finalfn_oid))
{
fmgr_info(finalfn_oid, &peraggstate->finalfn);
- fmgr_info_expr((Node *) finalfnexpr, &peraggstate->finalfn);
+ fmgr_info_set_collation(wfunc->inputcollid, &peraggstate->finalfn);
+ fmgr_info_set_expr((Node *) finalfnexpr, &peraggstate->finalfn);
}
get_typlenbyval(wfunc->wintype,
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c0490e93ea5..6e52d36a17e 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1103,7 +1103,7 @@ _copyParam(Param *from)
COPY_SCALAR_FIELD(paramid);
COPY_SCALAR_FIELD(paramtype);
COPY_SCALAR_FIELD(paramtypmod);
- COPY_SCALAR_FIELD(paramcollation);
+ COPY_SCALAR_FIELD(paramcollid);
COPY_LOCATION_FIELD(location);
return newnode;
@@ -1119,12 +1119,13 @@ _copyAggref(Aggref *from)
COPY_SCALAR_FIELD(aggfnoid);
COPY_SCALAR_FIELD(aggtype);
+ COPY_SCALAR_FIELD(aggcollid);
+ COPY_SCALAR_FIELD(inputcollid);
COPY_NODE_FIELD(args);
COPY_NODE_FIELD(aggorder);
COPY_NODE_FIELD(aggdistinct);
COPY_SCALAR_FIELD(aggstar);
COPY_SCALAR_FIELD(agglevelsup);
- COPY_SCALAR_FIELD(collid);
COPY_LOCATION_FIELD(location);
return newnode;
@@ -1140,11 +1141,12 @@ _copyWindowFunc(WindowFunc *from)
COPY_SCALAR_FIELD(winfnoid);
COPY_SCALAR_FIELD(wintype);
+ COPY_SCALAR_FIELD(wincollid);
+ COPY_SCALAR_FIELD(inputcollid);
COPY_NODE_FIELD(args);
COPY_SCALAR_FIELD(winref);
COPY_SCALAR_FIELD(winstar);
COPY_SCALAR_FIELD(winagg);
- COPY_SCALAR_FIELD(collid);
COPY_LOCATION_FIELD(location);
return newnode;
@@ -1182,8 +1184,9 @@ _copyFuncExpr(FuncExpr *from)
COPY_SCALAR_FIELD(funcresulttype);
COPY_SCALAR_FIELD(funcretset);
COPY_SCALAR_FIELD(funcformat);
+ COPY_SCALAR_FIELD(funccollid);
+ COPY_SCALAR_FIELD(inputcollid);
COPY_NODE_FIELD(args);
- COPY_SCALAR_FIELD(collid);
COPY_LOCATION_FIELD(location);
return newnode;
@@ -1217,8 +1220,9 @@ _copyOpExpr(OpExpr *from)
COPY_SCALAR_FIELD(opfuncid);
COPY_SCALAR_FIELD(opresulttype);
COPY_SCALAR_FIELD(opretset);
+ COPY_SCALAR_FIELD(opcollid);
+ COPY_SCALAR_FIELD(inputcollid);
COPY_NODE_FIELD(args);
- COPY_SCALAR_FIELD(collid);
COPY_LOCATION_FIELD(location);
return newnode;
@@ -1236,8 +1240,29 @@ _copyDistinctExpr(DistinctExpr *from)
COPY_SCALAR_FIELD(opfuncid);
COPY_SCALAR_FIELD(opresulttype);
COPY_SCALAR_FIELD(opretset);
+ COPY_SCALAR_FIELD(opcollid);
+ COPY_SCALAR_FIELD(inputcollid);
+ COPY_NODE_FIELD(args);
+ COPY_LOCATION_FIELD(location);
+
+ return newnode;
+}
+
+/*
+ * _copyNullIfExpr (same as OpExpr)
+ */
+static NullIfExpr *
+_copyNullIfExpr(NullIfExpr *from)
+{
+ NullIfExpr *newnode = makeNode(NullIfExpr);
+
+ COPY_SCALAR_FIELD(opno);
+ COPY_SCALAR_FIELD(opfuncid);
+ COPY_SCALAR_FIELD(opresulttype);
+ COPY_SCALAR_FIELD(opretset);
+ COPY_SCALAR_FIELD(opcollid);
+ COPY_SCALAR_FIELD(inputcollid);
COPY_NODE_FIELD(args);
- COPY_SCALAR_FIELD(collid);
COPY_LOCATION_FIELD(location);
return newnode;
@@ -1254,8 +1279,8 @@ _copyScalarArrayOpExpr(ScalarArrayOpExpr *from)
COPY_SCALAR_FIELD(opno);
COPY_SCALAR_FIELD(opfuncid);
COPY_SCALAR_FIELD(useOr);
+ COPY_SCALAR_FIELD(inputcollid);
COPY_NODE_FIELD(args);
- COPY_SCALAR_FIELD(collid);
COPY_LOCATION_FIELD(location);
return newnode;
@@ -1345,7 +1370,7 @@ _copyFieldSelect(FieldSelect *from)
COPY_SCALAR_FIELD(fieldnum);
COPY_SCALAR_FIELD(resulttype);
COPY_SCALAR_FIELD(resulttypmod);
- COPY_SCALAR_FIELD(resultcollation);
+ COPY_SCALAR_FIELD(resultcollid);
return newnode;
}
@@ -1377,6 +1402,7 @@ _copyRelabelType(RelabelType *from)
COPY_NODE_FIELD(arg);
COPY_SCALAR_FIELD(resulttype);
COPY_SCALAR_FIELD(resulttypmod);
+ COPY_SCALAR_FIELD(resultcollid);
COPY_SCALAR_FIELD(relabelformat);
COPY_LOCATION_FIELD(location);
@@ -1393,6 +1419,7 @@ _copyCoerceViaIO(CoerceViaIO *from)
COPY_NODE_FIELD(arg);
COPY_SCALAR_FIELD(resulttype);
+ COPY_SCALAR_FIELD(resultcollid);
COPY_SCALAR_FIELD(coerceformat);
COPY_LOCATION_FIELD(location);
@@ -1411,6 +1438,7 @@ _copyArrayCoerceExpr(ArrayCoerceExpr *from)
COPY_SCALAR_FIELD(elemfuncid);
COPY_SCALAR_FIELD(resulttype);
COPY_SCALAR_FIELD(resulttypmod);
+ COPY_SCALAR_FIELD(resultcollid);
COPY_SCALAR_FIELD(isExplicit);
COPY_SCALAR_FIELD(coerceformat);
COPY_LOCATION_FIELD(location);
@@ -1458,7 +1486,7 @@ _copyCaseExpr(CaseExpr *from)
CaseExpr *newnode = makeNode(CaseExpr);
COPY_SCALAR_FIELD(casetype);
- COPY_SCALAR_FIELD(casecollation);
+ COPY_SCALAR_FIELD(casecollid);
COPY_NODE_FIELD(arg);
COPY_NODE_FIELD(args);
COPY_NODE_FIELD(defresult);
@@ -1506,6 +1534,7 @@ _copyArrayExpr(ArrayExpr *from)
ArrayExpr *newnode = makeNode(ArrayExpr);
COPY_SCALAR_FIELD(array_typeid);
+ COPY_SCALAR_FIELD(array_collid);
COPY_SCALAR_FIELD(element_typeid);
COPY_NODE_FIELD(elements);
COPY_SCALAR_FIELD(multidims);
@@ -1542,7 +1571,7 @@ _copyRowCompareExpr(RowCompareExpr *from)
COPY_SCALAR_FIELD(rctype);
COPY_NODE_FIELD(opnos);
COPY_NODE_FIELD(opfamilies);
- COPY_NODE_FIELD(collids);
+ COPY_NODE_FIELD(inputcollids);
COPY_NODE_FIELD(largs);
COPY_NODE_FIELD(rargs);
@@ -1558,7 +1587,7 @@ _copyCoalesceExpr(CoalesceExpr *from)
CoalesceExpr *newnode = makeNode(CoalesceExpr);
COPY_SCALAR_FIELD(coalescetype);
- COPY_SCALAR_FIELD(coalescecollation);
+ COPY_SCALAR_FIELD(coalescecollid);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
@@ -1574,9 +1603,10 @@ _copyMinMaxExpr(MinMaxExpr *from)
MinMaxExpr *newnode = makeNode(MinMaxExpr);
COPY_SCALAR_FIELD(minmaxtype);
+ COPY_SCALAR_FIELD(minmaxcollid);
+ COPY_SCALAR_FIELD(inputcollid);
COPY_SCALAR_FIELD(op);
COPY_NODE_FIELD(args);
- COPY_SCALAR_FIELD(collid);
COPY_LOCATION_FIELD(location);
return newnode;
@@ -1604,24 +1634,6 @@ _copyXmlExpr(XmlExpr *from)
}
/*
- * _copyNullIfExpr (same as OpExpr)
- */
-static NullIfExpr *
-_copyNullIfExpr(NullIfExpr *from)
-{
- NullIfExpr *newnode = makeNode(NullIfExpr);
-
- COPY_SCALAR_FIELD(opno);
- COPY_SCALAR_FIELD(opfuncid);
- COPY_SCALAR_FIELD(opresulttype);
- COPY_SCALAR_FIELD(opretset);
- COPY_NODE_FIELD(args);
- COPY_LOCATION_FIELD(location);
-
- return newnode;
-}
-
-/*
* _copyNullTest
*/
static NullTest *
@@ -1661,6 +1673,7 @@ _copyCoerceToDomain(CoerceToDomain *from)
COPY_NODE_FIELD(arg);
COPY_SCALAR_FIELD(resulttype);
COPY_SCALAR_FIELD(resulttypmod);
+ COPY_SCALAR_FIELD(resultcollid);
COPY_SCALAR_FIELD(coercionformat);
COPY_LOCATION_FIELD(location);
@@ -1677,6 +1690,7 @@ _copyCoerceToDomainValue(CoerceToDomainValue *from)
COPY_SCALAR_FIELD(typeId);
COPY_SCALAR_FIELD(typeMod);
+ COPY_SCALAR_FIELD(collation);
COPY_LOCATION_FIELD(location);
return newnode;
@@ -1692,7 +1706,7 @@ _copySetToDefault(SetToDefault *from)
COPY_SCALAR_FIELD(typeId);
COPY_SCALAR_FIELD(typeMod);
- COPY_SCALAR_FIELD(collid);
+ COPY_SCALAR_FIELD(collation);
COPY_LOCATION_FIELD(location);
return newnode;
@@ -1798,7 +1812,6 @@ _copyPathKey(PathKey *from)
/* EquivalenceClasses are never moved, so just shallow-copy the pointer */
COPY_SCALAR_FIELD(pk_eclass);
COPY_SCALAR_FIELD(pk_opfamily);
- COPY_SCALAR_FIELD(pk_collation);
COPY_SCALAR_FIELD(pk_strategy);
COPY_SCALAR_FIELD(pk_nulls_first);
@@ -3998,6 +4011,9 @@ copyObject(void *from)
case T_DistinctExpr:
retval = _copyDistinctExpr(from);
break;
+ case T_NullIfExpr:
+ retval = _copyNullIfExpr(from);
+ break;
case T_ScalarArrayOpExpr:
retval = _copyScalarArrayOpExpr(from);
break;
@@ -4061,9 +4077,6 @@ copyObject(void *from)
case T_XmlExpr:
retval = _copyXmlExpr(from);
break;
- case T_NullIfExpr:
- retval = _copyNullIfExpr(from);
- break;
case T_NullTest:
retval = _copyNullTest(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 3726006f1d2..7340aa05251 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -174,7 +174,7 @@ _equalParam(Param *a, Param *b)
COMPARE_SCALAR_FIELD(paramid);
COMPARE_SCALAR_FIELD(paramtype);
COMPARE_SCALAR_FIELD(paramtypmod);
- COMPARE_SCALAR_FIELD(paramcollation);
+ COMPARE_SCALAR_FIELD(paramcollid);
COMPARE_LOCATION_FIELD(location);
return true;
@@ -185,12 +185,13 @@ _equalAggref(Aggref *a, Aggref *b)
{
COMPARE_SCALAR_FIELD(aggfnoid);
COMPARE_SCALAR_FIELD(aggtype);
+ COMPARE_SCALAR_FIELD(aggcollid);
+ COMPARE_SCALAR_FIELD(inputcollid);
COMPARE_NODE_FIELD(args);
COMPARE_NODE_FIELD(aggorder);
COMPARE_NODE_FIELD(aggdistinct);
COMPARE_SCALAR_FIELD(aggstar);
COMPARE_SCALAR_FIELD(agglevelsup);
- COMPARE_SCALAR_FIELD(collid);
COMPARE_LOCATION_FIELD(location);
return true;
@@ -201,11 +202,12 @@ _equalWindowFunc(WindowFunc *a, WindowFunc *b)
{
COMPARE_SCALAR_FIELD(winfnoid);
COMPARE_SCALAR_FIELD(wintype);
+ COMPARE_SCALAR_FIELD(wincollid);
+ COMPARE_SCALAR_FIELD(inputcollid);
COMPARE_NODE_FIELD(args);
COMPARE_SCALAR_FIELD(winref);
COMPARE_SCALAR_FIELD(winstar);
COMPARE_SCALAR_FIELD(winagg);
- COMPARE_SCALAR_FIELD(collid);
COMPARE_LOCATION_FIELD(location);
return true;
@@ -242,8 +244,9 @@ _equalFuncExpr(FuncExpr *a, FuncExpr *b)
b->funcformat != COERCE_DONTCARE)
return false;
+ COMPARE_SCALAR_FIELD(funccollid);
+ COMPARE_SCALAR_FIELD(inputcollid);
COMPARE_NODE_FIELD(args);
- COMPARE_SCALAR_FIELD(collid);
COMPARE_LOCATION_FIELD(location);
return true;
@@ -278,8 +281,9 @@ _equalOpExpr(OpExpr *a, OpExpr *b)
COMPARE_SCALAR_FIELD(opresulttype);
COMPARE_SCALAR_FIELD(opretset);
+ COMPARE_SCALAR_FIELD(opcollid);
+ COMPARE_SCALAR_FIELD(inputcollid);
COMPARE_NODE_FIELD(args);
- COMPARE_SCALAR_FIELD(collid);
COMPARE_LOCATION_FIELD(location);
return true;
@@ -303,8 +307,35 @@ _equalDistinctExpr(DistinctExpr *a, DistinctExpr *b)
COMPARE_SCALAR_FIELD(opresulttype);
COMPARE_SCALAR_FIELD(opretset);
+ COMPARE_SCALAR_FIELD(opcollid);
+ COMPARE_SCALAR_FIELD(inputcollid);
+ COMPARE_NODE_FIELD(args);
+ COMPARE_LOCATION_FIELD(location);
+
+ return true;
+}
+
+static bool
+_equalNullIfExpr(NullIfExpr *a, NullIfExpr *b)
+{
+ COMPARE_SCALAR_FIELD(opno);
+
+ /*
+ * Special-case opfuncid: it is allowable for it to differ if one node
+ * contains zero and the other doesn't. This just means that the one node
+ * isn't as far along in the parse/plan pipeline and hasn't had the
+ * opfuncid cache filled yet.
+ */
+ if (a->opfuncid != b->opfuncid &&
+ a->opfuncid != 0 &&
+ b->opfuncid != 0)
+ return false;
+
+ COMPARE_SCALAR_FIELD(opresulttype);
+ COMPARE_SCALAR_FIELD(opretset);
+ COMPARE_SCALAR_FIELD(opcollid);
+ COMPARE_SCALAR_FIELD(inputcollid);
COMPARE_NODE_FIELD(args);
- COMPARE_SCALAR_FIELD(collid);
COMPARE_LOCATION_FIELD(location);
return true;
@@ -327,8 +358,8 @@ _equalScalarArrayOpExpr(ScalarArrayOpExpr *a, ScalarArrayOpExpr *b)
return false;
COMPARE_SCALAR_FIELD(useOr);
+ COMPARE_SCALAR_FIELD(inputcollid);
COMPARE_NODE_FIELD(args);
- COMPARE_SCALAR_FIELD(collid);
COMPARE_LOCATION_FIELD(location);
return true;
@@ -393,7 +424,7 @@ _equalFieldSelect(FieldSelect *a, FieldSelect *b)
COMPARE_SCALAR_FIELD(fieldnum);
COMPARE_SCALAR_FIELD(resulttype);
COMPARE_SCALAR_FIELD(resulttypmod);
- COMPARE_SCALAR_FIELD(resultcollation);
+ COMPARE_SCALAR_FIELD(resultcollid);
return true;
}
@@ -415,6 +446,7 @@ _equalRelabelType(RelabelType *a, RelabelType *b)
COMPARE_NODE_FIELD(arg);
COMPARE_SCALAR_FIELD(resulttype);
COMPARE_SCALAR_FIELD(resulttypmod);
+ COMPARE_SCALAR_FIELD(resultcollid);
/*
* Special-case COERCE_DONTCARE, so that planner can build coercion nodes
@@ -435,6 +467,7 @@ _equalCoerceViaIO(CoerceViaIO *a, CoerceViaIO *b)
{
COMPARE_NODE_FIELD(arg);
COMPARE_SCALAR_FIELD(resulttype);
+ COMPARE_SCALAR_FIELD(resultcollid);
/*
* Special-case COERCE_DONTCARE, so that planner can build coercion nodes
@@ -457,6 +490,7 @@ _equalArrayCoerceExpr(ArrayCoerceExpr *a, ArrayCoerceExpr *b)
COMPARE_SCALAR_FIELD(elemfuncid);
COMPARE_SCALAR_FIELD(resulttype);
COMPARE_SCALAR_FIELD(resulttypmod);
+ COMPARE_SCALAR_FIELD(resultcollid);
COMPARE_SCALAR_FIELD(isExplicit);
/*
@@ -507,7 +541,7 @@ static bool
_equalCaseExpr(CaseExpr *a, CaseExpr *b)
{
COMPARE_SCALAR_FIELD(casetype);
- COMPARE_SCALAR_FIELD(casecollation);
+ COMPARE_SCALAR_FIELD(casecollid);
COMPARE_NODE_FIELD(arg);
COMPARE_NODE_FIELD(args);
COMPARE_NODE_FIELD(defresult);
@@ -540,6 +574,7 @@ static bool
_equalArrayExpr(ArrayExpr *a, ArrayExpr *b)
{
COMPARE_SCALAR_FIELD(array_typeid);
+ COMPARE_SCALAR_FIELD(array_collid);
COMPARE_SCALAR_FIELD(element_typeid);
COMPARE_NODE_FIELD(elements);
COMPARE_SCALAR_FIELD(multidims);
@@ -575,7 +610,7 @@ _equalRowCompareExpr(RowCompareExpr *a, RowCompareExpr *b)
COMPARE_SCALAR_FIELD(rctype);
COMPARE_NODE_FIELD(opnos);
COMPARE_NODE_FIELD(opfamilies);
- COMPARE_NODE_FIELD(collids);
+ COMPARE_NODE_FIELD(inputcollids);
COMPARE_NODE_FIELD(largs);
COMPARE_NODE_FIELD(rargs);
@@ -586,7 +621,7 @@ static bool
_equalCoalesceExpr(CoalesceExpr *a, CoalesceExpr *b)
{
COMPARE_SCALAR_FIELD(coalescetype);
- COMPARE_SCALAR_FIELD(coalescecollation);
+ COMPARE_SCALAR_FIELD(coalescecollid);
COMPARE_NODE_FIELD(args);
COMPARE_LOCATION_FIELD(location);
@@ -597,9 +632,10 @@ static bool
_equalMinMaxExpr(MinMaxExpr *a, MinMaxExpr *b)
{
COMPARE_SCALAR_FIELD(minmaxtype);
+ COMPARE_SCALAR_FIELD(minmaxcollid);
+ COMPARE_SCALAR_FIELD(inputcollid);
COMPARE_SCALAR_FIELD(op);
COMPARE_NODE_FIELD(args);
- COMPARE_SCALAR_FIELD(collid);
COMPARE_LOCATION_FIELD(location);
return true;
@@ -622,30 +658,6 @@ _equalXmlExpr(XmlExpr *a, XmlExpr *b)
}
static bool
-_equalNullIfExpr(NullIfExpr *a, NullIfExpr *b)
-{
- COMPARE_SCALAR_FIELD(opno);
-
- /*
- * Special-case opfuncid: it is allowable for it to differ if one node
- * contains zero and the other doesn't. This just means that the one node
- * isn't as far along in the parse/plan pipeline and hasn't had the
- * opfuncid cache filled yet.
- */
- if (a->opfuncid != b->opfuncid &&
- a->opfuncid != 0 &&
- b->opfuncid != 0)
- return false;
-
- COMPARE_SCALAR_FIELD(opresulttype);
- COMPARE_SCALAR_FIELD(opretset);
- COMPARE_NODE_FIELD(args);
- COMPARE_LOCATION_FIELD(location);
-
- return true;
-}
-
-static bool
_equalNullTest(NullTest *a, NullTest *b)
{
COMPARE_NODE_FIELD(arg);
@@ -670,6 +682,7 @@ _equalCoerceToDomain(CoerceToDomain *a, CoerceToDomain *b)
COMPARE_NODE_FIELD(arg);
COMPARE_SCALAR_FIELD(resulttype);
COMPARE_SCALAR_FIELD(resulttypmod);
+ COMPARE_SCALAR_FIELD(resultcollid);
/*
* Special-case COERCE_DONTCARE, so that planner can build coercion nodes
@@ -690,6 +703,7 @@ _equalCoerceToDomainValue(CoerceToDomainValue *a, CoerceToDomainValue *b)
{
COMPARE_SCALAR_FIELD(typeId);
COMPARE_SCALAR_FIELD(typeMod);
+ COMPARE_SCALAR_FIELD(collation);
COMPARE_LOCATION_FIELD(location);
return true;
@@ -700,7 +714,7 @@ _equalSetToDefault(SetToDefault *a, SetToDefault *b)
{
COMPARE_SCALAR_FIELD(typeId);
COMPARE_SCALAR_FIELD(typeMod);
- COMPARE_SCALAR_FIELD(collid);
+ COMPARE_SCALAR_FIELD(collation);
COMPARE_LOCATION_FIELD(location);
return true;
@@ -787,7 +801,6 @@ _equalPathKey(PathKey *a, PathKey *b)
if (a_eclass != b_eclass)
return false;
COMPARE_SCALAR_FIELD(pk_opfamily);
- COMPARE_SCALAR_FIELD(pk_collation);
COMPARE_SCALAR_FIELD(pk_strategy);
COMPARE_SCALAR_FIELD(pk_nulls_first);
@@ -2559,6 +2572,9 @@ equal(void *a, void *b)
case T_DistinctExpr:
retval = _equalDistinctExpr(a, b);
break;
+ case T_NullIfExpr:
+ retval = _equalNullIfExpr(a, b);
+ break;
case T_ScalarArrayOpExpr:
retval = _equalScalarArrayOpExpr(a, b);
break;
@@ -2622,9 +2638,6 @@ equal(void *a, void *b)
case T_XmlExpr:
retval = _equalXmlExpr(a, b);
break;
- case T_NullIfExpr:
- retval = _equalNullIfExpr(a, b);
- break;
case T_NullTest:
retval = _equalNullTest(a, b);
break;
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index d9f16452383..41e597cfffb 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -362,13 +362,15 @@ makeAlias(const char *aliasname, List *colnames)
* creates a RelabelType node
*/
RelabelType *
-makeRelabelType(Expr *arg, Oid rtype, int32 rtypmod, CoercionForm rformat)
+makeRelabelType(Expr *arg, Oid rtype, int32 rtypmod, Oid rcollid,
+ CoercionForm rformat)
{
RelabelType *r = makeNode(RelabelType);
r->arg = arg;
r->resulttype = rtype;
r->resulttypmod = rtypmod;
+ r->resultcollid = rcollid;
r->relabelformat = rformat;
r->location = -1;
@@ -447,7 +449,8 @@ makeTypeNameFromOid(Oid typeOid, int32 typmod)
* The argument expressions must have been transformed already.
*/
FuncExpr *
-makeFuncExpr(Oid funcid, Oid rettype, List *args, Oid collid, CoercionForm fformat)
+makeFuncExpr(Oid funcid, Oid rettype, List *args,
+ Oid funccollid, Oid inputcollid, CoercionForm fformat)
{
FuncExpr *funcexpr;
@@ -456,8 +459,9 @@ makeFuncExpr(Oid funcid, Oid rettype, List *args, Oid collid, CoercionForm fform
funcexpr->funcresulttype = rettype;
funcexpr->funcretset = false; /* only allowed case here */
funcexpr->funcformat = fformat;
+ funcexpr->funccollid = funccollid;
+ funcexpr->inputcollid = inputcollid;
funcexpr->args = args;
- funcexpr->collid = collid;
funcexpr->location = -1;
return funcexpr;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 5394851a1f5..d9e5d686c25 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -79,6 +79,9 @@ exprType(Node *expr)
case T_DistinctExpr:
type = ((DistinctExpr *) expr)->opresulttype;
break;
+ case T_NullIfExpr:
+ type = ((NullIfExpr *) expr)->opresulttype;
+ break;
case T_ScalarArrayOpExpr:
type = BOOLOID;
break;
@@ -203,9 +206,6 @@ exprType(Node *expr)
else
type = XMLOID;
break;
- case T_NullIfExpr:
- type = exprType((Node *) linitial(((NullIfExpr *) expr)->args));
- break;
case T_NullTest:
type = BOOLOID;
break;
@@ -268,6 +268,17 @@ exprTypmod(Node *expr)
break;
case T_NamedArgExpr:
return exprTypmod((Node *) ((NamedArgExpr *) expr)->arg);
+ case T_NullIfExpr:
+ {
+ /*
+ * Result is either first argument or NULL, so we can report
+ * first argument's typmod if known.
+ */
+ NullIfExpr *nexpr = (NullIfExpr *) expr;
+
+ return exprTypmod((Node *) linitial(nexpr->args));
+ }
+ break;
case T_SubLink:
{
SubLink *sublink = (SubLink *) expr;
@@ -444,13 +455,6 @@ exprTypmod(Node *expr)
return typmod;
}
break;
- case T_NullIfExpr:
- {
- NullIfExpr *nexpr = (NullIfExpr *) expr;
-
- return exprTypmod((Node *) linitial(nexpr->args));
- }
- break;
case T_CoerceToDomain:
return ((CoerceToDomain *) expr)->resulttypmod;
case T_CoerceToDomainValue:
@@ -466,8 +470,166 @@ exprTypmod(Node *expr)
}
/*
+ * exprIsLengthCoercion
+ * Detect whether an expression tree is an application of a datatype's
+ * typmod-coercion function. Optionally extract the result's typmod.
+ *
+ * If coercedTypmod is not NULL, the typmod is stored there if the expression
+ * is a length-coercion function, else -1 is stored there.
+ *
+ * Note that a combined type-and-length coercion will be treated as a
+ * length coercion by this routine.
+ */
+bool
+exprIsLengthCoercion(Node *expr, int32 *coercedTypmod)
+{
+ if (coercedTypmod != NULL)
+ *coercedTypmod = -1; /* default result on failure */
+
+ /*
+ * Scalar-type length coercions are FuncExprs, array-type length coercions
+ * are ArrayCoerceExprs
+ */
+ if (expr && IsA(expr, FuncExpr))
+ {
+ FuncExpr *func = (FuncExpr *) expr;
+ int nargs;
+ Const *second_arg;
+
+ /*
+ * If it didn't come from a coercion context, reject.
+ */
+ if (func->funcformat != COERCE_EXPLICIT_CAST &&
+ func->funcformat != COERCE_IMPLICIT_CAST)
+ return false;
+
+ /*
+ * If it's not a two-argument or three-argument function with the
+ * second argument being an int4 constant, it can't have been created
+ * from a length coercion (it must be a type coercion, instead).
+ */
+ nargs = list_length(func->args);
+ if (nargs < 2 || nargs > 3)
+ return false;
+
+ second_arg = (Const *) lsecond(func->args);
+ if (!IsA(second_arg, Const) ||
+ second_arg->consttype != INT4OID ||
+ second_arg->constisnull)
+ return false;
+
+ /*
+ * OK, it is indeed a length-coercion function.
+ */
+ if (coercedTypmod != NULL)
+ *coercedTypmod = DatumGetInt32(second_arg->constvalue);
+
+ return true;
+ }
+
+ if (expr && IsA(expr, ArrayCoerceExpr))
+ {
+ ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) expr;
+
+ /* It's not a length coercion unless there's a nondefault typmod */
+ if (acoerce->resulttypmod < 0)
+ return false;
+
+ /*
+ * OK, it is indeed a length-coercion expression.
+ */
+ if (coercedTypmod != NULL)
+ *coercedTypmod = acoerce->resulttypmod;
+
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * expression_returns_set
+ * Test whether an expression returns a set result.
+ *
+ * Because we use expression_tree_walker(), this can also be applied to
+ * whole targetlists; it'll produce TRUE if any one of the tlist items
+ * returns a set.
+ */
+bool
+expression_returns_set(Node *clause)
+{
+ return expression_returns_set_walker(clause, NULL);
+}
+
+static bool
+expression_returns_set_walker(Node *node, void *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, FuncExpr))
+ {
+ FuncExpr *expr = (FuncExpr *) node;
+
+ if (expr->funcretset)
+ return true;
+ /* else fall through to check args */
+ }
+ if (IsA(node, OpExpr))
+ {
+ OpExpr *expr = (OpExpr *) node;
+
+ if (expr->opretset)
+ return true;
+ /* else fall through to check args */
+ }
+
+ /* Avoid recursion for some cases that can't return a set */
+ if (IsA(node, Aggref))
+ return false;
+ if (IsA(node, WindowFunc))
+ return false;
+ if (IsA(node, DistinctExpr))
+ return false;
+ if (IsA(node, NullIfExpr))
+ return false;
+ if (IsA(node, ScalarArrayOpExpr))
+ return false;
+ if (IsA(node, BoolExpr))
+ return false;
+ if (IsA(node, SubLink))
+ return false;
+ if (IsA(node, SubPlan))
+ return false;
+ if (IsA(node, AlternativeSubPlan))
+ return false;
+ if (IsA(node, ArrayExpr))
+ return false;
+ if (IsA(node, RowExpr))
+ return false;
+ if (IsA(node, RowCompareExpr))
+ return false;
+ if (IsA(node, CoalesceExpr))
+ return false;
+ if (IsA(node, MinMaxExpr))
+ return false;
+ if (IsA(node, XmlExpr))
+ return false;
+
+ return expression_tree_walker(node, expression_returns_set_walker,
+ context);
+}
+
+
+/*
* exprCollation -
* returns the Oid of the collation of the expression's result.
+ *
+ * Note: expression nodes that can invoke functions generally have an
+ * "inputcollid" field, which is what the function should use as collation.
+ * That is the resolved common collation of the node's inputs. It is often
+ * but not always the same as the result collation; in particular, if the
+ * function produces a non-collatable result type from collatable inputs
+ * or vice versa, the two are different.
*/
Oid
exprCollation(Node *expr)
@@ -486,34 +648,37 @@ exprCollation(Node *expr)
coll = ((Const *) expr)->constcollid;
break;
case T_Param:
- coll = ((Param *) expr)->paramcollation;
+ coll = ((Param *) expr)->paramcollid;
break;
case T_Aggref:
- coll = ((Aggref *) expr)->collid;
+ coll = ((Aggref *) expr)->aggcollid;
break;
case T_WindowFunc:
- coll = ((WindowFunc *) expr)->collid;
+ coll = ((WindowFunc *) expr)->wincollid;
break;
case T_ArrayRef:
coll = ((ArrayRef *) expr)->refcollid;
break;
case T_FuncExpr:
- coll = ((FuncExpr *) expr)->collid;
+ coll = ((FuncExpr *) expr)->funccollid;
break;
case T_NamedArgExpr:
coll = exprCollation((Node *) ((NamedArgExpr *) expr)->arg);
break;
case T_OpExpr:
- coll = ((OpExpr *) expr)->collid;
+ coll = ((OpExpr *) expr)->opcollid;
break;
case T_DistinctExpr:
- coll = ((DistinctExpr *) expr)->collid;
+ coll = ((DistinctExpr *) expr)->opcollid;
+ break;
+ case T_NullIfExpr:
+ coll = ((NullIfExpr *) expr)->opcollid;
break;
case T_ScalarArrayOpExpr:
- coll = ((ScalarArrayOpExpr *) expr)->collid;
+ coll = InvalidOid; /* result is always boolean */
break;
case T_BoolExpr:
- coll = InvalidOid; /* not applicable */
+ coll = InvalidOid; /* result is always boolean */
break;
case T_SubLink:
{
@@ -522,7 +687,7 @@ exprCollation(Node *expr)
if (sublink->subLinkType == EXPR_SUBLINK ||
sublink->subLinkType == ARRAY_SUBLINK)
{
- /* get the collation of the subselect's first target column */
+ /* get the collation of subselect's first target column */
Query *qtree = (Query *) sublink->subselect;
TargetEntry *tent;
@@ -532,10 +697,13 @@ exprCollation(Node *expr)
Assert(IsA(tent, TargetEntry));
Assert(!tent->resjunk);
coll = exprCollation((Node *) tent->expr);
- /* note we don't need to care if it's an array */
+ /* collation doesn't change if it's converted to array */
}
else
+ {
+ /* for all other sublink types, result is boolean */
coll = InvalidOid;
+ }
}
break;
case T_SubPlan:
@@ -545,9 +713,9 @@ exprCollation(Node *expr)
if (subplan->subLinkType == EXPR_SUBLINK ||
subplan->subLinkType == ARRAY_SUBLINK)
{
- /* get the collation of the subselect's first target column */
- /* note we don't need to care if it's an array */
+ /* get the collation of subselect's first target column */
coll = subplan->firstColCollation;
+ /* collation doesn't change if it's converted to array */
}
else
{
@@ -565,84 +733,75 @@ exprCollation(Node *expr)
}
break;
case T_FieldSelect:
- coll = ((FieldSelect *) expr)->resultcollation;
+ coll = ((FieldSelect *) expr)->resultcollid;
break;
case T_FieldStore:
- coll = InvalidOid; /* not applicable */
+ coll = InvalidOid; /* result is always composite */
break;
case T_RelabelType:
- coll = exprCollation((Node *) ((RelabelType *) expr)->arg);
+ coll = ((RelabelType *) expr)->resultcollid;
break;
case T_CoerceViaIO:
- {
- CoerceViaIO *cvio = (CoerceViaIO *) expr;
- coll = coercion_expression_result_collation(cvio->resulttype, (Node *) cvio->arg);
+ coll = ((CoerceViaIO *) expr)->resultcollid;
break;
- }
case T_ArrayCoerceExpr:
- {
- ArrayCoerceExpr *ace = (ArrayCoerceExpr *) expr;
- coll = coercion_expression_result_collation(ace->resulttype, (Node *) ace->arg);
+ coll = ((ArrayCoerceExpr *) expr)->resultcollid;
break;
- }
case T_ConvertRowtypeExpr:
- {
- ConvertRowtypeExpr *cre = (ConvertRowtypeExpr *) expr;
- coll = coercion_expression_result_collation(cre->resulttype, (Node *) cre->arg);
+ coll = InvalidOid; /* result is always composite */
break;
- }
case T_CollateExpr:
coll = ((CollateExpr *) expr)->collOid;
break;
case T_CaseExpr:
- coll = ((CaseExpr *) expr)->casecollation;
+ coll = ((CaseExpr *) expr)->casecollid;
break;
case T_CaseTestExpr:
coll = ((CaseTestExpr *) expr)->collation;
break;
case T_ArrayExpr:
- coll = get_typcollation(((ArrayExpr *) expr)->array_typeid);
+ coll = ((ArrayExpr *) expr)->array_collid;
break;
case T_RowExpr:
- coll = InvalidOid; /* not applicable */
+ coll = InvalidOid; /* result is always composite */
break;
case T_RowCompareExpr:
- coll = InvalidOid; /* not applicable */
+ coll = InvalidOid; /* result is always boolean */
break;
case T_CoalesceExpr:
- coll = ((CoalesceExpr *) expr)->coalescecollation;
+ coll = ((CoalesceExpr *) expr)->coalescecollid;
break;
case T_MinMaxExpr:
- coll = ((MinMaxExpr *) expr)->collid;
+ coll = ((MinMaxExpr *) expr)->minmaxcollid;
break;
case T_XmlExpr:
+ /*
+ * XMLSERIALIZE returns text from non-collatable inputs, so its
+ * collation is always default. The other cases return boolean
+ * or XML, which are non-collatable.
+ */
if (((XmlExpr *) expr)->op == IS_XMLSERIALIZE)
coll = DEFAULT_COLLATION_OID;
else
coll = InvalidOid;
break;
- case T_NullIfExpr:
- coll = exprCollation((Node *) linitial(((NullIfExpr *) expr)->args));
- break;
case T_NullTest:
- coll = InvalidOid; /* not applicable */
+ coll = InvalidOid; /* result is always boolean */
break;
case T_BooleanTest:
- coll = InvalidOid; /* not applicable */
+ coll = InvalidOid; /* result is always boolean */
break;
case T_CoerceToDomain:
- coll = get_typcollation(((CoerceToDomain *) expr)->resulttype);
- if (coll == DEFAULT_COLLATION_OID)
- coll = exprCollation((Node *) ((CoerceToDomain *) expr)->arg);
+ coll = ((CoerceToDomain *) expr)->resultcollid;
break;
case T_CoerceToDomainValue:
- coll = get_typcollation(((CoerceToDomainValue *) expr)->typeId);
+ coll = ((CoerceToDomainValue *) expr)->collation;
break;
case T_SetToDefault:
- coll = ((SetToDefault *) expr)->collid;
+ coll = ((SetToDefault *) expr)->collation;
break;
case T_CurrentOfExpr:
- coll = InvalidOid; /* not applicable */
+ coll = InvalidOid; /* result is always boolean */
break;
case T_PlaceHolderVar:
coll = exprCollation((Node *) ((PlaceHolderVar *) expr)->phexpr);
@@ -652,176 +811,239 @@ exprCollation(Node *expr)
coll = InvalidOid; /* keep compiler quiet */
break;
}
-
return coll;
}
/*
- * Compute the result collation of a coercion-like expression that
- * converts arg to resulttype.
+ * exprInputCollation -
+ * returns the Oid of the collation a function should use, if available.
+ *
+ * Result is InvalidOid if the node type doesn't store this information.
*/
Oid
-coercion_expression_result_collation(Oid resulttype, Node *arg)
+exprInputCollation(Node *expr)
{
- if (type_is_collatable(resulttype))
+ Oid coll;
+
+ if (!expr)
+ return InvalidOid;
+
+ switch (nodeTag(expr))
{
- if (type_is_collatable(exprType(arg)))
- return exprCollation(arg);
- else
- return DEFAULT_COLLATION_OID;
+ case T_Aggref:
+ coll = ((Aggref *) expr)->inputcollid;
+ break;
+ case T_WindowFunc:
+ coll = ((WindowFunc *) expr)->inputcollid;
+ break;
+ case T_FuncExpr:
+ coll = ((FuncExpr *) expr)->inputcollid;
+ break;
+ case T_OpExpr:
+ coll = ((OpExpr *) expr)->inputcollid;
+ break;
+ case T_DistinctExpr:
+ coll = ((DistinctExpr *) expr)->inputcollid;
+ break;
+ case T_NullIfExpr:
+ coll = ((NullIfExpr *) expr)->inputcollid;
+ break;
+ case T_ScalarArrayOpExpr:
+ coll = ((ScalarArrayOpExpr *) expr)->inputcollid;
+ break;
+ case T_MinMaxExpr:
+ coll = ((MinMaxExpr *) expr)->inputcollid;
+ break;
+ default:
+ coll = InvalidOid;
+ break;
}
- else
- return InvalidOid;
+ return coll;
}
/*
- * exprIsLengthCoercion
- * Detect whether an expression tree is an application of a datatype's
- * typmod-coercion function. Optionally extract the result's typmod.
- *
- * If coercedTypmod is not NULL, the typmod is stored there if the expression
- * is a length-coercion function, else -1 is stored there.
+ * exprSetCollation -
+ * Assign collation information to an expression tree node.
*
- * Note that a combined type-and-length coercion will be treated as a
- * length coercion by this routine.
+ * Note: since this is only used during parse analysis, we don't need to
+ * worry about subplans or PlaceHolderVars.
*/
-bool
-exprIsLengthCoercion(Node *expr, int32 *coercedTypmod)
+void
+exprSetCollation(Node *expr, Oid collation)
{
- if (coercedTypmod != NULL)
- *coercedTypmod = -1; /* default result on failure */
-
- /*
- * Scalar-type length coercions are FuncExprs, array-type length coercions
- * are ArrayCoerceExprs
- */
- if (expr && IsA(expr, FuncExpr))
- {
- FuncExpr *func = (FuncExpr *) expr;
- int nargs;
- Const *second_arg;
-
- /*
- * If it didn't come from a coercion context, reject.
- */
- if (func->funcformat != COERCE_EXPLICIT_CAST &&
- func->funcformat != COERCE_IMPLICIT_CAST)
- return false;
-
- /*
- * If it's not a two-argument or three-argument function with the
- * second argument being an int4 constant, it can't have been created
- * from a length coercion (it must be a type coercion, instead).
- */
- nargs = list_length(func->args);
- if (nargs < 2 || nargs > 3)
- return false;
-
- second_arg = (Const *) lsecond(func->args);
- if (!IsA(second_arg, Const) ||
- second_arg->consttype != INT4OID ||
- second_arg->constisnull)
- return false;
-
- /*
- * OK, it is indeed a length-coercion function.
- */
- if (coercedTypmod != NULL)
- *coercedTypmod = DatumGetInt32(second_arg->constvalue);
-
- return true;
- }
-
- if (expr && IsA(expr, ArrayCoerceExpr))
+ switch (nodeTag(expr))
{
- ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) expr;
-
- /* It's not a length coercion unless there's a nondefault typmod */
- if (acoerce->resulttypmod < 0)
- return false;
+ case T_Var:
+ ((Var *) expr)->varcollid = collation;
+ break;
+ case T_Const:
+ ((Const *) expr)->constcollid = collation;
+ break;
+ case T_Param:
+ ((Param *) expr)->paramcollid = collation;
+ break;
+ case T_Aggref:
+ ((Aggref *) expr)->aggcollid = collation;
+ break;
+ case T_WindowFunc:
+ ((WindowFunc *) expr)->wincollid = collation;
+ break;
+ case T_ArrayRef:
+ ((ArrayRef *) expr)->refcollid = collation;
+ break;
+ case T_FuncExpr:
+ ((FuncExpr *) expr)->funccollid = collation;
+ break;
+ case T_NamedArgExpr:
+ Assert(collation == exprCollation((Node *) ((NamedArgExpr *) expr)->arg));
+ break;
+ case T_OpExpr:
+ ((OpExpr *) expr)->opcollid = collation;
+ break;
+ case T_DistinctExpr:
+ ((DistinctExpr *) expr)->opcollid = collation;
+ break;
+ case T_NullIfExpr:
+ ((NullIfExpr *) expr)->opcollid = collation;
+ break;
+ case T_ScalarArrayOpExpr:
+ Assert(!OidIsValid(collation)); /* result is always boolean */
+ break;
+ case T_BoolExpr:
+ Assert(!OidIsValid(collation)); /* result is always boolean */
+ break;
+ case T_SubLink:
+#ifdef USE_ASSERT_CHECKING
+ {
+ SubLink *sublink = (SubLink *) expr;
- /*
- * OK, it is indeed a length-coercion expression.
- */
- if (coercedTypmod != NULL)
- *coercedTypmod = acoerce->resulttypmod;
+ if (sublink->subLinkType == EXPR_SUBLINK ||
+ sublink->subLinkType == ARRAY_SUBLINK)
+ {
+ /* get the collation of subselect's first target column */
+ Query *qtree = (Query *) sublink->subselect;
+ TargetEntry *tent;
- return true;
+ if (!qtree || !IsA(qtree, Query))
+ elog(ERROR, "cannot set collation for untransformed sublink");
+ tent = (TargetEntry *) linitial(qtree->targetList);
+ Assert(IsA(tent, TargetEntry));
+ Assert(!tent->resjunk);
+ Assert(collation == exprCollation((Node *) tent->expr));
+ }
+ else
+ {
+ /* for all other sublink types, result is boolean */
+ Assert(!OidIsValid(collation));
+ }
+ }
+#endif /* USE_ASSERT_CHECKING */
+ break;
+ case T_FieldSelect:
+ ((FieldSelect *) expr)->resultcollid = collation;
+ break;
+ case T_FieldStore:
+ Assert(!OidIsValid(collation)); /* result is always composite */
+ break;
+ case T_RelabelType:
+ ((RelabelType *) expr)->resultcollid = collation;
+ break;
+ case T_CoerceViaIO:
+ ((CoerceViaIO *) expr)->resultcollid = collation;
+ break;
+ case T_ArrayCoerceExpr:
+ ((ArrayCoerceExpr *) expr)->resultcollid = collation;
+ break;
+ case T_ConvertRowtypeExpr:
+ Assert(!OidIsValid(collation)); /* result is always composite */
+ break;
+ case T_CaseExpr:
+ ((CaseExpr *) expr)->casecollid = collation;
+ break;
+ case T_ArrayExpr:
+ ((ArrayExpr *) expr)->array_collid = collation;
+ break;
+ case T_RowExpr:
+ Assert(!OidIsValid(collation)); /* result is always composite */
+ break;
+ case T_RowCompareExpr:
+ Assert(!OidIsValid(collation)); /* result is always boolean */
+ break;
+ case T_CoalesceExpr:
+ ((CoalesceExpr *) expr)->coalescecollid = collation;
+ break;
+ case T_MinMaxExpr:
+ ((MinMaxExpr *) expr)->minmaxcollid = collation;
+ break;
+ case T_XmlExpr:
+ Assert((((XmlExpr *) expr)->op == IS_XMLSERIALIZE) ?
+ (collation == DEFAULT_COLLATION_OID) :
+ (collation == InvalidOid));
+ break;
+ case T_NullTest:
+ Assert(!OidIsValid(collation)); /* result is always boolean */
+ break;
+ case T_BooleanTest:
+ Assert(!OidIsValid(collation)); /* result is always boolean */
+ break;
+ case T_CoerceToDomain:
+ ((CoerceToDomain *) expr)->resultcollid = collation;
+ break;
+ case T_CoerceToDomainValue:
+ ((CoerceToDomainValue *) expr)->collation = collation;
+ break;
+ case T_SetToDefault:
+ ((SetToDefault *) expr)->collation = collation;
+ break;
+ case T_CurrentOfExpr:
+ Assert(!OidIsValid(collation)); /* result is always boolean */
+ break;
+ default:
+ elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
+ break;
}
-
- return false;
}
/*
- * expression_returns_set
- * Test whether an expression returns a set result.
+ * exprSetInputCollation -
+ * Assign input-collation information to an expression tree node.
*
- * Because we use expression_tree_walker(), this can also be applied to
- * whole targetlists; it'll produce TRUE if any one of the tlist items
- * returns a set.
+ * This is a no-op for node types that don't store their input collation.
+ * Note we omit RowCompareExpr, which needs special treatment since it
+ * contains multiple input collation OIDs.
*/
-bool
-expression_returns_set(Node *clause)
+void
+exprSetInputCollation(Node *expr, Oid inputcollation)
{
- return expression_returns_set_walker(clause, NULL);
-}
-
-static bool
-expression_returns_set_walker(Node *node, void *context)
-{
- if (node == NULL)
- return false;
- if (IsA(node, FuncExpr))
- {
- FuncExpr *expr = (FuncExpr *) node;
-
- if (expr->funcretset)
- return true;
- /* else fall through to check args */
- }
- if (IsA(node, OpExpr))
+ switch (nodeTag(expr))
{
- OpExpr *expr = (OpExpr *) node;
-
- if (expr->opretset)
- return true;
- /* else fall through to check args */
+ case T_Aggref:
+ ((Aggref *) expr)->inputcollid = inputcollation;
+ break;
+ case T_WindowFunc:
+ ((WindowFunc *) expr)->inputcollid = inputcollation;
+ break;
+ case T_FuncExpr:
+ ((FuncExpr *) expr)->inputcollid = inputcollation;
+ break;
+ case T_OpExpr:
+ ((OpExpr *) expr)->inputcollid = inputcollation;
+ break;
+ case T_DistinctExpr:
+ ((DistinctExpr *) expr)->inputcollid = inputcollation;
+ break;
+ case T_NullIfExpr:
+ ((NullIfExpr *) expr)->inputcollid = inputcollation;
+ break;
+ case T_ScalarArrayOpExpr:
+ ((ScalarArrayOpExpr *) expr)->inputcollid = inputcollation;
+ break;
+ case T_MinMaxExpr:
+ ((MinMaxExpr *) expr)->inputcollid = inputcollation;
+ break;
+ default:
+ break;
}
-
- /* Avoid recursion for some cases that can't return a set */
- if (IsA(node, Aggref))
- return false;
- if (IsA(node, WindowFunc))
- return false;
- if (IsA(node, DistinctExpr))
- return false;
- if (IsA(node, ScalarArrayOpExpr))
- return false;
- if (IsA(node, BoolExpr))
- return false;
- if (IsA(node, SubLink))
- return false;
- if (IsA(node, SubPlan))
- return false;
- if (IsA(node, AlternativeSubPlan))
- return false;
- if (IsA(node, ArrayExpr))
- return false;
- if (IsA(node, RowExpr))
- return false;
- if (IsA(node, RowCompareExpr))
- return false;
- if (IsA(node, CoalesceExpr))
- return false;
- if (IsA(node, MinMaxExpr))
- return false;
- if (IsA(node, XmlExpr))
- return false;
- if (IsA(node, NullIfExpr))
- return false;
-
- return expression_tree_walker(node, expression_returns_set_walker,
- context);
}
@@ -1365,6 +1587,8 @@ expression_tree_walker(Node *node,
case T_NamedArgExpr:
return walker(((NamedArgExpr *) node)->arg, context);
case T_OpExpr:
+ case T_DistinctExpr: /* struct-equivalent to OpExpr */
+ case T_NullIfExpr: /* struct-equivalent to OpExpr */
{
OpExpr *expr = (OpExpr *) node;
@@ -1373,15 +1597,6 @@ expression_tree_walker(Node *node,
return true;
}
break;
- case T_DistinctExpr:
- {
- DistinctExpr *expr = (DistinctExpr *) node;
-
- if (expression_tree_walker((Node *) expr->args,
- walker, context))
- return true;
- }
- break;
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -1502,8 +1717,6 @@ expression_tree_walker(Node *node,
return true;
}
break;
- case T_NullIfExpr:
- return walker(((NullIfExpr *) node)->args, context);
case T_NullTest:
return walker(((NullTest *) node)->arg, context);
case T_BooleanTest:
@@ -1648,8 +1861,11 @@ query_tree_walker(Query *query,
if (walker((Node *) query->cteList, context))
return true;
}
- if (range_table_walker(query->rtable, walker, context, flags))
- return true;
+ if (!(flags & QTW_IGNORE_RANGE_TABLE))
+ {
+ if (range_table_walker(query->rtable, walker, context, flags))
+ return true;
+ }
return false;
}
@@ -1908,6 +2124,16 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
+ case T_NullIfExpr:
+ {
+ NullIfExpr *expr = (NullIfExpr *) node;
+ NullIfExpr *newnode;
+
+ FLATCOPY(newnode, expr, NullIfExpr);
+ MUTATE(newnode->args, expr->args, List *);
+ return (Node *) newnode;
+ }
+ break;
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -2127,16 +2353,6 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
- case T_NullIfExpr:
- {
- NullIfExpr *expr = (NullIfExpr *) node;
- NullIfExpr *newnode;
-
- FLATCOPY(newnode, expr, NullIfExpr);
- MUTATE(newnode->args, expr->args, List *);
- return (Node *) newnode;
- }
- break;
case T_NullTest:
{
NullTest *ntest = (NullTest *) node;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index d56e4dac011..db4a33c30d1 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -939,7 +939,7 @@ _outParam(StringInfo str, Param *node)
WRITE_INT_FIELD(paramid);
WRITE_OID_FIELD(paramtype);
WRITE_INT_FIELD(paramtypmod);
- WRITE_OID_FIELD(paramcollation);
+ WRITE_OID_FIELD(paramcollid);
WRITE_LOCATION_FIELD(location);
}
@@ -950,12 +950,13 @@ _outAggref(StringInfo str, Aggref *node)
WRITE_OID_FIELD(aggfnoid);
WRITE_OID_FIELD(aggtype);
+ WRITE_OID_FIELD(aggcollid);
+ WRITE_OID_FIELD(inputcollid);
WRITE_NODE_FIELD(args);
WRITE_NODE_FIELD(aggorder);
WRITE_NODE_FIELD(aggdistinct);
WRITE_BOOL_FIELD(aggstar);
WRITE_UINT_FIELD(agglevelsup);
- WRITE_OID_FIELD(collid);
WRITE_LOCATION_FIELD(location);
}
@@ -966,11 +967,12 @@ _outWindowFunc(StringInfo str, WindowFunc *node)
WRITE_OID_FIELD(winfnoid);
WRITE_OID_FIELD(wintype);
+ WRITE_OID_FIELD(wincollid);
+ WRITE_OID_FIELD(inputcollid);
WRITE_NODE_FIELD(args);
WRITE_UINT_FIELD(winref);
WRITE_BOOL_FIELD(winstar);
WRITE_BOOL_FIELD(winagg);
- WRITE_OID_FIELD(collid);
WRITE_LOCATION_FIELD(location);
}
@@ -982,7 +984,7 @@ _outArrayRef(StringInfo str, ArrayRef *node)
WRITE_OID_FIELD(refarraytype);
WRITE_OID_FIELD(refelemtype);
WRITE_INT_FIELD(reftypmod);
- WRITE_INT_FIELD(refcollid);
+ WRITE_OID_FIELD(refcollid);
WRITE_NODE_FIELD(refupperindexpr);
WRITE_NODE_FIELD(reflowerindexpr);
WRITE_NODE_FIELD(refexpr);
@@ -998,8 +1000,9 @@ _outFuncExpr(StringInfo str, FuncExpr *node)
WRITE_OID_FIELD(funcresulttype);
WRITE_BOOL_FIELD(funcretset);
WRITE_ENUM_FIELD(funcformat, CoercionForm);
+ WRITE_OID_FIELD(funccollid);
+ WRITE_OID_FIELD(inputcollid);
WRITE_NODE_FIELD(args);
- WRITE_OID_FIELD(collid);
WRITE_LOCATION_FIELD(location);
}
@@ -1023,8 +1026,9 @@ _outOpExpr(StringInfo str, OpExpr *node)
WRITE_OID_FIELD(opfuncid);
WRITE_OID_FIELD(opresulttype);
WRITE_BOOL_FIELD(opretset);
+ WRITE_OID_FIELD(opcollid);
+ WRITE_OID_FIELD(inputcollid);
WRITE_NODE_FIELD(args);
- WRITE_OID_FIELD(collid);
WRITE_LOCATION_FIELD(location);
}
@@ -1037,8 +1041,24 @@ _outDistinctExpr(StringInfo str, DistinctExpr *node)
WRITE_OID_FIELD(opfuncid);
WRITE_OID_FIELD(opresulttype);
WRITE_BOOL_FIELD(opretset);
+ WRITE_OID_FIELD(opcollid);
+ WRITE_OID_FIELD(inputcollid);
+ WRITE_NODE_FIELD(args);
+ WRITE_LOCATION_FIELD(location);
+}
+
+static void
+_outNullIfExpr(StringInfo str, NullIfExpr *node)
+{
+ WRITE_NODE_TYPE("NULLIFEXPR");
+
+ WRITE_OID_FIELD(opno);
+ WRITE_OID_FIELD(opfuncid);
+ WRITE_OID_FIELD(opresulttype);
+ WRITE_BOOL_FIELD(opretset);
+ WRITE_OID_FIELD(opcollid);
+ WRITE_OID_FIELD(inputcollid);
WRITE_NODE_FIELD(args);
- WRITE_OID_FIELD(collid);
WRITE_LOCATION_FIELD(location);
}
@@ -1050,8 +1070,8 @@ _outScalarArrayOpExpr(StringInfo str, ScalarArrayOpExpr *node)
WRITE_OID_FIELD(opno);
WRITE_OID_FIELD(opfuncid);
WRITE_BOOL_FIELD(useOr);
+ WRITE_OID_FIELD(inputcollid);
WRITE_NODE_FIELD(args);
- WRITE_OID_FIELD(collid);
WRITE_LOCATION_FIELD(location);
}
@@ -1133,7 +1153,7 @@ _outFieldSelect(StringInfo str, FieldSelect *node)
WRITE_INT_FIELD(fieldnum);
WRITE_OID_FIELD(resulttype);
WRITE_INT_FIELD(resulttypmod);
- WRITE_OID_FIELD(resultcollation);
+ WRITE_OID_FIELD(resultcollid);
}
static void
@@ -1155,6 +1175,7 @@ _outRelabelType(StringInfo str, RelabelType *node)
WRITE_NODE_FIELD(arg);
WRITE_OID_FIELD(resulttype);
WRITE_INT_FIELD(resulttypmod);
+ WRITE_OID_FIELD(resultcollid);
WRITE_ENUM_FIELD(relabelformat, CoercionForm);
WRITE_LOCATION_FIELD(location);
}
@@ -1166,6 +1187,7 @@ _outCoerceViaIO(StringInfo str, CoerceViaIO *node)
WRITE_NODE_FIELD(arg);
WRITE_OID_FIELD(resulttype);
+ WRITE_OID_FIELD(resultcollid);
WRITE_ENUM_FIELD(coerceformat, CoercionForm);
WRITE_LOCATION_FIELD(location);
}
@@ -1179,6 +1201,7 @@ _outArrayCoerceExpr(StringInfo str, ArrayCoerceExpr *node)
WRITE_OID_FIELD(elemfuncid);
WRITE_OID_FIELD(resulttype);
WRITE_INT_FIELD(resulttypmod);
+ WRITE_OID_FIELD(resultcollid);
WRITE_BOOL_FIELD(isExplicit);
WRITE_ENUM_FIELD(coerceformat, CoercionForm);
WRITE_LOCATION_FIELD(location);
@@ -1211,7 +1234,7 @@ _outCaseExpr(StringInfo str, CaseExpr *node)
WRITE_NODE_TYPE("CASE");
WRITE_OID_FIELD(casetype);
- WRITE_OID_FIELD(casecollation);
+ WRITE_OID_FIELD(casecollid);
WRITE_NODE_FIELD(arg);
WRITE_NODE_FIELD(args);
WRITE_NODE_FIELD(defresult);
@@ -1244,6 +1267,7 @@ _outArrayExpr(StringInfo str, ArrayExpr *node)
WRITE_NODE_TYPE("ARRAY");
WRITE_OID_FIELD(array_typeid);
+ WRITE_OID_FIELD(array_collid);
WRITE_OID_FIELD(element_typeid);
WRITE_NODE_FIELD(elements);
WRITE_BOOL_FIELD(multidims);
@@ -1270,7 +1294,7 @@ _outRowCompareExpr(StringInfo str, RowCompareExpr *node)
WRITE_ENUM_FIELD(rctype, RowCompareType);
WRITE_NODE_FIELD(opnos);
WRITE_NODE_FIELD(opfamilies);
- WRITE_NODE_FIELD(collids);
+ WRITE_NODE_FIELD(inputcollids);
WRITE_NODE_FIELD(largs);
WRITE_NODE_FIELD(rargs);
}
@@ -1281,7 +1305,7 @@ _outCoalesceExpr(StringInfo str, CoalesceExpr *node)
WRITE_NODE_TYPE("COALESCE");
WRITE_OID_FIELD(coalescetype);
- WRITE_OID_FIELD(coalescecollation);
+ WRITE_OID_FIELD(coalescecollid);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
}
@@ -1292,9 +1316,10 @@ _outMinMaxExpr(StringInfo str, MinMaxExpr *node)
WRITE_NODE_TYPE("MINMAX");
WRITE_OID_FIELD(minmaxtype);
+ WRITE_OID_FIELD(minmaxcollid);
+ WRITE_OID_FIELD(inputcollid);
WRITE_ENUM_FIELD(op, MinMaxOp);
WRITE_NODE_FIELD(args);
- WRITE_OID_FIELD(collid);
WRITE_LOCATION_FIELD(location);
}
@@ -1315,19 +1340,6 @@ _outXmlExpr(StringInfo str, XmlExpr *node)
}
static void
-_outNullIfExpr(StringInfo str, NullIfExpr *node)
-{
- WRITE_NODE_TYPE("NULLIFEXPR");
-
- WRITE_OID_FIELD(opno);
- WRITE_OID_FIELD(opfuncid);
- WRITE_OID_FIELD(opresulttype);
- WRITE_BOOL_FIELD(opretset);
- WRITE_NODE_FIELD(args);
- WRITE_LOCATION_FIELD(location);
-}
-
-static void
_outNullTest(StringInfo str, NullTest *node)
{
WRITE_NODE_TYPE("NULLTEST");
@@ -1354,6 +1366,7 @@ _outCoerceToDomain(StringInfo str, CoerceToDomain *node)
WRITE_NODE_FIELD(arg);
WRITE_OID_FIELD(resulttype);
WRITE_INT_FIELD(resulttypmod);
+ WRITE_OID_FIELD(resultcollid);
WRITE_ENUM_FIELD(coercionformat, CoercionForm);
WRITE_LOCATION_FIELD(location);
}
@@ -1365,6 +1378,7 @@ _outCoerceToDomainValue(StringInfo str, CoerceToDomainValue *node)
WRITE_OID_FIELD(typeId);
WRITE_INT_FIELD(typeMod);
+ WRITE_OID_FIELD(collation);
WRITE_LOCATION_FIELD(location);
}
@@ -1375,7 +1389,7 @@ _outSetToDefault(StringInfo str, SetToDefault *node)
WRITE_OID_FIELD(typeId);
WRITE_INT_FIELD(typeMod);
- WRITE_OID_FIELD(collid);
+ WRITE_OID_FIELD(collation);
WRITE_LOCATION_FIELD(location);
}
@@ -1766,6 +1780,7 @@ _outEquivalenceClass(StringInfo str, EquivalenceClass *node)
WRITE_NODE_TYPE("EQUIVALENCECLASS");
WRITE_NODE_FIELD(ec_opfamilies);
+ WRITE_OID_FIELD(ec_collation);
WRITE_NODE_FIELD(ec_members);
WRITE_NODE_FIELD(ec_sources);
WRITE_NODE_FIELD(ec_derives);
@@ -1796,7 +1811,6 @@ _outPathKey(StringInfo str, PathKey *node)
WRITE_NODE_FIELD(pk_eclass);
WRITE_OID_FIELD(pk_opfamily);
- WRITE_OID_FIELD(pk_collation);
WRITE_INT_FIELD(pk_strategy);
WRITE_BOOL_FIELD(pk_nulls_first);
}
@@ -2814,6 +2828,9 @@ _outNode(StringInfo str, void *obj)
case T_DistinctExpr:
_outDistinctExpr(str, obj);
break;
+ case T_NullIfExpr:
+ _outNullIfExpr(str, obj);
+ break;
case T_ScalarArrayOpExpr:
_outScalarArrayOpExpr(str, obj);
break;
@@ -2877,9 +2894,6 @@ _outNode(StringInfo str, void *obj)
case T_XmlExpr:
_outXmlExpr(str, obj);
break;
- case T_NullIfExpr:
- _outNullIfExpr(str, obj);
- break;
case T_NullTest:
_outNullTest(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 6da61285b00..5f1fd32b9f2 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -455,7 +455,7 @@ _readParam(void)
READ_INT_FIELD(paramid);
READ_OID_FIELD(paramtype);
READ_INT_FIELD(paramtypmod);
- READ_OID_FIELD(paramcollation);
+ READ_OID_FIELD(paramcollid);
READ_LOCATION_FIELD(location);
READ_DONE();
@@ -471,12 +471,13 @@ _readAggref(void)
READ_OID_FIELD(aggfnoid);
READ_OID_FIELD(aggtype);
+ READ_OID_FIELD(aggcollid);
+ READ_OID_FIELD(inputcollid);
READ_NODE_FIELD(args);
READ_NODE_FIELD(aggorder);
READ_NODE_FIELD(aggdistinct);
READ_BOOL_FIELD(aggstar);
READ_UINT_FIELD(agglevelsup);
- READ_OID_FIELD(collid);
READ_LOCATION_FIELD(location);
READ_DONE();
@@ -492,11 +493,12 @@ _readWindowFunc(void)
READ_OID_FIELD(winfnoid);
READ_OID_FIELD(wintype);
+ READ_OID_FIELD(wincollid);
+ READ_OID_FIELD(inputcollid);
READ_NODE_FIELD(args);
READ_UINT_FIELD(winref);
READ_BOOL_FIELD(winstar);
READ_BOOL_FIELD(winagg);
- READ_OID_FIELD(collid);
READ_LOCATION_FIELD(location);
READ_DONE();
@@ -513,7 +515,7 @@ _readArrayRef(void)
READ_OID_FIELD(refarraytype);
READ_OID_FIELD(refelemtype);
READ_INT_FIELD(reftypmod);
- READ_INT_FIELD(refcollid);
+ READ_OID_FIELD(refcollid);
READ_NODE_FIELD(refupperindexpr);
READ_NODE_FIELD(reflowerindexpr);
READ_NODE_FIELD(refexpr);
@@ -534,8 +536,9 @@ _readFuncExpr(void)
READ_OID_FIELD(funcresulttype);
READ_BOOL_FIELD(funcretset);
READ_ENUM_FIELD(funcformat, CoercionForm);
+ READ_OID_FIELD(funccollid);
+ READ_OID_FIELD(inputcollid);
READ_NODE_FIELD(args);
- READ_OID_FIELD(collid);
READ_LOCATION_FIELD(location);
READ_DONE();
@@ -580,8 +583,9 @@ _readOpExpr(void)
READ_OID_FIELD(opresulttype);
READ_BOOL_FIELD(opretset);
+ READ_OID_FIELD(opcollid);
+ READ_OID_FIELD(inputcollid);
READ_NODE_FIELD(args);
- READ_OID_FIELD(collid);
READ_LOCATION_FIELD(location);
READ_DONE();
@@ -610,8 +614,40 @@ _readDistinctExpr(void)
READ_OID_FIELD(opresulttype);
READ_BOOL_FIELD(opretset);
+ READ_OID_FIELD(opcollid);
+ READ_OID_FIELD(inputcollid);
+ READ_NODE_FIELD(args);
+ READ_LOCATION_FIELD(location);
+
+ READ_DONE();
+}
+
+/*
+ * _readNullIfExpr
+ */
+static NullIfExpr *
+_readNullIfExpr(void)
+{
+ READ_LOCALS(NullIfExpr);
+
+ READ_OID_FIELD(opno);
+ READ_OID_FIELD(opfuncid);
+
+ /*
+ * The opfuncid is stored in the textual format primarily for debugging
+ * and documentation reasons. We want to always read it as zero to force
+ * it to be re-looked-up in the pg_operator entry. This ensures that
+ * stored rules don't have hidden dependencies on operators' functions.
+ * (We don't currently support an ALTER OPERATOR command, but might
+ * someday.)
+ */
+ local_node->opfuncid = InvalidOid;
+
+ READ_OID_FIELD(opresulttype);
+ READ_BOOL_FIELD(opretset);
+ READ_OID_FIELD(opcollid);
+ READ_OID_FIELD(inputcollid);
READ_NODE_FIELD(args);
- READ_OID_FIELD(collid);
READ_LOCATION_FIELD(location);
READ_DONE();
@@ -639,8 +675,8 @@ _readScalarArrayOpExpr(void)
local_node->opfuncid = InvalidOid;
READ_BOOL_FIELD(useOr);
+ READ_OID_FIELD(inputcollid);
READ_NODE_FIELD(args);
- READ_OID_FIELD(collid);
READ_LOCATION_FIELD(location);
READ_DONE();
@@ -705,7 +741,7 @@ _readFieldSelect(void)
READ_INT_FIELD(fieldnum);
READ_OID_FIELD(resulttype);
READ_INT_FIELD(resulttypmod);
- READ_OID_FIELD(resultcollation);
+ READ_OID_FIELD(resultcollid);
READ_DONE();
}
@@ -737,6 +773,7 @@ _readRelabelType(void)
READ_NODE_FIELD(arg);
READ_OID_FIELD(resulttype);
READ_INT_FIELD(resulttypmod);
+ READ_OID_FIELD(resultcollid);
READ_ENUM_FIELD(relabelformat, CoercionForm);
READ_LOCATION_FIELD(location);
@@ -753,6 +790,7 @@ _readCoerceViaIO(void)
READ_NODE_FIELD(arg);
READ_OID_FIELD(resulttype);
+ READ_OID_FIELD(resultcollid);
READ_ENUM_FIELD(coerceformat, CoercionForm);
READ_LOCATION_FIELD(location);
@@ -771,6 +809,7 @@ _readArrayCoerceExpr(void)
READ_OID_FIELD(elemfuncid);
READ_OID_FIELD(resulttype);
READ_INT_FIELD(resulttypmod);
+ READ_OID_FIELD(resultcollid);
READ_BOOL_FIELD(isExplicit);
READ_ENUM_FIELD(coerceformat, CoercionForm);
READ_LOCATION_FIELD(location);
@@ -818,7 +857,7 @@ _readCaseExpr(void)
READ_LOCALS(CaseExpr);
READ_OID_FIELD(casetype);
- READ_OID_FIELD(casecollation);
+ READ_OID_FIELD(casecollid);
READ_NODE_FIELD(arg);
READ_NODE_FIELD(args);
READ_NODE_FIELD(defresult);
@@ -866,6 +905,7 @@ _readArrayExpr(void)
READ_LOCALS(ArrayExpr);
READ_OID_FIELD(array_typeid);
+ READ_OID_FIELD(array_collid);
READ_OID_FIELD(element_typeid);
READ_NODE_FIELD(elements);
READ_BOOL_FIELD(multidims);
@@ -902,7 +942,7 @@ _readRowCompareExpr(void)
READ_ENUM_FIELD(rctype, RowCompareType);
READ_NODE_FIELD(opnos);
READ_NODE_FIELD(opfamilies);
- READ_NODE_FIELD(collids);
+ READ_NODE_FIELD(inputcollids);
READ_NODE_FIELD(largs);
READ_NODE_FIELD(rargs);
@@ -918,7 +958,7 @@ _readCoalesceExpr(void)
READ_LOCALS(CoalesceExpr);
READ_OID_FIELD(coalescetype);
- READ_OID_FIELD(coalescecollation);
+ READ_OID_FIELD(coalescecollid);
READ_NODE_FIELD(args);
READ_LOCATION_FIELD(location);
@@ -934,9 +974,10 @@ _readMinMaxExpr(void)
READ_LOCALS(MinMaxExpr);
READ_OID_FIELD(minmaxtype);
+ READ_OID_FIELD(minmaxcollid);
+ READ_OID_FIELD(inputcollid);
READ_ENUM_FIELD(op, MinMaxOp);
READ_NODE_FIELD(args);
- READ_OID_FIELD(collid);
READ_LOCATION_FIELD(location);
READ_DONE();
@@ -964,35 +1005,6 @@ _readXmlExpr(void)
}
/*
- * _readNullIfExpr
- */
-static NullIfExpr *
-_readNullIfExpr(void)
-{
- READ_LOCALS(NullIfExpr);
-
- READ_OID_FIELD(opno);
- READ_OID_FIELD(opfuncid);
-
- /*
- * The opfuncid is stored in the textual format primarily for debugging
- * and documentation reasons. We want to always read it as zero to force
- * it to be re-looked-up in the pg_operator entry. This ensures that
- * stored rules don't have hidden dependencies on operators' functions.
- * (We don't currently support an ALTER OPERATOR command, but might
- * someday.)
- */
- local_node->opfuncid = InvalidOid;
-
- READ_OID_FIELD(opresulttype);
- READ_BOOL_FIELD(opretset);
- READ_NODE_FIELD(args);
- READ_LOCATION_FIELD(location);
-
- READ_DONE();
-}
-
-/*
* _readNullTest
*/
static NullTest *
@@ -1032,6 +1044,7 @@ _readCoerceToDomain(void)
READ_NODE_FIELD(arg);
READ_OID_FIELD(resulttype);
READ_INT_FIELD(resulttypmod);
+ READ_OID_FIELD(resultcollid);
READ_ENUM_FIELD(coercionformat, CoercionForm);
READ_LOCATION_FIELD(location);
@@ -1048,6 +1061,7 @@ _readCoerceToDomainValue(void)
READ_OID_FIELD(typeId);
READ_INT_FIELD(typeMod);
+ READ_OID_FIELD(collation);
READ_LOCATION_FIELD(location);
READ_DONE();
@@ -1063,7 +1077,7 @@ _readSetToDefault(void)
READ_OID_FIELD(typeId);
READ_INT_FIELD(typeMod);
- READ_OID_FIELD(collid);
+ READ_OID_FIELD(collation);
READ_LOCATION_FIELD(location);
READ_DONE();
@@ -1273,6 +1287,8 @@ parseNodeString(void)
return_value = _readOpExpr();
else if (MATCH("DISTINCTEXPR", 12))
return_value = _readDistinctExpr();
+ else if (MATCH("NULLIFEXPR", 10))
+ return_value = _readNullIfExpr();
else if (MATCH("SCALARARRAYOPEXPR", 17))
return_value = _readScalarArrayOpExpr();
else if (MATCH("BOOLEXPR", 8))
@@ -1311,8 +1327,6 @@ parseNodeString(void)
return_value = _readMinMaxExpr();
else if (MATCH("XMLEXPR", 7))
return_value = _readXmlExpr();
- else if (MATCH("NULLIFEXPR", 10))
- return_value = _readNullIfExpr();
else if (MATCH("NULLTEST", 8))
return_value = _readNullTest();
else if (MATCH("BOOLEANTEST", 11))
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 756874b817c..8f763b43695 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -1795,7 +1795,7 @@ cost_mergejoin(MergePath *path, PlannerInfo *root, SpecialJoinInfo *sjinfo)
ipathkey = (PathKey *) linitial(ipathkeys);
/* debugging check */
if (opathkey->pk_opfamily != ipathkey->pk_opfamily ||
- opathkey->pk_collation != ipathkey->pk_collation ||
+ opathkey->pk_eclass->ec_collation != ipathkey->pk_eclass->ec_collation ||
opathkey->pk_strategy != ipathkey->pk_strategy ||
opathkey->pk_nulls_first != ipathkey->pk_nulls_first)
elog(ERROR, "left and right pathkeys do not match in mergejoin");
@@ -2046,7 +2046,7 @@ cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey)
{
cache = (MergeScanSelCache *) lfirst(lc);
if (cache->opfamily == pathkey->pk_opfamily &&
- cache->collation == pathkey->pk_collation &&
+ cache->collation == pathkey->pk_eclass->ec_collation &&
cache->strategy == pathkey->pk_strategy &&
cache->nulls_first == pathkey->pk_nulls_first)
return cache;
@@ -2068,7 +2068,7 @@ cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey)
cache = (MergeScanSelCache *) palloc(sizeof(MergeScanSelCache));
cache->opfamily = pathkey->pk_opfamily;
- cache->collation = pathkey->pk_collation;
+ cache->collation = pathkey->pk_eclass->ec_collation;
cache->strategy = pathkey->pk_strategy;
cache->nulls_first = pathkey->pk_nulls_first;
cache->leftstartsel = leftstartsel;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 3d87a5b9037..9a32e16940b 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -17,6 +17,8 @@
#include "postgres.h"
#include "access/skey.h"
+#include "catalog/pg_type.h"
+#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
@@ -97,6 +99,7 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
{
Expr *clause = restrictinfo->clause;
Oid opno,
+ collation,
item1_type,
item2_type;
Expr *item1;
@@ -117,12 +120,24 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
/* Extract info from given clause */
Assert(is_opclause(clause));
opno = ((OpExpr *) clause)->opno;
+ collation = ((OpExpr *) clause)->inputcollid;
item1 = (Expr *) get_leftop(clause);
item2 = (Expr *) get_rightop(clause);
item1_relids = restrictinfo->left_relids;
item2_relids = restrictinfo->right_relids;
/*
+ * Ensure both input expressions expose the desired collation (their types
+ * should be OK already); see comments for canonicalize_ec_expression.
+ */
+ item1 = canonicalize_ec_expression(item1,
+ exprType((Node *) item1),
+ collation);
+ item2 = canonicalize_ec_expression(item2,
+ exprType((Node *) item2),
+ collation);
+
+ /*
* Reject clauses of the form X=X. These are not as redundant as they
* might seem at first glance: assuming the operator is strict, this is
* really an expensive way to write X IS NOT NULL. So we must not risk
@@ -189,6 +204,13 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
continue;
/*
+ * The collation has to match; check this first since it's cheaper
+ * than the opfamily comparison.
+ */
+ if (collation != cur_ec->ec_collation)
+ continue;
+
+ /*
* A "match" requires matching sets of btree opfamilies. Use of
* equal() for this test has implications discussed in the comments
* for get_mergejoin_opfamilies().
@@ -315,6 +337,7 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
EquivalenceClass *ec = makeNode(EquivalenceClass);
ec->ec_opfamilies = opfamilies;
+ ec->ec_collation = collation;
ec->ec_members = NIL;
ec->ec_sources = list_make1(restrictinfo);
ec->ec_derives = NIL;
@@ -342,6 +365,84 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
}
/*
+ * canonicalize_ec_expression
+ *
+ * This function ensures that the expression exposes the expected type and
+ * collation, so that it will be equal() to other equivalence-class expressions
+ * that it ought to be equal() to.
+ *
+ * The rule for datatypes is that the exposed type should match what it would
+ * be for an input to an operator of the EC's opfamilies; which is usually
+ * the declared input type of the operator, but in the case of polymorphic
+ * operators no relabeling is wanted (compare the behavior of parse_coerce.c).
+ * Expressions coming in from quals will generally have the right type
+ * already, but expressions coming from indexkeys may not (because they are
+ * represented without any explicit relabel in pg_index), and the same problem
+ * occurs for sort expressions (because the parser is likewise cavalier about
+ * putting relabels on them). Such cases will be binary-compatible with the
+ * real operators, so adding a RelabelType is sufficient.
+ *
+ * Also, the expression's exposed collation must match the EC's collation.
+ * This is important because in comparisons like "foo < bar COLLATE baz",
+ * only one of the expressions has the correct exposed collation as we receive
+ * it from the parser. Forcing both of them to have it ensures that all
+ * variant spellings of such a construct behave the same. Again, we can
+ * stick on a RelabelType to force the right exposed collation. (It might
+ * work to not label the collation at all in EC members, but this is risky
+ * since some parts of the system expect exprCollation() to deliver the
+ * right answer for a sort key.)
+ *
+ * Note this code assumes that the expression has already been through
+ * eval_const_expressions, so there are no CollateExprs and no redundant
+ * RelabelTypes.
+ */
+Expr *
+canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
+{
+ Oid expr_type = exprType((Node *) expr);
+
+ /*
+ * For a polymorphic-input-type opclass, just keep the same exposed type.
+ */
+ if (IsPolymorphicType(req_type))
+ req_type = expr_type;
+
+ /*
+ * No work if the expression exposes the right type/collation already.
+ */
+ if (expr_type != req_type ||
+ exprCollation((Node *) expr) != req_collation)
+ {
+ /*
+ * Strip any existing RelabelType, then add a new one if needed.
+ * This is to preserve the invariant of no redundant RelabelTypes.
+ *
+ * If we have to change the exposed type of the stripped expression,
+ * set typmod to -1 (since the new type may not have the same typmod
+ * interpretation). If we only have to change collation, preserve
+ * the exposed typmod.
+ */
+ while (expr && IsA(expr, RelabelType))
+ expr = (Expr *) ((RelabelType *) expr)->arg;
+
+ if (exprType((Node *) expr) != req_type)
+ expr = (Expr *) makeRelabelType(expr,
+ req_type,
+ -1,
+ req_collation,
+ COERCE_DONTCARE);
+ else if (exprCollation((Node *) expr) != req_collation)
+ expr = (Expr *) makeRelabelType(expr,
+ req_type,
+ exprTypmod((Node *) expr),
+ req_collation,
+ COERCE_DONTCARE);
+ }
+
+ return expr;
+}
+
+/*
* add_eq_member - build a new EquivalenceMember and add it to an EC
*/
static EquivalenceMember *
@@ -383,9 +484,9 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
/*
* get_eclass_for_sort_expr
- * Given an expression and opfamily info, find an existing equivalence
- * class it is a member of; if none, optionally build a new single-member
- * EquivalenceClass for it.
+ * Given an expression and opfamily/collation info, find an existing
+ * equivalence class it is a member of; if none, optionally build a new
+ * single-member EquivalenceClass for it.
*
* sortref is the SortGroupRef of the originating SortGroupClause, if any,
* or zero if not. (It should never be zero if the expression is volatile!)
@@ -406,8 +507,9 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
EquivalenceClass *
get_eclass_for_sort_expr(PlannerInfo *root,
Expr *expr,
- Oid expr_datatype,
List *opfamilies,
+ Oid opcintype,
+ Oid collation,
Index sortref,
bool create_it)
{
@@ -417,6 +519,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
MemoryContext oldcontext;
/*
+ * Ensure the expression exposes the correct type and collation.
+ */
+ expr = canonicalize_ec_expression(expr, opcintype, collation);
+
+ /*
* Scan through the existing EquivalenceClasses for a match
*/
foreach(lc1, root->eq_classes)
@@ -432,6 +539,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
(sortref == 0 || sortref != cur_ec->ec_sortref))
continue;
+ if (collation != cur_ec->ec_collation)
+ continue;
if (!equal(opfamilies, cur_ec->ec_opfamilies))
continue;
@@ -447,7 +556,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
cur_em->em_is_const)
continue;
- if (expr_datatype == cur_em->em_datatype &&
+ if (opcintype == cur_em->em_datatype &&
equal(expr, cur_em->em_expr))
return cur_ec; /* Match! */
}
@@ -460,13 +569,13 @@ get_eclass_for_sort_expr(PlannerInfo *root,
/*
* OK, build a new single-member EC
*
- * Here, we must be sure that we construct the EC in the right context. We
- * can assume, however, that the passed expr is long-lived.
+ * Here, we must be sure that we construct the EC in the right context.
*/
oldcontext = MemoryContextSwitchTo(root->planner_cxt);
newec = makeNode(EquivalenceClass);
newec->ec_opfamilies = list_copy(opfamilies);
+ newec->ec_collation = collation;
newec->ec_members = NIL;
newec->ec_sources = NIL;
newec->ec_derives = NIL;
@@ -481,8 +590,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
if (newec->ec_has_volatile && sortref == 0) /* should not happen */
elog(ERROR, "volatile EquivalenceClass has no sortref");
- newem = add_eq_member(newec, expr, pull_varnos((Node *) expr),
- false, expr_datatype);
+ newem = add_eq_member(newec, copyObject(expr), pull_varnos((Node *) expr),
+ false, opcintype);
/*
* add_eq_member doesn't check for volatile functions, set-returning
@@ -660,7 +769,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
ec->ec_broken = true;
break;
}
- process_implied_equality(root, eq_op,
+ process_implied_equality(root, eq_op, ec->ec_collation,
cur_em->em_expr, const_em->em_expr,
ec->ec_relids,
ec->ec_below_outer_join,
@@ -715,7 +824,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
ec->ec_broken = true;
break;
}
- process_implied_equality(root, eq_op,
+ process_implied_equality(root, eq_op, ec->ec_collation,
prev_em->em_expr, cur_em->em_expr,
ec->ec_relids,
ec->ec_below_outer_join,
@@ -1117,6 +1226,7 @@ create_join_clause(PlannerInfo *root,
oldcontext = MemoryContextSwitchTo(root->planner_cxt);
rinfo = build_implied_join_equality(opno,
+ ec->ec_collation,
leftem->em_expr,
rightem->em_expr,
bms_union(leftem->em_relids,
@@ -1338,6 +1448,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
Expr *outervar,
*innervar;
Oid opno,
+ collation,
left_type,
right_type,
inner_datatype;
@@ -1346,6 +1457,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
Assert(is_opclause(rinfo->clause));
opno = ((OpExpr *) rinfo->clause)->opno;
+ collation = ((OpExpr *) rinfo->clause)->inputcollid;
/* If clause is outerjoin_delayed, operator must be strict */
if (rinfo->outerjoin_delayed && !op_strict(opno))
@@ -1381,7 +1493,9 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
/* Never match to a volatile EC */
if (cur_ec->ec_has_volatile)
continue;
- /* It has to match the outer-join clause as to opfamilies, too */
+ /* It has to match the outer-join clause as to semantics, too */
+ if (collation != cur_ec->ec_collation)
+ continue;
if (!equal(rinfo->mergeopfamilies, cur_ec->ec_opfamilies))
continue;
/* Does it contain a match to outervar? */
@@ -1419,6 +1533,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
if (!OidIsValid(eq_op))
continue; /* can't generate equality */
newrinfo = build_implied_join_equality(eq_op,
+ cur_ec->ec_collation,
innervar,
cur_em->em_expr,
inner_relids);
@@ -1451,6 +1566,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
Expr *leftvar;
Expr *rightvar;
Oid opno,
+ collation,
left_type,
right_type;
Relids left_relids,
@@ -1464,6 +1580,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
/* Extract needed info from the clause */
Assert(is_opclause(rinfo->clause));
opno = ((OpExpr *) rinfo->clause)->opno;
+ collation = ((OpExpr *) rinfo->clause)->inputcollid;
op_input_types(opno, &left_type, &right_type);
leftvar = (Expr *) get_leftop(rinfo->clause);
rightvar = (Expr *) get_rightop(rinfo->clause);
@@ -1485,7 +1602,9 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
/* Never match to a volatile EC */
if (cur_ec->ec_has_volatile)
continue;
- /* It has to match the outer-join clause as to opfamilies, too */
+ /* It has to match the outer-join clause as to semantics, too */
+ if (collation != cur_ec->ec_collation)
+ continue;
if (!equal(rinfo->mergeopfamilies, cur_ec->ec_opfamilies))
continue;
@@ -1548,6 +1667,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
if (OidIsValid(eq_op))
{
newrinfo = build_implied_join_equality(eq_op,
+ cur_ec->ec_collation,
leftvar,
cur_em->em_expr,
left_relids);
@@ -1560,6 +1680,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
if (OidIsValid(eq_op))
{
newrinfo = build_implied_join_equality(eq_op,
+ cur_ec->ec_collation,
rightvar,
cur_em->em_expr,
right_relids);
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 65bc9be8da8..1ac0ff6ee87 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -1201,13 +1201,14 @@ match_clause_to_indexcol(IndexOptInfo *index,
SaOpControl saop_control)
{
Expr *clause = rinfo->clause;
- Oid collation = index->indexcollations[indexcol];
Oid opfamily = index->opfamily[indexcol];
+ Oid collation = index->indexcollations[indexcol];
Node *leftop,
*rightop;
Relids left_relids;
Relids right_relids;
Oid expr_op;
+ Oid expr_coll;
bool plain_op;
/*
@@ -1241,6 +1242,7 @@ match_clause_to_indexcol(IndexOptInfo *index,
left_relids = rinfo->left_relids;
right_relids = rinfo->right_relids;
expr_op = ((OpExpr *) clause)->opno;
+ expr_coll = ((OpExpr *) clause)->inputcollid;
plain_op = true;
}
else if (saop_control != SAOP_FORBID &&
@@ -1256,6 +1258,7 @@ match_clause_to_indexcol(IndexOptInfo *index,
left_relids = NULL; /* not actually needed */
right_relids = pull_varnos(rightop);
expr_op = saop->opno;
+ expr_coll = saop->inputcollid;
plain_op = false;
}
else if (clause && IsA(clause, RowCompareExpr))
@@ -1284,8 +1287,8 @@ match_clause_to_indexcol(IndexOptInfo *index,
bms_is_subset(right_relids, outer_relids) &&
!contain_volatile_functions(rightop))
{
- if (is_indexable_operator(expr_op, opfamily, true) &&
- (!collation || collation == exprCollation((Node *) clause)))
+ if (collation == expr_coll &&
+ is_indexable_operator(expr_op, opfamily, true))
return true;
/*
@@ -1303,8 +1306,8 @@ match_clause_to_indexcol(IndexOptInfo *index,
bms_is_subset(left_relids, outer_relids) &&
!contain_volatile_functions(leftop))
{
- if (is_indexable_operator(expr_op, opfamily, false) &&
- (!collation || collation == exprCollation((Node *) clause)))
+ if (collation == expr_coll &&
+ is_indexable_operator(expr_op, opfamily, false))
return true;
/*
@@ -1397,7 +1400,7 @@ match_rowcompare_to_indexcol(IndexOptInfo *index,
else
return false;
- if (index->indexcollations[indexcol] != linitial_oid(clause->collids))
+ if (index->indexcollations[indexcol] != linitial_oid(clause->inputcollids))
return false;
/* We're good if the operator is the right type of opfamily member */
@@ -1808,6 +1811,7 @@ eclass_matches_any_index(EquivalenceClass *ec, EquivalenceMember *em,
for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
{
Oid curFamily = index->opfamily[indexcol];
+ Oid curCollation = index->indexcollations[indexcol];
/*
* If it's a btree index, we can reject it if its opfamily isn't
@@ -1818,9 +1822,12 @@ eclass_matches_any_index(EquivalenceClass *ec, EquivalenceMember *em,
* mean we return "true" for a useless index, but that will just
* cause some wasted planner cycles; it's better than ignoring
* useful indexes.
+ *
+ * We insist on collation match for all index types, though.
*/
if ((index->relam != BTREE_AM_OID ||
list_member_oid(ec->ec_opfamilies, curFamily)) &&
+ ec->ec_collation == curCollation &&
match_index_to_operand((Node *) em->em_expr, indexcol, index))
return true;
}
@@ -2671,7 +2678,8 @@ expand_boolean_index_clause(Node *clause,
/* convert to indexkey = TRUE */
return make_opclause(BooleanEqualOperator, BOOLOID, false,
(Expr *) clause,
- (Expr *) makeBoolConst(true, false));
+ (Expr *) makeBoolConst(true, false),
+ InvalidOid, InvalidOid);
}
/* NOT clause? */
if (not_clause(clause))
@@ -2683,7 +2691,8 @@ expand_boolean_index_clause(Node *clause,
/* convert to indexkey = FALSE */
return make_opclause(BooleanEqualOperator, BOOLOID, false,
(Expr *) arg,
- (Expr *) makeBoolConst(false, false));
+ (Expr *) makeBoolConst(false, false),
+ InvalidOid, InvalidOid);
}
if (clause && IsA(clause, BooleanTest))
{
@@ -2697,14 +2706,16 @@ expand_boolean_index_clause(Node *clause,
/* convert to indexkey = TRUE */
return make_opclause(BooleanEqualOperator, BOOLOID, false,
(Expr *) arg,
- (Expr *) makeBoolConst(true, false));
+ (Expr *) makeBoolConst(true, false),
+ InvalidOid, InvalidOid);
}
if (btest->booltesttype == IS_FALSE)
{
/* convert to indexkey = FALSE */
return make_opclause(BooleanEqualOperator, BOOLOID, false,
(Expr *) arg,
- (Expr *) makeBoolConst(false, false));
+ (Expr *) makeBoolConst(false, false),
+ InvalidOid, InvalidOid);
}
/* Oops */
Assert(false);
@@ -2876,7 +2887,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
largs_cell = lnext(list_head(clause->largs));
rargs_cell = lnext(list_head(clause->rargs));
opnos_cell = lnext(list_head(clause->opnos));
- collids_cell = lnext(list_head(clause->collids));
+ collids_cell = lnext(list_head(clause->inputcollids));
while (largs_cell != NULL)
{
@@ -3010,8 +3021,8 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
rc->opnos = new_ops;
rc->opfamilies = list_truncate(list_copy(clause->opfamilies),
matching_cols);
- rc->collids = list_truncate(list_copy(clause->collids),
- matching_cols);
+ rc->inputcollids = list_truncate(list_copy(clause->inputcollids),
+ matching_cols);
rc->largs = list_truncate((List *) copyObject(clause->largs),
matching_cols);
rc->rargs = list_truncate((List *) copyObject(clause->rargs),
@@ -3024,7 +3035,9 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
opexpr = make_opclause(linitial_oid(new_ops), BOOLOID, false,
copyObject(linitial(clause->largs)),
- copyObject(linitial(clause->rargs)));
+ copyObject(linitial(clause->rargs)),
+ InvalidOid,
+ linitial_oid(clause->inputcollids));
return make_simple_restrictinfo(opexpr);
}
}
@@ -3033,7 +3046,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
* Given a fixed prefix that all the "leftop" values must have,
* generate suitable indexqual condition(s). opfamily is the index
* operator family; we use it to deduce the appropriate comparison
- * operators and operand datatypes.
+ * operators and operand datatypes. collation is the input collation to use.
*/
static List *
prefix_quals(Node *leftop, Oid opfamily, Oid collation,
@@ -3110,7 +3123,8 @@ prefix_quals(Node *leftop, Oid opfamily, Oid collation,
if (oproid == InvalidOid)
elog(ERROR, "no = operator for opfamily %u", opfamily);
expr = make_opclause(oproid, BOOLOID, false,
- (Expr *) leftop, (Expr *) prefix_const);
+ (Expr *) leftop, (Expr *) prefix_const,
+ InvalidOid, collation);
result = list_make1(make_simple_restrictinfo(expr));
return result;
}
@@ -3125,7 +3139,8 @@ prefix_quals(Node *leftop, Oid opfamily, Oid collation,
if (oproid == InvalidOid)
elog(ERROR, "no >= operator for opfamily %u", opfamily);
expr = make_opclause(oproid, BOOLOID, false,
- (Expr *) leftop, (Expr *) prefix_const);
+ (Expr *) leftop, (Expr *) prefix_const,
+ InvalidOid, collation);
result = list_make1(make_simple_restrictinfo(expr));
/*-------
@@ -3138,12 +3153,13 @@ prefix_quals(Node *leftop, Oid opfamily, Oid collation,
if (oproid == InvalidOid)
elog(ERROR, "no < operator for opfamily %u", opfamily);
fmgr_info(get_opcode(oproid), &ltproc);
- fmgr_info_collation(collation, &ltproc);
+ fmgr_info_set_collation(collation, &ltproc);
greaterstr = make_greater_string(prefix_const, &ltproc);
if (greaterstr)
{
expr = make_opclause(oproid, BOOLOID, false,
- (Expr *) leftop, (Expr *) greaterstr);
+ (Expr *) leftop, (Expr *) greaterstr,
+ InvalidOid, collation);
result = lappend(result, make_simple_restrictinfo(expr));
}
@@ -3206,7 +3222,8 @@ network_prefix_quals(Node *leftop, Oid expr_op, Oid opfamily, Datum rightop)
expr = make_opclause(opr1oid, BOOLOID, false,
(Expr *) leftop,
(Expr *) makeConst(datatype, -1, -1, opr1right,
- false, false));
+ false, false),
+ InvalidOid, InvalidOid);
result = list_make1(make_simple_restrictinfo(expr));
/* create clause "key <= network_scan_last( rightop )" */
@@ -3221,7 +3238,8 @@ network_prefix_quals(Node *leftop, Oid expr_op, Oid opfamily, Datum rightop)
expr = make_opclause(opr2oid, BOOLOID, false,
(Expr *) leftop,
(Expr *) makeConst(datatype, -1, -1, opr2right,
- false, false));
+ false, false),
+ InvalidOid, InvalidOid);
result = lappend(result, make_simple_restrictinfo(expr));
return result;
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index fd759281ed5..de3e4ac74e7 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -18,8 +18,6 @@
#include "postgres.h"
#include "access/skey.h"
-#include "catalog/pg_collation.h"
-#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/plannodes.h"
@@ -31,10 +29,10 @@
#include "utils/lsyscache.h"
-static PathKey *makePathKey(EquivalenceClass *eclass, Oid opfamily, Oid collation,
+static PathKey *makePathKey(EquivalenceClass *eclass, Oid opfamily,
int strategy, bool nulls_first);
static PathKey *make_canonical_pathkey(PlannerInfo *root,
- EquivalenceClass *eclass, Oid opfamily, Oid collation,
+ EquivalenceClass *eclass, Oid opfamily,
int strategy, bool nulls_first);
static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys);
static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel,
@@ -54,14 +52,13 @@ static bool right_merge_direction(PlannerInfo *root, PathKey *pathkey);
* convenience routine to build the specified node.
*/
static PathKey *
-makePathKey(EquivalenceClass *eclass, Oid opfamily, Oid collation,
+makePathKey(EquivalenceClass *eclass, Oid opfamily,
int strategy, bool nulls_first)
{
PathKey *pk = makeNode(PathKey);
pk->pk_eclass = eclass;
pk->pk_opfamily = opfamily;
- pk->pk_collation = collation;
pk->pk_strategy = strategy;
pk->pk_nulls_first = nulls_first;
@@ -79,7 +76,7 @@ makePathKey(EquivalenceClass *eclass, Oid opfamily, Oid collation,
*/
static PathKey *
make_canonical_pathkey(PlannerInfo *root,
- EquivalenceClass *eclass, Oid opfamily, Oid collation,
+ EquivalenceClass *eclass, Oid opfamily,
int strategy, bool nulls_first)
{
PathKey *pk;
@@ -95,7 +92,6 @@ make_canonical_pathkey(PlannerInfo *root,
pk = (PathKey *) lfirst(lc);
if (eclass == pk->pk_eclass &&
opfamily == pk->pk_opfamily &&
- collation == pk->pk_collation &&
strategy == pk->pk_strategy &&
nulls_first == pk->pk_nulls_first)
return pk;
@@ -107,7 +103,7 @@ make_canonical_pathkey(PlannerInfo *root,
*/
oldcontext = MemoryContextSwitchTo(root->planner_cxt);
- pk = makePathKey(eclass, opfamily, collation, strategy, nulls_first);
+ pk = makePathKey(eclass, opfamily, strategy, nulls_first);
root->canon_pathkeys = lappend(root->canon_pathkeys, pk);
MemoryContextSwitchTo(oldcontext);
@@ -209,7 +205,6 @@ canonicalize_pathkeys(PlannerInfo *root, List *pathkeys)
cpathkey = make_canonical_pathkey(root,
eclass,
pathkey->pk_opfamily,
- pathkey->pk_collation,
pathkey->pk_strategy,
pathkey->pk_nulls_first);
@@ -241,6 +236,7 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
Expr *expr,
Oid opfamily,
Oid opcintype,
+ Oid collation,
bool reverse_sort,
bool nulls_first,
Index sortref,
@@ -251,7 +247,6 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
Oid equality_op;
List *opfamilies;
EquivalenceClass *eclass;
- Oid collation;
strategy = reverse_sort ? BTGreaterStrategyNumber : BTLessStrategyNumber;
@@ -273,47 +268,21 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
elog(ERROR, "could not find opfamilies for equality operator %u",
equality_op);
- /*
- * When dealing with binary-compatible opclasses, we have to ensure that
- * the exposed type of the expression tree matches the declared input type
- * of the opclass, except when that is a polymorphic type (compare the
- * behavior of parse_coerce.c). This ensures that we can correctly match
- * the indexkey or sortclause expression to other expressions we find in
- * the query, because arguments of ordinary operator expressions will be
- * cast that way. (We have to do this for indexkeys because they are
- * represented without any explicit relabel in pg_index, and for sort
- * clauses because the parser is likewise cavalier about putting relabels
- * on them.)
- */
- if (exprType((Node *) expr) != opcintype &&
- !IsPolymorphicType(opcintype))
- {
- /* Strip any existing RelabelType, and add a new one if needed */
- while (expr && IsA(expr, RelabelType))
- expr = (Expr *) ((RelabelType *) expr)->arg;
- if (exprType((Node *) expr) != opcintype)
- expr = (Expr *) makeRelabelType(expr,
- opcintype,
- -1,
- COERCE_DONTCARE);
- }
-
/* Now find or (optionally) create a matching EquivalenceClass */
- eclass = get_eclass_for_sort_expr(root, expr, opcintype, opfamilies,
+ eclass = get_eclass_for_sort_expr(root, expr, opfamilies,
+ opcintype, collation,
sortref, create_it);
/* Fail if no EC and !create_it */
if (!eclass)
return NULL;
- collation = exprCollation((Node *) expr);
-
/* And finally we can find or create a PathKey node */
if (canonicalize)
- return make_canonical_pathkey(root, eclass, opfamily, collation,
+ return make_canonical_pathkey(root, eclass, opfamily,
strategy, nulls_first);
else
- return makePathKey(eclass, opfamily, collation, strategy, nulls_first);
+ return makePathKey(eclass, opfamily, strategy, nulls_first);
}
/*
@@ -333,7 +302,8 @@ make_pathkey_from_sortop(PlannerInfo *root,
bool canonicalize)
{
Oid opfamily,
- opcintype;
+ opcintype,
+ collation;
int16 strategy;
/* Find the operator in pg_amop --- failure shouldn't happen */
@@ -341,10 +311,15 @@ make_pathkey_from_sortop(PlannerInfo *root,
&opfamily, &opcintype, &strategy))
elog(ERROR, "operator %u is not a valid ordering operator",
ordering_op);
+
+ /* Because SortGroupClause doesn't carry collation, consult the expr */
+ collation = exprCollation((Node *) expr);
+
return make_pathkey_from_sortinfo(root,
expr,
opfamily,
opcintype,
+ collation,
(strategy == BTGreaterStrategyNumber),
nulls_first,
sortref,
@@ -575,6 +550,7 @@ build_index_pathkeys(PlannerInfo *root,
indexkey,
index->sortopfamily[i],
index->opcintype[i],
+ index->indexcollations[i],
reverse_sort,
nulls_first,
0,
@@ -698,8 +674,9 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
outer_ec =
get_eclass_for_sort_expr(root,
outer_expr,
- sub_member->em_datatype,
sub_eclass->ec_opfamilies,
+ sub_member->em_datatype,
+ sub_eclass->ec_collation,
0,
false);
@@ -712,7 +689,6 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
make_canonical_pathkey(root,
outer_ec,
sub_pathkey->pk_opfamily,
- sub_pathkey->pk_collation,
sub_pathkey->pk_strategy,
sub_pathkey->pk_nulls_first);
}
@@ -742,23 +718,14 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
{
EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
Expr *sub_expr = sub_member->em_expr;
- Expr *sub_stripped;
+ Oid sub_expr_type = sub_member->em_datatype;
+ Oid sub_expr_coll = sub_eclass->ec_collation;
ListCell *k;
- /*
- * We handle two cases: the sub_pathkey key can be either an
- * exact match for a targetlist entry, or it could match after
- * stripping RelabelType nodes. (We need that case since
- * make_pathkey_from_sortinfo could add or remove
- * RelabelType.)
- */
- sub_stripped = sub_expr;
- while (sub_stripped && IsA(sub_stripped, RelabelType))
- sub_stripped = ((RelabelType *) sub_stripped)->arg;
-
foreach(k, sub_tlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(k);
+ Expr *tle_expr;
Expr *outer_expr;
EquivalenceClass *outer_ec;
PathKey *outer_pk;
@@ -768,40 +735,31 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
if (tle->resjunk)
continue;
- if (equal(tle->expr, sub_expr))
- {
- /* Exact match */
- outer_expr = (Expr *) makeVarFromTargetEntry(rel->relid, tle);
- }
- else
- {
- Expr *tle_stripped;
-
- tle_stripped = tle->expr;
- while (tle_stripped && IsA(tle_stripped, RelabelType))
- tle_stripped = ((RelabelType *) tle_stripped)->arg;
-
- if (equal(tle_stripped, sub_stripped))
- {
- /* Match after discarding RelabelType */
- outer_expr = (Expr *) makeVarFromTargetEntry(rel->relid, tle);
- if (exprType((Node *) outer_expr) !=
- exprType((Node *) sub_expr))
- outer_expr = (Expr *)
- makeRelabelType(outer_expr,
- exprType((Node *) sub_expr),
- -1,
- COERCE_DONTCARE);
- }
- else
- continue;
- }
+ /*
+ * The targetlist entry is considered to match if it
+ * matches after sort-key canonicalization. That is
+ * needed since the sub_expr has been through the same
+ * process.
+ */
+ tle_expr = canonicalize_ec_expression(tle->expr,
+ sub_expr_type,
+ sub_expr_coll);
+ if (!equal(tle_expr, sub_expr))
+ continue;
- /* Found a representation for this sub_pathkey */
+ /*
+ * Build a representation of this targetlist entry as
+ * an outer Var.
+ */
+ outer_expr = (Expr *) makeVarFromTargetEntry(rel->relid,
+ tle);
+
+ /* See if we have a matching EC for that */
outer_ec = get_eclass_for_sort_expr(root,
outer_expr,
- sub_member->em_datatype,
sub_eclass->ec_opfamilies,
+ sub_expr_type,
+ sub_expr_coll,
0,
false);
@@ -815,7 +773,6 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
outer_pk = make_canonical_pathkey(root,
outer_ec,
sub_pathkey->pk_opfamily,
- sub_pathkey->pk_collation,
sub_pathkey->pk_strategy,
sub_pathkey->pk_nulls_first);
/* score = # of equivalence peers */
@@ -1024,15 +981,17 @@ initialize_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo)
restrictinfo->left_ec =
get_eclass_for_sort_expr(root,
(Expr *) get_leftop(clause),
- lefttype,
restrictinfo->mergeopfamilies,
+ lefttype,
+ ((OpExpr *) clause)->inputcollid,
0,
true);
restrictinfo->right_ec =
get_eclass_for_sort_expr(root,
(Expr *) get_rightop(clause),
- righttype,
restrictinfo->mergeopfamilies,
+ righttype,
+ ((OpExpr *) clause)->inputcollid,
0,
true);
}
@@ -1337,7 +1296,6 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
pathkey = make_canonical_pathkey(root,
ec,
linitial_oid(ec->ec_opfamilies),
- DEFAULT_COLLATION_OID,
BTLessStrategyNumber,
false);
/* can't be redundant because no duplicate ECs */
@@ -1431,7 +1389,6 @@ make_inner_pathkeys_for_merge(PlannerInfo *root,
pathkey = make_canonical_pathkey(root,
ieclass,
opathkey->pk_opfamily,
- opathkey->pk_collation,
opathkey->pk_strategy,
opathkey->pk_nulls_first);
@@ -1552,7 +1509,6 @@ right_merge_direction(PlannerInfo *root, PathKey *pathkey)
PathKey *query_pathkey = (PathKey *) lfirst(l);
if (pathkey->pk_eclass == query_pathkey->pk_eclass &&
- pathkey->pk_collation == query_pathkey->pk_collation &&
pathkey->pk_opfamily == query_pathkey->pk_opfamily)
{
/*
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 8a0135c9a74..bdd14f524db 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -2148,14 +2148,14 @@ create_mergejoin_plan(PlannerInfo *root,
/* pathkeys should match each other too (more debugging) */
if (opathkey->pk_opfamily != ipathkey->pk_opfamily ||
- opathkey->pk_collation != ipathkey->pk_collation ||
+ opathkey->pk_eclass->ec_collation != ipathkey->pk_eclass->ec_collation ||
opathkey->pk_strategy != ipathkey->pk_strategy ||
opathkey->pk_nulls_first != ipathkey->pk_nulls_first)
elog(ERROR, "left and right pathkeys do not match in mergejoin");
/* OK, save info for executor */
mergefamilies[i] = opathkey->pk_opfamily;
- mergecollations[i] = opathkey->pk_collation;
+ mergecollations[i] = opathkey->pk_eclass->ec_collation;
mergestrategies[i] = opathkey->pk_strategy;
mergenullsfirst[i] = opathkey->pk_nulls_first;
i++;
@@ -3603,7 +3603,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
*/
numsortkeys = add_sort_column(tle->resno,
sortop,
- pathkey->pk_collation,
+ pathkey->pk_eclass->ec_collation,
pathkey->pk_nulls_first,
numsortkeys,
sortColIdx, sortOperators, collations, nullsFirst);
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 845b4ae34b8..0e00df64335 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -1371,6 +1371,7 @@ distribute_restrictinfo_to_rels(PlannerInfo *root,
void
process_implied_equality(PlannerInfo *root,
Oid opno,
+ Oid collation,
Expr *item1,
Expr *item2,
Relids qualscope,
@@ -1387,7 +1388,9 @@ process_implied_equality(PlannerInfo *root,
BOOLOID, /* opresulttype */
false, /* opretset */
(Expr *) copyObject(item1),
- (Expr *) copyObject(item2));
+ (Expr *) copyObject(item2),
+ InvalidOid,
+ collation);
/* If both constant, try to reduce to a boolean constant. */
if (both_const)
@@ -1427,6 +1430,7 @@ process_implied_equality(PlannerInfo *root,
*/
RestrictInfo *
build_implied_join_equality(Oid opno,
+ Oid collation,
Expr *item1,
Expr *item2,
Relids qualscope)
@@ -1442,7 +1446,9 @@ build_implied_join_equality(Oid opno,
BOOLOID, /* opresulttype */
false, /* opretset */
(Expr *) copyObject(item1),
- (Expr *) copyObject(item2));
+ (Expr *) copyObject(item2),
+ InvalidOid,
+ collation);
/* Make a copy of qualscope to avoid problems if source EC changes */
qualscope = bms_copy(qualscope);
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index a9649212f20..f2b586d19cc 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -157,7 +157,7 @@ replace_outer_var(PlannerInfo *root, Var *var)
retval->paramid = i;
retval->paramtype = var->vartype;
retval->paramtypmod = var->vartypmod;
- retval->paramcollation = var->varcollid;
+ retval->paramcollid = var->varcollid;
retval->location = -1;
return retval;
@@ -186,7 +186,7 @@ assign_nestloop_param(PlannerInfo *root, Var *var)
retval->paramid = i;
retval->paramtype = var->vartype;
retval->paramtypmod = var->vartypmod;
- retval->paramcollation = var->varcollid;
+ retval->paramcollid = var->varcollid;
retval->location = -1;
return retval;
@@ -227,7 +227,7 @@ replace_outer_agg(PlannerInfo *root, Aggref *agg)
retval->paramid = i;
retval->paramtype = agg->aggtype;
retval->paramtypmod = -1;
- retval->paramcollation = agg->collid;
+ retval->paramcollid = agg->aggcollid;
retval->location = -1;
return retval;
@@ -239,7 +239,8 @@ replace_outer_agg(PlannerInfo *root, Aggref *agg)
* This is used to allocate PARAM_EXEC slots for subplan outputs.
*/
static Param *
-generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod, Oid paramcollation)
+generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod,
+ Oid paramcollation)
{
Param *retval;
PlannerParamItem *pitem;
@@ -249,7 +250,7 @@ generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod, Oid para
retval->paramid = list_length(root->glob->paramlist);
retval->paramtype = paramtype;
retval->paramtypmod = paramtypmod;
- retval->paramcollation = paramcollation;
+ retval->paramcollid = paramcollation;
retval->location = -1;
pitem = makeNode(PlannerParamItem);
@@ -282,10 +283,11 @@ SS_assign_special_param(PlannerInfo *root)
/*
* Get the datatype of the first column of the plan's output.
*
- * This is stored for ARRAY_SUBLINK execution and for exprType()/exprTypmod()/exprCollation(),
- * which have no way to get at the plan associated with a SubPlan node.
- * We really only need the info for EXPR_SUBLINK and ARRAY_SUBLINK subplans,
- * but for consistency we save it always.
+ * This information is stored for ARRAY_SUBLINK execution and for
+ * exprType()/exprTypmod()/exprCollation(), which have no way to get at the
+ * plan associated with a SubPlan node. We really only need the info for
+ * EXPR_SUBLINK and ARRAY_SUBLINK subplans, but for consistency we save it
+ * always.
*/
static void
get_first_col_type(Plan *plan, Oid *coltype, int32 *coltypmod, Oid *colcollation)
@@ -1395,13 +1397,15 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
List *leftargs,
*rightargs,
*opids,
+ *opcollations,
*newWhere,
*tlist,
*testlist,
*paramids;
ListCell *lc,
*rc,
- *oc;
+ *oc,
+ *cc;
AttrNumber resno;
/*
@@ -1465,7 +1469,7 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
* we aren't trying hard yet to ensure that we have only outer or only
* inner on each side; we'll check that if we get to the end.
*/
- leftargs = rightargs = opids = newWhere = NIL;
+ leftargs = rightargs = opids = opcollations = newWhere = NIL;
foreach(lc, (List *) whereClause)
{
OpExpr *expr = (OpExpr *) lfirst(lc);
@@ -1481,6 +1485,7 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
leftargs = lappend(leftargs, leftarg);
rightargs = lappend(rightargs, rightarg);
opids = lappend_oid(opids, expr->opno);
+ opcollations = lappend_oid(opcollations, expr->inputcollid);
continue;
}
if (contain_vars_of_level(rightarg, 1))
@@ -1497,6 +1502,7 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
leftargs = lappend(leftargs, rightarg);
rightargs = lappend(rightargs, leftarg);
opids = lappend_oid(opids, expr->opno);
+ opcollations = lappend_oid(opcollations, expr->inputcollid);
continue;
}
/* If no commutator, no chance to optimize the WHERE clause */
@@ -1565,16 +1571,17 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
*/
tlist = testlist = paramids = NIL;
resno = 1;
- /* there's no "for3" so we have to chase one of the lists manually */
- oc = list_head(opids);
- forboth(lc, leftargs, rc, rightargs)
+ /* there's no "forfour" so we have to chase one of the lists manually */
+ cc = list_head(opcollations);
+ forthree(lc, leftargs, rc, rightargs, oc, opids)
{
Node *leftarg = (Node *) lfirst(lc);
Node *rightarg = (Node *) lfirst(rc);
Oid opid = lfirst_oid(oc);
+ Oid opcollation = lfirst_oid(cc);
Param *param;
- oc = lnext(oc);
+ cc = lnext(cc);
param = generate_new_param(root,
exprType(rightarg),
exprTypmod(rightarg),
@@ -1586,7 +1593,8 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
false));
testlist = lappend(testlist,
make_opclause(opid, BOOLOID, false,
- (Expr *) leftarg, (Expr *) param));
+ (Expr *) leftarg, (Expr *) param,
+ InvalidOid, opcollation));
paramids = lappend_int(paramids, param->paramid);
}
@@ -2360,7 +2368,7 @@ finalize_primnode(Node *node, finalize_primnode_context *context)
/*
* SS_make_initplan_from_plan - given a plan tree, make it an InitPlan
*
- * The plan is expected to return a scalar value of the indicated type.
+ * The plan is expected to return a scalar value of the given type/collation.
* We build an EXPR_SUBLINK SubPlan node and put it into the initplan
* list for the current query level. A Param that represents the initplan's
* output is returned.
@@ -2369,7 +2377,8 @@ finalize_primnode(Node *node, finalize_primnode_context *context)
*/
Param *
SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
- Oid resulttype, int32 resulttypmod, Oid resultcollation)
+ Oid resulttype, int32 resulttypmod,
+ Oid resultcollation)
{
SubPlan *node;
Param *prm;
@@ -2405,7 +2414,8 @@ SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
*/
node = makeNode(SubPlan);
node->subLinkType = EXPR_SUBLINK;
- get_first_col_type(plan, &node->firstColType, &node->firstColTypmod, &node->firstColCollation);
+ get_first_col_type(plan, &node->firstColType, &node->firstColTypmod,
+ &node->firstColCollation);
node->plan_id = list_length(root->glob->subplans);
root->init_plans = lappend(root->init_plans, node);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 8503792df44..7b31b6b4fa6 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -100,7 +100,8 @@ static List *simplify_and_arguments(List *args,
bool *haveNull, bool *forceFalse);
static Node *simplify_boolean_equality(Oid opno, List *args);
static Expr *simplify_function(Oid funcid,
- Oid result_type, int32 result_typmod, Oid collid, List **args,
+ Oid result_type, int32 result_typmod,
+ Oid input_collid, List **args,
bool has_named_args,
bool allow_inline,
eval_const_expressions_context *context);
@@ -114,8 +115,8 @@ static List *fetch_function_defaults(HeapTuple func_tuple);
static void recheck_cast_function_args(List *args, Oid result_type,
HeapTuple func_tuple);
static Expr *evaluate_function(Oid funcid,
- Oid result_type, int32 result_typmod, Oid collid, List *args,
- HeapTuple func_tuple,
+ Oid result_type, int32 result_typmod,
+ Oid input_collid, List *args, HeapTuple func_tuple,
eval_const_expressions_context *context);
static Expr *inline_function(Oid funcid, Oid result_type, List *args,
HeapTuple func_tuple,
@@ -139,12 +140,14 @@ static bool tlist_matches_coltypelist(List *tlist, List *coltypelist);
/*
* make_opclause
- * Creates an operator clause given its operator info, left operand,
- * and right operand (pass NULL to create single-operand clause).
+ * Creates an operator clause given its operator info, left operand
+ * and right operand (pass NULL to create single-operand clause),
+ * and collation info.
*/
Expr *
make_opclause(Oid opno, Oid opresulttype, bool opretset,
- Expr *leftop, Expr *rightop)
+ Expr *leftop, Expr *rightop,
+ Oid opcollid, Oid inputcollid)
{
OpExpr *expr = makeNode(OpExpr);
@@ -152,11 +155,12 @@ make_opclause(Oid opno, Oid opresulttype, bool opretset,
expr->opfuncid = InvalidOid;
expr->opresulttype = opresulttype;
expr->opretset = opretset;
+ expr->opcollid = opcollid;
+ expr->inputcollid = inputcollid;
if (rightop)
expr->args = list_make2(leftop, rightop);
else
expr->args = list_make1(leftop);
- expr->collid = select_common_collation(NULL, expr->args, false);
expr->location = -1;
return (Expr *) expr;
}
@@ -709,6 +713,8 @@ expression_returns_set_rows_walker(Node *node, double *count)
return false;
if (IsA(node, DistinctExpr))
return false;
+ if (IsA(node, NullIfExpr))
+ return false;
if (IsA(node, ScalarArrayOpExpr))
return false;
if (IsA(node, BoolExpr))
@@ -731,8 +737,6 @@ expression_returns_set_rows_walker(Node *node, double *count)
return false;
if (IsA(node, XmlExpr))
return false;
- if (IsA(node, NullIfExpr))
- return false;
return expression_tree_walker(node, expression_returns_set_rows_walker,
(void *) count);
@@ -826,6 +830,15 @@ contain_mutable_functions_walker(Node *node, void *context)
return true;
/* else fall through to check args */
}
+ else if (IsA(node, NullIfExpr))
+ {
+ NullIfExpr *expr = (NullIfExpr *) node;
+
+ set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */
+ if (func_volatile(expr->opfuncid) != PROVOLATILE_IMMUTABLE)
+ return true;
+ /* else fall through to check args */
+ }
else if (IsA(node, ScalarArrayOpExpr))
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -863,15 +876,6 @@ contain_mutable_functions_walker(Node *node, void *context)
return true;
/* else fall through to check args */
}
- else if (IsA(node, NullIfExpr))
- {
- NullIfExpr *expr = (NullIfExpr *) node;
-
- set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */
- if (func_volatile(expr->opfuncid) != PROVOLATILE_IMMUTABLE)
- return true;
- /* else fall through to check args */
- }
else if (IsA(node, RowCompareExpr))
{
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
@@ -941,6 +945,15 @@ contain_volatile_functions_walker(Node *node, void *context)
return true;
/* else fall through to check args */
}
+ else if (IsA(node, NullIfExpr))
+ {
+ NullIfExpr *expr = (NullIfExpr *) node;
+
+ set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */
+ if (func_volatile(expr->opfuncid) == PROVOLATILE_VOLATILE)
+ return true;
+ /* else fall through to check args */
+ }
else if (IsA(node, ScalarArrayOpExpr))
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -978,15 +991,6 @@ contain_volatile_functions_walker(Node *node, void *context)
return true;
/* else fall through to check args */
}
- else if (IsA(node, NullIfExpr))
- {
- NullIfExpr *expr = (NullIfExpr *) node;
-
- set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */
- if (func_volatile(expr->opfuncid) == PROVOLATILE_VOLATILE)
- return true;
- /* else fall through to check args */
- }
else if (IsA(node, RowCompareExpr))
{
/* RowCompare probably can't have volatile ops, but check anyway */
@@ -1071,6 +1075,8 @@ contain_nonstrict_functions_walker(Node *node, void *context)
/* IS DISTINCT FROM is inherently non-strict */
return true;
}
+ if (IsA(node, NullIfExpr))
+ return true;
if (IsA(node, ScalarArrayOpExpr))
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -1119,8 +1125,6 @@ contain_nonstrict_functions_walker(Node *node, void *context)
return true;
if (IsA(node, XmlExpr))
return true;
- if (IsA(node, NullIfExpr))
- return true;
if (IsA(node, NullTest))
return true;
if (IsA(node, BooleanTest))
@@ -1874,6 +1878,7 @@ CommuteRowCompareExpr(RowCompareExpr *clause)
/*
* Note: we need not change the opfamilies list; we assume any btree
* opfamily containing an operator will also contain its commutator.
+ * Collations don't change either.
*/
temp = clause->largs;
@@ -1986,7 +1991,8 @@ set_coercionform_dontcare_walker(Node *node, void *context)
*/
static bool
rowtype_field_matches(Oid rowtypeid, int fieldnum,
- Oid expectedtype, int32 expectedtypmod, Oid expectedcollation)
+ Oid expectedtype, int32 expectedtypmod,
+ Oid expectedcollation)
{
TupleDesc tupdesc;
Form_pg_attribute attr;
@@ -2144,12 +2150,12 @@ eval_const_expressions_mutator(Node *node,
else
pval = datumCopy(prm->value, typByVal, typLen);
cnst = makeConst(param->paramtype,
- param->paramtypmod,
- (int) typLen,
- pval,
- prm->isnull,
- typByVal);
- cnst->constcollid = param->paramcollation;
+ param->paramtypmod,
+ (int) typLen,
+ pval,
+ prm->isnull,
+ typByVal);
+ cnst->constcollid = param->paramcollid;
return (Node *) cnst;
}
}
@@ -2190,7 +2196,7 @@ eval_const_expressions_mutator(Node *node,
*/
simple = simplify_function(expr->funcid,
expr->funcresulttype, exprTypmod(node),
- expr->collid,
+ expr->inputcollid,
&args,
has_named_args, true, context);
if (simple) /* successfully simplified it */
@@ -2207,8 +2213,9 @@ eval_const_expressions_mutator(Node *node,
newexpr->funcresulttype = expr->funcresulttype;
newexpr->funcretset = expr->funcretset;
newexpr->funcformat = expr->funcformat;
+ newexpr->funccollid = expr->funccollid;
+ newexpr->inputcollid = expr->inputcollid;
newexpr->args = args;
- newexpr->collid = expr->collid;
newexpr->location = expr->location;
return (Node *) newexpr;
}
@@ -2240,7 +2247,7 @@ eval_const_expressions_mutator(Node *node,
*/
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
- expr->collid,
+ expr->inputcollid,
&args,
false, true, context);
if (simple) /* successfully simplified it */
@@ -2269,8 +2276,9 @@ eval_const_expressions_mutator(Node *node,
newexpr->opfuncid = expr->opfuncid;
newexpr->opresulttype = expr->opresulttype;
newexpr->opretset = expr->opretset;
+ newexpr->opcollid = expr->opcollid;
+ newexpr->inputcollid = expr->inputcollid;
newexpr->args = args;
- newexpr->collid = expr->collid;
newexpr->location = expr->location;
return (Node *) newexpr;
}
@@ -2335,7 +2343,7 @@ eval_const_expressions_mutator(Node *node,
*/
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
- expr->collid,
+ expr->inputcollid,
&args,
false, false, context);
if (simple) /* successfully simplified it */
@@ -2363,8 +2371,9 @@ eval_const_expressions_mutator(Node *node,
newexpr->opfuncid = expr->opfuncid;
newexpr->opresulttype = expr->opresulttype;
newexpr->opretset = expr->opretset;
+ newexpr->opcollid = expr->opcollid;
+ newexpr->inputcollid = expr->inputcollid;
newexpr->args = args;
- newexpr->collid = expr->collid;
newexpr->location = expr->location;
return (Node *) newexpr;
}
@@ -2473,6 +2482,7 @@ eval_const_expressions_mutator(Node *node,
con->consttype = relabel->resulttype;
con->consttypmod = relabel->resulttypmod;
+ con->constcollid = relabel->resultcollid;
return (Node *) con;
}
else
@@ -2482,6 +2492,7 @@ eval_const_expressions_mutator(Node *node,
newrelabel->arg = (Expr *) arg;
newrelabel->resulttype = relabel->resulttype;
newrelabel->resulttypmod = relabel->resulttypmod;
+ newrelabel->resultcollid = relabel->resultcollid;
newrelabel->relabelformat = relabel->relabelformat;
newrelabel->location = relabel->location;
return (Node *) newrelabel;
@@ -2511,12 +2522,16 @@ eval_const_expressions_mutator(Node *node,
* then the result type's input function. So, try to simplify it as
* though it were a stack of two such function calls. First we need
* to know what the functions are.
+ *
+ * Note that the coercion functions are assumed not to care about
+ * input collation, so we just pass InvalidOid for that.
*/
getTypeOutputInfo(exprType((Node *) arg), &outfunc, &outtypisvarlena);
getTypeInputInfo(expr->resulttype, &infunc, &intypioparam);
simple = simplify_function(outfunc,
- CSTRINGOID, -1, InvalidOid,
+ CSTRINGOID, -1,
+ InvalidOid,
&args,
false, true, context);
if (simple) /* successfully simplified output fn */
@@ -2533,11 +2548,9 @@ eval_const_expressions_mutator(Node *node,
Int32GetDatum(-1),
false, true));
- /* preserve collation of input expression */
simple = simplify_function(infunc,
- expr->resulttype,
- -1,
- exprCollation((Node *) arg),
+ expr->resulttype, -1,
+ InvalidOid,
&args,
false, true, context);
if (simple) /* successfully simplified input fn */
@@ -2552,6 +2565,7 @@ eval_const_expressions_mutator(Node *node,
newexpr = makeNode(CoerceViaIO);
newexpr->arg = arg;
newexpr->resulttype = expr->resulttype;
+ newexpr->resultcollid = expr->resultcollid;
newexpr->coerceformat = expr->coerceformat;
newexpr->location = expr->location;
return (Node *) newexpr;
@@ -2574,6 +2588,7 @@ eval_const_expressions_mutator(Node *node,
newexpr->elemfuncid = expr->elemfuncid;
newexpr->resulttype = expr->resulttype;
newexpr->resulttypmod = expr->resulttypmod;
+ newexpr->resultcollid = expr->resultcollid;
newexpr->isExplicit = expr->isExplicit;
newexpr->coerceformat = expr->coerceformat;
newexpr->location = expr->location;
@@ -2596,8 +2611,10 @@ eval_const_expressions_mutator(Node *node,
{
/*
* 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 node at all: just change the constcollid field of the
+ * Const node. Otherwise, replace the CollateExpr with a RelabelType.
+ * (We do that so as to improve uniformity of expression representation
+ * and thus simplify comparison of expressions.)
*/
CollateExpr *collate = (CollateExpr *) node;
Node *arg;
@@ -2605,12 +2622,6 @@ eval_const_expressions_mutator(Node *node,
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;
@@ -2618,14 +2629,27 @@ eval_const_expressions_mutator(Node *node,
con->constcollid = collate->collOid;
return (Node *) con;
}
+ else if (collate->collOid == exprCollation(arg))
+ {
+ /* Don't need a RelabelType either... */
+ return arg;
+ }
else
{
- CollateExpr *newcollate = makeNode(CollateExpr);
+ RelabelType *relabel = makeNode(RelabelType);
+
+ relabel->resulttype = exprType(arg);
+ relabel->resulttypmod = exprTypmod(arg);
+ relabel->resultcollid = collate->collOid;
+ relabel->relabelformat = COERCE_DONTCARE;
+ relabel->location = collate->location;
+
+ /* Don't create stacked RelabelTypes */
+ while (arg && IsA(arg, RelabelType))
+ arg = (Node *) ((RelabelType *) arg)->arg;
+ relabel->arg = (Expr *) arg;
- newcollate->arg = (Expr *) arg;
- newcollate->collOid = collate->collOid;
- newcollate->location = collate->location;
- return (Node *) newcollate;
+ return (Node *) relabel;
}
}
if (IsA(node, CaseExpr))
@@ -2752,7 +2776,7 @@ eval_const_expressions_mutator(Node *node,
/* Otherwise we need a new CASE node */
newcase = makeNode(CaseExpr);
newcase->casetype = caseexpr->casetype;
- newcase->casecollation = caseexpr->casecollation;
+ newcase->casecollid = caseexpr->casecollid;
newcase->arg = (Expr *) newarg;
newcase->args = newargs;
newcase->defresult = (Expr *) defresult;
@@ -2793,6 +2817,7 @@ eval_const_expressions_mutator(Node *node,
newarray = makeNode(ArrayExpr);
newarray->array_typeid = arrayexpr->array_typeid;
+ newarray->array_collid = arrayexpr->array_collid;
newarray->element_typeid = arrayexpr->element_typeid;
newarray->elements = newelems;
newarray->multidims = arrayexpr->multidims;
@@ -2845,7 +2870,7 @@ eval_const_expressions_mutator(Node *node,
newcoalesce = makeNode(CoalesceExpr);
newcoalesce->coalescetype = coalesceexpr->coalescetype;
- newcoalesce->coalescecollation = coalesceexpr->coalescecollation;
+ newcoalesce->coalescecollid = coalesceexpr->coalescecollid;
newcoalesce->args = newargs;
newcoalesce->location = coalesceexpr->location;
return (Node *) newcoalesce;
@@ -2876,12 +2901,12 @@ eval_const_expressions_mutator(Node *node,
fselect->fieldnum,
fselect->resulttype,
fselect->resulttypmod,
- fselect->resultcollation))
+ fselect->resultcollid))
return (Node *) makeVar(((Var *) arg)->varno,
fselect->fieldnum,
fselect->resulttype,
fselect->resulttypmod,
- fselect->resultcollation,
+ fselect->resultcollid,
((Var *) arg)->varlevelsup);
}
if (arg && IsA(arg, RowExpr))
@@ -2898,10 +2923,10 @@ eval_const_expressions_mutator(Node *node,
fselect->fieldnum,
fselect->resulttype,
fselect->resulttypmod,
- fselect->resultcollation) &&
+ fselect->resultcollid) &&
fselect->resulttype == exprType(fld) &&
fselect->resulttypmod == exprTypmod(fld) &&
- fselect->resultcollation == exprCollation(fld))
+ fselect->resultcollid == exprCollation(fld))
return fld;
}
}
@@ -2910,7 +2935,7 @@ eval_const_expressions_mutator(Node *node,
newfselect->fieldnum = fselect->fieldnum;
newfselect->resulttype = fselect->resulttype;
newfselect->resulttypmod = fselect->resulttypmod;
- newfselect->resultcollation = fselect->resultcollation;
+ newfselect->resultcollid = fselect->resultcollid;
return (Node *) newfselect;
}
if (IsA(node, NullTest))
@@ -3355,7 +3380,8 @@ simplify_boolean_equality(Oid opno, List *args)
* (which might originally have been an operator; we don't care)
*
* Inputs are the function OID, actual result type OID (which is needed for
- * polymorphic functions) and typmod, and the pre-simplified argument list;
+ * polymorphic functions) and typmod, input collation to use for the function,
+ * the pre-simplified argument list, and some flags;
* also the context data for eval_const_expressions.
*
* Returns a simplified expression if successful, or NULL if cannot
@@ -3368,7 +3394,8 @@ simplify_boolean_equality(Oid opno, List *args)
* pass-by-reference, and it may get modified even if simplification fails.
*/
static Expr *
-simplify_function(Oid funcid, Oid result_type, int32 result_typmod, Oid collid,
+simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
+ Oid input_collid,
List **args,
bool has_named_args,
bool allow_inline,
@@ -3399,7 +3426,8 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod, Oid collid,
else if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args))
*args = add_function_defaults(*args, result_type, func_tuple, context);
- newexpr = evaluate_function(funcid, result_type, result_typmod, collid, *args,
+ newexpr = evaluate_function(funcid, result_type, result_typmod,
+ input_collid, *args,
func_tuple, context);
if (!newexpr && allow_inline)
@@ -3650,9 +3678,8 @@ recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple)
* simplify the function.
*/
static Expr *
-evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, Oid collid,
- List *args,
- HeapTuple func_tuple,
+evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
+ Oid input_collid, List *args, HeapTuple func_tuple,
eval_const_expressions_context *context)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
@@ -3733,8 +3760,9 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, Oid collid,
newexpr->funcresulttype = result_type;
newexpr->funcretset = false;
newexpr->funcformat = COERCE_DONTCARE; /* doesn't matter */
+ newexpr->funccollid = InvalidOid; /* doesn't matter */
+ newexpr->inputcollid = input_collid;
newexpr->args = args;
- newexpr->collid = collid;
newexpr->location = -1;
return evaluate_expr((Expr *) newexpr, result_type, result_typmod);
diff --git a/src/backend/optimizer/util/predtest.c b/src/backend/optimizer/util/predtest.c
index 4561e8e92f1..6a1f7291ba5 100644
--- a/src/backend/optimizer/util/predtest.c
+++ b/src/backend/optimizer/util/predtest.c
@@ -1345,6 +1345,8 @@ btree_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
*clause_const;
bool pred_var_on_left,
clause_var_on_left;
+ Oid pred_collation,
+ clause_collation;
Oid pred_op,
clause_op,
test_op;
@@ -1421,6 +1423,14 @@ btree_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
return false;
/*
+ * They'd better have the same collation, too.
+ */
+ pred_collation = ((OpExpr *) predicate)->inputcollid;
+ clause_collation = ((OpExpr *) clause)->inputcollid;
+ if (pred_collation != clause_collation)
+ return false;
+
+ /*
* Okay, get the operators in the two clauses we're comparing. Commute
* them if needed so that we can assume the variables are on the left.
*/
@@ -1465,7 +1475,9 @@ btree_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
BOOLOID,
false,
(Expr *) pred_const,
- (Expr *) clause_const);
+ (Expr *) clause_const,
+ InvalidOid,
+ pred_collation);
/* Fill in opfuncids */
fix_opfuncids((Node *) test_expr);
diff --git a/src/backend/parser/Makefile b/src/backend/parser/Makefile
index ee92a8ca463..665250b9e76 100644
--- a/src/backend/parser/Makefile
+++ b/src/backend/parser/Makefile
@@ -13,9 +13,9 @@ include $(top_builddir)/src/Makefile.global
override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
OBJS= analyze.o gram.o keywords.o kwlookup.o parser.o \
- parse_agg.o parse_clause.o parse_coerce.o parse_cte.o parse_expr.o \
- parse_func.o parse_node.o parse_oper.o parse_param.o parse_relation.o \
- parse_target.o parse_type.o parse_utilcmd.o scansup.o
+ parse_agg.o parse_clause.o parse_coerce.o parse_collate.o parse_cte.o \
+ parse_expr.o parse_func.o parse_node.o parse_oper.o parse_param.o \
+ parse_relation.o parse_target.o parse_type.o parse_utilcmd.o scansup.o
FLEXFLAGS = -CF
diff --git a/src/backend/parser/README b/src/backend/parser/README
index 59cc32fef3a..08625e427d2 100644
--- a/src/backend/parser/README
+++ b/src/backend/parser/README
@@ -17,6 +17,7 @@ analyze.c top level of parse analysis for optimizable queries
parse_agg.c handle aggregates, like SUM(col1), AVG(col2), ...
parse_clause.c handle clauses like WHERE, ORDER BY, GROUP BY, ...
parse_coerce.c handle coercing expressions to different data types
+parse_collate.c assign collation information in completed expressions
parse_cte.c handle Common Table Expressions (WITH clauses)
parse_expr.c handle expressions like col, col + 3, x = 3 or x = 4
parse_func.c handle functions, table.column and column identifiers
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 85f231da9c5..315f067b17a 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -33,6 +33,7 @@
#include "parser/parse_agg.h"
#include "parser/parse_clause.h"
#include "parser/parse_coerce.h"
+#include "parser/parse_collate.h"
#include "parser/parse_cte.h"
#include "parser/parse_oper.h"
#include "parser/parse_param.h"
@@ -323,6 +324,8 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
if (pstate->p_hasWindowFuncs)
parseCheckWindowFuncs(pstate, qry);
+ assign_query_collations(pstate, qry);
+
return qry;
}
@@ -566,6 +569,14 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
stmt->cols,
icolumns, attrnos);
+ /*
+ * We must assign collations now because assign_query_collations
+ * doesn't process rangetable entries. We just assign all the
+ * collations independently in each row, and don't worry about
+ * whether they are consistent vertically either.
+ */
+ assign_list_collations(pstate, sublist);
+
exprsLists = lappend(exprsLists, sublist);
}
@@ -705,6 +716,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
parser_errposition(pstate,
locate_windowfunc((Node *) qry))));
+ assign_query_collations(pstate, qry);
+
return qry;
}
@@ -960,6 +973,8 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
(LockingClause *) lfirst(l), false);
}
+ assign_query_collations(pstate, qry);
+
return qry;
}
@@ -1082,6 +1097,14 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
i++;
}
+ /*
+ * We must assign collations now because assign_query_collations
+ * doesn't process rangetable entries. We just assign all the
+ * collations independently in each row, and don't worry about
+ * whether they are consistent vertically either.
+ */
+ assign_list_collations(pstate, newsublist);
+
newExprsLists = lappend(newExprsLists, newsublist);
}
@@ -1176,6 +1199,8 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
parser_errposition(pstate,
locate_windowfunc((Node *) newExprsLists))));
+ assign_query_collations(pstate, qry);
+
return qry;
}
@@ -1417,6 +1442,8 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
(LockingClause *) lfirst(l), false);
}
+ assign_query_collations(pstate, qry);
+
return qry;
}
@@ -1634,12 +1661,6 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
rescoltypmod = lcoltypmod;
else
rescoltypmod = -1;
- /* Select common collation. A common collation is
- * required for all set operators except UNION ALL; see
- * SQL:2008-2 7.13 SR 15c. */
- rescolcoll = select_common_collation(pstate,
- list_make2(lcolnode, rcolnode),
- (op->op == SETOP_UNION && op->all));
/*
* Verify the coercions are actually possible. If not, we'd fail
@@ -1662,26 +1683,46 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
* output type of the child query and the resolved target type.
* Such a discrepancy would disable optimization in the planner.
*
- * If it's some other UNKNOWN-type node, eg a Var, we do nothing.
- * The planner is sometimes able to fold an UNKNOWN Var to a
- * constant before it has to coerce the type, so failing now would
- * just break cases that might work.
+ * If it's some other UNKNOWN-type node, eg a Var, we do nothing
+ * (knowing that coerce_to_common_type would fail). The planner
+ * is sometimes able to fold an UNKNOWN Var to a constant before
+ * it has to coerce the type, so failing now would just break
+ * cases that might work.
*/
if (lcoltype != UNKNOWNOID)
- (void) coerce_to_common_type(pstate, lcolnode,
- rescoltype, context);
- else if (IsA(lcolnode, Const) ||IsA(lcolnode, Param))
- ltle->expr = (Expr *)
- coerce_to_common_type(pstate, lcolnode,
- rescoltype, context);
+ lcolnode = coerce_to_common_type(pstate, lcolnode,
+ rescoltype, context);
+ else if (IsA(lcolnode, Const) ||
+ IsA(lcolnode, Param))
+ {
+ lcolnode = coerce_to_common_type(pstate, lcolnode,
+ rescoltype, context);
+ ltle->expr = (Expr *) lcolnode;
+ }
if (rcoltype != UNKNOWNOID)
- (void) coerce_to_common_type(pstate, rcolnode,
- rescoltype, context);
- else if (IsA(rcolnode, Const) ||IsA(rcolnode, Param))
- rtle->expr = (Expr *)
- coerce_to_common_type(pstate, rcolnode,
- rescoltype, context);
+ rcolnode = coerce_to_common_type(pstate, rcolnode,
+ rescoltype, context);
+ else if (IsA(rcolnode, Const) ||
+ IsA(rcolnode, Param))
+ {
+ rcolnode = coerce_to_common_type(pstate, rcolnode,
+ rescoltype, context);
+ rtle->expr = (Expr *) rcolnode;
+ }
+
+ /*
+ * Select common collation. A common collation is required for
+ * all set operators except UNION ALL; see SQL:2008 7.13 <query
+ * expression> Syntax Rule 15c. (If we fail to identify a common
+ * collation for a UNION ALL column, the curCollations element
+ * will be set to InvalidOid, which may result in a runtime error
+ * if something at a higher query level wants to use the column's
+ * collation.)
+ */
+ rescolcoll = select_common_collation(pstate,
+ list_make2(lcolnode, rcolnode),
+ (op->op == SETOP_UNION && op->all));
/* emit results */
op->colTypes = lappend_oid(op->colTypes, rescoltype);
@@ -1734,7 +1775,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
rescolnode->typeId = rescoltype;
rescolnode->typeMod = rescoltypmod;
- rescolnode->collid = rescolcoll;
+ rescolnode->collation = rescolcoll;
rescolnode->location = bestlocation;
restle = makeTargetEntry((Expr *) rescolnode,
0, /* no need to set resno */
@@ -1966,6 +2007,8 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
if (origTargetList != NULL)
elog(ERROR, "UPDATE target count mismatch --- internal error");
+ assign_query_collations(pstate, qry);
+
return qry;
}
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 8267627c42f..523d6e6989a 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -709,6 +709,7 @@ check_ungrouped_columns_walker(Node *node,
* agg_input_types, agg_state_type, agg_result_type identify the input,
* transition, and result types of the aggregate. These should all be
* resolved to actual types (ie, none should ever be ANYELEMENT etc).
+ * agg_input_collation is the aggregate function's input collation.
*
* transfn_oid and finalfn_oid identify the funcs to be called; the latter
* may be InvalidOid.
@@ -721,9 +722,9 @@ build_aggregate_fnexprs(Oid *agg_input_types,
int agg_num_inputs,
Oid agg_state_type,
Oid agg_result_type,
+ Oid agg_input_collation,
Oid transfn_oid,
Oid finalfn_oid,
- Oid collation,
Expr **transfnexpr,
Expr **finalfnexpr)
{
@@ -742,7 +743,7 @@ build_aggregate_fnexprs(Oid *agg_input_types,
argp->paramid = -1;
argp->paramtype = agg_state_type;
argp->paramtypmod = -1;
- argp->paramcollation = collation;
+ argp->paramcollid = agg_input_collation;
argp->location = -1;
args = list_make1(argp);
@@ -754,7 +755,7 @@ build_aggregate_fnexprs(Oid *agg_input_types,
argp->paramid = -1;
argp->paramtype = agg_input_types[i];
argp->paramtypmod = -1;
- argp->paramcollation = collation;
+ argp->paramcollid = agg_input_collation;
argp->location = -1;
args = lappend(args, argp);
}
@@ -762,7 +763,8 @@ build_aggregate_fnexprs(Oid *agg_input_types,
*transfnexpr = (Expr *) makeFuncExpr(transfn_oid,
agg_state_type,
args,
- collation,
+ InvalidOid,
+ agg_input_collation,
COERCE_DONTCARE);
/* see if we have a final function */
@@ -780,13 +782,14 @@ build_aggregate_fnexprs(Oid *agg_input_types,
argp->paramid = -1;
argp->paramtype = agg_state_type;
argp->paramtypmod = -1;
- argp->paramcollation = collation;
+ argp->paramcollid = agg_input_collation;
argp->location = -1;
args = list_make1(argp);
*finalfnexpr = (Expr *) makeFuncExpr(finalfn_oid,
agg_result_type,
args,
- collation,
+ InvalidOid,
+ agg_input_collation,
COERCE_DONTCARE);
}
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 4c5a6fe0b01..6c0a78474cd 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -28,6 +28,7 @@
#include "parser/parsetree.h"
#include "parser/parse_clause.h"
#include "parser/parse_coerce.h"
+#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_oper.h"
#include "parser/parse_relation.h"
@@ -558,6 +559,11 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
funcexpr = transformExpr(pstate, r->funccallnode);
/*
+ * We must assign collations now so that we can fill funccolcollations.
+ */
+ assign_expr_collations(pstate, funcexpr);
+
+ /*
* The function parameters cannot make use of any variables from other
* FROM items. (Compare to transformRangeSubselect(); the coding is
* different though because we didn't parse as a sub-select with its own
@@ -1072,6 +1078,7 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
else if (l_colvar->vartypmod != outcoltypmod)
l_node = (Node *) makeRelabelType((Expr *) l_colvar,
outcoltype, outcoltypmod,
+ InvalidOid, /* fixed below */
COERCE_IMPLICIT_CAST);
else
l_node = (Node *) l_colvar;
@@ -1083,6 +1090,7 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
else if (r_colvar->vartypmod != outcoltypmod)
r_node = (Node *) makeRelabelType((Expr *) r_colvar,
outcoltype, outcoltypmod,
+ InvalidOid, /* fixed below */
COERCE_IMPLICIT_CAST);
else
r_node = (Node *) r_colvar;
@@ -1121,6 +1129,7 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
CoalesceExpr *c = makeNode(CoalesceExpr);
c->coalescetype = outcoltype;
+ /* coalescecollid will get set below */
c->args = list_make2(l_node, r_node);
c->location = -1;
res_node = (Node *) c;
@@ -1132,6 +1141,13 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
break;
}
+ /*
+ * Apply assign_expr_collations to fix up the collation info in the
+ * coercion and CoalesceExpr nodes, if we made any. This must be done
+ * now so that the join node's alias vars show correct collation info.
+ */
+ assign_expr_collations(pstate, res_node);
+
return res_node;
}
@@ -1936,7 +1952,6 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
bool resolveUnknown)
{
Oid restype = exprType((Node *) tle->expr);
- Oid rescollation = exprCollation((Node *) tle->expr);
Oid sortop;
Oid eqop;
bool hashable;
@@ -2020,12 +2035,6 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
break;
}
- if (type_is_collatable(restype) && !OidIsValid(rescollation))
- ereport(ERROR,
- (errcode(ERRCODE_INDETERMINATE_COLLATION),
- errmsg("no collation was derived for the sort expression"),
- errhint("Use the COLLATE clause to set the collation explicitly.")));
-
cancel_parser_errposition_callback(&pcbstate);
/* avoid making duplicate sortlist entries */
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 6aff34dd90d..9b59b032976 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -16,7 +16,6 @@
#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"
@@ -123,6 +122,9 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
* pstate is only used in the case that we are able to resolve the type of
* a previously UNKNOWN Param. It is okay to pass pstate = NULL if the
* caller does not want type information updated for Params.
+ *
+ * Note: this function must not modify the given expression tree, only add
+ * decoration on top of it. See transformSetOperationTree, for example.
*/
Node *
coerce_type(ParseState *pstate, Node *node,
@@ -282,16 +284,21 @@ coerce_type(ParseState *pstate, Node *node,
if (IsA(node, CollateExpr))
{
/*
- * 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.
+ * If we have a COLLATE clause, we have to push the coercion
+ * underneath the COLLATE. This is really ugly, but there is little
+ * choice because the above hacks on Consts and Params wouldn't happen
+ * otherwise.
*/
- CollateExpr *cc = (CollateExpr *) node;
-
- cc->arg = (Expr *) coerce_type(pstate, (Node *) cc->arg,
- inputTypeId, targetTypeId, targetTypeMod,
- ccontext, cformat, location);
- return (Node *) cc;
+ CollateExpr *coll = (CollateExpr *) node;
+ CollateExpr *newcoll = makeNode(CollateExpr);
+
+ newcoll->arg = (Expr *)
+ coerce_type(pstate, (Node *) coll->arg,
+ inputTypeId, targetTypeId, targetTypeMod,
+ ccontext, cformat, location);
+ newcoll->collOid = coll->collOid;
+ newcoll->location = coll->location;
+ return (Node *) newcoll;
}
pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
&funcId);
@@ -352,6 +359,7 @@ coerce_type(ParseState *pstate, Node *node,
*/
RelabelType *r = makeRelabelType((Expr *) result,
targetTypeId, -1,
+ InvalidOid,
cformat);
r->location = location;
@@ -591,6 +599,7 @@ coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId,
result->arg = (Expr *) arg;
result->resulttype = typeId;
result->resulttypmod = -1; /* currently, always -1 for domains */
+ /* resultcollid will be set by parse_collate.c */
result->coercionformat = cformat;
result->location = location;
@@ -734,7 +743,6 @@ build_coercion_expression(Node *node,
FuncExpr *fexpr;
List *args;
Const *cons;
- Oid collation;
Assert(OidIsValid(funcId));
@@ -766,9 +774,8 @@ build_coercion_expression(Node *node,
args = lappend(args, cons);
}
- collation = coercion_expression_result_collation(targetTypeId, node);
-
- fexpr = makeFuncExpr(funcId, targetTypeId, args, collation, cformat);
+ fexpr = makeFuncExpr(funcId, targetTypeId, args,
+ InvalidOid, InvalidOid, cformat);
fexpr->location = location;
return (Node *) fexpr;
}
@@ -2100,120 +2107,3 @@ 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, CollateExpr);
-
- if (pcoll && pexplicit)
- {
- ListCell *lc2;
- for_each_cell(lc2, lnext(lc))
- {
- Node *nexpr = (Node *) lfirst(lc2);
- Oid ncoll = exprCollation(nexpr);
- bool nexplicit = IsA(nexpr, CollateExpr);
-
- 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;
-}
diff --git a/src/backend/parser/parse_collate.c b/src/backend/parser/parse_collate.c
new file mode 100644
index 00000000000..0b77e3ea2b7
--- /dev/null
+++ b/src/backend/parser/parse_collate.c
@@ -0,0 +1,763 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_collate.c
+ * Routines for assigning collation information.
+ *
+ * We choose to handle collation analysis in a post-pass over the output
+ * of expression parse analysis. This is because we need more state to
+ * perform this processing than is needed in the finished tree. If we
+ * did it on-the-fly while building the tree, all that state would have
+ * to be kept in expression node trees permanently. This way, the extra
+ * storage is just local variables in this recursive routine.
+ *
+ * The info that is actually saved in the finished tree is:
+ * 1. The output collation of each expression node, or InvalidOid if it
+ * returns a noncollatable data type. This can also be InvalidOid if the
+ * result type is collatable but the collation is indeterminate.
+ * 2. The collation to be used in executing each function. InvalidOid means
+ * that there are no collatable inputs or their collation is indeterminate.
+ * This value is only stored in node types that might call collation-using
+ * functions.
+ *
+ * You might think we could get away with storing only one collation per
+ * node, but the two concepts really need to be kept distinct. Otherwise
+ * it's too confusing when a function produces a collatable output type but
+ * has no collatable inputs or produces noncollatable output from collatable
+ * inputs.
+ *
+ * Cases with indeterminate collation might result in an error being thrown
+ * at runtime. If we knew exactly which functions require collation
+ * information, we could throw those errors at parse time instead.
+ *
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/parser/parse_collate.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "catalog/pg_collation.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_collate.h"
+#include "utils/lsyscache.h"
+
+
+/*
+ * Collation strength (the SQL standard calls this "derivation"). Order is
+ * chosen to allow comparisons to work usefully. Note: the standard doesn't
+ * seem to distingish between NONE and CONFLICT.
+ */
+typedef enum
+{
+ COLLATE_NONE, /* expression is of a noncollatable datatype */
+ COLLATE_IMPLICIT, /* collation was derived implicitly */
+ COLLATE_CONFLICT, /* we had a conflict of implicit collations */
+ COLLATE_EXPLICIT /* collation was derived explicitly */
+} CollateStrength;
+
+typedef struct
+{
+ ParseState *pstate; /* parse state (for error reporting) */
+ Oid collation; /* OID of current collation, if any */
+ CollateStrength strength; /* strength of current collation choice */
+ int location; /* location of expr that set collation */
+ /* Remaining fields are only valid when strength == COLLATE_CONFLICT */
+ Oid collation2; /* OID of conflicting collation */
+ int location2; /* location of expr that set collation2 */
+} assign_collations_context;
+
+static bool assign_query_collations_walker(Node *node, ParseState *pstate);
+static bool assign_collations_walker(Node *node,
+ assign_collations_context *context);
+
+
+/*
+ * assign_query_collations()
+ * Mark all expressions in the given Query with collation information.
+ *
+ * This should be applied to each Query after completion of parse analysis
+ * for expressions. Note that we do not recurse into sub-Queries, since
+ * those should have been processed when built.
+ */
+void
+assign_query_collations(ParseState *pstate, Query *query)
+{
+ /*
+ * We just use query_tree_walker() to visit all the contained expressions.
+ * We can skip the rangetable and CTE subqueries, though, since RTEs and
+ * subqueries had better have been processed already (else Vars referring
+ * to them would not get created with the right collation).
+ */
+ (void) query_tree_walker(query,
+ assign_query_collations_walker,
+ (void *) pstate,
+ QTW_IGNORE_RANGE_TABLE |
+ QTW_IGNORE_CTE_SUBQUERIES);
+}
+
+/*
+ * Walker for assign_query_collations
+ *
+ * Each expression found by query_tree_walker is processed independently.
+ * Note that query_tree_walker may pass us a whole List, such as the
+ * targetlist, in which case each subexpression must be processed
+ * independently --- we don't want to bleat if two different targetentries
+ * have different collations.
+ */
+static bool
+assign_query_collations_walker(Node *node, ParseState *pstate)
+{
+ /* Need do nothing for empty subexpressions */
+ if (node == NULL)
+ return false;
+
+ /*
+ * We don't want to recurse into a set-operations tree; it's already
+ * been fully processed in transformSetOperationStmt.
+ */
+ if (IsA(node, SetOperationStmt))
+ return false;
+
+ if (IsA(node, List))
+ assign_list_collations(pstate, (List *) node);
+ else
+ assign_expr_collations(pstate, node);
+
+ return false;
+}
+
+/*
+ * assign_list_collations()
+ * Mark all nodes in the list of expressions with collation information.
+ *
+ * The list member expressions are processed independently; they do not have
+ * to share a common collation.
+ */
+void
+assign_list_collations(ParseState *pstate, List *exprs)
+{
+ ListCell *lc;
+
+ foreach(lc, exprs)
+ {
+ Node *node = (Node *) lfirst(lc);
+
+ assign_expr_collations(pstate, node);
+ }
+}
+
+/*
+ * assign_expr_collations()
+ * Mark all nodes in the given expression tree with collation information.
+ *
+ * This is exported for the benefit of various utility commands that process
+ * expressions without building a complete Query. It should be applied after
+ * calling transformExpr() plus any expression-modifying operations such as
+ * coerce_to_boolean().
+ */
+void
+assign_expr_collations(ParseState *pstate, Node *expr)
+{
+ assign_collations_context context;
+
+ /* initialize context for tree walk */
+ context.pstate = pstate;
+ context.collation = InvalidOid;
+ context.strength = COLLATE_NONE;
+ context.location = -1;
+
+ /* and away we go */
+ (void) assign_collations_walker(expr, &context);
+}
+
+/*
+ * select_common_collation()
+ * Identify a common collation for a list of expressions.
+ *
+ * The expressions should all return the same datatype, else this is not
+ * terribly meaningful.
+ *
+ * none_ok means that it is permitted to return InvalidOid, indicating that
+ * no common collation could be identified, even for collatable datatypes.
+ * Otherwise, an error is thrown for conflict of implicit collations.
+ *
+ * In theory, 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)
+{
+ assign_collations_context context;
+
+ /* initialize context for tree walk */
+ context.pstate = pstate;
+ context.collation = InvalidOid;
+ context.strength = COLLATE_NONE;
+ context.location = -1;
+
+ /* and away we go */
+ (void) assign_collations_walker((Node *) exprs, &context);
+
+ /* deal with collation conflict */
+ if (context.strength == COLLATE_CONFLICT)
+ {
+ if (none_ok)
+ return InvalidOid;
+ ereport(ERROR,
+ (errcode(ERRCODE_COLLATION_MISMATCH),
+ errmsg("collation mismatch between implicit collations \"%s\" and \"%s\"",
+ get_collation_name(context.collation),
+ get_collation_name(context.collation2)),
+ errhint("You can choose the collation by applying the COLLATE clause to one or both expressions."),
+ parser_errposition(context.pstate, context.location2)));
+ }
+
+ /*
+ * Note: if strength is still COLLATE_NONE, we'll return InvalidOid, but
+ * that's okay because it must mean none of the expressions returned
+ * collatable datatypes.
+ */
+ return context.collation;
+}
+
+/*
+ * assign_collations_walker()
+ * Recursive guts of collation processing.
+ *
+ * Nodes with no children (eg, Vars, Consts, Params) must have been marked
+ * when built. All upper-level nodes are marked here.
+ *
+ * Note: if this is invoked directly on a List, it will attempt to infer a
+ * common collation for all the list members. In particular, it will throw
+ * error if there are conflicting explicit collations for different members.
+ */
+static bool
+assign_collations_walker(Node *node, assign_collations_context *context)
+{
+ assign_collations_context loccontext;
+ Oid collation;
+ CollateStrength strength;
+ int location;
+
+ /* Need do nothing for empty subexpressions */
+ if (node == NULL)
+ return false;
+
+ /*
+ * Prepare for recursion. For most node types, though not all, the
+ * first thing we do is recurse to process all nodes below this one.
+ * Each level of the tree has its own local context.
+ */
+ loccontext.pstate = context->pstate;
+ loccontext.collation = InvalidOid;
+ loccontext.strength = COLLATE_NONE;
+ loccontext.location = -1;
+
+ /*
+ * Recurse if appropriate, then determine the collation for this node.
+ *
+ * Note: the general cases are at the bottom of the switch, after various
+ * special cases.
+ */
+ switch (nodeTag(node))
+ {
+ case T_CollateExpr:
+ {
+ /*
+ * COLLATE sets an explicitly derived collation, regardless of
+ * what the child state is. But we must recurse to set up
+ * collation info below here.
+ */
+ CollateExpr *expr = (CollateExpr *) node;
+
+ (void) expression_tree_walker(node,
+ assign_collations_walker,
+ (void *) &loccontext);
+
+ collation = expr->collOid;
+ Assert(OidIsValid(collation));
+ strength = COLLATE_EXPLICIT;
+ location = expr->location;
+ }
+ break;
+ case T_FieldSelect:
+ {
+ /*
+ * FieldSelect is a special case because the field may have
+ * a non-default collation, in which case we should use that.
+ * The field's collation was already looked up and saved
+ * in the node.
+ */
+ FieldSelect *expr = (FieldSelect *) node;
+
+ /* ... but first, recurse */
+ (void) expression_tree_walker(node,
+ assign_collations_walker,
+ (void *) &loccontext);
+
+ if (OidIsValid(expr->resultcollid))
+ {
+ /* Node's result type is collatable. */
+ if (expr->resultcollid == DEFAULT_COLLATION_OID)
+ {
+ /*
+ * The immediate input node necessarily yields a
+ * composite type, so it will have no exposed
+ * collation. However, if we are selecting a field
+ * from a function returning composite, see if we
+ * can bubble up a collation from the function's
+ * input. XXX this is a bit of a hack, rethink ...
+ */
+ if (IsA(expr->arg, FuncExpr))
+ {
+ FuncExpr *fexpr = (FuncExpr *) expr->arg;
+
+ if (OidIsValid(fexpr->inputcollid))
+ expr->resultcollid = fexpr->inputcollid;
+ }
+ }
+ /* Pass up field's collation as an implicit choice. */
+ collation = expr->resultcollid;
+ strength = COLLATE_IMPLICIT;
+ location = exprLocation(node);
+ }
+ else
+ {
+ /* Node's result type isn't collatable. */
+ collation = InvalidOid;
+ strength = COLLATE_NONE;
+ location = -1; /* won't be used */
+ }
+ }
+ break;
+ case T_CaseExpr:
+ {
+ /*
+ * CaseExpr is a special case because we do not want to
+ * recurse into the test expression (if any). It was
+ * already marked with collations during transformCaseExpr,
+ * and furthermore its collation is not relevant to the
+ * result of the CASE --- only the output expressions are.
+ * So we can't use expression_tree_walker here.
+ */
+ CaseExpr *expr = (CaseExpr *) node;
+ Oid typcollation;
+ ListCell *lc;
+
+ foreach(lc, expr->args)
+ {
+ CaseWhen *when = (CaseWhen *) lfirst(lc);
+
+ Assert(IsA(when, CaseWhen));
+ /*
+ * The condition expressions mustn't affect the CASE's
+ * result collation either; but since they are known to
+ * yield boolean, it's safe to recurse directly on them
+ * --- they won't change loccontext.
+ */
+ (void) assign_collations_walker((Node *) when->expr,
+ &loccontext);
+ (void) assign_collations_walker((Node *) when->result,
+ &loccontext);
+ }
+ (void) assign_collations_walker((Node *) expr->defresult,
+ &loccontext);
+
+ /*
+ * Now determine the CASE's output collation. This is the
+ * same as the general case below.
+ */
+ typcollation = get_typcollation(exprType(node));
+ if (OidIsValid(typcollation))
+ {
+ /* Node's result is collatable; what about its input? */
+ if (loccontext.strength > COLLATE_NONE)
+ {
+ /* Collation state bubbles up from children. */
+ collation = loccontext.collation;
+ strength = loccontext.strength;
+ location = loccontext.location;
+ }
+ else
+ {
+ /*
+ * Collatable output produced without any collatable
+ * input. Use the type's collation (which is usually
+ * DEFAULT_COLLATION_OID, but might be different for a
+ * domain).
+ */
+ collation = typcollation;
+ strength = COLLATE_IMPLICIT;
+ location = exprLocation(node);
+ }
+ }
+ else
+ {
+ /* Node's result type isn't collatable. */
+ collation = InvalidOid;
+ strength = COLLATE_NONE;
+ location = -1; /* won't be used */
+ }
+
+ /*
+ * Save the state into the expression node. We know it
+ * doesn't care about input collation.
+ */
+ if (strength == COLLATE_CONFLICT)
+ exprSetCollation(node, InvalidOid);
+ else
+ exprSetCollation(node, collation);
+ }
+ break;
+ case T_RowExpr:
+ {
+ /*
+ * RowExpr is a special case because the subexpressions
+ * are independent: we don't want to complain if some of
+ * them have incompatible explicit collations.
+ */
+ RowExpr *expr = (RowExpr *) node;
+
+ assign_list_collations(context->pstate, expr->args);
+
+ /*
+ * Since the result is always composite and therefore never
+ * has a collation, we can just stop here: this node has no
+ * impact on the collation of its parent.
+ */
+ return false; /* done */
+ }
+ case T_RowCompareExpr:
+ {
+ /*
+ * For RowCompare, we have to find the common collation of
+ * each pair of input columns and build a list. If we can't
+ * find a common collation, we just put InvalidOid into the
+ * list, which may or may not cause an error at runtime.
+ */
+ RowCompareExpr *expr = (RowCompareExpr *) node;
+ List *colls = NIL;
+ ListCell *l;
+ ListCell *r;
+
+ forboth(l, expr->largs, r, expr->rargs)
+ {
+ Node *le = (Node *) lfirst(l);
+ Node *re = (Node *) lfirst(r);
+ Oid coll;
+
+ coll = select_common_collation(context->pstate,
+ list_make2(le, re),
+ true);
+ colls = lappend_oid(colls, coll);
+ }
+ expr->inputcollids = colls;
+
+ /*
+ * Since the result is always boolean and therefore never
+ * has a collation, we can just stop here: this node has no
+ * impact on the collation of its parent.
+ */
+ return false; /* done */
+ }
+ case T_CoerceToDomain:
+ {
+ /*
+ * If the domain declaration included a non-default COLLATE
+ * spec, then use that collation as the output collation of
+ * the coercion. Otherwise allow the input collation to
+ * bubble up. (The input should be of the domain's base
+ * type, therefore we don't need to worry about it not being
+ * collatable when the domain is.)
+ */
+ CoerceToDomain *expr = (CoerceToDomain *) node;
+ Oid typcollation = get_typcollation(expr->resulttype);
+
+ /* ... but first, recurse */
+ (void) expression_tree_walker(node,
+ assign_collations_walker,
+ (void *) &loccontext);
+
+ if (OidIsValid(typcollation))
+ {
+ /* Node's result type is collatable. */
+ if (typcollation == DEFAULT_COLLATION_OID)
+ {
+ /* Collation state bubbles up from child. */
+ collation = loccontext.collation;
+ strength = loccontext.strength;
+ location = loccontext.location;
+ }
+ else
+ {
+ /* Use domain's collation as an implicit choice. */
+ collation = typcollation;
+ strength = COLLATE_IMPLICIT;
+ location = exprLocation(node);
+ }
+ }
+ else
+ {
+ /* Node's result type isn't collatable. */
+ collation = InvalidOid;
+ strength = COLLATE_NONE;
+ location = -1; /* won't be used */
+ }
+
+ /*
+ * Save the state into the expression node. We know it
+ * doesn't care about input collation.
+ */
+ if (strength == COLLATE_CONFLICT)
+ exprSetCollation(node, InvalidOid);
+ else
+ exprSetCollation(node, collation);
+ }
+ break;
+ case T_TargetEntry:
+ (void) expression_tree_walker(node,
+ assign_collations_walker,
+ (void *) &loccontext);
+
+ /*
+ * TargetEntry can have only one child, and should bubble that
+ * state up to its parent. We can't use the general-case code
+ * below because exprType and friends don't work on TargetEntry.
+ */
+ collation = loccontext.collation;
+ strength = loccontext.strength;
+ location = loccontext.location;
+ break;
+ case T_RangeTblRef:
+ case T_JoinExpr:
+ case T_FromExpr:
+ case T_SortGroupClause:
+ (void) expression_tree_walker(node,
+ assign_collations_walker,
+ (void *) &loccontext);
+ /*
+ * When we're invoked on a query's jointree, we don't need to do
+ * anything with join nodes except recurse through them to process
+ * WHERE/ON expressions. So just stop here. Likewise, we don't
+ * need to do anything when invoked on sort/group lists.
+ */
+ return false;
+ case T_Query:
+ {
+ /*
+ * We get here when we're invoked on the Query belonging to a
+ * SubLink. Act as though the Query returns its first output
+ * column, which indeed is what it does for EXPR_SUBLINK and
+ * ARRAY_SUBLINK cases. In the cases where the SubLink
+ * returns boolean, this info will be ignored.
+ *
+ * We needn't recurse, since the Query is already processed.
+ */
+ Query *qtree = (Query *) node;
+ TargetEntry *tent;
+
+ tent = (TargetEntry *) linitial(qtree->targetList);
+ Assert(IsA(tent, TargetEntry));
+ Assert(!tent->resjunk);
+ collation = exprCollation((Node *) tent->expr);
+ /* collation doesn't change if it's converted to array */
+ strength = COLLATE_IMPLICIT;
+ location = exprLocation((Node *) tent->expr);
+ }
+ break;
+ case T_List:
+ (void) expression_tree_walker(node,
+ assign_collations_walker,
+ (void *) &loccontext);
+
+ /*
+ * When processing a list, collation state just bubbles up from
+ * the list elements.
+ */
+ collation = loccontext.collation;
+ strength = loccontext.strength;
+ location = loccontext.location;
+ break;
+
+ case T_Var:
+ case T_Const:
+ case T_Param:
+ case T_CoerceToDomainValue:
+ case T_CaseTestExpr:
+ case T_SetToDefault:
+ case T_CurrentOfExpr:
+ /*
+ * General case for childless expression nodes. These should
+ * already have a collation assigned; it is not this function's
+ * responsibility to look into the catalogs for base-case
+ * information.
+ */
+ collation = exprCollation(node);
+
+ /*
+ * Note: in most cases, there will be an assigned collation
+ * whenever type_is_collatable(exprType(node)); but an exception
+ * occurs for a Var referencing a subquery output column for
+ * which a unique collation was not determinable. That may lead
+ * to a runtime failure if a collation-sensitive function is
+ * applied to the Var.
+ */
+
+ if (OidIsValid(collation))
+ strength = COLLATE_IMPLICIT;
+ else
+ strength = COLLATE_NONE;
+ location = exprLocation(node);
+ break;
+
+ default:
+ {
+ /*
+ * General case for most expression nodes with children.
+ * First recurse, then figure out what to assign here.
+ */
+ Oid typcollation;
+
+ (void) expression_tree_walker(node,
+ assign_collations_walker,
+ (void *) &loccontext);
+
+ typcollation = get_typcollation(exprType(node));
+ if (OidIsValid(typcollation))
+ {
+ /* Node's result is collatable; what about its input? */
+ if (loccontext.strength > COLLATE_NONE)
+ {
+ /* Collation state bubbles up from children. */
+ collation = loccontext.collation;
+ strength = loccontext.strength;
+ location = loccontext.location;
+ }
+ else
+ {
+ /*
+ * Collatable output produced without any collatable
+ * input. Use the type's collation (which is usually
+ * DEFAULT_COLLATION_OID, but might be different for a
+ * domain).
+ */
+ collation = typcollation;
+ strength = COLLATE_IMPLICIT;
+ location = exprLocation(node);
+ }
+ }
+ else
+ {
+ /* Node's result type isn't collatable. */
+ collation = InvalidOid;
+ strength = COLLATE_NONE;
+ location = -1; /* won't be used */
+ }
+
+ /*
+ * Save the result collation into the expression node.
+ * If the state is COLLATE_CONFLICT, we'll set the collation
+ * to InvalidOid, which might result in an error at runtime.
+ */
+ if (strength == COLLATE_CONFLICT)
+ exprSetCollation(node, InvalidOid);
+ else
+ exprSetCollation(node, collation);
+
+ /*
+ * Likewise save the input collation, which is the one that
+ * any function called by this node should use.
+ */
+ if (loccontext.strength == COLLATE_CONFLICT)
+ exprSetInputCollation(node, InvalidOid);
+ else
+ exprSetInputCollation(node, loccontext.collation);
+ }
+ break;
+ }
+
+ /*
+ * Now, merge my information into my parent's state. If the collation
+ * strength for this node is different from what's already in *context,
+ * then this node either dominates or is dominated by earlier siblings.
+ */
+ if (strength > context->strength)
+ {
+ /* Override previous parent state */
+ context->collation = collation;
+ context->strength = strength;
+ context->location = location;
+ /* Bubble up error info if applicable */
+ if (strength == COLLATE_CONFLICT)
+ {
+ context->collation2 = loccontext.collation2;
+ context->location2 = loccontext.location2;
+ }
+ }
+ else if (strength == context->strength)
+ {
+ /* Merge, or detect error if there's a collation conflict */
+ switch (strength)
+ {
+ case COLLATE_NONE:
+ /* Nothing + nothing is still nothing */
+ break;
+ case COLLATE_IMPLICIT:
+ if (collation != context->collation)
+ {
+ /*
+ * Non-default implicit collation always beats default.
+ */
+ if (context->collation == DEFAULT_COLLATION_OID)
+ {
+ /* Override previous parent state */
+ context->collation = collation;
+ context->strength = strength;
+ context->location = location;
+ }
+ else if (collation != DEFAULT_COLLATION_OID)
+ {
+ /*
+ * Ooops, we have a conflict. We cannot throw error
+ * here, since the conflict could be resolved by a
+ * later sibling CollateExpr, or the parent might not
+ * care about collation anyway. Return enough info to
+ * throw the error later, if needed.
+ */
+ context->strength = COLLATE_CONFLICT;
+ context->collation2 = collation;
+ context->location2 = location;
+ }
+ }
+ break;
+ case COLLATE_CONFLICT:
+ /* We're still conflicted ... */
+ break;
+ case COLLATE_EXPLICIT:
+ if (collation != context->collation)
+ {
+ /*
+ * Ooops, we have a conflict of explicit COLLATE clauses.
+ * Here we choose to throw error immediately; that is what
+ * the SQL standard says to do, and there's no good reason
+ * to be less strict.
+ */
+ ereport(ERROR,
+ (errcode(ERRCODE_COLLATION_MISMATCH),
+ errmsg("collation mismatch between explicit collations \"%s\" and \"%s\"",
+ get_collation_name(context->collation),
+ get_collation_name(collation)),
+ parser_errposition(context->pstate, location)));
+ }
+ break;
+ }
+ }
+
+ return false;
+}
diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c
index 23b72b245b2..c527f7589e2 100644
--- a/src/backend/parser/parse_cte.c
+++ b/src/backend/parser/parse_cte.c
@@ -405,12 +405,16 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
* might see "unknown" as a result of an untyped literal in the
* non-recursive term's select list, and if we don't convert to text
* then we'll have a mismatch against the UNION result.
+ *
+ * The column might contain 'foo' COLLATE "bar", so don't override
+ * collation if it's already set.
*/
if (cte->cterecursive && coltype == UNKNOWNOID)
{
coltype = TEXTOID;
coltypmod = -1; /* should be -1 already, but be sure */
- colcoll = DEFAULT_COLLATION_OID;
+ if (!OidIsValid(colcoll))
+ colcoll = DEFAULT_COLLATION_OID;
}
cte->ctecoltypes = lappend_oid(cte->ctecoltypes, coltype);
cte->ctecoltypmods = lappend_int(cte->ctecoltypmods, coltypmod);
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 17bd2bf50ae..4986e0e5fab 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -23,6 +23,7 @@
#include "optimizer/var.h"
#include "parser/analyze.h"
#include "parser/parse_coerce.h"
+#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
@@ -309,8 +310,8 @@ transformExpr(ParseState *pstate, Node *expr)
case T_FuncExpr:
case T_OpExpr:
case T_DistinctExpr:
- case T_ScalarArrayOpExpr:
case T_NullIfExpr:
+ case T_ScalarArrayOpExpr:
case T_BoolExpr:
case T_FieldSelect:
case T_FieldStore:
@@ -429,7 +430,6 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
exprType(result),
InvalidOid,
exprTypmod(result),
- exprCollation(result),
subscripts,
NULL);
subscripts = NIL;
@@ -451,7 +451,6 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
exprType(result),
InvalidOid,
exprTypmod(result),
- exprCollation(result),
subscripts,
NULL);
@@ -1001,25 +1000,34 @@ transformAExprNullIf(ParseState *pstate, A_Expr *a)
{
Node *lexpr = transformExpr(pstate, a->lexpr);
Node *rexpr = transformExpr(pstate, a->rexpr);
- Node *result;
+ OpExpr *result;
- result = (Node *) make_op(pstate,
- a->name,
- lexpr,
- rexpr,
- a->location);
- if (((OpExpr *) result)->opresulttype != BOOLOID)
+ result = (OpExpr *) make_op(pstate,
+ a->name,
+ lexpr,
+ rexpr,
+ a->location);
+
+ /*
+ * The comparison operator itself should yield boolean ...
+ */
+ if (result->opresulttype != BOOLOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("NULLIF requires = operator to yield boolean"),
parser_errposition(pstate, a->location)));
/*
+ * ... but the NullIfExpr will yield the first operand's type.
+ */
+ result->opresulttype = exprType((Node *) linitial(result->args));
+
+ /*
* We rely on NullIfExpr and OpExpr being the same struct
*/
NodeSetTag(result, T_NullIfExpr);
- return result;
+ return (Node *) result;
}
static Node *
@@ -1153,6 +1161,7 @@ transformAExprIn(ParseState *pstate, A_Expr *a)
}
newa = makeNode(ArrayExpr);
newa->array_typeid = array_type;
+ /* array_collid will be set by parse_collate.c */
newa->element_typeid = scalar_type;
newa->elements = aexprs;
newa->multidims = false;
@@ -1272,6 +1281,14 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c)
if (exprType(arg) == UNKNOWNOID)
arg = coerce_to_common_type(pstate, arg, TEXTOID, "CASE");
+ /*
+ * Run collation assignment on the test expression so that we know
+ * what collation to mark the placeholder with. In principle we
+ * could leave it to parse_collate.c to do that later, but propagating
+ * the result to the CaseTestExpr would be unnecessarily complicated.
+ */
+ assign_expr_collations(pstate, arg);
+
placeholder = makeNode(CaseTestExpr);
placeholder->typeId = exprType(arg);
placeholder->typeMod = exprTypmod(arg);
@@ -1340,6 +1357,7 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c)
ptype = select_common_type(pstate, resultexprs, "CASE", NULL);
Assert(OidIsValid(ptype));
newc->casetype = ptype;
+ /* casecollid will be set by parse_collate.c */
/* Convert default result clause, if necessary */
newc->defresult = (Expr *)
@@ -1360,8 +1378,6 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c)
"CASE/WHEN");
}
- newc->casecollation = select_common_collation(pstate, resultexprs, true);
-
newc->location = c->location;
return (Node *) newc;
@@ -1472,7 +1488,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
param->paramid = tent->resno;
param->paramtype = exprType((Node *) tent->expr);
param->paramtypmod = exprTypmod((Node *) tent->expr);
- param->paramcollation = exprCollation((Node *) tent->expr);
+ param->paramcollid = exprCollation((Node *) tent->expr);
param->location = -1;
right_list = lappend(right_list, param);
@@ -1660,6 +1676,7 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
}
newa->array_typeid = array_type;
+ /* array_collid will be set by parse_collate.c */
newa->element_typeid = element_type;
newa->elements = newcoercedelems;
newa->location = a->location;
@@ -1702,6 +1719,7 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c)
}
newc->coalescetype = select_common_type(pstate, newargs, "COALESCE", NULL);
+ /* coalescecollid will be set by parse_collate.c */
/* Convert arguments if necessary */
foreach(args, newargs)
@@ -1716,7 +1734,6 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c)
}
newc->args = newcoercedargs;
- newc->coalescecollation = select_common_collation(pstate, newcoercedargs, true);
newc->location = c->location;
return (Node *) newc;
}
@@ -1741,7 +1758,7 @@ transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m)
}
newm->minmaxtype = select_common_type(pstate, newargs, funcname, NULL);
- newm->collid = select_common_collation(pstate, newargs, false);
+ /* minmaxcollid and inputcollid will be set by parse_collate.c */
/* Convert arguments if necessary */
foreach(args, newargs)
@@ -2149,7 +2166,6 @@ make_row_comparison_op(ParseState *pstate, List *opname,
List *opexprs;
List *opnos;
List *opfamilies;
- List *collids;
ListCell *l,
*r;
List **opfamily_lists;
@@ -2320,7 +2336,6 @@ make_row_comparison_op(ParseState *pstate, List *opname,
* possibility that make_op inserted coercion operations.
*/
opnos = NIL;
- collids = NIL;
largs = NIL;
rargs = NIL;
foreach(l, opexprs)
@@ -2328,7 +2343,6 @@ make_row_comparison_op(ParseState *pstate, List *opname,
OpExpr *cmp = (OpExpr *) lfirst(l);
opnos = lappend_oid(opnos, cmp->opno);
- collids = lappend_oid(collids, cmp->collid);
largs = lappend(largs, linitial(cmp->args));
rargs = lappend(rargs, lsecond(cmp->args));
}
@@ -2337,7 +2351,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
rcexpr->rctype = rctype;
rcexpr->opnos = opnos;
rcexpr->opfamilies = opfamilies;
- rcexpr->collids = collids;
+ rcexpr->inputcollids = NIL; /* assign_expr_collations will fix this */
rcexpr->largs = largs;
rcexpr->rargs = rargs;
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index a2d6c598104..a187287e283 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -78,7 +78,6 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
bool retset;
int nvargs;
FuncDetailCode fdresult;
- Oid funccollid;
/*
* Most of the rest of the parser just assumes that functions do not have
@@ -344,12 +343,6 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
/* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types);
- /* XXX: If we knew which functions required collation information,
- * we could selectively set the last argument to true here. */
- funccollid = select_common_collation(pstate, fargs, false);
- if (!OidIsValid(funccollid))
- funccollid = get_typcollation(rettype);
-
/*
* If it's a variadic function call, transform the last nvargs arguments
* into an array --- unless it's an "any" variadic.
@@ -374,6 +367,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
errmsg("could not find array type for data type %s",
format_type_be(newa->element_typeid)),
parser_errposition(pstate, exprLocation((Node *) vargs))));
+ /* array_collid will be set by parse_collate.c */
newa->multidims = false;
newa->location = exprLocation((Node *) vargs);
@@ -389,8 +383,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
funcexpr->funcresulttype = rettype;
funcexpr->funcretset = retset;
funcexpr->funcformat = COERCE_EXPLICIT_CALL;
+ /* funccollid and inputcollid will be set by parse_collate.c */
funcexpr->args = fargs;
- funcexpr->collid = funccollid;
funcexpr->location = location;
retval = (Node *) funcexpr;
@@ -402,9 +396,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
aggref->aggfnoid = funcid;
aggref->aggtype = rettype;
+ /* aggcollid and inputcollid will be set by parse_collate.c */
/* args, aggorder, aggdistinct will be set by transformAggregateCall */
aggref->aggstar = agg_star;
- aggref->collid = funccollid;
/* agglevelsup will be set by transformAggregateCall */
aggref->location = location;
@@ -458,11 +452,11 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
wfunc->winfnoid = funcid;
wfunc->wintype = rettype;
+ /* wincollid and inputcollid will be set by parse_collate.c */
wfunc->args = fargs;
/* winref will be set by transformWindowFuncCall */
wfunc->winstar = agg_star;
wfunc->winagg = (fdresult == FUNCDETAIL_AGGREGATE);
- wfunc->collid = funccollid;
wfunc->location = location;
/*
@@ -1390,7 +1384,8 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg,
fselect->fieldnum = i + 1;
fselect->resulttype = att->atttypid;
fselect->resulttypmod = att->atttypmod;
- fselect->resultcollation = att->attcollation;
+ /* resultcollid may get overridden by parse_collate.c */
+ fselect->resultcollid = att->attcollation;
return (Node *) fselect;
}
}
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 163fc891799..2c76c557ecc 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -270,7 +270,6 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
* elementType OID of array's element type (fetch with transformArrayType,
* or pass InvalidOid to do it here)
* arrayTypMod typmod for the array (which is also typmod for the elements)
- * arrayColl OID of collation of array and array's elements
* indirection Untransformed list of subscripts (must not be NIL)
* assignFrom NULL for array fetch, else transformed expression for source.
*/
@@ -280,7 +279,6 @@ transformArraySubscripts(ParseState *pstate,
Oid arrayType,
Oid elementType,
int32 arrayTypMod,
- Oid arrayColl,
List *indirection,
Node *assignFrom)
{
@@ -407,7 +405,7 @@ transformArraySubscripts(ParseState *pstate,
aref->refarraytype = arrayType;
aref->refelemtype = elementType;
aref->reftypmod = arrayTypMod;
- aref->refcollid = arrayColl;
+ /* refcollid will be set by parse_collate.c */
aref->refupperindexpr = upperIndexpr;
aref->reflowerindexpr = lowerIndexpr;
aref->refexpr = (Expr *) arrayBase;
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index cad41d46f09..822e0a0a628 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -782,7 +782,6 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
List *args;
Oid rettype;
OpExpr *result;
- Oid opcollid;
/* Select the operator */
if (rtree == NULL)
@@ -862,20 +861,14 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
/* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
- /* XXX: If we knew which functions required collation information,
- * we could selectively set the last argument to true here. */
- opcollid = select_common_collation(pstate, args, false);
- if (!OidIsValid(opcollid))
- opcollid = get_typcollation(rettype);
-
/* and build the expression node */
result = makeNode(OpExpr);
result->opno = oprid(tup);
result->opfuncid = opform->oprcode;
result->opresulttype = rettype;
result->opretset = get_func_retset(opform->oprcode);
+ /* opcollid and inputcollid will be set by parse_collate.c */
result->args = args;
- result->collid = opcollid;
result->location = location;
ReleaseSysCache(tup);
@@ -904,7 +897,6 @@ make_scalar_array_op(ParseState *pstate, List *opname,
List *args;
Oid rettype;
ScalarArrayOpExpr *result;
- Oid opcollid;
ltypeId = exprType(ltree);
atypeId = exprType(rtree);
@@ -999,19 +991,13 @@ make_scalar_array_op(ParseState *pstate, List *opname,
/* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
- /* XXX: If we knew which functions required collation information,
- * we could selectively set the last argument to true here. */
- opcollid = select_common_collation(pstate, args, false);
- if (!OidIsValid(opcollid))
- opcollid = get_typcollation(rettype);
-
/* and build the expression node */
result = makeNode(ScalarArrayOpExpr);
result->opno = oprid(tup);
result->opfuncid = opform->oprcode;
result->useOr = useOr;
+ /* inputcollid will be set by parse_collate.c */
result->args = args;
- result->collid = opcollid;
result->location = location;
ReleaseSysCache(tup);
diff --git a/src/backend/parser/parse_param.c b/src/backend/parser/parse_param.c
index 9e9f2e3ca0b..1cf255669ac 100644
--- a/src/backend/parser/parse_param.c
+++ b/src/backend/parser/parse_param.c
@@ -114,7 +114,7 @@ fixed_paramref_hook(ParseState *pstate, ParamRef *pref)
param->paramid = paramno;
param->paramtype = parstate->paramTypes[paramno - 1];
param->paramtypmod = -1;
- param->paramcollation = get_typcollation(param->paramtype);
+ param->paramcollid = get_typcollation(param->paramtype);
param->location = pref->location;
return (Node *) param;
@@ -167,7 +167,7 @@ variable_paramref_hook(ParseState *pstate, ParamRef *pref)
param->paramid = paramno;
param->paramtype = *pptype;
param->paramtypmod = -1;
- param->paramcollation = get_typcollation(param->paramtype);
+ param->paramcollid = get_typcollation(param->paramtype);
param->location = pref->location;
return (Node *) param;
@@ -231,6 +231,8 @@ variable_coerce_param_hook(ParseState *pstate, Param *param,
*/
param->paramtypmod = -1;
+ param->paramcollid = get_typcollation(param->paramtype);
+
/* Use the leftmost of the param's and coercion's locations */
if (location >= 0 &&
(param->location < 0 || location < param->location))
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index fd1529fb3f9..550783547e8 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -398,7 +398,7 @@ transformAssignedExpr(ParseState *pstate,
def->typeId = attrtype;
def->typeMod = attrtypmod;
- def->collid = attrcollation;
+ def->collation = attrcollation;
if (indirection)
{
if (IsA(linitial(indirection), A_Indices))
@@ -785,7 +785,6 @@ transformAssignmentSubscripts(ParseState *pstate,
arrayType,
elementTypeId,
arrayTypMod,
- InvalidOid,
subscripts,
rhs);
@@ -1267,7 +1266,8 @@ ExpandRowReference(ParseState *pstate, Node *expr,
fselect->fieldnum = i + 1;
fselect->resulttype = att->atttypid;
fselect->resulttypmod = att->atttypmod;
- fselect->resultcollation = att->attcollation;
+ /* resultcollid may get overridden by parse_collate.c */
+ fselect->resultcollid = att->attcollation;
if (targetlist)
{
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 06baf89886a..3dffcded4ac 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -46,6 +46,7 @@
#include "nodes/nodeFuncs.h"
#include "parser/analyze.h"
#include "parser/parse_clause.h"
+#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
@@ -1798,6 +1799,9 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString)
/* Now do parse transformation of the expression */
ielem->expr = transformExpr(pstate, ielem->expr);
+ /* We have to fix its collations too */
+ assign_expr_collations(pstate, ielem->expr);
+
/*
* We check only that the result type is legitimate; this is for
* consistency with what transformWhereClause() checks for the
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index ac0c53a7f32..573c8dd4104 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -4869,6 +4869,16 @@ get_rule_expr(Node *node, deparse_context *context,
}
break;
+ case T_NullIfExpr:
+ {
+ NullIfExpr *nullifexpr = (NullIfExpr *) node;
+
+ appendStringInfo(buf, "NULLIF(");
+ get_rule_expr((Node *) nullifexpr->args, context, true);
+ appendStringInfoChar(buf, ')');
+ }
+ break;
+
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -5529,8 +5539,9 @@ get_rule_expr(Node *node, deparse_context *context,
}
if (xexpr->op == IS_XMLSERIALIZE)
- appendStringInfo(buf, " AS %s", format_type_with_typemod(xexpr->type,
- xexpr->typmod));
+ appendStringInfo(buf, " AS %s",
+ format_type_with_typemod(xexpr->type,
+ xexpr->typmod));
if (xexpr->op == IS_DOCUMENT)
appendStringInfoString(buf, " IS DOCUMENT");
else
@@ -5538,16 +5549,6 @@ get_rule_expr(Node *node, deparse_context *context,
}
break;
- case T_NullIfExpr:
- {
- NullIfExpr *nullifexpr = (NullIfExpr *) node;
-
- appendStringInfo(buf, "NULLIF(");
- get_rule_expr((Node *) nullifexpr->args, context, true);
- appendStringInfoChar(buf, ')');
- }
- break;
-
case T_NullTest:
{
NullTest *ntest = (NullTest *) node;
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 5cad1b88ad5..33f300bfea2 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -285,7 +285,7 @@ var_eq_const(VariableStatData *vardata, Oid operator,
FmgrInfo eqproc;
fmgr_info(get_opcode(operator), &eqproc);
- fmgr_info_collation(DEFAULT_COLLATION_OID, &eqproc);
+ fmgr_info_set_collation(DEFAULT_COLLATION_OID, &eqproc);
for (i = 0; i < nvalues; i++)
{
@@ -515,7 +515,7 @@ scalarineqsel(PlannerInfo *root, Oid operator, bool isgt,
stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple);
fmgr_info(get_opcode(operator), &opproc);
- fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc);
+ fmgr_info_set_collation(DEFAULT_COLLATION_OID, &opproc);
/*
* If we have most-common-values info, add up the fractions of the MCV
@@ -1252,7 +1252,7 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype, bool negate)
/* Try to use the histogram entries to get selectivity */
fmgr_info(get_opcode(operator), &opproc);
- fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc);
+ fmgr_info_set_collation(DEFAULT_COLLATION_OID, &opproc);
selec = histogram_selectivity(&vardata, &opproc, constval, true,
10, 1, &hist_size);
@@ -1701,7 +1701,7 @@ scalararraysel(PlannerInfo *root,
if (!oprsel)
return (Selectivity) 0.5;
fmgr_info(oprsel, &oprselproc);
- fmgr_info_collation(DEFAULT_COLLATION_OID, &oprselproc);
+ fmgr_info_set_collation(DEFAULT_COLLATION_OID, &oprselproc);
/* deconstruct the expression */
Assert(list_length(clause->args) == 2);
@@ -1839,6 +1839,7 @@ scalararraysel(PlannerInfo *root,
dummyexpr = makeNode(CaseTestExpr);
dummyexpr->typeId = nominal_element_type;
dummyexpr->typeMod = -1;
+ dummyexpr->collation = clause->inputcollid;
args = list_make2(leftop, dummyexpr);
if (is_join_clause)
s2 = DatumGetFloat8(FunctionCall5(&oprselproc,
@@ -2118,7 +2119,7 @@ eqjoinsel_inner(Oid operator,
nmatches;
fmgr_info(get_opcode(operator), &eqproc);
- fmgr_info_collation(DEFAULT_COLLATION_OID, &eqproc);
+ fmgr_info_set_collation(DEFAULT_COLLATION_OID, &eqproc);
hasmatch1 = (bool *) palloc0(nvalues1 * sizeof(bool));
hasmatch2 = (bool *) palloc0(nvalues2 * sizeof(bool));
@@ -2341,7 +2342,7 @@ eqjoinsel_semi(Oid operator,
nmatches;
fmgr_info(get_opcode(operator), &eqproc);
- fmgr_info_collation(DEFAULT_COLLATION_OID, &eqproc);
+ fmgr_info_set_collation(DEFAULT_COLLATION_OID, &eqproc);
hasmatch1 = (bool *) palloc0(nvalues1 * sizeof(bool));
hasmatch2 = (bool *) palloc0(nvalues2 * sizeof(bool));
@@ -4484,7 +4485,7 @@ get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop,
FmgrInfo opproc;
fmgr_info(get_opcode(sortop), &opproc);
- fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc);
+ fmgr_info_set_collation(DEFAULT_COLLATION_OID, &opproc);
for (i = 0; i < nvalues; i++)
{
@@ -5111,7 +5112,7 @@ prefix_selectivity(PlannerInfo *root, VariableStatData *vardata,
if (cmpopr == InvalidOid)
elog(ERROR, "no >= operator for opfamily %u", opfamily);
fmgr_info(get_opcode(cmpopr), &opproc);
- fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc);
+ fmgr_info_set_collation(DEFAULT_COLLATION_OID, &opproc);
prefixsel = ineq_histogram_selectivity(root, vardata, &opproc, true,
prefixcon->constvalue,
@@ -5133,7 +5134,7 @@ prefix_selectivity(PlannerInfo *root, VariableStatData *vardata,
if (cmpopr == InvalidOid)
elog(ERROR, "no < operator for opfamily %u", opfamily);
fmgr_info(get_opcode(cmpopr), &opproc);
- fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc);
+ fmgr_info_set_collation(DEFAULT_COLLATION_OID, &opproc);
greaterstrcon = make_greater_string(prefixcon, &opproc);
if (greaterstrcon)
diff --git a/src/backend/utils/fmgr/README b/src/backend/utils/fmgr/README
index 72695fe197d..6647fe95f72 100644
--- a/src/backend/utils/fmgr/README
+++ b/src/backend/utils/fmgr/README
@@ -71,6 +71,7 @@ typedef struct
bool fn_strict; /* function is "strict" (NULL in => NULL out) */
bool fn_retset; /* function returns a set (over multiple calls) */
unsigned char fn_stats; /* collect stats if track_functions > this */
+ Oid fn_collation; /* collation that function should use */
void *fn_extra; /* extra space for use by handler */
MemoryContext fn_mcxt; /* memory context to store fn_extra in */
Node *fn_expr; /* expression parse tree for call, or NULL */
@@ -89,12 +90,16 @@ is the number of arguments expected by the function, fn_strict is its
strictness flag, and fn_retset shows whether it returns a set; all of
these values come from the function's pg_proc entry. fn_stats is also
set up to control whether or not to track runtime statistics for calling
-this function. If the function is being called as part of a SQL expression,
+this function.
+
+fn_collation supplies the collation to use for collation-sensitive
+functions. If the function is being called as part of a SQL expression,
fn_expr will point to the expression parse tree for the function call; this
can be used to extract parse-time knowledge about the actual arguments.
-
-FmgrInfo already exists in the current code, but has fewer fields. This
-change should be transparent at the source-code level.
+Note that these two fields really are information about the arguments
+rather than information about the function, but it's proven to be more
+convenient to keep them in FmgrInfo than in FunctionCallInfoData where
+they might more logically go.
During a call of a function, the following data structure is created
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index a115a29da4c..e193e560eab 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -192,7 +192,7 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,
* elogs.
*/
finfo->fn_oid = InvalidOid;
- finfo->fn_collation = InvalidOid;
+ finfo->fn_collation = InvalidOid; /* caller may set this later */
finfo->fn_extra = NULL;
finfo->fn_mcxt = mcxt;
finfo->fn_expr = NULL; /* caller may set this later */
@@ -421,25 +421,6 @@ fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
}
/*
- * Initialize the fn_collation field
- */
-void
-fmgr_info_collation(Oid collationId, FmgrInfo *finfo)
-{
- finfo->fn_collation = collationId;
-}
-
-/*
- * Initialize the fn_expr field and set the collation based on it
- */
-void
-fmgr_info_expr(Node *expr, FmgrInfo *finfo)
-{
- finfo->fn_expr = expr;
- finfo->fn_collation = exprCollation(expr);
-}
-
-/*
* Fetch and validate the information record for the given external function.
* The function is specified by a handle for the containing library
* (obtained from load_external_function) as well as the function name.
@@ -920,6 +901,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
fmgr_info_cxt_security(fcinfo->flinfo->fn_oid, &fcache->flinfo,
fcinfo->flinfo->fn_mcxt, true);
+ fcache->flinfo.fn_collation = fcinfo->flinfo->fn_collation;
fcache->flinfo.fn_expr = fcinfo->flinfo->fn_expr;
tuple = SearchSysCache1(PROCOID,
@@ -1293,6 +1275,11 @@ DirectFunctionCall9(PGFunction func, Datum arg1, Datum arg2,
return result;
}
+
+/*
+ * These are the same as DirectFunctionCallN except that a nonzero
+ * collation can be specified. No other fields of FmgrInfo are made valid.
+ */
Datum
DirectFunctionCall1WithCollation(PGFunction func, Oid collation, Datum arg1)
{
@@ -1300,8 +1287,9 @@ DirectFunctionCall1WithCollation(PGFunction func, Oid collation, Datum arg1)
FmgrInfo flinfo;
Datum result;
+ MemSet(&flinfo, 0, sizeof(flinfo));
+ flinfo.fn_collation = collation;
InitFunctionCallInfoData(fcinfo, &flinfo, 1, NULL, NULL);
- fcinfo.flinfo->fn_collation = collation;
fcinfo.arg[0] = arg1;
fcinfo.argnull[0] = false;
@@ -1316,14 +1304,16 @@ DirectFunctionCall1WithCollation(PGFunction func, Oid collation, Datum arg1)
}
Datum
-DirectFunctionCall2WithCollation(PGFunction func, Oid collation, Datum arg1, Datum arg2)
+DirectFunctionCall2WithCollation(PGFunction func, Oid collation,
+ Datum arg1, Datum arg2)
{
FunctionCallInfoData fcinfo;
FmgrInfo flinfo;
Datum result;
+ MemSet(&flinfo, 0, sizeof(flinfo));
+ flinfo.fn_collation = collation;
InitFunctionCallInfoData(fcinfo, &flinfo, 2, NULL, NULL);
- fcinfo.flinfo->fn_collation = collation;
fcinfo.arg[0] = arg1;
fcinfo.arg[1] = arg2;
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 321b4e7f8ff..cad4a371b7d 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -411,6 +411,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyenum = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
+ Oid anycollation;
int i;
/* See if there are any polymorphic outputs; quick out if not */
@@ -468,6 +469,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
/* If nothing found, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
return false;
+
/* If needed, deduce one polymorphic type from the other */
if (have_anyelement_result && !OidIsValid(anyelement_type))
anyelement_type = resolve_generic_type(ANYELEMENTOID,
@@ -486,6 +488,24 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (have_anyenum && !type_is_enum(anyelement_type))
return false;
+ /*
+ * Identify the collation to use for polymorphic OUT parameters.
+ * (It'll necessarily be the same for both anyelement and anyarray.)
+ */
+ anycollation = get_typcollation(OidIsValid(anyelement_type) ? anyelement_type : anyarray_type);
+ if (OidIsValid(anycollation))
+ {
+ /*
+ * The types are collatable, so consider whether to use a nondefault
+ * collation. We do so if we can identify the input collation used
+ * for the function.
+ */
+ Oid inputcollation = exprInputCollation(call_expr);
+
+ if (OidIsValid(inputcollation))
+ anycollation = inputcollation;
+ }
+
/* And finally replace the tuple column types as needed */
for (i = 0; i < natts; i++)
{
@@ -499,6 +519,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
anyelement_type,
-1,
0);
+ TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
break;
case ANYARRAYOID:
TupleDescInitEntry(tupdesc, i + 1,
@@ -506,13 +527,11 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
anyarray_type,
-1,
0);
+ TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
break;
default:
break;
}
- /* Set collation based on actual argument types */
- TupleDescInitEntryCollation(tupdesc, i + 1,
- exprCollation(call_expr));
}
return true;
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index f2449ea6b13..56185fcabc7 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -836,7 +836,7 @@ tuplesort_begin_datum(Oid datumType,
elog(ERROR, "operator %u is not a valid ordering operator",
sortOperator);
fmgr_info(sortFunction, &state->sortOpFn);
- fmgr_info_collation(sortCollation, &state->sortOpFn);
+ fmgr_info_set_collation(sortCollation, &state->sortOpFn);
/* set ordering flags */
state->sortFnFlags = reverse ? SK_BT_DESC : 0;
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 657015616cd..c10de537bec 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201103112
+#define CATALOG_VERSION_NO 201103191
#endif
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 7539acace82..9e5224d374d 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -40,6 +40,11 @@ typedef Datum (*PGFunction) (FunctionCallInfo fcinfo);
* before a function can be called through fmgr. If the same function is
* to be called multiple times, the lookup need be done only once and the
* info struct saved for re-use.
+ *
+ * Note that fn_collation and fn_expr really are parse-time-determined
+ * information about the arguments, rather than about the function itself.
+ * But it's convenient to store them here rather than in FunctionCallInfoData,
+ * where they might more logically belong.
*/
typedef struct FmgrInfo
{
@@ -50,7 +55,7 @@ typedef struct FmgrInfo
bool fn_strict; /* function is "strict" (NULL in => NULL out) */
bool fn_retset; /* function returns a set */
unsigned char fn_stats; /* collect stats if track_functions > this */
- Oid fn_collation; /* collation to use */
+ Oid fn_collation; /* collation that function should use */
void *fn_extra; /* extra space for use by handler */
MemoryContext fn_mcxt; /* memory context to store fn_extra in */
fmNodePtr fn_expr; /* expression parse tree for call, or NULL */
@@ -84,15 +89,11 @@ extern void fmgr_info(Oid functionId, FmgrInfo *finfo);
extern void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo,
MemoryContext mcxt);
-/*
- * Initialize the fn_collation field
- */
-extern void fmgr_info_collation(Oid collationId, FmgrInfo *finfo);
-
-/*
- * Initialize the fn_expr field and set the collation based on it
- */
-extern void fmgr_info_expr(fmNodePtr expr, FmgrInfo *finfo);
+/* Macros for setting the fn_collation and fn_expr fields */
+#define fmgr_info_set_collation(collationId, finfo) \
+ ((finfo)->fn_collation = (collationId))
+#define fmgr_info_set_expr(expr, finfo) \
+ ((finfo)->fn_expr = (expr))
/*
* Copy an FmgrInfo struct
@@ -147,6 +148,12 @@ extern void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo,
#define PG_FUNCTION_ARGS FunctionCallInfo fcinfo
/*
+ * Get collation function should use.
+ */
+#define PG_GET_COLLATION() \
+ (fcinfo->flinfo ? fcinfo->flinfo->fn_collation : InvalidOid)
+
+/*
* Get number of arguments passed to function.
*/
#define PG_NARGS() (fcinfo->nargs)
@@ -307,7 +314,6 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena * datum);
#define PG_RETURN_VARCHAR_P(x) PG_RETURN_POINTER(x)
#define PG_RETURN_HEAPTUPLEHEADER(x) PG_RETURN_POINTER(x)
-#define PG_GET_COLLATION() (fcinfo->flinfo ? fcinfo->flinfo->fn_collation : InvalidOid)
/*-------------------------------------------------------------------------
* Support for detecting call convention of dynamically-loaded functions
@@ -450,7 +456,7 @@ extern Datum DirectFunctionCall9(PGFunction func, Datum arg1, Datum arg2,
Datum arg6, Datum arg7, Datum arg8,
Datum arg9);
-/* the same but passing a collation */
+/* The same, but passing a collation to use */
extern Datum DirectFunctionCall1WithCollation(PGFunction func, Oid collation,
Datum arg1);
extern Datum DirectFunctionCall2WithCollation(PGFunction func, Oid collation,
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index 6691b0dc77e..ead7c403cce 100644
--- a/src/include/nodes/makefuncs.h
+++ b/src/include/nodes/makefuncs.h
@@ -62,7 +62,7 @@ extern Expr *makeBoolExpr(BoolExprType boolop, List *args, int location);
extern Alias *makeAlias(const char *aliasname, List *colnames);
extern RelabelType *makeRelabelType(Expr *arg, Oid rtype, int32 rtypmod,
- CoercionForm rformat);
+ Oid rcollid, CoercionForm rformat);
extern RangeVar *makeRangeVar(char *schemaname, char *relname, int location);
@@ -70,8 +70,8 @@ extern TypeName *makeTypeName(char *typnam);
extern TypeName *makeTypeNameFromNameList(List *names);
extern TypeName *makeTypeNameFromOid(Oid typeOid, int32 typmod);
-extern FuncExpr *makeFuncExpr(Oid funcid, Oid rettype,
- List *args, Oid collid, CoercionForm fformat);
+extern FuncExpr *makeFuncExpr(Oid funcid, Oid rettype, List *args,
+ Oid funccollid, Oid inputcollid, CoercionForm fformat);
extern DefElem *makeDefElem(char *name, Node *arg);
extern DefElem *makeDefElemExtended(char *nameSpace, char *name, Node *arg,
diff --git a/src/include/nodes/nodeFuncs.h b/src/include/nodes/nodeFuncs.h
index 32f09f68ad1..591f2a9ca03 100644
--- a/src/include/nodes/nodeFuncs.h
+++ b/src/include/nodes/nodeFuncs.h
@@ -21,17 +21,21 @@
#define QTW_IGNORE_CTE_SUBQUERIES 0x02 /* subqueries in cteList */
#define QTW_IGNORE_RC_SUBQUERIES 0x03 /* both of above */
#define QTW_IGNORE_JOINALIASES 0x04 /* JOIN alias var lists */
-#define QTW_EXAMINE_RTES 0x08 /* examine RTEs */
-#define QTW_DONT_COPY_QUERY 0x10 /* do not copy top Query */
+#define QTW_IGNORE_RANGE_TABLE 0x08 /* skip rangetable entirely */
+#define QTW_EXAMINE_RTES 0x10 /* examine RTEs */
+#define QTW_DONT_COPY_QUERY 0x20 /* do not copy top Query */
extern Oid exprType(Node *expr);
extern int32 exprTypmod(Node *expr);
-extern Oid exprCollation(Node *expr);
-extern Oid coercion_expression_result_collation(Oid resulttype, Node *arg);
extern bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod);
extern bool expression_returns_set(Node *clause);
+extern Oid exprCollation(Node *expr);
+extern Oid exprInputCollation(Node *expr);
+extern void exprSetCollation(Node *expr, Oid collation);
+extern void exprSetInputCollation(Node *expr, Oid inputcollation);
+
extern int exprLocation(Node *expr);
extern bool expression_tree_walker(Node *node, bool (*walker) (),
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 8ed819b4dd4..d8bc6b82143 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -137,6 +137,7 @@ typedef enum NodeTag
T_NamedArgExpr,
T_OpExpr,
T_DistinctExpr,
+ T_NullIfExpr,
T_ScalarArrayOpExpr,
T_BoolExpr,
T_SubLink,
@@ -158,7 +159,6 @@ typedef enum NodeTag
T_CoalesceExpr,
T_MinMaxExpr,
T_XmlExpr,
- T_NullIfExpr,
T_NullTest,
T_BooleanTest,
T_CoerceToDomain,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 904bd5e9e18..b7e7104f44d 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -787,7 +787,12 @@ typedef struct RangeTblEntry
*
* In an ORDER BY item, all fields must be valid. (The eqop isn't essential
* here, but it's cheap to get it along with the sortop, and requiring it
- * to be valid eases comparisons to grouping items.)
+ * to be valid eases comparisons to grouping items.) Note that this isn't
+ * actually enough information to determine an ordering: if the sortop is
+ * collation-sensitive, a collation OID is needed too. We don't store the
+ * collation in SortGroupClause because it's not available at the time the
+ * parser builds the SortGroupClause; instead, consult the exposed collation
+ * of the referenced targetlist expression to find out what it is.
*
* In a grouping item, eqop must be valid. If the eqop is a btree equality
* operator, then sortop should be set to a compatible ordering operator.
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 41fd56e1bf1..609f253c077 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -139,7 +139,7 @@ typedef struct Var
* all */
Oid vartype; /* pg_type OID for the type of this var */
int32 vartypmod; /* pg_attribute typmod value */
- Oid varcollid; /* collation */
+ Oid varcollid; /* OID of collation, or InvalidOid if none */
Index varlevelsup; /* for subquery variables referencing outer
* relations; 0 in a normal var, >0 means N
* levels up */
@@ -156,7 +156,7 @@ typedef struct Const
Expr xpr;
Oid consttype; /* pg_type OID of the constant's datatype */
int32 consttypmod; /* typmod value, if any */
- Oid constcollid; /* collation */
+ Oid constcollid; /* OID of collation, or InvalidOid if none */
int constlen; /* typlen of the constant's datatype */
Datum constvalue; /* the constant's value */
bool constisnull; /* whether the constant is null (if true,
@@ -207,7 +207,7 @@ typedef struct Param
int paramid; /* numeric ID for parameter */
Oid paramtype; /* pg_type OID of parameter's datatype */
int32 paramtypmod; /* typmod value, if known */
- Oid paramcollation; /* parameter's collation */
+ Oid paramcollid; /* OID of collation, or InvalidOid if none */
int location; /* token location, or -1 if unknown */
} Param;
@@ -231,12 +231,13 @@ typedef struct Aggref
Expr xpr;
Oid aggfnoid; /* pg_proc Oid of the aggregate */
Oid aggtype; /* type Oid of result of the aggregate */
+ Oid aggcollid; /* OID of collation of result */
+ Oid inputcollid; /* OID of collation that function should use */
List *args; /* arguments and sort expressions */
List *aggorder; /* ORDER BY (list of SortGroupClause) */
List *aggdistinct; /* DISTINCT (list of SortGroupClause) */
bool aggstar; /* TRUE if argument list was really '*' */
Index agglevelsup; /* > 0 if agg belongs to outer query */
- Oid collid; /* collation OID to use by function */
int location; /* token location, or -1 if unknown */
} Aggref;
@@ -248,11 +249,12 @@ typedef struct WindowFunc
Expr xpr;
Oid winfnoid; /* pg_proc Oid of the function */
Oid wintype; /* type Oid of result of the window function */
+ Oid wincollid; /* OID of collation of result */
+ Oid inputcollid; /* OID of collation that function should use */
List *args; /* arguments to the window function */
Index winref; /* index of associated WindowClause */
bool winstar; /* TRUE if argument list was really '*' */
bool winagg; /* is function a simple aggregate? */
- Oid collid; /* collation OID to use by function */
int location; /* token location, or -1 if unknown */
} WindowFunc;
@@ -284,7 +286,7 @@ typedef struct ArrayRef
Oid refarraytype; /* type of the array proper */
Oid refelemtype; /* type of the array elements */
int32 reftypmod; /* typmod of the array (and elements too) */
- Oid refcollid; /* collation */
+ Oid refcollid; /* OID of collation, or InvalidOid if none */
List *refupperindexpr;/* expressions that evaluate to upper array
* indexes */
List *reflowerindexpr;/* expressions that evaluate to lower array
@@ -329,8 +331,9 @@ typedef struct FuncExpr
Oid funcresulttype; /* PG_TYPE OID of result value */
bool funcretset; /* true if function returns set */
CoercionForm funcformat; /* how to display this function call */
+ Oid funccollid; /* OID of collation of result */
+ Oid inputcollid; /* OID of collation that function should use */
List *args; /* arguments to the function */
- Oid collid; /* collation OID to use by function */
int location; /* token location, or -1 if unknown */
} FuncExpr;
@@ -373,8 +376,9 @@ typedef struct OpExpr
Oid opfuncid; /* PG_PROC OID of underlying function */
Oid opresulttype; /* PG_TYPE OID of result value */
bool opretset; /* true if operator returns set */
+ Oid opcollid; /* OID of collation of result */
+ Oid inputcollid; /* OID of collation that operator should use */
List *args; /* arguments to the operator (1 or 2) */
- Oid collid; /* collation OID to use by operator */
int location; /* token location, or -1 if unknown */
} OpExpr;
@@ -391,6 +395,14 @@ typedef struct OpExpr
typedef OpExpr DistinctExpr;
/*
+ * NullIfExpr - a NULLIF expression
+ *
+ * Like DistinctExpr, this is represented the same as an OpExpr referencing
+ * the "=" operator for x and y.
+ */
+typedef OpExpr NullIfExpr;
+
+/*
* ScalarArrayOpExpr - expression node for "scalar op ANY/ALL (array)"
*
* The operator must yield boolean. It is applied to the left operand
@@ -398,7 +410,7 @@ typedef OpExpr DistinctExpr;
* with OR or AND (for ANY or ALL respectively). The node representation
* is almost the same as for the underlying operator, but we need a useOr
* flag to remember whether it's ANY or ALL, and we don't have to store
- * the result type because it must be boolean.
+ * the result type (or the collation) because it must be boolean.
*/
typedef struct ScalarArrayOpExpr
{
@@ -406,8 +418,8 @@ typedef struct ScalarArrayOpExpr
Oid opno; /* PG_OPERATOR OID of the operator */
Oid opfuncid; /* PG_PROC OID of underlying function */
bool useOr; /* true for ANY, false for ALL */
+ Oid inputcollid; /* OID of collation that operator should use */
List *args; /* the scalar and array operands */
- Oid collid; /* collation OID to use by operator */
int location; /* token location, or -1 if unknown */
} ScalarArrayOpExpr;
@@ -602,7 +614,7 @@ typedef struct FieldSelect
Oid resulttype; /* type of the field (result type of this
* node) */
int32 resulttypmod; /* output typmod (usually -1) */
- Oid resultcollation;/* collation of the field */
+ Oid resultcollid; /* OID of collation of the field */
} FieldSelect;
/* ----------------
@@ -627,7 +639,7 @@ typedef struct FieldStore
List *newvals; /* new value(s) for field(s) */
List *fieldnums; /* integer list of field attnums */
Oid resulttype; /* type of result (same as type of arg) */
- /* Like RowExpr, we deliberately omit a typmod here */
+ /* Like RowExpr, we deliberately omit a typmod and collation here */
} FieldStore;
/* ----------------
@@ -649,6 +661,7 @@ typedef struct RelabelType
Expr *arg; /* input expression */
Oid resulttype; /* output type of coercion expression */
int32 resulttypmod; /* output typmod (usually -1) */
+ Oid resultcollid; /* OID of collation, or InvalidOid if none */
CoercionForm relabelformat; /* how to display this node */
int location; /* token location, or -1 if unknown */
} RelabelType;
@@ -668,6 +681,7 @@ typedef struct CoerceViaIO
Expr *arg; /* input expression */
Oid resulttype; /* output type of coercion */
/* output typmod is not stored, but is presumed -1 */
+ Oid resultcollid; /* OID of collation, or InvalidOid if none */
CoercionForm coerceformat; /* how to display this node */
int location; /* token location, or -1 if unknown */
} CoerceViaIO;
@@ -691,6 +705,7 @@ typedef struct ArrayCoerceExpr
Oid elemfuncid; /* OID of element coercion function, or 0 */
Oid resulttype; /* output type of coercion (an array type) */
int32 resulttypmod; /* output typmod (also element typmod) */
+ Oid resultcollid; /* OID of collation, or InvalidOid if none */
bool isExplicit; /* conversion semantics flag to pass to func */
CoercionForm coerceformat; /* how to display this node */
int location; /* token location, or -1 if unknown */
@@ -713,13 +728,16 @@ typedef struct ConvertRowtypeExpr
Expr xpr;
Expr *arg; /* input expression */
Oid resulttype; /* output type (always a composite type) */
- /* result typmod is not stored, but must be -1; see RowExpr comments */
+ /* Like RowExpr, we deliberately omit a typmod and collation here */
CoercionForm convertformat; /* how to display this node */
int location; /* token location, or -1 if unknown */
} ConvertRowtypeExpr;
/*----------
* CollateExpr - COLLATE
+ *
+ * The planner replaces CollateExpr with RelabelType during expression
+ * preprocessing, so execution never sees a CollateExpr.
*----------
*/
typedef struct CollateExpr
@@ -756,7 +774,7 @@ typedef struct CaseExpr
{
Expr xpr;
Oid casetype; /* type of expression result */
- Oid casecollation; /* collation of expression result */
+ Oid casecollid; /* OID of collation, or InvalidOid if none */
Expr *arg; /* implicit equality comparison argument */
List *args; /* the arguments (list of WHEN clauses) */
Expr *defresult; /* the default result (ELSE clause) */
@@ -802,6 +820,7 @@ typedef struct ArrayExpr
{
Expr xpr;
Oid array_typeid; /* type of expression result */
+ Oid array_collid; /* OID of collation, or InvalidOid if none */
Oid element_typeid; /* common type of array elements */
List *elements; /* the array elements or sub-arrays */
bool multidims; /* true if elements are sub-arrays */
@@ -838,6 +857,9 @@ typedef struct RowExpr
* associated with specific RECORD types at runtime, it will differ for
* different backends, and so cannot safely be stored in stored
* parsetrees. We must assume typmod -1 for a RowExpr node.
+ *
+ * We don't need to store a collation either. The result type is
+ * necessarily composite, and composite types never have a collation.
*/
CoercionForm row_format; /* how to display this node */
List *colnames; /* list of String, or NIL */
@@ -875,7 +897,7 @@ typedef struct RowCompareExpr
RowCompareType rctype; /* LT LE GE or GT, never EQ or NE */
List *opnos; /* OID list of pairwise comparison ops */
List *opfamilies; /* OID list of containing operator families */
- List *collids; /* OID list of collations for the comparisons */
+ List *inputcollids; /* OID list of collations for comparisons */
List *largs; /* the left-hand input arguments */
List *rargs; /* the right-hand input arguments */
} RowCompareExpr;
@@ -887,7 +909,7 @@ typedef struct CoalesceExpr
{
Expr xpr;
Oid coalescetype; /* type of expression result */
- Oid coalescecollation; /* collation of expression result */
+ Oid coalescecollid; /* OID of collation, or InvalidOid if none */
List *args; /* the arguments */
int location; /* token location, or -1 if unknown */
} CoalesceExpr;
@@ -905,9 +927,10 @@ typedef struct MinMaxExpr
{
Expr xpr;
Oid minmaxtype; /* common type of arguments and result */
+ Oid minmaxcollid; /* OID of collation of result */
+ Oid inputcollid; /* OID of collation that function should use */
MinMaxOp op; /* function to execute */
List *args; /* the arguments */
- Oid collid; /* collation to use */
int location; /* token location, or -1 if unknown */
} MinMaxExpr;
@@ -917,6 +940,10 @@ typedef struct MinMaxExpr
* 'name' carries the "NAME foo" argument (already XML-escaped).
* 'named_args' and 'arg_names' represent an xml_attribute list.
* 'args' carries all other arguments.
+ *
+ * Note: result type/typmod/collation are not stored, but can be deduced
+ * from the XmlExprOp. The type/typmod fields are just used for display
+ * purposes, and are NOT the true result type of the node.
*/
typedef enum XmlExprOp
{
@@ -945,19 +972,11 @@ typedef struct XmlExpr
List *arg_names; /* parallel list of Value strings */
List *args; /* list of expressions */
XmlOptionType xmloption; /* DOCUMENT or CONTENT */
- Oid type; /* target type for XMLSERIALIZE */
+ Oid type; /* target type/typmod for XMLSERIALIZE */
int32 typmod;
int location; /* token location, or -1 if unknown */
} XmlExpr;
-/*
- * NullIfExpr - a NULLIF expression
- *
- * Like DistinctExpr, this is represented the same as an OpExpr referencing
- * the "=" operator for x and y.
- */
-typedef OpExpr NullIfExpr;
-
/* ----------------
* NullTest
*
@@ -1018,6 +1037,7 @@ typedef struct CoerceToDomain
Expr *arg; /* input expression */
Oid resulttype; /* domain type ID (result type) */
int32 resulttypmod; /* output typmod (currently always -1) */
+ Oid resultcollid; /* OID of collation, or InvalidOid if none */
CoercionForm coercionformat; /* how to display this node */
int location; /* token location, or -1 if unknown */
} CoerceToDomain;
@@ -1036,6 +1056,7 @@ typedef struct CoerceToDomainValue
Expr xpr;
Oid typeId; /* type for substituted value */
int32 typeMod; /* typemod for substituted value */
+ Oid collation; /* collation for the substituted value */
int location; /* token location, or -1 if unknown */
} CoerceToDomainValue;
@@ -1051,7 +1072,7 @@ typedef struct SetToDefault
Expr xpr;
Oid typeId; /* type for substituted value */
int32 typeMod; /* typemod for substituted value */
- Oid collid; /* collation for the substituted value */
+ Oid collation; /* collation for the substituted value */
int location; /* token location, or -1 if unknown */
} SetToDefault;
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 8bcc4006a1a..7fee3f1464e 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -488,10 +488,13 @@ typedef struct IndexOptInfo
* require merging two existing EquivalenceClasses. At the end of the qual
* distribution process, we have sets of values that are known all transitively
* equal to each other, where "equal" is according to the rules of the btree
- * operator family(s) shown in ec_opfamilies. (We restrict an EC to contain
- * only equalities whose operators belong to the same set of opfamilies. This
- * could probably be relaxed, but for now it's not worth the trouble, since
- * nearly all equality operators belong to only one btree opclass anyway.)
+ * operator family(s) shown in ec_opfamilies, as well as the collation shown
+ * by ec_collation. (We restrict an EC to contain only equalities whose
+ * operators belong to the same set of opfamilies. This could probably be
+ * relaxed, but for now it's not worth the trouble, since nearly all equality
+ * operators belong to only one btree opclass anyway. Similarly, we suppose
+ * that all or none of the input datatypes are collatable, so that a single
+ * collation value is sufficient.)
*
* We also use EquivalenceClasses as the base structure for PathKeys, letting
* us represent knowledge about different sort orderings being equivalent.
@@ -520,6 +523,7 @@ typedef struct EquivalenceClass
NodeTag type;
List *ec_opfamilies; /* btree operator family OIDs */
+ Oid ec_collation; /* collation, if datatypes are collatable */
List *ec_members; /* list of EquivalenceMembers */
List *ec_sources; /* list of generating RestrictInfos */
List *ec_derives; /* list of derived RestrictInfos */
@@ -574,9 +578,10 @@ typedef struct EquivalenceMember
* represents the primary sort key, the second the first secondary sort key,
* etc. The value being sorted is represented by linking to an
* EquivalenceClass containing that value and including pk_opfamily among its
- * ec_opfamilies. This is a convenient method because it makes it trivial
- * to detect equivalent and closely-related orderings. (See optimizer/README
- * for more information.)
+ * ec_opfamilies. The EquivalenceClass tells which collation to use, too.
+ * This is a convenient method because it makes it trivial to detect
+ * equivalent and closely-related orderings. (See optimizer/README for more
+ * information.)
*
* Note: pk_strategy is either BTLessStrategyNumber (for ASC) or
* BTGreaterStrategyNumber (for DESC). We assume that all ordering-capable
@@ -589,7 +594,6 @@ typedef struct PathKey
EquivalenceClass *pk_eclass; /* the value that is ordered */
Oid pk_opfamily; /* btree opfamily defining the ordering */
- Oid pk_collation; /* collation */
int pk_strategy; /* sort direction (ASC or DESC) */
bool pk_nulls_first; /* do NULLs come before normal values? */
} PathKey;
@@ -1117,7 +1121,7 @@ typedef struct MergeScanSelCache
{
/* Ordering details (cache lookup key) */
Oid opfamily; /* btree opfamily defining the ordering */
- Oid collation;
+ Oid collation; /* collation for the ordering */
int strategy; /* sort direction (ASC or DESC) */
bool nulls_first; /* do NULLs come before normal values? */
/* Results */
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index 945957244c3..7ae236d167c 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -36,7 +36,8 @@ typedef struct
extern Expr *make_opclause(Oid opno, Oid opresulttype, bool opretset,
- Expr *leftop, Expr *rightop);
+ Expr *leftop, Expr *rightop,
+ Oid opcollid, Oid inputcollid);
extern Node *get_leftop(Expr *clause);
extern Node *get_rightop(Expr *clause);
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index ef769bf04df..06aed5f3178 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -111,11 +111,14 @@ extern bool have_join_order_restriction(PlannerInfo *root,
*/
extern bool process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
bool below_outer_join);
+extern Expr *canonicalize_ec_expression(Expr *expr,
+ Oid req_type, Oid req_collation);
extern void reconsider_outer_join_clauses(PlannerInfo *root);
extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
Expr *expr,
- Oid expr_datatype,
List *opfamilies,
+ Oid opcintype,
+ Oid collation,
Index sortref,
bool create_it);
extern void generate_base_implied_equalities(PlannerInfo *root);
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 7e03bc924ed..d48bf39e410 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -98,12 +98,14 @@ extern void distribute_restrictinfo_to_rels(PlannerInfo *root,
RestrictInfo *restrictinfo);
extern void process_implied_equality(PlannerInfo *root,
Oid opno,
+ Oid collation,
Expr *item1,
Expr *item2,
Relids qualscope,
bool below_outer_join,
bool both_const);
extern RestrictInfo *build_implied_join_equality(Oid opno,
+ Oid collation,
Expr *item1,
Expr *item2,
Relids qualscope);
diff --git a/src/include/parser/parse_agg.h b/src/include/parser/parse_agg.h
index 76d806dd40e..543d2e7857d 100644
--- a/src/include/parser/parse_agg.h
+++ b/src/include/parser/parse_agg.h
@@ -28,9 +28,9 @@ extern void build_aggregate_fnexprs(Oid *agg_input_types,
int agg_num_inputs,
Oid agg_state_type,
Oid agg_result_type,
+ Oid agg_input_collation,
Oid transfn_oid,
Oid finalfn_oid,
- Oid collation,
Expr **transfnexpr,
Expr **finalfnexpr);
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index 9f79ad89d41..ceaff2f9a92 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -87,6 +87,4 @@ extern CoercionPathType find_coercion_pathway(Oid targetTypeId,
extern CoercionPathType find_typmod_coercion_function(Oid typeId,
Oid *funcid);
-extern Oid select_common_collation(ParseState *pstate, List *exprs, bool none_ok);
-
#endif /* PARSE_COERCE_H */
diff --git a/src/include/parser/parse_collate.h b/src/include/parser/parse_collate.h
new file mode 100644
index 00000000000..20acb43504f
--- /dev/null
+++ b/src/include/parser/parse_collate.h
@@ -0,0 +1,27 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_collate.h
+ * Routines for assigning collation information.
+ *
+ *
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/parser/parse_collate.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PARSE_COLLATE_H
+#define PARSE_COLLATE_H
+
+#include "parser/parse_node.h"
+
+extern void assign_query_collations(ParseState *pstate, Query *query);
+
+extern void assign_list_collations(ParseState *pstate, List *exprs);
+
+extern void assign_expr_collations(ParseState *pstate, Node *expr);
+
+extern Oid select_common_collation(ParseState *pstate, List *exprs, bool none_ok);
+
+#endif /* PARSE_COLLATE_H */
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index a68f7cf5087..0ca79145585 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -146,7 +146,6 @@ extern ArrayRef *transformArraySubscripts(ParseState *pstate,
Oid arrayType,
Oid elementType,
int32 arrayTypMod,
- Oid arrayColl,
List *indirection,
Node *assignFrom);
extern Const *make_const(ParseState *pstate, Value *value, int location);
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 41188a2369f..a928e2f2f3d 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -1238,6 +1238,7 @@ make_datum_param(PLpgSQL_expr *expr, int dno, int location)
param->paramid = dno + 1;
param->paramtype = exec_get_datum_type(estate, estate->datums[dno]);
param->paramtypmod = -1;
+ param->paramcollid = get_typcollation(param->paramtype);
param->location = location;
return (Node *) param;
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 88cb8eb1045..1f4d5ac57ad 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -5303,6 +5303,18 @@ exec_simple_check_node(Node *node)
return TRUE;
}
+ case T_NullIfExpr:
+ {
+ NullIfExpr *expr = (NullIfExpr *) node;
+
+ if (expr->opretset)
+ return FALSE;
+ if (!exec_simple_check_node((Node *) expr->args))
+ return FALSE;
+
+ return TRUE;
+ }
+
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -5350,9 +5362,6 @@ 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;
@@ -5446,18 +5455,6 @@ exec_simple_check_node(Node *node)
return TRUE;
}
- case T_NullIfExpr:
- {
- NullIfExpr *expr = (NullIfExpr *) node;
-
- if (expr->opretset)
- return FALSE;
- if (!exec_simple_check_node((Node *) expr->args))
- return FALSE;
-
- return TRUE;
- }
-
case T_NullTest:
return exec_simple_check_node((Node *) ((NullTest *) node)->arg);
diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out
index 46a8207b7e0..f967998be63 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: ...ELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLL...
+LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
^
SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US";
ERROR: collation mismatch between explicit collations "C" and "en_US"
-LINE 1: ...ELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLL...
+LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
^
CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE.utf8";
CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE.utf8"; -- fails
@@ -586,10 +586,7 @@ SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2
(3 rows)
SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail
-ERROR: no collation was derived for the sort expression
-LINE 1: ...e_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2;
- ^
-HINT: Use the COLLATE clause to set the collation explicitly.
+ERROR: locale operation to be invoked, but no collation was derived
SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok
a | b
---+-----
@@ -607,7 +604,7 @@ SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2;
ERROR: collation mismatch between implicit collations "en_US.utf8" and "C"
LINE 1: SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collat...
^
-HINT: You can override the collation by applying the COLLATE clause to one or both expressions.
+HINT: You can choose the collation by applying the COLLATE clause to one or both expressions.
SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok
a | b
---+-----
@@ -621,15 +618,25 @@ SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY
ERROR: collation mismatch between implicit collations "en_US.utf8" and "C"
LINE 1: ...ELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM col...
^
-HINT: You can override the collation by applying the COLLATE clause to one or both expressions.
+HINT: You can choose the collation by applying the COLLATE clause to one or both expressions.
SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail
ERROR: collation mismatch between implicit collations "en_US.utf8" and "C"
LINE 1: SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM colla...
^
-HINT: You can override the collation by applying the COLLATE clause to one or both expressions.
+HINT: You can choose the collation by applying the COLLATE clause to one or both expressions.
CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail
ERROR: no collation was derived for column "b" with collatable type text
HINT: Use the COLLATE clause to set the collation explicitly.
+-- ideally this would be a parse-time error, but for now it must be run-time:
+select x < y from collate_test10; -- fail
+ERROR: locale operation to be invoked, but no collation was derived
+select x || y from collate_test10; -- ok, because || is not collation aware
+ ?column?
+----------
+ hijhij
+ HIJHIJ
+(2 rows)
+
-- collation mismatch between recursive and non-recursive term
WITH RECURSIVE foo(x) AS
(SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x)
diff --git a/src/test/regress/sql/collate.linux.utf8.sql b/src/test/regress/sql/collate.linux.utf8.sql
index 55af5091bc0..62e66811b64 100644
--- a/src/test/regress/sql/collate.linux.utf8.sql
+++ b/src/test/regress/sql/collate.linux.utf8.sql
@@ -190,6 +190,10 @@ SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2;
CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail
+-- ideally this would be a parse-time error, but for now it must be run-time:
+select x < y from collate_test10; -- fail
+select x || y from collate_test10; -- ok, because || is not collation aware
+
-- collation mismatch between recursive and non-recursive term
WITH RECURSIVE foo(x) AS
(SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x)