OSDN Git Service

Revise collation derivation method and expression-tree representation.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 20 Mar 2011 00:29:08 +0000 (20:29 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
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 35f71c8..2396544 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 803bb06..88f73e8 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 9e2028a..de24ef7 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 50efa47..567eb7f 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;
 }
 
@@ -2415,6 +2421,11 @@ cookConstraint(ParseState *pstate,
        expr = coerce_to_boolean(pstate, expr, "CHECK");
 
        /*
+        * Take care of collations.
+        */
+       assign_expr_collations(pstate, expr);
+
+       /*
         * Make sure no outside relations are referred to.
         */
        if (list_length(pstate->p_rtable) != 1)
index a9acc7c..774bb04 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 a8ef947..c8cbe03 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 c8e21b6..163980b 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 6115e86..adbf587 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 ee34cfa..f677a8e 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 ee3bca1..4c06d89 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;
@@ -2353,6 +2355,11 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
        expr = coerce_to_boolean(pstate, expr, "CHECK");
 
        /*
+        * Fix up collation information.
+        */
+       assign_expr_collations(pstate, expr);
+
+       /*
         * Make sure no outside relations are referred to.
         */
        if (list_length(pstate->p_rtable) != 0)
index 794a56e..250d026 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 0faf52d..e410818 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);
        }
@@ -2754,20 +2754,6 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
 }
 
 /* ----------------------------------------------------------------
- *             ExecEvalCollateExpr
- *
- *             Evaluate a CollateExpr node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCollateExpr(GenericExprState *exprstate,
-                                       ExprContext *econtext,
-                                       bool *isNull, ExprDoneCond *isDone)
-{
-       return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone);
-}
-
-/* ----------------------------------------------------------------
  *             ExecEvalCase
  *
  *             Evaluate a CASE clause. Will have boolean expressions
@@ -3542,7 +3528,7 @@ ExecEvalNullIf(FuncExprState *nullIfExpr,
        {
                NullIfExpr *op = (NullIfExpr *) nullIfExpr->xprstate.expr;
 
-               init_fcache(op->opfuncid, nullIfExpr,
+               init_fcache(op->opfuncid, op->inputcollid, nullIfExpr,
                                        econtext->ecxt_per_query_memory, true);
                Assert(!nullIfExpr->func.fn_retset);
        }
@@ -4129,9 +4115,8 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
                /* Set up the primary fmgr lookup information */
                fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc),
                                          econtext->ecxt_per_query_memory);
-
-               /* Initialize additional info */
-               fmgr_info_expr((Node *) acoerce, &(astate->elemfunc));
+               /* Note: coercion functions are assumed to not use collation */
+               fmgr_info_set_expr((Node *) acoerce, &(astate->elemfunc));
        }
 
        /*
@@ -4400,6 +4385,18 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                state = (ExprState *) fstate;
                        }
                        break;
+               case T_NullIfExpr:
+                       {
+                               NullIfExpr *nullifexpr = (NullIfExpr *) node;
+                               FuncExprState *fstate = makeNode(FuncExprState);
+
+                               fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullIf;
+                               fstate->args = (List *)
+                                       ExecInitExpr((Expr *) nullifexpr->args, parent);
+                               fstate->func.fn_oid = InvalidOid;               /* not initialized */
+                               state = (ExprState *) fstate;
+                       }
+                       break;
                case T_ScalarArrayOpExpr:
                        {
                                ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
@@ -4551,16 +4548,6 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                state = (ExprState *) cstate;
                        }
                        break;
-               case T_CollateExpr:
-                       {
-                               CollateExpr *collate = (CollateExpr *) node;
-                               GenericExprState *gstate = makeNode(GenericExprState);
-
-                               gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCollateExpr;
-                               gstate->arg = ExecInitExpr(collate->arg, parent);
-                               state = (ExprState *) gstate;
-                       }
-                       break;
                case T_CaseExpr:
                        {
                                CaseExpr   *caseexpr = (CaseExpr *) node;
@@ -4713,11 +4700,11 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                Assert(list_length(rcexpr->opfamilies) == nopers);
                                rstate->funcs = (FmgrInfo *) palloc(nopers * sizeof(FmgrInfo));
                                i = 0;
-                               forthree(l, rcexpr->opnos, l2, rcexpr->opfamilies, l3, rcexpr->collids)
+                               forthree(l, rcexpr->opnos, l2, rcexpr->opfamilies, l3, rcexpr->inputcollids)
                                {
                                        Oid                     opno = lfirst_oid(l);
                                        Oid                     opfamily = lfirst_oid(l2);
-                                       Oid                     collid = lfirst_oid(l3);
+                                       Oid                     inputcollid = lfirst_oid(l3);
                                        int                     strategy;
                                        Oid                     lefttype;
                                        Oid                     righttype;
@@ -4739,7 +4726,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                         * does this code.
                                         */
                                        fmgr_info(proc, &(rstate->funcs[i]));
-                                       fmgr_info_collation(collid, &(rstate->funcs[i]));
+                                       fmgr_info_set_collation(inputcollid, &(rstate->funcs[i]));
                                        i++;
                                }
                                state = (ExprState *) rstate;
@@ -4799,7 +4786,8 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                 * code.
                                 */
                                fmgr_info(typentry->cmp_proc, &(mstate->cfunc));
-                               fmgr_info_collation(minmaxexpr->collid, &(mstate->cfunc));
+                               fmgr_info_set_collation(minmaxexpr->inputcollid,
+                                                                               &(mstate->cfunc));
                                state = (ExprState *) mstate;
                        }
                        break;
@@ -4836,18 +4824,6 @@ ExecInitExpr(Expr *node, PlanState *parent)
                                state = (ExprState *) xstate;
                        }
                        break;
-               case T_NullIfExpr:
-                       {
-                               NullIfExpr *nullifexpr = (NullIfExpr *) node;
-                               FuncExprState *fstate = makeNode(FuncExprState);
-
-                               fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullIf;
-                               fstate->args = (List *)
-                                       ExecInitExpr((Expr *) nullifexpr->args, parent);
-                               fstate->func.fn_oid = InvalidOid;               /* not initialized */
-                               state = (ExprState *) fstate;
-                       }
-                       break;
                case T_NullTest:
                        {
                                NullTest   *ntest = (NullTest *) node;
index a2f4fac..0421be5 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 d9bed22..51b1228 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 55fce94..e60db78 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 c0b9f23..75c3a64 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 e9b3d76..08a3017 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 372262a..5680efe 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 c0490e9..6e52d36 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;
@@ -1604,24 +1634,6 @@ _copyXmlExpr(XmlExpr *from)
 }
 
 /*
- * _copyNullIfExpr (same as OpExpr)
- */
-static NullIfExpr *
-_copyNullIfExpr(NullIfExpr *from)
-{
-       NullIfExpr *newnode = makeNode(NullIfExpr);
-
-       COPY_SCALAR_FIELD(opno);
-       COPY_SCALAR_FIELD(opfuncid);
-       COPY_SCALAR_FIELD(opresulttype);
-       COPY_SCALAR_FIELD(opretset);
-       COPY_NODE_FIELD(args);
-       COPY_LOCATION_FIELD(location);
-
-       return newnode;
-}
-
-/*
  * _copyNullTest
  */
 static NullTest *
@@ -1661,6 +1673,7 @@ _copyCoerceToDomain(CoerceToDomain *from)
        COPY_NODE_FIELD(arg);
        COPY_SCALAR_FIELD(resulttype);
        COPY_SCALAR_FIELD(resulttypmod);
+       COPY_SCALAR_FIELD(resultcollid);
        COPY_SCALAR_FIELD(coercionformat);
        COPY_LOCATION_FIELD(location);
 
@@ -1677,6 +1690,7 @@ _copyCoerceToDomainValue(CoerceToDomainValue *from)
 
        COPY_SCALAR_FIELD(typeId);
        COPY_SCALAR_FIELD(typeMod);
+       COPY_SCALAR_FIELD(collation);
        COPY_LOCATION_FIELD(location);
 
        return newnode;
@@ -1692,7 +1706,7 @@ _copySetToDefault(SetToDefault *from)
 
        COPY_SCALAR_FIELD(typeId);
        COPY_SCALAR_FIELD(typeMod);
-       COPY_SCALAR_FIELD(collid);
+       COPY_SCALAR_FIELD(collation);
        COPY_LOCATION_FIELD(location);
 
        return newnode;
@@ -1798,7 +1812,6 @@ _copyPathKey(PathKey *from)
        /* EquivalenceClasses are never moved, so just shallow-copy the pointer */
        COPY_SCALAR_FIELD(pk_eclass);
        COPY_SCALAR_FIELD(pk_opfamily);
-       COPY_SCALAR_FIELD(pk_collation);
        COPY_SCALAR_FIELD(pk_strategy);
        COPY_SCALAR_FIELD(pk_nulls_first);
 
@@ -3998,6 +4011,9 @@ copyObject(void *from)
                case T_DistinctExpr:
                        retval = _copyDistinctExpr(from);
                        break;
+               case T_NullIfExpr:
+                       retval = _copyNullIfExpr(from);
+                       break;
                case T_ScalarArrayOpExpr:
                        retval = _copyScalarArrayOpExpr(from);
                        break;
@@ -4061,9 +4077,6 @@ copyObject(void *from)
                case T_XmlExpr:
                        retval = _copyXmlExpr(from);
                        break;
-               case T_NullIfExpr:
-                       retval = _copyNullIfExpr(from);
-                       break;
                case T_NullTest:
                        retval = _copyNullTest(from);
                        break;
index 3726006..7340aa0 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;
@@ -622,30 +658,6 @@ _equalXmlExpr(XmlExpr *a, XmlExpr *b)
 }
 
 static bool
-_equalNullIfExpr(NullIfExpr *a, NullIfExpr *b)
-{
-       COMPARE_SCALAR_FIELD(opno);
-
-       /*
-        * Special-case opfuncid: it is allowable for it to differ if one node
-        * contains zero and the other doesn't.  This just means that the one node
-        * isn't as far along in the parse/plan pipeline and hasn't had the
-        * opfuncid cache filled yet.
-        */
-       if (a->opfuncid != b->opfuncid &&
-               a->opfuncid != 0 &&
-               b->opfuncid != 0)
-               return false;
-
-       COMPARE_SCALAR_FIELD(opresulttype);
-       COMPARE_SCALAR_FIELD(opretset);
-       COMPARE_NODE_FIELD(args);
-       COMPARE_LOCATION_FIELD(location);
-
-       return true;
-}
-
-static bool
 _equalNullTest(NullTest *a, NullTest *b)
 {
        COMPARE_NODE_FIELD(arg);
@@ -670,6 +682,7 @@ _equalCoerceToDomain(CoerceToDomain *a, CoerceToDomain *b)
        COMPARE_NODE_FIELD(arg);
        COMPARE_SCALAR_FIELD(resulttype);
        COMPARE_SCALAR_FIELD(resulttypmod);
+       COMPARE_SCALAR_FIELD(resultcollid);
 
        /*
         * Special-case COERCE_DONTCARE, so that planner can build coercion nodes
@@ -690,6 +703,7 @@ _equalCoerceToDomainValue(CoerceToDomainValue *a, CoerceToDomainValue *b)
 {
        COMPARE_SCALAR_FIELD(typeId);
        COMPARE_SCALAR_FIELD(typeMod);
+       COMPARE_SCALAR_FIELD(collation);
        COMPARE_LOCATION_FIELD(location);
 
        return true;
@@ -700,7 +714,7 @@ _equalSetToDefault(SetToDefault *a, SetToDefault *b)
 {
        COMPARE_SCALAR_FIELD(typeId);
        COMPARE_SCALAR_FIELD(typeMod);
-       COMPARE_SCALAR_FIELD(collid);
+       COMPARE_SCALAR_FIELD(collation);
        COMPARE_LOCATION_FIELD(location);
 
        return true;
@@ -787,7 +801,6 @@ _equalPathKey(PathKey *a, PathKey *b)
        if (a_eclass != b_eclass)
                return false;
        COMPARE_SCALAR_FIELD(pk_opfamily);
-       COMPARE_SCALAR_FIELD(pk_collation);
        COMPARE_SCALAR_FIELD(pk_strategy);
        COMPARE_SCALAR_FIELD(pk_nulls_first);
 
@@ -2559,6 +2572,9 @@ equal(void *a, void *b)
                case T_DistinctExpr:
                        retval = _equalDistinctExpr(a, b);
                        break;
+               case T_NullIfExpr:
+                       retval = _equalNullIfExpr(a, b);
+                       break;
                case T_ScalarArrayOpExpr:
                        retval = _equalScalarArrayOpExpr(a, b);
                        break;
@@ -2622,9 +2638,6 @@ equal(void *a, void *b)
                case T_XmlExpr:
                        retval = _equalXmlExpr(a, b);
                        break;
-               case T_NullIfExpr:
-                       retval = _equalNullIfExpr(a, b);
-                       break;
                case T_NullTest:
                        retval = _equalNullTest(a, b);
                        break;
index d9f1645..41e597c 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 5394851..d9e5d68 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:
@@ -466,8 +470,166 @@ exprTypmod(Node *expr)
 }
 
 /*
+ * exprIsLengthCoercion
+ *             Detect whether an expression tree is an application of a datatype's
+ *             typmod-coercion function.  Optionally extract the result's typmod.
+ *
+ * If coercedTypmod is not NULL, the typmod is stored there if the expression
+ * is a length-coercion function, else -1 is stored there.
+ *
+ * Note that a combined type-and-length coercion will be treated as a
+ * length coercion by this routine.
+ */
+bool
+exprIsLengthCoercion(Node *expr, int32 *coercedTypmod)
+{
+       if (coercedTypmod != NULL)
+               *coercedTypmod = -1;    /* default result on failure */
+
+       /*
+        * Scalar-type length coercions are FuncExprs, array-type length coercions
+        * are ArrayCoerceExprs
+        */
+       if (expr && IsA(expr, FuncExpr))
+       {
+               FuncExpr   *func = (FuncExpr *) expr;
+               int                     nargs;
+               Const      *second_arg;
+
+               /*
+                * If it didn't come from a coercion context, reject.
+                */
+               if (func->funcformat != COERCE_EXPLICIT_CAST &&
+                       func->funcformat != COERCE_IMPLICIT_CAST)
+                       return false;
+
+               /*
+                * If it's not a two-argument or three-argument function with the
+                * second argument being an int4 constant, it can't have been created
+                * from a length coercion (it must be a type coercion, instead).
+                */
+               nargs = list_length(func->args);
+               if (nargs < 2 || nargs > 3)
+                       return false;
+
+               second_arg = (Const *) lsecond(func->args);
+               if (!IsA(second_arg, Const) ||
+                       second_arg->consttype != INT4OID ||
+                       second_arg->constisnull)
+                       return false;
+
+               /*
+                * OK, it is indeed a length-coercion function.
+                */
+               if (coercedTypmod != NULL)
+                       *coercedTypmod = DatumGetInt32(second_arg->constvalue);
+
+               return true;
+       }
+
+       if (expr && IsA(expr, ArrayCoerceExpr))
+       {
+               ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) expr;
+
+               /* It's not a length coercion unless there's a nondefault typmod */
+               if (acoerce->resulttypmod < 0)
+                       return false;
+
+               /*
+                * OK, it is indeed a length-coercion expression.
+                */
+               if (coercedTypmod != NULL)
+                       *coercedTypmod = acoerce->resulttypmod;
+
+               return true;
+       }
+
+       return false;
+}
+
+/*
+ * expression_returns_set
+ *       Test whether an expression returns a set result.
+ *
+ * Because we use expression_tree_walker(), this can also be applied to
+ * whole targetlists; it'll produce TRUE if any one of the tlist items
+ * returns a set.
+ */
+bool
+expression_returns_set(Node *clause)
+{
+       return expression_returns_set_walker(clause, NULL);
+}
+
+static bool
+expression_returns_set_walker(Node *node, void *context)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, FuncExpr))
+       {
+               FuncExpr   *expr = (FuncExpr *) node;
+
+               if (expr->funcretset)
+                       return true;
+               /* else fall through to check args */
+       }
+       if (IsA(node, OpExpr))
+       {
+               OpExpr     *expr = (OpExpr *) node;
+
+               if (expr->opretset)
+                       return true;
+               /* else fall through to check args */
+       }
+
+       /* Avoid recursion for some cases that can't return a set */
+       if (IsA(node, Aggref))
+               return false;
+       if (IsA(node, WindowFunc))
+               return false;
+       if (IsA(node, DistinctExpr))
+               return false;
+       if (IsA(node, NullIfExpr))
+               return false;
+       if (IsA(node, ScalarArrayOpExpr))
+               return false;
+       if (IsA(node, BoolExpr))
+               return false;
+       if (IsA(node, SubLink))
+               return false;
+       if (IsA(node, SubPlan))
+               return false;
+       if (IsA(node, AlternativeSubPlan))
+               return false;
+       if (IsA(node, ArrayExpr))
+               return false;
+       if (IsA(node, RowExpr))
+               return false;
+       if (IsA(node, RowCompareExpr))
+               return false;
+       if (IsA(node, CoalesceExpr))
+               return false;
+       if (IsA(node, MinMaxExpr))
+               return false;
+       if (IsA(node, XmlExpr))
+               return false;
+
+       return expression_tree_walker(node, expression_returns_set_walker,
+                                                                 context);
+}
+
+
+/*
  *     exprCollation -
  *       returns the Oid of the collation of the expression's result.
+ *
+ * Note: expression nodes that can invoke functions generally have an
+ * "inputcollid" field, which is what the function should use as collation.
+ * That is the resolved common collation of the node's inputs.  It is often
+ * but not always the same as the result collation; in particular, if the
+ * function produces a non-collatable result type from collatable inputs
+ * or vice versa, the two are different.
  */
 Oid
 exprCollation(Node *expr)
