Revise collation derivation method and expression-tree representation.
authorTom Lane <[email protected]>
Sun, 20 Mar 2011 00:29:08 +0000 (20:29 -0400)
committerTom Lane <[email protected]>
Sun, 20 Mar 2011 00:30:08 +0000 (20:30 -0400)
All expression nodes now have an explicit output-collation field, unless
they are known to only return a noncollatable data type (such as boolean
or record).  Also, nodes that can invoke collation-aware functions store
a separate field that is the collation value to pass to the function.
This avoids confusion that arises when a function has collatable inputs
and noncollatable output type, or vice versa.

Also, replace the parser's on-the-fly collation assignment method with
a post-pass over the completed expression tree.  This allows us to use
a more complex (and hopefully more nearly spec-compliant) assignment
rule without paying for it in extra storage in every expression node.

Fix assorted bugs in the planner's handling of collations by making
collation one of the defining properties of an EquivalenceClass and
by converting CollateExprs into discardable RelabelType nodes during
expression preprocessing.

73 files changed:
src/backend/access/gin/ginutil.c
src/backend/access/index/indexam.c
src/backend/catalog/dependency.c
src/backend/catalog/heap.c
src/backend/commands/analyze.c
src/backend/commands/functioncmds.c
src/backend/commands/indexcmds.c
src/backend/commands/prepare.c
src/backend/commands/tablecmds.c
src/backend/commands/typecmds.c
src/backend/commands/view.c
src/backend/executor/execQual.c
src/backend/executor/functions.c
src/backend/executor/nodeAgg.c
src/backend/executor/nodeIndexscan.c
src/backend/executor/nodeMergejoin.c
src/backend/executor/nodeSubplan.c
src/backend/executor/nodeWindowAgg.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/makefuncs.c
src/backend/nodes/nodeFuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/path/costsize.c
src/backend/optimizer/path/equivclass.c
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/path/pathkeys.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/initsplan.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/util/clauses.c
src/backend/optimizer/util/predtest.c
src/backend/parser/Makefile
src/backend/parser/README
src/backend/parser/analyze.c
src/backend/parser/parse_agg.c
src/backend/parser/parse_clause.c
src/backend/parser/parse_coerce.c
src/backend/parser/parse_collate.c [new file with mode: 0644]
src/backend/parser/parse_cte.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_func.c
src/backend/parser/parse_node.c
src/backend/parser/parse_oper.c
src/backend/parser/parse_param.c
src/backend/parser/parse_target.c
src/backend/parser/parse_utilcmd.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/adt/selfuncs.c
src/backend/utils/fmgr/README
src/backend/utils/fmgr/fmgr.c
src/backend/utils/fmgr/funcapi.c
src/backend/utils/sort/tuplesort.c
src/include/catalog/catversion.h
src/include/fmgr.h
src/include/nodes/makefuncs.h
src/include/nodes/nodeFuncs.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/nodes/primnodes.h
src/include/nodes/relation.h
src/include/optimizer/clauses.h
src/include/optimizer/paths.h
src/include/optimizer/planmain.h
src/include/parser/parse_agg.h
src/include/parser/parse_coerce.h
src/include/parser/parse_collate.h [new file with mode: 0644]
src/include/parser/parse_node.h
src/pl/plpgsql/src/pl_comp.c
src/pl/plpgsql/src/pl_exec.c
src/test/regress/expected/collate.linux.utf8.out
src/test/regress/sql/collate.linux.utf8.sql

index 35f71c843e1a41b94435a405e6a43eb1f80909f6..23965449df2b34d45cd52edaec2c044a6fc0c4d6 100644 (file)
@@ -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),
index 803bb06398583de764232d59215ef340803c89f2..88f73e8241e5c8a521e9f9209c49f8041f9336d4 100644 (file)
@@ -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;
index 9e2028a64ff6d2d5c67e5f493ba8271966d70d48..de24ef7a094c66f4cdd1e1549ec6f5006ca5b39e 100644 (file)
@@ -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))
        {
index 50efa4782d640558204e17a381609448e0bd9e33..567eb7fd6ebb42acff259e71235c651f2440a3a6 100644 (file)
@@ -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;
 }
 
@@ -2414,6 +2420,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.
         */
