aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/initsplan.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan/initsplan.c')
-rw-r--r--src/backend/optimizer/plan/initsplan.c213
1 files changed, 111 insertions, 102 deletions
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 0f4163bffd6..35b2dc10347 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -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;
}
/*