aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/initsplan.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan/initsplan.c')
-rw-r--r--src/backend/optimizer/plan/initsplan.c162
1 files changed, 55 insertions, 107 deletions
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index dded896064d..f2dce6a2969 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -47,7 +47,6 @@ typedef struct PostponedQual
static void extract_lateral_references(PlannerInfo *root, RelOptInfo *brel,
Index rtindex);
-static void add_lateral_info(PlannerInfo *root, Relids lhs, Relids rhs);
static List *deconstruct_recurse(PlannerInfo *root, Node *jtnode,
bool below_outer_join,
Relids *qualscope, Relids *inner_join_rels,
@@ -382,11 +381,8 @@ extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex)
/*
* create_lateral_join_info
- * For each unflattened LATERAL subquery, create LateralJoinInfo(s) and add
- * them to root->lateral_info_list, and fill in the per-rel lateral_relids
- * and lateral_referencers sets. Also generate LateralJoinInfo(s) to
- * represent any lateral references within PlaceHolderVars (this part deals
- * with the effects of flattened LATERAL subqueries).
+ * Fill in the per-base-relation direct_lateral_relids, lateral_relids
+ * and lateral_referencers sets.
*
* This has to run after deconstruct_jointree, because we need to know the
* final ph_eval_at values for PlaceHolderVars.
@@ -394,6 +390,7 @@ extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex)
void
create_lateral_join_info(PlannerInfo *root)
{
+ bool found_laterals = false;
Index rti;
ListCell *lc;
@@ -430,8 +427,7 @@ create_lateral_join_info(PlannerInfo *root)
{
Var *var = (Var *) node;
- add_lateral_info(root, bms_make_singleton(var->varno),
- brel->relids);
+ found_laterals = true;
lateral_relids = bms_add_member(lateral_relids,
var->varno);
}
@@ -441,7 +437,7 @@ create_lateral_join_info(PlannerInfo *root)
PlaceHolderInfo *phinfo = find_placeholder_info(root, phv,
false);
- add_lateral_info(root, phinfo->ph_eval_at, brel->relids);
+ found_laterals = true;
lateral_relids = bms_add_members(lateral_relids,
phinfo->ph_eval_at);
}
@@ -449,69 +445,54 @@ create_lateral_join_info(PlannerInfo *root)
Assert(false);
}
- /* We now have all the direct lateral refs from this rel */
- brel->lateral_relids = lateral_relids;
+ /* We now have all the simple lateral refs from this rel */
+ brel->direct_lateral_relids = lateral_relids;
+ brel->lateral_relids = bms_copy(lateral_relids);
}
/*
- * Now check for lateral references within PlaceHolderVars, and make
- * LateralJoinInfos describing each such reference. Unlike references in
- * unflattened LATERAL RTEs, the referencing location could be a join.
+ * Now check for lateral references within PlaceHolderVars, and mark their
+ * eval_at rels as having lateral references to the source rels.
*
- * For a PHV that is due to be evaluated at a join, we mark each of the
- * join's member baserels as having the PHV's lateral references too. Even
- * though the baserels could be scanned without considering those lateral
- * refs, we will never be able to form the join except as a path
- * parameterized by the lateral refs, so there is no point in considering
- * unparameterized paths for the baserels; and we mustn't try to join any
- * of those baserels to the lateral refs too soon, either.
+ * For a PHV that is due to be evaluated at a baserel, mark its source(s)
+ * as direct lateral dependencies of the baserel (adding onto the ones
+ * recorded above). If it's due to be evaluated at a join, mark its
+ * source(s) as indirect lateral dependencies of each baserel in the join,
+ * ie put them into lateral_relids but not direct_lateral_relids. This is
+ * appropriate because we can't put any such baserel on the outside of a
+ * join to one of the PHV's lateral dependencies, but on the other hand we
+ * also can't yet join it directly to the dependency.
*/
foreach(lc, root->placeholder_list)
{
PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
Relids eval_at = phinfo->ph_eval_at;
+ int varno;
- if (phinfo->ph_lateral != NULL)
- {
- List *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
- PVC_RECURSE_AGGREGATES,
- PVC_INCLUDE_PLACEHOLDERS);
- ListCell *lc2;
- int ev_at;
-
- foreach(lc2, vars)
- {
- Node *node = (Node *) lfirst(lc2);
-
- if (IsA(node, Var))
- {
- Var *var = (Var *) node;
-
- if (!bms_is_member(var->varno, eval_at))
- add_lateral_info(root,
- bms_make_singleton(var->varno),
- eval_at);
- }
- else if (IsA(node, PlaceHolderVar))
- {
- PlaceHolderVar *other_phv = (PlaceHolderVar *) node;
- PlaceHolderInfo *other_phi;
-
- other_phi = find_placeholder_info(root, other_phv,
- false);
- if (!bms_is_subset(other_phi->ph_eval_at, eval_at))
- add_lateral_info(root, other_phi->ph_eval_at, eval_at);
- }
- else
- Assert(false);
- }
+ if (phinfo->ph_lateral == NULL)
+ continue; /* PHV is uninteresting if no lateral refs */
- list_free(vars);
+ found_laterals = true;
- ev_at = -1;
- while ((ev_at = bms_next_member(eval_at, ev_at)) >= 0)
+ if (bms_get_singleton_member(eval_at, &varno))
+ {
+ /* Evaluation site is a baserel */
+ RelOptInfo *brel = find_base_rel(root, varno);
+
+ brel->direct_lateral_relids =
+ bms_add_members(brel->direct_lateral_relids,
+ phinfo->ph_lateral);
+ brel->lateral_relids =
+ bms_add_members(brel->lateral_relids,
+ phinfo->ph_lateral);
+ }
+ else
+ {
+ /* Evaluation site is a join */
+ varno = -1;
+ while ((varno = bms_next_member(eval_at, varno)) >= 0)
{
- RelOptInfo *brel = find_base_rel(root, ev_at);
+ RelOptInfo *brel = find_base_rel(root, varno);
brel->lateral_relids = bms_add_members(brel->lateral_relids,
phinfo->ph_lateral);
@@ -519,17 +500,22 @@ create_lateral_join_info(PlannerInfo *root)
}
}
- /* If we found no lateral references, we're done. */
- if (root->lateral_info_list == NIL)
+ /*
+ * If we found no actual lateral references, we're done; but reset the
+ * hasLateralRTEs flag to avoid useless work later.
+ */
+ if (!found_laterals)
+ {
+ root->hasLateralRTEs = false;
return;
+ }
/*
- * At this point the lateral_relids sets represent only direct lateral
- * references. Replace them by their transitive closure, so that they
- * describe both direct and indirect lateral references. If relation X
- * references Y laterally, and Y references Z laterally, then we will have
- * to scan X on the inside of a nestloop with Z, so for all intents and
- * purposes X is laterally dependent on Z too.
+ * Calculate the transitive closure of the lateral_relids sets, so that
+ * they describe both direct and indirect lateral references. If relation
+ * X references Y laterally, and Y references Z laterally, then we will
+ * have to scan X on the inside of a nestloop with Z, so for all intents
+ * and purposes X is laterally dependent on Z too.
*
* This code is essentially Warshall's algorithm for transitive closure.
* The outer loop considers each baserel, and propagates its lateral
@@ -632,6 +618,8 @@ create_lateral_join_info(PlannerInfo *root)
continue;
childrel = root->simple_rel_array[appinfo->child_relid];
Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
+ Assert(childrel->direct_lateral_relids == NULL);
+ childrel->direct_lateral_relids = brel->direct_lateral_relids;
Assert(childrel->lateral_relids == NULL);
childrel->lateral_relids = brel->lateral_relids;
Assert(childrel->lateral_referencers == NULL);
@@ -641,46 +629,6 @@ create_lateral_join_info(PlannerInfo *root)
}
}
-/*
- * add_lateral_info
- * Add a LateralJoinInfo to root->lateral_info_list, if needed
- *
- * We suppress redundant list entries. The passed Relids are copied if saved.
- */
-static void
-add_lateral_info(PlannerInfo *root, Relids lhs, Relids rhs)
-{
- LateralJoinInfo *ljinfo;
- ListCell *lc;
-
- /* Sanity-check the input */
- Assert(!bms_is_empty(lhs));
- Assert(!bms_is_empty(rhs));
- Assert(!bms_overlap(lhs, rhs));
-
- /*
- * The input is redundant if it has the same RHS and an LHS that is a
- * subset of an existing entry's. If an existing entry has the same RHS
- * and an LHS that is a subset of the new one, it's redundant, but we
- * don't trouble to get rid of it. The only case that is really worth
- * worrying about is identical entries, and we handle that well enough
- * with this simple logic.
- */
- foreach(lc, root->lateral_info_list)
- {
- ljinfo = (LateralJoinInfo *) lfirst(lc);
- if (bms_equal(rhs, ljinfo->lateral_rhs) &&
- bms_is_subset(lhs, ljinfo->lateral_lhs))
- return;
- }
-
- /* Not there, so make a new entry */
- ljinfo = makeNode(LateralJoinInfo);
- ljinfo->lateral_lhs = bms_copy(lhs);
- ljinfo->lateral_rhs = bms_copy(rhs);
- root->lateral_info_list = lappend(root->lateral_info_list, ljinfo);
-}
-
/*****************************************************************************
*