Skip to content

Commit 666a4de

Browse files
committed
Fix miscomputation of direct_lateral_relids for join relations.
If a PlaceHolderVar is to be evaluated at a join relation, but its value is only needed there and not at higher levels, we neglected to update the joinrel's direct_lateral_relids to include the PHV's source rel. This causes problems because join_is_legal() then won't allow joining the joinrel to the PHV's source rel at all, leading to "failed to build any N-way joins" planner failures. Per report from Andreas Seltenreich. Back-patch to 9.5 where the problem originated. Discussion: https://postgr.es/m/87blfgqa4t.fsf@aurora.ydns.eu
1 parent 74d6fb0 commit 666a4de

File tree

3 files changed

+118
-11
lines changed

3 files changed

+118
-11
lines changed

src/backend/optimizer/util/placeholder.c

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -404,8 +404,10 @@ add_placeholders_to_base_rels(PlannerInfo *root)
404404
* and if they contain lateral references, add those references to the
405405
* joinrel's direct_lateral_relids.
406406
*
407-
* A join rel should emit a PlaceHolderVar if (a) the PHV is needed above
408-
* this join level and (b) the PHV can be computed at or below this level.
407+
* A join rel should emit a PlaceHolderVar if (a) the PHV can be computed
408+
* at or below this join level and (b) the PHV is needed above this level.
409+
* However, condition (a) is sufficient to add to direct_lateral_relids,
410+
* as explained below.
409411
*/
410412
void
411413
add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
@@ -418,11 +420,11 @@ add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
418420
{
419421
PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
420422

421-
/* Is it still needed above this joinrel? */
422-
if (bms_nonempty_difference(phinfo->ph_needed, relids))
423+
/* Is it computable here? */
424+
if (bms_is_subset(phinfo->ph_eval_at, relids))
423425
{
424-
/* Is it computable here? */
425-
if (bms_is_subset(phinfo->ph_eval_at, relids))
426+
/* Is it still needed above this joinrel? */
427+
if (bms_nonempty_difference(phinfo->ph_needed, relids))
426428
{
427429
/* Yup, add it to the output */
428430
joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs,
@@ -450,12 +452,26 @@ add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
450452
joinrel->reltarget->cost.startup += cost.startup;
451453
joinrel->reltarget->cost.per_tuple += cost.per_tuple;
452454
}
453-
454-
/* Adjust joinrel's direct_lateral_relids as needed */
455-
joinrel->direct_lateral_relids =
456-
bms_add_members(joinrel->direct_lateral_relids,
457-
phinfo->ph_lateral);
458455
}
456+
457+
/*
458+
* Also adjust joinrel's direct_lateral_relids to include the
459+
* PHV's source rel(s). We must do this even if we're not
460+
* actually going to emit the PHV, otherwise join_is_legal() will
461+
* reject valid join orderings. (In principle maybe we could
462+
* instead remove the joinrel's lateral_relids dependency; but
463+
* that's complicated to get right, and cases where we're not
464+
* going to emit the PHV are too rare to justify the work.)
465+
*
466+
* In principle we should only do this if the join doesn't yet
467+
* include the PHV's source rel(s). But our caller
468+
* build_join_rel() will clean things up by removing the join's
469+
* own relids from its direct_lateral_relids, so we needn't
470+
* account for that here.
471+
*/
472+
joinrel->direct_lateral_relids =
473+
bms_add_members(joinrel->direct_lateral_relids,
474+
phinfo->ph_lateral);
459475
}
460476
}
461477
}

src/test/regress/expected/join.out

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3009,6 +3009,70 @@ order by 1,2;
30093009
4567890123456789 | 4567890123456789 | 4567890123456789 | 42
30103010
(8 rows)
30113011

3012+
--
3013+
-- variant where a PlaceHolderVar is needed at a join, but not above the join
3014+
--
3015+
explain (costs off)
3016+
select * from
3017+
int4_tbl as i41,
3018+
lateral
3019+
(select 1 as x from
3020+
(select i41.f1 as lat,
3021+
i42.f1 as loc from
3022+
int8_tbl as i81, int4_tbl as i42) as ss1
3023+
right join int4_tbl as i43 on (i43.f1 > 1)
3024+
where ss1.loc = ss1.lat) as ss2
3025+
where i41.f1 > 0;
3026+
QUERY PLAN
3027+
--------------------------------------------------
3028+
Nested Loop
3029+
-> Nested Loop
3030+
-> Seq Scan on int4_tbl i41
3031+
Filter: (f1 > 0)
3032+
-> Nested Loop
3033+
Join Filter: (i41.f1 = i42.f1)
3034+
-> Seq Scan on int8_tbl i81
3035+
-> Materialize
3036+
-> Seq Scan on int4_tbl i42
3037+
-> Materialize
3038+
-> Seq Scan on int4_tbl i43
3039+
Filter: (f1 > 1)
3040+
(12 rows)
3041+
3042+
select * from
3043+
int4_tbl as i41,
3044+
lateral
3045+
(select 1 as x from
3046+
(select i41.f1 as lat,
3047+
i42.f1 as loc from
3048+
int8_tbl as i81, int4_tbl as i42) as ss1
3049+
right join int4_tbl as i43 on (i43.f1 > 1)
3050+
where ss1.loc = ss1.lat) as ss2
3051+
where i41.f1 > 0;
3052+
f1 | x
3053+
------------+---
3054+
123456 | 1
3055+
123456 | 1
3056+
123456 | 1
3057+
123456 | 1
3058+
123456 | 1
3059+
123456 | 1
3060+
123456 | 1
3061+
123456 | 1
3062+
123456 | 1
3063+
123456 | 1
3064+
2147483647 | 1
3065+
2147483647 | 1
3066+
2147483647 | 1
3067+
2147483647 | 1
3068+
2147483647 | 1
3069+
2147483647 | 1
3070+
2147483647 | 1
3071+
2147483647 | 1
3072+
2147483647 | 1
3073+
2147483647 | 1
3074+
(20 rows)
3075+
30123076
--
30133077
-- test the corner cases FULL JOIN ON TRUE and FULL JOIN ON FALSE
30143078
--

src/test/regress/sql/join.sql

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -904,6 +904,33 @@ where
904904
1 = (select 1 from int8_tbl t3 where ss.y is not null limit 1)
905905
order by 1,2;
906906

907+
--
908+
-- variant where a PlaceHolderVar is needed at a join, but not above the join
909+
--
910+
911+
explain (costs off)
912+
select * from
913+
int4_tbl as i41,
914+
lateral
915+
(select 1 as x from
916+
(select i41.f1 as lat,
917+
i42.f1 as loc from
918+
int8_tbl as i81, int4_tbl as i42) as ss1
919+
right join int4_tbl as i43 on (i43.f1 > 1)
920+
where ss1.loc = ss1.lat) as ss2
921+
where i41.f1 > 0;
922+
923+
select * from
924+
int4_tbl as i41,
925+
lateral
926+
(select 1 as x from
927+
(select i41.f1 as lat,
928+
i42.f1 as loc from
929+
int8_tbl as i81, int4_tbl as i42) as ss1
930+
right join int4_tbl as i43 on (i43.f1 > 1)
931+
where ss1.loc = ss1.lat) as ss2
932+
where i41.f1 > 0;
933+
907934
--
908935
-- test the corner cases FULL JOIN ON TRUE and FULL JOIN ON FALSE
909936
--

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy