mirror of
https://github.com/postgres/postgres.git
synced 2025-07-11 10:01:57 +03:00
Invent "join domains" to replace the below_outer_join hack.
EquivalenceClasses are now understood as applying within a "join domain", which is a set of inner-joined relations (possibly underneath an outer join). We no longer need to treat an EC from below an outer join as a second-class citizen. I have hopes of eventually being able to treat outer-join clauses via EquivalenceClasses, by means of only applying deductions within the EC's join domain. There are still problems in the way of that, though, so for now the reconsider_outer_join_clause logic is still here. I haven't been able to get rid of RestrictInfo.is_pushed_down either, but I wonder if that could be recast using JoinDomains. I had to hack one test case in postgres_fdw.sql to make it still test what it was meant to, because postgres_fdw is inconsistent about how it deals with quals containing non-shippable expressions; see https://postgr.es/m/1691374.1671659838@sss.pgh.pa.us. That should be improved, but I don't think it's within the scope of this patch series. Patch by me; thanks to Richard Guo for review. Discussion: https://postgr.es/m/830269.1656693747@sss.pgh.pa.us
This commit is contained in:
@ -61,7 +61,7 @@ typedef struct JoinTreeItem
|
||||
{
|
||||
/* Fields filled during deconstruct_recurse: */
|
||||
Node *jtnode; /* jointree node to examine */
|
||||
bool below_outer_join; /* is it below an outer join? */
|
||||
JoinDomain *jdomain; /* join domain for its ON/WHERE clauses */
|
||||
Relids qualscope; /* base+OJ Relids syntactically included in
|
||||
* this jointree node */
|
||||
Relids inner_join_rels; /* base+OJ Relids syntactically included
|
||||
@ -87,13 +87,13 @@ typedef struct PostponedQual
|
||||
static void extract_lateral_references(PlannerInfo *root, RelOptInfo *brel,
|
||||
Index rtindex);
|
||||
static List *deconstruct_recurse(PlannerInfo *root, Node *jtnode,
|
||||
bool below_outer_join,
|
||||
JoinDomain *parent_domain,
|
||||
List **item_list);
|
||||
static void deconstruct_distribute(PlannerInfo *root, JoinTreeItem *jtitem,
|
||||
List **postponed_qual_list);
|
||||
static void process_security_barrier_quals(PlannerInfo *root,
|
||||
int rti, Relids qualscope,
|
||||
bool below_outer_join);
|
||||
JoinDomain *jdomain);
|
||||
static void mark_rels_nulled_by_join(PlannerInfo *root, Index ojrelid,
|
||||
Relids lower_rels);
|
||||
static SpecialJoinInfo *make_outerjoininfo(PlannerInfo *root,
|
||||
@ -107,7 +107,7 @@ static void deconstruct_distribute_oj_quals(PlannerInfo *root,
|
||||
List *jtitems,
|
||||
JoinTreeItem *jtitem);
|
||||
static void distribute_quals_to_rels(PlannerInfo *root, List *clauses,
|
||||
bool below_outer_join,
|
||||
JoinDomain *jdomain,
|
||||
SpecialJoinInfo *sjinfo,
|
||||
Index security_level,
|
||||
Relids qualscope,
|
||||
@ -119,7 +119,7 @@ static void distribute_quals_to_rels(PlannerInfo *root, List *clauses,
|
||||
List **postponed_qual_list,
|
||||
List **postponed_oj_qual_list);
|
||||
static void distribute_qual_to_rels(PlannerInfo *root, Node *clause,
|
||||
bool below_outer_join,
|
||||
JoinDomain *jdomain,
|
||||
SpecialJoinInfo *sjinfo,
|
||||
Index security_level,
|
||||
Relids qualscope,
|
||||
@ -740,6 +740,7 @@ List *
|
||||
deconstruct_jointree(PlannerInfo *root)
|
||||
{
|
||||
List *result;
|
||||
JoinDomain *top_jdomain;
|
||||
List *item_list = NIL;
|
||||
List *postponed_qual_list = NIL;
|
||||
ListCell *lc;
|
||||
@ -751,6 +752,10 @@ deconstruct_jointree(PlannerInfo *root)
|
||||
*/
|
||||
root->placeholdersFrozen = true;
|
||||
|
||||
/* Fetch the already-created top-level join domain for the query */
|
||||
top_jdomain = linitial_node(JoinDomain, root->join_domains);
|
||||
top_jdomain->jd_relids = NULL; /* filled during deconstruct_recurse */
|
||||
|
||||
/* Start recursion at top of jointree */
|
||||
Assert(root->parse->jointree != NULL &&
|
||||
IsA(root->parse->jointree, FromExpr));
|
||||
@ -761,12 +766,15 @@ deconstruct_jointree(PlannerInfo *root)
|
||||
|
||||
/* Perform the initial scan of the jointree */
|
||||
result = deconstruct_recurse(root, (Node *) root->parse->jointree,
|
||||
false,
|
||||
top_jdomain,
|
||||
&item_list);
|
||||
|
||||
/* Now we can form the value of all_query_rels, too */
|
||||
root->all_query_rels = bms_union(root->all_baserels, root->outer_join_rels);
|
||||
|
||||
/* ... which should match what we computed for the top join domain */
|
||||
Assert(bms_equal(root->all_query_rels, top_jdomain->jd_relids));
|
||||
|
||||
/* Now scan all the jointree nodes again, and distribute quals */
|
||||
foreach(lc, item_list)
|
||||
{
|
||||
@ -804,10 +812,9 @@ deconstruct_jointree(PlannerInfo *root)
|
||||
* deconstruct_recurse
|
||||
* One recursion level of deconstruct_jointree's initial jointree scan.
|
||||
*
|
||||
* Inputs:
|
||||
* jtnode is the jointree node to examine
|
||||
* below_outer_join is true if this node is within the nullable side of a
|
||||
* higher-level outer join
|
||||
* jtnode is the jointree node to examine, and parent_domain is the
|
||||
* enclosing join domain. (We must add all base+OJ relids appearing
|
||||
* here or below to parent_domain.)
|
||||
*
|
||||
* item_list is an in/out parameter: we add a JoinTreeItem struct to
|
||||
* that list for each jointree node, in depth-first traversal order.
|
||||
@ -817,7 +824,7 @@ deconstruct_jointree(PlannerInfo *root)
|
||||
*/
|
||||
static List *
|
||||
deconstruct_recurse(PlannerInfo *root, Node *jtnode,
|
||||
bool below_outer_join,
|
||||
JoinDomain *parent_domain,
|
||||
List **item_list)
|
||||
{
|
||||
List *joinlist;
|
||||
@ -828,7 +835,6 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode,
|
||||
/* Make the new JoinTreeItem, but don't add it to item_list yet */
|
||||
jtitem = palloc0_object(JoinTreeItem);
|
||||
jtitem->jtnode = jtnode;
|
||||
jtitem->below_outer_join = below_outer_join;
|
||||
|
||||
if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
@ -836,6 +842,10 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode,
|
||||
|
||||
/* Fill all_baserels as we encounter baserel jointree nodes */
|
||||
root->all_baserels = bms_add_member(root->all_baserels, varno);
|
||||
/* This node belongs to parent_domain */
|
||||
jtitem->jdomain = parent_domain;
|
||||
parent_domain->jd_relids = bms_add_member(parent_domain->jd_relids,
|
||||
varno);
|
||||
/* qualscope is just the one RTE */
|
||||
jtitem->qualscope = bms_make_singleton(varno);
|
||||
/* A single baserel does not create an inner join */
|
||||
@ -848,6 +858,9 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode,
|
||||
int remaining;
|
||||
ListCell *l;
|
||||
|
||||
/* This node belongs to parent_domain, as do its children */
|
||||
jtitem->jdomain = parent_domain;
|
||||
|
||||
/*
|
||||
* Recurse to handle child nodes, and compute output joinlist. We
|
||||
* collapse subproblems into a single joinlist whenever the resulting
|
||||
@ -866,7 +879,7 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode,
|
||||
int sub_members;
|
||||
|
||||
sub_joinlist = deconstruct_recurse(root, lfirst(l),
|
||||
below_outer_join,
|
||||
parent_domain,
|
||||
item_list);
|
||||
sub_item = (JoinTreeItem *) llast(*item_list);
|
||||
jtitem->qualscope = bms_add_members(jtitem->qualscope,
|
||||
@ -894,6 +907,8 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode,
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) jtnode;
|
||||
JoinDomain *child_domain,
|
||||
*fj_domain;
|
||||
JoinTreeItem *left_item,
|
||||
*right_item;
|
||||
List *leftjoinlist,
|
||||
@ -902,13 +917,15 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode,
|
||||
switch (j->jointype)
|
||||
{
|
||||
case JOIN_INNER:
|
||||
/* This node belongs to parent_domain, as do its children */
|
||||
jtitem->jdomain = parent_domain;
|
||||
/* Recurse */
|
||||
leftjoinlist = deconstruct_recurse(root, j->larg,
|
||||
below_outer_join,
|
||||
parent_domain,
|
||||
item_list);
|
||||
left_item = (JoinTreeItem *) llast(*item_list);
|
||||
rightjoinlist = deconstruct_recurse(root, j->rarg,
|
||||
below_outer_join,
|
||||
parent_domain,
|
||||
item_list);
|
||||
right_item = (JoinTreeItem *) llast(*item_list);
|
||||
/* Compute qualscope etc */
|
||||
@ -922,21 +939,32 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode,
|
||||
break;
|
||||
case JOIN_LEFT:
|
||||
case JOIN_ANTI:
|
||||
/* Make new join domain for my quals and the RHS */
|
||||
child_domain = makeNode(JoinDomain);
|
||||
child_domain->jd_relids = NULL; /* filled by recursion */
|
||||
root->join_domains = lappend(root->join_domains, child_domain);
|
||||
jtitem->jdomain = child_domain;
|
||||
/* Recurse */
|
||||
leftjoinlist = deconstruct_recurse(root, j->larg,
|
||||
below_outer_join,
|
||||
parent_domain,
|
||||
item_list);
|
||||
left_item = (JoinTreeItem *) llast(*item_list);
|
||||
rightjoinlist = deconstruct_recurse(root, j->rarg,
|
||||
true,
|
||||
child_domain,
|
||||
item_list);
|
||||
right_item = (JoinTreeItem *) llast(*item_list);
|
||||
/* Compute qualscope etc */
|
||||
/* Compute join domain contents, qualscope etc */
|
||||
parent_domain->jd_relids =
|
||||
bms_add_members(parent_domain->jd_relids,
|
||||
child_domain->jd_relids);
|
||||
jtitem->qualscope = bms_union(left_item->qualscope,
|
||||
right_item->qualscope);
|
||||
/* caution: ANTI join derived from SEMI will lack rtindex */
|
||||
if (j->rtindex != 0)
|
||||
{
|
||||
parent_domain->jd_relids =
|
||||
bms_add_member(parent_domain->jd_relids,
|
||||
j->rtindex);
|
||||
jtitem->qualscope = bms_add_member(jtitem->qualscope,
|
||||
j->rtindex);
|
||||
root->outer_join_rels = bms_add_member(root->outer_join_rels,
|
||||
@ -951,13 +979,15 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode,
|
||||
jtitem->nonnullable_rels = left_item->qualscope;
|
||||
break;
|
||||
case JOIN_SEMI:
|
||||
/* This node belongs to parent_domain, as do its children */
|
||||
jtitem->jdomain = parent_domain;
|
||||
/* Recurse */
|
||||
leftjoinlist = deconstruct_recurse(root, j->larg,
|
||||
below_outer_join,
|
||||
parent_domain,
|
||||
item_list);
|
||||
left_item = (JoinTreeItem *) llast(*item_list);
|
||||
rightjoinlist = deconstruct_recurse(root, j->rarg,
|
||||
below_outer_join,
|
||||
parent_domain,
|
||||
item_list);
|
||||
right_item = (JoinTreeItem *) llast(*item_list);
|
||||
/* Compute qualscope etc */
|
||||
@ -973,19 +1003,36 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode,
|
||||
jtitem->nonnullable_rels = NULL;
|
||||
break;
|
||||
case JOIN_FULL:
|
||||
/* Recurse */
|
||||
/* The FULL JOIN's quals need their very own domain */
|
||||
fj_domain = makeNode(JoinDomain);
|
||||
root->join_domains = lappend(root->join_domains, fj_domain);
|
||||
jtitem->jdomain = fj_domain;
|
||||
/* Recurse, giving each side its own join domain */
|
||||
child_domain = makeNode(JoinDomain);
|
||||
child_domain->jd_relids = NULL; /* filled by recursion */
|
||||
root->join_domains = lappend(root->join_domains, child_domain);
|
||||
leftjoinlist = deconstruct_recurse(root, j->larg,
|
||||
true,
|
||||
child_domain,
|
||||
item_list);
|
||||
left_item = (JoinTreeItem *) llast(*item_list);
|
||||
fj_domain->jd_relids = bms_copy(child_domain->jd_relids);
|
||||
child_domain = makeNode(JoinDomain);
|
||||
child_domain->jd_relids = NULL; /* filled by recursion */
|
||||
root->join_domains = lappend(root->join_domains, child_domain);
|
||||
rightjoinlist = deconstruct_recurse(root, j->rarg,
|
||||
true,
|
||||
child_domain,
|
||||
item_list);
|
||||
right_item = (JoinTreeItem *) llast(*item_list);
|
||||
/* Compute qualscope etc */
|
||||
fj_domain->jd_relids = bms_add_members(fj_domain->jd_relids,
|
||||
child_domain->jd_relids);
|
||||
parent_domain->jd_relids = bms_add_members(parent_domain->jd_relids,
|
||||
fj_domain->jd_relids);
|
||||
jtitem->qualscope = bms_union(left_item->qualscope,
|
||||
right_item->qualscope);
|
||||
Assert(j->rtindex != 0);
|
||||
parent_domain->jd_relids = bms_add_member(parent_domain->jd_relids,
|
||||
j->rtindex);
|
||||
jtitem->qualscope = bms_add_member(jtitem->qualscope,
|
||||
j->rtindex);
|
||||
root->outer_join_rels = bms_add_member(root->outer_join_rels,
|
||||
@ -1087,7 +1134,7 @@ deconstruct_distribute(PlannerInfo *root, JoinTreeItem *jtitem,
|
||||
process_security_barrier_quals(root,
|
||||
varno,
|
||||
jtitem->qualscope,
|
||||
jtitem->below_outer_join);
|
||||
jtitem->jdomain);
|
||||
}
|
||||
else if (IsA(jtnode, FromExpr))
|
||||
{
|
||||
@ -1105,7 +1152,7 @@ deconstruct_distribute(PlannerInfo *root, JoinTreeItem *jtitem,
|
||||
|
||||
if (bms_is_subset(pq->relids, jtitem->qualscope))
|
||||
distribute_qual_to_rels(root, pq->qual,
|
||||
jtitem->below_outer_join,
|
||||
jtitem->jdomain,
|
||||
NULL,
|
||||
root->qual_security_level,
|
||||
jtitem->qualscope, NULL, NULL,
|
||||
@ -1120,7 +1167,7 @@ deconstruct_distribute(PlannerInfo *root, JoinTreeItem *jtitem,
|
||||
* Now process the top-level quals.
|
||||
*/
|
||||
distribute_quals_to_rels(root, (List *) f->quals,
|
||||
jtitem->below_outer_join,
|
||||
jtitem->jdomain,
|
||||
NULL,
|
||||
root->qual_security_level,
|
||||
jtitem->qualscope, NULL, NULL,
|
||||
@ -1221,7 +1268,7 @@ deconstruct_distribute(PlannerInfo *root, JoinTreeItem *jtitem,
|
||||
|
||||
/* Process the JOIN's qual clauses */
|
||||
distribute_quals_to_rels(root, my_quals,
|
||||
jtitem->below_outer_join,
|
||||
jtitem->jdomain,
|
||||
sjinfo,
|
||||
root->qual_security_level,
|
||||
jtitem->qualscope,
|
||||
@ -1258,7 +1305,7 @@ deconstruct_distribute(PlannerInfo *root, JoinTreeItem *jtitem,
|
||||
static void
|
||||
process_security_barrier_quals(PlannerInfo *root,
|
||||
int rti, Relids qualscope,
|
||||
bool below_outer_join)
|
||||
JoinDomain *jdomain)
|
||||
{
|
||||
RangeTblEntry *rte = root->simple_rte_array[rti];
|
||||
Index security_level = 0;
|
||||
@ -1281,7 +1328,7 @@ process_security_barrier_quals(PlannerInfo *root,
|
||||
* pushed up to top of tree, which we don't want.
|
||||
*/
|
||||
distribute_quals_to_rels(root, qualset,
|
||||
below_outer_join,
|
||||
jdomain,
|
||||
NULL,
|
||||
security_level,
|
||||
qualscope,
|
||||
@ -1991,7 +2038,7 @@ deconstruct_distribute_oj_quals(PlannerInfo *root,
|
||||
is_clone = !has_clone;
|
||||
|
||||
distribute_quals_to_rels(root, quals,
|
||||
true,
|
||||
otherjtitem->jdomain,
|
||||
sjinfo,
|
||||
root->qual_security_level,
|
||||
this_qualscope,
|
||||
@ -2020,7 +2067,7 @@ deconstruct_distribute_oj_quals(PlannerInfo *root,
|
||||
{
|
||||
/* No commutation possible, just process the postponed clauses */
|
||||
distribute_quals_to_rels(root, jtitem->oj_joinclauses,
|
||||
true,
|
||||
jtitem->jdomain,
|
||||
sjinfo,
|
||||
root->qual_security_level,
|
||||
qualscope,
|
||||
@ -2045,7 +2092,7 @@ deconstruct_distribute_oj_quals(PlannerInfo *root,
|
||||
*/
|
||||
static void
|
||||
distribute_quals_to_rels(PlannerInfo *root, List *clauses,
|
||||
bool below_outer_join,
|
||||
JoinDomain *jdomain,
|
||||
SpecialJoinInfo *sjinfo,
|
||||
Index security_level,
|
||||
Relids qualscope,
|
||||
@ -2064,7 +2111,7 @@ distribute_quals_to_rels(PlannerInfo *root, List *clauses,
|
||||
Node *clause = (Node *) lfirst(lc);
|
||||
|
||||
distribute_qual_to_rels(root, clause,
|
||||
below_outer_join,
|
||||
jdomain,
|
||||
sjinfo,
|
||||
security_level,
|
||||
qualscope,
|
||||
@ -2092,8 +2139,7 @@ distribute_quals_to_rels(PlannerInfo *root, List *clauses,
|
||||
* These will be dealt with in later steps of deconstruct_jointree.
|
||||
*
|
||||
* 'clause': the qual clause to be distributed
|
||||
* 'below_outer_join': true if the qual is from a JOIN/ON that is below the
|
||||
* nullable side of a higher-level outer join
|
||||
* 'jdomain': the join domain containing the clause
|
||||
* 'sjinfo': join's SpecialJoinInfo (NULL for an inner join or WHERE clause)
|
||||
* 'security_level': security_level to assign to the qual
|
||||
* 'qualscope': set of base+OJ rels the qual's syntactic scope covers
|
||||
@ -2124,7 +2170,7 @@ distribute_quals_to_rels(PlannerInfo *root, List *clauses,
|
||||
*/
|
||||
static void
|
||||
distribute_qual_to_rels(PlannerInfo *root, Node *clause,
|
||||
bool below_outer_join,
|
||||
JoinDomain *jdomain,
|
||||
SpecialJoinInfo *sjinfo,
|
||||
Index security_level,
|
||||
Relids qualscope,
|
||||
@ -2196,12 +2242,8 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
|
||||
* RestrictInfo lists for the moment, but eventually createplan.c will
|
||||
* pull it out and make a gating Result node immediately above whatever
|
||||
* plan node the pseudoconstant clause is assigned to. It's usually best
|
||||
* to put a gating node as high in the plan tree as possible. If we are
|
||||
* not below an outer join, we can actually push the pseudoconstant qual
|
||||
* all the way to the top of the tree. If we are below an outer join, we
|
||||
* leave the qual at its original syntactic level (we could push it up to
|
||||
* just below the outer join, but that seems more complex than it's
|
||||
* worth).
|
||||
* to put a gating node as high in the plan tree as possible, which we can
|
||||
* do by assigning it the full relid set of the current JoinDomain.
|
||||
*/
|
||||
if (bms_is_empty(relids))
|
||||
{
|
||||
@ -2211,25 +2253,20 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
|
||||
relids = bms_copy(ojscope);
|
||||
/* mustn't use as gating qual, so don't mark pseudoconstant */
|
||||
}
|
||||
else
|
||||
else if (contain_volatile_functions(clause))
|
||||
{
|
||||
/* eval at original syntactic level */
|
||||
relids = bms_copy(qualscope);
|
||||
if (!contain_volatile_functions(clause))
|
||||
{
|
||||
/* mark as gating qual */
|
||||
pseudoconstant = true;
|
||||
/* tell createplan.c to check for gating quals */
|
||||
root->hasPseudoConstantQuals = true;
|
||||
/* if not below outer join, push it to top of tree */
|
||||
if (!below_outer_join)
|
||||
{
|
||||
relids =
|
||||
get_relids_in_jointree((Node *) root->parse->jointree,
|
||||
true, false);
|
||||
qualscope = bms_copy(relids);
|
||||
}
|
||||
}
|
||||
/* again, can't mark pseudoconstant */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* eval at join domain level */
|
||||
relids = bms_copy(jdomain->jd_relids);
|
||||
/* mark as gating qual */
|
||||
pseudoconstant = true;
|
||||
/* tell createplan.c to check for gating quals */
|
||||
root->hasPseudoConstantQuals = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2319,23 +2356,8 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
|
||||
if (check_redundant_nullability_qual(root, clause))
|
||||
return;
|
||||
|
||||
if (!allow_equivalence)
|
||||
{
|
||||
/* Caller says it mustn't become an equivalence class */
|
||||
maybe_equivalence = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Consider feeding qual to the equivalence machinery. However,
|
||||
* if it's itself within an outer-join clause, treat it as though
|
||||
* it appeared below that outer join (note that we can only get
|
||||
* here when the clause references only nullable-side rels).
|
||||
*/
|
||||
maybe_equivalence = true;
|
||||
if (outerjoin_nonnullable != NULL)
|
||||
below_outer_join = true;
|
||||
}
|
||||
/* Feed qual to the equivalence machinery, if allowed by caller */
|
||||
maybe_equivalence = allow_equivalence;
|
||||
|
||||
/*
|
||||
* Since it doesn't mention the LHS, it's certainly not useful as a
|
||||
@ -2401,16 +2423,14 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
|
||||
check_mergejoinable(restrictinfo);
|
||||
|
||||
/*
|
||||
* XXX rewrite:
|
||||
*
|
||||
* If it is a true equivalence clause, send it to the EquivalenceClass
|
||||
* machinery. We do *not* attach it directly to any restriction or join
|
||||
* lists. The EC code will propagate it to the appropriate places later.
|
||||
*
|
||||
* If the clause has a mergejoinable operator and is not
|
||||
* outerjoin-delayed, yet isn't an equivalence because it is an outer-join
|
||||
* clause, the EC code may yet be able to do something with it. We add it
|
||||
* to appropriate lists for further consideration later. Specifically:
|
||||
* If the clause has a mergejoinable operator, yet isn't an equivalence
|
||||
* because it is an outer-join clause, the EC code may still be able to do
|
||||
* something with it. We add it to appropriate lists for further
|
||||
* consideration later. Specifically:
|
||||
*
|
||||
* If it is a left or right outer-join qualification that relates the two
|
||||
* sides of the outer join (no funny business like leftvar1 = leftvar2 +
|
||||
@ -2438,7 +2458,7 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
|
||||
{
|
||||
if (maybe_equivalence)
|
||||
{
|
||||
if (process_equivalence(root, &restrictinfo, below_outer_join))
|
||||
if (process_equivalence(root, &restrictinfo, jdomain))
|
||||
return;
|
||||
/* EC rejected it, so set left_ec/right_ec the hard way ... */
|
||||
if (restrictinfo->mergeopfamilies) /* EC might have changed this */
|
||||
@ -2628,8 +2648,9 @@ distribute_restrictinfo_to_rels(PlannerInfo *root,
|
||||
* "qualscope" is the nominal syntactic level to impute to the restrictinfo.
|
||||
* This must contain at least all the rels used in the expressions, but it
|
||||
* is used only to set the qual application level when both exprs are
|
||||
* variable-free. Otherwise the qual is applied at the lowest join level
|
||||
* that provides all its variables.
|
||||
* variable-free. (Hence, it should usually match the join domain in which
|
||||
* the clause applies.) Otherwise the qual is applied at the lowest join
|
||||
* level that provides all its variables.
|
||||
*
|
||||
* "security_level" is the security level to assign to the new restrictinfo.
|
||||
*
|
||||
@ -2657,7 +2678,6 @@ process_implied_equality(PlannerInfo *root,
|
||||
Expr *item2,
|
||||
Relids qualscope,
|
||||
Index security_level,
|
||||
bool below_outer_join,
|
||||
bool both_const)
|
||||
{
|
||||
RestrictInfo *restrictinfo;
|
||||
@ -2706,27 +2726,16 @@ process_implied_equality(PlannerInfo *root,
|
||||
/*
|
||||
* If the clause is variable-free, our normal heuristic for pushing it
|
||||
* down to just the mentioned rels doesn't work, because there are none.
|
||||
* Apply at the given qualscope, or at the top of tree if it's nonvolatile
|
||||
* (which it very likely is, but we'll check, just to be sure).
|
||||
* Apply it as a gating qual at the given qualscope.
|
||||
*/
|
||||
if (bms_is_empty(relids))
|
||||
{
|
||||
/* eval at original syntactic level */
|
||||
/* eval at join domain level */
|
||||
relids = bms_copy(qualscope);
|
||||
if (!contain_volatile_functions(clause))
|
||||
{
|
||||
/* mark as gating qual */
|
||||
pseudoconstant = true;
|
||||
/* tell createplan.c to check for gating quals */
|
||||
root->hasPseudoConstantQuals = true;
|
||||
/* if not below outer join, push it to top of tree */
|
||||
if (!below_outer_join)
|
||||
{
|
||||
relids =
|
||||
get_relids_in_jointree((Node *) root->parse->jointree,
|
||||
true, false);
|
||||
}
|
||||
}
|
||||
/* mark as gating qual */
|
||||
pseudoconstant = true;
|
||||
/* tell createplan.c to check for gating quals */
|
||||
root->hasPseudoConstantQuals = true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
Reference in New Issue
Block a user