@@ -486,34 +648,37 @@ exprCollation(Node *expr)
                        coll = ((Const *) expr)->constcollid;
                        break;
                case T_Param:
-                       coll = ((Param *) expr)->paramcollation;
+                       coll = ((Param *) expr)->paramcollid;
                        break;
                case T_Aggref:
-                       coll = ((Aggref *) expr)->collid;
+                       coll = ((Aggref *) expr)->aggcollid;
                        break;
                case T_WindowFunc:
-                       coll = ((WindowFunc *) expr)->collid;
+                       coll = ((WindowFunc *) expr)->wincollid;
                        break;
                case T_ArrayRef:
                        coll = ((ArrayRef *) expr)->refcollid;
                        break;
                case T_FuncExpr:
-                       coll = ((FuncExpr *) expr)->collid;
+                       coll = ((FuncExpr *) expr)->funccollid;
                        break;
                case T_NamedArgExpr:
                        coll = exprCollation((Node *) ((NamedArgExpr *) expr)->arg);
                        break;
                case T_OpExpr:
-                       coll = ((OpExpr *) expr)->collid;
+                       coll = ((OpExpr *) expr)->opcollid;
                        break;
                case T_DistinctExpr:
-                       coll = ((DistinctExpr *) expr)->collid;
+                       coll = ((DistinctExpr *) expr)->opcollid;
+                       break;
+               case T_NullIfExpr:
+                       coll = ((NullIfExpr *) expr)->opcollid;
                        break;
                case T_ScalarArrayOpExpr:
-                       coll = ((ScalarArrayOpExpr *) expr)->collid;
+                       coll = InvalidOid;                              /* result is always boolean */
                        break;
                case T_BoolExpr:
-                       coll = InvalidOid; /* not applicable */
+                       coll = InvalidOid;                              /* result is always boolean */
                        break;
                case T_SubLink:
                        {
@@ -522,7 +687,7 @@ exprCollation(Node *expr)
                                if (sublink->subLinkType == EXPR_SUBLINK ||
                                        sublink->subLinkType == ARRAY_SUBLINK)
                                {
-                                       /* get the collation of the subselect's first target column */
+                                       /* get the collation of subselect's first target column */
                                        Query      *qtree = (Query *) sublink->subselect;
                                        TargetEntry *tent;
 
@@ -532,10 +697,13 @@ exprCollation(Node *expr)
                                        Assert(IsA(tent, TargetEntry));
                                        Assert(!tent->resjunk);
                                        coll = exprCollation((Node *) tent->expr);
-                                       /* note we don't need to care if it's an array */
+                                       /* collation doesn't change if it's converted to array */
                                }
                                else
+                               {
+                                       /* for all other sublink types, result is boolean */
                                        coll = InvalidOid;
+                               }
                        }
                        break;
                case T_SubPlan:
@@ -545,9 +713,9 @@ exprCollation(Node *expr)
                                if (subplan->subLinkType == EXPR_SUBLINK ||
                                        subplan->subLinkType == ARRAY_SUBLINK)
                                {
-                                       /* get the collation of the subselect's first target column */
-                                       /* note we don't need to care if it's an array */
+                                       /* get the collation of subselect's first target column */
                                        coll = subplan->firstColCollation;
+                                       /* collation doesn't change if it's converted to array */
                                }
                                else
                                {
@@ -565,84 +733,75 @@ exprCollation(Node *expr)
                        }
                        break;
                case T_FieldSelect:
-                       coll = ((FieldSelect *) expr)->resultcollation;
+                       coll = ((FieldSelect *) expr)->resultcollid;
                        break;
                case T_FieldStore:
-                       coll = InvalidOid; /* not applicable */
+                       coll = InvalidOid;                              /* result is always composite */
                        break;
                case T_RelabelType:
-                       coll = exprCollation((Node *) ((RelabelType *) expr)->arg);
+                       coll = ((RelabelType *) expr)->resultcollid;
                        break;
                case T_CoerceViaIO:
-               {
-                       CoerceViaIO *cvio = (CoerceViaIO *) expr;
-                       coll = coercion_expression_result_collation(cvio->resulttype, (Node *) cvio->arg);
+                       coll = ((CoerceViaIO *) expr)->resultcollid;
                        break;
-               }
                case T_ArrayCoerceExpr:
-               {
-                       ArrayCoerceExpr *ace = (ArrayCoerceExpr *) expr;
-                       coll = coercion_expression_result_collation(ace->resulttype, (Node *) ace->arg);
+                       coll = ((ArrayCoerceExpr *) expr)->resultcollid;
                        break;
-               }
                case T_ConvertRowtypeExpr:
-               {
-                       ConvertRowtypeExpr *cre = (ConvertRowtypeExpr *) expr;
-                       coll = coercion_expression_result_collation(cre->resulttype, (Node *) cre->arg);
+                       coll = InvalidOid;                              /* result is always composite */
                        break;
-               }
                case T_CollateExpr:
                        coll = ((CollateExpr *) expr)->collOid;
                        break;
                case T_CaseExpr:
-                       coll = ((CaseExpr *) expr)->casecollation;
+                       coll = ((CaseExpr *) expr)->casecollid;
                        break;
                case T_CaseTestExpr:
                        coll = ((CaseTestExpr *) expr)->collation;
                        break;
                case T_ArrayExpr:
-                       coll = get_typcollation(((ArrayExpr *) expr)->array_typeid);
+                       coll = ((ArrayExpr *) expr)->array_collid;
                        break;
                case T_RowExpr:
-                       coll = InvalidOid; /* not applicable */
+                       coll = InvalidOid;                              /* result is always composite */
                        break;
                case T_RowCompareExpr:
-                       coll = InvalidOid; /* not applicable */
+                       coll = InvalidOid;                              /* result is always boolean */
                        break;
                case T_CoalesceExpr:
-                       coll = ((CoalesceExpr *) expr)->coalescecollation;
+                       coll = ((CoalesceExpr *) expr)->coalescecollid;
                        break;
                case T_MinMaxExpr:
-                       coll = ((MinMaxExpr *) expr)->collid;
+                       coll = ((MinMaxExpr *) expr)->minmaxcollid;
                        break;
                case T_XmlExpr:
+                       /*
+                        * XMLSERIALIZE returns text from non-collatable inputs, so its
+                        * collation is always default.  The other cases return boolean
+                        * or XML, which are non-collatable.
+                        */
                        if (((XmlExpr *) expr)->op == IS_XMLSERIALIZE)
                                coll = DEFAULT_COLLATION_OID;
                        else
                                coll = InvalidOid;
                        break;
-               case T_NullIfExpr:
-                       coll = exprCollation((Node *) linitial(((NullIfExpr *) expr)->args));
-                       break;
                case T_NullTest:
-                       coll = InvalidOid; /* not applicable */
+                       coll = InvalidOid;                              /* result is always boolean */
                        break;
                case T_BooleanTest:
-                       coll = InvalidOid; /* not applicable */
+                       coll = InvalidOid;                              /* result is always boolean */
                        break;
                case T_CoerceToDomain:
-                       coll = get_typcollation(((CoerceToDomain *) expr)->resulttype);
-                       if (coll == DEFAULT_COLLATION_OID)
-                               coll = exprCollation((Node *) ((CoerceToDomain *) expr)->arg);
+                       coll = ((CoerceToDomain *) expr)->resultcollid;
                        break;
                case T_CoerceToDomainValue:
-                       coll = get_typcollation(((CoerceToDomainValue *) expr)->typeId);
+                       coll = ((CoerceToDomainValue *) expr)->collation;
                        break;
                case T_SetToDefault:
-                       coll = ((SetToDefault *) expr)->collid;
+                       coll = ((SetToDefault *) expr)->collation;
                        break;
                case T_CurrentOfExpr:
-                       coll = InvalidOid; /* not applicable */
+                       coll = InvalidOid;                              /* result is always boolean */
                        break;
                case T_PlaceHolderVar:
                        coll = exprCollation((Node *) ((PlaceHolderVar *) expr)->phexpr);
@@ -652,176 +811,239 @@ exprCollation(Node *expr)
                        coll = InvalidOid;      /* keep compiler quiet */
                        break;
        }
-
        return coll;
 }
 
 /*
- * Compute the result collation of a coercion-like expression that
- * converts arg to resulttype.
+ *     exprInputCollation -
+ *       returns the Oid of the collation a function should use, if available.
+ *
+ * Result is InvalidOid if the node type doesn't store this information.
  */
 Oid
-coercion_expression_result_collation(Oid resulttype, Node *arg)
+exprInputCollation(Node *expr)
 {
-       if (type_is_collatable(resulttype))
+       Oid                     coll;
+
+       if (!expr)
+               return InvalidOid;
+
+       switch (nodeTag(expr))
        {
-               if (type_is_collatable(exprType(arg)))
-                       return exprCollation(arg);
-               else
-                       return DEFAULT_COLLATION_OID;
+               case T_Aggref:
+                       coll = ((Aggref *) expr)->inputcollid;
+                       break;
+               case T_WindowFunc:
+                       coll = ((WindowFunc *) expr)->inputcollid;
+                       break;
+               case T_FuncExpr:
+                       coll = ((FuncExpr *) expr)->inputcollid;
+                       break;
+               case T_OpExpr:
+                       coll = ((OpExpr *) expr)->inputcollid;
+                       break;
+               case T_DistinctExpr:
+                       coll = ((DistinctExpr *) expr)->inputcollid;
+                       break;
+               case T_NullIfExpr:
+                       coll = ((NullIfExpr *) expr)->inputcollid;
+                       break;
+               case T_ScalarArrayOpExpr:
+                       coll = ((ScalarArrayOpExpr *) expr)->inputcollid;
+                       break;
+               case T_MinMaxExpr:
+                       coll = ((MinMaxExpr *) expr)->inputcollid;
+                       break;
+               default:
+                       coll = InvalidOid;
+                       break;
        }
-       else
-               return InvalidOid;
+       return coll;
 }
 
 /*
- * exprIsLengthCoercion
- *             Detect whether an expression tree is an application of a datatype's
- *             typmod-coercion function.  Optionally extract the result's typmod.
- *
- * If coercedTypmod is not NULL, the typmod is stored there if the expression
- * is a length-coercion function, else -1 is stored there.
+ *     exprSetCollation -
+ *       Assign collation information to an expression tree node.
  *
- * Note that a combined type-and-length coercion will be treated as a
- * length coercion by this routine.
+ * Note: since this is only used during parse analysis, we don't need to
+ * worry about subplans or PlaceHolderVars.
  */
