mirror of
https://github.com/postgres/postgres.git
synced 2025-11-19 13:42:17 +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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user