/* Fields filled during deconstruct_recurse: */
Node *jtnode; /* jointree node to examine */
JoinDomain *jdomain; /* join domain for its ON/WHERE clauses */
+ struct JoinTreeItem *jti_parent; /* JoinTreeItem for this node's
+ * parent, or NULL if it's the top */
Relids qualscope; /* base+OJ Relids syntactically included in
* this jointree node */
Relids inner_join_rels; /* base+OJ Relids syntactically included
/* Fields filled during deconstruct_distribute: */
SpecialJoinInfo *sjinfo; /* if outer join, its SpecialJoinInfo */
List *oj_joinclauses; /* outer join quals not yet distributed */
+ List *lateral_clauses; /* quals postponed from children due to
+ * lateral references */
} JoinTreeItem;
-/* Elements of the postponed_qual_list used during deconstruct_distribute */
-typedef struct PostponedQual
-{
- Node *qual; /* a qual clause waiting to be processed */
- Relids relids; /* the set of baserels it references */
-} PostponedQual;
-
static void extract_lateral_references(PlannerInfo *root, RelOptInfo *brel,
Index rtindex);
static List *deconstruct_recurse(PlannerInfo *root, Node *jtnode,
JoinDomain *parent_domain,
+ JoinTreeItem *parent_jtitem,
List **item_list);
-static void deconstruct_distribute(PlannerInfo *root, JoinTreeItem *jtitem,
- List **postponed_qual_list);
+static void deconstruct_distribute(PlannerInfo *root, JoinTreeItem *jtitem);
static void process_security_barrier_quals(PlannerInfo *root,
- int rti, Relids qualscope,
- JoinDomain *jdomain);
+ int rti, JoinTreeItem *jtitem);
static void mark_rels_nulled_by_join(PlannerInfo *root, Index ojrelid,
Relids lower_rels);
static SpecialJoinInfo *make_outerjoininfo(PlannerInfo *root,
List *jtitems,
JoinTreeItem *jtitem);
static void distribute_quals_to_rels(PlannerInfo *root, List *clauses,
- JoinDomain *jdomain,
+ JoinTreeItem *jtitem,
SpecialJoinInfo *sjinfo,
Index security_level,
Relids qualscope,
bool allow_equivalence,
bool has_clone,
bool is_clone,
- List **postponed_qual_list,
List **postponed_oj_qual_list);
static void distribute_qual_to_rels(PlannerInfo *root, Node *clause,
- JoinDomain *jdomain,
+ JoinTreeItem *jtitem,
SpecialJoinInfo *sjinfo,
Index security_level,
Relids qualscope,
bool allow_equivalence,
bool has_clone,
bool is_clone,
- List **postponed_qual_list,
List **postponed_oj_qual_list);
static bool check_redundant_nullability_qual(PlannerInfo *root, Node *clause);
static void check_mergejoinable(RestrictInfo *restrictinfo);
List *result;
JoinDomain *top_jdomain;
List *item_list = NIL;
- List *postponed_qual_list = NIL;
ListCell *lc;
/*
/* Perform the initial scan of the jointree */
result = deconstruct_recurse(root, (Node *) root->parse->jointree,
- top_jdomain,
+ top_jdomain, NULL,
&item_list);
/* Now we can form the value of all_query_rels, too */
{
JoinTreeItem *jtitem = (JoinTreeItem *) lfirst(lc);
- deconstruct_distribute(root, jtitem,
- &postponed_qual_list);
+ deconstruct_distribute(root, jtitem);
}
- /* Shouldn't be any leftover postponed quals */
- Assert(postponed_qual_list == NIL);
-
/*
- * However, if there were any special joins then we may have some
- * postponed LEFT JOIN clauses to deal with.
+ * If there were any special joins then we may have some postponed LEFT
+ * JOIN clauses to deal with.
*/
if (root->join_info_list)
{
*
* 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.)
+ * here or below to parent_domain.) parent_jtitem is the JoinTreeItem
+ * for the parent jointree node, or NULL at the top of the recursion.
*
* item_list is an in/out parameter: we add a JoinTreeItem struct to
* that list for each jointree node, in depth-first traversal order.
static List *
deconstruct_recurse(PlannerInfo *root, Node *jtnode,
JoinDomain *parent_domain,
+ JoinTreeItem *parent_jtitem,
List **item_list)
{
List *joinlist;
/* Make the new JoinTreeItem, but don't add it to item_list yet */
jtitem = palloc0_object(JoinTreeItem);
jtitem->jtnode = jtnode;
+ jtitem->jti_parent = parent_jtitem;
if (IsA(jtnode, RangeTblRef))
{
sub_joinlist = deconstruct_recurse(root, lfirst(l),
parent_domain,
+ jtitem,
item_list);
sub_item = (JoinTreeItem *) llast(*item_list);
jtitem->qualscope = bms_add_members(jtitem->qualscope,
/* Recurse */
leftjoinlist = deconstruct_recurse(root, j->larg,
parent_domain,
+ jtitem,
item_list);
left_item = (JoinTreeItem *) llast(*item_list);
rightjoinlist = deconstruct_recurse(root, j->rarg,
parent_domain,
+ jtitem,
item_list);
right_item = (JoinTreeItem *) llast(*item_list);
/* Compute qualscope etc */
/* Recurse */
leftjoinlist = deconstruct_recurse(root, j->larg,
parent_domain,
+ jtitem,
item_list);
left_item = (JoinTreeItem *) llast(*item_list);
rightjoinlist = deconstruct_recurse(root, j->rarg,
child_domain,
+ jtitem,
item_list);
right_item = (JoinTreeItem *) llast(*item_list);
/* Compute join domain contents, qualscope etc */
/* Recurse */
leftjoinlist = deconstruct_recurse(root, j->larg,
parent_domain,
+ jtitem,
item_list);
left_item = (JoinTreeItem *) llast(*item_list);
rightjoinlist = deconstruct_recurse(root, j->rarg,
parent_domain,
+ jtitem,
item_list);
right_item = (JoinTreeItem *) llast(*item_list);
/* Compute qualscope etc */
root->join_domains = lappend(root->join_domains, child_domain);
leftjoinlist = deconstruct_recurse(root, j->larg,
child_domain,
+ jtitem,
item_list);
left_item = (JoinTreeItem *) llast(*item_list);
fj_domain->jd_relids = bms_copy(child_domain->jd_relids);
root->join_domains = lappend(root->join_domains, child_domain);
rightjoinlist = deconstruct_recurse(root, j->rarg,
child_domain,
+ jtitem,
item_list);
right_item = (JoinTreeItem *) llast(*item_list);
/* Compute qualscope etc */
*
* Distribute quals of the node to appropriate restriction and join lists.
* In addition, entries will be added to root->join_info_list for outer joins.
- *
- * Inputs:
- * jtitem is the JoinTreeItem to examine
- * Input/Outputs:
- * *postponed_qual_list is a list of PostponedQual structs
- *
- * On entry, *postponed_qual_list contains any quals that had to be postponed
- * out of lower join levels (because they contain lateral references).
- * On exit, *postponed_qual_list contains quals that can't be processed yet
- * (because their lateral references are still unsatisfied).
*/
static void
-deconstruct_distribute(PlannerInfo *root, JoinTreeItem *jtitem,
- List **postponed_qual_list)
+deconstruct_distribute(PlannerInfo *root, JoinTreeItem *jtitem)
{
Node *jtnode = jtitem->jtnode;
if (root->qual_security_level > 0)
process_security_barrier_quals(root,
varno,
- jtitem->qualscope,
- jtitem->jdomain);
+ jtitem);
}
else if (IsA(jtnode, FromExpr))
{
FromExpr *f = (FromExpr *) jtnode;
- List *new_postponed_quals = NIL;
- ListCell *l;
/*
- * Try to process any quals postponed by children. If they need
- * further postponement, add them to my output postponed_qual_list.
+ * Process any lateral-referencing quals that were postponed to this
+ * level by children.
*/
- foreach(l, *postponed_qual_list)
- {
- PostponedQual *pq = (PostponedQual *) lfirst(l);
-
- if (bms_is_subset(pq->relids, jtitem->qualscope))
- distribute_qual_to_rels(root, pq->qual,
- jtitem->jdomain,
- NULL,
- root->qual_security_level,
- jtitem->qualscope, NULL, NULL,
- true, false, false,
- NULL, NULL);
- else
- new_postponed_quals = lappend(new_postponed_quals, pq);
- }
- *postponed_qual_list = new_postponed_quals;
+ distribute_quals_to_rels(root, jtitem->lateral_clauses,
+ jtitem,
+ NULL,
+ root->qual_security_level,
+ jtitem->qualscope, NULL, NULL,
+ true, false, false,
+ NULL);
/*
* Now process the top-level quals.
*/
distribute_quals_to_rels(root, (List *) f->quals,
- jtitem->jdomain,
+ jtitem,
NULL,
root->qual_security_level,
jtitem->qualscope, NULL, NULL,
true, false, false,
- postponed_qual_list, NULL);
+ NULL);
}
else if (IsA(jtnode, JoinExpr))
{
JoinExpr *j = (JoinExpr *) jtnode;
- List *new_postponed_quals = NIL;
Relids ojscope;
List *my_quals;
SpecialJoinInfo *sjinfo;
List **postponed_oj_qual_list;
- ListCell *l;
/*
- * Try to process any quals postponed by children. If they need
- * further postponement, add them to my output postponed_qual_list.
- * Quals that can be processed now must be included in my_quals, so
- * that they'll be handled properly in make_outerjoininfo.
+ * Include lateral-referencing quals postponed from children in
+ * my_quals, so that they'll be handled properly in
+ * make_outerjoininfo. (This is destructive to
+ * jtitem->lateral_clauses, but we won't use that again.)
*/
- my_quals = NIL;
- foreach(l, *postponed_qual_list)
- {
- PostponedQual *pq = (PostponedQual *) lfirst(l);
-
- if (bms_is_subset(pq->relids, jtitem->qualscope))
- my_quals = lappend(my_quals, pq->qual);
- else
- {
- /*
- * We should not be postponing any quals past an outer join.
- * If this Assert fires, pull_up_subqueries() messed up.
- */
- Assert(j->jointype == JOIN_INNER);
- new_postponed_quals = lappend(new_postponed_quals, pq);
- }
- }
- *postponed_qual_list = new_postponed_quals;
- my_quals = list_concat(my_quals, (List *) j->quals);
+ my_quals = list_concat(jtitem->lateral_clauses,
+ (List *) j->quals);
/*
* For an OJ, form the SpecialJoinInfo now, so that we can pass it to
/* Process the JOIN's qual clauses */
distribute_quals_to_rels(root, my_quals,
- jtitem->jdomain,
+ jtitem,
sjinfo,
root->qual_security_level,
jtitem->qualscope,
ojscope, jtitem->nonnullable_rels,
true, /* allow_equivalence */
false, false, /* not clones */
- postponed_qual_list,
postponed_oj_qual_list);
/* And add the SpecialJoinInfo to join_info_list */
*/
static void
process_security_barrier_quals(PlannerInfo *root,
- int rti, Relids qualscope,
- JoinDomain *jdomain)
+ int rti, JoinTreeItem *jtitem)
{
RangeTblEntry *rte = root->simple_rte_array[rti];
Index security_level = 0;
* pushed up to top of tree, which we don't want.
*/
distribute_quals_to_rels(root, qualset,
- jdomain,
+ jtitem,
NULL,
security_level,
- qualscope,
- qualscope,
+ jtitem->qualscope,
+ jtitem->qualscope,
NULL,
true,
false, false, /* not clones */
- NULL,
NULL);
security_level++;
}
is_clone = !has_clone;
distribute_quals_to_rels(root, quals,
- otherjtitem->jdomain,
+ otherjtitem,
sjinfo,
root->qual_security_level,
this_qualscope,
allow_equivalence,
has_clone,
is_clone,
- NULL, NULL); /* no more postponement */
+ NULL); /* no more postponement */
/*
* Adjust qual nulling bits for next level up, if needed. We
{
/* No commutation possible, just process the postponed clauses */
distribute_quals_to_rels(root, jtitem->oj_joinclauses,
- jtitem->jdomain,
+ jtitem,
sjinfo,
root->qual_security_level,
qualscope,
ojscope, nonnullable_rels,
true, /* allow_equivalence */
false, false, /* not clones */
- NULL, NULL); /* no more postponement */
+ NULL); /* no more postponement */
}
}
*/
static void
distribute_quals_to_rels(PlannerInfo *root, List *clauses,
- JoinDomain *jdomain,
+ JoinTreeItem *jtitem,
SpecialJoinInfo *sjinfo,
Index security_level,
Relids qualscope,
bool allow_equivalence,
bool has_clone,
bool is_clone,
- List **postponed_qual_list,
List **postponed_oj_qual_list)
{
ListCell *lc;
Node *clause = (Node *) lfirst(lc);
distribute_qual_to_rels(root, clause,
- jdomain,
+ jtitem,
sjinfo,
security_level,
qualscope,
allow_equivalence,
has_clone,
is_clone,
- postponed_qual_list,
postponed_oj_qual_list);
}
}
* mergejoinable operator, enter its left- and right-side expressions into
* the query's EquivalenceClasses.
*
- * In some cases, quals will be added to postponed_qual_list or
- * postponed_oj_qual_list instead of being processed right away.
- * These will be dealt with in later steps of deconstruct_jointree.
+ * In some cases, quals will be added to parent jtitems' lateral_clauses
+ * or to postponed_oj_qual_list instead of being processed right away.
+ * These will be dealt with in later calls of deconstruct_distribute.
*
* 'clause': the qual clause to be distributed
- * 'jdomain': the join domain containing the clause
+ * 'jtitem': the JoinTreeItem for the containing jointree node
* '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
* EquivalenceClass
* 'has_clone': has_clone property to assign to the qual
* 'is_clone': is_clone property to assign to the qual
- * 'postponed_qual_list': list of PostponedQual structs, which we can add
- * this qual to if it turns out to belong to a higher join level.
- * Can be NULL if caller knows postponement is impossible.
* 'postponed_oj_qual_list': if not NULL, non-degenerate outer join clauses
* should be added to this list instead of being processed (list entries
* are just the bare clauses)
*/
static void
distribute_qual_to_rels(PlannerInfo *root, Node *clause,
- JoinDomain *jdomain,
+ JoinTreeItem *jtitem,
SpecialJoinInfo *sjinfo,
Index security_level,
Relids qualscope,
bool allow_equivalence,
bool has_clone,
bool is_clone,
- List **postponed_qual_list,
List **postponed_oj_qual_list)
{
Relids relids;
* level that includes every rel they reference. Although we could make
* pull_up_subqueries() place such quals correctly to begin with, it's
* easier to handle it here. When we find a clause that contains Vars
- * outside its syntactic scope, we add it to the postponed-quals list, and
- * process it once we've recursed back up to the appropriate join level.
+ * outside its syntactic scope, locate the nearest parent join level that
+ * includes all the required rels and add the clause to that level's
+ * lateral_clauses list. We'll process it when we reach that join level.
*/
if (!bms_is_subset(relids, qualscope))
{
- PostponedQual *pq = (PostponedQual *) palloc(sizeof(PostponedQual));
+ JoinTreeItem *pitem;
Assert(root->hasLateralRTEs); /* shouldn't happen otherwise */
Assert(sjinfo == NULL); /* mustn't postpone past outer join */
- pq->qual = clause;
- pq->relids = relids;
- *postponed_qual_list = lappend(*postponed_qual_list, pq);
- return;
+ for (pitem = jtitem->jti_parent; pitem; pitem = pitem->jti_parent)
+ {
+ if (bms_is_subset(relids, pitem->qualscope))
+ {
+ pitem->lateral_clauses = lappend(pitem->lateral_clauses,
+ clause);
+ return;
+ }
+
+ /*
+ * We should not be postponing any quals past an outer join. If
+ * this Assert fires, pull_up_subqueries() messed up.
+ */
+ Assert(pitem->sjinfo == NULL);
+ }
+ elog(ERROR, "failed to postpone qual containing lateral reference");
}
/*
else
{
/* eval at join domain level */
- relids = bms_copy(jdomain->jd_relids);
+ relids = bms_copy(jtitem->jdomain->jd_relids);
/* mark as gating qual */
pseudoconstant = true;
/* tell createplan.c to check for gating quals */
{
if (maybe_equivalence)
{
- if (process_equivalence(root, &restrictinfo, jdomain))
+ if (process_equivalence(root, &restrictinfo, jtitem->jdomain))
return;
/* EC rejected it, so set left_ec/right_ec the hard way ... */
if (restrictinfo->mergeopfamilies) /* EC might have changed this */