-bool
-exprIsLengthCoercion(Node *expr, int32 *coercedTypmod)
+void
+exprSetCollation(Node *expr, Oid collation)
 {
-       if (coercedTypmod != NULL)
-               *coercedTypmod = -1;    /* default result on failure */
-
-       /*
-        * Scalar-type length coercions are FuncExprs, array-type length coercions
-        * are ArrayCoerceExprs
-        */
-       if (expr && IsA(expr, FuncExpr))
-       {
-               FuncExpr   *func = (FuncExpr *) expr;
-               int                     nargs;
-               Const      *second_arg;
-
-               /*
-                * If it didn't come from a coercion context, reject.
-                */
-               if (func->funcformat != COERCE_EXPLICIT_CAST &&
-                       func->funcformat != COERCE_IMPLICIT_CAST)
-                       return false;
-
-               /*
-                * If it's not a two-argument or three-argument function with the
-                * second argument being an int4 constant, it can't have been created
-                * from a length coercion (it must be a type coercion, instead).
-                */
-               nargs = list_length(func->args);
-               if (nargs < 2 || nargs > 3)
-                       return false;
-
-               second_arg = (Const *) lsecond(func->args);
-               if (!IsA(second_arg, Const) ||
-                       second_arg->consttype != INT4OID ||
-                       second_arg->constisnull)
-                       return false;
-
-               /*
-                * OK, it is indeed a length-coercion function.
-                */
-               if (coercedTypmod != NULL)
-                       *coercedTypmod = DatumGetInt32(second_arg->constvalue);
-
-               return true;
-       }
-
-       if (expr && IsA(expr, ArrayCoerceExpr))
+       switch (nodeTag(expr))
        {
-               ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) expr;
-
-               /* It's not a length coercion unless there's a nondefault typmod */
-               if (acoerce->resulttypmod < 0)
-                       return false;
+               case T_Var:
+                       ((Var *) expr)->varcollid = collation;
+                       break;
+               case T_Const:
+                       ((Const *) expr)->constcollid = collation;
+                       break;
+               case T_Param:
+                       ((Param *) expr)->paramcollid = collation;
+                       break;
+               case T_Aggref:
+                       ((Aggref *) expr)->aggcollid = collation;
+                       break;
+               case T_WindowFunc:
+                       ((WindowFunc *) expr)->wincollid = collation;
+                       break;
+               case T_ArrayRef:
+                       ((ArrayRef *) expr)->refcollid = collation;
+                       break;
+               case T_FuncExpr:
+                       ((FuncExpr *) expr)->funccollid = collation;
+                       break;
+               case T_NamedArgExpr:
+                       Assert(collation == exprCollation((Node *) ((NamedArgExpr *) expr)->arg));
+                       break;
+               case T_OpExpr:
+                       ((OpExpr *) expr)->opcollid = collation;
+                       break;
+               case T_DistinctExpr:
+                       ((DistinctExpr *) expr)->opcollid = collation;
+                       break;
+               case T_NullIfExpr:
+                       ((NullIfExpr *) expr)->opcollid = collation;
+                       break;
+               case T_ScalarArrayOpExpr:
+                       Assert(!OidIsValid(collation)); /* result is always boolean */
+                       break;
+               case T_BoolExpr:
+                       Assert(!OidIsValid(collation)); /* result is always boolean */
+                       break;
+               case T_SubLink:
+#ifdef USE_ASSERT_CHECKING
+                       {
+                               SubLink    *sublink = (SubLink *) expr;
 
-               /*
-                * OK, it is indeed a length-coercion expression.
-                */
-               if (coercedTypmod != NULL)
-                       *coercedTypmod = acoerce->resulttypmod;
+                               if (sublink->subLinkType == EXPR_SUBLINK ||
+                                       sublink->subLinkType == ARRAY_SUBLINK)
+                               {
+                                       /* get the collation of subselect's first target column */
+                                       Query      *qtree = (Query *) sublink->subselect;
+                                       TargetEntry *tent;
 
-               return true;
+                                       if (!qtree || !IsA(qtree, Query))
+                                               elog(ERROR, "cannot set collation for untransformed sublink");
+                                       tent = (TargetEntry *) linitial(qtree->targetList);
+                                       Assert(IsA(tent, TargetEntry));
+                                       Assert(!tent->resjunk);
+                                       Assert(collation == exprCollation((Node *) tent->expr));
+                               }
+                               else
+                               {
+                                       /* for all other sublink types, result is boolean */
+                                       Assert(!OidIsValid(collation));
+                               }
+                       }
+#endif /* USE_ASSERT_CHECKING */
+                       break;
+               case T_FieldSelect:
+                       ((FieldSelect *) expr)->resultcollid = collation;
+                       break;
+               case T_FieldStore:
+                       Assert(!OidIsValid(collation)); /* result is always composite */
+                       break;
+               case T_RelabelType:
+                       ((RelabelType *) expr)->resultcollid = collation;
+                       break;
+               case T_CoerceViaIO:
+                       ((CoerceViaIO *) expr)->resultcollid = collation;
+                       break;
+               case T_ArrayCoerceExpr:
+                       ((ArrayCoerceExpr *) expr)->resultcollid = collation;
+                       break;
+               case T_ConvertRowtypeExpr:
+                       Assert(!OidIsValid(collation)); /* result is always composite */
+                       break;
+               case T_CaseExpr:
+                       ((CaseExpr *) expr)->casecollid = collation;
+                       break;
+               case T_ArrayExpr:
+                       ((ArrayExpr *) expr)->array_collid = collation;
+                       break;
+               case T_RowExpr:
+                       Assert(!OidIsValid(collation)); /* result is always composite */
+                       break;
+               case T_RowCompareExpr:
+                       Assert(!OidIsValid(collation)); /* result is always boolean */
+                       break;
+               case T_CoalesceExpr:
+                       ((CoalesceExpr *) expr)->coalescecollid = collation;
+                       break;
+               case T_MinMaxExpr:
+                       ((MinMaxExpr *) expr)->minmaxcollid = collation;
+                       break;
+               case T_XmlExpr:
+                       Assert((((XmlExpr *) expr)->op == IS_XMLSERIALIZE) ?
+                                  (collation == DEFAULT_COLLATION_OID) :
+                                  (collation == InvalidOid));
+                       break;
+               case T_NullTest:
+                       Assert(!OidIsValid(collation)); /* result is always boolean */
+                       break;
+               case T_BooleanTest:
+                       Assert(!OidIsValid(collation)); /* result is always boolean */
+                       break;
+               case T_CoerceToDomain:
+                       ((CoerceToDomain *) expr)->resultcollid = collation;
+                       break;
+               case T_CoerceToDomainValue:
+                       ((CoerceToDomainValue *) expr)->collation = collation;
+                       break;
+               case T_SetToDefault:
+                       ((SetToDefault *) expr)->collation = collation;
+                       break;
+               case T_CurrentOfExpr:
+                       Assert(!OidIsValid(collation)); /* result is always boolean */
+                       break;
+               default:
+                       elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
+                       break;
        }
-
-       return false;
 }
 
 /*
- * expression_returns_set
- *       Test whether an expression returns a set result.
+ *     exprSetInputCollation -
+ *       Assign input-collation information to an expression tree node.
  *
- * Because we use expression_tree_walker(), this can also be applied to
- * whole targetlists; it'll produce TRUE if any one of the tlist items
- * returns a set.
+ * This is a no-op for node types that don't store their input collation.
+ * Note we omit RowCompareExpr, which needs special treatment since it
+ * contains multiple input collation OIDs.
  */
-bool
-expression_returns_set(Node *clause)
+void
+exprSetInputCollation(Node *expr, Oid inputcollation)
 {
-       return expression_returns_set_walker(clause, NULL);
-}
-
-static bool
-expression_returns_set_walker(Node *node, void *context)
-{
-       if (node == NULL)
-               return false;
-       if (IsA(node, FuncExpr))
-       {
-               FuncExpr   *expr = (FuncExpr *) node;
-
-               if (expr->funcretset)
-                       return true;
-               /* else fall through to check args */
-       }
-       if (IsA(node, OpExpr))
+       switch (nodeTag(expr))
        {
-               OpExpr     *expr = (OpExpr *) node;
-
-               if (expr->opretset)
-                       return true;
-               /* else fall through to check args */
+               case T_Aggref:
+                       ((Aggref *) expr)->inputcollid = inputcollation;
+                       break;
+               case T_WindowFunc:
+                       ((WindowFunc *) expr)->inputcollid = inputcollation;
+                       break;
+               case T_FuncExpr:
+                       ((FuncExpr *) expr)->inputcollid = inputcollation;
+                       break;
+               case T_OpExpr:
+                       ((OpExpr *) expr)->inputcollid = inputcollation;
+                       break;
+               case T_DistinctExpr:
+                       ((DistinctExpr *) expr)->inputcollid = inputcollation;
+                       break;
+               case T_NullIfExpr:
+                       ((NullIfExpr *) expr)->inputcollid = inputcollation;
+                       break;
+               case T_ScalarArrayOpExpr:
+                       ((ScalarArrayOpExpr *) expr)->inputcollid = inputcollation;
+                       break;
+               case T_MinMaxExpr:
+                       ((MinMaxExpr *) expr)->inputcollid = inputcollation;
+                       break;
+               default:
+                       break;
        }
-
-       /* Avoid recursion for some cases that can't return a set */
-       if (IsA(node, Aggref))
-               return false;
-       if (IsA(node, WindowFunc))
-               return false;
-       if (IsA(node, DistinctExpr))
-               return false;
-       if (IsA(node, ScalarArrayOpExpr))
-               return false;
-       if (IsA(node, BoolExpr))
-               return false;
-       if (IsA(node, SubLink))
-               return false;
-       if (IsA(node, SubPlan))
-               return false;
-       if (IsA(node, AlternativeSubPlan))
-               return false;
-       if (IsA(node, ArrayExpr))
-               return false;
-       if (IsA(node, RowExpr))
-               return false;
-       if (IsA(node, RowCompareExpr))
-               return false;
-       if (IsA(node, CoalesceExpr))
-               return false;
-       if (IsA(node, MinMaxExpr))
-               return false;
-       if (IsA(node, XmlExpr))
-               return false;
-       if (IsA(node, NullIfExpr))
-               return false;
-
-       return expression_tree_walker(node, expression_returns_set_walker,
-                                                                 context);
 }
 
 
@@ -1365,6 +1587,8 @@ expression_tree_walker(Node *node,
                case T_NamedArgExpr:
                        return walker(((NamedArgExpr *) node)->arg, context);
                case T_OpExpr:
+               case T_DistinctExpr:    /* struct-equivalent to OpExpr */
+               case T_NullIfExpr:              /* struct-equivalent to OpExpr */
                        {
                                OpExpr     *expr = (OpExpr *) node;
 
@@ -1373,15 +1597,6 @@ expression_tree_walker(Node *node,
                                        return true;
                        }
                        break;
-               case T_DistinctExpr:
-                       {
-                               DistinctExpr *expr = (DistinctExpr *) node;
-
-                               if (expression_tree_walker((Node *) expr->args,
-                                                                                  walker, context))
-                                       return true;
-                       }
-                       break;
                case T_ScalarArrayOpExpr:
                        {
                                ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -1502,8 +1717,6 @@ expression_tree_walker(Node *node,
                                        return true;
                        }
                        break;
-               case T_NullIfExpr:
-                       return walker(((NullIfExpr *) node)->args, context);
                case T_NullTest:
                        return walker(((NullTest *) node)->arg, context);
                case T_BooleanTest:
@@ -1648,8 +1861,11 @@ query_tree_walker(Query *query,
                if (walker((Node *) query->cteList, context))
                        return true;
        }
-       if (range_table_walker(query->rtable, walker, context, flags))
-               return true;
+       if (!(flags & QTW_IGNORE_RANGE_TABLE))
+       {
+               if (range_table_walker(query->rtable, walker, context, flags))
+                       return true;
+       }
        return false;
 }
 
@@ -1908,6 +2124,16 @@ expression_tree_mutator(Node *node,
                                return (Node *) newnode;
                        }
                        break;
+               case T_NullIfExpr:
+                       {
+                               NullIfExpr *expr = (NullIfExpr *) node;
+                               NullIfExpr *newnode;
+
+                               FLATCOPY(newnode, expr, NullIfExpr);
+                               MUTATE(newnode->args, expr->args, List *);
+                               return (Node *) newnode;
+                       }
+                       break;
                case T_ScalarArrayOpExpr:
                        {
                                ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -2127,16 +2353,6 @@ expression_tree_mutator(Node *node,
                                return (Node *) newnode;
                        }
                        break;
-               case T_NullIfExpr:
-                       {
-                               NullIfExpr *expr = (NullIfExpr *) node;
-                               NullIfExpr *newnode;
-
-                               FLATCOPY(newnode, expr, NullIfExpr);
-                               MUTATE(newnode->args, expr->args, List *);
-                               return (Node *) newnode;
-                       }
-                       break;
                case T_NullTest:
                        {
                                NullTest   *ntest = (NullTest *) node;
index d56e4da..db4a33c 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);
 }
 
@@ -1315,19 +1340,6 @@ _outXmlExpr(StringInfo str, XmlExpr *node)
 }
 
 static void
-_outNullIfExpr(StringInfo str, NullIfExpr *node)
-{
-       WRITE_NODE_TYPE("NULLIFEXPR");
-
-       WRITE_OID_FIELD(opno);
-       WRITE_OID_FIELD(opfuncid);
-       WRITE_OID_FIELD(opresulttype);
-       WRITE_BOOL_FIELD(opretset);
-       WRITE_NODE_FIELD(args);
-       WRITE_LOCATION_FIELD(location);
-}
-
-static void
 _outNullTest(StringInfo str, NullTest *node)
 {
        WRITE_NODE_TYPE("NULLTEST");
@@ -1354,6 +1366,7 @@ _outCoerceToDomain(StringInfo str, CoerceToDomain *node)
        WRITE_NODE_FIELD(arg);
        WRITE_OID_FIELD(resulttype);
        WRITE_INT_FIELD(resulttypmod);
+       WRITE_OID_FIELD(resultcollid);
        WRITE_ENUM_FIELD(coercionformat, CoercionForm);
        WRITE_LOCATION_FIELD(location);
 }
@@ -1365,6 +1378,7 @@ _outCoerceToDomainValue(StringInfo str, CoerceToDomainValue *node)
 
        WRITE_OID_FIELD(typeId);
        WRITE_INT_FIELD(typeMod);
+       WRITE_OID_FIELD(collation);
        WRITE_LOCATION_FIELD(location);
 }
 
@@ -1375,7 +1389,7 @@ _outSetToDefault(StringInfo str, SetToDefault *node)
 
        WRITE_OID_FIELD(typeId);
        WRITE_INT_FIELD(typeMod);
-       WRITE_OID_FIELD(collid);
+       WRITE_OID_FIELD(collation);
        WRITE_LOCATION_FIELD(location);
 }
 
@@ -1766,6 +1780,7 @@ _outEquivalenceClass(StringInfo str, EquivalenceClass *node)
        WRITE_NODE_TYPE("EQUIVALENCECLASS");
 
        WRITE_NODE_FIELD(ec_opfamilies);
+       WRITE_OID_FIELD(ec_collation);
        WRITE_NODE_FIELD(ec_members);
        WRITE_NODE_FIELD(ec_sources);
        WRITE_NODE_FIELD(ec_derives);
@@ -1796,7 +1811,6 @@ _outPathKey(StringInfo str, PathKey *node)
 
        WRITE_NODE_FIELD(pk_eclass);
        WRITE_OID_FIELD(pk_opfamily);
-       WRITE_OID_FIELD(pk_collation);
        WRITE_INT_FIELD(pk_strategy);
        WRITE_BOOL_FIELD(pk_nulls_first);
 }
@@ -2814,6 +2828,9 @@ _outNode(StringInfo str, void *obj)
                        case T_DistinctExpr:
                                _outDistinctExpr(str, obj);
                                break;
+                       case T_NullIfExpr:
+                               _outNullIfExpr(str, obj);
+                               break;
                        case T_ScalarArrayOpExpr:
                                _outScalarArrayOpExpr(str, obj);
                                break;
@@ -2877,9 +2894,6 @@ _outNode(StringInfo str, void *obj)
                        case T_XmlExpr:
                                _outXmlExpr(str, obj);
                                break;
-                       case T_NullIfExpr:
-                               _outNullIfExpr(str, obj);
-                               break;
                        case T_NullTest:
                                _outNullTest(str, obj);
                                break;
