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