Get rid of the planner's LateralJoinInfo data structure.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 11 Dec 2015 20:52:16 +0000 (15:52 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 11 Dec 2015 20:52:38 +0000 (15:52 -0500)
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.

17 files changed:
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/optimizer/path/joinrels.c
src/backend/optimizer/plan/analyzejoins.c
src/backend/optimizer/plan/initsplan.c
src/backend/optimizer/plan/planagg.c
src/backend/optimizer/plan/planmain.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/prep/prepunion.c
src/backend/optimizer/util/placeholder.c
src/backend/optimizer/util/relnode.c
src/backend/optimizer/util/var.c
src/backend/rewrite/rewriteManip.c
src/include/nodes/nodes.h
src/include/nodes/relation.h

index 26264cbfab49e0ba52ab7c1370585d9a64ef81b8..ba04b7227ca86b6a2a692a346ba841cc03e2513a 100644 (file)
@@ -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;
index aa6e10210d3359399cfebb4764c6de68cbeb1ddd..356fcafeb49a9773fa25e524a176aa6f73308961 100644 (file)
@@ -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;
index f07c7933b14916a84605dbc13f722bbc69dd57f0..63fae82aba00807b625241a2fef577e9d2af5a95 100644 (file)
@@ -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;
index 47837e1eda8762635166a290aa38b975af6920f0..ad580581b5c0219c2e734fa2e556060792c42970 100644 (file)
@@ -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,
index 7912b153c59e42ca360f0ecc9ba9b589954827c5..d188d9724aff0d3faf561d18d0f034614da01e68 100644 (file)
@@ -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.
index dded896064d05667832cbf353527b4a51ab95703..f2dce6a296926ecc9edbebf11b25074a781e2d0f 100644 (file)
@@ -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);
-}
-
 
 /*****************************************************************************
  *
index a761cfdb09b09ed7ba641a64e5e58e41a9a4be6c..02d38f15d1cd1012cd15952a0f5328a62d00e80e 100644 (file)
@@ -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);
 
index d73e7c0ab0fe4b693d8ec8203a1d7ec9978fddbd..894f9685e0f057b8d4362bcf275e68a1ccc917d9 100644 (file)
@@ -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);
 
index a9cccee7d7400aa5cfc109ce9e51e1b712feb2ef..797df31c883fa994bdf95b25e407abbc13527dc3 100644 (file)
@@ -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 */
index 401ba5ba735834b7ba6c6fffa7a01ba9a6304280..5d3de14c43cea8ffa6f99c1a974b6509d2404e66 100644 (file)
@@ -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));
index 8884fb1bae3e910c06514f2afaad4b92203a412e..2e55131a59165714aee97ebac0a9d9dbe4de1576 100644 (file)
@@ -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));
index 287031547a3f795f8450608ea107bb19d4c5e2bb..7fa93fb29ac1002cc5dfde29edc49576d6622ed3 100644 (file)
@@ -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);
            }
        }
    }
index a2be2ede85e1dbf6512137bd59156e9a47f33908..f2bdfcc745ac7473fcf6a32507b7d89cc7c5ab9a 100644 (file)
@@ -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
index 773e7b2be1c9ea4f278ed57514ec979766b6b74a..32038ce37590e658b1c8608d4db0844783978077 100644 (file)
@@ -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));
 
index 1da90ff8943bee1728e050eff057a544472d6ab3..6f221687f67dcee70d7249dc2a7fcb3d9c533af2 100644 (file)
@@ -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));
index 94bdb7c9af50f3ce761ec39ded1437381b332948..603edd3e4f23a0aa22011fe487608e96137bc1a1 100644 (file)
@@ -247,7 +247,6 @@ typedef enum NodeTag
    T_RestrictInfo,
    T_PlaceHolderVar,
    T_SpecialJoinInfo,
-   T_LateralJoinInfo,
    T_AppendRelInfo,
    T_PlaceHolderInfo,
    T_MinMaxAggInfo,
index f7796f8efbeaef07ccff4ef65793d52772da3508..539300561b9ab856fb7faea207b770557b219611 100644 (file)
@@ -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.
  *