1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-31 22:04:40 +03:00

Revise collation derivation method and expression-tree representation.

All expression nodes now have an explicit output-collation field, unless
they are known to only return a noncollatable data type (such as boolean
or record).  Also, nodes that can invoke collation-aware functions store
a separate field that is the collation value to pass to the function.
This avoids confusion that arises when a function has collatable inputs
and noncollatable output type, or vice versa.

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

Fix assorted bugs in the planner's handling of collations by making
collation one of the defining properties of an EquivalenceClass and
by converting CollateExprs into discardable RelabelType nodes during
expression preprocessing.
This commit is contained in:
Tom Lane
2011-03-19 20:29:08 -04:00
parent 025f4c72f0
commit b310b6e31c
73 changed files with 2365 additions and 1084 deletions

View File

@ -13,9 +13,9 @@ include $(top_builddir)/src/Makefile.global
override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
OBJS= analyze.o gram.o keywords.o kwlookup.o parser.o \
parse_agg.o parse_clause.o parse_coerce.o parse_cte.o parse_expr.o \
parse_func.o parse_node.o parse_oper.o parse_param.o parse_relation.o \
parse_target.o parse_type.o parse_utilcmd.o scansup.o
parse_agg.o parse_clause.o parse_coerce.o parse_collate.o parse_cte.o \
parse_expr.o parse_func.o parse_node.o parse_oper.o parse_param.o \
parse_relation.o parse_target.o parse_type.o parse_utilcmd.o scansup.o
FLEXFLAGS = -CF

View File

@ -17,6 +17,7 @@ analyze.c top level of parse analysis for optimizable queries
parse_agg.c handle aggregates, like SUM(col1), AVG(col2), ...
parse_clause.c handle clauses like WHERE, ORDER BY, GROUP BY, ...
parse_coerce.c handle coercing expressions to different data types
parse_collate.c assign collation information in completed expressions
parse_cte.c handle Common Table Expressions (WITH clauses)
parse_expr.c handle expressions like col, col + 3, x = 3 or x = 4
parse_func.c handle functions, table.column and column identifiers

View File

@ -33,6 +33,7 @@
#include "parser/parse_agg.h"
#include "parser/parse_clause.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_cte.h"
#include "parser/parse_oper.h"
#include "parser/parse_param.h"
@ -323,6 +324,8 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
if (pstate->p_hasWindowFuncs)
parseCheckWindowFuncs(pstate, qry);
assign_query_collations(pstate, qry);
return qry;
}
@ -566,6 +569,14 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
stmt->cols,
icolumns, attrnos);
/*
* We must assign collations now because assign_query_collations
* doesn't process rangetable entries. We just assign all the
* collations independently in each row, and don't worry about
* whether they are consistent vertically either.
*/
assign_list_collations(pstate, sublist);
exprsLists = lappend(exprsLists, sublist);
}
@ -705,6 +716,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
parser_errposition(pstate,
locate_windowfunc((Node *) qry))));
assign_query_collations(pstate, qry);
return qry;
}
@ -960,6 +973,8 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
(LockingClause *) lfirst(l), false);
}
assign_query_collations(pstate, qry);
return qry;
}
@ -1082,6 +1097,14 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
i++;
}
/*
* We must assign collations now because assign_query_collations
* doesn't process rangetable entries. We just assign all the
* collations independently in each row, and don't worry about
* whether they are consistent vertically either.
*/
assign_list_collations(pstate, newsublist);
newExprsLists = lappend(newExprsLists, newsublist);
}
@ -1176,6 +1199,8 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
parser_errposition(pstate,
locate_windowfunc((Node *) newExprsLists))));
assign_query_collations(pstate, qry);
return qry;
}
@ -1417,6 +1442,8 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
(LockingClause *) lfirst(l), false);
}
assign_query_collations(pstate, qry);
return qry;
}
@ -1634,12 +1661,6 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
rescoltypmod = lcoltypmod;
else
rescoltypmod = -1;
/* Select common collation. A common collation is
* required for all set operators except UNION ALL; see
* SQL:2008-2 7.13 SR 15c. */
rescolcoll = select_common_collation(pstate,
list_make2(lcolnode, rcolnode),
(op->op == SETOP_UNION && op->all));
/*
* Verify the coercions are actually possible. If not, we'd fail
@ -1662,26 +1683,46 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
* output type of the child query and the resolved target type.
* Such a discrepancy would disable optimization in the planner.
*
* If it's some other UNKNOWN-type node, eg a Var, we do nothing.
* The planner is sometimes able to fold an UNKNOWN Var to a
* constant before it has to coerce the type, so failing now would
* just break cases that might work.
* If it's some other UNKNOWN-type node, eg a Var, we do nothing
* (knowing that coerce_to_common_type would fail). The planner
* is sometimes able to fold an UNKNOWN Var to a constant before
* it has to coerce the type, so failing now would just break
* cases that might work.
*/
if (lcoltype != UNKNOWNOID)
(void) coerce_to_common_type(pstate, lcolnode,
rescoltype, context);
else if (IsA(lcolnode, Const) ||IsA(lcolnode, Param))
ltle->expr = (Expr *)
coerce_to_common_type(pstate, lcolnode,
rescoltype, context);
lcolnode = coerce_to_common_type(pstate, lcolnode,
rescoltype, context);
else if (IsA(lcolnode, Const) ||
IsA(lcolnode, Param))
{
lcolnode = coerce_to_common_type(pstate, lcolnode,
rescoltype, context);
ltle->expr = (Expr *) lcolnode;
}
if (rcoltype != UNKNOWNOID)
(void) coerce_to_common_type(pstate, rcolnode,
rescoltype, context);
else if (IsA(rcolnode, Const) ||IsA(rcolnode, Param))
rtle->expr = (Expr *)
coerce_to_common_type(pstate, rcolnode,
rescoltype, context);
rcolnode = coerce_to_common_type(pstate, rcolnode,
rescoltype, context);
else if (IsA(rcolnode, Const) ||
IsA(rcolnode, Param))
{
rcolnode = coerce_to_common_type(pstate, rcolnode,
rescoltype, context);
rtle->expr = (Expr *) rcolnode;
}
/*
* Select common collation. A common collation is required for
* all set operators except UNION ALL; see SQL:2008 7.13 <query
* expression> Syntax Rule 15c. (If we fail to identify a common
* collation for a UNION ALL column, the curCollations element
* will be set to InvalidOid, which may result in a runtime error
* if something at a higher query level wants to use the column's
* collation.)
*/
rescolcoll = select_common_collation(pstate,
list_make2(lcolnode, rcolnode),
(op->op == SETOP_UNION && op->all));
/* emit results */
op->colTypes = lappend_oid(op->colTypes, rescoltype);
@ -1734,7 +1775,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
rescolnode->typeId = rescoltype;
rescolnode->typeMod = rescoltypmod;
rescolnode->collid = rescolcoll;
rescolnode->collation = rescolcoll;
rescolnode->location = bestlocation;
restle = makeTargetEntry((Expr *) rescolnode,
0, /* no need to set resno */
@ -1966,6 +2007,8 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
if (origTargetList != NULL)
elog(ERROR, "UPDATE target count mismatch --- internal error");
assign_query_collations(pstate, qry);
return qry;
}

