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),
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;
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))
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 */
}
/* 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))
{
#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"
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;
}
*/
expr = coerce_to_boolean(pstate, expr, "CHECK");
+ /*
+ * Take care of collations.
+ */
+ assign_expr_collations(pstate, expr);
+
/*
* Make sure no outside relations are referred to.
*/
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++)
{
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++)
#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"
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.
}
/*
- * 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;
/*
#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"
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++;
}
#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"
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.
#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"
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;
*/
expr = coerce_to_boolean(pstate, expr, "CHECK");
+ /*
+ * Fix up collation information.
+ */
+ assign_expr_collations(pstate, expr);
+
/*
* Make sure no outside relations are referred to.
*/
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);
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,
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,
* 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;
/* 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),
{
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;
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;
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;
{
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);
}
*/
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);
}
return HeapTupleGetDatum(result);
}
-/* ----------------------------------------------------------------
- * ExecEvalCollateExpr
- *
- * Evaluate a CollateExpr node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCollateExpr(GenericExprState *exprstate,
- ExprContext *econtext,
- bool *isNull, ExprDoneCond *isDone)
-{
- return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone);
-}
-
/* ----------------------------------------------------------------
* ExecEvalCase
*
{
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);
}
/* 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));
}
/*
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;
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;
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;
* 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;
* 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;
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;
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)
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)
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)
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,
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);
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 */
opfuncid, /* reg proc to use */
scanvalue); /* constant */
ScanKeyEntryInitializeCollation(this_scan_key,
- ((OpExpr *) clause)->collid);
+ ((OpExpr *) clause)->inputcollid);
}
else if (IsA(clause, RowCompareExpr))
{
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;
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
op_righttype,
BTORDER_PROC);
- collation = lfirst_oid(collids_cell);
+ inputcollation = lfirst_oid(collids_cell);
collids_cell = lnext(collids_cell);
/*
opfuncid, /* reg proc to use */
scanvalue); /* constant */
ScanKeyEntryInitializeCollation(this_sub_key,
- collation);
+ inputcollation);
n_sub_key++;
}
opfuncid, /* reg proc to use */
(Datum) 0); /* constant */
ScanKeyEntryInitializeCollation(this_scan_key,
- saop->collid);
+ saop->inputcollid);
}
else if (IsA(clause, NullTest))
{
/* 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)
/* 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,
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,
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);
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,
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;
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;
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;
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;
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;
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;
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;
COPY_SCALAR_FIELD(fieldnum);
COPY_SCALAR_FIELD(resulttype);
COPY_SCALAR_FIELD(resulttypmod);
- COPY_SCALAR_FIELD(resultcollation);
+ COPY_SCALAR_FIELD(resultcollid);
return newnode;
}
COPY_NODE_FIELD(arg);
COPY_SCALAR_FIELD(resulttype);
COPY_SCALAR_FIELD(resulttypmod);
+ COPY_SCALAR_FIELD(resultcollid);
COPY_SCALAR_FIELD(relabelformat);
COPY_LOCATION_FIELD(location);
COPY_NODE_FIELD(arg);
COPY_SCALAR_FIELD(resulttype);
+ COPY_SCALAR_FIELD(resultcollid);
COPY_SCALAR_FIELD(coerceformat);
COPY_LOCATION_FIELD(location);
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);
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);
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);
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);
CoalesceExpr *newnode = makeNode(CoalesceExpr);
COPY_SCALAR_FIELD(coalescetype);
- COPY_SCALAR_FIELD(coalescecollation);
+ COPY_SCALAR_FIELD(coalescecollid);
COPY_NODE_FIELD(args);
COPY_LOCATION_FIELD(location);
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;
return newnode;
}
-/*
- * _copyNullIfExpr (same as OpExpr)
- */
-static NullIfExpr *
-_copyNullIfExpr(NullIfExpr *from)
-{
- NullIfExpr *newnode = makeNode(NullIfExpr);
-
- COPY_SCALAR_FIELD(opno);
- COPY_SCALAR_FIELD(opfuncid);
- COPY_SCALAR_FIELD(opresulttype);
- COPY_SCALAR_FIELD(opretset);
- COPY_NODE_FIELD(args);
- COPY_LOCATION_FIELD(location);
-
- return newnode;
-}
-
/*
* _copyNullTest
*/
COPY_NODE_FIELD(arg);
COPY_SCALAR_FIELD(resulttype);
COPY_SCALAR_FIELD(resulttypmod);
+ COPY_SCALAR_FIELD(resultcollid);
COPY_SCALAR_FIELD(coercionformat);
COPY_LOCATION_FIELD(location);
COPY_SCALAR_FIELD(typeId);
COPY_SCALAR_FIELD(typeMod);
+ COPY_SCALAR_FIELD(collation);
COPY_LOCATION_FIELD(location);
return newnode;
COPY_SCALAR_FIELD(typeId);
COPY_SCALAR_FIELD(typeMod);
- COPY_SCALAR_FIELD(collid);
+ COPY_SCALAR_FIELD(collation);
COPY_LOCATION_FIELD(location);
return newnode;
/* 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);
case T_DistinctExpr:
retval = _copyDistinctExpr(from);
break;
+ case T_NullIfExpr:
+ retval = _copyNullIfExpr(from);
+ break;
case T_ScalarArrayOpExpr:
retval = _copyScalarArrayOpExpr(from);
break;
case T_XmlExpr:
retval = _copyXmlExpr(from);
break;
- case T_NullIfExpr:
- retval = _copyNullIfExpr(from);
- break;
case T_NullTest:
retval = _copyNullTest(from);
break;
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;
{
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;
{
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;
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;
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;
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;
return false;
COMPARE_SCALAR_FIELD(useOr);
+ COMPARE_SCALAR_FIELD(inputcollid);
COMPARE_NODE_FIELD(args);
- COMPARE_SCALAR_FIELD(collid);
COMPARE_LOCATION_FIELD(location);
return true;
COMPARE_SCALAR_FIELD(fieldnum);
COMPARE_SCALAR_FIELD(resulttype);
COMPARE_SCALAR_FIELD(resulttypmod);
- COMPARE_SCALAR_FIELD(resultcollation);
+ COMPARE_SCALAR_FIELD(resultcollid);
return true;
}
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
{
COMPARE_NODE_FIELD(arg);
COMPARE_SCALAR_FIELD(resulttype);
+ COMPARE_SCALAR_FIELD(resultcollid);
/*
* Special-case COERCE_DONTCARE, so that planner can build coercion nodes
COMPARE_SCALAR_FIELD(elemfuncid);
COMPARE_SCALAR_FIELD(resulttype);
COMPARE_SCALAR_FIELD(resulttypmod);
+ COMPARE_SCALAR_FIELD(resultcollid);
COMPARE_SCALAR_FIELD(isExplicit);
/*
_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);
_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);
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);
_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);
_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;
return true;
}
-static bool
-_equalNullIfExpr(NullIfExpr *a, NullIfExpr *b)
-{
- COMPARE_SCALAR_FIELD(opno);
-
- /*
- * Special-case opfuncid: it is allowable for it to differ if one node
- * contains zero and the other doesn't. This just means that the one node
- * isn't as far along in the parse/plan pipeline and hasn't had the
- * opfuncid cache filled yet.
- */
- if (a->opfuncid != b->opfuncid &&
- a->opfuncid != 0 &&
- b->opfuncid != 0)
- return false;
-
- COMPARE_SCALAR_FIELD(opresulttype);
- COMPARE_SCALAR_FIELD(opretset);
- COMPARE_NODE_FIELD(args);
- COMPARE_LOCATION_FIELD(location);
-
- return true;
-}
-
static bool
_equalNullTest(NullTest *a, NullTest *b)
{
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
{
COMPARE_SCALAR_FIELD(typeId);
COMPARE_SCALAR_FIELD(typeMod);
+ COMPARE_SCALAR_FIELD(collation);
COMPARE_LOCATION_FIELD(location);
return true;
{
COMPARE_SCALAR_FIELD(typeId);
COMPARE_SCALAR_FIELD(typeMod);
- COMPARE_SCALAR_FIELD(collid);
+ COMPARE_SCALAR_FIELD(collation);
COMPARE_LOCATION_FIELD(location);
return true;
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);
case T_DistinctExpr:
retval = _equalDistinctExpr(a, b);
break;
+ case T_NullIfExpr:
+ retval = _equalNullIfExpr(a, b);
+ break;
case T_ScalarArrayOpExpr:
retval = _equalScalarArrayOpExpr(a, b);
break;
case T_XmlExpr:
retval = _equalXmlExpr(a, b);
break;
- case T_NullIfExpr:
- retval = _equalNullIfExpr(a, b);
- break;
case T_NullTest:
retval = _equalNullTest(a, b);
break;
* 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;
* 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;
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;
case T_DistinctExpr:
type = ((DistinctExpr *) expr)->opresulttype;
break;
+ case T_NullIfExpr:
+ type = ((NullIfExpr *) expr)->opresulttype;
+ break;
case T_ScalarArrayOpExpr:
type = BOOLOID;
break;
else
type = XMLOID;
break;
- case T_NullIfExpr:
- type = exprType((Node *) linitial(((NullIfExpr *) expr)->args));
- break;
case T_NullTest:
type = BOOLOID;
break;
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;
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:
return -1;
}
+/*
+ * exprIsLengthCoercion
+ * Detect whether an expression tree is an application of a datatype's
+ * typmod-coercion function. Optionally extract the result's typmod.
+ *
+ * If coercedTypmod is not NULL, the typmod is stored there if the expression
+ * is a length-coercion function, else -1 is stored there.
+ *
+ * Note that a combined type-and-length coercion will be treated as a
+ * length coercion by this routine.
+ */
+bool
+exprIsLengthCoercion(Node *expr, int32 *coercedTypmod)
+{
+ if (coercedTypmod != NULL)
+ *coercedTypmod = -1; /* default result on failure */
+
+ /*
+ * Scalar-type length coercions are FuncExprs, array-type length coercions
+ * are ArrayCoerceExprs
+ */
+ if (expr && IsA(expr, FuncExpr))
+ {
+ FuncExpr *func = (FuncExpr *) expr;
+ int nargs;
+ Const *second_arg;
+
+ /*
+ * If it didn't come from a coercion context, reject.
+ */
+ if (func->funcformat != COERCE_EXPLICIT_CAST &&
+ func->funcformat != COERCE_IMPLICIT_CAST)
+ return false;
+
+ /*
+ * If it's not a two-argument or three-argument function with the
+ * second argument being an int4 constant, it can't have been created
+ * from a length coercion (it must be a type coercion, instead).
+ */
+ nargs = list_length(func->args);
+ if (nargs < 2 || nargs > 3)
+ return false;
+
+ second_arg = (Const *) lsecond(func->args);
+ if (!IsA(second_arg, Const) ||
+ second_arg->consttype != INT4OID ||
+ second_arg->constisnull)
+ return false;
+
+ /*
+ * OK, it is indeed a length-coercion function.
+ */
+ if (coercedTypmod != NULL)
+ *coercedTypmod = DatumGetInt32(second_arg->constvalue);
+
+ return true;
+ }
+
+ if (expr && IsA(expr, ArrayCoerceExpr))
+ {
+ ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) expr;
+
+ /* It's not a length coercion unless there's a nondefault typmod */
+ if (acoerce->resulttypmod < 0)
+ return false;
+
+ /*
+ * OK, it is indeed a length-coercion expression.
+ */
+ if (coercedTypmod != NULL)
+ *coercedTypmod = acoerce->resulttypmod;
+
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * expression_returns_set
+ * Test whether an expression returns a set result.
+ *
+ * Because we use expression_tree_walker(), this can also be applied to
+ * whole targetlists; it'll produce TRUE if any one of the tlist items
+ * returns a set.
+ */
+bool
+expression_returns_set(Node *clause)
+{
+ return expression_returns_set_walker(clause, NULL);
+}
+
+static bool
+expression_returns_set_walker(Node *node, void *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, FuncExpr))
+ {
+ FuncExpr *expr = (FuncExpr *) node;
+
+ if (expr->funcretset)
+ return true;
+ /* else fall through to check args */
+ }
+ if (IsA(node, OpExpr))
+ {
+ OpExpr *expr = (OpExpr *) node;
+
+ if (expr->opretset)
+ return true;
+ /* else fall through to check args */
+ }
+
+ /* Avoid recursion for some cases that can't return a set */
+ if (IsA(node, Aggref))
+ return false;
+ if (IsA(node, WindowFunc))
+ return false;
+ if (IsA(node, DistinctExpr))
+ return false;
+ if (IsA(node, NullIfExpr))
+ return false;
+ if (IsA(node, ScalarArrayOpExpr))
+ return false;
+ if (IsA(node, BoolExpr))
+ return false;
+ if (IsA(node, SubLink))
+ return false;
+ if (IsA(node, SubPlan))
+ return false;
+ if (IsA(node, AlternativeSubPlan))
+ return false;
+ if (IsA(node, ArrayExpr))
+ return false;
+ if (IsA(node, RowExpr))
+ return false;
+ if (IsA(node, RowCompareExpr))
+ return false;
+ if (IsA(node, CoalesceExpr))
+ return false;
+ if (IsA(node, MinMaxExpr))
+ return false;
+ if (IsA(node, XmlExpr))
+ return false;
+
+ return expression_tree_walker(node, expression_returns_set_walker,
+ context);
+}
+
+
/*
* exprCollation -
* returns the Oid of the collation of the expression's result.
+ *
+ * Note: expression nodes that can invoke functions generally have an
+ * "inputcollid" field, which is what the function should use as collation.
+ * That is the resolved common collation of the node's inputs. It is often
+ * but not always the same as the result collation; in particular, if the
+ * function produces a non-collatable result type from collatable inputs
+ * or vice versa, the two are different.
*/
Oid
exprCollation(Node *expr)
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:
{
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;
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:
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
{
}
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);
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);
}
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;
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;
return true;
}
break;
- case T_NullIfExpr:
- return walker(((NullIfExpr *) node)->args, context);
case T_NullTest:
return walker(((NullTest *) node)->arg, context);
case T_BooleanTest:
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;
}
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;
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;
WRITE_INT_FIELD(paramid);
WRITE_OID_FIELD(paramtype);
WRITE_INT_FIELD(paramtypmod);
- WRITE_OID_FIELD(paramcollation);
+ WRITE_OID_FIELD(paramcollid);
WRITE_LOCATION_FIELD(location);
}
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);
}
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);
}
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);
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);
}
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);
}
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);
}
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);
}
WRITE_INT_FIELD(fieldnum);
WRITE_OID_FIELD(resulttype);
WRITE_INT_FIELD(resulttypmod);
- WRITE_OID_FIELD(resultcollation);
+ WRITE_OID_FIELD(resultcollid);
}
static void
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);
}
WRITE_NODE_FIELD(arg);
WRITE_OID_FIELD(resulttype);
+ WRITE_OID_FIELD(resultcollid);
WRITE_ENUM_FIELD(coerceformat, CoercionForm);
WRITE_LOCATION_FIELD(location);
}
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);
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);
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);
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);
}
WRITE_NODE_TYPE("COALESCE");
WRITE_OID_FIELD(coalescetype);
- WRITE_OID_FIELD(coalescecollation);
+ WRITE_OID_FIELD(coalescecollid);
WRITE_NODE_FIELD(args);
WRITE_LOCATION_FIELD(location);
}
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);
}
WRITE_LOCATION_FIELD(location);
}
-static void
-_outNullIfExpr(StringInfo str, NullIfExpr *node)
-{
- WRITE_NODE_TYPE("NULLIFEXPR");
-
- WRITE_OID_FIELD(opno);
- WRITE_OID_FIELD(opfuncid);
- WRITE_OID_FIELD(opresulttype);
- WRITE_BOOL_FIELD(opretset);
- WRITE_NODE_FIELD(args);
- WRITE_LOCATION_FIELD(location);
-}
-
static void
_outNullTest(StringInfo str, NullTest *node)
{
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);
}
WRITE_OID_FIELD(typeId);
WRITE_INT_FIELD(typeMod);
+ WRITE_OID_FIELD(collation);
WRITE_LOCATION_FIELD(location);
}
WRITE_OID_FIELD(typeId);
WRITE_INT_FIELD(typeMod);
- WRITE_OID_FIELD(collid);
+ WRITE_OID_FIELD(collation);
WRITE_LOCATION_FIELD(location);
}
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);
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);
}
case T_DistinctExpr:
_outDistinctExpr(str, obj);
break;
+ case T_NullIfExpr:
+ _outNullIfExpr(str, obj);
+ break;
case T_ScalarArrayOpExpr:
_outScalarArrayOpExpr(str, obj);
break;
case T_XmlExpr:
_outXmlExpr(str, obj);
break;
- case T_NullIfExpr:
- _outNullIfExpr(str, obj);
- break;
case T_NullTest:
_outNullTest(str, obj);
break;
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();
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();
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();
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);
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();
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();
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();
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();
READ_INT_FIELD(fieldnum);
READ_OID_FIELD(resulttype);
READ_INT_FIELD(resulttypmod);
- READ_OID_FIELD(resultcollation);
+ READ_OID_FIELD(resultcollid);
READ_DONE();
}
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);
READ_NODE_FIELD(arg);
READ_OID_FIELD(resulttype);
+ READ_OID_FIELD(resultcollid);
READ_ENUM_FIELD(coerceformat, CoercionForm);
READ_LOCATION_FIELD(location);
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);
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);
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);
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);
READ_LOCALS(CoalesceExpr);
READ_OID_FIELD(coalescetype);
- READ_OID_FIELD(coalescecollation);
+ READ_OID_FIELD(coalescecollid);
READ_NODE_FIELD(args);
READ_LOCATION_FIELD(location);
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();
READ_DONE();
}
-/*
- * _readNullIfExpr
- */
-static NullIfExpr *
-_readNullIfExpr(void)
-{
- READ_LOCALS(NullIfExpr);
-
- READ_OID_FIELD(opno);
- READ_OID_FIELD(opfuncid);
-
- /*
- * The opfuncid is stored in the textual format primarily for debugging
- * and documentation reasons. We want to always read it as zero to force
- * it to be re-looked-up in the pg_operator entry. This ensures that
- * stored rules don't have hidden dependencies on operators' functions.
- * (We don't currently support an ALTER OPERATOR command, but might
- * someday.)
- */
- local_node->opfuncid = InvalidOid;
-
- READ_OID_FIELD(opresulttype);
- READ_BOOL_FIELD(opretset);
- READ_NODE_FIELD(args);
- READ_LOCATION_FIELD(location);
-
- READ_DONE();
-}
-
/*
* _readNullTest
*/
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);
READ_OID_FIELD(typeId);
READ_INT_FIELD(typeMod);
+ READ_OID_FIELD(collation);
READ_LOCATION_FIELD(location);
READ_DONE();
READ_OID_FIELD(typeId);
READ_INT_FIELD(typeMod);
- READ_OID_FIELD(collid);
+ READ_OID_FIELD(collation);
READ_LOCATION_FIELD(location);
READ_DONE();
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))
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))
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");
{
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;
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;
#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"
{
Expr *clause = restrictinfo->clause;
Oid opno,
+ collation,
item1_type,
item2_type;
Expr *item1;
/* 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
if (cur_ec->ec_has_volatile)
continue;
+ /*
+ * The collation has to match; check this first since it's cheaper
+ * than the opfamily comparison.
+ */
+ if (collation != cur_ec->ec_collation)
+ continue;
+
/*
* A "match" requires matching sets of btree opfamilies. Use of
* equal() for this test has implications discussed in the comments
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;
return true;
}
+/*
+ * canonicalize_ec_expression
+ *
+ * This function ensures that the expression exposes the expected type and
+ * collation, so that it will be equal() to other equivalence-class expressions
+ * that it ought to be equal() to.
+ *
+ * The rule for datatypes is that the exposed type should match what it would
+ * be for an input to an operator of the EC's opfamilies; which is usually
+ * the declared input type of the operator, but in the case of polymorphic
+ * operators no relabeling is wanted (compare the behavior of parse_coerce.c).
+ * Expressions coming in from quals will generally have the right type
+ * already, but expressions coming from indexkeys may not (because they are
+ * represented without any explicit relabel in pg_index), and the same problem
+ * occurs for sort expressions (because the parser is likewise cavalier about
+ * putting relabels on them). Such cases will be binary-compatible with the
+ * real operators, so adding a RelabelType is sufficient.
+ *
+ * Also, the expression's exposed collation must match the EC's collation.
+ * This is important because in comparisons like "foo < bar COLLATE baz",
+ * only one of the expressions has the correct exposed collation as we receive
+ * it from the parser. Forcing both of them to have it ensures that all
+ * variant spellings of such a construct behave the same. Again, we can
+ * stick on a RelabelType to force the right exposed collation. (It might
+ * work to not label the collation at all in EC members, but this is risky
+ * since some parts of the system expect exprCollation() to deliver the
+ * right answer for a sort key.)
+ *
+ * Note this code assumes that the expression has already been through
+ * eval_const_expressions, so there are no CollateExprs and no redundant
+ * RelabelTypes.
+ */
+Expr *
+canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
+{
+ Oid expr_type = exprType((Node *) expr);
+
+ /*
+ * For a polymorphic-input-type opclass, just keep the same exposed type.
+ */
+ if (IsPolymorphicType(req_type))
+ req_type = expr_type;
+
+ /*
+ * No work if the expression exposes the right type/collation already.
+ */
+ if (expr_type != req_type ||
+ exprCollation((Node *) expr) != req_collation)
+ {
+ /*
+ * Strip any existing RelabelType, then add a new one if needed.
+ * This is to preserve the invariant of no redundant RelabelTypes.
+ *
+ * If we have to change the exposed type of the stripped expression,
+ * set typmod to -1 (since the new type may not have the same typmod
+ * interpretation). If we only have to change collation, preserve
+ * the exposed typmod.
+ */
+ while (expr && IsA(expr, RelabelType))
+ expr = (Expr *) ((RelabelType *) expr)->arg;
+
+ if (exprType((Node *) expr) != req_type)
+ expr = (Expr *) makeRelabelType(expr,
+ req_type,
+ -1,
+ req_collation,
+ COERCE_DONTCARE);
+ else if (exprCollation((Node *) expr) != req_collation)
+ expr = (Expr *) makeRelabelType(expr,
+ req_type,
+ exprTypmod((Node *) expr),
+ req_collation,
+ COERCE_DONTCARE);
+ }
+
+ return expr;
+}
+
/*
* add_eq_member - build a new EquivalenceMember and add it to an EC
*/
/*
* 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!)
EquivalenceClass *
get_eclass_for_sort_expr(PlannerInfo *root,
Expr *expr,
- Oid expr_datatype,
List *opfamilies,
+ Oid opcintype,
+ Oid collation,
Index sortref,
bool create_it)
{
ListCell *lc1;
MemoryContext oldcontext;
+ /*
+ * Ensure the expression exposes the correct type and collation.
+ */
+ expr = canonicalize_ec_expression(expr, opcintype, collation);
+
/*
* Scan through the existing EquivalenceClasses for a match
*/
(sortref == 0 || sortref != cur_ec->ec_sortref))
continue;
+ if (collation != cur_ec->ec_collation)
+ continue;
if (!equal(opfamilies, cur_ec->ec_opfamilies))
continue;
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! */
}
/*
* 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;
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
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,
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,
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,
Expr *outervar,
*innervar;
Oid opno,
+ collation,
left_type,
right_type,
inner_datatype;
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))
/* 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? */
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);
Expr *leftvar;
Expr *rightvar;
Oid opno,
+ collation,
left_type,
right_type;
Relids left_relids,
/* 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);
/* 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;
if (OidIsValid(eq_op))
{
newrinfo = build_implied_join_equality(eq_op,
+ cur_ec->ec_collation,
leftvar,
cur_em->em_expr,
left_relids);
if (OidIsValid(eq_op))
{
newrinfo = build_implied_join_equality(eq_op,
+ cur_ec->ec_collation,
rightvar,
cur_em->em_expr,
right_relids);
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;
/*
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 &&
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))
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;
/*
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;
/*
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 */
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
* 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;
}
/* 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))
/* 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))
{
/* 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);
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)
{
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),
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);
}
}
* 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,
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;
}
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));
/*-------
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));
}
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 )" */
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;
#include "postgres.h"
#include "access/skey.h"
-#include "catalog/pg_collation.h"
-#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/plannodes.h"
#include "utils/lsyscache.h"
-static PathKey *makePathKey(EquivalenceClass *eclass, Oid opfamily, Oid collation,
+static PathKey *makePathKey(EquivalenceClass *eclass, Oid opfamily,
int strategy, bool nulls_first);
static PathKey *make_canonical_pathkey(PlannerInfo *root,
- EquivalenceClass *eclass, Oid opfamily, Oid collation,
+ EquivalenceClass *eclass, Oid opfamily,
int strategy, bool nulls_first);
static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys);
static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel,
* 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;
*/
static PathKey *
make_canonical_pathkey(PlannerInfo *root,
- EquivalenceClass *eclass, Oid opfamily, Oid collation,
+ EquivalenceClass *eclass, Oid opfamily,
int strategy, bool nulls_first)
{
PathKey *pk;
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;
*/
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);
cpathkey = make_canonical_pathkey(root,
eclass,
pathkey->pk_opfamily,
- pathkey->pk_collation,
pathkey->pk_strategy,
pathkey->pk_nulls_first);
Expr *expr,
Oid opfamily,
Oid opcintype,
+ Oid collation,
bool reverse_sort,
bool nulls_first,
Index sortref,
Oid equality_op;
List *opfamilies;
EquivalenceClass *eclass;
- Oid collation;
strategy = reverse_sort ? BTGreaterStrategyNumber : BTLessStrategyNumber;
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);
}
/*
bool canonicalize)
{
Oid opfamily,
- opcintype;
+ opcintype,
+ collation;
int16 strategy;
/* Find the operator in pg_amop --- failure shouldn't happen */
&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,
indexkey,
index->sortopfamily[i],
index->opcintype[i],
+ index->indexcollations[i],
reverse_sort,
nulls_first,
0,
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);
make_canonical_pathkey(root,
outer_ec,
sub_pathkey->pk_opfamily,
- sub_pathkey->pk_collation,
sub_pathkey->pk_strategy,
sub_pathkey->pk_nulls_first);
}
{
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;
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);
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 */
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);
}
pathkey = make_canonical_pathkey(root,
ec,
linitial_oid(ec->ec_opfamilies),
- DEFAULT_COLLATION_OID,
BTLessStrategyNumber,
false);
/* can't be redundant because no duplicate ECs */
pathkey = make_canonical_pathkey(root,
ieclass,
opathkey->pk_opfamily,
- opathkey->pk_collation,
opathkey->pk_strategy,
opathkey->pk_nulls_first);
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)
{
/*
/* 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++;
*/
numsortkeys = add_sort_column(tle->resno,
sortop,
- pathkey->pk_collation,
+ pathkey->pk_eclass->ec_collation,
pathkey->pk_nulls_first,
numsortkeys,
sortColIdx, sortOperators, collations, nullsFirst);
void
process_implied_equality(PlannerInfo *root,
Oid opno,
+ Oid collation,
Expr *item1,
Expr *item2,
Relids qualscope,
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)
*/
RestrictInfo *
build_implied_join_equality(Oid opno,
+ Oid collation,
Expr *item1,
Expr *item2,
Relids qualscope)
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);
retval->paramid = i;
retval->paramtype = var->vartype;
retval->paramtypmod = var->vartypmod;
- retval->paramcollation = var->varcollid;
+ retval->paramcollid = var->varcollid;
retval->location = -1;
return retval;
retval->paramid = i;
retval->paramtype = var->vartype;
retval->paramtypmod = var->vartypmod;
- retval->paramcollation = var->varcollid;
+ retval->paramcollid = var->varcollid;
retval->location = -1;
return retval;
retval->paramid = i;
retval->paramtype = agg->aggtype;
retval->paramtypmod = -1;
- retval->paramcollation = agg->collid;
+ retval->paramcollid = agg->aggcollid;
retval->location = -1;
return retval;
* 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;
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);
/*
* 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)
List *leftargs,
*rightargs,
*opids,
+ *opcollations,
*newWhere,
*tlist,
*testlist,
*paramids;
ListCell *lc,
*rc,
- *oc;
+ *oc,
+ *cc;
AttrNumber resno;
/*
* 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);
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))
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 */
*/
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),
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);
}
/*
* 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.
*/
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;
*/
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);
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);
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,
/*
* 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);
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;
}
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, XmlExpr))
return false;
- if (IsA(node, NullIfExpr))
- return false;
return expression_tree_walker(node, expression_returns_set_rows_walker,
(void *) count);
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;
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;
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;
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 */
/* IS DISTINCT FROM is inherently non-strict */
return true;
}
+ if (IsA(node, NullIfExpr))
+ return true;
if (IsA(node, ScalarArrayOpExpr))
{
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
return true;
if (IsA(node, XmlExpr))
return true;
- if (IsA(node, NullIfExpr))
- return true;
if (IsA(node, NullTest))
return true;
if (IsA(node, BooleanTest))
/*
* 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;
*/
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;
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;
}
}
*/
simple = simplify_function(expr->funcid,
expr->funcresulttype, exprTypmod(node),
- expr->collid,
+ expr->inputcollid,
&args,
has_named_args, true, context);
if (simple) /* successfully simplified it */
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;
}
*/
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
- expr->collid,
+ expr->inputcollid,
&args,
false, true, context);
if (simple) /* successfully simplified it */
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;
}
*/
simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
- expr->collid,
+ expr->inputcollid,
&args,
false, false, context);
if (simple) /* successfully simplified it */
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;
}
con->consttype = relabel->resulttype;
con->consttypmod = relabel->resulttypmod;
+ con->constcollid = relabel->resultcollid;
return (Node *) con;
}
else
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;
* 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 */
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 */
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;
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;
{
/*
* 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;
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;
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))
/* 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;
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;
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;
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))
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;
}
}
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))
* (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
* 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,
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)
* 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);
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);
*clause_const;
bool pred_var_on_left,
clause_var_on_left;
+ Oid pred_collation,
+ clause_collation;
Oid pred_op,
clause_op,
test_op;
if (!equal(pred_var, clause_var))
return false;
+ /*
+ * They'd better have the same collation, too.
+ */
+ pred_collation = ((OpExpr *) predicate)->inputcollid;
+ clause_collation = ((OpExpr *) clause)->inputcollid;
+ if (pred_collation != clause_collation)
+ return false;
+
/*
* Okay, get the operators in the two clauses we're comparing. Commute
* them if needed so that we can assume the variables are on the left.
BOOLOID,
false,
(Expr *) pred_const,
- (Expr *) clause_const);
+ (Expr *) clause_const,
+ InvalidOid,
+ pred_collation);
/* Fill in opfuncids */
fix_opfuncids((Node *) test_expr);
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
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
#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"
if (pstate->p_hasWindowFuncs)
parseCheckWindowFuncs(pstate, qry);
+ assign_query_collations(pstate, qry);
+
return qry;
}
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);
}
parser_errposition(pstate,
locate_windowfunc((Node *) qry))));
+ assign_query_collations(pstate, qry);
+
return qry;
}
(LockingClause *) lfirst(l), false);
}
+ assign_query_collations(pstate, qry);
+
return qry;
}
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);
}
parser_errposition(pstate,
locate_windowfunc((Node *) newExprsLists))));
+ assign_query_collations(pstate, qry);
+
return qry;
}
(LockingClause *) lfirst(l), false);
}
+ assign_query_collations(pstate, qry);
+
return qry;
}
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
* 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);
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 */
if (origTargetList != NULL)
elog(ERROR, "UPDATE target count mismatch --- internal error");
+ assign_query_collations(pstate, qry);
+
return qry;
}
* 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.
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)
{
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);
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);
}
*transfnexpr = (Expr *) makeFuncExpr(transfn_oid,
agg_state_type,
args,
- collation,
+ InvalidOid,
+ agg_input_collation,
COERCE_DONTCARE);
/* see if we have a final function */
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);
}
#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"
*/
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
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;
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;
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;
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;
}
bool resolveUnknown)
{
Oid restype = exprType((Node *) tle->expr);
- Oid rescollation = exprCollation((Node *) tle->expr);
Oid sortop;
Oid eqop;
bool hashable;
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 */
#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"
* 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,
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);
*/
RelabelType *r = makeRelabelType((Expr *) result,
targetTypeId, -1,
+ InvalidOid,
cformat);
r->location = location;
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;
FuncExpr *fexpr;
List *args;
Const *cons;
- Oid collation;
Assert(OidIsValid(funcId));
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;
}
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;
-}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+}
* 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);
#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"
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:
exprType(result),
InvalidOid,
exprTypmod(result),
- exprCollation(result),
subscripts,
NULL);
subscripts = NIL;
exprType(result),
InvalidOid,
exprTypmod(result),
- exprCollation(result),
subscripts,
NULL);
{
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 *
}
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;
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);
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 *)
"CASE/WHEN");
}
- newc->casecollation = select_common_collation(pstate, resultexprs, true);
-
newc->location = c->location;
return (Node *) newc;
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);
}
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;
}
newc->coalescetype = select_common_type(pstate, newargs, "COALESCE", NULL);
+ /* coalescecollid will be set by parse_collate.c */
/* Convert arguments if necessary */
foreach(args, newargs)
}
newc->args = newcoercedargs;
- newc->coalescecollation = select_common_collation(pstate, newcoercedargs, true);
newc->location = c->location;
return (Node *) newc;
}
}
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)
List *opexprs;
List *opnos;
List *opfamilies;
- List *collids;
ListCell *l,
*r;
List **opfamily_lists;
* possibility that make_op inserted coercion operations.
*/
opnos = NIL;
- collids = NIL;
largs = NIL;
rargs = NIL;
foreach(l, opexprs)
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));
}
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;
bool retset;
int nvargs;
FuncDetailCode fdresult;
- Oid funccollid;
/*
* Most of the rest of the parser just assumes that functions do not have
/* 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.
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);
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;
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;
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;
/*
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;
}
}
* 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.
*/
Oid arrayType,
Oid elementType,
int32 arrayTypMod,
- Oid arrayColl,
List *indirection,
Node *assignFrom)
{
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;
List *args;
Oid rettype;
OpExpr *result;
- Oid opcollid;
/* Select the operator */
if (rtree == NULL)
/* 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);
List *args;
Oid rettype;
ScalarArrayOpExpr *result;
- Oid opcollid;
ltypeId = exprType(ltree);
atypeId = exprType(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(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);
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;
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;
*/
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))
def->typeId = attrtype;
def->typeMod = attrtypmod;
- def->collid = attrcollation;
+ def->collation = attrcollation;
if (indirection)
{
if (IsA(linitial(indirection), A_Indices))
arrayType,
elementTypeId,
arrayTypMod,
- InvalidOid,
subscripts,
rhs);
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)
{
#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"
/* 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
}
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;
}
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
}
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;
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++)
{
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
/* 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);
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);
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,
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));
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));
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++)
{
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,
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)
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 */
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
* 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 */
ReleaseSysCache(languageTuple);
}
-/*
- * Initialize the fn_collation field
- */
-void
-fmgr_info_collation(Oid collationId, FmgrInfo *finfo)
-{
- finfo->fn_collation = collationId;
-}
-
-/*
- * Initialize the fn_expr field and set the collation based on it
- */
-void
-fmgr_info_expr(Node *expr, FmgrInfo *finfo)
-{
- finfo->fn_expr = expr;
- finfo->fn_collation = exprCollation(expr);
-}
-
/*
* Fetch and validate the information record for the given external function.
* The function is specified by a handle for the containing library
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,
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)
{
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;
}
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;
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 */
/* 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,
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++)
{
anyelement_type,
-1,
0);
+ TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
break;
case ANYARRAYOID:
TupleDescInitEntry(tupdesc, i + 1,
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;
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;
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201103112
+#define CATALOG_VERSION_NO 201103191
#endif
* 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
{
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 */
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
/* Standard parameter list for fmgr-compatible functions */
#define PG_FUNCTION_ARGS FunctionCallInfo fcinfo
+/*
+ * Get collation function should use.
+ */
+#define PG_GET_COLLATION() \
+ (fcinfo->flinfo ? fcinfo->flinfo->fn_collation : InvalidOid)
+
/*
* Get number of arguments passed to function.
*/
#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
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,
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);
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,
#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) (),
T_NamedArgExpr,
T_OpExpr,
T_DistinctExpr,
+ T_NullIfExpr,
T_ScalarArrayOpExpr,
T_BoolExpr,
T_SubLink,
T_CoalesceExpr,
T_MinMaxExpr,
T_XmlExpr,
- T_NullIfExpr,
T_NullTest,
T_BooleanTest,
T_CoerceToDomain,
*
* 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.
* 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 */
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,
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;
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;
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;
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
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;
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;
*/
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)"
*
* 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
{
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;
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;
/* ----------------
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;
/* ----------------
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;
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;
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 */
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
{
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) */
{
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 */
* 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 */
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;
{
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;
{
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;
* '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
{
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
*
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;
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;
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;
* 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.
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 */
* 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
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;
{
/* 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 */
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);
*/
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);
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);
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);
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 */
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
Oid arrayType,
Oid elementType,
int32 arrayTypMod,
- Oid arrayColl,
List *indirection,
Node *assignFrom);
extern Const *make_const(ParseState *pstate, Value *value, 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;
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;
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;
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);
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
(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
---+-----
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
---+-----
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)
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)