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