index 6da6128..5f1fd32 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();
@@ -964,35 +1005,6 @@ _readXmlExpr(void)
 }
 
 /*
- * _readNullIfExpr
- */
-static NullIfExpr *
-_readNullIfExpr(void)
-{
-       READ_LOCALS(NullIfExpr);
-
-       READ_OID_FIELD(opno);
-       READ_OID_FIELD(opfuncid);
-
-       /*
-        * The opfuncid is stored in the textual format primarily for debugging
-        * and documentation reasons.  We want to always read it as zero to force
-        * it to be re-looked-up in the pg_operator entry.      This ensures that
-        * stored rules don't have hidden dependencies on operators' functions.
-        * (We don't currently support an ALTER OPERATOR command, but might
-        * someday.)
-        */
-       local_node->opfuncid = InvalidOid;
-
-       READ_OID_FIELD(opresulttype);
-       READ_BOOL_FIELD(opretset);
-       READ_NODE_FIELD(args);
-       READ_LOCATION_FIELD(location);
-
-       READ_DONE();
-}
-
-/*
  * _readNullTest
  */
 static NullTest *
@@ -1032,6 +1044,7 @@ _readCoerceToDomain(void)
        READ_NODE_FIELD(arg);
        READ_OID_FIELD(resulttype);
        READ_INT_FIELD(resulttypmod);
+       READ_OID_FIELD(resultcollid);
        READ_ENUM_FIELD(coercionformat, CoercionForm);
        READ_LOCATION_FIELD(location);
 
@@ -1048,6 +1061,7 @@ _readCoerceToDomainValue(void)
 
        READ_OID_FIELD(typeId);
        READ_INT_FIELD(typeMod);
+       READ_OID_FIELD(collation);
        READ_LOCATION_FIELD(location);
 
        READ_DONE();
@@ -1063,7 +1077,7 @@ _readSetToDefault(void)
 
        READ_OID_FIELD(typeId);
        READ_INT_FIELD(typeMod);
-       READ_OID_FIELD(collid);
+       READ_OID_FIELD(collation);
        READ_LOCATION_FIELD(location);
 
        READ_DONE();
@@ -1273,6 +1287,8 @@ parseNodeString(void)
                return_value = _readOpExpr();
        else if (MATCH("DISTINCTEXPR", 12))
                return_value = _readDistinctExpr();
+       else if (MATCH("NULLIFEXPR", 10))
+               return_value = _readNullIfExpr();
        else if (MATCH("SCALARARRAYOPEXPR", 17))
                return_value = _readScalarArrayOpExpr();
        else if (MATCH("BOOLEXPR", 8))
@@ -1311,8 +1327,6 @@ parseNodeString(void)
                return_value = _readMinMaxExpr();
        else if (MATCH("XMLEXPR", 7))
                return_value = _readXmlExpr();
-       else if (MATCH("NULLIFEXPR", 10))
-               return_value = _readNullIfExpr();
        else if (MATCH("NULLTEST", 8))
                return_value = _readNullTest();
        else if (MATCH("BOOLEANTEST", 11))
index 756874b..8f763b4 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 3d87a5b..9a32e16 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,12 +120,24 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
        /* Extract info from given clause */
        Assert(is_opclause(clause));
        opno = ((OpExpr *) clause)->opno;
+       collation = ((OpExpr *) clause)->inputcollid;
        item1 = (Expr *) get_leftop(clause);
        item2 = (Expr *) get_rightop(clause);
        item1_relids = restrictinfo->left_relids;
        item2_relids = restrictinfo->right_relids;
 
        /*
+        * Ensure both input expressions expose the desired collation (their types
+        * should be OK already); see comments for canonicalize_ec_expression.
+        */
+       item1 = canonicalize_ec_expression(item1,
+                                                                          exprType((Node *) item1),
+                                                                          collation);
+       item2 = canonicalize_ec_expression(item2,
+                                                                          exprType((Node *) item2),
+                                                                          collation);
+
+       /*
         * Reject clauses of the form X=X.      These are not as redundant as they
         * might seem at first glance: assuming the operator is strict, this is
         * really an expensive way to write X IS NOT NULL.      So we must not risk
@@ -189,6 +204,13 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
                        continue;
 
                /*
+                * The collation has to match; check this first since it's cheaper
+                * than the opfamily comparison.
+                */
+               if (collation != cur_ec->ec_collation)
+                       continue;
+
+               /*
                 * A "match" requires matching sets of btree opfamilies.  Use of
                 * equal() for this test has implications discussed in the comments
                 * for get_mergejoin_opfamilies().
@@ -315,6 +337,7 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
                EquivalenceClass *ec = makeNode(EquivalenceClass);
 
                ec->ec_opfamilies = opfamilies;
+               ec->ec_collation = collation;
                ec->ec_members = NIL;
                ec->ec_sources = list_make1(restrictinfo);
                ec->ec_derives = NIL;
@@ -342,6 +365,84 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
 }
 
 /*
+ * canonicalize_ec_expression
+ *
+ * This function ensures that the expression exposes the expected type and
+ * collation, so that it will be equal() to other equivalence-class expressions
+ * that it ought to be equal() to.
+ *
+ * The rule for datatypes is that the exposed type should match what it would
+ * be for an input to an operator of the EC's opfamilies; which is usually
+ * the declared input type of the operator, but in the case of polymorphic
+ * operators no relabeling is wanted (compare the behavior of parse_coerce.c).
+ * Expressions coming in from quals will generally have the right type
+ * already, but expressions coming from indexkeys may not (because they are
+ * represented without any explicit relabel in pg_index), and the same problem
+ * occurs for sort expressions (because the parser is likewise cavalier about
+ * putting relabels on them).  Such cases will be binary-compatible with the
+ * real operators, so adding a RelabelType is sufficient.
+ *
+ * Also, the expression's exposed collation must match the EC's collation.
+ * This is important because in comparisons like "foo < bar COLLATE baz",
+ * only one of the expressions has the correct exposed collation as we receive
+ * it from the parser.  Forcing both of them to have it ensures that all
+ * variant spellings of such a construct behave the same.  Again, we can
+ * stick on a RelabelType to force the right exposed collation.  (It might
+ * work to not label the collation at all in EC members, but this is risky
+ * since some parts of the system expect exprCollation() to deliver the
+ * right answer for a sort key.)
+ *
+ * Note this code assumes that the expression has already been through
+ * eval_const_expressions, so there are no CollateExprs and no redundant
+ * RelabelTypes.
+ */
+Expr *
+canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
+{
+       Oid                     expr_type = exprType((Node *) expr);
+
+       /*
+        * For a polymorphic-input-type opclass, just keep the same exposed type.
+        */
+       if (IsPolymorphicType(req_type))
+               req_type = expr_type;
+
+       /*
+        * No work if the expression exposes the right type/collation already.
+        */
+       if (expr_type != req_type ||
+               exprCollation((Node *) expr) != req_collation)
+       {
+               /*
+                * Strip any existing RelabelType, then add a new one if needed.
+                * This is to preserve the invariant of no redundant RelabelTypes.
+                *
+                * If we have to change the exposed type of the stripped expression,
+                * set typmod to -1 (since the new type may not have the same typmod
+                * interpretation).  If we only have to change collation, preserve
+                * the exposed typmod.
+                */
+               while (expr && IsA(expr, RelabelType))
+                       expr = (Expr *) ((RelabelType *) expr)->arg;
+
+               if (exprType((Node *) expr) != req_type)
+                       expr = (Expr *) makeRelabelType(expr,
+                                                                                       req_type,
+                                                                                       -1,
+                                                                                       req_collation,
+                                                                                       COERCE_DONTCARE);
+               else if (exprCollation((Node *) expr) != req_collation)
+                       expr = (Expr *) makeRelabelType(expr,
+                                                                                       req_type,
+                                                                                       exprTypmod((Node *) expr),
+                                                                                       req_collation,
+                                                                                       COERCE_DONTCARE);
+       }
+
+       return expr;
+}
+
+/*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
 static EquivalenceMember *
@@ -383,9 +484,9 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 
 /*
  * get_eclass_for_sort_expr
- *       Given an expression and opfamily info, find an existing equivalence
- *       class it is a member of; if none, optionally build a new single-member
- *       EquivalenceClass for it.
+ *       Given an expression and opfamily/collation info, find an existing
+ *       equivalence class it is a member of; if none, optionally build a new
+ *       single-member EquivalenceClass for it.
  *
  * sortref is the SortGroupRef of the originating SortGroupClause, if any,
  * or zero if not.     (It should never be zero if the expression is volatile!)
@@ -406,8 +507,9 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 EquivalenceClass *
 get_eclass_for_sort_expr(PlannerInfo *root,
                                                 Expr *expr,
-                                                Oid expr_datatype,
                                                 List *opfamilies,
+                                                Oid opcintype,
+                                                Oid collation,
                                                 Index sortref,
                                                 bool create_it)
 {
@@ -417,6 +519,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
        MemoryContext oldcontext;
 
        /*
+        * Ensure the expression exposes the correct type and collation.
+        */
+       expr = canonicalize_ec_expression(expr, opcintype, collation);
+
+       /*
         * Scan through the existing EquivalenceClasses for a match
         */
        foreach(lc1, root->eq_classes)
@@ -432,6 +539,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
                        (sortref == 0 || sortref != cur_ec->ec_sortref))
                        continue;
 
+               if (collation != cur_ec->ec_collation)
+                       continue;
                if (!equal(opfamilies, cur_ec->ec_opfamilies))
                        continue;
 
@@ -447,7 +556,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
                                cur_em->em_is_const)
                                continue;
 
-                       if (expr_datatype == cur_em->em_datatype &&
+                       if (opcintype == cur_em->em_datatype &&
                                equal(expr, cur_em->em_expr))
                                return cur_ec;  /* Match! */
                }
@@ -460,13 +569,13 @@ get_eclass_for_sort_expr(PlannerInfo *root,
        /*
         * OK, build a new single-member EC
         *
-        * Here, we must be sure that we construct the EC in the right context. We
-        * can assume, however, that the passed expr is long-lived.
+        * Here, we must be sure that we construct the EC in the right context.
         */
        oldcontext = MemoryContextSwitchTo(root->planner_cxt);
 
        newec = makeNode(EquivalenceClass);
        newec->ec_opfamilies = list_copy(opfamilies);
+       newec->ec_collation = collation;
        newec->ec_members = NIL;
        newec->ec_sources = NIL;
        newec->ec_derives = NIL;
@@ -481,8 +590,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
        if (newec->ec_has_volatile && sortref == 0) /* should not happen */
                elog(ERROR, "volatile EquivalenceClass has no sortref");
 
-       newem = add_eq_member(newec, expr, pull_varnos((Node *) expr),
-                                                 false, expr_datatype);
+       newem = add_eq_member(newec, copyObject(expr), pull_varnos((Node *) expr),
+                                                 false, opcintype);
 
        /*
         * add_eq_member doesn't check for volatile functions, set-returning
@@ -660,7 +769,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
                        ec->ec_broken = true;
                        break;
                }
-               process_implied_equality(root, eq_op,
+               process_implied_equality(root, eq_op, ec->ec_collation,
                                                                 cur_em->em_expr, const_em->em_expr,
                                                                 ec->ec_relids,
                                                                 ec->ec_below_outer_join,
@@ -715,7 +824,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
                                ec->ec_broken = true;
                                break;
                        }
-                       process_implied_equality(root, eq_op,
+                       process_implied_equality(root, eq_op, ec->ec_collation,
                                                                         prev_em->em_expr, cur_em->em_expr,
                                                                         ec->ec_relids,
                                                                         ec->ec_below_outer_join,
@@ -1117,6 +1226,7 @@ create_join_clause(PlannerInfo *root,
        oldcontext = MemoryContextSwitchTo(root->planner_cxt);
 
        rinfo = build_implied_join_equality(opno,
+                                                                               ec->ec_collation,
                                                                                leftem->em_expr,
                                                                                rightem->em_expr,
                                                                                bms_union(leftem->em_relids,
@@ -1338,6 +1448,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
        Expr       *outervar,
                           *innervar;
        Oid                     opno,
+                               collation,
                                left_type,
                                right_type,
                                inner_datatype;
@@ -1346,6 +1457,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
 
        Assert(is_opclause(rinfo->clause));
        opno = ((OpExpr *) rinfo->clause)->opno;
+       collation = ((OpExpr *) rinfo->clause)->inputcollid;
 
        /* If clause is outerjoin_delayed, operator must be strict */
        if (rinfo->outerjoin_delayed && !op_strict(opno))
@@ -1381,7 +1493,9 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
                /* Never match to a volatile EC */
                if (cur_ec->ec_has_volatile)
                        continue;
-               /* It has to match the outer-join clause as to opfamilies, too */
+               /* It has to match the outer-join clause as to semantics, too */
+               if (collation != cur_ec->ec_collation)
+                       continue;
                if (!equal(rinfo->mergeopfamilies, cur_ec->ec_opfamilies))
                        continue;
                /* Does it contain a match to outervar? */
@@ -1419,6 +1533,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
                        if (!OidIsValid(eq_op))
                                continue;               /* can't generate equality */
                        newrinfo = build_implied_join_equality(eq_op,
+                                                                                                  cur_ec->ec_collation,
                                                                                                   innervar,
                                                                                                   cur_em->em_expr,
                                                                                                   inner_relids);
@@ -1451,6 +1566,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
        Expr       *leftvar;
        Expr       *rightvar;
        Oid                     opno,
+                               collation,
                                left_type,
                                right_type;
        Relids          left_relids,
@@ -1464,6 +1580,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
        /* Extract needed info from the clause */
        Assert(is_opclause(rinfo->clause));
        opno = ((OpExpr *) rinfo->clause)->opno;
+       collation = ((OpExpr *) rinfo->clause)->inputcollid;
        op_input_types(opno, &left_type, &right_type);
        leftvar = (Expr *) get_leftop(rinfo->clause);
        rightvar = (Expr *) get_rightop(rinfo->clause);
@@ -1485,7 +1602,9 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
                /* Never match to a volatile EC */
                if (cur_ec->ec_has_volatile)
                        continue;
-               /* It has to match the outer-join clause as to opfamilies, too */
+               /* It has to match the outer-join clause as to semantics, too */
+               if (collation != cur_ec->ec_collation)
+                       continue;
                if (!equal(rinfo->mergeopfamilies, cur_ec->ec_opfamilies))
                        continue;
 
@@ -1548,6 +1667,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
                        if (OidIsValid(eq_op))
                        {
                                newrinfo = build_implied_join_equality(eq_op,
+                                                                                                          cur_ec->ec_collation,
                                                                                                           leftvar,
                                                                                                           cur_em->em_expr,
                                                                                                           left_relids);
@@ -1560,6 +1680,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
                        if (OidIsValid(eq_op))
                        {
                                newrinfo = build_implied_join_equality(eq_op,
+                                                                                                          cur_ec->ec_collation,
                                                                                                           rightvar,
                                                                                                           cur_em->em_expr,
                                                                                                           right_relids);
index 65bc9be..1ac0ff6 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 fd75928..de3e4ac 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 8a0135c..bdd14f5 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 845b4ae..0e00df6 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 a964921..f2b586d 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 8503792..7b31b6b 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 4561e8e..6a1f729 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;
@@ -1421,6 +1423,14 @@ btree_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
                return false;
 
        /*
+        * They'd better have the same collation, too.
+        */
+       pred_collation = ((OpExpr *) predicate)->inputcollid;
+       clause_collation = ((OpExpr *) clause)->inputcollid;
+       if (pred_collation != clause_collation)
+               return false;
+
+       /*
         * Okay, get the operators in the two clauses we're comparing. Commute
         * them if needed so that we can assume the variables are on the left.
         */
@@ -1465,7 +1475,9 @@ btree_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
                                                          BOOLOID,
                                                          false,
                                                          (Expr *) pred_const,
-                                                         (Expr *) clause_const);
+                                                         (Expr *) clause_const,
+                                                         InvalidOid,
+                                                         pred_collation);
 
        /* Fill in opfuncids */
        fix_opfuncids((Node *) test_expr);
index ee92a8c..665250b 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 59cc32f..08625e4 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 85f231d..315f067 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 8267627..523d6e6 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 4c5a6fe..6c0a784 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"
@@ -558,6 +559,11 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
        funcexpr = transformExpr(pstate, r->funccallnode);
 
        /*
+        * We must assign collations now so that we can fill funccolcollations.
+        */
+       assign_expr_collations(pstate, funcexpr);
+
+       /*
         * The function parameters cannot make use of any variables from other
         * FROM items.  (Compare to transformRangeSubselect(); the coding is
         * different though because we didn't parse as a sub-select with its own
@@ -1072,6 +1078,7 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
        else if (l_colvar->vartypmod != outcoltypmod)
                l_node = (Node *) makeRelabelType((Expr *) l_colvar,
                                                                                  outcoltype, outcoltypmod,
+                                                                                 InvalidOid,           /* fixed below */
                                                                                  COERCE_IMPLICIT_CAST);
        else
                l_node = (Node *) l_colvar;
