Skip to content

Commit 7145d35

Browse files
committed
Simplify LATERAL-related calculations within add_paths_to_joinrel().
While convincing myself that commit 7e19db0 would solve both of the problems recently reported by Andreas Seltenreich, I realized that add_paths_to_joinrel's handling of LATERAL restrictions could be made noticeably simpler and faster if we were to retain the minimum possible parameterization for each joinrel (that is, the set of relids supplying unsatisfied lateral references in it). We already retain that for baserels, in RelOptInfo.lateral_relids, so we can use that field for joinrels too. This is a back-port of commit edca44b. I originally intended not to back-patch that, but additional hacking in this area turns out to be needed, making it necessary not optional to compute lateral_relids for joinrels. In preparation for those fixes, sync the relevant code with HEAD as much as practical. (I did not risk rearranging fields of RelOptInfo in released branches, however.)
1 parent 56a79a5 commit 7145d35

File tree

3 files changed

+28
-91
lines changed

3 files changed

+28
-91
lines changed

src/backend/optimizer/path/joinpath.c

+25-88
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,19 @@ static void sort_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel,
2929
RelOptInfo *outerrel, RelOptInfo *innerrel,
3030
List *restrictlist, List *mergeclause_list,
3131
JoinType jointype, SpecialJoinInfo *sjinfo,
32-
Relids param_source_rels, Relids extra_lateral_rels);
32+
Relids param_source_rels);
3333
static void match_unsorted_outer(PlannerInfo *root, RelOptInfo *joinrel,
3434
RelOptInfo *outerrel, RelOptInfo *innerrel,
3535
List *restrictlist, List *mergeclause_list,
3636
JoinType jointype, SpecialJoinInfo *sjinfo,
3737
SemiAntiJoinFactors *semifactors,
38-
Relids param_source_rels, Relids extra_lateral_rels);
38+
Relids param_source_rels);
3939
static void hash_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel,
4040
RelOptInfo *outerrel, RelOptInfo *innerrel,
4141
List *restrictlist,
4242
JoinType jointype, SpecialJoinInfo *sjinfo,
4343
SemiAntiJoinFactors *semifactors,
44-
Relids param_source_rels, Relids extra_lateral_rels);
44+
Relids param_source_rels);
4545
static List *select_mergejoin_clauses(PlannerInfo *root,
4646
RelOptInfo *joinrel,
4747
RelOptInfo *outerrel,
@@ -87,7 +87,6 @@ add_paths_to_joinrel(PlannerInfo *root,
8787
bool mergejoin_allowed = true;
8888
SemiAntiJoinFactors semifactors;
8989
Relids param_source_rels = NULL;
90-
Relids extra_lateral_rels = NULL;
9190
ListCell *lc;
9291

9392
/*
@@ -153,59 +152,14 @@ add_paths_to_joinrel(PlannerInfo *root,
153152
}
154153

155154
/*
156-
* However, when a LATERAL subquery is involved, we have to be a bit
157-
* laxer, because there will simply not be any paths for the joinrel that
158-
* aren't parameterized by whatever the subquery is parameterized by,
159-
* unless its parameterization is resolved within the joinrel. Hence, add
160-
* to param_source_rels anything that is laterally referenced in either
161-
* input and is not in the join already.
155+
* However, when a LATERAL subquery is involved, there will simply not be
156+
* any paths for the joinrel that aren't parameterized by whatever the
157+
* subquery is parameterized by, unless its parameterization is resolved
158+
* within the joinrel. So we might as well allow additional dependencies
159+
* on whatever residual lateral dependencies the joinrel will have.
162160
*/
163-
foreach(lc, root->lateral_info_list)
164-
{
165-
LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(lc);
166-
167-
if (bms_is_subset(ljinfo->lateral_rhs, joinrel->relids))
168-
param_source_rels = bms_join(param_source_rels,
169-
bms_difference(ljinfo->lateral_lhs,
170-
joinrel->relids));
171-
}
172-
173-
/*
174-
* Another issue created by LATERAL references is that PlaceHolderVars
175-
* that need to be computed at this join level might contain lateral
176-
* references to rels not in the join, meaning that the paths for the join
177-
* would need to be marked as parameterized by those rels, independently
178-
* of all other considerations. Set extra_lateral_rels to the set of such
179-
* rels. This will not affect our decisions as to which paths to
180-
* generate; we merely add these rels to their required_outer sets.
181-
*/
182-
foreach(lc, root->placeholder_list)
183-
{
184-
PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
185-
186-
/* PHVs without lateral refs can be skipped over quickly */
187-
if (phinfo->ph_lateral == NULL)
188-
continue;
189-
/* Is it due to be evaluated at this join, and not in either input? */
190-
if (bms_is_subset(phinfo->ph_eval_at, joinrel->relids) &&
191-
!bms_is_subset(phinfo->ph_eval_at, outerrel->relids) &&
192-
!bms_is_subset(phinfo->ph_eval_at, innerrel->relids))
193-
{
194-
/* Yes, remember its lateral rels */
195-
extra_lateral_rels = bms_add_members(extra_lateral_rels,
196-
phinfo->ph_lateral);
197-
}
198-
}
199-
200-
/*
201-
* Make sure extra_lateral_rels doesn't list anything within the join, and
202-
* that it's NULL if empty. (This allows us to use bms_add_members to add
203-
* it to required_outer below, while preserving the property that
204-
* required_outer is exactly NULL if empty.)
205-
*/
206-
extra_lateral_rels = bms_del_members(extra_lateral_rels, joinrel->relids);
207-
if (bms_is_empty(extra_lateral_rels))
208-
extra_lateral_rels = NULL;
161+
param_source_rels = bms_add_members(param_source_rels,
162+
joinrel->lateral_relids);
209163

210164
/*
211165
* 1. Consider mergejoin paths where both relations must be explicitly
@@ -215,7 +169,7 @@ add_paths_to_joinrel(PlannerInfo *root,
215169
sort_inner_and_outer(root, joinrel, outerrel, innerrel,
216170
restrictlist, mergeclause_list, jointype,
217171
sjinfo,
218-
param_source_rels, extra_lateral_rels);
172+
param_source_rels);
219173

220174
/*
221175
* 2. Consider paths where the outer relation need not be explicitly
@@ -228,7 +182,7 @@ add_paths_to_joinrel(PlannerInfo *root,
228182
match_unsorted_outer(root, joinrel, outerrel, innerrel,
229183
restrictlist, mergeclause_list, jointype,
230184
sjinfo, &semifactors,
231-
param_source_rels, extra_lateral_rels);
185+
param_source_rels);
232186

233187
#ifdef NOT_USED
234188

@@ -247,7 +201,7 @@ add_paths_to_joinrel(PlannerInfo *root,
247201
match_unsorted_inner(root, joinrel, outerrel, innerrel,
248202
restrictlist, mergeclause_list, jointype,
249203
sjinfo, &semifactors,
250-
param_source_rels, extra_lateral_rels);
204+
param_source_rels);
251205
#endif
252206

253207
/*
@@ -259,7 +213,7 @@ add_paths_to_joinrel(PlannerInfo *root,
259213
hash_inner_and_outer(root, joinrel, outerrel, innerrel,
260214
restrictlist, jointype,
261215
sjinfo, &semifactors,
262-
param_source_rels, extra_lateral_rels);
216+
param_source_rels);
263217
}
264218

265219
/*
@@ -353,7 +307,6 @@ try_nestloop_path(PlannerInfo *root,
353307
SpecialJoinInfo *sjinfo,
354308
SemiAntiJoinFactors *semifactors,
355309
Relids param_source_rels,
356-
Relids extra_lateral_rels,
357310
Path *outer_path,
358311
Path *inner_path,
359312
List *restrict_clauses,
@@ -382,9 +335,13 @@ try_nestloop_path(PlannerInfo *root,
382335

383336
/*
384337
* Independently of that, add parameterization needed for any
385-
* PlaceHolderVars that need to be computed at the join.
338+
* PlaceHolderVars that need to be computed at the join. We can handle
339+
* that just by adding joinrel->lateral_relids; that might include some
340+
* rels that are already in required_outer, but no harm done. (Note that
341+
* lateral_relids is exactly NULL if empty, so this will not break the
342+
* property that required_outer is too.)
386343
*/
387-
required_outer = bms_add_members(required_outer, extra_lateral_rels);
344+
required_outer = bms_add_members(required_outer, joinrel->lateral_relids);
388345

389346
/*
390347
* Do a precheck to quickly eliminate obviously-inferior paths. We
@@ -434,7 +391,6 @@ try_mergejoin_path(PlannerInfo *root,
434391
JoinType jointype,
435392
SpecialJoinInfo *sjinfo,
436393
Relids param_source_rels,
437-
Relids extra_lateral_rels,
438394
Path *outer_path,
439395
Path *inner_path,
440396
List *restrict_clauses,
@@ -464,7 +420,7 @@ try_mergejoin_path(PlannerInfo *root,
464420
* Independently of that, add parameterization needed for any
465421
* PlaceHolderVars that need to be computed at the join.
466422
*/
467-
required_outer = bms_add_members(required_outer, extra_lateral_rels);
423+
required_outer = bms_add_members(required_outer, joinrel->lateral_relids);
468424

469425
/*
470426
* If the given paths are already well enough ordered, we can skip doing
@@ -523,7 +479,6 @@ try_hashjoin_path(PlannerInfo *root,
523479
SpecialJoinInfo *sjinfo,
524480
SemiAntiJoinFactors *semifactors,
525481
Relids param_source_rels,
526-
Relids extra_lateral_rels,
527482
Path *outer_path,
528483
Path *inner_path,
529484
List *restrict_clauses,
@@ -550,7 +505,7 @@ try_hashjoin_path(PlannerInfo *root,
550505
* Independently of that, add parameterization needed for any
551506
* PlaceHolderVars that need to be computed at the join.
552507
*/
553-
required_outer = bms_add_members(required_outer, extra_lateral_rels);
508+
required_outer = bms_add_members(required_outer, joinrel->lateral_relids);
554509

555510
/*
556511
* See comments in try_nestloop_path(). Also note that hashjoin paths
@@ -630,7 +585,6 @@ clause_sides_match_join(RestrictInfo *rinfo, RelOptInfo *outerrel,
630585
* 'jointype' is the type of join to do
631586
* 'sjinfo' is extra info about the join for selectivity estimation
632587
* 'param_source_rels' are OK targets for parameterization of result paths
633-
* 'extra_lateral_rels' are additional parameterization for result paths
634588
*/
635589
static void
636590
sort_inner_and_outer(PlannerInfo *root,
@@ -641,8 +595,7 @@ sort_inner_and_outer(PlannerInfo *root,
641595
List *mergeclause_list,
642596
JoinType jointype,
643597
SpecialJoinInfo *sjinfo,
644-
Relids param_source_rels,
645-
Relids extra_lateral_rels)
598+
Relids param_source_rels)
646599
{
647600
Path *outer_path;
648601
Path *inner_path;
@@ -772,7 +725,6 @@ sort_inner_and_outer(PlannerInfo *root,
772725
jointype,
773726
sjinfo,
774727
param_source_rels,
775-
extra_lateral_rels,
776728
outer_path,
777729
inner_path,
778730
restrictlist,
@@ -818,7 +770,6 @@ sort_inner_and_outer(PlannerInfo *root,
818770
* 'sjinfo' is extra info about the join for selectivity estimation
819771
* 'semifactors' contains valid data if jointype is SEMI or ANTI
820772
* 'param_source_rels' are OK targets for parameterization of result paths
821-
* 'extra_lateral_rels' are additional parameterization for result paths
822773
*/
823774
static void
824775
match_unsorted_outer(PlannerInfo *root,
@@ -830,8 +781,7 @@ match_unsorted_outer(PlannerInfo *root,
830781
JoinType jointype,
831782
SpecialJoinInfo *sjinfo,
832783
SemiAntiJoinFactors *semifactors,
833-
Relids param_source_rels,
834-
Relids extra_lateral_rels)
784+
Relids param_source_rels)
835785
{
836786
JoinType save_jointype = jointype;
837787
bool nestjoinOK;
@@ -961,7 +911,6 @@ match_unsorted_outer(PlannerInfo *root,
961911
sjinfo,
962912
semifactors,
963913
param_source_rels,
964-
extra_lateral_rels,
965914
outerpath,
966915
inner_cheapest_total,
967916
restrictlist,
@@ -987,7 +936,6 @@ match_unsorted_outer(PlannerInfo *root,
987936
sjinfo,
988937
semifactors,
989938
param_source_rels,
990-
extra_lateral_rels,
991939
outerpath,
992940
innerpath,
993941
restrictlist,
@@ -1002,7 +950,6 @@ match_unsorted_outer(PlannerInfo *root,
1002950
sjinfo,
1003951
semifactors,
1004952
param_source_rels,
1005-
extra_lateral_rels,
1006953
outerpath,
1007954
matpath,
1008955
restrictlist,
@@ -1058,7 +1005,6 @@ match_unsorted_outer(PlannerInfo *root,
10581005
jointype,
10591006
sjinfo,
10601007
param_source_rels,
1061-
extra_lateral_rels,
10621008
outerpath,
10631009
inner_cheapest_total,
10641010
restrictlist,
@@ -1157,7 +1103,6 @@ match_unsorted_outer(PlannerInfo *root,
11571103
jointype,
11581104
sjinfo,
11591105
param_source_rels,
1160-
extra_lateral_rels,
11611106
outerpath,
11621107
innerpath,
11631108
restrictlist,
@@ -1203,7 +1148,6 @@ match_unsorted_outer(PlannerInfo *root,
12031148
jointype,
12041149
sjinfo,
12051150
param_source_rels,
1206-
extra_lateral_rels,
12071151
outerpath,
12081152
innerpath,
12091153
restrictlist,
@@ -1238,7 +1182,6 @@ match_unsorted_outer(PlannerInfo *root,
12381182
* 'sjinfo' is extra info about the join for selectivity estimation
12391183
* 'semifactors' contains valid data if jointype is SEMI or ANTI
12401184
* 'param_source_rels' are OK targets for parameterization of result paths
1241-
* 'extra_lateral_rels' are additional parameterization for result paths
12421185
*/
12431186
static void
12441187
hash_inner_and_outer(PlannerInfo *root,
@@ -1249,8 +1192,7 @@ hash_inner_and_outer(PlannerInfo *root,
12491192
JoinType jointype,
12501193
SpecialJoinInfo *sjinfo,
12511194
SemiAntiJoinFactors *semifactors,
1252-
Relids param_source_rels,
1253-
Relids extra_lateral_rels)
1195+
Relids param_source_rels)
12541196
{
12551197
bool isouterjoin = IS_OUTER_JOIN(jointype);
12561198
List *hashclauses;
@@ -1324,7 +1266,6 @@ hash_inner_and_outer(PlannerInfo *root,
13241266
sjinfo,
13251267
semifactors,
13261268
param_source_rels,
1327-
extra_lateral_rels,
13281269
cheapest_total_outer,
13291270
cheapest_total_inner,
13301271
restrictlist,
@@ -1344,7 +1285,6 @@ hash_inner_and_outer(PlannerInfo *root,
13441285
sjinfo,
13451286
semifactors,
13461287
param_source_rels,
1347-
extra_lateral_rels,
13481288
cheapest_total_outer,
13491289
cheapest_total_inner,
13501290
restrictlist,
@@ -1357,7 +1297,6 @@ hash_inner_and_outer(PlannerInfo *root,
13571297
sjinfo,
13581298
semifactors,
13591299
param_source_rels,
1360-
extra_lateral_rels,
13611300
cheapest_startup_outer,
13621301
cheapest_total_inner,
13631302
restrictlist,
@@ -1382,7 +1321,6 @@ hash_inner_and_outer(PlannerInfo *root,
13821321
sjinfo,
13831322
semifactors,
13841323
param_source_rels,
1385-
extra_lateral_rels,
13861324
cheapest_startup_outer,
13871325
cheapest_total_inner,
13881326
restrictlist,
@@ -1420,7 +1358,6 @@ hash_inner_and_outer(PlannerInfo *root,
14201358
sjinfo,
14211359
semifactors,
14221360
param_source_rels,
1423-
extra_lateral_rels,
14241361
outerpath,
14251362
innerpath,
14261363
restrictlist,

src/backend/optimizer/util/relnode.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ build_join_rel(PlannerInfo *root,
376376
joinrel->attr_needed = NULL;
377377
joinrel->attr_widths = NULL;
378378
joinrel->lateral_vars = NIL;
379-
joinrel->lateral_relids = NULL;
379+
joinrel->lateral_relids = min_join_parameterization(root, joinrel->relids);
380380
joinrel->lateral_referencers = NULL;
381381
joinrel->indexlist = NIL;
382382
joinrel->pages = 0;

src/include/nodes/relation.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ typedef struct PlannerInfo
349349
* lateral_vars - lateral cross-references of rel, if any (list of
350350
* Vars and PlaceHolderVars)
351351
* lateral_relids - required outer rels for LATERAL, as a Relids set
352-
* (for child rels this can be more than lateral_vars)
352+
* (this is now used for join rels too, but we won't move it till 9.5)
353353
* lateral_referencers - relids of rels that reference this one laterally
354354
* indexlist - list of IndexOptInfo nodes for relation's indexes
355355
* (always NIL if it's not a table)
@@ -368,7 +368,7 @@ typedef struct PlannerInfo
368368
* and fdw_private are filled during initial path creation.
369369
*
370370
* For otherrels that are appendrel members, these fields are filled
371-
* in just as for a baserel.
371+
* in just as for a baserel, except we don't bother with lateral_vars.
372372
*
373373
* The presence of the remaining fields depends on the restrictions
374374
* and joins that the relation participates in:

0 commit comments

Comments
 (0)