index a9acc7c30367c96f74bceabf1c953455c20ae4c6..774bb044715123b7f7f120e2a91eedc3687d7975 100644 (file)
@@ -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++)
index a8ef947240e3d7ff178ee42d15bff7f7b6be9a77..c8cbe035f057e7103a1d0608b3a68c55f7f7fa66 100644 (file)
@@ -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.
index c8e21b68f58928967057bd39dd0d946dece8a990..163980bbfa342df1b5f267c98ede1ea54f32b6f8 100644 (file)
@@ -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;
 
                /*
index 6115e8678cdb59bc477a977494d08939f4faff10..adbf5872f38dc32f4bd8540b1b3fa0a89c65c9d2 100644 (file)
@@ -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++;
        }
index ee34cfa97e15030678b15ae2c034a9430c960939..f677a8e8bd08f65396f6e314a6cf8b906d339c4c 100644 (file)
@@ -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.
index ee3bca17d17a3419b2eb8fdf22fed98e22cfce02..4c06d898a88cce473d24bd5d1db92b07d8ee6d89 100644 (file)
@@ -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;
@@ -2352,6 +2354,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.
         */
index 794a56e84dee23c9fa3d6b794681e039cae14de9..250d02605cf4f46ff20d4fe9e59e6438dc4feda7 100644 (file)
@@ -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);
index 0faf52dfd793b1167624f3951bdc6d33330720ed..e410818900d5c12f4ea76761eb238285ed58ec42 100644 (file)
@@ -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);
        }
@@ -2753,20 +2753,6 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
        return HeapTupleGetDatum(result);
 }
 
-/* ----------------------------------------------------------------
- *             ExecEvalCollateExpr
- *
- *             Evaluate a CollateExpr node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCollateExpr(GenericExprState *exprstate,
-                                       ExprContext *econtext,
-                                       bool *isNull, ExprDoneCond *isDone)
-{
-       return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone);
-}
-
 /* ----------------------------------------------------------------
  *             ExecEvalCase
  *
@@ -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;
index a2f4fac74cbb244a276a0e68a5b7cc0635b54105..0421be57a41305ffc60718ad2890704c04e33bde 100644 (file)
@@ -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)
index d9bed220e4af31d05300c7de1509ea33005ab2a1..51b1228c26ff3f6c3310fccddb11013ea266d9c1 100644 (file)
@@ -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);
index 55fce94b3215809823f607ec8e0b1f4b53d19032..e60db7813a4d0f2642670c2045e13d9f926132a9 100644 (file)
@@ -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))
                {
index c0b9f2308556e634b73a4b61ba10894afa8279d3..75c3a645359131160f1b0b60e6d755ad3330f0db 100644 (file)
@@ -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)
index e9b3d76df1ca47fd7dd5b5d19a369950cee3d128..08a3017e6145e13e630c2bb88efd5f5c76b8d357 100644 (file)
@@ -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,
index 372262ad7f603c6492f897d9bf65602f2c205f73..5680efeb69ebe19e18882ecfc9e31c7bbb3366a1 100644 (file)
@@ -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,
index c0490e93ea50c045aff08da3ed6a16bcca5b545b..6e52d36a17ee9eb9427aca0041973593e4b31814 100644 (file)
@@ -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;
@@ -1603,24 +1633,6 @@ _copyXmlExpr(XmlExpr *from)
        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_NODE_FIELD(args);
-       COPY_LOCATION_FIELD(location);
-
-       return newnode;
-}
-
 /*
  * _copyNullTest
  */
@@ -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;
index 3726006f1d2fd5e0eaf77521777a749dd39bf363..7340aa05251252b118328b51aa41595f5b7f2edd 100644 (file)
@@ -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;
@@ -621,30 +657,6 @@ _equalXmlExpr(XmlExpr *a, XmlExpr *b)
        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_NODE_FIELD(args);
-       COMPARE_LOCATION_FIELD(location);
-
-       return true;
-}
-
 static bool
 _equalNullTest(NullTest *a, NullTest *b)
 {
@@ -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;
index d9f164523831512c332223dd378ef2603f7ed017..41e597cfffb93adcc1431a2ae4edf5c15dcda4e8 100644 (file)
@@ -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;
index 5394851a1f55bfcb744b0680da4883f16a8bdef1..d9e5d686c25b768ed9c0cfaf70a910ebd02e0fce 100644 (file)
@@ -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:
@@ -465,9 +469,167 @@ exprTypmod(Node *expr)
        return -1;
 }
 