@@ -1083,6 +1090,7 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
        else if (r_colvar->vartypmod != outcoltypmod)
                r_node = (Node *) makeRelabelType((Expr *) r_colvar,
                                                                                  outcoltype, outcoltypmod,
+                                                                                 InvalidOid,           /* fixed below */
                                                                                  COERCE_IMPLICIT_CAST);
        else
                r_node = (Node *) r_colvar;
@@ -1121,6 +1129,7 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
                                CoalesceExpr *c = makeNode(CoalesceExpr);
 
                                c->coalescetype = outcoltype;
+                               /* coalescecollid will get set below */
                                c->args = list_make2(l_node, r_node);
                                c->location = -1;
                                res_node = (Node *) c;
@@ -1132,6 +1141,13 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
                        break;
        }
 
+       /*
+        * Apply assign_expr_collations to fix up the collation info in the
+        * coercion and CoalesceExpr nodes, if we made any.  This must be done
+        * now so that the join node's alias vars show correct collation info.
+        */
+       assign_expr_collations(pstate, res_node);
+
        return res_node;
 }
 
@@ -1936,7 +1952,6 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
                                        bool resolveUnknown)
 {
        Oid                     restype = exprType((Node *) tle->expr);
-       Oid                     rescollation = exprCollation((Node *) tle->expr);
        Oid                     sortop;
        Oid                     eqop;
        bool            hashable;
@@ -2020,12 +2035,6 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
                        break;
        }
 
-       if (type_is_collatable(restype) && !OidIsValid(rescollation))
-               ereport(ERROR,
-                               (errcode(ERRCODE_INDETERMINATE_COLLATION),
-                                errmsg("no collation was derived for the sort expression"),
-                                errhint("Use the COLLATE clause to set the collation explicitly.")));
-
        cancel_parser_errposition_callback(&pcbstate);
 
        /* avoid making duplicate sortlist entries */
index 6aff34d..9b59b03 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 23b72b2..c527f75 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 17bd2bf..4986e0e 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 a2d6c59..a187287 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))));
+               /* array_collid will be set by parse_collate.c */
                newa->multidims = false;
                newa->location = exprLocation((Node *) vargs);
 
@@ -389,8 +383,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
                funcexpr->funcresulttype = rettype;
                funcexpr->funcretset = retset;
                funcexpr->funcformat = COERCE_EXPLICIT_CALL;
+               /* funccollid and inputcollid will be set by parse_collate.c */
                funcexpr->args = fargs;
-               funcexpr->collid = funccollid;
                funcexpr->location = location;
 
                retval = (Node *) funcexpr;
@@ -402,9 +396,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 
                aggref->aggfnoid = funcid;
                aggref->aggtype = rettype;
+               /* aggcollid and inputcollid will be set by parse_collate.c */
                /* args, aggorder, aggdistinct will be set by transformAggregateCall */
                aggref->aggstar = agg_star;
-               aggref->collid = funccollid;
                /* agglevelsup will be set by transformAggregateCall */
                aggref->location = location;
 
@@ -458,11 +452,11 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 
                wfunc->winfnoid = funcid;
                wfunc->wintype = rettype;
+               /* wincollid and inputcollid will be set by parse_collate.c */
                wfunc->args = fargs;
                /* winref will be set by transformWindowFuncCall */
                wfunc->winstar = agg_star;
                wfunc->winagg = (fdresult == FUNCDETAIL_AGGREGATE);
-               wfunc->collid = funccollid;
                wfunc->location = location;
 
                /*
@@ -1390,7 +1384,8 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg,
                        fselect->fieldnum = i + 1;
                        fselect->resulttype = att->atttypid;
                        fselect->resulttypmod = att->atttypmod;
-                       fselect->resultcollation = att->attcollation;
+                       /* resultcollid may get overridden by parse_collate.c */
+                       fselect->resultcollid = att->attcollation;
                        return (Node *) fselect;
                }
        }
index 163fc89..2c76c55 100644 (file)
@@ -270,7 +270,6 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
  * elementType OID of array's element type (fetch with transformArrayType,
  *                             or pass InvalidOid to do it here)
  * arrayTypMod typmod for the array (which is also typmod for the elements)
- * arrayColl   OID of collation of array and array's elements
  * indirection Untransformed list of subscripts (must not be NIL)
  * assignFrom  NULL for array fetch, else transformed expression for source.
  */
