diff options
Diffstat (limited to 'src/backend/optimizer/plan/initsplan.c')
-rw-r--r-- | src/backend/optimizer/plan/initsplan.c | 129 |
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) |