+/*
+ * 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;
index d56e4dac011cd613eaa152df59282cf72ac1890d..db4a33c30d134844e9381ab1ace4510dfb2ac639 100644 (file)
@@ -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);
 }
 
@@ -1314,19 +1339,6 @@ _outXmlExpr(StringInfo str, XmlExpr *node)
        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_NODE_FIELD(args);
-       WRITE_LOCATION_FIELD(location);
-}
-
 static void
 _outNullTest(StringInfo str, NullTest *node)
 {
@@ -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;
index 6da61285b004570cd91ebf36c1799ded9bfcacff..5f1fd32b9f246265986abe8b48cc6fcfe696cbc7 100644 (file)
@@ -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();
@@ -963,35 +1004,6 @@ _readXmlExpr(void)
        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_NODE_FIELD(args);
-       READ_LOCATION_FIELD(location);
-
-       READ_DONE();
-}
-
 /*
  * _readNullTest
  */
@@ -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))
index 756874b817c3698cc37fde4947ea62c10ffb3b36..8f763b436956fa2641aa04ee62b924f9bac706a7 100644 (file)
@@ -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;
index 3d87a5b90374de9e26f21a6626cd7a062b4fa1c7..9a32e16940b19bd38de19e9c1ab0f690b4a683c5 100644 (file)
@@ -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,11 +120,23 @@ 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
@@ -188,6 +203,13 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
                if (cur_ec->ec_has_volatile)
                        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
@@ -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;
@@ -341,6 +364,84 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
        return true;
 }
 
+/*
+ * 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
  */
@@ -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)
 {
@@ -416,6 +518,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
        ListCell   *lc1;
        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
         */
@@ -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);
index 65bc9be8da805a20670769ece58e111a6ce13055..1ac0ff6ee87e09dc14f69964cca3df17ebd5bde7 100644 (file)
@@ -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;
index fd759281ed52649b7dd205ab2ef95fd132690802..de3e4ac74e774ebf6010235d3fcec3bf9e8b139c 100644 (file)
@@ -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"
 #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)
                {
                        /*
index 8a0135c9a74b4aa1d370287e82e3c359b12e5939..bdd14f524db920e941c0e14c4b247a30b0b689d7 100644 (file)
@@ -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);
index 845b4ae34b84e9e8717167e79056b4be122417c0..0e00df64335d0d25d0752aca840d686d9f3fc1d4 100644 (file)
@@ -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);
index a9649212f205a12558fd8f79f1aa1a5020379b17..f2b586d19cc99c241b0682e2e6d493de3c7124fe 100644 (file)
@@ -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);
index 8503792df448ccde6b03d728e61d28f6fb81c6a6..7b31b6b4fa6639025228989bcf7d23136a8627a5 100644 (file)
@@ -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);
index 4561e8e92f136e6527d06980f5dae39b55eaa334..6a1f7291ba5851252e5f531ac1b4edc9d35e1986 100644 (file)
@@ -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;
@@ -1420,6 +1422,14 @@ btree_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
        if (!equal(pred_var, clause_var))
                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);
index ee92a8ca463046cf8267c9f3825e79465792ccbd..665250b9e760b80ae4d7f7da9d6fd306fd32b783 100644 (file)
@@ -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
 
index 59cc32fef3ae59177a7a4814b8eafe3ea4fefc13..08625e427d2baa6682df0ca4af3a2e19a43b4f09 100644 (file)
@@ -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
index 85f231da9c546f38c250a5749dc17032d752a177..315f067b17a5cb2e56960425a7fed757eaf9559b 100644 (file)
@@ -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;
 }
 
index 8267627c42fd943e33930d631a375b67af648bb4..523d6e6989a2cea32ebc993665e692baf44d387a 100644 (file)
@@ -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);
 }
index 4c5a6fe0b0115a0f7fcb6baa88bcdcb90b2dea51..6c0a78474cd1c89d2914b135eadf2b5a5b1cbc21 100644 (file)
@@ -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"
@@ -557,6 +558,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
@@ -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 */
index 6aff34dd90d5b2e6124787e242bcc76a476aa318..9b59b032976333138bcbeb6f308c90a34501adcd 100644 (file)
@@ -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 (file)
index 0000000..0b77e3e
--- /dev/null
@@ -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;
+}
index 23b72b245b2f02ccb8b13071c516a8b69fbbdd81..c527f7589e20646d02f9fdaf97eada30d744a048 100644 (file)
@@ -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);
index 17bd2bf50aee9d5322659b94621d4762b6348120..4986e0e5fab551b76d85b4a77b9aaf67578eefce 100644 (file)
@@ -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;
 
index a2d6c598104cac0a8e1579984b5abe95a156f2db..a187287e2830c2db907cf531c2bcdb4cfaee47d8 100644 (file)
@@ -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))));