@@ -280,7 +279,6 @@ transformArraySubscripts(ParseState *pstate,
                                                 Oid arrayType,
                                                 Oid elementType,
                                                 int32 arrayTypMod,
-                                                Oid arrayColl,
                                                 List *indirection,
                                                 Node *assignFrom)
 {
@@ -407,7 +405,7 @@ transformArraySubscripts(ParseState *pstate,
        aref->refarraytype = arrayType;
        aref->refelemtype = elementType;
        aref->reftypmod = arrayTypMod;
-       aref->refcollid = arrayColl;
+       /* refcollid will be set by parse_collate.c */
        aref->refupperindexpr = upperIndexpr;
        aref->reflowerindexpr = lowerIndexpr;
        aref->refexpr = (Expr *) arrayBase;
index cad41d4..822e0a0 100644 (file)
@@ -782,7 +782,6 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
        List       *args;
        Oid                     rettype;
        OpExpr     *result;
-       Oid                     opcollid;
 
        /* Select the operator */
        if (rtree == NULL)
@@ -862,20 +861,14 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
        /* perform the necessary typecasting of arguments */
        make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
 
-       /* XXX: If we knew which functions required collation information,
-        * we could selectively set the last argument to true here. */
-       opcollid = select_common_collation(pstate, args, false);
-       if (!OidIsValid(opcollid))
-               opcollid = get_typcollation(rettype);
-
        /* and build the expression node */
        result = makeNode(OpExpr);
        result->opno = oprid(tup);
        result->opfuncid = opform->oprcode;
        result->opresulttype = rettype;
        result->opretset = get_func_retset(opform->oprcode);
+       /* opcollid and inputcollid will be set by parse_collate.c */
        result->args = args;
-       result->collid = opcollid;
        result->location = location;
 
        ReleaseSysCache(tup);
@@ -904,7 +897,6 @@ make_scalar_array_op(ParseState *pstate, List *opname,
        List       *args;
        Oid                     rettype;
        ScalarArrayOpExpr *result;
-       Oid                     opcollid;
 
        ltypeId = exprType(ltree);
        atypeId = exprType(rtree);
@@ -999,19 +991,13 @@ make_scalar_array_op(ParseState *pstate, List *opname,
        /* perform the necessary typecasting of arguments */
        make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
 
-       /* XXX: If we knew which functions required collation information,
-        * we could selectively set the last argument to true here. */
-       opcollid = select_common_collation(pstate, args, false);
-       if (!OidIsValid(opcollid))
-               opcollid = get_typcollation(rettype);
-
        /* and build the expression node */
        result = makeNode(ScalarArrayOpExpr);
        result->opno = oprid(tup);
        result->opfuncid = opform->oprcode;
        result->useOr = useOr;
+       /* inputcollid will be set by parse_collate.c */
        result->args = args;
-       result->collid = opcollid;
        result->location = location;
 
        ReleaseSysCache(tup);
index 9e9f2e3..1cf2556 100644 (file)
@@ -114,7 +114,7 @@ fixed_paramref_hook(ParseState *pstate, ParamRef *pref)
        param->paramid = paramno;
        param->paramtype = parstate->paramTypes[paramno - 1];
        param->paramtypmod = -1;
-       param->paramcollation = get_typcollation(param->paramtype);
+       param->paramcollid = get_typcollation(param->paramtype);
        param->location = pref->location;
 
        return (Node *) param;
@@ -167,7 +167,7 @@ variable_paramref_hook(ParseState *pstate, ParamRef *pref)
        param->paramid = paramno;
        param->paramtype = *pptype;
        param->paramtypmod = -1;
-       param->paramcollation = get_typcollation(param->paramtype);
+       param->paramcollid = get_typcollation(param->paramtype);
        param->location = pref->location;
 
        return (Node *) param;
@@ -231,6 +231,8 @@ variable_coerce_param_hook(ParseState *pstate, Param *param,
                 */
                param->paramtypmod = -1;
 
+               param->paramcollid = get_typcollation(param->paramtype);
+
                /* Use the leftmost of the param's and coercion's locations */
                if (location >= 0 &&
                        (param->location < 0 || location < param->location))
index fd1529f..5507835 100644 (file)
@@ -398,7 +398,7 @@ transformAssignedExpr(ParseState *pstate,
 
                def->typeId = attrtype;
                def->typeMod = attrtypmod;
-               def->collid = attrcollation;
+               def->collation = attrcollation;
                if (indirection)
                {
                        if (IsA(linitial(indirection), A_Indices))
@@ -785,7 +785,6 @@ transformAssignmentSubscripts(ParseState *pstate,
                                                                                           arrayType,
                                                                                           elementTypeId,
                                                                                           arrayTypMod,
-                                                                                          InvalidOid,
                                                                                           subscripts,
                                                                                           rhs);
 
@@ -1267,7 +1266,8 @@ ExpandRowReference(ParseState *pstate, Node *expr,
                fselect->fieldnum = i + 1;
                fselect->resulttype = att->atttypid;
                fselect->resulttypmod = att->atttypmod;
-               fselect->resultcollation = att->attcollation;
+               /* resultcollid may get overridden by parse_collate.c */
+               fselect->resultcollid = att->attcollation;
 
                if (targetlist)
                {
index 06baf89..3dffcde 100644 (file)
@@ -46,6 +46,7 @@
 #include "nodes/nodeFuncs.h"
 #include "parser/analyze.h"
 #include "parser/parse_clause.h"
+#include "parser/parse_collate.h"
 #include "parser/parse_expr.h"
 #include "parser/parse_relation.h"
 #include "parser/parse_target.h"
@@ -1798,6 +1799,9 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString)
                        /* Now do parse transformation of the expression */
                        ielem->expr = transformExpr(pstate, ielem->expr);
 
+                       /* We have to fix its collations too */
+                       assign_expr_collations(pstate, ielem->expr);
+
                        /*
                         * We check only that the result type is legitimate; this is for
                         * consistency with what transformWhereClause() checks for the
index ac0c53a..573c8dd 100644 (file)
@@ -4869,6 +4869,16 @@ get_rule_expr(Node *node, deparse_context *context,
                        }
                        break;
 
+               case T_NullIfExpr:
+                       {
+                               NullIfExpr *nullifexpr = (NullIfExpr *) node;
+
+                               appendStringInfo(buf, "NULLIF(");
+                               get_rule_expr((Node *) nullifexpr->args, context, true);
+                               appendStringInfoChar(buf, ')');
+                       }
+                       break;
+
                case T_ScalarArrayOpExpr:
                        {
                                ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -5529,8 +5539,9 @@ get_rule_expr(Node *node, deparse_context *context,
 
                                }
                                if (xexpr->op == IS_XMLSERIALIZE)
-                                       appendStringInfo(buf, " AS %s", format_type_with_typemod(xexpr->type,
-                                                                                                                        xexpr->typmod));
+                                       appendStringInfo(buf, " AS %s",
+                                                                        format_type_with_typemod(xexpr->type,
+                                                                                                                         xexpr->typmod));
                                if (xexpr->op == IS_DOCUMENT)
                                        appendStringInfoString(buf, " IS DOCUMENT");
                                else
@@ -5538,16 +5549,6 @@ get_rule_expr(Node *node, deparse_context *context,
                        }
                        break;
 
-               case T_NullIfExpr:
-                       {
-                               NullIfExpr *nullifexpr = (NullIfExpr *) node;
-
-                               appendStringInfo(buf, "NULLIF(");
-                               get_rule_expr((Node *) nullifexpr->args, context, true);
-                               appendStringInfoChar(buf, ')');
-                       }
-                       break;
-
                case T_NullTest:
                        {
                                NullTest   *ntest = (NullTest *) node;
index 5cad1b8..33f300b 100644 (file)
@@ -285,7 +285,7 @@ var_eq_const(VariableStatData *vardata, Oid operator,
                        FmgrInfo        eqproc;
 
                        fmgr_info(get_opcode(operator), &eqproc);
-                       fmgr_info_collation(DEFAULT_COLLATION_OID, &eqproc);
+                       fmgr_info_set_collation(DEFAULT_COLLATION_OID, &eqproc);
 
                        for (i = 0; i < nvalues; i++)
                        {
@@ -515,7 +515,7 @@ scalarineqsel(PlannerInfo *root, Oid operator, bool isgt,
        stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple);
 
        fmgr_info(get_opcode(operator), &opproc);
-       fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc);
+       fmgr_info_set_collation(DEFAULT_COLLATION_OID, &opproc);
 
        /*
         * If we have most-common-values info, add up the fractions of the MCV
@@ -1252,7 +1252,7 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype, bool negate)
 
                /* Try to use the histogram entries to get selectivity */
                fmgr_info(get_opcode(operator), &opproc);
-               fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc);
+               fmgr_info_set_collation(DEFAULT_COLLATION_OID, &opproc);
 
                selec = histogram_selectivity(&vardata, &opproc, constval, true,
                                                                          10, 1, &hist_size);
@@ -1701,7 +1701,7 @@ scalararraysel(PlannerInfo *root,
        if (!oprsel)
                return (Selectivity) 0.5;
        fmgr_info(oprsel, &oprselproc);
-       fmgr_info_collation(DEFAULT_COLLATION_OID, &oprselproc);
+       fmgr_info_set_collation(DEFAULT_COLLATION_OID, &oprselproc);
 
        /* deconstruct the expression */
        Assert(list_length(clause->args) == 2);
@@ -1839,6 +1839,7 @@ scalararraysel(PlannerInfo *root,
                dummyexpr = makeNode(CaseTestExpr);
                dummyexpr->typeId = nominal_element_type;
                dummyexpr->typeMod = -1;
+               dummyexpr->collation = clause->inputcollid;
                args = list_make2(leftop, dummyexpr);
                if (is_join_clause)
                        s2 = DatumGetFloat8(FunctionCall5(&oprselproc,
@@ -2118,7 +2119,7 @@ eqjoinsel_inner(Oid operator,
                                        nmatches;
 
                fmgr_info(get_opcode(operator), &eqproc);
-               fmgr_info_collation(DEFAULT_COLLATION_OID, &eqproc);
+               fmgr_info_set_collation(DEFAULT_COLLATION_OID, &eqproc);
                hasmatch1 = (bool *) palloc0(nvalues1 * sizeof(bool));
                hasmatch2 = (bool *) palloc0(nvalues2 * sizeof(bool));
 
@@ -2341,7 +2342,7 @@ eqjoinsel_semi(Oid operator,
                                        nmatches;
 
                fmgr_info(get_opcode(operator), &eqproc);
-               fmgr_info_collation(DEFAULT_COLLATION_OID, &eqproc);
+               fmgr_info_set_collation(DEFAULT_COLLATION_OID, &eqproc);
                hasmatch1 = (bool *) palloc0(nvalues1 * sizeof(bool));
                hasmatch2 = (bool *) palloc0(nvalues2 * sizeof(bool));
 
@@ -4484,7 +4485,7 @@ get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop,
                FmgrInfo        opproc;
 
                fmgr_info(get_opcode(sortop), &opproc);
-               fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc);
+               fmgr_info_set_collation(DEFAULT_COLLATION_OID, &opproc);
 
                for (i = 0; i < nvalues; i++)
                {
@@ -5111,7 +5112,7 @@ prefix_selectivity(PlannerInfo *root, VariableStatData *vardata,
        if (cmpopr == InvalidOid)
                elog(ERROR, "no >= operator for opfamily %u", opfamily);
        fmgr_info(get_opcode(cmpopr), &opproc);
-       fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc);
+       fmgr_info_set_collation(DEFAULT_COLLATION_OID, &opproc);
 
        prefixsel = ineq_histogram_selectivity(root, vardata, &opproc, true,
                                                                                   prefixcon->constvalue,
@@ -5133,7 +5134,7 @@ prefix_selectivity(PlannerInfo *root, VariableStatData *vardata,
        if (cmpopr == InvalidOid)
                elog(ERROR, "no < operator for opfamily %u", opfamily);
        fmgr_info(get_opcode(cmpopr), &opproc);
-       fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc);
+       fmgr_info_set_collation(DEFAULT_COLLATION_OID, &opproc);
 
        greaterstrcon = make_greater_string(prefixcon, &opproc);
        if (greaterstrcon)
index 72695fe..6647fe9 100644 (file)
@@ -71,6 +71,7 @@ typedef struct
     bool        fn_strict;  /* function is "strict" (NULL in => NULL out) */
     bool        fn_retset;  /* function returns a set (over multiple calls) */
     unsigned char fn_stats; /* collect stats if track_functions > this */
+    Oid         fn_collation;   /* collation that function should use */
     void       *fn_extra;   /* extra space for use by handler */
     MemoryContext fn_mcxt;  /* memory context to store fn_extra in */
     Node       *fn_expr;    /* expression parse tree for call, or NULL */
@@ -89,12 +90,16 @@ is the number of arguments expected by the function, fn_strict is its
 strictness flag, and fn_retset shows whether it returns a set; all of
 these values come from the function's pg_proc entry.  fn_stats is also
 set up to control whether or not to track runtime statistics for calling
-this function.  If the function is being called as part of a SQL expression,
+this function.
+
+fn_collation supplies the collation to use for collation-sensitive
+functions.  If the function is being called as part of a SQL expression,
 fn_expr will point to the expression parse tree for the function call; this
 can be used to extract parse-time knowledge about the actual arguments.
-
-FmgrInfo already exists in the current code, but has fewer fields.  This
-change should be transparent at the source-code level.
+Note that these two fields really are information about the arguments
+rather than information about the function, but it's proven to be more
+convenient to keep them in FmgrInfo than in FunctionCallInfoData where
+they might more logically go.
 
 
 During a call of a function, the following data structure is created
index a115a29..e193e56 100644 (file)
@@ -192,7 +192,7 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,
         * elogs.
         */
        finfo->fn_oid = InvalidOid;
-       finfo->fn_collation = InvalidOid;
+       finfo->fn_collation = InvalidOid;               /* caller may set this later */
        finfo->fn_extra = NULL;
        finfo->fn_mcxt = mcxt;
        finfo->fn_expr = NULL;          /* caller may set this later */
@@ -421,25 +421,6 @@ fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
 }
 
 /*
- * Initialize the fn_collation field
- */
-void
-fmgr_info_collation(Oid collationId, FmgrInfo *finfo)
-{
-       finfo->fn_collation = collationId;
-}
-
-/*
- * Initialize the fn_expr field and set the collation based on it
- */
-void
-fmgr_info_expr(Node *expr, FmgrInfo *finfo)
-{
-       finfo->fn_expr = expr;
-       finfo->fn_collation = exprCollation(expr);
-}
-
-/*
  * Fetch and validate the information record for the given external function.
  * The function is specified by a handle for the containing library
  * (obtained from load_external_function) as well as the function name.
@@ -920,6 +901,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
 
                fmgr_info_cxt_security(fcinfo->flinfo->fn_oid, &fcache->flinfo,
                                                           fcinfo->flinfo->fn_mcxt, true);
+               fcache->flinfo.fn_collation = fcinfo->flinfo->fn_collation;
                fcache->flinfo.fn_expr = fcinfo->flinfo->fn_expr;
 
                tuple = SearchSysCache1(PROCOID,
@@ -1293,6 +1275,11 @@ DirectFunctionCall9(PGFunction func, Datum arg1, Datum arg2,
        return result;
 }
 
+
+/*
+ * These are the same as DirectFunctionCallN except that a nonzero
+ * collation can be specified.  No other fields of FmgrInfo are made valid.
+ */
 Datum
 DirectFunctionCall1WithCollation(PGFunction func, Oid collation, Datum arg1)
 {
@@ -1300,8 +1287,9 @@ DirectFunctionCall1WithCollation(PGFunction func, Oid collation, Datum arg1)
        FmgrInfo        flinfo;
        Datum           result;
 
+       MemSet(&flinfo, 0, sizeof(flinfo));
+       flinfo.fn_collation = collation;
        InitFunctionCallInfoData(fcinfo, &flinfo, 1, NULL, NULL);
-       fcinfo.flinfo->fn_collation = collation;
 
        fcinfo.arg[0] = arg1;
        fcinfo.argnull[0] = false;
@@ -1316,14 +1304,16 @@ DirectFunctionCall1WithCollation(PGFunction func, Oid collation, Datum arg1)
 }
 
 Datum
-DirectFunctionCall2WithCollation(PGFunction func, Oid collation, Datum arg1, Datum arg2)
+DirectFunctionCall2WithCollation(PGFunction func, Oid collation,
+                                                                Datum arg1, Datum arg2)
 {
        FunctionCallInfoData fcinfo;
        FmgrInfo        flinfo;
        Datum           result;
 
+       MemSet(&flinfo, 0, sizeof(flinfo));
+       flinfo.fn_collation = collation;
        InitFunctionCallInfoData(fcinfo, &flinfo, 2, NULL, NULL);
-       fcinfo.flinfo->fn_collation = collation;
 
        fcinfo.arg[0] = arg1;
        fcinfo.arg[1] = arg2;
index 321b4e7..cad4a37 100644 (file)
@@ -411,6 +411,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
        bool            have_anyenum = false;
        Oid                     anyelement_type = InvalidOid;
        Oid                     anyarray_type = InvalidOid;
+       Oid                     anycollation;
        int                     i;
 
        /* See if there are any polymorphic outputs; quick out if not */
@@ -468,6 +469,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
        /* If nothing found, parser messed up */
        if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
                return false;
+
        /* If needed, deduce one polymorphic type from the other */
        if (have_anyelement_result && !OidIsValid(anyelement_type))
                anyelement_type = resolve_generic_type(ANYELEMENTOID,
@@ -486,6 +488,24 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
        if (have_anyenum && !type_is_enum(anyelement_type))
                return false;
 
+       /*
+        * Identify the collation to use for polymorphic OUT parameters.
+        * (It'll necessarily be the same for both anyelement and anyarray.)
+        */
+       anycollation = get_typcollation(OidIsValid(anyelement_type) ? anyelement_type : anyarray_type);
+       if (OidIsValid(anycollation))
+       {
+               /*
+                * The types are collatable, so consider whether to use a nondefault
+                * collation.  We do so if we can identify the input collation used
+                * for the function.
+                */
+               Oid             inputcollation = exprInputCollation(call_expr);
+
+               if (OidIsValid(inputcollation))
+                       anycollation = inputcollation;
+       }
+
        /* And finally replace the tuple column types as needed */
        for (i = 0; i < natts; i++)
        {
@@ -499,6 +519,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
                                                                   anyelement_type,
                                                                   -1,
                                                                   0);
+                               TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
                                break;
                        case ANYARRAYOID:
                                TupleDescInitEntry(tupdesc, i + 1,
@@ -506,13 +527,11 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
                                                                   anyarray_type,
                                                                   -1,
                                                                   0);
+                               TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
                                break;
                        default:
                                break;
                }
-               /* Set collation based on actual argument types */
-               TupleDescInitEntryCollation(tupdesc, i + 1,
-                                                                       exprCollation(call_expr));
        }
 
        return true;
index f2449ea..56185fc 100644 (file)
@@ -836,7 +836,7 @@ tuplesort_begin_datum(Oid datumType,
                elog(ERROR, "operator %u is not a valid ordering operator",
                         sortOperator);
        fmgr_info(sortFunction, &state->sortOpFn);
-       fmgr_info_collation(sortCollation, &state->sortOpFn);
+       fmgr_info_set_collation(sortCollation, &state->sortOpFn);
 
        /* set ordering flags */
        state->sortFnFlags = reverse ? SK_BT_DESC : 0;
index 6570156..c10de53 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201103112
+#define CATALOG_VERSION_NO     201103191
 
 #endif
index 7539aca..9e5224d 100644 (file)
@@ -40,6 +40,11 @@ typedef Datum (*PGFunction) (FunctionCallInfo fcinfo);
  * before a function can be called through fmgr.  If the same function is
  * to be called multiple times, the lookup need be done only once and the
  * info struct saved for re-use.
+ *
+ * Note that fn_collation and fn_expr really are parse-time-determined
+ * information about the arguments, rather than about the function itself.
+ * But it's convenient to store them here rather than in FunctionCallInfoData,
+ * where they might more logically belong.
  */
 typedef struct FmgrInfo
 {
@@ -50,7 +55,7 @@ typedef struct FmgrInfo
        bool            fn_strict;              /* function is "strict" (NULL in => NULL out) */
        bool            fn_retset;              /* function returns a set */
        unsigned char fn_stats;         /* collect stats if track_functions > this */
-       Oid                     fn_collation;   /* collation to use */
+       Oid                     fn_collation;   /* collation that function should use */
        void       *fn_extra;           /* extra space for use by handler */
        MemoryContext fn_mcxt;          /* memory context to store fn_extra in */
        fmNodePtr       fn_expr;                /* expression parse tree for call, or NULL */
@@ -84,15 +89,11 @@ extern void fmgr_info(Oid functionId, FmgrInfo *finfo);
 extern void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo,
                          MemoryContext mcxt);
 
-/*
- * Initialize the fn_collation field
- */
-extern void fmgr_info_collation(Oid collationId, FmgrInfo *finfo);
-
-/*
- * Initialize the fn_expr field and set the collation based on it
- */
-extern void fmgr_info_expr(fmNodePtr expr, FmgrInfo *finfo);
+/* Macros for setting the fn_collation and fn_expr fields */
+#define fmgr_info_set_collation(collationId, finfo) \
+       ((finfo)->fn_collation = (collationId))
+#define fmgr_info_set_expr(expr, finfo) \
+       ((finfo)->fn_expr = (expr))
 
 /*
  * Copy an FmgrInfo struct
@@ -147,6 +148,12 @@ extern void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo,
 #define PG_FUNCTION_ARGS       FunctionCallInfo fcinfo
 
 /*
+ * Get collation function should use.
+ */
+#define PG_GET_COLLATION() \
+       (fcinfo->flinfo ? fcinfo->flinfo->fn_collation : InvalidOid)
+
+/*
  * Get number of arguments passed to function.
  */
 #define PG_NARGS() (fcinfo->nargs)
@@ -307,7 +314,6 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena * datum);
 #define PG_RETURN_VARCHAR_P(x) PG_RETURN_POINTER(x)
 #define PG_RETURN_HEAPTUPLEHEADER(x)  PG_RETURN_POINTER(x)
 
-#define PG_GET_COLLATION()             (fcinfo->flinfo ? fcinfo->flinfo->fn_collation : InvalidOid)
 
 /*-------------------------------------------------------------------------
  *             Support for detecting call convention of dynamically-loaded functions
@@ -450,7 +456,7 @@ extern Datum DirectFunctionCall9(PGFunction func, Datum arg1, Datum arg2,
                                        Datum arg6, Datum arg7, Datum arg8,
                                        Datum arg9);
 
-/* the same but passing a collation */
+/* The same, but passing a collation to use */
 extern Datum DirectFunctionCall1WithCollation(PGFunction func, Oid collation,
                                                                                          Datum arg1);
 extern Datum DirectFunctionCall2WithCollation(PGFunction func, Oid collation,
index 6691b0d..ead7c40 100644 (file)
@@ -62,7 +62,7 @@ extern Expr *makeBoolExpr(BoolExprType boolop, List *args, int location);
 extern Alias *makeAlias(const char *aliasname, List *colnames);
 
 extern RelabelType *makeRelabelType(Expr *arg, Oid rtype, int32 rtypmod,
-                               CoercionForm rformat);
+                                                                       Oid rcollid, CoercionForm rformat);
 
 extern RangeVar *makeRangeVar(char *schemaname, char *relname, int location);
 
@@ -70,8 +70,8 @@ extern TypeName *makeTypeName(char *typnam);
 extern TypeName *makeTypeNameFromNameList(List *names);
 extern TypeName *makeTypeNameFromOid(Oid typeOid, int32 typmod);
 
-extern FuncExpr *makeFuncExpr(Oid funcid, Oid rettype,
-                        List *args, Oid collid, CoercionForm fformat);
+extern FuncExpr *makeFuncExpr(Oid funcid, Oid rettype, List *args,
+                        Oid funccollid, Oid inputcollid, CoercionForm fformat);
 
 extern DefElem *makeDefElem(char *name, Node *arg);
 extern DefElem *makeDefElemExtended(char *nameSpace, char *name, Node *arg,
index 32f09f6..591f2a9 100644 (file)
 #define QTW_IGNORE_CTE_SUBQUERIES      0x02            /* subqueries in cteList */
 #define QTW_IGNORE_RC_SUBQUERIES       0x03            /* both of above */
 #define QTW_IGNORE_JOINALIASES         0x04            /* JOIN alias var lists */
-#define QTW_EXAMINE_RTES                       0x08            /* examine RTEs */
-#define QTW_DONT_COPY_QUERY                    0x10            /* do not copy top Query */
+#define QTW_IGNORE_RANGE_TABLE         0x08            /* skip rangetable entirely */
+#define QTW_EXAMINE_RTES                       0x10            /* examine RTEs */
+#define QTW_DONT_COPY_QUERY                    0x20            /* do not copy top Query */
 
 
 extern Oid     exprType(Node *expr);
 extern int32 exprTypmod(Node *expr);
-extern Oid     exprCollation(Node *expr);
-extern Oid coercion_expression_result_collation(Oid resulttype, Node *arg);
 extern bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod);
 extern bool expression_returns_set(Node *clause);
 
+extern Oid     exprCollation(Node *expr);
+extern Oid     exprInputCollation(Node *expr);
+extern void exprSetCollation(Node *expr, Oid collation);
+extern void exprSetInputCollation(Node *expr, Oid inputcollation);
+
 extern int     exprLocation(Node *expr);
 
 extern bool expression_tree_walker(Node *node, bool (*walker) (),
index 8ed819b..d8bc6b8 100644 (file)
@@ -137,6 +137,7 @@ typedef enum NodeTag
        T_NamedArgExpr,
        T_OpExpr,
        T_DistinctExpr,
+       T_NullIfExpr,
        T_ScalarArrayOpExpr,
        T_BoolExpr,
        T_SubLink,
@@ -158,7 +159,6 @@ typedef enum NodeTag
        T_CoalesceExpr,
        T_MinMaxExpr,
        T_XmlExpr,
-       T_NullIfExpr,
        T_NullTest,
        T_BooleanTest,
        T_CoerceToDomain,
index 904bd5e..b7e7104 100644 (file)
@@ -787,7 +787,12 @@ typedef struct RangeTblEntry
  *
  * In an ORDER BY item, all fields must be valid.  (The eqop isn't essential
  * here, but it's cheap to get it along with the sortop, and requiring it
- * to be valid eases comparisons to grouping items.)
+ * to be valid eases comparisons to grouping items.)  Note that this isn't
+ * actually enough information to determine an ordering: if the sortop is
+ * collation-sensitive, a collation OID is needed too.  We don't store the
+ * collation in SortGroupClause because it's not available at the time the
+ * parser builds the SortGroupClause; instead, consult the exposed collation
+ * of the referenced targetlist expression to find out what it is.
  *
  * In a grouping item, eqop must be valid.     If the eqop is a btree equality
  * operator, then sortop should be set to a compatible ordering operator.
index 41fd56e..609f253 100644 (file)
@@ -139,7 +139,7 @@ typedef struct Var
                                                                 * all */
        Oid                     vartype;                /* pg_type OID for the type of this var */
        int32           vartypmod;              /* pg_attribute typmod value */
-       Oid                     varcollid;              /* collation */
+       Oid                     varcollid;              /* OID of collation, or InvalidOid if none */
        Index           varlevelsup;    /* for subquery variables referencing outer
                                                                 * relations; 0 in a normal var, >0 means N
                                                                 * levels up */
@@ -156,7 +156,7 @@ typedef struct Const
        Expr            xpr;
        Oid                     consttype;              /* pg_type OID of the constant's datatype */
        int32           consttypmod;    /* typmod value, if any */
-       Oid                     constcollid;    /* collation */
+       Oid                     constcollid;    /* OID of collation, or InvalidOid if none */
        int                     constlen;               /* typlen of the constant's datatype */
        Datum           constvalue;             /* the constant's value */
        bool            constisnull;    /* whether the constant is null (if true,
@@ -207,7 +207,7 @@ typedef struct Param
        int                     paramid;                /* numeric ID for parameter */
        Oid                     paramtype;              /* pg_type OID of parameter's datatype */
        int32           paramtypmod;    /* typmod value, if known */
-       Oid                     paramcollation; /* parameter's collation */
+       Oid                     paramcollid;    /* OID of collation, or InvalidOid if none */
        int                     location;               /* token location, or -1 if unknown */
 } Param;
 
@@ -231,12 +231,13 @@ typedef struct Aggref
        Expr            xpr;
        Oid                     aggfnoid;               /* pg_proc Oid of the aggregate */
        Oid                     aggtype;                /* type Oid of result of the aggregate */
+       Oid                     aggcollid;              /* OID of collation of result */
+       Oid                     inputcollid;    /* OID of collation that function should use */
        List       *args;                       /* arguments and sort expressions */
        List       *aggorder;           /* ORDER BY (list of SortGroupClause) */
        List       *aggdistinct;        /* DISTINCT (list of SortGroupClause) */
        bool            aggstar;                /* TRUE if argument list was really '*' */
        Index           agglevelsup;    /* > 0 if agg belongs to outer query */
-       Oid                     collid;                 /* collation OID to use by function */
        int                     location;               /* token location, or -1 if unknown */
 } Aggref;
 
@@ -248,11 +249,12 @@ typedef struct WindowFunc
        Expr            xpr;
        Oid                     winfnoid;               /* pg_proc Oid of the function */
        Oid                     wintype;                /* type Oid of result of the window function */
+       Oid                     wincollid;              /* OID of collation of result */
+       Oid                     inputcollid;    /* OID of collation that function should use */
        List       *args;                       /* arguments to the window function */
        Index           winref;                 /* index of associated WindowClause */
        bool            winstar;                /* TRUE if argument list was really '*' */
        bool            winagg;                 /* is function a simple aggregate? */
-       Oid                     collid;                 /* collation OID to use by function */
        int                     location;               /* token location, or -1 if unknown */
 } WindowFunc;
 
@@ -284,7 +286,7 @@ typedef struct ArrayRef
        Oid                     refarraytype;   /* type of the array proper */
        Oid                     refelemtype;    /* type of the array elements */
        int32           reftypmod;              /* typmod of the array (and elements too) */
-       Oid                     refcollid;              /* collation */
+       Oid                     refcollid;              /* OID of collation, or InvalidOid if none */
        List       *refupperindexpr;/* expressions that evaluate to upper array
                                                                 * indexes */
        List       *reflowerindexpr;/* expressions that evaluate to lower array
@@ -329,8 +331,9 @@ typedef struct FuncExpr
        Oid                     funcresulttype; /* PG_TYPE OID of result value */
        bool            funcretset;             /* true if function returns set */
        CoercionForm funcformat;        /* how to display this function call */
+       Oid                     funccollid;             /* OID of collation of result */
+       Oid                     inputcollid;    /* OID of collation that function should use */
        List       *args;                       /* arguments to the function */
-       Oid                     collid;                 /* collation OID to use by function */
        int                     location;               /* token location, or -1 if unknown */
 } FuncExpr;
 
@@ -373,8 +376,9 @@ typedef struct OpExpr
        Oid                     opfuncid;               /* PG_PROC OID of underlying function */
        Oid                     opresulttype;   /* PG_TYPE OID of result value */
        bool            opretset;               /* true if operator returns set */
+       Oid                     opcollid;               /* OID of collation of result */
+       Oid                     inputcollid;    /* OID of collation that operator should use */
        List       *args;                       /* arguments to the operator (1 or 2) */
-       Oid                     collid;                 /* collation OID to use by operator */
        int                     location;               /* token location, or -1 if unknown */
 } OpExpr;
 
@@ -391,6 +395,14 @@ typedef struct OpExpr
 typedef OpExpr DistinctExpr;
 
 /*
+ * NullIfExpr - a NULLIF expression
+ *
+ * Like DistinctExpr, this is represented the same as an OpExpr referencing
+ * the "=" operator for x and y.
+ */
+typedef OpExpr NullIfExpr;
+
+/*
  * ScalarArrayOpExpr - expression node for "scalar op ANY/ALL (array)"
  *
  * The operator must yield boolean.  It is applied to the left operand
@@ -398,7 +410,7 @@ typedef OpExpr DistinctExpr;
  * with OR or AND (for ANY or ALL respectively).  The node representation
  * is almost the same as for the underlying operator, but we need a useOr
  * flag to remember whether it's ANY or ALL, and we don't have to store
- * the result type because it must be boolean.
+ * the result type (or the collation) because it must be boolean.
  */
 typedef struct ScalarArrayOpExpr
 {
@@ -406,8 +418,8 @@ typedef struct ScalarArrayOpExpr
        Oid                     opno;                   /* PG_OPERATOR OID of the operator */
        Oid                     opfuncid;               /* PG_PROC OID of underlying function */
        bool            useOr;                  /* true for ANY, false for ALL */
+       Oid                     inputcollid;    /* OID of collation that operator should use */
        List       *args;                       /* the scalar and array operands */
-       Oid                     collid;                 /* collation OID to use by operator */
        int                     location;               /* token location, or -1 if unknown */
 } ScalarArrayOpExpr;
 
@@ -602,7 +614,7 @@ typedef struct FieldSelect
        Oid                     resulttype;             /* type of the field (result type of this
                                                                 * node) */
        int32           resulttypmod;   /* output typmod (usually -1) */
-       Oid                     resultcollation;/* collation of the field */
+       Oid                     resultcollid;   /* OID of collation of the field */
 } FieldSelect;
 
 /* ----------------
@@ -627,7 +639,7 @@ typedef struct FieldStore
        List       *newvals;            /* new value(s) for field(s) */
        List       *fieldnums;          /* integer list of field attnums */
        Oid                     resulttype;             /* type of result (same as type of arg) */
-       /* Like RowExpr, we deliberately omit a typmod here */
+       /* Like RowExpr, we deliberately omit a typmod and collation here */
 } FieldStore;
 
 /* ----------------
@@ -649,6 +661,7 @@ typedef struct RelabelType
        Expr       *arg;                        /* input expression */
        Oid                     resulttype;             /* output type of coercion expression */
        int32           resulttypmod;   /* output typmod (usually -1) */
+       Oid                     resultcollid;   /* OID of collation, or InvalidOid if none */
        CoercionForm relabelformat; /* how to display this node */
        int                     location;               /* token location, or -1 if unknown */
 } RelabelType;
@@ -668,6 +681,7 @@ typedef struct CoerceViaIO
        Expr       *arg;                        /* input expression */
        Oid                     resulttype;             /* output type of coercion */
        /* output typmod is not stored, but is presumed -1 */
+       Oid                     resultcollid;   /* OID of collation, or InvalidOid if none */
        CoercionForm coerceformat;      /* how to display this node */
        int                     location;               /* token location, or -1 if unknown */
 } CoerceViaIO;
@@ -691,6 +705,7 @@ typedef struct ArrayCoerceExpr
        Oid                     elemfuncid;             /* OID of element coercion function, or 0 */
        Oid                     resulttype;             /* output type of coercion (an array type) */
        int32           resulttypmod;   /* output typmod (also element typmod) */
+       Oid                     resultcollid;   /* OID of collation, or InvalidOid if none */
        bool            isExplicit;             /* conversion semantics flag to pass to func */
        CoercionForm coerceformat;      /* how to display this node */
        int                     location;               /* token location, or -1 if unknown */
@@ -713,13 +728,16 @@ typedef struct ConvertRowtypeExpr
        Expr            xpr;
        Expr       *arg;                        /* input expression */
        Oid                     resulttype;             /* output type (always a composite type) */
-       /* result typmod is not stored, but must be -1; see RowExpr comments */
+       /* Like RowExpr, we deliberately omit a typmod and collation here */
        CoercionForm convertformat; /* how to display this node */
        int                     location;               /* token location, or -1 if unknown */
 } ConvertRowtypeExpr;
 
 /*----------
  * CollateExpr - COLLATE
+ *
+ * The planner replaces CollateExpr with RelabelType during expression
+ * preprocessing, so execution never sees a CollateExpr.
  *----------
  */
 typedef struct CollateExpr
@@ -756,7 +774,7 @@ typedef struct CaseExpr
 {
        Expr            xpr;
        Oid                     casetype;               /* type of expression result */
-       Oid                     casecollation;  /* collation of expression result */
+       Oid                     casecollid;             /* OID of collation, or InvalidOid if none */
        Expr       *arg;                        /* implicit equality comparison argument */
        List       *args;                       /* the arguments (list of WHEN clauses) */
        Expr       *defresult;          /* the default result (ELSE clause) */
@@ -802,6 +820,7 @@ typedef struct ArrayExpr
 {
        Expr            xpr;
        Oid                     array_typeid;   /* type of expression result */
+       Oid                     array_collid;   /* OID of collation, or InvalidOid if none */
        Oid                     element_typeid; /* common type of array elements */
        List       *elements;           /* the array elements or sub-arrays */
        bool            multidims;              /* true if elements are sub-arrays */
@@ -838,6 +857,9 @@ typedef struct RowExpr
         * associated with specific RECORD types at runtime, it will differ for
         * different backends, and so cannot safely be stored in stored
         * parsetrees.  We must assume typmod -1 for a RowExpr node.
+        *
+        * We don't need to store a collation either.  The result type is
+        * necessarily composite, and composite types never have a collation.
         */
        CoercionForm row_format;        /* how to display this node */
        List       *colnames;           /* list of String, or NIL */
@@ -875,7 +897,7 @@ typedef struct RowCompareExpr
        RowCompareType rctype;          /* LT LE GE or GT, never EQ or NE */
        List       *opnos;                      /* OID list of pairwise comparison ops */
        List       *opfamilies;         /* OID list of containing operator families */
-       List       *collids;            /* OID list of collations for the comparisons */
+       List       *inputcollids;       /* OID list of collations for comparisons */
        List       *largs;                      /* the left-hand input arguments */
        List       *rargs;                      /* the right-hand input arguments */
 } RowCompareExpr;
@@ -887,7 +909,7 @@ typedef struct CoalesceExpr
 {
        Expr            xpr;
        Oid                     coalescetype;   /* type of expression result */
-       Oid                     coalescecollation;      /* collation of expression result */
+       Oid                     coalescecollid; /* OID of collation, or InvalidOid if none */
        List       *args;                       /* the arguments */
        int                     location;               /* token location, or -1 if unknown */
 } CoalesceExpr;
@@ -905,9 +927,10 @@ typedef struct MinMaxExpr
 {
        Expr            xpr;
        Oid                     minmaxtype;             /* common type of arguments and result */
+       Oid                     minmaxcollid;   /* OID of collation of result */
+       Oid                     inputcollid;    /* OID of collation that function should use */
        MinMaxOp        op;                             /* function to execute */
        List       *args;                       /* the arguments */
-       Oid                     collid;                 /* collation to use */
        int                     location;               /* token location, or -1 if unknown */
 } MinMaxExpr;
 
@@ -917,6 +940,10 @@ typedef struct MinMaxExpr
  * 'name' carries the "NAME foo" argument (already XML-escaped).
  * 'named_args' and 'arg_names' represent an xml_attribute list.
  * 'args' carries all other arguments.
+ *
+ * Note: result type/typmod/collation are not stored, but can be deduced
+ * from the XmlExprOp.  The type/typmod fields are just used for display
+ * purposes, and are NOT the true result type of the node.
  */
 typedef enum XmlExprOp
 {
@@ -945,19 +972,11 @@ typedef struct XmlExpr
        List       *arg_names;          /* parallel list of Value strings */
        List       *args;                       /* list of expressions */
        XmlOptionType xmloption;        /* DOCUMENT or CONTENT */
-       Oid                     type;                   /* target type for XMLSERIALIZE */
+       Oid                     type;                   /* target type/typmod for XMLSERIALIZE */
        int32           typmod;
        int                     location;               /* token location, or -1 if unknown */
 } XmlExpr;
 
-/*
- * NullIfExpr - a NULLIF expression
- *
- * Like DistinctExpr, this is represented the same as an OpExpr referencing
- * the "=" operator for x and y.
- */
-typedef OpExpr NullIfExpr;
-
 /* ----------------
  * NullTest
  *
@@ -1018,6 +1037,7 @@ typedef struct CoerceToDomain
        Expr       *arg;                        /* input expression */
        Oid                     resulttype;             /* domain type ID (result type) */
        int32           resulttypmod;   /* output typmod (currently always -1) */
+       Oid                     resultcollid;   /* OID of collation, or InvalidOid if none */
        CoercionForm coercionformat;    /* how to display this node */
        int                     location;               /* token location, or -1 if unknown */
 } CoerceToDomain;
@@ -1036,6 +1056,7 @@ typedef struct CoerceToDomainValue
        Expr            xpr;
        Oid                     typeId;                 /* type for substituted value */
        int32           typeMod;                /* typemod for substituted value */
+       Oid                     collation;              /* collation for the substituted value */
        int                     location;               /* token location, or -1 if unknown */
 } CoerceToDomainValue;
 
@@ -1051,7 +1072,7 @@ typedef struct SetToDefault
        Expr            xpr;
        Oid                     typeId;                 /* type for substituted value */
        int32           typeMod;                /* typemod for substituted value */
-       Oid                     collid;                 /* collation for the substituted value */
+       Oid                     collation;              /* collation for the substituted value */
        int                     location;               /* token location, or -1 if unknown */
 } SetToDefault;
 
index 8bcc400..7fee3f1 100644 (file)
@@ -488,10 +488,13 @@ typedef struct IndexOptInfo
  * require merging two existing EquivalenceClasses.  At the end of the qual
  * distribution process, we have sets of values that are known all transitively
  * equal to each other, where "equal" is according to the rules of the btree
- * operator family(s) shown in ec_opfamilies.  (We restrict an EC to contain
- * only equalities whose operators belong to the same set of opfamilies.  This
- * could probably be relaxed, but for now it's not worth the trouble, since
- * nearly all equality operators belong to only one btree opclass anyway.)
+ * operator family(s) shown in ec_opfamilies, as well as the collation shown
+ * by ec_collation.  (We restrict an EC to contain only equalities whose
+ * operators belong to the same set of opfamilies.  This could probably be
+ * relaxed, but for now it's not worth the trouble, since nearly all equality
+ * operators belong to only one btree opclass anyway.  Similarly, we suppose
+ * that all or none of the input datatypes are collatable, so that a single
+ * collation value is sufficient.)
  *
  * We also use EquivalenceClasses as the base structure for PathKeys, letting
  * us represent knowledge about different sort orderings being equivalent.
@@ -520,6 +523,7 @@ typedef struct EquivalenceClass
        NodeTag         type;
 
        List       *ec_opfamilies;      /* btree operator family OIDs */
+       Oid                     ec_collation;   /* collation, if datatypes are collatable */
        List       *ec_members;         /* list of EquivalenceMembers */
        List       *ec_sources;         /* list of generating RestrictInfos */
        List       *ec_derives;         /* list of derived RestrictInfos */
@@ -574,9 +578,10 @@ typedef struct EquivalenceMember
  * represents the primary sort key, the second the first secondary sort key,
  * etc.  The value being sorted is represented by linking to an
  * EquivalenceClass containing that value and including pk_opfamily among its
- * ec_opfamilies.  This is a convenient method because it makes it trivial
- * to detect equivalent and closely-related orderings. (See optimizer/README
- * for more information.)
+ * ec_opfamilies.  The EquivalenceClass tells which collation to use, too.
+ * This is a convenient method because it makes it trivial to detect
+ * equivalent and closely-related orderings. (See optimizer/README for more
+ * information.)
  *
  * Note: pk_strategy is either BTLessStrategyNumber (for ASC) or
  * BTGreaterStrategyNumber (for DESC). We assume that all ordering-capable
@@ -589,7 +594,6 @@ typedef struct PathKey
 
        EquivalenceClass *pk_eclass;    /* the value that is ordered */
        Oid                     pk_opfamily;    /* btree opfamily defining the ordering */
-       Oid                     pk_collation;   /* collation */
        int                     pk_strategy;    /* sort direction (ASC or DESC) */
        bool            pk_nulls_first; /* do NULLs come before normal values? */
 } PathKey;