View File

@ -709,6 +709,7 @@ check_ungrouped_columns_walker(Node *node,
* agg_input_types, agg_state_type, agg_result_type identify the input,
* transition, and result types of the aggregate. These should all be
* resolved to actual types (ie, none should ever be ANYELEMENT etc).
* agg_input_collation is the aggregate function's input collation.
*
* transfn_oid and finalfn_oid identify the funcs to be called; the latter
* may be InvalidOid.
@ -721,9 +722,9 @@ build_aggregate_fnexprs(Oid *agg_input_types,
int agg_num_inputs,
Oid agg_state_type,
Oid agg_result_type,
Oid agg_input_collation,
Oid transfn_oid,
Oid finalfn_oid,
Oid collation,
Expr **transfnexpr,
Expr **finalfnexpr)
{
@ -742,7 +743,7 @@ build_aggregate_fnexprs(Oid *agg_input_types,
argp->paramid = -1;
argp->paramtype = agg_state_type;
argp->paramtypmod = -1;
argp->paramcollation = collation;
argp->paramcollid = agg_input_collation;
argp->location = -1;
args = list_make1(argp);
@ -754,7 +755,7 @@ build_aggregate_fnexprs(Oid *agg_input_types,
argp->paramid = -1;
argp->paramtype = agg_input_types[i];
argp->paramtypmod = -1;
argp->paramcollation = collation;
argp->paramcollid = agg_input_collation;
argp->location = -1;
args = lappend(args, argp);
}
@ -762,7 +763,8 @@ build_aggregate_fnexprs(Oid *agg_input_types,
*transfnexpr = (Expr *) makeFuncExpr(transfn_oid,
agg_state_type,
args,
collation,
InvalidOid,
agg_input_collation,
COERCE_DONTCARE);
/* see if we have a final function */
@ -780,13 +782,14 @@ build_aggregate_fnexprs(Oid *agg_input_types,
argp->paramid = -1;
argp->paramtype = agg_state_type;
argp->paramtypmod = -1;
argp->paramcollation = collation;
argp->paramcollid = agg_input_collation;
argp->location = -1;
args = list_make1(argp);
*finalfnexpr = (Expr *) makeFuncExpr(finalfn_oid,
agg_result_type,
args,
collation,
InvalidOid,
agg_input_collation,
COERCE_DONTCARE);
}

View File

@ -28,6 +28,7 @@
#include "parser/parsetree.h"
#include "parser/parse_clause.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_oper.h"
#include "parser/parse_relation.h"
@ -557,6 +558,11 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
*/
funcexpr = transformExpr(pstate, r->funccallnode);
/*
* We must assign collations now so that we can fill funccolcollations.
*/
assign_expr_collations(pstate, funcexpr);
/*
* The function parameters cannot make use of any variables from other
* FROM items. (Compare to transformRangeSubselect(); the coding is
@ -1072,6 +1078,7 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
else if (l_colvar->vartypmod != outcoltypmod)
l_node = (Node *) makeRelabelType((Expr *) l_colvar,
outcoltype, outcoltypmod,
InvalidOid, /* fixed below */
COERCE_IMPLICIT_CAST);
else
l_node = (Node *) l_colvar;
@ -1083,6 +1090,7 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
else if (r_colvar->vartypmod != outcoltypmod)
r_node = (Node *) makeRelabelType((Expr *) r_colvar,
outcoltype, outcoltypmod,
InvalidOid, /* fixed below */
COERCE_IMPLICIT_CAST);
else
r_node = (Node *) r_colvar;
@ -1121,6 +1129,7 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
CoalesceExpr *c = makeNode(CoalesceExpr);
c->coalescetype = outcoltype;
/* coalescecollid will get set below */
c->args = list_make2(l_node, r_node);
c->location = -1;
res_node = (Node *) c;
@ -1132,6 +1141,13 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
break;
}
/*
* Apply assign_expr_collations to fix up the collation info in the
* coercion and CoalesceExpr nodes, if we made any. This must be done
* now so that the join node's alias vars show correct collation info.
*/
assign_expr_collations(pstate, res_node);
return res_node;
}
@ -1936,7 +1952,6 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
bool resolveUnknown)
{
Oid restype = exprType((Node *) tle->expr);
Oid rescollation = exprCollation((Node *) tle->expr);
Oid sortop;
Oid eqop;
bool hashable;
@ -2020,12 +2035,6 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
break;
}
if (type_is_collatable(restype) && !OidIsValid(rescollation))
ereport(ERROR,
(errcode(ERRCODE_INDETERMINATE_COLLATION),
errmsg("no collation was derived for the sort expression"),
errhint("Use the COLLATE clause to set the collation explicitly.")));
cancel_parser_errposition_callback(&pcbstate);
/* avoid making duplicate sortlist entries */

View File

