mirror of
https://github.com/postgres/postgres.git
synced 2025-09-03 15:22:11 +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:
@@ -1795,7 +1795,7 @@ cost_mergejoin(MergePath *path, PlannerInfo *root, SpecialJoinInfo *sjinfo)
|
||||
ipathkey = (PathKey *) linitial(ipathkeys);
|
||||
/* debugging check */
|
||||
if (opathkey->pk_opfamily != ipathkey->pk_opfamily ||
|
||||
opathkey->pk_collation != ipathkey->pk_collation ||
|
||||
opathkey->pk_eclass->ec_collation != ipathkey->pk_eclass->ec_collation ||
|
||||
opathkey->pk_strategy != ipathkey->pk_strategy ||
|
||||
opathkey->pk_nulls_first != ipathkey->pk_nulls_first)
|
||||
elog(ERROR, "left and right pathkeys do not match in mergejoin");
|
||||
@@ -2046,7 +2046,7 @@ cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey)
|
||||
{
|
||||
cache = (MergeScanSelCache *) lfirst(lc);
|
||||
if (cache->opfamily == pathkey->pk_opfamily &&
|
||||
cache->collation == pathkey->pk_collation &&
|
||||
cache->collation == pathkey->pk_eclass->ec_collation &&
|
||||
cache->strategy == pathkey->pk_strategy &&
|
||||
cache->nulls_first == pathkey->pk_nulls_first)
|
||||
return cache;
|
||||
@@ -2068,7 +2068,7 @@ cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey)
|
||||
|
||||
cache = (MergeScanSelCache *) palloc(sizeof(MergeScanSelCache));
|
||||
cache->opfamily = pathkey->pk_opfamily;
|
||||
cache->collation = pathkey->pk_collation;
|
||||
cache->collation = pathkey->pk_eclass->ec_collation;
|
||||
cache->strategy = pathkey->pk_strategy;
|
||||
cache->nulls_first = pathkey->pk_nulls_first;
|
||||
cache->leftstartsel = leftstartsel;
|
||||
|
@@ -17,6 +17,8 @@
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/skey.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/cost.h"
|
||||
@@ -97,6 +99,7 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
|
||||
{
|
||||
Expr *clause = restrictinfo->clause;
|
||||
Oid opno,
|
||||
collation,
|
||||
item1_type,
|
||||
item2_type;
|
||||
Expr *item1;
|
||||
@@ -117,11 +120,23 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
|
||||
/* Extract info from given clause */
|
||||
Assert(is_opclause(clause));
|
||||
opno = ((OpExpr *) clause)->opno;
|
||||
collation = ((OpExpr *) clause)->inputcollid;
|
||||
item1 = (Expr *) get_leftop(clause);
|
||||
item2 = (Expr *) get_rightop(clause);
|
||||
item1_relids = restrictinfo->left_relids;
|
||||
item2_relids = restrictinfo->right_relids;
|
||||
|
||||
/*
|
||||
* Ensure both input expressions expose the desired collation (their types
|
||||
* should be OK already); see comments for canonicalize_ec_expression.
|
||||
*/
|
||||
item1 = canonicalize_ec_expression(item1,
|
||||
exprType((Node *) item1),
|
||||
collation);
|
||||
item2 = canonicalize_ec_expression(item2,
|
||||
exprType((Node *) item2),
|
||||
collation);
|
||||
|
||||
/*
|
||||
* Reject clauses of the form X=X. These are not as redundant as they
|
||||
* might seem at first glance: assuming the operator is strict, this is
|
||||
@@ -188,6 +203,13 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
|
||||
if (cur_ec->ec_has_volatile)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* The collation has to match; check this first since it's cheaper
|
||||
* than the opfamily comparison.
|
||||
*/
|
||||
if (collation != cur_ec->ec_collation)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* A "match" requires matching sets of btree opfamilies. Use of
|
||||
* equal() for this test has implications discussed in the comments
|
||||
@@ -315,6 +337,7 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
|
||||
EquivalenceClass *ec = makeNode(EquivalenceClass);
|
||||
|
||||
ec->ec_opfamilies = opfamilies;
|
||||
ec->ec_collation = collation;
|
||||
ec->ec_members = NIL;
|
||||
ec->ec_sources = list_make1(restrictinfo);
|
||||
ec->ec_derives = NIL;
|
||||
@@ -341,6 +364,84 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* canonicalize_ec_expression
|
||||
*
|
||||
* This function ensures that the expression exposes the expected type and
|
||||
* collation, so that it will be equal() to other equivalence-class expressions
|
||||
* that it ought to be equal() to.
|
||||
*
|
||||
* The rule for datatypes is that the exposed type should match what it would
|
||||
* be for an input to an operator of the EC's opfamilies; which is usually
|
||||
* the declared input type of the operator, but in the case of polymorphic
|
||||
* operators no relabeling is wanted (compare the behavior of parse_coerce.c).
|
||||
* Expressions coming in from quals will generally have the right type
|
||||
* already, but expressions coming from indexkeys may not (because they are
|
||||
* represented without any explicit relabel in pg_index), and the same problem
|
||||
* occurs for sort expressions (because the parser is likewise cavalier about
|
||||
* putting relabels on them). Such cases will be binary-compatible with the
|
||||
* real operators, so adding a RelabelType is sufficient.
|
||||
*
|
||||
* Also, the expression's exposed collation must match the EC's collation.
|
||||
* This is important because in comparisons like "foo < bar COLLATE baz",
|
||||
* only one of the expressions has the correct exposed collation as we receive
|
||||
* it from the parser. Forcing both of them to have it ensures that all
|
||||
* variant spellings of such a construct behave the same. Again, we can
|
||||
* stick on a RelabelType to force the right exposed collation. (It might
|
||||
* work to not label the collation at all in EC members, but this is risky
|
||||
* since some parts of the system expect exprCollation() to deliver the
|
||||
* right answer for a sort key.)
|
||||
*
|
||||
* Note this code assumes that the expression has already been through
|
||||
* eval_const_expressions, so there are no CollateExprs and no redundant
|
||||
* RelabelTypes.
|
||||
*/
|
||||
Expr *
|
||||
canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
|
||||
{
|
||||
Oid expr_type = exprType((Node *) expr);
|
||||
|
||||
/*
|
||||
* For a polymorphic-input-type opclass, just keep the same exposed type.
|
||||
*/
|
||||
if (IsPolymorphicType(req_type))
|
||||
req_type = expr_type;
|
||||
|
||||
/*
|
||||
* No work if the expression exposes the right type/collation already.
|
||||
*/
|
||||
if (expr_type != req_type ||
|
||||
exprCollation((Node *) expr) != req_collation)
|
||||
{
|
||||
/*
|
||||
* Strip any existing RelabelType, then add a new one if needed.
|
||||
* This is to preserve the invariant of no redundant RelabelTypes.
|
||||
*
|
||||
* If we have to change the exposed type of the stripped expression,
|
||||
* set typmod to -1 (since the new type may not have the same typmod
|
||||
* interpretation). If we only have to change collation, preserve
|
||||
* the exposed typmod.
|
||||
*/
|
||||
while (expr && IsA(expr, RelabelType))
|
||||
expr = (Expr *) ((RelabelType *) expr)->arg;
|
||||
|
||||
if (exprType((Node *) expr) != req_type)
|
||||
expr = (Expr *) makeRelabelType(expr,
|
||||
req_type,
|
||||
-1,
|
||||
req_collation,
|
||||
COERCE_DONTCARE);
|
||||
else if (exprCollation((Node *) expr) != req_collation)
|
||||
expr = (Expr *) makeRelabelType(expr,
|
||||
req_type,
|
||||
exprTypmod((Node *) expr),
|
||||
req_collation,
|
||||
COERCE_DONTCARE);
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
/*
|
||||
* add_eq_member - build a new EquivalenceMember and add it to an EC
|
||||
*/
|
||||
@@ -383,9 +484,9 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
|
||||
|
||||
/*
|
||||
* get_eclass_for_sort_expr
|
||||
* Given an expression and opfamily info, find an existing equivalence
|
||||
* class it is a member of; if none, optionally build a new single-member
|
||||
* EquivalenceClass for it.
|
||||
* Given an expression and opfamily/collation info, find an existing
|
||||
* equivalence class it is a member of; if none, optionally build a new
|
||||
* single-member EquivalenceClass for it.
|
||||
*
|
||||
* sortref is the SortGroupRef of the originating SortGroupClause, if any,
|
||||
* or zero if not. (It should never be zero if the expression is volatile!)
|
||||
@@ -406,8 +507,9 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
|
||||
EquivalenceClass *
|
||||
get_eclass_for_sort_expr(PlannerInfo *root,
|
||||
Expr *expr,
|
||||
Oid expr_datatype,
|
||||
List *opfamilies,
|
||||
Oid opcintype,
|
||||
Oid collation,
|
||||
Index sortref,
|
||||
bool create_it)
|
||||
{
|
||||
@@ -416,6 +518,11 @@ get_eclass_for_sort_expr(PlannerInfo *root,
|
||||
ListCell *lc1;
|
||||
MemoryContext oldcontext;
|
||||
|
||||
/*
|
||||
* Ensure the expression exposes the correct type and collation.
|
||||
*/
|
||||
expr = canonicalize_ec_expression(expr, opcintype, collation);
|
||||
|
||||
/*
|
||||
* Scan through the existing EquivalenceClasses for a match
|
||||
*/
|
||||
@@ -432,6 +539,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
|
||||
(sortref == 0 || sortref != cur_ec->ec_sortref))
|
||||
continue;
|
||||
|
||||
if (collation != cur_ec->ec_collation)
|
||||
continue;
|
||||
if (!equal(opfamilies, cur_ec->ec_opfamilies))
|
||||
continue;
|
||||
|
||||
@@ -447,7 +556,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
|
||||
cur_em->em_is_const)
|
||||
continue;
|
||||
|
||||
if (expr_datatype == cur_em->em_datatype &&
|
||||
if (opcintype == cur_em->em_datatype &&
|
||||
equal(expr, cur_em->em_expr))
|
||||
return cur_ec; /* Match! */
|
||||
}
|
||||
@@ -460,13 +569,13 @@ get_eclass_for_sort_expr(PlannerInfo *root,
|
||||
/*
|
||||
* OK, build a new single-member EC
|
||||
*
|
||||
* Here, we must be sure that we construct the EC in the right context. We
|
||||
* can assume, however, that the passed expr is long-lived.
|
||||
* Here, we must be sure that we construct the EC in the right context.
|
||||
*/
|
||||
oldcontext = MemoryContextSwitchTo(root->planner_cxt);
|
||||
|
||||
newec = makeNode(EquivalenceClass);
|
||||
newec->ec_opfamilies = list_copy(opfamilies);
|
||||
newec->ec_collation = collation;
|
||||
newec->ec_members = NIL;
|
||||
newec->ec_sources = NIL;
|
||||
newec->ec_derives = NIL;
|
||||
@@ -481,8 +590,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
|
||||
if (newec->ec_has_volatile && sortref == 0) /* should not happen */
|
||||
elog(ERROR, "volatile EquivalenceClass has no sortref");
|
||||
|
||||
newem = add_eq_member(newec, expr, pull_varnos((Node *) expr),
|
||||
false, expr_datatype);
|
||||
newem = add_eq_member(newec, copyObject(expr), pull_varnos((Node *) expr),
|
||||
false, opcintype);
|
||||
|
||||
/*
|
||||
* add_eq_member doesn't check for volatile functions, set-returning
|
||||
@@ -660,7 +769,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
|
||||
ec->ec_broken = true;
|
||||
break;
|
||||
}
|
||||
process_implied_equality(root, eq_op,
|
||||
process_implied_equality(root, eq_op, ec->ec_collation,
|
||||
cur_em->em_expr, const_em->em_expr,
|
||||
ec->ec_relids,
|
||||
ec->ec_below_outer_join,
|
||||
@@ -715,7 +824,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
|
||||
ec->ec_broken = true;
|
||||
break;
|
||||
}
|
||||
process_implied_equality(root, eq_op,
|
||||
process_implied_equality(root, eq_op, ec->ec_collation,
|
||||
prev_em->em_expr, cur_em->em_expr,
|
||||
ec->ec_relids,
|
||||
ec->ec_below_outer_join,
|
||||
@@ -1117,6 +1226,7 @@ create_join_clause(PlannerInfo *root,
|
||||
oldcontext = MemoryContextSwitchTo(root->planner_cxt);
|
||||
|
||||
rinfo = build_implied_join_equality(opno,
|
||||
ec->ec_collation,
|
||||
leftem->em_expr,
|
||||
rightem->em_expr,
|
||||
bms_union(leftem->em_relids,
|
||||
@@ -1338,6 +1448,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
|
||||
Expr *outervar,
|
||||
*innervar;
|
||||
Oid opno,
|
||||
collation,
|
||||
left_type,
|
||||
right_type,
|
||||
inner_datatype;
|
||||
@@ -1346,6 +1457,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
|
||||
|
||||
Assert(is_opclause(rinfo->clause));
|
||||
opno = ((OpExpr *) rinfo->clause)->opno;
|
||||
collation = ((OpExpr *) rinfo->clause)->inputcollid;
|
||||
|
||||
/* If clause is outerjoin_delayed, operator must be strict */
|
||||
if (rinfo->outerjoin_delayed && !op_strict(opno))
|
||||
@@ -1381,7 +1493,9 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
|
||||
/* Never match to a volatile EC */
|
||||
if (cur_ec->ec_has_volatile)
|
||||
continue;
|
||||
/* It has to match the outer-join clause as to opfamilies, too */
|
||||
/* It has to match the outer-join clause as to semantics, too */
|
||||
if (collation != cur_ec->ec_collation)
|
||||
continue;
|
||||
if (!equal(rinfo->mergeopfamilies, cur_ec->ec_opfamilies))
|
||||
continue;
|
||||
/* Does it contain a match to outervar? */
|
||||
@@ -1419,6 +1533,7 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
|
||||
if (!OidIsValid(eq_op))
|
||||
continue; /* can't generate equality */
|
||||
newrinfo = build_implied_join_equality(eq_op,
|
||||
cur_ec->ec_collation,
|
||||
innervar,
|
||||
cur_em->em_expr,
|
||||
inner_relids);
|
||||
@@ -1451,6 +1566,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
|
||||
Expr *leftvar;
|
||||
Expr *rightvar;
|
||||
Oid opno,
|
||||
collation,
|
||||
left_type,
|
||||
right_type;
|
||||
Relids left_relids,
|
||||
@@ -1464,6 +1580,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
|
||||
/* Extract needed info from the clause */
|
||||
Assert(is_opclause(rinfo->clause));
|
||||
opno = ((OpExpr *) rinfo->clause)->opno;
|
||||
collation = ((OpExpr *) rinfo->clause)->inputcollid;
|
||||
op_input_types(opno, &left_type, &right_type);
|
||||
leftvar = (Expr *) get_leftop(rinfo->clause);
|
||||
rightvar = (Expr *) get_rightop(rinfo->clause);
|
||||
@@ -1485,7 +1602,9 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
|
||||
/* Never match to a volatile EC */
|
||||
if (cur_ec->ec_has_volatile)
|
||||
continue;
|
||||
/* It has to match the outer-join clause as to opfamilies, too */
|
||||
/* It has to match the outer-join clause as to semantics, too */
|
||||
if (collation != cur_ec->ec_collation)
|
||||
continue;
|
||||
if (!equal(rinfo->mergeopfamilies, cur_ec->ec_opfamilies))
|
||||
continue;
|
||||
|
||||
@@ -1548,6 +1667,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
|
||||
if (OidIsValid(eq_op))
|
||||
{
|
||||
newrinfo = build_implied_join_equality(eq_op,
|
||||
cur_ec->ec_collation,
|
||||
leftvar,
|
||||
cur_em->em_expr,
|
||||
left_relids);
|
||||
@@ -1560,6 +1680,7 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
|
||||
if (OidIsValid(eq_op))
|
||||
{
|
||||
newrinfo = build_implied_join_equality(eq_op,
|
||||
cur_ec->ec_collation,
|
||||
rightvar,
|
||||
cur_em->em_expr,
|
||||
right_relids);
|
||||
|
@@ -1201,13 +1201,14 @@ match_clause_to_indexcol(IndexOptInfo *index,
|
||||
SaOpControl saop_control)
|
||||
{
|
||||
Expr *clause = rinfo->clause;
|
||||
Oid collation = index->indexcollations[indexcol];
|
||||
Oid opfamily = index->opfamily[indexcol];
|
||||
Oid collation = index->indexcollations[indexcol];
|
||||
Node *leftop,
|
||||
*rightop;
|
||||
Relids left_relids;
|
||||
Relids right_relids;
|
||||
Oid expr_op;
|
||||
Oid expr_coll;
|
||||
bool plain_op;
|
||||
|
||||
/*
|
||||
@@ -1241,6 +1242,7 @@ match_clause_to_indexcol(IndexOptInfo *index,
|
||||
left_relids = rinfo->left_relids;
|
||||
right_relids = rinfo->right_relids;
|
||||
expr_op = ((OpExpr *) clause)->opno;
|
||||
expr_coll = ((OpExpr *) clause)->inputcollid;
|
||||
plain_op = true;
|
||||
}
|
||||
else if (saop_control != SAOP_FORBID &&
|
||||
@@ -1256,6 +1258,7 @@ match_clause_to_indexcol(IndexOptInfo *index,
|
||||
left_relids = NULL; /* not actually needed */
|
||||
right_relids = pull_varnos(rightop);
|
||||
expr_op = saop->opno;
|
||||
expr_coll = saop->inputcollid;
|
||||
plain_op = false;
|
||||
}
|
||||
else if (clause && IsA(clause, RowCompareExpr))
|
||||
@@ -1284,8 +1287,8 @@ match_clause_to_indexcol(IndexOptInfo *index,
|
||||
bms_is_subset(right_relids, outer_relids) &&
|
||||
!contain_volatile_functions(rightop))
|
||||
{
|
||||
if (is_indexable_operator(expr_op, opfamily, true) &&
|
||||
(!collation || collation == exprCollation((Node *) clause)))
|
||||
if (collation == expr_coll &&
|
||||
is_indexable_operator(expr_op, opfamily, true))
|
||||
return true;
|
||||
|
||||
/*
|
||||
@@ -1303,8 +1306,8 @@ match_clause_to_indexcol(IndexOptInfo *index,
|
||||
bms_is_subset(left_relids, outer_relids) &&
|
||||
!contain_volatile_functions(leftop))
|
||||
{
|
||||
if (is_indexable_operator(expr_op, opfamily, false) &&
|
||||
(!collation || collation == exprCollation((Node *) clause)))
|
||||
if (collation == expr_coll &&
|
||||
is_indexable_operator(expr_op, opfamily, false))
|
||||
return true;
|
||||
|
||||
/*
|
||||
@@ -1397,7 +1400,7 @@ match_rowcompare_to_indexcol(IndexOptInfo *index,
|
||||
else
|
||||
return false;
|
||||
|
||||
if (index->indexcollations[indexcol] != linitial_oid(clause->collids))
|
||||
if (index->indexcollations[indexcol] != linitial_oid(clause->inputcollids))
|
||||
return false;
|
||||
|
||||
/* We're good if the operator is the right type of opfamily member */
|
||||
@@ -1808,6 +1811,7 @@ eclass_matches_any_index(EquivalenceClass *ec, EquivalenceMember *em,
|
||||
for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
|
||||
{
|
||||
Oid curFamily = index->opfamily[indexcol];
|
||||
Oid curCollation = index->indexcollations[indexcol];
|
||||
|
||||
/*
|
||||
* If it's a btree index, we can reject it if its opfamily isn't
|
||||
@@ -1818,9 +1822,12 @@ eclass_matches_any_index(EquivalenceClass *ec, EquivalenceMember *em,
|
||||
* mean we return "true" for a useless index, but that will just
|
||||
* cause some wasted planner cycles; it's better than ignoring
|
||||
* useful indexes.
|
||||
*
|
||||
* We insist on collation match for all index types, though.
|
||||
*/
|
||||
if ((index->relam != BTREE_AM_OID ||
|
||||
list_member_oid(ec->ec_opfamilies, curFamily)) &&
|
||||
ec->ec_collation == curCollation &&
|
||||
match_index_to_operand((Node *) em->em_expr, indexcol, index))
|
||||
return true;
|
||||
}
|
||||
@@ -2671,7 +2678,8 @@ expand_boolean_index_clause(Node *clause,
|
||||
/* convert to indexkey = TRUE */
|
||||
return make_opclause(BooleanEqualOperator, BOOLOID, false,
|
||||
(Expr *) clause,
|
||||
(Expr *) makeBoolConst(true, false));
|
||||
(Expr *) makeBoolConst(true, false),
|
||||
InvalidOid, InvalidOid);
|
||||
}
|
||||
/* NOT clause? */
|
||||
if (not_clause(clause))
|
||||
@@ -2683,7 +2691,8 @@ expand_boolean_index_clause(Node *clause,
|
||||
/* convert to indexkey = FALSE */
|
||||
return make_opclause(BooleanEqualOperator, BOOLOID, false,
|
||||
(Expr *) arg,
|
||||
(Expr *) makeBoolConst(false, false));
|
||||
(Expr *) makeBoolConst(false, false),
|
||||
InvalidOid, InvalidOid);
|
||||
}
|
||||
if (clause && IsA(clause, BooleanTest))
|
||||
{
|
||||
@@ -2697,14 +2706,16 @@ expand_boolean_index_clause(Node *clause,
|
||||
/* convert to indexkey = TRUE */
|
||||
return make_opclause(BooleanEqualOperator, BOOLOID, false,
|
||||
(Expr *) arg,
|
||||
(Expr *) makeBoolConst(true, false));
|
||||
(Expr *) makeBoolConst(true, false),
|
||||
InvalidOid, InvalidOid);
|
||||
}
|
||||
if (btest->booltesttype == IS_FALSE)
|
||||
{
|
||||
/* convert to indexkey = FALSE */
|
||||
return make_opclause(BooleanEqualOperator, BOOLOID, false,
|
||||
(Expr *) arg,
|
||||
(Expr *) makeBoolConst(false, false));
|
||||
(Expr *) makeBoolConst(false, false),
|
||||
InvalidOid, InvalidOid);
|
||||
}
|
||||
/* Oops */
|
||||
Assert(false);
|
||||
@@ -2876,7 +2887,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
|
||||
largs_cell = lnext(list_head(clause->largs));
|
||||
rargs_cell = lnext(list_head(clause->rargs));
|
||||
opnos_cell = lnext(list_head(clause->opnos));
|
||||
collids_cell = lnext(list_head(clause->collids));
|
||||
collids_cell = lnext(list_head(clause->inputcollids));
|
||||
|
||||
while (largs_cell != NULL)
|
||||
{
|
||||
@@ -3010,8 +3021,8 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
|
||||
rc->opnos = new_ops;
|
||||
rc->opfamilies = list_truncate(list_copy(clause->opfamilies),
|
||||
matching_cols);
|
||||
rc->collids = list_truncate(list_copy(clause->collids),
|
||||
matching_cols);
|
||||
rc->inputcollids = list_truncate(list_copy(clause->inputcollids),
|
||||
matching_cols);
|
||||
rc->largs = list_truncate((List *) copyObject(clause->largs),
|
||||
matching_cols);
|
||||
rc->rargs = list_truncate((List *) copyObject(clause->rargs),
|
||||
@@ -3024,7 +3035,9 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
|
||||
|
||||
opexpr = make_opclause(linitial_oid(new_ops), BOOLOID, false,
|
||||
copyObject(linitial(clause->largs)),
|
||||
copyObject(linitial(clause->rargs)));
|
||||
copyObject(linitial(clause->rargs)),
|
||||
InvalidOid,
|
||||
linitial_oid(clause->inputcollids));
|
||||
return make_simple_restrictinfo(opexpr);
|
||||
}
|
||||
}
|
||||
@@ -3033,7 +3046,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo,
|
||||
* Given a fixed prefix that all the "leftop" values must have,
|
||||
* generate suitable indexqual condition(s). opfamily is the index
|
||||
* operator family; we use it to deduce the appropriate comparison
|
||||
* operators and operand datatypes.
|
||||
* operators and operand datatypes. collation is the input collation to use.
|
||||
*/
|
||||
static List *
|
||||
prefix_quals(Node *leftop, Oid opfamily, Oid collation,
|
||||
@@ -3110,7 +3123,8 @@ prefix_quals(Node *leftop, Oid opfamily, Oid collation,
|
||||
if (oproid == InvalidOid)
|
||||
elog(ERROR, "no = operator for opfamily %u", opfamily);
|
||||
expr = make_opclause(oproid, BOOLOID, false,
|
||||
(Expr *) leftop, (Expr *) prefix_const);
|
||||
(Expr *) leftop, (Expr *) prefix_const,
|
||||
InvalidOid, collation);
|
||||
result = list_make1(make_simple_restrictinfo(expr));
|
||||
return result;
|
||||
}
|
||||
@@ -3125,7 +3139,8 @@ prefix_quals(Node *leftop, Oid opfamily, Oid collation,
|
||||
if (oproid == InvalidOid)
|
||||
elog(ERROR, "no >= operator for opfamily %u", opfamily);
|
||||
expr = make_opclause(oproid, BOOLOID, false,
|
||||
(Expr *) leftop, (Expr *) prefix_const);
|
||||
(Expr *) leftop, (Expr *) prefix_const,
|
||||
InvalidOid, collation);
|
||||
result = list_make1(make_simple_restrictinfo(expr));
|
||||
|
||||
/*-------
|
||||
@@ -3138,12 +3153,13 @@ prefix_quals(Node *leftop, Oid opfamily, Oid collation,
|
||||
if (oproid == InvalidOid)
|
||||
elog(ERROR, "no < operator for opfamily %u", opfamily);
|
||||
fmgr_info(get_opcode(oproid), <proc);
|
||||
fmgr_info_collation(collation, <proc);
|
||||
fmgr_info_set_collation(collation, <proc);
|
||||
greaterstr = make_greater_string(prefix_const, <proc);
|
||||
if (greaterstr)
|
||||
{
|
||||
expr = make_opclause(oproid, BOOLOID, false,
|
||||
(Expr *) leftop, (Expr *) greaterstr);
|
||||
(Expr *) leftop, (Expr *) greaterstr,
|
||||
InvalidOid, collation);
|
||||
result = lappend(result, make_simple_restrictinfo(expr));
|
||||
}
|
||||
|
||||
@@ -3206,7 +3222,8 @@ network_prefix_quals(Node *leftop, Oid expr_op, Oid opfamily, Datum rightop)
|
||||
expr = make_opclause(opr1oid, BOOLOID, false,
|
||||
(Expr *) leftop,
|
||||
(Expr *) makeConst(datatype, -1, -1, opr1right,
|
||||
false, false));
|
||||
false, false),
|
||||
InvalidOid, InvalidOid);
|
||||
result = list_make1(make_simple_restrictinfo(expr));
|
||||
|
||||
/* create clause "key <= network_scan_last( rightop )" */
|
||||
@@ -3221,7 +3238,8 @@ network_prefix_quals(Node *leftop, Oid expr_op, Oid opfamily, Datum rightop)
|
||||
expr = make_opclause(opr2oid, BOOLOID, false,
|
||||
(Expr *) leftop,
|
||||
(Expr *) makeConst(datatype, -1, -1, opr2right,
|
||||
false, false));
|
||||
false, false),
|
||||
InvalidOid, InvalidOid);
|
||||
result = lappend(result, make_simple_restrictinfo(expr));
|
||||
|
||||
return result;
|
||||
|
@@ -18,8 +18,6 @@
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/skey.h"
|
||||
#include "catalog/pg_collation.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
#include "nodes/plannodes.h"
|
||||
@@ -31,10 +29,10 @@
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
|
||||
static PathKey *makePathKey(EquivalenceClass *eclass, Oid opfamily, Oid collation,
|
||||
static PathKey *makePathKey(EquivalenceClass *eclass, Oid opfamily,
|
||||
int strategy, bool nulls_first);
|
||||
static PathKey *make_canonical_pathkey(PlannerInfo *root,
|
||||
EquivalenceClass *eclass, Oid opfamily, Oid collation,
|
||||
EquivalenceClass *eclass, Oid opfamily,
|
||||
int strategy, bool nulls_first);
|
||||
static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys);
|
||||
static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel,
|
||||
@@ -54,14 +52,13 @@ static bool right_merge_direction(PlannerInfo *root, PathKey *pathkey);
|
||||
* convenience routine to build the specified node.
|
||||
*/
|
||||
static PathKey *
|
||||
makePathKey(EquivalenceClass *eclass, Oid opfamily, Oid collation,
|
||||
makePathKey(EquivalenceClass *eclass, Oid opfamily,
|
||||
int strategy, bool nulls_first)
|
||||
{
|
||||
PathKey *pk = makeNode(PathKey);
|
||||
|
||||
pk->pk_eclass = eclass;
|
||||
pk->pk_opfamily = opfamily;
|
||||
pk->pk_collation = collation;
|
||||
pk->pk_strategy = strategy;
|
||||
pk->pk_nulls_first = nulls_first;
|
||||
|
||||
@@ -79,7 +76,7 @@ makePathKey(EquivalenceClass *eclass, Oid opfamily, Oid collation,
|
||||
*/
|
||||
static PathKey *
|
||||
make_canonical_pathkey(PlannerInfo *root,
|
||||
EquivalenceClass *eclass, Oid opfamily, Oid collation,
|
||||
EquivalenceClass *eclass, Oid opfamily,
|
||||
int strategy, bool nulls_first)
|
||||
{
|
||||
PathKey *pk;
|
||||
@@ -95,7 +92,6 @@ make_canonical_pathkey(PlannerInfo *root,
|
||||
pk = (PathKey *) lfirst(lc);
|
||||
if (eclass == pk->pk_eclass &&
|
||||
opfamily == pk->pk_opfamily &&
|
||||
collation == pk->pk_collation &&
|
||||
strategy == pk->pk_strategy &&
|
||||
nulls_first == pk->pk_nulls_first)
|
||||
return pk;
|
||||
@@ -107,7 +103,7 @@ make_canonical_pathkey(PlannerInfo *root,
|
||||
*/
|
||||
oldcontext = MemoryContextSwitchTo(root->planner_cxt);
|
||||
|
||||
pk = makePathKey(eclass, opfamily, collation, strategy, nulls_first);
|
||||
pk = makePathKey(eclass, opfamily, strategy, nulls_first);
|
||||
root->canon_pathkeys = lappend(root->canon_pathkeys, pk);
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
@@ -209,7 +205,6 @@ canonicalize_pathkeys(PlannerInfo *root, List *pathkeys)
|
||||
cpathkey = make_canonical_pathkey(root,
|
||||
eclass,
|
||||
pathkey->pk_opfamily,
|
||||
pathkey->pk_collation,
|
||||
pathkey->pk_strategy,
|
||||
pathkey->pk_nulls_first);
|
||||
|
||||
@@ -241,6 +236,7 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
|
||||
Expr *expr,
|
||||
Oid opfamily,
|
||||
Oid opcintype,
|
||||
Oid collation,
|
||||
bool reverse_sort,
|
||||
bool nulls_first,
|
||||
Index sortref,
|
||||
@@ -251,7 +247,6 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
|
||||
Oid equality_op;
|
||||
List *opfamilies;
|
||||
EquivalenceClass *eclass;
|
||||
Oid collation;
|
||||
|
||||
strategy = reverse_sort ? BTGreaterStrategyNumber : BTLessStrategyNumber;
|
||||
|
||||
@@ -273,47 +268,21 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
|
||||
elog(ERROR, "could not find opfamilies for equality operator %u",
|
||||
equality_op);
|
||||
|
||||
/*
|
||||
* When dealing with binary-compatible opclasses, we have to ensure that
|
||||
* the exposed type of the expression tree matches the declared input type
|
||||
* of the opclass, except when that is a polymorphic type (compare the
|
||||
* behavior of parse_coerce.c). This ensures that we can correctly match
|
||||
* the indexkey or sortclause expression to other expressions we find in
|
||||
* the query, because arguments of ordinary operator expressions will be
|
||||
* cast that way. (We have to do this for indexkeys because they are
|
||||
* represented without any explicit relabel in pg_index, and for sort
|
||||
* clauses because the parser is likewise cavalier about putting relabels
|
||||
* on them.)
|
||||
*/
|
||||
if (exprType((Node *) expr) != opcintype &&
|
||||
!IsPolymorphicType(opcintype))
|
||||
{
|
||||
/* Strip any existing RelabelType, and add a new one if needed */
|
||||
while (expr && IsA(expr, RelabelType))
|
||||
expr = (Expr *) ((RelabelType *) expr)->arg;
|
||||
if (exprType((Node *) expr) != opcintype)
|
||||
expr = (Expr *) makeRelabelType(expr,
|
||||
opcintype,
|
||||
-1,
|
||||
COERCE_DONTCARE);
|
||||
}
|
||||
|
||||
/* Now find or (optionally) create a matching EquivalenceClass */
|
||||
eclass = get_eclass_for_sort_expr(root, expr, opcintype, opfamilies,
|
||||
eclass = get_eclass_for_sort_expr(root, expr, opfamilies,
|
||||
opcintype, collation,
|
||||
sortref, create_it);
|
||||
|
||||
/* Fail if no EC and !create_it */
|
||||
if (!eclass)
|
||||
return NULL;
|
||||
|
||||
collation = exprCollation((Node *) expr);
|
||||
|
||||
/* And finally we can find or create a PathKey node */
|
||||
if (canonicalize)
|
||||
return make_canonical_pathkey(root, eclass, opfamily, collation,
|
||||
return make_canonical_pathkey(root, eclass, opfamily,
|
||||
strategy, nulls_first);
|
||||
else
|
||||
return makePathKey(eclass, opfamily, collation, strategy, nulls_first);
|
||||
return makePathKey(eclass, opfamily, strategy, nulls_first);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -333,7 +302,8 @@ make_pathkey_from_sortop(PlannerInfo *root,
|
||||
bool canonicalize)
|
||||
{
|
||||
Oid opfamily,
|
||||
opcintype;
|
||||
opcintype,
|
||||
collation;
|
||||
int16 strategy;
|
||||
|
||||
/* Find the operator in pg_amop --- failure shouldn't happen */
|
||||
@@ -341,10 +311,15 @@ make_pathkey_from_sortop(PlannerInfo *root,
|
||||
&opfamily, &opcintype, &strategy))
|
||||
elog(ERROR, "operator %u is not a valid ordering operator",
|
||||
ordering_op);
|
||||
|
||||
/* Because SortGroupClause doesn't carry collation, consult the expr */
|
||||
collation = exprCollation((Node *) expr);
|
||||
|
||||
return make_pathkey_from_sortinfo(root,
|
||||
expr,
|
||||
opfamily,
|
||||
opcintype,
|
||||
collation,
|
||||
(strategy == BTGreaterStrategyNumber),
|
||||
nulls_first,
|
||||
sortref,
|
||||
@@ -575,6 +550,7 @@ build_index_pathkeys(PlannerInfo *root,
|
||||
indexkey,
|
||||
index->sortopfamily[i],
|
||||
index->opcintype[i],
|
||||
index->indexcollations[i],
|
||||
reverse_sort,
|
||||
nulls_first,
|
||||
0,
|
||||
@@ -698,8 +674,9 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
|
||||
outer_ec =
|
||||
get_eclass_for_sort_expr(root,
|
||||
outer_expr,
|
||||
sub_member->em_datatype,
|
||||
sub_eclass->ec_opfamilies,
|
||||
sub_member->em_datatype,
|
||||
sub_eclass->ec_collation,
|
||||
0,
|
||||
false);
|
||||
|
||||
@@ -712,7 +689,6 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
|
||||
make_canonical_pathkey(root,
|
||||
outer_ec,
|
||||
sub_pathkey->pk_opfamily,
|
||||
sub_pathkey->pk_collation,
|
||||
sub_pathkey->pk_strategy,
|
||||
sub_pathkey->pk_nulls_first);
|
||||
}
|
||||
@@ -742,23 +718,14 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
|
||||
{
|
||||
EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
|
||||
Expr *sub_expr = sub_member->em_expr;
|
||||
Expr *sub_stripped;
|
||||
Oid sub_expr_type = sub_member->em_datatype;
|
||||
Oid sub_expr_coll = sub_eclass->ec_collation;
|
||||
ListCell *k;
|
||||
|
||||
/*
|
||||
* We handle two cases: the sub_pathkey key can be either an
|
||||
* exact match for a targetlist entry, or it could match after
|
||||
* stripping RelabelType nodes. (We need that case since
|
||||
* make_pathkey_from_sortinfo could add or remove
|
||||
* RelabelType.)
|
||||
*/
|
||||
sub_stripped = sub_expr;
|
||||
while (sub_stripped && IsA(sub_stripped, RelabelType))
|
||||
sub_stripped = ((RelabelType *) sub_stripped)->arg;
|
||||
|
||||
foreach(k, sub_tlist)
|
||||
{
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(k);
|
||||
Expr *tle_expr;
|
||||
Expr *outer_expr;
|
||||
EquivalenceClass *outer_ec;
|
||||
PathKey *outer_pk;
|
||||
@@ -768,40 +735,31 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
|
||||
if (tle->resjunk)
|
||||
continue;
|
||||
|
||||
if (equal(tle->expr, sub_expr))
|
||||
{
|
||||
/* Exact match */
|
||||
outer_expr = (Expr *) makeVarFromTargetEntry(rel->relid, tle);
|
||||
}
|
||||
else
|
||||
{
|
||||
Expr *tle_stripped;
|
||||
/*
|
||||
* 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;
|
||||
|
||||
tle_stripped = tle->expr;
|
||||
while (tle_stripped && IsA(tle_stripped, RelabelType))
|
||||
tle_stripped = ((RelabelType *) tle_stripped)->arg;
|
||||
/*
|
||||
* Build a representation of this targetlist entry as
|
||||
* an outer Var.
|
||||
*/
|
||||
outer_expr = (Expr *) makeVarFromTargetEntry(rel->relid,
|
||||
tle);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* Found a representation for this sub_pathkey */
|
||||
/* See if we have a matching EC for that */
|
||||
outer_ec = get_eclass_for_sort_expr(root,
|
||||
outer_expr,
|
||||
sub_member->em_datatype,
|
||||
sub_eclass->ec_opfamilies,
|
||||
sub_expr_type,
|
||||
sub_expr_coll,
|
||||
0,
|
||||
false);
|
||||
|
||||
@@ -815,7 +773,6 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
|
||||
outer_pk = make_canonical_pathkey(root,
|
||||
outer_ec,
|
||||
sub_pathkey->pk_opfamily,
|
||||
sub_pathkey->pk_collation,
|
||||
sub_pathkey->pk_strategy,
|
||||
sub_pathkey->pk_nulls_first);
|
||||
/* score = # of equivalence peers */
|
||||
@@ -1024,15 +981,17 @@ initialize_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo)
|
||||
restrictinfo->left_ec =
|
||||
get_eclass_for_sort_expr(root,
|
||||
(Expr *) get_leftop(clause),
|
||||
lefttype,
|
||||
restrictinfo->mergeopfamilies,
|
||||
lefttype,
|
||||
((OpExpr *) clause)->inputcollid,
|
||||
0,
|
||||
true);
|
||||
restrictinfo->right_ec =
|
||||
get_eclass_for_sort_expr(root,
|
||||
(Expr *) get_rightop(clause),
|
||||
righttype,
|
||||
restrictinfo->mergeopfamilies,
|
||||
righttype,
|
||||
((OpExpr *) clause)->inputcollid,
|
||||
0,
|
||||
true);
|
||||
}
|
||||
@@ -1337,7 +1296,6 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
|
||||
pathkey = make_canonical_pathkey(root,
|
||||
ec,
|
||||
linitial_oid(ec->ec_opfamilies),
|
||||
DEFAULT_COLLATION_OID,
|
||||
BTLessStrategyNumber,
|
||||
false);
|
||||
/* can't be redundant because no duplicate ECs */
|
||||
@@ -1431,7 +1389,6 @@ make_inner_pathkeys_for_merge(PlannerInfo *root,
|
||||
pathkey = make_canonical_pathkey(root,
|
||||
ieclass,
|
||||
opathkey->pk_opfamily,
|
||||
opathkey->pk_collation,
|
||||
opathkey->pk_strategy,
|
||||
opathkey->pk_nulls_first);
|
||||
|
||||
@@ -1552,7 +1509,6 @@ right_merge_direction(PlannerInfo *root, PathKey *pathkey)
|
||||
PathKey *query_pathkey = (PathKey *) lfirst(l);
|
||||
|
||||
if (pathkey->pk_eclass == query_pathkey->pk_eclass &&
|
||||
pathkey->pk_collation == query_pathkey->pk_collation &&
|
||||
pathkey->pk_opfamily == query_pathkey->pk_opfamily)
|
||||
{
|
||||
/*
|
||||
|
@@ -2148,14 +2148,14 @@ create_mergejoin_plan(PlannerInfo *root,
|
||||
|
||||
/* pathkeys should match each other too (more debugging) */
|
||||
if (opathkey->pk_opfamily != ipathkey->pk_opfamily ||
|
||||
opathkey->pk_collation != ipathkey->pk_collation ||
|
||||
opathkey->pk_eclass->ec_collation != ipathkey->pk_eclass->ec_collation ||
|
||||
opathkey->pk_strategy != ipathkey->pk_strategy ||
|
||||
opathkey->pk_nulls_first != ipathkey->pk_nulls_first)
|
||||
elog(ERROR, "left and right pathkeys do not match in mergejoin");
|
||||
|
||||
/* OK, save info for executor */
|
||||
mergefamilies[i] = opathkey->pk_opfamily;
|
||||
mergecollations[i] = opathkey->pk_collation;
|
||||
mergecollations[i] = opathkey->pk_eclass->ec_collation;
|
||||
mergestrategies[i] = opathkey->pk_strategy;
|
||||
mergenullsfirst[i] = opathkey->pk_nulls_first;
|
||||
i++;
|
||||
@@ -3603,7 +3603,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
|
||||
*/
|
||||
numsortkeys = add_sort_column(tle->resno,
|
||||
sortop,
|
||||
pathkey->pk_collation,
|
||||
pathkey->pk_eclass->ec_collation,
|
||||
pathkey->pk_nulls_first,
|
||||
numsortkeys,
|
||||
sortColIdx, sortOperators, collations, nullsFirst);
|
||||
|
@@ -1371,6 +1371,7 @@ distribute_restrictinfo_to_rels(PlannerInfo *root,
|
||||
void
|
||||
process_implied_equality(PlannerInfo *root,
|
||||
Oid opno,
|
||||
Oid collation,
|
||||
Expr *item1,
|
||||
Expr *item2,
|
||||
Relids qualscope,
|
||||
@@ -1387,7 +1388,9 @@ process_implied_equality(PlannerInfo *root,
|
||||
BOOLOID, /* opresulttype */
|
||||
false, /* opretset */
|
||||
(Expr *) copyObject(item1),
|
||||
(Expr *) copyObject(item2));
|
||||
(Expr *) copyObject(item2),
|
||||
InvalidOid,
|
||||
collation);
|
||||
|
||||
/* If both constant, try to reduce to a boolean constant. */
|
||||
if (both_const)
|
||||
@@ -1427,6 +1430,7 @@ process_implied_equality(PlannerInfo *root,
|
||||
*/
|
||||
RestrictInfo *
|
||||
build_implied_join_equality(Oid opno,
|
||||
Oid collation,
|
||||
Expr *item1,
|
||||
Expr *item2,
|
||||
Relids qualscope)
|
||||
@@ -1442,7 +1446,9 @@ build_implied_join_equality(Oid opno,
|
||||
BOOLOID, /* opresulttype */
|
||||
false, /* opretset */
|
||||
(Expr *) copyObject(item1),
|
||||
(Expr *) copyObject(item2));
|
||||
(Expr *) copyObject(item2),
|
||||
InvalidOid,
|
||||
collation);
|
||||
|
||||
/* Make a copy of qualscope to avoid problems if source EC changes */
|
||||
qualscope = bms_copy(qualscope);
|
||||
|
@@ -157,7 +157,7 @@ replace_outer_var(PlannerInfo *root, Var *var)
|
||||
retval->paramid = i;
|
||||
retval->paramtype = var->vartype;
|
||||
retval->paramtypmod = var->vartypmod;
|
||||
retval->paramcollation = var->varcollid;
|
||||
retval->paramcollid = var->varcollid;
|
||||
retval->location = -1;
|
||||
|
||||
return retval;
|
||||
@@ -186,7 +186,7 @@ assign_nestloop_param(PlannerInfo *root, Var *var)
|
||||
retval->paramid = i;
|
||||
retval->paramtype = var->vartype;
|
||||
retval->paramtypmod = var->vartypmod;
|
||||
retval->paramcollation = var->varcollid;
|
||||
retval->paramcollid = var->varcollid;
|
||||
retval->location = -1;
|
||||
|
||||
return retval;
|
||||
@@ -227,7 +227,7 @@ replace_outer_agg(PlannerInfo *root, Aggref *agg)
|
||||
retval->paramid = i;
|
||||
retval->paramtype = agg->aggtype;
|
||||
retval->paramtypmod = -1;
|
||||
retval->paramcollation = agg->collid;
|
||||
retval->paramcollid = agg->aggcollid;
|
||||
retval->location = -1;
|
||||
|
||||
return retval;
|
||||
@@ -239,7 +239,8 @@ replace_outer_agg(PlannerInfo *root, Aggref *agg)
|
||||
* This is used to allocate PARAM_EXEC slots for subplan outputs.
|
||||
*/
|
||||
static Param *
|
||||
generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod, Oid paramcollation)
|
||||
generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod,
|
||||
Oid paramcollation)
|
||||
{
|
||||
Param *retval;
|
||||
PlannerParamItem *pitem;
|
||||
@@ -249,7 +250,7 @@ generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod, Oid para
|
||||
retval->paramid = list_length(root->glob->paramlist);
|
||||
retval->paramtype = paramtype;
|
||||
retval->paramtypmod = paramtypmod;
|
||||
retval->paramcollation = paramcollation;
|
||||
retval->paramcollid = paramcollation;
|
||||
retval->location = -1;
|
||||
|
||||
pitem = makeNode(PlannerParamItem);
|
||||
@@ -282,10 +283,11 @@ SS_assign_special_param(PlannerInfo *root)
|
||||
/*
|
||||
* Get the datatype of the first column of the plan's output.
|
||||
*
|
||||
* This is stored for ARRAY_SUBLINK execution and for exprType()/exprTypmod()/exprCollation(),
|
||||
* which have no way to get at the plan associated with a SubPlan node.
|
||||
* We really only need the info for EXPR_SUBLINK and ARRAY_SUBLINK subplans,
|
||||
* but for consistency we save it always.
|
||||
* This information is stored for ARRAY_SUBLINK execution and for
|
||||
* exprType()/exprTypmod()/exprCollation(), which have no way to get at the
|
||||
* plan associated with a SubPlan node. We really only need the info for
|
||||
* EXPR_SUBLINK and ARRAY_SUBLINK subplans, but for consistency we save it
|
||||
* always.
|
||||
*/
|
||||
static void
|
||||
get_first_col_type(Plan *plan, Oid *coltype, int32 *coltypmod, Oid *colcollation)
|
||||
@@ -1395,13 +1397,15 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
|
||||
List *leftargs,
|
||||
*rightargs,
|
||||
*opids,
|
||||
*opcollations,
|
||||
*newWhere,
|
||||
*tlist,
|
||||
*testlist,
|
||||
*paramids;
|
||||
ListCell *lc,
|
||||
*rc,
|
||||
*oc;
|
||||
*oc,
|
||||
*cc;
|
||||
AttrNumber resno;
|
||||
|
||||
/*
|
||||
@@ -1465,7 +1469,7 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
|
||||
* we aren't trying hard yet to ensure that we have only outer or only
|
||||
* inner on each side; we'll check that if we get to the end.
|
||||
*/
|
||||
leftargs = rightargs = opids = newWhere = NIL;
|
||||
leftargs = rightargs = opids = opcollations = newWhere = NIL;
|
||||
foreach(lc, (List *) whereClause)
|
||||
{
|
||||
OpExpr *expr = (OpExpr *) lfirst(lc);
|
||||
@@ -1481,6 +1485,7 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
|
||||
leftargs = lappend(leftargs, leftarg);
|
||||
rightargs = lappend(rightargs, rightarg);
|
||||
opids = lappend_oid(opids, expr->opno);
|
||||
opcollations = lappend_oid(opcollations, expr->inputcollid);
|
||||
continue;
|
||||
}
|
||||
if (contain_vars_of_level(rightarg, 1))
|
||||
@@ -1497,6 +1502,7 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
|
||||
leftargs = lappend(leftargs, rightarg);
|
||||
rightargs = lappend(rightargs, leftarg);
|
||||
opids = lappend_oid(opids, expr->opno);
|
||||
opcollations = lappend_oid(opcollations, expr->inputcollid);
|
||||
continue;
|
||||
}
|
||||
/* If no commutator, no chance to optimize the WHERE clause */
|
||||
@@ -1565,16 +1571,17 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
|
||||
*/
|
||||
tlist = testlist = paramids = NIL;
|
||||
resno = 1;
|
||||
/* there's no "for3" so we have to chase one of the lists manually */
|
||||
oc = list_head(opids);
|
||||
forboth(lc, leftargs, rc, rightargs)
|
||||
/* there's no "forfour" so we have to chase one of the lists manually */
|
||||
cc = list_head(opcollations);
|
||||
forthree(lc, leftargs, rc, rightargs, oc, opids)
|
||||
{
|
||||
Node *leftarg = (Node *) lfirst(lc);
|
||||
Node *rightarg = (Node *) lfirst(rc);
|
||||
Oid opid = lfirst_oid(oc);
|
||||
Oid opcollation = lfirst_oid(cc);
|
||||
Param *param;
|
||||
|
||||
oc = lnext(oc);
|
||||
cc = lnext(cc);
|
||||
param = generate_new_param(root,
|
||||
exprType(rightarg),
|
||||
exprTypmod(rightarg),
|
||||
@@ -1586,7 +1593,8 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
|
||||
false));
|
||||
testlist = lappend(testlist,
|
||||
make_opclause(opid, BOOLOID, false,
|
||||
(Expr *) leftarg, (Expr *) param));
|
||||
(Expr *) leftarg, (Expr *) param,
|
||||
InvalidOid, opcollation));
|
||||
paramids = lappend_int(paramids, param->paramid);
|
||||
}
|
||||
|
||||
@@ -2360,7 +2368,7 @@ finalize_primnode(Node *node, finalize_primnode_context *context)
|
||||
/*
|
||||
* SS_make_initplan_from_plan - given a plan tree, make it an InitPlan
|
||||
*
|
||||
* The plan is expected to return a scalar value of the indicated type.
|
||||
* The plan is expected to return a scalar value of the given type/collation.
|
||||
* We build an EXPR_SUBLINK SubPlan node and put it into the initplan
|
||||
* list for the current query level. A Param that represents the initplan's
|
||||
* output is returned.
|
||||
@@ -2369,7 +2377,8 @@ finalize_primnode(Node *node, finalize_primnode_context *context)
|
||||
*/
|
||||
Param *
|
||||
SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
|
||||
Oid resulttype, int32 resulttypmod, Oid resultcollation)
|
||||
Oid resulttype, int32 resulttypmod,
|
||||
Oid resultcollation)
|
||||
{
|
||||
SubPlan *node;
|
||||
Param *prm;
|
||||
@@ -2405,7 +2414,8 @@ SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
|
||||
*/
|
||||
node = makeNode(SubPlan);
|
||||
node->subLinkType = EXPR_SUBLINK;
|
||||
get_first_col_type(plan, &node->firstColType, &node->firstColTypmod, &node->firstColCollation);
|
||||
get_first_col_type(plan, &node->firstColType, &node->firstColTypmod,
|
||||
&node->firstColCollation);
|
||||
node->plan_id = list_length(root->glob->subplans);
|
||||
|
||||
root->init_plans = lappend(root->init_plans, node);
|
||||
|
@@ -100,7 +100,8 @@ static List *simplify_and_arguments(List *args,
|
||||
bool *haveNull, bool *forceFalse);
|
||||
static Node *simplify_boolean_equality(Oid opno, List *args);
|
||||
static Expr *simplify_function(Oid funcid,
|
||||
Oid result_type, int32 result_typmod, Oid collid, List **args,
|
||||
Oid result_type, int32 result_typmod,
|
||||
Oid input_collid, List **args,
|
||||
bool has_named_args,
|
||||
bool allow_inline,
|
||||
eval_const_expressions_context *context);
|
||||
@@ -114,8 +115,8 @@ static List *fetch_function_defaults(HeapTuple func_tuple);
|
||||
static void recheck_cast_function_args(List *args, Oid result_type,
|
||||
HeapTuple func_tuple);
|
||||
static Expr *evaluate_function(Oid funcid,
|
||||
Oid result_type, int32 result_typmod, Oid collid, List *args,
|
||||
HeapTuple func_tuple,
|
||||
Oid result_type, int32 result_typmod,
|
||||
Oid input_collid, List *args, HeapTuple func_tuple,
|
||||
eval_const_expressions_context *context);
|
||||
static Expr *inline_function(Oid funcid, Oid result_type, List *args,
|
||||
HeapTuple func_tuple,
|
||||
@@ -139,12 +140,14 @@ static bool tlist_matches_coltypelist(List *tlist, List *coltypelist);
|
||||
|
||||
/*
|
||||
* make_opclause
|
||||
* Creates an operator clause given its operator info, left operand,
|
||||
* and right operand (pass NULL to create single-operand clause).
|
||||
* Creates an operator clause given its operator info, left operand
|
||||
* and right operand (pass NULL to create single-operand clause),
|
||||
* and collation info.
|
||||
*/
|
||||
Expr *
|
||||
make_opclause(Oid opno, Oid opresulttype, bool opretset,
|
||||
Expr *leftop, Expr *rightop)
|
||||
Expr *leftop, Expr *rightop,
|
||||
Oid opcollid, Oid inputcollid)
|
||||
{
|
||||
OpExpr *expr = makeNode(OpExpr);
|
||||
|
||||
@@ -152,11 +155,12 @@ make_opclause(Oid opno, Oid opresulttype, bool opretset,
|
||||
expr->opfuncid = InvalidOid;
|
||||
expr->opresulttype = opresulttype;
|
||||
expr->opretset = opretset;
|
||||
expr->opcollid = opcollid;
|
||||
expr->inputcollid = inputcollid;
|
||||
if (rightop)
|
||||
expr->args = list_make2(leftop, rightop);
|
||||
else
|
||||
expr->args = list_make1(leftop);
|
||||
expr->collid = select_common_collation(NULL, expr->args, false);
|
||||
expr->location = -1;
|
||||
return (Expr *) expr;
|
||||
}
|
||||
@@ -709,6 +713,8 @@ expression_returns_set_rows_walker(Node *node, double *count)
|
||||
return false;
|
||||
if (IsA(node, DistinctExpr))
|
||||
return false;
|
||||
if (IsA(node, NullIfExpr))
|
||||
return false;
|
||||
if (IsA(node, ScalarArrayOpExpr))
|
||||
return false;
|
||||
if (IsA(node, BoolExpr))
|
||||
@@ -731,8 +737,6 @@ expression_returns_set_rows_walker(Node *node, double *count)
|
||||
return false;
|
||||
if (IsA(node, XmlExpr))
|
||||
return false;
|
||||
if (IsA(node, NullIfExpr))
|
||||
return false;
|
||||
|
||||
return expression_tree_walker(node, expression_returns_set_rows_walker,
|
||||
(void *) count);
|
||||
@@ -826,6 +830,15 @@ contain_mutable_functions_walker(Node *node, void *context)
|
||||
return true;
|
||||
/* else fall through to check args */
|
||||
}
|
||||
else if (IsA(node, NullIfExpr))
|
||||
{
|
||||
NullIfExpr *expr = (NullIfExpr *) node;
|
||||
|
||||
set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */
|
||||
if (func_volatile(expr->opfuncid) != PROVOLATILE_IMMUTABLE)
|
||||
return true;
|
||||
/* else fall through to check args */
|
||||
}
|
||||
else if (IsA(node, ScalarArrayOpExpr))
|
||||
{
|
||||
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
|
||||
@@ -863,15 +876,6 @@ contain_mutable_functions_walker(Node *node, void *context)
|
||||
return true;
|
||||
/* else fall through to check args */
|
||||
}
|
||||
else if (IsA(node, NullIfExpr))
|
||||
{
|
||||
NullIfExpr *expr = (NullIfExpr *) node;
|
||||
|
||||
set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */
|
||||
if (func_volatile(expr->opfuncid) != PROVOLATILE_IMMUTABLE)
|
||||
return true;
|
||||
/* else fall through to check args */
|
||||
}
|
||||
else if (IsA(node, RowCompareExpr))
|
||||
{
|
||||
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
|
||||
@@ -941,6 +945,15 @@ contain_volatile_functions_walker(Node *node, void *context)
|
||||
return true;
|
||||
/* else fall through to check args */
|
||||
}
|
||||
else if (IsA(node, NullIfExpr))
|
||||
{
|
||||
NullIfExpr *expr = (NullIfExpr *) node;
|
||||
|
||||
set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */
|
||||
if (func_volatile(expr->opfuncid) == PROVOLATILE_VOLATILE)
|
||||
return true;
|
||||
/* else fall through to check args */
|
||||
}
|
||||
else if (IsA(node, ScalarArrayOpExpr))
|
||||
{
|
||||
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
|
||||
@@ -978,15 +991,6 @@ contain_volatile_functions_walker(Node *node, void *context)
|
||||
return true;
|
||||
/* else fall through to check args */
|
||||
}
|
||||
else if (IsA(node, NullIfExpr))
|
||||
{
|
||||
NullIfExpr *expr = (NullIfExpr *) node;
|
||||
|
||||
set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */
|
||||
if (func_volatile(expr->opfuncid) == PROVOLATILE_VOLATILE)
|
||||
return true;
|
||||
/* else fall through to check args */
|
||||
}
|
||||
else if (IsA(node, RowCompareExpr))
|
||||
{
|
||||
/* RowCompare probably can't have volatile ops, but check anyway */
|
||||
@@ -1071,6 +1075,8 @@ contain_nonstrict_functions_walker(Node *node, void *context)
|
||||
/* IS DISTINCT FROM is inherently non-strict */
|
||||
return true;
|
||||
}
|
||||
if (IsA(node, NullIfExpr))
|
||||
return true;
|
||||
if (IsA(node, ScalarArrayOpExpr))
|
||||
{
|
||||
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
|
||||
@@ -1119,8 +1125,6 @@ contain_nonstrict_functions_walker(Node *node, void *context)
|
||||
return true;
|
||||
if (IsA(node, XmlExpr))
|
||||
return true;
|
||||
if (IsA(node, NullIfExpr))
|
||||
return true;
|
||||
if (IsA(node, NullTest))
|
||||
return true;
|
||||
if (IsA(node, BooleanTest))
|
||||
@@ -1874,6 +1878,7 @@ CommuteRowCompareExpr(RowCompareExpr *clause)
|
||||
/*
|
||||
* Note: we need not change the opfamilies list; we assume any btree
|
||||
* opfamily containing an operator will also contain its commutator.
|
||||
* Collations don't change either.
|
||||
*/
|
||||
|
||||
temp = clause->largs;
|
||||
@@ -1986,7 +1991,8 @@ set_coercionform_dontcare_walker(Node *node, void *context)
|
||||
*/
|
||||
static bool
|
||||
rowtype_field_matches(Oid rowtypeid, int fieldnum,
|
||||
Oid expectedtype, int32 expectedtypmod, Oid expectedcollation)
|
||||
Oid expectedtype, int32 expectedtypmod,
|
||||
Oid expectedcollation)
|
||||
{
|
||||
TupleDesc tupdesc;
|
||||
Form_pg_attribute attr;
|
||||
@@ -2144,12 +2150,12 @@ eval_const_expressions_mutator(Node *node,
|
||||
else
|
||||
pval = datumCopy(prm->value, typByVal, typLen);
|
||||
cnst = makeConst(param->paramtype,
|
||||
param->paramtypmod,
|
||||
(int) typLen,
|
||||
pval,
|
||||
prm->isnull,
|
||||
typByVal);
|
||||
cnst->constcollid = param->paramcollation;
|
||||
param->paramtypmod,
|
||||
(int) typLen,
|
||||
pval,
|
||||
prm->isnull,
|
||||
typByVal);
|
||||
cnst->constcollid = param->paramcollid;
|
||||
return (Node *) cnst;
|
||||
}
|
||||
}
|
||||
@@ -2190,7 +2196,7 @@ eval_const_expressions_mutator(Node *node,
|
||||
*/
|
||||
simple = simplify_function(expr->funcid,
|
||||
expr->funcresulttype, exprTypmod(node),
|
||||
expr->collid,
|
||||
expr->inputcollid,
|
||||
&args,
|
||||
has_named_args, true, context);
|
||||
if (simple) /* successfully simplified it */
|
||||
@@ -2207,8 +2213,9 @@ eval_const_expressions_mutator(Node *node,
|
||||
newexpr->funcresulttype = expr->funcresulttype;
|
||||
newexpr->funcretset = expr->funcretset;
|
||||
newexpr->funcformat = expr->funcformat;
|
||||
newexpr->funccollid = expr->funccollid;
|
||||
newexpr->inputcollid = expr->inputcollid;
|
||||
newexpr->args = args;
|
||||
newexpr->collid = expr->collid;
|
||||
newexpr->location = expr->location;
|
||||
return (Node *) newexpr;
|
||||
}
|
||||
@@ -2240,7 +2247,7 @@ eval_const_expressions_mutator(Node *node,
|
||||
*/
|
||||
simple = simplify_function(expr->opfuncid,
|
||||
expr->opresulttype, -1,
|
||||
expr->collid,
|
||||
expr->inputcollid,
|
||||
&args,
|
||||
false, true, context);
|
||||
if (simple) /* successfully simplified it */
|
||||
@@ -2269,8 +2276,9 @@ eval_const_expressions_mutator(Node *node,
|
||||
newexpr->opfuncid = expr->opfuncid;
|
||||
newexpr->opresulttype = expr->opresulttype;
|
||||
newexpr->opretset = expr->opretset;
|
||||
newexpr->opcollid = expr->opcollid;
|
||||
newexpr->inputcollid = expr->inputcollid;
|
||||
newexpr->args = args;
|
||||
newexpr->collid = expr->collid;
|
||||
newexpr->location = expr->location;
|
||||
return (Node *) newexpr;
|
||||
}
|
||||
@@ -2335,7 +2343,7 @@ eval_const_expressions_mutator(Node *node,
|
||||
*/
|
||||
simple = simplify_function(expr->opfuncid,
|
||||
expr->opresulttype, -1,
|
||||
expr->collid,
|
||||
expr->inputcollid,
|
||||
&args,
|
||||
false, false, context);
|
||||
if (simple) /* successfully simplified it */
|
||||
@@ -2363,8 +2371,9 @@ eval_const_expressions_mutator(Node *node,
|
||||
newexpr->opfuncid = expr->opfuncid;
|
||||
newexpr->opresulttype = expr->opresulttype;
|
||||
newexpr->opretset = expr->opretset;
|
||||
newexpr->opcollid = expr->opcollid;
|
||||
newexpr->inputcollid = expr->inputcollid;
|
||||
newexpr->args = args;
|
||||
newexpr->collid = expr->collid;
|
||||
newexpr->location = expr->location;
|
||||
return (Node *) newexpr;
|
||||
}
|
||||
@@ -2473,6 +2482,7 @@ eval_const_expressions_mutator(Node *node,
|
||||
|
||||
con->consttype = relabel->resulttype;
|
||||
con->consttypmod = relabel->resulttypmod;
|
||||
con->constcollid = relabel->resultcollid;
|
||||
return (Node *) con;
|
||||
}
|
||||
else
|
||||
@@ -2482,6 +2492,7 @@ eval_const_expressions_mutator(Node *node,
|
||||
newrelabel->arg = (Expr *) arg;
|
||||
newrelabel->resulttype = relabel->resulttype;
|
||||
newrelabel->resulttypmod = relabel->resulttypmod;
|
||||
newrelabel->resultcollid = relabel->resultcollid;
|
||||
newrelabel->relabelformat = relabel->relabelformat;
|
||||
newrelabel->location = relabel->location;
|
||||
return (Node *) newrelabel;
|
||||
@@ -2511,12 +2522,16 @@ eval_const_expressions_mutator(Node *node,
|
||||
* then the result type's input function. So, try to simplify it as
|
||||
* though it were a stack of two such function calls. First we need
|
||||
* to know what the functions are.
|
||||
*
|
||||
* Note that the coercion functions are assumed not to care about
|
||||
* input collation, so we just pass InvalidOid for that.
|
||||
*/
|
||||
getTypeOutputInfo(exprType((Node *) arg), &outfunc, &outtypisvarlena);
|
||||
getTypeInputInfo(expr->resulttype, &infunc, &intypioparam);
|
||||
|
||||
simple = simplify_function(outfunc,
|
||||
CSTRINGOID, -1, InvalidOid,
|
||||
CSTRINGOID, -1,
|
||||
InvalidOid,
|
||||
&args,
|
||||
false, true, context);
|
||||
if (simple) /* successfully simplified output fn */
|
||||
@@ -2533,11 +2548,9 @@ eval_const_expressions_mutator(Node *node,
|
||||
Int32GetDatum(-1),
|
||||
false, true));
|
||||
|
||||
/* preserve collation of input expression */
|
||||
simple = simplify_function(infunc,
|
||||
expr->resulttype,
|
||||
-1,
|
||||
exprCollation((Node *) arg),
|
||||
expr->resulttype, -1,
|
||||
InvalidOid,
|
||||
&args,
|
||||
false, true, context);
|
||||
if (simple) /* successfully simplified input fn */
|
||||
@@ -2552,6 +2565,7 @@ eval_const_expressions_mutator(Node *node,
|
||||
newexpr = makeNode(CoerceViaIO);
|
||||
newexpr->arg = arg;
|
||||
newexpr->resulttype = expr->resulttype;
|
||||
newexpr->resultcollid = expr->resultcollid;
|
||||
newexpr->coerceformat = expr->coerceformat;
|
||||
newexpr->location = expr->location;
|
||||
return (Node *) newexpr;
|
||||
@@ -2574,6 +2588,7 @@ eval_const_expressions_mutator(Node *node,
|
||||
newexpr->elemfuncid = expr->elemfuncid;
|
||||
newexpr->resulttype = expr->resulttype;
|
||||
newexpr->resulttypmod = expr->resulttypmod;
|
||||
newexpr->resultcollid = expr->resultcollid;
|
||||
newexpr->isExplicit = expr->isExplicit;
|
||||
newexpr->coerceformat = expr->coerceformat;
|
||||
newexpr->location = expr->location;
|
||||
@@ -2596,8 +2611,10 @@ eval_const_expressions_mutator(Node *node,
|
||||
{
|
||||
/*
|
||||
* If we can simplify the input to a constant, then we don't need the
|
||||
* CollateExpr node anymore: just change the constcollid field of the
|
||||
* Const node. Otherwise, must copy the CollateExpr node.
|
||||
* CollateExpr node at all: just change the constcollid field of the
|
||||
* Const node. Otherwise, replace the CollateExpr with a RelabelType.
|
||||
* (We do that so as to improve uniformity of expression representation
|
||||
* and thus simplify comparison of expressions.)
|
||||
*/
|
||||
CollateExpr *collate = (CollateExpr *) node;
|
||||
Node *arg;
|
||||
@@ -2605,12 +2622,6 @@ eval_const_expressions_mutator(Node *node,
|
||||
arg = eval_const_expressions_mutator((Node *) collate->arg,
|
||||
context);
|
||||
|
||||
/*
|
||||
* If we find stacked CollateExprs, we can discard all but the top one.
|
||||
*/
|
||||
while (arg && IsA(arg, CollateExpr))
|
||||
arg = (Node *) ((CollateExpr *) arg)->arg;
|
||||
|
||||
if (arg && IsA(arg, Const))
|
||||
{
|
||||
Const *con = (Const *) arg;
|
||||
@@ -2618,14 +2629,27 @@ eval_const_expressions_mutator(Node *node,
|
||||
con->constcollid = collate->collOid;
|
||||
return (Node *) con;
|
||||
}
|
||||
else if (collate->collOid == exprCollation(arg))
|
||||
{
|
||||
/* Don't need a RelabelType either... */
|
||||
return arg;
|
||||
}
|
||||
else
|
||||
{
|
||||
CollateExpr *newcollate = makeNode(CollateExpr);
|
||||
RelabelType *relabel = makeNode(RelabelType);
|
||||
|
||||
newcollate->arg = (Expr *) arg;
|
||||
newcollate->collOid = collate->collOid;
|
||||
newcollate->location = collate->location;
|
||||
return (Node *) newcollate;
|
||||
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;
|
||||
|
||||
return (Node *) relabel;
|
||||
}
|
||||
}
|
||||
if (IsA(node, CaseExpr))
|
||||
@@ -2752,7 +2776,7 @@ eval_const_expressions_mutator(Node *node,
|
||||
/* Otherwise we need a new CASE node */
|
||||
newcase = makeNode(CaseExpr);
|
||||
newcase->casetype = caseexpr->casetype;
|
||||
newcase->casecollation = caseexpr->casecollation;
|
||||
newcase->casecollid = caseexpr->casecollid;
|
||||
newcase->arg = (Expr *) newarg;
|
||||
newcase->args = newargs;
|
||||
newcase->defresult = (Expr *) defresult;
|
||||
@@ -2793,6 +2817,7 @@ eval_const_expressions_mutator(Node *node,
|
||||
|
||||
newarray = makeNode(ArrayExpr);
|
||||
newarray->array_typeid = arrayexpr->array_typeid;
|
||||
newarray->array_collid = arrayexpr->array_collid;
|
||||
newarray->element_typeid = arrayexpr->element_typeid;
|
||||
newarray->elements = newelems;
|
||||
newarray->multidims = arrayexpr->multidims;
|
||||
@@ -2845,7 +2870,7 @@ eval_const_expressions_mutator(Node *node,
|
||||
|
||||
newcoalesce = makeNode(CoalesceExpr);
|
||||
newcoalesce->coalescetype = coalesceexpr->coalescetype;
|
||||
newcoalesce->coalescecollation = coalesceexpr->coalescecollation;
|
||||
newcoalesce->coalescecollid = coalesceexpr->coalescecollid;
|
||||
newcoalesce->args = newargs;
|
||||
newcoalesce->location = coalesceexpr->location;
|
||||
return (Node *) newcoalesce;
|
||||
@@ -2876,12 +2901,12 @@ eval_const_expressions_mutator(Node *node,
|
||||
fselect->fieldnum,
|
||||
fselect->resulttype,
|
||||
fselect->resulttypmod,
|
||||
fselect->resultcollation))
|
||||
fselect->resultcollid))
|
||||
return (Node *) makeVar(((Var *) arg)->varno,
|
||||
fselect->fieldnum,
|
||||
fselect->resulttype,
|
||||
fselect->resulttypmod,
|
||||
fselect->resultcollation,
|
||||
fselect->resultcollid,
|
||||
((Var *) arg)->varlevelsup);
|
||||
}
|
||||
if (arg && IsA(arg, RowExpr))
|
||||
@@ -2898,10 +2923,10 @@ eval_const_expressions_mutator(Node *node,
|
||||
fselect->fieldnum,
|
||||
fselect->resulttype,
|
||||
fselect->resulttypmod,
|
||||
fselect->resultcollation) &&
|
||||
fselect->resultcollid) &&
|
||||
fselect->resulttype == exprType(fld) &&
|
||||
fselect->resulttypmod == exprTypmod(fld) &&
|
||||
fselect->resultcollation == exprCollation(fld))
|
||||
fselect->resultcollid == exprCollation(fld))
|
||||
return fld;
|
||||
}
|
||||
}
|
||||
@@ -2910,7 +2935,7 @@ eval_const_expressions_mutator(Node *node,
|
||||
newfselect->fieldnum = fselect->fieldnum;
|
||||
newfselect->resulttype = fselect->resulttype;
|
||||
newfselect->resulttypmod = fselect->resulttypmod;
|
||||
newfselect->resultcollation = fselect->resultcollation;
|
||||
newfselect->resultcollid = fselect->resultcollid;
|
||||
return (Node *) newfselect;
|
||||
}
|
||||
if (IsA(node, NullTest))
|
||||
@@ -3355,7 +3380,8 @@ simplify_boolean_equality(Oid opno, List *args)
|
||||
* (which might originally have been an operator; we don't care)
|
||||
*
|
||||
* Inputs are the function OID, actual result type OID (which is needed for
|
||||
* polymorphic functions) and typmod, and the pre-simplified argument list;
|
||||
* polymorphic functions) and typmod, input collation to use for the function,
|
||||
* the pre-simplified argument list, and some flags;
|
||||
* also the context data for eval_const_expressions.
|
||||
*
|
||||
* Returns a simplified expression if successful, or NULL if cannot
|
||||
@@ -3368,7 +3394,8 @@ simplify_boolean_equality(Oid opno, List *args)
|
||||
* pass-by-reference, and it may get modified even if simplification fails.
|
||||
*/
|
||||
static Expr *
|
||||
simplify_function(Oid funcid, Oid result_type, int32 result_typmod, Oid collid,
|
||||
simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
|
||||
Oid input_collid,
|
||||
List **args,
|
||||
bool has_named_args,
|
||||
bool allow_inline,
|
||||
@@ -3399,7 +3426,8 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod, Oid collid,
|
||||
else if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args))
|
||||
*args = add_function_defaults(*args, result_type, func_tuple, context);
|
||||
|
||||
newexpr = evaluate_function(funcid, result_type, result_typmod, collid, *args,
|
||||
newexpr = evaluate_function(funcid, result_type, result_typmod,
|
||||
input_collid, *args,
|
||||
func_tuple, context);
|
||||
|
||||
if (!newexpr && allow_inline)
|
||||
@@ -3650,9 +3678,8 @@ recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple)
|
||||
* simplify the function.
|
||||
*/
|
||||
static Expr *
|
||||
evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, Oid collid,
|
||||
List *args,
|
||||
HeapTuple func_tuple,
|
||||
evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
|
||||
Oid input_collid, List *args, HeapTuple func_tuple,
|
||||
eval_const_expressions_context *context)
|
||||
{
|
||||
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
|
||||
@@ -3733,8 +3760,9 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, Oid collid,
|
||||
newexpr->funcresulttype = result_type;
|
||||
newexpr->funcretset = false;
|
||||
newexpr->funcformat = COERCE_DONTCARE; /* doesn't matter */
|
||||
newexpr->funccollid = InvalidOid; /* doesn't matter */
|
||||
newexpr->inputcollid = input_collid;
|
||||
newexpr->args = args;
|
||||
newexpr->collid = collid;
|
||||
newexpr->location = -1;
|
||||
|
||||
return evaluate_expr((Expr *) newexpr, result_type, result_typmod);
|
||||
|
@@ -1345,6 +1345,8 @@ btree_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
|
||||
*clause_const;
|
||||
bool pred_var_on_left,
|
||||
clause_var_on_left;
|
||||
Oid pred_collation,
|
||||
clause_collation;
|
||||
Oid pred_op,
|
||||
clause_op,
|
||||
test_op;
|
||||
@@ -1420,6 +1422,14 @@ btree_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
|
||||
if (!equal(pred_var, clause_var))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* They'd better have the same collation, too.
|
||||
*/
|
||||
pred_collation = ((OpExpr *) predicate)->inputcollid;
|
||||
clause_collation = ((OpExpr *) clause)->inputcollid;
|
||||
if (pred_collation != clause_collation)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Okay, get the operators in the two clauses we're comparing. Commute
|
||||
* them if needed so that we can assume the variables are on the left.
|
||||
@@ -1465,7 +1475,9 @@ btree_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
|
||||
BOOLOID,
|
||||
false,
|
||||
(Expr *) pred_const,
|
||||
(Expr *) clause_const);
|
||||
(Expr *) clause_const,
|
||||
InvalidOid,
|
||||
pred_collation);
|
||||
|
||||
/* Fill in opfuncids */
|
||||
fix_opfuncids((Node *) test_expr);
|
||||
|
Reference in New Issue
Block a user