@@ -1117,7 +1121,7 @@ typedef struct MergeScanSelCache
 {
        /* Ordering details (cache lookup key) */
        Oid                     opfamily;               /* btree opfamily defining the ordering */
-       Oid                     collation;
+       Oid                     collation;              /* collation for the ordering */
        int                     strategy;               /* sort direction (ASC or DESC) */
        bool            nulls_first;    /* do NULLs come before normal values? */
        /* Results */
index 9459572..7ae236d 100644 (file)
@@ -36,7 +36,8 @@ typedef struct
 
 
 extern Expr *make_opclause(Oid opno, Oid opresulttype, bool opretset,
-                         Expr *leftop, Expr *rightop);
+                         Expr *leftop, Expr *rightop,
+                         Oid opcollid, Oid inputcollid);
 extern Node *get_leftop(Expr *clause);
 extern Node *get_rightop(Expr *clause);
 
index ef769bf..06aed5f 100644 (file)
@@ -111,11 +111,14 @@ extern bool have_join_order_restriction(PlannerInfo *root,
  */
 extern bool process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
                                        bool below_outer_join);
+extern Expr *canonicalize_ec_expression(Expr *expr,
+                                                                               Oid req_type, Oid req_collation);
 extern void reconsider_outer_join_clauses(PlannerInfo *root);
 extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
                                                 Expr *expr,