@ -16,7 +16,6 @@
#include "catalog/pg_cast.h"
#include "catalog/pg_class.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
@ -123,6 +122,9 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
* pstate is only used in the case that we are able to resolve the type of
* a previously UNKNOWN Param. It is okay to pass pstate = NULL if the
* caller does not want type information updated for Params.
*
* Note: this function must not modify the given expression tree, only add
* decoration on top of it. See transformSetOperationTree, for example.
*/
Node *
coerce_type(ParseState *pstate, Node *node,
@ -282,16 +284,21 @@ coerce_type(ParseState *pstate, Node *node,
if (IsA(node, CollateExpr))
{
/*
* XXX very ugly kluge to push the coercion underneath the CollateExpr.
* This needs to be rethought, as it almost certainly doesn't cover
* all cases.
* If we have a COLLATE clause, we have to push the coercion
* underneath the COLLATE. This is really ugly, but there is little
* choice because the above hacks on Consts and Params wouldn't happen
* otherwise.
*/
CollateExpr *cc = (CollateExpr *) node;
CollateExpr *coll = (CollateExpr *) node;
CollateExpr *newcoll = makeNode(CollateExpr);
cc->arg = (Expr *) coerce_type(pstate, (Node *) cc->arg,
inputTypeId, targetTypeId, targetTypeMod,
ccontext, cformat, location);
return (Node *) cc;
newcoll->arg = (Expr *)
coerce_type(pstate, (Node *) coll->arg,
inputTypeId, targetTypeId, targetTypeMod,
ccontext, cformat, location);
newcoll->collOid = coll->collOid;
newcoll->location = coll->location;
return (Node *) newcoll;
}
pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
&funcId);
@ -352,6 +359,7 @@ coerce_type(ParseState *pstate, Node *node,
*/
RelabelType *r = makeRelabelType((Expr *) result,
targetTypeId, -1,
InvalidOid,
cformat);
r->location = location;
@ -591,6 +599,7 @@ coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId,
result->arg = (Expr *) arg;
result->resulttype = typeId;
result->resulttypmod = -1; /* currently, always -1 for domains */
/* resultcollid will be set by parse_collate.c */
result->coercionformat = cformat;
result->location = location;
@ -734,7 +743,6 @@ build_coercion_expression(Node *node,
FuncExpr *fexpr;
List *args;
Const *cons;
Oid collation;
Assert(OidIsValid(funcId));
@ -766,9 +774,8 @@ build_coercion_expression(Node *node,
args = lappend(args, cons);
}
collation = coercion_expression_result_collation(targetTypeId, node);
fexpr = makeFuncExpr(funcId, targetTypeId, args, collation, cformat);
fexpr = makeFuncExpr(funcId, targetTypeId, args,
InvalidOid, InvalidOid, cformat);
fexpr->location = location;
return (Node *) fexpr;
}
@ -2100,120 +2107,3 @@ typeIsOfTypedTable(Oid reltypeId, Oid reloftypeId)
return result;
}
/*
* select_common_collation() -- determine one collation to apply for
* an expression node, for evaluating the expression itself or to
* label the result of the expression node.
*
* none_ok means that it is permitted to return "no" collation. It is
* then not possible to sort the result value of whatever expression
* is applying this. none_ok = true reflects the rules of SQL
* standard clause "Result of data type combinations", none_ok = false
* reflects the rules of clause "Collation determination" (in some
* cases invoked via "Grouping operations").
*/
Oid
select_common_collation(ParseState *pstate, List *exprs, bool none_ok)
{
ListCell *lc;
/*
* Check if there are any explicit collation derivations. If so,
* they must all be the same.
*/
foreach(lc, exprs)
{
Node *pexpr = (Node *) lfirst(lc);
Oid pcoll = exprCollation(pexpr);
bool pexplicit = IsA(pexpr, CollateExpr);
if (pcoll && pexplicit)
{
ListCell *lc2;
for_each_cell(lc2, lnext(lc))
{
Node *nexpr = (Node *) lfirst(lc2);
Oid ncoll = exprCollation(nexpr);
bool nexplicit = IsA(nexpr, CollateExpr);
if (!ncoll || !nexplicit)
continue;
if (ncoll != pcoll)
ereport(ERROR,
(errcode(ERRCODE_COLLATION_MISMATCH),
errmsg("collation mismatch between explicit collations \"%s\" and \"%s\"",
get_collation_name(pcoll),
get_collation_name(ncoll)),
parser_errposition(pstate, exprLocation(nexpr))));
}
return pcoll;
}
}
/*
* Check if there are any implicit collation derivations.
*/
foreach(lc, exprs)
{
Node *pexpr = (Node *) lfirst(lc);
Oid pcoll = exprCollation(pexpr);
if (pcoll && pcoll != DEFAULT_COLLATION_OID)
{
ListCell *lc2;
for_each_cell(lc2, lnext(lc))
{
Node *nexpr = (Node *) lfirst(lc2);
Oid ncoll = exprCollation(nexpr);
if (!ncoll || ncoll == DEFAULT_COLLATION_OID)
continue;
if (ncoll != pcoll)
{
if (none_ok)
return InvalidOid;
ereport(ERROR,
(errcode(ERRCODE_COLLATION_MISMATCH),
errmsg("collation mismatch between implicit collations \"%s\" and \"%s\"",
get_collation_name(pcoll),
get_collation_name(ncoll)),
errhint("You can override the collation by applying the COLLATE clause to one or both expressions."),
parser_errposition(pstate, exprLocation(nexpr))));
}
}
return pcoll;
}
}
foreach(lc, exprs)
{
Node *pexpr = (Node *) lfirst(lc);
Oid pcoll = exprCollation(pexpr);
if (pcoll == DEFAULT_COLLATION_OID)
{
ListCell *lc2;
for_each_cell(lc2, lnext(lc))
{
Node *nexpr = (Node *) lfirst(lc2);
Oid ncoll = exprCollation(nexpr);
if (ncoll != pcoll)
break;
}
return pcoll;
}
}
/*
* Else use default
*/
return InvalidOid;
}

View File

@ -0,0 +1,763 @@
/*-------------------------------------------------------------------------
*
* parse_collate.c
* Routines for assigning collation information.
*
* We choose to handle collation analysis in a post-pass over the output
* of expression parse analysis. This is because we need more state to
* perform this processing than is needed in the finished tree. If we
* did it on-the-fly while building the tree, all that state would have
* to be kept in expression node trees permanently. This way, the extra
* storage is just local variables in this recursive routine.
*
* The info that is actually saved in the finished tree is:
* 1. The output collation of each expression node, or InvalidOid if it
* returns a noncollatable data type. This can also be InvalidOid if the
* result type is collatable but the collation is indeterminate.
* 2. The collation to be used in executing each function. InvalidOid means
* that there are no collatable inputs or their collation is indeterminate.
* This value is only stored in node types that might call collation-using
* functions.
*
* You might think we could get away with storing only one collation per
* node, but the two concepts really need to be kept distinct. Otherwise
* it's too confusing when a function produces a collatable output type but
* has no collatable inputs or produces noncollatable output from collatable
* inputs.
*
* Cases with indeterminate collation might result in an error being thrown
* at runtime. If we knew exactly which functions require collation
* information, we could throw those errors at parse time instead.
*
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/parser/parse_collate.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "catalog/pg_collation.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_collate.h"
#include "utils/lsyscache.h"
/*
* Collation strength (the SQL standard calls this "derivation"). Order is
* chosen to allow comparisons to work usefully. Note: the standard doesn't
* seem to distingish between NONE and CONFLICT.
*/
typedef enum
{
COLLATE_NONE, /* expression is of a noncollatable datatype */
COLLATE_IMPLICIT, /* collation was derived implicitly */
COLLATE_CONFLICT, /* we had a conflict of implicit collations */
COLLATE_EXPLICIT /* collation was derived explicitly */
} CollateStrength;
typedef struct
{
ParseState *pstate; /* parse state (for error reporting) */
Oid collation; /* OID of current collation, if any */
CollateStrength strength; /* strength of current collation choice */
int location; /* location of expr that set collation */
/* Remaining fields are only valid when strength == COLLATE_CONFLICT */
Oid collation2; /* OID of conflicting collation */
int location2; /* location of expr that set collation2 */
} assign_collations_context;
static bool assign_query_collations_walker(Node *node, ParseState *pstate);
static bool assign_collations_walker(Node *node,
assign_collations_context *context);
/*
* assign_query_collations()
* Mark all expressions in the given Query with collation information.
*
* This should be applied to each Query after completion of parse analysis
* for expressions. Note that we do not recurse into sub-Queries, since
* those should have been processed when built.
*/
void
assign_query_collations(ParseState *pstate, Query *query)
{
/*
* We just use query_tree_walker() to visit all the contained expressions.
* We can skip the rangetable and CTE subqueries, though, since RTEs and
* subqueries had better have been processed already (else Vars referring
* to them would not get created with the right collation).
*/
(void) query_tree_walker(query,
assign_query_collations_walker,
(void *) pstate,
QTW_IGNORE_RANGE_TABLE |
QTW_IGNORE_CTE_SUBQUERIES);
}
/*
* Walker for assign_query_collations
*
* Each expression found by query_tree_walker is processed independently.
* Note that query_tree_walker may pass us a whole List, such as the
* targetlist, in which case each subexpression must be processed
* independently --- we don't want to bleat if two different targetentries
* have different collations.
*/
static bool
assign_query_collations_walker(Node *node, ParseState *pstate)
{
/* Need do nothing for empty subexpressions */
if (node == NULL)
return false;
/*
* We don't want to recurse into a set-operations tree; it's already
* been fully processed in transformSetOperationStmt.
*/
if (IsA(node, SetOperationStmt))
return false;
if (IsA(node, List))
assign_list_collations(pstate, (List *) node);
else
assign_expr_collations(pstate, node);
return false;
}
/*
* assign_list_collations()
* Mark all nodes in the list of expressions with collation information.
*
* The list member expressions are processed independently; they do not have
* to share a common collation.
*/
void
assign_list_collations(ParseState *pstate, List *exprs)
{
ListCell *lc;
foreach(lc, exprs)
{
Node *node = (Node *) lfirst(lc);
assign_expr_collations(pstate, node);
}
}
/*
* assign_expr_collations()
* Mark all nodes in the given expression tree with collation information.
*
* This is exported for the benefit of various utility commands that process
* expressions without building a complete Query. It should be applied after
* calling transformExpr() plus any expression-modifying operations such as
* coerce_to_boolean().
*/
void
assign_expr_collations(ParseState *pstate, Node *expr)
{
assign_collations_context context;
/* initialize context for tree walk */
context.pstate = pstate;
context.collation = InvalidOid;
context.strength = COLLATE_NONE;
context.location = -1;
/* and away we go */
(void) assign_collations_walker(expr, &context);
}
/*
* select_common_collation()
* Identify a common collation for a list of expressions.
*
* The expressions should all return the same datatype, else this is not
* terribly meaningful.
*
* none_ok means that it is permitted to return InvalidOid, indicating that
* no common collation could be identified, even for collatable datatypes.
* Otherwise, an error is thrown for conflict of implicit collations.
*
* In theory, none_ok = true reflects the rules of SQL standard clause "Result
* of data type combinations", none_ok = false reflects the rules of clause
* "Collation determination" (in some cases invoked via "Grouping
* operations").
*/
Oid
select_common_collation(ParseState *pstate, List *exprs, bool none_ok)
{
assign_collations_context context;
/* initialize context for tree walk */
context.pstate = pstate;
context.collation = InvalidOid;
context.strength = COLLATE_NONE;
context.location = -1;
/* and away we go */
(void) assign_collations_walker((Node *) exprs, &context);
/* deal with collation conflict */
if (context.strength == COLLATE_CONFLICT)
{
if (none_ok)
return InvalidOid;
ereport(ERROR,
(errcode(ERRCODE_COLLATION_MISMATCH),
errmsg("collation mismatch between implicit collations \"%s\" and \"%s\"",
get_collation_name(context.collation),
get_collation_name(context.collation2)),
errhint("You can choose the collation by applying the COLLATE clause to one or both expressions."),
parser_errposition(context.pstate, context.location2)));
}
/*
* Note: if strength is still COLLATE_NONE, we'll return InvalidOid, but
* that's okay because it must mean none of the expressions returned
* collatable datatypes.
*/
return context.collation;
}
/*
* assign_collations_walker()
* Recursive guts of collation processing.
*
* Nodes with no children (eg, Vars, Consts, Params) must have been marked
* when built. All upper-level nodes are marked here.
*
* Note: if this is invoked directly on a List, it will attempt to infer a
* common collation for all the list members. In particular, it will throw
* error if there are conflicting explicit collations for different members.
*/
static bool
assign_collations_walker(Node *node, assign_collations_context *context)
{
assign_collations_context loccontext;
Oid collation;
CollateStrength strength;
int location;
/* Need do nothing for empty subexpressions */
if (node == NULL)
return false;
/*
* Prepare for recursion. For most node types, though not all, the
* first thing we do is recurse to process all nodes below this one.
* Each level of the tree has its own local context.
*/
loccontext.pstate = context->pstate;
loccontext.collation = InvalidOid;
loccontext.strength = COLLATE_NONE;
loccontext.location = -1;
/*
* Recurse if appropriate, then determine the collation for this node.
*
* Note: the general cases are at the bottom of the switch, after various
* special cases.
*/
switch (nodeTag(node))
{
case T_CollateExpr:
{
/*
* COLLATE sets an explicitly derived collation, regardless of
* what the child state is. But we must recurse to set up
* collation info below here.
*/
CollateExpr *expr = (CollateExpr *) node;
(void) expression_tree_walker(node,
assign_collations_walker,
(void *) &loccontext);
collation = expr->collOid;
Assert(OidIsValid(collation));
strength = COLLATE_EXPLICIT;
location = expr->location;
}
break;
case T_FieldSelect:
{
/*
* FieldSelect is a special case because the field may have
* a non-default collation, in which case we should use that.
* The field's collation was already looked up and saved
* in the node.
*/
FieldSelect *expr = (FieldSelect *) node;
/* ... but first, recurse */
(void) expression_tree_walker(node,
assign_collations_walker,
(void *) &loccontext);
if (OidIsValid(expr->resultcollid))
{
/* Node's result type is collatable. */
if (expr->resultcollid == DEFAULT_COLLATION_OID)
{
/*
* The immediate input node necessarily yields a
* composite type, so it will have no exposed
* collation. However, if we are selecting a field
* from a function returning composite, see if we
* can bubble up a collation from the function's
* input. XXX this is a bit of a hack, rethink ...
*/
if (IsA(expr->arg, FuncExpr))
{
FuncExpr *fexpr = (FuncExpr *) expr->arg;
if (OidIsValid(fexpr->inputcollid))
expr->resultcollid = fexpr->inputcollid;
}
}
/* Pass up field's collation as an implicit choice. */
collation = expr->resultcollid;
strength = COLLATE_IMPLICIT;
location = exprLocation(node);
}
else
{
/* Node's result type isn't collatable. */
collation = InvalidOid;
strength = COLLATE_NONE;
location = -1; /* won't be used */
}
}
break;
case T_CaseExpr:
{
/*
* CaseExpr is a special case because we do not want to
* recurse into the test expression (if any). It was
* already marked with collations during transformCaseExpr,
* and furthermore its collation is not relevant to the
* result of the CASE --- only the output expressions are.
* So we can't use expression_tree_walker here.
*/
CaseExpr *expr = (CaseExpr *) node;
Oid typcollation;
ListCell *lc;
foreach(lc, expr->args)
{
CaseWhen *when = (CaseWhen *) lfirst(lc);
Assert(IsA(when, CaseWhen));
/*
* The condition expressions mustn't affect the CASE's
* result collation either; but since they are known to
* yield boolean, it's safe to recurse directly on them
* --- they won't change loccontext.
*/
(void) assign_collations_walker((Node *) when->expr,
&loccontext);
(void) assign_collations_walker((Node *) when->result,
&loccontext);
}
(void) assign_collations_walker((Node *) expr->defresult,
&loccontext);
/*
* Now determine the CASE's output collation. This is the
* same as the general case below.
*/
typcollation = get_typcollation(exprType(node));
if (OidIsValid(typcollation))
{
/* Node's result is collatable; what about its input? */
if (loccontext.strength > COLLATE_NONE)
{
/* Collation state bubbles up from children. */
collation = loccontext.collation;
strength = loccontext.strength;
location = loccontext.location;
}
else
{
/*
* Collatable output produced without any collatable
* input. Use the type's collation (which is usually
* DEFAULT_COLLATION_OID, but might be different for a
* domain).
*/
collation = typcollation;
strength = COLLATE_IMPLICIT;
location = exprLocation(node);
}
}
else
{
/* Node's result type isn't collatable. */
collation = InvalidOid;
strength = COLLATE_NONE;
location = -1; /* won't be used */
}
/*
* Save the state into the expression node. We know it
* doesn't care about input collation.
*/
if (strength == COLLATE_CONFLICT)
exprSetCollation(node, InvalidOid);
else
exprSetCollation(node, collation);
}
break;
case T_RowExpr:
{
/*
* RowExpr is a special case because the subexpressions
* are independent: we don't want to complain if some of
* them have incompatible explicit collations.
*/
RowExpr *expr = (RowExpr *) node;
assign_list_collations(context->pstate, expr->args);
/*
* Since the result is always composite and therefore never
* has a collation, we can just stop here: this node has no
* impact on the collation of its parent.
*/
return false; /* done */
}
case T_RowCompareExpr:
{
/*
* For RowCompare, we have to find the common collation of
* each pair of input columns and build a list. If we can't
* find a common collation, we just put InvalidOid into the
* list, which may or may not cause an error at runtime.
*/
RowCompareExpr *expr = (RowCompareExpr *) node;
List *colls = NIL;
ListCell *l;
ListCell *r;
forboth(l, expr->largs, r, expr->rargs)
{
Node *le = (Node *) lfirst(l);
Node *re = (Node *) lfirst(r);
Oid coll;
coll = select_common_collation(context->pstate,
list_make2(le, re),
true);
colls = lappend_oid(colls, coll);
}
expr->inputcollids = colls;
/*
* Since the result is always boolean and therefore never
* has a collation, we can just stop here: this node has no
* impact on the collation of its parent.
*/
return false; /* done */
}
case T_CoerceToDomain:
{
/*
* If the domain declaration included a non-default COLLATE
* spec, then use that collation as the output collation of
* the coercion. Otherwise allow the input collation to
* bubble up. (The input should be of the domain's base
* type, therefore we don't need to worry about it not being
* collatable when the domain is.)
*/
CoerceToDomain *expr = (CoerceToDomain *) node;
Oid typcollation = get_typcollation(expr->resulttype);
/* ... but first, recurse */
(void) expression_tree_walker(node,
assign_collations_walker,
(void *) &loccontext);
if (OidIsValid(typcollation))
{
/* Node's result type is collatable. */
if (typcollation == DEFAULT_COLLATION_OID)
{
/* Collation state bubbles up from child. */
collation = loccontext.collation;
strength = loccontext.strength;
location = loccontext.location;
}
else
{
/* Use domain's collation as an implicit choice. */
collation = typcollation;
strength = COLLATE_IMPLICIT;
location = exprLocation(node);
}
}
else
{
/* Node's result type isn't collatable. */
collation = InvalidOid;
strength = COLLATE_NONE;
location = -1; /* won't be used */
}
/*
* Save the state into the expression node. We know it
* doesn't care about input collation.
*/
if (strength == COLLATE_CONFLICT)
exprSetCollation(node, InvalidOid);
else
exprSetCollation(node, collation);
}
break;
case T_TargetEntry:
(void) expression_tree_walker(node,
assign_collations_walker,
(void *) &loccontext);
/*
* TargetEntry can have only one child, and should bubble that
* state up to its parent. We can't use the general-case code
* below because exprType and friends don't work on TargetEntry.
*/
collation = loccontext.collation;
strength = loccontext.strength;
location = loccontext.location;
break;
case T_RangeTblRef:
case T_JoinExpr:
case T_FromExpr:
case T_SortGroupClause:
(void) expression_tree_walker(node,
assign_collations_walker,
(void *) &loccontext);
/*
* When we're invoked on a query's jointree, we don't need to do
* anything with join nodes except recurse through them to process
* WHERE/ON expressions. So just stop here. Likewise, we don't
* need to do anything when invoked on sort/group lists.
*/
return false;
case T_Query:
{
/*
* We get here when we're invoked on the Query belonging to a
* SubLink. Act as though the Query returns its first output
* column, which indeed is what it does for EXPR_SUBLINK and
* ARRAY_SUBLINK cases. In the cases where the SubLink
* returns boolean, this info will be ignored.
*
* We needn't recurse, since the Query is already processed.
*/
Query *qtree = (Query *) node;
TargetEntry *tent;
tent = (TargetEntry *) linitial(qtree->targetList);
Assert(IsA(tent, TargetEntry));
Assert(!tent->resjunk);
collation = exprCollation((Node *) tent->expr);
/* collation doesn't change if it's converted to array */
strength = COLLATE_IMPLICIT;
location = exprLocation((Node *) tent->expr);
}
break;
case T_List:
(void) expression_tree_walker(node,
assign_collations_walker,
(void *) &loccontext);
/*
* When processing a list, collation state just bubbles up from
* the list elements.
*/
collation = loccontext.collation;
strength = loccontext.strength;
location = loccontext.location;
break;
case T_Var:
case T_Const:
case T_Param:
case T_CoerceToDomainValue:
case T_CaseTestExpr:
case T_SetToDefault:
case T_CurrentOfExpr:
/*
* General case for childless expression nodes. These should
* already have a collation assigned; it is not this function's
* responsibility to look into the catalogs for base-case
* information.
*/
collation = exprCollation(node);
/*
* Note: in most cases, there will be an assigned collation
* whenever type_is_collatable(exprType(node)); but an exception
* occurs for a Var referencing a subquery output column for
* which a unique collation was not determinable. That may lead
* to a runtime failure if a collation-sensitive function is
* applied to the Var.
*/
if (OidIsValid(collation))
strength = COLLATE_IMPLICIT;
else
strength = COLLATE_NONE;
location = exprLocation(node);
break;
default:
{
/*
* General case for most expression nodes with children.
* First recurse, then figure out what to assign here.
*/
Oid typcollation;
(void) expression_tree_walker(node,
assign_collations_walker,
(void *) &loccontext);
typcollation = get_typcollation(exprType(node));
if (OidIsValid(typcollation))
{
/* Node's result is collatable; what about its input? */
if (loccontext.strength > COLLATE_NONE)
{
/* Collation state bubbles up from children. */
collation = loccontext.collation;
strength = loccontext.strength;
location = loccontext.location;
}
else
{
/*
* Collatable output produced without any collatable
* input. Use the type's collation (which is usually
* DEFAULT_COLLATION_OID, but might be different for a
* domain).
*/
collation = typcollation;
strength = COLLATE_IMPLICIT;
location = exprLocation(node);
}
}
else
{
/* Node's result type isn't collatable. */
collation = InvalidOid;
strength = COLLATE_NONE;
location = -1; /* won't be used */
}
/*
* Save the result collation into the expression node.
* If the state is COLLATE_CONFLICT, we'll set the collation
* to InvalidOid, which might result in an error at runtime.
*/
if (strength == COLLATE_CONFLICT)
exprSetCollation(node, InvalidOid);
else
exprSetCollation(node, collation);
/*
* Likewise save the input collation, which is the one that
* any function called by this node should use.
*/
if (loccontext.strength == COLLATE_CONFLICT)
exprSetInputCollation(node, InvalidOid);
else
exprSetInputCollation(node, loccontext.collation);
}
break;
}
/*
* Now, merge my information into my parent's state. If the collation
* strength for this node is different from what's already in *context,
* then this node either dominates or is dominated by earlier siblings.
*/
if (strength > context->strength)
{
/* Override previous parent state */
context->collation = collation;
context->strength = strength;
context->location = location;
/* Bubble up error info if applicable */
if (strength == COLLATE_CONFLICT)
{
context->collation2 = loccontext.collation2;
context->location2 = loccontext.location2;
}
}
else if (strength == context->strength)
{
/* Merge, or detect error if there's a collation conflict */
switch (strength)
{
case COLLATE_NONE:
/* Nothing + nothing is still nothing */
break;
case COLLATE_IMPLICIT:
if (collation != context->collation)
{
/*
* Non-default implicit collation always beats default.
*/
if (context->collation == DEFAULT_COLLATION_OID)
{
/* Override previous parent state */
context->collation = collation;
context->strength = strength;
context->location = location;
}
else if (collation != DEFAULT_COLLATION_OID)
{
/*
* Ooops, we have a conflict. We cannot throw error
* here, since the conflict could be resolved by a
* later sibling CollateExpr, or the parent might not
* care about collation anyway. Return enough info to
* throw the error later, if needed.
*/
context->strength = COLLATE_CONFLICT;
context->collation2 = collation;
context->location2 = location;
}
}
break;
case COLLATE_CONFLICT:
/* We're still conflicted ... */
break;
case COLLATE_EXPLICIT:
if (collation != context->collation)
{
/*
* Ooops, we have a conflict of explicit COLLATE clauses.
* Here we choose to throw error immediately; that is what
* the SQL standard says to do, and there's no good reason
* to be less strict.
*/
ereport(ERROR,
(errcode(ERRCODE_COLLATION_MISMATCH),
errmsg("collation mismatch between explicit collations \"%s\" and \"%s\"",
get_collation_name(context->collation),
get_collation_name(collation)),
parser_errposition(context->pstate, location)));
}
break;
}
}
return false;
}

View File

@ -405,12 +405,16 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
* might see "unknown" as a result of an untyped literal in the
* non-recursive term's select list, and if we don't convert to text
* then we'll have a mismatch against the UNION result.
*
* The column might contain 'foo' COLLATE "bar", so don't override
* collation if it's already set.
*/
if (cte->cterecursive && coltype == UNKNOWNOID)
{
coltype = TEXTOID;
coltypmod = -1; /* should be -1 already, but be sure */
colcoll = DEFAULT_COLLATION_OID;
if (!OidIsValid(colcoll))
colcoll = DEFAULT_COLLATION_OID;
}
cte->ctecoltypes = lappend_oid(cte->ctecoltypes, coltype);
cte->ctecoltypmods = lappend_int(cte->ctecoltypmods, coltypmod);

View File

@ -23,6 +23,7 @@
#include "optimizer/var.h"
#include "parser/analyze.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
@ -309,8 +310,8 @@ transformExpr(ParseState *pstate, Node *expr)
case T_FuncExpr:
case T_OpExpr:
case T_DistinctExpr:
case T_ScalarArrayOpExpr:
case T_NullIfExpr:
case T_ScalarArrayOpExpr:
case T_BoolExpr:
case T_FieldSelect:
case T_FieldStore:
@ -429,7 +430,6 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
exprType(result),
InvalidOid,
exprTypmod(result),
exprCollation(result),
subscripts,
NULL);
subscripts = NIL;
@ -451,7 +451,6 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
exprType(result),
InvalidOid,
exprTypmod(result),
exprCollation(result),
subscripts,
NULL);
@ -1001,25 +1000,34 @@ transformAExprNullIf(ParseState *pstate, A_Expr *a)
{
Node *lexpr = transformExpr(pstate, a->lexpr);
Node *rexpr = transformExpr(pstate, a->rexpr);
Node *result;
OpExpr *result;
result = (Node *) make_op(pstate,
a->name,
lexpr,
rexpr,
a->location);
if (((OpExpr *) result)->opresulttype != BOOLOID)
result = (OpExpr *) make_op(pstate,
a->name,
lexpr,
rexpr,
a->location);
/*
* The comparison operator itself should yield boolean ...
*/
if (result->opresulttype != BOOLOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("NULLIF requires = operator to yield boolean"),
parser_errposition(pstate, a->location)));
/*
* ... but the NullIfExpr will yield the first operand's type.
*/
result->opresulttype = exprType((Node *) linitial(result->args));
/*
* We rely on NullIfExpr and OpExpr being the same struct
*/
NodeSetTag(result, T_NullIfExpr);
return result;
return (Node *) result;
}
static Node *
@ -1153,6 +1161,7 @@ transformAExprIn(ParseState *pstate, A_Expr *a)
}
newa = makeNode(ArrayExpr);
newa->array_typeid = array_type;
/* array_collid will be set by parse_collate.c */
newa->element_typeid = scalar_type;
newa->elements = aexprs;
newa->multidims = false;
@ -1272,6 +1281,14 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c)
if (exprType(arg) == UNKNOWNOID)
arg = coerce_to_common_type(pstate, arg, TEXTOID, "CASE");
/*
* Run collation assignment on the test expression so that we know
* what collation to mark the placeholder with. In principle we
* could leave it to parse_collate.c to do that later, but propagating
* the result to the CaseTestExpr would be unnecessarily complicated.
*/
assign_expr_collations(pstate, arg);
placeholder = makeNode(CaseTestExpr);
placeholder->typeId = exprType(arg);
placeholder->typeMod = exprTypmod(arg);
@ -1340,6 +1357,7 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c)
ptype = select_common_type(pstate, resultexprs, "CASE", NULL);
Assert(OidIsValid(ptype));
newc->casetype = ptype;
/* casecollid will be set by parse_collate.c */
/* Convert default result clause, if necessary */
newc->defresult = (Expr *)
@ -1360,8 +1378,6 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c)
"CASE/WHEN");
}
newc->casecollation = select_common_collation(pstate, resultexprs, true);
newc->location = c->location;
return (Node *) newc;
@ -1472,7 +1488,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
param->paramid = tent->resno;
param->paramtype = exprType((Node *) tent->expr);
param->paramtypmod = exprTypmod((Node *) tent->expr);
param->paramcollation = exprCollation((Node *) tent->expr);
param->paramcollid = exprCollation((Node *) tent->expr);
param->location = -1;
right_list = lappend(right_list, param);
@ -1660,6 +1676,7 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
}
newa->array_typeid = array_type;
/* array_collid will be set by parse_collate.c */
newa->element_typeid = element_type;
newa->elements = newcoercedelems;
newa->location = a->location;
@ -1702,6 +1719,7 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c)
}
newc->coalescetype = select_common_type(pstate, newargs, "COALESCE", NULL);
/* coalescecollid will be set by parse_collate.c */
/* Convert arguments if necessary */
foreach(args, newargs)
@ -1716,7 +1734,6 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c)
}
newc->args = newcoercedargs;
newc->coalescecollation = select_common_collation(pstate, newcoercedargs, true);
newc->location = c->location;
return (Node *) newc;
}
@ -1741,7 +1758,7 @@ transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m)
}
newm->minmaxtype = select_common_type(pstate, newargs, funcname, NULL);
newm->collid = select_common_collation(pstate, newargs, false);
/* minmaxcollid and inputcollid will be set by parse_collate.c */
/* Convert arguments if necessary */
foreach(args, newargs)
@ -2149,7 +2166,6 @@ make_row_comparison_op(ParseState *pstate, List *opname,
List *opexprs;
List *opnos;
List *opfamilies;
List *collids;
ListCell *l,
*r;
List **opfamily_lists;
@ -2320,7 +2336,6 @@ make_row_comparison_op(ParseState *pstate, List *opname,
* possibility that make_op inserted coercion operations.
*/
opnos = NIL;
collids = NIL;
largs = NIL;
rargs = NIL;
foreach(l, opexprs)
@ -2328,7 +2343,6 @@ make_row_comparison_op(ParseState *pstate, List *opname,
OpExpr *cmp = (OpExpr *) lfirst(l);
opnos = lappend_oid(opnos, cmp->opno);
collids = lappend_oid(collids, cmp->collid);
largs = lappend(largs, linitial(cmp->args));
rargs = lappend(rargs, lsecond(cmp->args));
}
@ -2337,7 +2351,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
rcexpr->rctype = rctype;
rcexpr->opnos = opnos;
rcexpr->opfamilies = opfamilies;
rcexpr->collids = collids;
rcexpr->inputcollids = NIL; /* assign_expr_collations will fix this */
rcexpr->largs = largs;
rcexpr->rargs = rargs;

View File

@ -78,7 +78,6 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
bool retset;
int nvargs;
FuncDetailCode fdresult;
Oid funccollid;
/*
* Most of the rest of the parser just assumes that functions do not have
@ -344,12 +343,6 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
/* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types);
/* XXX: If we knew which functions required collation information,
* we could selectively set the last argument to true here. */
funccollid = select_common_collation(pstate, fargs, false);
if (!OidIsValid(funccollid))
funccollid = get_typcollation(rettype);
/*
* If it's a variadic function call, transform the last nvargs arguments
* into an array --- unless it's an "any" variadic.
@ -374,6 +367,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
errmsg("could not find array type for data type %s",
format_type_be(newa->element_typeid)),
parser_errposition(pstate, exprLocation((Node *) vargs))));
/* array_collid will be set by parse_collate.c */
newa->multidims = false;
newa->location = exprLocation((Node *) vargs);
@ -389,8 +383,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
funcexpr->funcresulttype = rettype;
funcexpr->funcretset = retset;
funcexpr->funcformat = COERCE_EXPLICIT_CALL;
/* funccollid and inputcollid will be set by parse_collate.c */
funcexpr->args = fargs;
funcexpr->collid = funccollid;
funcexpr->location = location;
retval = (Node *) funcexpr;
@ -402,9 +396,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
aggref->aggfnoid = funcid;
aggref->aggtype = rettype;
/* aggcollid and inputcollid will be set by parse_collate.c */
/* args, aggorder, aggdistinct will be set by transformAggregateCall */
aggref->aggstar = agg_star;
aggref->collid = funccollid;
/* agglevelsup will be set by transformAggregateCall */
aggref->location = location;
@ -458,11 +452,11 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
wfunc->winfnoid = funcid;
wfunc->wintype = rettype;
/* wincollid and inputcollid will be set by parse_collate.c */
wfunc->args = fargs;
/* winref will be set by transformWindowFuncCall */
wfunc->winstar = agg_star;
wfunc->winagg = (fdresult == FUNCDETAIL_AGGREGATE);
wfunc->collid = funccollid;
wfunc->location = location;
/*
@ -1390,7 +1384,8 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg,
fselect->fieldnum = i + 1;
fselect->resulttype = att->atttypid;
fselect->resulttypmod = att->atttypmod;
fselect->resultcollation = att->attcollation;
/* resultcollid may get overridden by parse_collate.c */
fselect->resultcollid = att->attcollation;
return (Node *) fselect;
}
}

