From 4fcf48450d38e28e69e629c779a7866183d8ea41 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 11 Dec 2015 15:52:16 -0500 Subject: [PATCH] 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. --- src/backend/nodes/copyfuncs.c | 17 --- src/backend/nodes/equalfuncs.c | 12 -- src/backend/nodes/outfuncs.c | 14 +- src/backend/optimizer/path/joinrels.c | 36 +---- src/backend/optimizer/plan/analyzejoins.c | 3 - src/backend/optimizer/plan/initsplan.c | 162 ++++++++-------------- src/backend/optimizer/plan/planagg.c | 3 +- src/backend/optimizer/plan/planmain.c | 6 +- src/backend/optimizer/plan/planner.c | 3 +- src/backend/optimizer/prep/prepjointree.c | 9 +- src/backend/optimizer/prep/prepunion.c | 1 - src/backend/optimizer/util/placeholder.c | 60 ++------ src/backend/optimizer/util/relnode.c | 17 +++ src/backend/optimizer/util/var.c | 1 - src/backend/rewrite/rewriteManip.c | 3 - src/include/nodes/nodes.h | 1 - src/include/nodes/relation.h | 41 +----- 17 files changed, 100 insertions(+), 289 deletions(-) diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 26264cbfab..ba04b7227c 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -2066,20 +2066,6 @@ _copySpecialJoinInfo(const SpecialJoinInfo *from) return newnode; } -/* - * _copyLateralJoinInfo - */ -static LateralJoinInfo * -_copyLateralJoinInfo(const LateralJoinInfo *from) -{ - LateralJoinInfo *newnode = makeNode(LateralJoinInfo); - - COPY_BITMAPSET_FIELD(lateral_lhs); - COPY_BITMAPSET_FIELD(lateral_rhs); - - return newnode; -} - /* * _copyAppendRelInfo */ @@ -4519,9 +4505,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 aa6e10210d..356fcafeb4 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -845,15 +845,6 @@ _equalSpecialJoinInfo(const SpecialJoinInfo *a, const SpecialJoinInfo *b) return true; } -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) { @@ -2860,9 +2851,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 f07c7933b1..63fae82aba 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1847,7 +1847,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); @@ -1892,6 +1891,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); @@ -2056,15 +2056,6 @@ _outSpecialJoinInfo(StringInfo str, const SpecialJoinInfo *node) WRITE_NODE_FIELD(semi_rhs_exprs); } -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) { @@ -3355,9 +3346,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 47837e1eda..ad580581b5 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 7912b153c5..d188d9724a 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 dded896064..f2dce6a296 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 a761cfdb09..02d38f15d1 100644 --- a/src/backend/optimizer/plan/planagg.c +++ b/src/backend/optimizer/plan/planagg.c @@ -433,9 +433,8 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo, subroot->plan_params = NIL; subroot->outer_params = NULL; subroot->init_plans = NIL; - /* 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 d73e7c0ab0..894f9685e0 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -114,7 +114,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; @@ -201,9 +200,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 a9cccee7d7..797df31c88 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -1132,9 +1132,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 401ba5ba73..5d3de14c43 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -1150,14 +1150,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); @@ -1642,7 +1639,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); /* @@ -2839,7 +2835,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 8884fb1bae..2e55131a59 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 287031547a..7fa93fb29a 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 a2be2ede85..f2bdfcc745 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -111,6 +111,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; @@ -373,6 +374,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 */ @@ -422,6 +427,18 @@ build_join_rel(PlannerInfo *root, build_joinrel_tlist(root, joinrel, inner_rel); 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 diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index 773e7b2be1..32038ce375 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 1da90ff894..6f221687f6 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 94bdb7c9af..603edd3e4f 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -247,7 +247,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 f7796f8efb..539300561b 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -226,8 +226,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 */ @@ -357,6 +355,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) * @@ -466,6 +465,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!) */ @@ -1461,43 +1461,6 @@ typedef struct SpecialJoinInfo List *semi_rhs_exprs; /* righthand-side expressions of these ops */ } 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. * -- 2.40.0