aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2015-12-11 15:52:16 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2015-12-11 15:52:39 -0500
commitdc4518814ecc2ae319c4d1679ee079e47dbd78e9 (patch)
tree5158f9a835583bc58b7f715784608c5d4b48cdf3 /src
parent12a54c888cf7bd9c37c4ce420e84cb52debe0184 (diff)
downloadpostgresql-dc4518814ecc2ae319c4d1679ee079e47dbd78e9.tar.gz
postgresql-dc4518814ecc2ae319c4d1679ee079e47dbd78e9.zip
Get rid of the planner's LateralJoinInfo data structure.
I originally modeled this data structure on SpecialJoinInfo, but after commit acfcd45cacb6df23 that looks like a pretty poor decision. All we really need is relid sets identifying laterally-referenced rels; and most of the time, what we want to know about includes indirect lateral references, a case the LateralJoinInfo data was unsuited to compute with any efficiency. The previous commit redefined RelOptInfo.lateral_relids as the transitive closure of lateral references, so that it easily supports checking indirect references. For the places where we really do want just direct references, add a new RelOptInfo field direct_lateral_relids, which is easily set up as a copy of lateral_relids before we perform the transitive closure calculation. Then we can just drop lateral_info_list and LateralJoinInfo and the supporting code. This makes the planner's handling of lateral references noticeably more efficient, and shorter too. Such a change can't be back-patched into stable branches for fear of breaking extensions that might be looking at the planner's data structures; but it seems not too late to push it into 9.5, so I've done so.
Diffstat (limited to 'src')
-rw-r--r--src/backend/nodes/copyfuncs.c17
-rw-r--r--src/backend/nodes/equalfuncs.c12
-rw-r--r--src/backend/nodes/outfuncs.c14
-rw-r--r--src/backend/optimizer/path/joinrels.c36
-rw-r--r--src/backend/optimizer/plan/analyzejoins.c3
-rw-r--r--src/backend/optimizer/plan/initsplan.c162
-rw-r--r--src/backend/optimizer/plan/planagg.c3
-rw-r--r--src/backend/optimizer/plan/planmain.c6
-rw-r--r--src/backend/optimizer/plan/planner.c3
-rw-r--r--src/backend/optimizer/prep/prepjointree.c9
-rw-r--r--src/backend/optimizer/prep/prepunion.c1
-rw-r--r--src/backend/optimizer/util/placeholder.c60
-rw-r--r--src/backend/optimizer/util/relnode.c17
-rw-r--r--src/backend/optimizer/util/var.c1
-rw-r--r--src/backend/rewrite/rewriteManip.c3
-rw-r--r--src/include/nodes/nodes.h1
-rw-r--r--src/include/nodes/relation.h41
17 files changed, 100 insertions, 289 deletions
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index e19fee4c318..3f19226b06a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2042,20 +2042,6 @@ _copySpecialJoinInfo(const SpecialJoinInfo *from)
}
/*
- * _copyLateralJoinInfo
- */
-static LateralJoinInfo *
-_copyLateralJoinInfo(const LateralJoinInfo *from)
-{
- LateralJoinInfo *newnode = makeNode(LateralJoinInfo);
-
- COPY_BITMAPSET_FIELD(lateral_lhs);
- COPY_BITMAPSET_FIELD(lateral_rhs);
-
- return newnode;
-}
-
-/*
* _copyAppendRelInfo
*/
static AppendRelInfo *
@@ -4479,9 +4465,6 @@ copyObject(const void *from)
case T_SpecialJoinInfo:
retval = _copySpecialJoinInfo(from);
break;
- case T_LateralJoinInfo:
- retval = _copyLateralJoinInfo(from);
- break;
case T_AppendRelInfo:
retval = _copyAppendRelInfo(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index fcdb3d02837..9a50523d476 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -846,15 +846,6 @@ _equalSpecialJoinInfo(const SpecialJoinInfo *a, const SpecialJoinInfo *b)
}
static bool
-_equalLateralJoinInfo(const LateralJoinInfo *a, const LateralJoinInfo *b)
-{
- COMPARE_BITMAPSET_FIELD(lateral_lhs);
- COMPARE_BITMAPSET_FIELD(lateral_rhs);
-
- return true;
-}
-
-static bool
_equalAppendRelInfo(const AppendRelInfo *a, const AppendRelInfo *b)
{
COMPARE_SCALAR_FIELD(parent_relid);
@@ -2850,9 +2841,6 @@ equal(const void *a, const void *b)
case T_SpecialJoinInfo:
retval = _equalSpecialJoinInfo(a, b);
break;
- case T_LateralJoinInfo:
- retval = _equalLateralJoinInfo(a, b);
- break;
case T_AppendRelInfo:
retval = _equalAppendRelInfo(a, b);
break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index bd84dbecbc1..2487f396cce 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1814,7 +1814,6 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node)
WRITE_NODE_FIELD(right_join_clauses);
WRITE_NODE_FIELD(full_join_clauses);
WRITE_NODE_FIELD(join_info_list);
- WRITE_NODE_FIELD(lateral_info_list);
WRITE_NODE_FIELD(append_rel_list);
WRITE_NODE_FIELD(rowMarks);
WRITE_NODE_FIELD(placeholder_list);
@@ -1858,6 +1857,7 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node)
WRITE_NODE_FIELD(cheapest_total_path);
WRITE_NODE_FIELD(cheapest_unique_path);
WRITE_NODE_FIELD(cheapest_parameterized_paths);
+ WRITE_BITMAPSET_FIELD(direct_lateral_relids);
WRITE_BITMAPSET_FIELD(lateral_relids);
WRITE_UINT_FIELD(relid);
WRITE_OID_FIELD(reltablespace);
@@ -2023,15 +2023,6 @@ _outSpecialJoinInfo(StringInfo str, const SpecialJoinInfo *node)
}
static void
-_outLateralJoinInfo(StringInfo str, const LateralJoinInfo *node)
-{
- WRITE_NODE_TYPE("LATERALJOININFO");
-
- WRITE_BITMAPSET_FIELD(lateral_lhs);
- WRITE_BITMAPSET_FIELD(lateral_rhs);
-}
-
-static void
_outAppendRelInfo(StringInfo str, const AppendRelInfo *node)
{
WRITE_NODE_TYPE("APPENDRELINFO");
@@ -3315,9 +3306,6 @@ _outNode(StringInfo str, const void *obj)
case T_SpecialJoinInfo:
_outSpecialJoinInfo(str, obj);
break;
- case T_LateralJoinInfo:
- _outLateralJoinInfo(str, obj);
- break;
case T_AppendRelInfo:
_outAppendRelInfo(str, obj);
break;
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 47837e1eda8..ad580581b5c 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -231,7 +231,7 @@ join_search_one_level(PlannerInfo *root, int level)
*/
if (joinrels[level] == NIL &&
root->join_info_list == NIL &&
- root->lateral_info_list == NIL)
+ !root->hasLateralRTEs)
elog(ERROR, "failed to build any %d-way joins", level);
}
}
@@ -559,15 +559,7 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
match_sjinfo->jointype == JOIN_FULL))
return false; /* not implementable as nestloop */
/* check there is a direct reference from rel2 to rel1 */
- foreach(l, root->lateral_info_list)
- {
- LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(l);
-
- if (bms_is_subset(ljinfo->lateral_rhs, rel2->relids) &&
- bms_is_subset(ljinfo->lateral_lhs, rel1->relids))
- break;
- }
- if (l == NULL)
+ if (!bms_overlap(rel1->relids, rel2->direct_lateral_relids))
return false; /* only indirect refs, so reject */
/* check we won't have a dangerous PHV */
if (have_dangerous_phv(root, rel1->relids, rel2->lateral_relids))
@@ -582,15 +574,7 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
match_sjinfo->jointype == JOIN_FULL))
return false; /* not implementable as nestloop */
/* check there is a direct reference from rel1 to rel2 */
- foreach(l, root->lateral_info_list)
- {
- LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(l);
-
- if (bms_is_subset(ljinfo->lateral_rhs, rel1->relids) &&
- bms_is_subset(ljinfo->lateral_lhs, rel2->relids))
- break;
- }
- if (l == NULL)
+ if (!bms_overlap(rel2->relids, rel1->direct_lateral_relids))
return false; /* only indirect refs, so reject */
/* check we won't have a dangerous PHV */
if (have_dangerous_phv(root, rel2->relids, rel1->lateral_relids))
@@ -922,17 +906,9 @@ have_join_order_restriction(PlannerInfo *root,
* If either side has a direct lateral reference to the other, attempt the
* join regardless of outer-join considerations.
*/
- foreach(l, root->lateral_info_list)
- {
- LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(l);
-
- if (bms_is_subset(ljinfo->lateral_rhs, rel2->relids) &&
- bms_overlap(ljinfo->lateral_lhs, rel1->relids))
- return true;
- if (bms_is_subset(ljinfo->lateral_rhs, rel1->relids) &&
- bms_overlap(ljinfo->lateral_lhs, rel2->relids))
- return true;
- }
+ if (bms_overlap(rel1->relids, rel2->direct_lateral_relids) ||
+ bms_overlap(rel2->relids, rel1->direct_lateral_relids))
+ return true;
/*
* Likewise, if both rels are needed to compute some PlaceHolderVar,
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 7912b153c59..d188d9724af 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -439,9 +439,6 @@ remove_rel_from_query(PlannerInfo *root, int relid, Relids joinrelids)
sjinfo->syn_righthand = bms_del_member(sjinfo->syn_righthand, relid);
}
- /* There shouldn't be any LATERAL info to translate, as yet */
- Assert(root->lateral_info_list == NIL);
-
/*
* Likewise remove references from PlaceHolderVar data structures,
* removing any no-longer-needed placeholders entirely.
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index dded896064d..f2dce6a2969 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -47,7 +47,6 @@ typedef struct PostponedQual
static void extract_lateral_references(PlannerInfo *root, RelOptInfo *brel,
Index rtindex);
-static void add_lateral_info(PlannerInfo *root, Relids lhs, Relids rhs);
static List *deconstruct_recurse(PlannerInfo *root, Node *jtnode,
bool below_outer_join,
Relids *qualscope, Relids *inner_join_rels,
@@ -382,11 +381,8 @@ extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex)
/*
* create_lateral_join_info
- * For each unflattened LATERAL subquery, create LateralJoinInfo(s) and add
- * them to root->lateral_info_list, and fill in the per-rel lateral_relids
- * and lateral_referencers sets. Also generate LateralJoinInfo(s) to
- * represent any lateral references within PlaceHolderVars (this part deals
- * with the effects of flattened LATERAL subqueries).
+ * Fill in the per-base-relation direct_lateral_relids, lateral_relids
+ * and lateral_referencers sets.
*
* This has to run after deconstruct_jointree, because we need to know the
* final ph_eval_at values for PlaceHolderVars.
@@ -394,6 +390,7 @@ extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex)
void
create_lateral_join_info(PlannerInfo *root)
{
+ bool found_laterals = false;
Index rti;
ListCell *lc;
@@ -430,8 +427,7 @@ create_lateral_join_info(PlannerInfo *root)
{
Var *var = (Var *) node;
- add_lateral_info(root, bms_make_singleton(var->varno),
- brel->relids);
+ found_laterals = true;
lateral_relids = bms_add_member(lateral_relids,
var->varno);
}
@@ -441,7 +437,7 @@ create_lateral_join_info(PlannerInfo *root)
PlaceHolderInfo *phinfo = find_placeholder_info(root, phv,
false);
- add_lateral_info(root, phinfo->ph_eval_at, brel->relids);
+ found_laterals = true;
lateral_relids = bms_add_members(lateral_relids,
phinfo->ph_eval_at);
}
@@ -449,69 +445,54 @@ create_lateral_join_info(PlannerInfo *root)
Assert(false);
}
- /* We now have all the direct lateral refs from this rel */
- brel->lateral_relids = lateral_relids;
+ /* We now have all the simple lateral refs from this rel */
+ brel->direct_lateral_relids = lateral_relids;
+ brel->lateral_relids = bms_copy(lateral_relids);
}
/*
- * Now check for lateral references within PlaceHolderVars, and make
- * LateralJoinInfos describing each such reference. Unlike references in
- * unflattened LATERAL RTEs, the referencing location could be a join.
+ * Now check for lateral references within PlaceHolderVars, and mark their
+ * eval_at rels as having lateral references to the source rels.
*
- * For a PHV that is due to be evaluated at a join, we mark each of the
- * join's member baserels as having the PHV's lateral references too. Even
- * though the baserels could be scanned without considering those lateral
- * refs, we will never be able to form the join except as a path
- * parameterized by the lateral refs, so there is no point in considering
- * unparameterized paths for the baserels; and we mustn't try to join any
- * of those baserels to the lateral refs too soon, either.
+ * For a PHV that is due to be evaluated at a baserel, mark its source(s)
+ * as direct lateral dependencies of the baserel (adding onto the ones
+ * recorded above). If it's due to be evaluated at a join, mark its
+ * source(s) as indirect lateral dependencies of each baserel in the join,
+ * ie put them into lateral_relids but not direct_lateral_relids. This is
+ * appropriate because we can't put any such baserel on the outside of a
+ * join to one of the PHV's lateral dependencies, but on the other hand we
+ * also can't yet join it directly to the dependency.
*/
foreach(lc, root->placeholder_list)
{
PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
Relids eval_at = phinfo->ph_eval_at;
+ int varno;
- if (phinfo->ph_lateral != NULL)
- {
- List *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
- PVC_RECURSE_AGGREGATES,
- PVC_INCLUDE_PLACEHOLDERS);
- ListCell *lc2;
- int ev_at;
-
- foreach(lc2, vars)
- {
- Node *node = (Node *) lfirst(lc2);
-
- if (IsA(node, Var))
- {
- Var *var = (Var *) node;
-
- if (!bms_is_member(var->varno, eval_at))
- add_lateral_info(root,
- bms_make_singleton(var->varno),
- eval_at);
- }
- else if (IsA(node, PlaceHolderVar))
- {
- PlaceHolderVar *other_phv = (PlaceHolderVar *) node;
- PlaceHolderInfo *other_phi;
-
- other_phi = find_placeholder_info(root, other_phv,
- false);
- if (!bms_is_subset(other_phi->ph_eval_at, eval_at))
- add_lateral_info(root, other_phi->ph_eval_at, eval_at);
- }
- else
- Assert(false);
- }
+ if (phinfo->ph_lateral == NULL)
+ continue; /* PHV is uninteresting if no lateral refs */
- list_free(vars);
+ found_laterals = true;
- ev_at = -1;
- while ((ev_at = bms_next_member(eval_at, ev_at)) >= 0)
+ if (bms_get_singleton_member(eval_at, &varno))
+ {
+ /* Evaluation site is a baserel */
+ RelOptInfo *brel = find_base_rel(root, varno);
+
+ brel->direct_lateral_relids =
+ bms_add_members(brel->direct_lateral_relids,
+ phinfo->ph_lateral);
+ brel->lateral_relids =
+ bms_add_members(brel->lateral_relids,
+ phinfo->ph_lateral);
+ }
+ else
+ {
+ /* Evaluation site is a join */
+ varno = -1;
+ while ((varno = bms_next_member(eval_at, varno)) >= 0)
{
- RelOptInfo *brel = find_base_rel(root, ev_at);
+ RelOptInfo *brel = find_base_rel(root, varno);
brel->lateral_relids = bms_add_members(brel->lateral_relids,
phinfo->ph_lateral);
@@ -519,17 +500,22 @@ create_lateral_join_info(PlannerInfo *root)
}
}
- /* If we found no lateral references, we're done. */
- if (root->lateral_info_list == NIL)
+ /*
+ * If we found no actual lateral references, we're done; but reset the
+ * hasLateralRTEs flag to avoid useless work later.
+ */
+ if (!found_laterals)
+ {
+ root->hasLateralRTEs = false;
return;
+ }
/*
- * At this point the lateral_relids sets represent only direct lateral
- * references. Replace them by their transitive closure, so that they
- * describe both direct and indirect lateral references. If relation X
- * references Y laterally, and Y references Z laterally, then we will have
- * to scan X on the inside of a nestloop with Z, so for all intents and
- * purposes X is laterally dependent on Z too.
+ * Calculate the transitive closure of the lateral_relids sets, so that
+ * they describe both direct and indirect lateral references. If relation
+ * X references Y laterally, and Y references Z laterally, then we will
+ * have to scan X on the inside of a nestloop with Z, so for all intents
+ * and purposes X is laterally dependent on Z too.
*
* This code is essentially Warshall's algorithm for transitive closure.
* The outer loop considers each baserel, and propagates its lateral
@@ -632,6 +618,8 @@ create_lateral_join_info(PlannerInfo *root)
continue;
childrel = root->simple_rel_array[appinfo->child_relid];
Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
+ Assert(childrel->direct_lateral_relids == NULL);
+ childrel->direct_lateral_relids = brel->direct_lateral_relids;
Assert(childrel->lateral_relids == NULL);
childrel->lateral_relids = brel->lateral_relids;
Assert(childrel->lateral_referencers == NULL);
@@ -641,46 +629,6 @@ create_lateral_join_info(PlannerInfo *root)
}
}
-/*
- * add_lateral_info
- * Add a LateralJoinInfo to root->lateral_info_list, if needed
- *
- * We suppress redundant list entries. The passed Relids are copied if saved.
- */
-static void
-add_lateral_info(PlannerInfo *root, Relids lhs, Relids rhs)
-{
- LateralJoinInfo *ljinfo;
- ListCell *lc;
-
- /* Sanity-check the input */
- Assert(!bms_is_empty(lhs));
- Assert(!bms_is_empty(rhs));
- Assert(!bms_overlap(lhs, rhs));
-
- /*
- * The input is redundant if it has the same RHS and an LHS that is a
- * subset of an existing entry's. If an existing entry has the same RHS
- * and an LHS that is a subset of the new one, it's redundant, but we
- * don't trouble to get rid of it. The only case that is really worth
- * worrying about is identical entries, and we handle that well enough
- * with this simple logic.
- */
- foreach(lc, root->lateral_info_list)
- {
- ljinfo = (LateralJoinInfo *) lfirst(lc);
- if (bms_equal(rhs, ljinfo->lateral_rhs) &&
- bms_is_subset(lhs, ljinfo->lateral_lhs))
- return;
- }
-
- /* Not there, so make a new entry */
- ljinfo = makeNode(LateralJoinInfo);
- ljinfo->lateral_lhs = bms_copy(lhs);
- ljinfo->lateral_rhs = bms_copy(rhs);
- root->lateral_info_list = lappend(root->lateral_info_list, ljinfo);
-}
-
/*****************************************************************************
*
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index f0e9c05a452..52bc18fda54 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -423,9 +423,8 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
subroot->parse = parse = (Query *) copyObject(root->parse);
/* make sure subroot planning won't change root->init_plans contents */
subroot->init_plans = list_copy(root->init_plans);
- /* There shouldn't be any OJ or LATERAL info to translate, as yet */
+ /* There shouldn't be any OJ info to translate, as yet */
Assert(subroot->join_info_list == NIL);
- Assert(subroot->lateral_info_list == NIL);
/* and we haven't created PlaceHolderInfos, either */
Assert(subroot->placeholder_list == NIL);
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 848df97013e..41cbec7d036 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -102,7 +102,6 @@ query_planner(PlannerInfo *root, List *tlist,
root->right_join_clauses = NIL;
root->full_join_clauses = NIL;
root->join_info_list = NIL;
- root->lateral_info_list = NIL;
root->placeholder_list = NIL;
root->initial_rels = NIL;
@@ -189,9 +188,8 @@ query_planner(PlannerInfo *root, List *tlist,
add_placeholders_to_base_rels(root);
/*
- * Create the LateralJoinInfo list now that we have finalized
- * PlaceHolderVar eval levels and made any necessary additions to the
- * lateral_vars lists for lateral references within PlaceHolderVars.
+ * Construct the lateral reference sets now that we have finalized
+ * PlaceHolderVar eval levels.
*/
create_lateral_join_info(root);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 09d4ea12e87..5a3eaefebdb 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1052,9 +1052,8 @@ inheritance_planner(PlannerInfo *root)
}
}
- /* There shouldn't be any OJ or LATERAL info to translate, as yet */
+ /* There shouldn't be any OJ info to translate, as yet */
Assert(subroot.join_info_list == NIL);
- Assert(subroot.lateral_info_list == NIL);
/* and we haven't created PlaceHolderInfos, either */
Assert(subroot.placeholder_list == NIL);
/* hack to mark target relation as an inheritance partition */
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 9bf1c662b53..39cce846024 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1149,14 +1149,11 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
subroot->append_rel_list);
/*
- * We don't have to do the equivalent bookkeeping for outer-join or
- * LATERAL info, because that hasn't been set up yet. placeholder_list
- * likewise.
+ * We don't have to do the equivalent bookkeeping for outer-join info,
+ * because that hasn't been set up yet. placeholder_list likewise.
*/
Assert(root->join_info_list == NIL);
Assert(subroot->join_info_list == NIL);
- Assert(root->lateral_info_list == NIL);
- Assert(subroot->lateral_info_list == NIL);
Assert(root->placeholder_list == NIL);
Assert(subroot->placeholder_list == NIL);
@@ -1641,7 +1638,6 @@ pull_up_simple_values(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
Assert(root->append_rel_list == NIL);
Assert(list_length(parse->rtable) == 1);
Assert(root->join_info_list == NIL);
- Assert(root->lateral_info_list == NIL);
Assert(root->placeholder_list == NIL);
/*
@@ -2838,7 +2834,6 @@ substitute_multiple_relids_walker(Node *node,
}
/* Shouldn't need to handle planner auxiliary nodes here */
Assert(!IsA(node, SpecialJoinInfo));
- Assert(!IsA(node, LateralJoinInfo));
Assert(!IsA(node, AppendRelInfo));
Assert(!IsA(node, PlaceHolderInfo));
Assert(!IsA(node, MinMaxAggInfo));
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 8884fb1bae3..2e55131a591 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1786,7 +1786,6 @@ adjust_appendrel_attrs_mutator(Node *node,
}
/* Shouldn't need to handle planner auxiliary nodes here */
Assert(!IsA(node, SpecialJoinInfo));
- Assert(!IsA(node, LateralJoinInfo));
Assert(!IsA(node, AppendRelInfo));
Assert(!IsA(node, PlaceHolderInfo));
Assert(!IsA(node, MinMaxAggInfo));
diff --git a/src/backend/optimizer/util/placeholder.c b/src/backend/optimizer/util/placeholder.c
index 287031547a3..7fa93fb29ac 100644
--- a/src/backend/optimizer/util/placeholder.c
+++ b/src/backend/optimizer/util/placeholder.c
@@ -363,12 +363,10 @@ fix_placeholder_input_needed_levels(PlannerInfo *root)
/*
* add_placeholders_to_base_rels
- * Add any required PlaceHolderVars to base rels' targetlists, and
- * update lateral_vars lists for lateral references contained in them.
+ * Add any required PlaceHolderVars to base rels' targetlists.
*
* If any placeholder can be computed at a base rel and is needed above it,
- * add it to that rel's targetlist, and add any lateral references it requires
- * to the rel's lateral_vars list. This might look like it could be merged
+ * add it to that rel's targetlist. This might look like it could be merged
* with fix_placeholder_input_needed_levels, but it must be separate because
* join removal happens in between, and can change the ph_eval_at sets. There
* is essentially the same logic in add_placeholders_to_joinrel, but we can't
@@ -385,58 +383,22 @@ add_placeholders_to_base_rels(PlannerInfo *root)
Relids eval_at = phinfo->ph_eval_at;
int varno;
- if (bms_get_singleton_member(eval_at, &varno))
+ if (bms_get_singleton_member(eval_at, &varno) &&
+ bms_nonempty_difference(phinfo->ph_needed, eval_at))
{
RelOptInfo *rel = find_base_rel(root, varno);
- /* add it to reltargetlist if needed above the rel scan level */
- if (bms_nonempty_difference(phinfo->ph_needed, eval_at))
- rel->reltargetlist = lappend(rel->reltargetlist,
- copyObject(phinfo->ph_var));
- /* if there are lateral refs in it, add them to lateral_vars */
- if (phinfo->ph_lateral != NULL)
- {
- List *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
- PVC_RECURSE_AGGREGATES,
- PVC_INCLUDE_PLACEHOLDERS);
- ListCell *lc2;
-
- foreach(lc2, vars)
- {
- Node *node = (Node *) lfirst(lc2);
-
- if (IsA(node, Var))
- {
- Var *var = (Var *) node;
-
- if (var->varno != varno)
- rel->lateral_vars = lappend(rel->lateral_vars,
- var);
- }
- else if (IsA(node, PlaceHolderVar))
- {
- PlaceHolderVar *other_phv = (PlaceHolderVar *) node;
- PlaceHolderInfo *other_phi;
-
- other_phi = find_placeholder_info(root, other_phv,
- false);
- if (!bms_is_subset(other_phi->ph_eval_at, eval_at))
- rel->lateral_vars = lappend(rel->lateral_vars,
- other_phv);
- }
- else
- Assert(false);
- }
-
- list_free(vars);
- }
+ rel->reltargetlist = lappend(rel->reltargetlist,
+ copyObject(phinfo->ph_var));
}
}
}
/*
* add_placeholders_to_joinrel
- * Add any required PlaceHolderVars to a join rel's targetlist.
+ * Add any required PlaceHolderVars to a join rel's targetlist;
+ * and if they contain lateral references, add those references to the
+ * joinrel's direct_lateral_relids.
*
* A join rel should emit a PlaceHolderVar if (a) the PHV is needed above
* this join level and (b) the PHV can be computed at or below this level.
@@ -463,6 +425,10 @@ add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel)
joinrel->reltargetlist = lappend(joinrel->reltargetlist,
phinfo->ph_var);
joinrel->width += phinfo->ph_width;
+ /* Adjust joinrel's direct_lateral_relids as needed */
+ joinrel->direct_lateral_relids =
+ bms_add_members(joinrel->direct_lateral_relids,
+ phinfo->ph_lateral);
}
}
}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index f7439f8a0e3..3c34711ab3b 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -109,6 +109,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
rel->cheapest_total_path = NULL;
rel->cheapest_unique_path = NULL;
rel->cheapest_parameterized_paths = NIL;
+ rel->direct_lateral_relids = NULL;
rel->lateral_relids = NULL;
rel->relid = relid;
rel->rtekind = rte->rtekind;
@@ -370,6 +371,10 @@ build_join_rel(PlannerInfo *root,
joinrel->cheapest_total_path = NULL;
joinrel->cheapest_unique_path = NULL;
joinrel->cheapest_parameterized_paths = NIL;
+ /* init direct_lateral_relids from children; we'll finish it up below */
+ joinrel->direct_lateral_relids =
+ bms_union(outer_rel->direct_lateral_relids,
+ inner_rel->direct_lateral_relids);
joinrel->lateral_relids = min_join_parameterization(root, joinrel->relids,
outer_rel, inner_rel);
joinrel->relid = 0; /* indicates not a baserel */
@@ -420,6 +425,18 @@ build_join_rel(PlannerInfo *root,
add_placeholders_to_joinrel(root, joinrel);
/*
+ * add_placeholders_to_joinrel also took care of adding the ph_lateral
+ * sets of any PlaceHolderVars computed here to direct_lateral_relids, so
+ * now we can finish computing that. This is much like the computation of
+ * the transitively-closed lateral_relids in min_join_parameterization,
+ * except that here we *do* have to consider the added PHVs.
+ */
+ joinrel->direct_lateral_relids =
+ bms_del_members(joinrel->direct_lateral_relids, joinrel->relids);
+ if (bms_is_empty(joinrel->direct_lateral_relids))
+ joinrel->direct_lateral_relids = NULL;
+
+ /*
* Construct restrict and join clause lists for the new joinrel. (The
* caller might or might not need the restrictlist, but I need it anyway
* for set_joinrel_size_estimates().)
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index 773e7b2be1c..32038ce3759 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -782,7 +782,6 @@ flatten_join_alias_vars_mutator(Node *node,
Assert(!IsA(node, SubPlan));
/* Shouldn't need to handle these planner auxiliary nodes here */
Assert(!IsA(node, SpecialJoinInfo));
- Assert(!IsA(node, LateralJoinInfo));
Assert(!IsA(node, PlaceHolderInfo));
Assert(!IsA(node, MinMaxAggInfo));
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 1da90ff8943..6f221687f67 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -402,7 +402,6 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
/* Shouldn't need to handle other planner auxiliary nodes here */
Assert(!IsA(node, PlanRowMark));
Assert(!IsA(node, SpecialJoinInfo));
- Assert(!IsA(node, LateralJoinInfo));
Assert(!IsA(node, PlaceHolderInfo));
Assert(!IsA(node, MinMaxAggInfo));
@@ -586,7 +585,6 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
}
/* Shouldn't need to handle other planner auxiliary nodes here */
Assert(!IsA(node, SpecialJoinInfo));
- Assert(!IsA(node, LateralJoinInfo));
Assert(!IsA(node, PlaceHolderInfo));
Assert(!IsA(node, MinMaxAggInfo));
@@ -868,7 +866,6 @@ rangeTableEntry_used_walker(Node *node,
Assert(!IsA(node, PlaceHolderVar));
Assert(!IsA(node, PlanRowMark));
Assert(!IsA(node, SpecialJoinInfo));
- Assert(!IsA(node, LateralJoinInfo));
Assert(!IsA(node, AppendRelInfo));
Assert(!IsA(node, PlaceHolderInfo));
Assert(!IsA(node, MinMaxAggInfo));
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 574b72df183..91505c81d9c 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -244,7 +244,6 @@ typedef enum NodeTag
T_RestrictInfo,
T_PlaceHolderVar,
T_SpecialJoinInfo,
- T_LateralJoinInfo,
T_AppendRelInfo,
T_PlaceHolderInfo,
T_MinMaxAggInfo,
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 2918b07abbe..5e6c34db99b 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -213,8 +213,6 @@ typedef struct PlannerInfo
List *join_info_list; /* list of SpecialJoinInfos */
- List *lateral_info_list; /* list of LateralJoinInfos */
-
List *append_rel_list; /* list of AppendRelInfos */
List *rowMarks; /* list of PlanRowMarks */
@@ -344,6 +342,7 @@ typedef struct PlannerInfo
* (no duplicates) output from relation; NULL if not yet requested
* cheapest_parameterized_paths - best paths for their parameterizations;
* always includes cheapest_total_path, even if that's unparameterized
+ * direct_lateral_relids - rels this rel has direct LATERAL references to
* lateral_relids - required outer rels for LATERAL, as a Relids set
* (includes both direct and indirect lateral references)
*
@@ -452,6 +451,7 @@ typedef struct RelOptInfo
/* parameterization information needed for both base rels and join rels */
/* (see also lateral_vars and lateral_referencers) */
+ Relids direct_lateral_relids; /* rels directly laterally referenced */
Relids lateral_relids; /* minimum parameterization of rel */
/* information about a base rel (not set for join rels!) */
@@ -1434,43 +1434,6 @@ typedef struct SpecialJoinInfo
} SpecialJoinInfo;
/*
- * "Lateral join" info.
- *
- * Lateral references constrain the join order in a way that's somewhat like
- * outer joins, though different in detail. We construct a LateralJoinInfo
- * for each lateral cross-reference, placing them in the PlannerInfo node's
- * lateral_info_list.
- *
- * For unflattened LATERAL RTEs, we generate LateralJoinInfo(s) in which
- * lateral_rhs is the relid of the LATERAL baserel, and lateral_lhs is a set
- * of relids of baserels it references, all of which must be present on the
- * LHS to compute a parameter needed by the RHS. Typically, lateral_lhs is
- * a singleton, but it can include multiple rels if the RHS references a
- * PlaceHolderVar with a multi-rel ph_eval_at level. We disallow joining to
- * only part of the LHS in such cases, since that would result in a join tree
- * with no convenient place to compute the PHV.
- *
- * When an appendrel contains lateral references (eg "LATERAL (SELECT x.col1
- * UNION ALL SELECT y.col2)"), the LateralJoinInfos reference the parent
- * baserel not the member otherrels, since it is the parent relid that is
- * considered for joining purposes.
- *
- * If any LATERAL RTEs were flattened into the parent query, it is possible
- * that the query now contains PlaceHolderVars containing lateral references,
- * representing expressions that need to be evaluated at particular spots in
- * the jointree but contain lateral references to Vars from elsewhere. These
- * give rise to LateralJoinInfos in which lateral_rhs is the evaluation point
- * of a PlaceHolderVar and lateral_lhs is the set of lateral rels it needs.
- */
-
-typedef struct LateralJoinInfo
-{
- NodeTag type;
- Relids lateral_lhs; /* rels needed to compute a lateral value */
- Relids lateral_rhs; /* rel where lateral value is needed */
-} LateralJoinInfo;
-
-/*
* Append-relation info.
*
* When we expand an inheritable table or a UNION-ALL subselect into an