View File

@ -270,7 +270,6 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
* elementType OID of array's element type (fetch with transformArrayType,
* or pass InvalidOid to do it here)
* arrayTypMod typmod for the array (which is also typmod for the elements)
* arrayColl OID of collation of array and array's elements
* indirection Untransformed list of subscripts (must not be NIL)
* assignFrom NULL for array fetch, else transformed expression for source.
*/
@ -280,7 +279,6 @@ transformArraySubscripts(ParseState *pstate,
Oid arrayType,
Oid elementType,
int32 arrayTypMod,
Oid arrayColl,
List *indirection,
Node *assignFrom)
{
@ -407,7 +405,7 @@ transformArraySubscripts(ParseState *pstate,
aref->refarraytype = arrayType;
aref->refelemtype = elementType;
aref->reftypmod = arrayTypMod;
aref->refcollid = arrayColl;
/* refcollid will be set by parse_collate.c */
aref->refupperindexpr = upperIndexpr;
aref->reflowerindexpr = lowerIndexpr;
aref->refexpr = (Expr *) arrayBase;

View File

@ -782,7 +782,6 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
List *args;
Oid rettype;
OpExpr *result;
Oid opcollid;
/* Select the operator */
if (rtree == NULL)
@ -862,20 +861,14 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
/* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
/* XXX: If we knew which functions required collation information,
* we could selectively set the last argument to true here. */
opcollid = select_common_collation(pstate, args, false);
if (!OidIsValid(opcollid))
opcollid = get_typcollation(rettype);
/* and build the expression node */
result = makeNode(OpExpr);
result->opno = oprid(tup);
result->opfuncid = opform->oprcode;
result->opresulttype = rettype;
result->opretset = get_func_retset(opform->oprcode);
/* opcollid and inputcollid will be set by parse_collate.c */
result->args = args;
result->collid = opcollid;
result->location = location;
ReleaseSysCache(tup);
@ -904,7 +897,6 @@ make_scalar_array_op(ParseState *pstate, List *opname,
List *args;
Oid rettype;
ScalarArrayOpExpr *result;
Oid opcollid;
ltypeId = exprType(ltree);
atypeId = exprType(rtree);
@ -999,19 +991,13 @@ make_scalar_array_op(ParseState *pstate, List *opname,
/* perform the necessary typecasting of arguments */
make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
/* XXX: If we knew which functions required collation information,
* we could selectively set the last argument to true here. */
opcollid = select_common_collation(pstate, args, false);
if (!OidIsValid(opcollid))
opcollid = get_typcollation(rettype);
/* and build the expression node */
result = makeNode(ScalarArrayOpExpr);
result->opno = oprid(tup);
result->opfuncid = opform->oprcode;
result->useOr = useOr;
/* inputcollid will be set by parse_collate.c */
result->args = args;
result->collid = opcollid;
result->location = location;
ReleaseSysCache(tup);

View File

@ -114,7 +114,7 @@ fixed_paramref_hook(ParseState *pstate, ParamRef *pref)
param->paramid = paramno;
param->paramtype = parstate->paramTypes[paramno - 1];
param->paramtypmod = -1;
param->paramcollation = get_typcollation(param->paramtype);
param->paramcollid = get_typcollation(param->paramtype);
param->location = pref->location;
return (Node *) param;
@ -167,7 +167,7 @@ variable_paramref_hook(ParseState *pstate, ParamRef *pref)
param->paramid = paramno;
param->paramtype = *pptype;
param->paramtypmod = -1;
param->paramcollation = get_typcollation(param->paramtype);
param->paramcollid = get_typcollation(param->paramtype);
param->location = pref->location;
return (Node *) param;
@ -231,6 +231,8 @@ variable_coerce_param_hook(ParseState *pstate, Param *param,
*/
param->paramtypmod = -1;
param->paramcollid = get_typcollation(param->paramtype);
/* Use the leftmost of the param's and coercion's locations */
if (location >= 0 &&
(param->location < 0 || location < param->location))

View File

@ -398,7 +398,7 @@ transformAssignedExpr(ParseState *pstate,
def->typeId = attrtype;
def->typeMod = attrtypmod;
def->collid = attrcollation;
def->collation = attrcollation;
if (indirection)
{
if (IsA(linitial(indirection), A_Indices))
@ -785,7 +785,6 @@ transformAssignmentSubscripts(ParseState *pstate,
arrayType,
elementTypeId,
arrayTypMod,
InvalidOid,
subscripts,
rhs);
@ -1267,7 +1266,8 @@ ExpandRowReference(ParseState *pstate, Node *expr,
fselect->fieldnum = i + 1;
fselect->resulttype = att->atttypid;
fselect->resulttypmod = att->atttypmod;
fselect->resultcollation = att->attcollation;
/* resultcollid may get overridden by parse_collate.c */
fselect->resultcollid = att->attcollation;
if (targetlist)
{

View File

@ -46,6 +46,7 @@
#include "nodes/nodeFuncs.h"
#include "parser/analyze.h"
#include "parser/parse_clause.h"
#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
@ -1798,6 +1799,9 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString)
/* Now do parse transformation of the expression */
ielem->expr = transformExpr(pstate, ielem->expr);
/* We have to fix its collations too */
assign_expr_collations(pstate, ielem->expr);
/*
* We check only that the result type is legitimate; this is for
* consistency with what transformWhereClause() checks for the