Skip to content

Commit 80bece3

Browse files
committed
Allow subquery pullup to wrap a PlaceHolderVar in another one.
The code for wrapping subquery output expressions in PlaceHolderVars believed that if the expression already was a PlaceHolderVar, it was never necessary to wrap that in another one. That's wrong if the expression is underneath an outer join and involves a lateral reference to outside that scope: failing to add an additional PHV risks evaluating the expression at the wrong place and hence not forcing it to null when the outer join should do so. This is an oversight in commit 9e7e29c, which added logic to forcibly wrap lateral-reference Vars in PlaceHolderVars, but didn't see that the adjacent case for PlaceHolderVars needed the same treatment. The test case we have for this doesn't fail before 4be058f, but now that I see the problem I wonder if it is possible to demonstrate related errors before that. That's moot though, since all such branches are out of support. Per bug #18284 from Holger Reise. Back-patch to all supported branches. Discussion: https://postgr.es/m/18284-47505a20c23647f8@postgresql.org
1 parent af36f19 commit 80bece3

File tree

3 files changed

+46
-2
lines changed

3 files changed

+46
-2
lines changed

src/backend/optimizer/prep/prepjointree.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2435,8 +2435,13 @@ pullup_replace_vars_callback(Var *var,
24352435
else if (newnode && IsA(newnode, PlaceHolderVar) &&
24362436
((PlaceHolderVar *) newnode)->phlevelsup == 0)
24372437
{
2438-
/* No need to wrap a PlaceHolderVar with another one, either */
2439-
wrap = false;
2438+
/* The same rules apply for a PlaceHolderVar */
2439+
if (rcon->target_rte->lateral &&
2440+
!bms_is_subset(((PlaceHolderVar *) newnode)->phrels,
2441+
rcon->relids))
2442+
wrap = true;
2443+
else
2444+
wrap = false;
24402445
}
24412446
else
24422447
{

src/test/regress/expected/join.out

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7013,6 +7013,33 @@ select * from
70137013
Output: (COALESCE((COALESCE(b.q2, '42'::bigint)), d.q2))
70147014
(24 rows)
70157015

7016+
-- another case requiring nested PlaceHolderVars
7017+
explain (verbose, costs off)
7018+
select * from
7019+
(select 0 as val0) as ss0
7020+
left join (select 1 as val) as ss1 on true
7021+
left join lateral (select ss1.val as val_filtered where false) as ss2 on true;
7022+
QUERY PLAN
7023+
--------------------------------
7024+
Nested Loop Left Join
7025+
Output: 0, (1), ((1))
7026+
Join Filter: false
7027+
-> Result
7028+
Output: 1
7029+
-> Result
7030+
Output: (1)
7031+
One-Time Filter: false
7032+
(8 rows)
7033+
7034+
select * from
7035+
(select 0 as val0) as ss0
7036+
left join (select 1 as val) as ss1 on true
7037+
left join lateral (select ss1.val as val_filtered where false) as ss2 on true;
7038+
val0 | val | val_filtered
7039+
------+-----+--------------
7040+
0 | 1 |
7041+
(1 row)
7042+
70167043
-- case that breaks the old ph_may_need optimization
70177044
explain (verbose, costs off)
70187045
select c.*,a.*,ss1.q1,ss2.q1,ss3.* from

src/test/regress/sql/join.sql

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2522,6 +2522,18 @@ select * from
25222522
) on c.q2 = ss2.q1,
25232523
lateral (select ss2.y offset 0) ss3;
25242524

2525+
-- another case requiring nested PlaceHolderVars
2526+
explain (verbose, costs off)
2527+
select * from
2528+
(select 0 as val0) as ss0
2529+
left join (select 1 as val) as ss1 on true
2530+
left join lateral (select ss1.val as val_filtered where false) as ss2 on true;
2531+
2532+
select * from
2533+
(select 0 as val0) as ss0
2534+
left join (select 1 as val) as ss1 on true
2535+
left join lateral (select ss1.val as val_filtered where false) as ss2 on true;
2536+
25252537
-- case that breaks the old ph_may_need optimization
25262538
explain (verbose, costs off)
25272539
select c.*,a.*,ss1.q1,ss2.q1,ss3.* from

0 commit comments

Comments
 (0)