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.c129
1 files changed, 101 insertions, 28 deletions
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 2f4e8181eb5..dded896064d 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -449,9 +449,7 @@ create_lateral_join_info(PlannerInfo *root)
Assert(false);
}
- /* We now know all the relids needed for lateral refs in this rel */
- if (bms_is_empty(lateral_relids))
- continue; /* ensure lateral_relids is NULL if empty */
+ /* We now have all the direct lateral refs from this rel */
brel->lateral_relids = lateral_relids;
}
@@ -459,6 +457,14 @@ create_lateral_join_info(PlannerInfo *root)
* 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)
{
@@ -471,6 +477,7 @@ create_lateral_join_info(PlannerInfo *root)
PVC_RECURSE_AGGREGATES,
PVC_INCLUDE_PLACEHOLDERS);
ListCell *lc2;
+ int ev_at;
foreach(lc2, vars)
{
@@ -500,6 +507,15 @@ create_lateral_join_info(PlannerInfo *root)
}
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);
+ }
}
}
@@ -508,46 +524,103 @@ create_lateral_join_info(PlannerInfo *root)
return;
/*
- * Now that we've identified all lateral references, make a second pass in
- * which we mark each baserel with the set of relids of rels that
- * reference it laterally (essentially, the inverse mapping of
- * lateral_relids). We'll need this for join_clause_is_movable_to().
+ * 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.
*
- * Also, propagate lateral_relids and lateral_referencers from appendrel
- * parent rels to their child rels. We intentionally give each child rel
- * the same minimum parameterization, even though it's quite possible that
- * some don't reference all the lateral rels. This is because any append
- * path for the parent will have to have the same parameterization for
- * every child anyway, and there's no value in forcing extra
- * reparameterize_path() calls. Similarly, a lateral reference to the
- * parent prevents use of otherwise-movable join rels for each child.
+ * This code is essentially Warshall's algorithm for transitive closure.
+ * The outer loop considers each baserel, and propagates its lateral
+ * dependencies to those baserels that have a lateral dependency on it.
*/
for (rti = 1; rti < root->simple_rel_array_size; rti++)
{
RelOptInfo *brel = root->simple_rel_array[rti];
- Relids lateral_referencers;
+ Relids outer_lateral_relids;
+ Index rti2;
- if (brel == NULL)
+ if (brel == NULL || brel->reloptkind != RELOPT_BASEREL)
continue;
- if (brel->reloptkind != RELOPT_BASEREL)
+
+ /* need not consider baserel further if it has no lateral refs */
+ outer_lateral_relids = brel->lateral_relids;
+ if (outer_lateral_relids == NULL)
continue;
- /* Compute lateral_referencers using the finished lateral_info_list */
- lateral_referencers = NULL;
- foreach(lc, root->lateral_info_list)
+ /* else scan all baserels */
+ for (rti2 = 1; rti2 < root->simple_rel_array_size; rti2++)
{
- LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(lc);
+ RelOptInfo *brel2 = root->simple_rel_array[rti2];
+
+ if (brel2 == NULL || brel2->reloptkind != RELOPT_BASEREL)
+ continue;
- if (bms_is_member(brel->relid, ljinfo->lateral_lhs))
- lateral_referencers = bms_add_members(lateral_referencers,
- ljinfo->lateral_rhs);
+ /* if brel2 has lateral ref to brel, propagate brel's refs */
+ if (bms_is_member(rti, brel2->lateral_relids))
+ brel2->lateral_relids = bms_add_members(brel2->lateral_relids,
+ outer_lateral_relids);
}
- brel->lateral_referencers = lateral_referencers;
+ }
+
+ /*
+ * Now that we've identified all lateral references, mark each baserel
+ * with the set of relids of rels that reference it laterally (possibly
+ * indirectly) --- that is, the inverse mapping of lateral_relids.
+ */
+ for (rti = 1; rti < root->simple_rel_array_size; rti++)
+ {
+ RelOptInfo *brel = root->simple_rel_array[rti];
+ Relids lateral_relids;
+ int rti2;
+
+ if (brel == NULL || brel->reloptkind != RELOPT_BASEREL)
+ continue;
+
+ /* Nothing to do at rels with no lateral refs */
+ lateral_relids = brel->lateral_relids;
+ if (lateral_relids == NULL)
+ continue;
/*
- * If it's an appendrel parent, copy its lateral_relids and
- * lateral_referencers to each child rel.
+ * We should not have broken the invariant that lateral_relids is
+ * exactly NULL if empty.
*/
+ Assert(!bms_is_empty(lateral_relids));
+
+ /* Also, no rel should have a lateral dependency on itself */
+ Assert(!bms_is_member(rti, lateral_relids));
+
+ /* Mark this rel's referencees */
+ rti2 = -1;
+ while ((rti2 = bms_next_member(lateral_relids, rti2)) >= 0)
+ {
+ RelOptInfo *brel2 = root->simple_rel_array[rti2];
+
+ Assert(brel2 != NULL && brel2->reloptkind == RELOPT_BASEREL);
+ brel2->lateral_referencers =
+ bms_add_member(brel2->lateral_referencers, rti);
+ }
+ }
+
+ /*
+ * Lastly, propagate lateral_relids and lateral_referencers from appendrel
+ * parent rels to their child rels. We intentionally give each child rel
+ * the same minimum parameterization, even though it's quite possible that
+ * some don't reference all the lateral rels. This is because any append
+ * path for the parent will have to have the same parameterization for
+ * every child anyway, and there's no value in forcing extra
+ * reparameterize_path() calls. Similarly, a lateral reference to the
+ * parent prevents use of otherwise-movable join rels for each child.
+ */
+ for (rti = 1; rti < root->simple_rel_array_size; rti++)
+ {
+ RelOptInfo *brel = root->simple_rel_array[rti];
+
+ if (brel == NULL || brel->reloptkind != RELOPT_BASEREL)
+ continue;
+
if (root->simple_rte_array[rti]->inh)
{
foreach(lc, root->append_rel_list)