-                                                Oid expr_datatype,
                                                 List *opfamilies,
+                                                Oid opcintype,
+                                                Oid collation,
                                                 Index sortref,
                                                 bool create_it);
 extern void generate_base_implied_equalities(PlannerInfo *root);
index 7e03bc9..d48bf39 100644 (file)
@@ -98,12 +98,14 @@ extern void distribute_restrictinfo_to_rels(PlannerInfo *root,
                                                                RestrictInfo *restrictinfo);
 extern void process_implied_equality(PlannerInfo *root,
                                                 Oid opno,
+                                                Oid collation,
                                                 Expr *item1,
                                                 Expr *item2,
                                                 Relids qualscope,
                                                 bool below_outer_join,
                                                 bool both_const);
 extern RestrictInfo *build_implied_join_equality(Oid opno,
+                                                       Oid collation,
                                                        Expr *item1,
                                                        Expr *item2,
                                                        Relids qualscope);
index 76d806d..543d2e7 100644 (file)
@@ -28,9 +28,9 @@ extern void build_aggregate_fnexprs(Oid *agg_input_types,
                                                int agg_num_inputs,
                                                Oid agg_state_type,
                                                Oid agg_result_type,
+                                               Oid agg_input_collation,
                                                Oid transfn_oid,
                                                Oid finalfn_oid,
-                                               Oid collation,
                                                Expr **transfnexpr,
                                                Expr **finalfnexpr);
 
index 9f79ad8..ceaff2f 100644 (file)
@@ -87,6 +87,4 @@ extern CoercionPathType find_coercion_pathway(Oid targetTypeId,
 extern CoercionPathType find_typmod_coercion_function(Oid typeId,
                                                          Oid *funcid);
 
-extern Oid select_common_collation(ParseState *pstate, List *exprs, bool none_ok);
-
 #endif   /* PARSE_COERCE_H */
diff --git a/src/include/parser/parse_collate.h b/src/include/parser/parse_collate.h
new file mode 100644 (file)
index 0000000..20acb43
--- /dev/null
@@ -0,0 +1,27 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_collate.h
+ *     Routines for assigning collation information.
+ *
+ *
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/parser/parse_collate.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PARSE_COLLATE_H
+#define PARSE_COLLATE_H
+
+#include "parser/parse_node.h"
+
+extern void assign_query_collations(ParseState *pstate, Query *query);
+
+extern void assign_list_collations(ParseState *pstate, List *exprs);
+
+extern void assign_expr_collations(ParseState *pstate, Node *expr);
+
+extern Oid select_common_collation(ParseState *pstate, List *exprs, bool none_ok);
+
+#endif   /* PARSE_COLLATE_H */
index a68f7cf..0ca7914 100644 (file)
@@ -146,7 +146,6 @@ extern ArrayRef *transformArraySubscripts(ParseState *pstate,
                                                 Oid arrayType,
                                                 Oid elementType,
                                                 int32 arrayTypMod,
-                                                Oid arrayColl,
                                                 List *indirection,
                                                 Node *assignFrom);
 extern Const *make_const(ParseState *pstate, Value *value, int location);
index 41188a2..a928e2f 100644 (file)
@@ -1238,6 +1238,7 @@ make_datum_param(PLpgSQL_expr *expr, int dno, int location)
        param->paramid = dno + 1;
        param->paramtype = exec_get_datum_type(estate, estate->datums[dno]);
        param->paramtypmod = -1;
+       param->paramcollid = get_typcollation(param->paramtype);
        param->location = location;
 
        return (Node *) param;
index 88cb8eb..1f4d5ac 100644 (file)
@@ -5303,6 +5303,18 @@ exec_simple_check_node(Node *node)
                                return TRUE;
                        }
 
+               case T_NullIfExpr:
+                       {
+                               NullIfExpr *expr = (NullIfExpr *) node;
+
+                               if (expr->opretset)
+                                       return FALSE;
+                               if (!exec_simple_check_node((Node *) expr->args))
+                                       return FALSE;
+
+                               return TRUE;
+                       }
+
                case T_ScalarArrayOpExpr:
                        {
                                ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -5350,9 +5362,6 @@ exec_simple_check_node(Node *node)
                case T_ConvertRowtypeExpr:
                        return exec_simple_check_node((Node *) ((ConvertRowtypeExpr *) node)->arg);
 
-               case T_CollateExpr:
-                       return exec_simple_check_node((Node *) ((CollateExpr *) node)->arg);
-
                case T_CaseExpr:
                        {
                                CaseExpr   *expr = (CaseExpr *) node;
@@ -5446,18 +5455,6 @@ exec_simple_check_node(Node *node)
                                return TRUE;
                        }
 
-               case T_NullIfExpr:
-                       {
-                               NullIfExpr *expr = (NullIfExpr *) node;
-
-                               if (expr->opretset)
-                                       return FALSE;
-                               if (!exec_simple_check_node((Node *) expr->args))
-                                       return FALSE;
-
-                               return TRUE;
-                       }
-
                case T_NullTest:
                        return exec_simple_check_node((Node *) ((NullTest *) node)->arg);
 
index 46a8207..f967998 100644 (file)
@@ -107,11 +107,11 @@ SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C";
 
 SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US.utf8";
 ERROR:  collation mismatch between explicit collations "C" and "en_US.utf8"
-LINE 1: ...ELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLL...
+LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
                                                              ^
 SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US";
 ERROR:  collation mismatch between explicit collations "C" and "en_US"
-LINE 1: ...ELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLL...
+LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e...
                                                              ^
 CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE.utf8";
 CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE.utf8"; -- fails
@@ -586,10 +586,7 @@ SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2
 (3 rows)
 
 SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail
-ERROR:  no collation was derived for the sort expression
-LINE 1: ...e_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2;
-                                                                     ^
-HINT:  Use the COLLATE clause to set the collation explicitly.
+ERROR:  locale operation to be invoked, but no collation was derived
 SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok
  a |  b  
 ---+-----
@@ -607,7 +604,7 @@ SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2;
 ERROR:  collation mismatch between implicit collations "en_US.utf8" and "C"
 LINE 1: SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collat...
                                                        ^
-HINT:  You can override the collation by applying the COLLATE clause to one or both expressions.
+HINT:  You can choose the collation by applying the COLLATE clause to one or both expressions.
 SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok
  a |  b  
 ---+-----
@@ -621,15 +618,25 @@ SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY
 ERROR:  collation mismatch between implicit collations "en_US.utf8" and "C"
 LINE 1: ...ELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM col...
                                                              ^
-HINT:  You can override the collation by applying the COLLATE clause to one or both expressions.
+HINT:  You can choose the collation by applying the COLLATE clause to one or both expressions.
 SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail
 ERROR:  collation mismatch between implicit collations "en_US.utf8" and "C"
 LINE 1: SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM colla...
                                                         ^
-HINT:  You can override the collation by applying the COLLATE clause to one or both expressions.
+HINT:  You can choose the collation by applying the COLLATE clause to one or both expressions.
 CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail
 ERROR:  no collation was derived for column "b" with collatable type text
 HINT:  Use the COLLATE clause to set the collation explicitly.
+-- ideally this would be a parse-time error, but for now it must be run-time:
+select x < y from collate_test10; -- fail
+ERROR:  locale operation to be invoked, but no collation was derived
+select x || y from collate_test10; -- ok, because || is not collation aware
+ ?column? 
+----------
+ hijhij
+ HIJHIJ
+(2 rows)
+
 -- collation mismatch between recursive and non-recursive term
 WITH RECURSIVE foo(x) AS
    (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x)
index 55af509..62e6681 100644 (file)
@@ -190,6 +190,10 @@ SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2;
 
 CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail
 
+-- ideally this would be a parse-time error, but for now it must be run-time:
+select x < y from collate_test10; -- fail
+select x || y from collate_test10; -- ok, because || is not collation aware
+
 -- collation mismatch between recursive and non-recursive term
 WITH RECURSIVE foo(x) AS
    (SELECT x FROM (VALUES('a' COLLATE "en_US"),('b')) t(x)