mirror of
https://github.com/postgres/postgres.git
synced 2025-07-14 08:21:07 +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)
|
||||
{
|
||||
/*
|
||||
|
Reference in New Issue
Block a user