I wrote: > As threatened, here's a patch on top of that that gets rid of > LateralJoinInfo. I'm pretty happy with this, although I have an > itchy feeling that we could dispense with the lateral_vars lists too.
I experimented with that and figured out what was bothering me: there is no need for add_placeholders_to_base_rels to fool around with adding items to lateral_vars anymore, because the code in create_lateral_join_info that adds placeholders' ph_lateral bits to the evaluation locations' lateral_relids sets now does everything that we needed to have happen. We don't care exactly what's inside the PHV expression, only which rels it comes from, and the ph_lateral bitmap is sufficient for that. We still need the lateral_vars field in RelOptInfo, but it now exists purely to carry the results of extract_lateral_references forward to create_lateral_join_info. (We wouldn't need even that so far as Vars are concerned, because we could just add their source rel to the referencing rel's lateral_relids on-the-fly in extract_lateral_references. But we can't handle PHVs that way, because at that stage we don't know precisely where they'll be evaluated, so we don't know what to add to the referencer's lateral_relids.) Updated patch attached; compared to the previous one, this just removes a big chunk of code in add_placeholders_to_base_rels and updates a couple of related comments. regards, tom lane
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 26264cb..ba04b72 100644 *** a/src/backend/nodes/copyfuncs.c --- b/src/backend/nodes/copyfuncs.c *************** _copySpecialJoinInfo(const SpecialJoinIn *** 2067,2086 **** } /* - * _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 * --- 2067,2072 ---- *************** copyObject(const void *from) *** 4519,4527 **** case T_SpecialJoinInfo: retval = _copySpecialJoinInfo(from); break; - case T_LateralJoinInfo: - retval = _copyLateralJoinInfo(from); - break; case T_AppendRelInfo: retval = _copyAppendRelInfo(from); break; --- 4505,4510 ---- diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index aa6e102..356fcaf 100644 *** a/src/backend/nodes/equalfuncs.c --- b/src/backend/nodes/equalfuncs.c *************** _equalSpecialJoinInfo(const SpecialJoinI *** 846,860 **** } 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); --- 846,851 ---- *************** equal(const void *a, const void *b) *** 2860,2868 **** case T_SpecialJoinInfo: retval = _equalSpecialJoinInfo(a, b); break; - case T_LateralJoinInfo: - retval = _equalLateralJoinInfo(a, b); - break; case T_AppendRelInfo: retval = _equalAppendRelInfo(a, b); break; --- 2851,2856 ---- diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index f07c793..63fae82 100644 *** a/src/backend/nodes/outfuncs.c --- b/src/backend/nodes/outfuncs.c *************** _outPlannerInfo(StringInfo str, const Pl *** 1847,1853 **** 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); --- 1847,1852 ---- *************** _outRelOptInfo(StringInfo str, const Rel *** 1892,1897 **** --- 1891,1897 ---- 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); *************** _outSpecialJoinInfo(StringInfo str, cons *** 2057,2071 **** } 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"); --- 2057,2062 ---- *************** _outNode(StringInfo str, const void *obj *** 3355,3363 **** case T_SpecialJoinInfo: _outSpecialJoinInfo(str, obj); break; - case T_LateralJoinInfo: - _outLateralJoinInfo(str, obj); - break; case T_AppendRelInfo: _outAppendRelInfo(str, obj); break; --- 3346,3351 ---- diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index e57b8e2..2ab650c 100644 *** a/src/backend/optimizer/path/joinrels.c --- b/src/backend/optimizer/path/joinrels.c *************** join_search_one_level(PlannerInfo *root, *** 231,237 **** */ if (joinrels[level] == NIL && root->join_info_list == NIL && ! root->lateral_info_list == NIL) elog(ERROR, "failed to build any %d-way joins", level); } } --- 231,237 ---- */ if (joinrels[level] == NIL && root->join_info_list == NIL && ! !root->hasLateralRTEs) elog(ERROR, "failed to build any %d-way joins", level); } } *************** join_is_legal(PlannerInfo *root, RelOptI *** 554,568 **** 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) 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)) --- 554,560 ---- match_sjinfo->jointype == JOIN_FULL)) return false; /* not implementable as nestloop */ /* check there is a direct reference from rel2 to rel1 */ ! 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)) *************** join_is_legal(PlannerInfo *root, RelOptI *** 577,591 **** 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) 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)) --- 569,575 ---- match_sjinfo->jointype == JOIN_FULL)) return false; /* not implementable as nestloop */ /* check there is a direct reference from rel1 to rel2 */ ! 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)) *************** have_join_order_restriction(PlannerInfo *** 917,933 **** * 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; ! } /* * Likewise, if both rels are needed to compute some PlaceHolderVar, --- 901,909 ---- * If either side has a direct lateral reference to the other, attempt the * join regardless of outer-join considerations. */ ! 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 7912b15..d188d97 100644 *** a/src/backend/optimizer/plan/analyzejoins.c --- b/src/backend/optimizer/plan/analyzejoins.c *************** remove_rel_from_query(PlannerInfo *root, *** 439,447 **** 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. --- 439,444 ---- diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index e5688ef..a3f79b6 100644 *** a/src/backend/optimizer/plan/initsplan.c --- b/src/backend/optimizer/plan/initsplan.c *************** typedef struct PostponedQual *** 47,53 **** 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, --- 47,52 ---- *************** extract_lateral_references(PlannerInfo * *** 382,392 **** /* * 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). * * This has to run after deconstruct_jointree, because we need to know the * final ph_eval_at values for PlaceHolderVars. --- 381,388 ---- /* * create_lateral_join_info ! * 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. *************** extract_lateral_references(PlannerInfo * *** 394,399 **** --- 390,396 ---- void create_lateral_join_info(PlannerInfo *root) { + bool found_laterals = false; Index rti; ListCell *lc; *************** create_lateral_join_info(PlannerInfo *ro *** 430,437 **** { Var *var = (Var *) node; ! add_lateral_info(root, bms_make_singleton(var->varno), ! brel->relids); lateral_relids = bms_add_member(lateral_relids, var->varno); } --- 427,433 ---- { Var *var = (Var *) node; ! found_laterals = true; lateral_relids = bms_add_member(lateral_relids, var->varno); } *************** create_lateral_join_info(PlannerInfo *ro *** 441,447 **** PlaceHolderInfo *phinfo = find_placeholder_info(root, phv, false); ! add_lateral_info(root, phinfo->ph_eval_at, brel->relids); lateral_relids = bms_add_members(lateral_relids, phinfo->ph_eval_at); } --- 437,443 ---- PlaceHolderInfo *phinfo = find_placeholder_info(root, phv, false); ! found_laterals = true; lateral_relids = bms_add_members(lateral_relids, phinfo->ph_eval_at); } *************** create_lateral_join_info(PlannerInfo *ro *** 449,517 **** Assert(false); } ! /* We now have all the direct lateral refs from this rel */ ! brel->lateral_relids = 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. * ! * 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. */ foreach(lc, root->placeholder_list) { PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc); Relids eval_at = phinfo->ph_eval_at; ! 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); ! } ! list_free(vars); ! ev_at = -1; ! while ((ev_at = bms_next_member(eval_at, ev_at)) >= 0) { ! RelOptInfo *brel = find_base_rel(root, ev_at); brel->lateral_relids = bms_add_members(brel->lateral_relids, phinfo->ph_lateral); --- 445,498 ---- Assert(false); } ! /* 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 mark their ! * eval_at rels as having lateral references to the source rels. * ! * 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) ! continue; /* PHV is uninteresting if no lateral refs */ ! found_laterals = true; ! 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, varno); brel->lateral_relids = bms_add_members(brel->lateral_relids, phinfo->ph_lateral); *************** create_lateral_join_info(PlannerInfo *ro *** 519,535 **** } } ! /* If we found no lateral references, we're done. */ ! if (root->lateral_info_list == NIL) 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. * * This code is essentially Warshall's algorithm for transitive closure. * The outer loop considers each baserel, and propagates its lateral --- 500,521 ---- } } ! /* ! * 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; + } /* ! * 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 *************** create_lateral_join_info(PlannerInfo *ro *** 623,632 **** if (brel == NULL || brel->reloptkind != RELOPT_BASEREL) continue; - /* - * If it's an appendrel parent, copy its lateral_relids and - * lateral_referencers to each child rel. - */ if (root->simple_rte_array[rti]->inh) { foreach(lc, root->append_rel_list) --- 609,614 ---- *************** create_lateral_join_info(PlannerInfo *ro *** 638,643 **** --- 620,627 ---- 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); *************** create_lateral_join_info(PlannerInfo *ro *** 647,692 **** } } - /* - * 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); - } - /***************************************************************************** * --- 631,636 ---- diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c index a761cfd..02d38f1 100644 *** a/src/backend/optimizer/plan/planagg.c --- b/src/backend/optimizer/plan/planagg.c *************** build_minmax_path(PlannerInfo *root, Min *** 433,441 **** 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 */ Assert(subroot->join_info_list == NIL); - Assert(subroot->lateral_info_list == NIL); /* and we haven't created PlaceHolderInfos, either */ Assert(subroot->placeholder_list == NIL); --- 433,440 ---- subroot->plan_params = NIL; subroot->outer_params = NULL; subroot->init_plans = NIL; ! /* There shouldn't be any OJ info to translate, as yet */ Assert(subroot->join_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 d73e7c0..894f968 100644 *** a/src/backend/optimizer/plan/planmain.c --- b/src/backend/optimizer/plan/planmain.c *************** query_planner(PlannerInfo *root, List *t *** 114,120 **** 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; --- 114,119 ---- *************** query_planner(PlannerInfo *root, List *t *** 201,209 **** 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. */ create_lateral_join_info(root); --- 200,207 ---- add_placeholders_to_base_rels(root); /* ! * 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 a9cccee..797df31 100644 *** a/src/backend/optimizer/plan/planner.c --- b/src/backend/optimizer/plan/planner.c *************** inheritance_planner(PlannerInfo *root) *** 1132,1140 **** } } ! /* There shouldn't be any OJ or LATERAL 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 */ --- 1132,1139 ---- } } ! /* There shouldn't be any OJ info to translate, as yet */ Assert(subroot.join_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 401ba5b..5d3de14 100644 *** a/src/backend/optimizer/prep/prepjointree.c --- b/src/backend/optimizer/prep/prepjointree.c *************** pull_up_simple_subquery(PlannerInfo *roo *** 1150,1163 **** 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. */ 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); --- 1150,1160 ---- subroot->append_rel_list); /* ! * 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->placeholder_list == NIL); Assert(subroot->placeholder_list == NIL); *************** pull_up_simple_values(PlannerInfo *root, *** 1642,1648 **** 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); /* --- 1639,1644 ---- *************** substitute_multiple_relids_walker(Node * *** 2839,2845 **** } /* 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)); --- 2835,2840 ---- diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 8884fb1..2e55131 100644 *** a/src/backend/optimizer/prep/prepunion.c --- b/src/backend/optimizer/prep/prepunion.c *************** adjust_appendrel_attrs_mutator(Node *nod *** 1786,1792 **** } /* 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)); --- 1786,1791 ---- diff --git a/src/backend/optimizer/util/placeholder.c b/src/backend/optimizer/util/placeholder.c index 2870315..7fa93fb 100644 *** a/src/backend/optimizer/util/placeholder.c --- b/src/backend/optimizer/util/placeholder.c *************** fix_placeholder_input_needed_levels(Plan *** 363,374 **** /* * add_placeholders_to_base_rels ! * Add any required PlaceHolderVars to base rels' targetlists, and ! * update lateral_vars lists for lateral references contained in them. * * 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 * 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 --- 363,372 ---- /* * add_placeholders_to_base_rels ! * 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. 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 *************** add_placeholders_to_base_rels(PlannerInf *** 385,442 **** Relids eval_at = phinfo->ph_eval_at; int varno; ! if (bms_get_singleton_member(eval_at, &varno)) { 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); ! } } } } /* * add_placeholders_to_joinrel ! * Add any required PlaceHolderVars to a join rel's targetlist. * * 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. --- 383,404 ---- Relids eval_at = phinfo->ph_eval_at; int varno; ! if (bms_get_singleton_member(eval_at, &varno) && ! bms_nonempty_difference(phinfo->ph_needed, eval_at)) { RelOptInfo *rel = find_base_rel(root, varno); ! rel->reltargetlist = lappend(rel->reltargetlist, ! copyObject(phinfo->ph_var)); } } } /* * add_placeholders_to_joinrel ! * 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. *************** add_placeholders_to_joinrel(PlannerInfo *** 463,468 **** --- 425,434 ---- 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 a2be2ed..f2bdfcc 100644 *** a/src/backend/optimizer/util/relnode.c --- b/src/backend/optimizer/util/relnode.c *************** build_simple_rel(PlannerInfo *root, int *** 111,116 **** --- 111,117 ---- 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; *************** build_join_rel(PlannerInfo *root, *** 373,378 **** --- 374,383 ---- 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 */ *************** build_join_rel(PlannerInfo *root, *** 423,428 **** --- 428,445 ---- 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 773e7b2..32038ce 100644 *** a/src/backend/optimizer/util/var.c --- b/src/backend/optimizer/util/var.c *************** flatten_join_alias_vars_mutator(Node *no *** 782,788 **** 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)); --- 782,787 ---- diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 1da90ff..6f22168 100644 *** a/src/backend/rewrite/rewriteManip.c --- b/src/backend/rewrite/rewriteManip.c *************** OffsetVarNodes_walker(Node *node, Offset *** 402,408 **** /* 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)); --- 402,407 ---- *************** ChangeVarNodes_walker(Node *node, Change *** 586,592 **** } /* 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)); --- 585,590 ---- *************** rangeTableEntry_used_walker(Node *node, *** 868,874 **** 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)); --- 866,871 ---- diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 94bdb7c..603edd3 100644 *** a/src/include/nodes/nodes.h --- b/src/include/nodes/nodes.h *************** typedef enum NodeTag *** 247,253 **** T_RestrictInfo, T_PlaceHolderVar, T_SpecialJoinInfo, - T_LateralJoinInfo, T_AppendRelInfo, T_PlaceHolderInfo, T_MinMaxAggInfo, --- 247,252 ---- diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 74f4daf..5393005 100644 *** a/src/include/nodes/relation.h --- b/src/include/nodes/relation.h *************** typedef struct PlannerInfo *** 226,233 **** 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 */ --- 226,231 ---- *************** typedef struct PlannerInfo *** 357,362 **** --- 355,361 ---- * (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) * *************** typedef struct PlannerInfo *** 374,379 **** --- 373,379 ---- * lateral_vars - lateral cross-references of rel, if any (list of * Vars and PlaceHolderVars) * lateral_referencers - relids of rels that reference this one laterally + * (includes both direct and indirect lateral references) * indexlist - list of IndexOptInfo nodes for relation's indexes * (always NIL if it's not a table) * pages - number of disk pages in relation (zero if not a table) *************** typedef struct RelOptInfo *** 465,470 **** --- 465,471 ---- /* 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!) */ *************** typedef struct SpecialJoinInfo *** 1461,1503 **** } 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 --- 1462,1467 ----
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers