diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index f582d4067b9255406d49c97e020bcabbc615901a..9565e2d607022feb2126d0897ef1d7000fcba5f6 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -199,10 +199,13 @@ add_vars_to_targetlist(PlannerInfo *root, List *vars, /* * If we are creating PlaceHolderInfos, mark them with the correct * maybe-needed locations. Otherwise, it's too late to change - * that. + * that, so we'd better not have set ph_needed to more than + * ph_may_need. */ if (create_new_ph) mark_placeholder_maybe_needed(root, phinfo, where_needed); + else + Assert(bms_is_subset(phinfo->ph_needed, phinfo->ph_may_need)); } else elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); diff --git a/src/backend/optimizer/util/placeholder.c b/src/backend/optimizer/util/placeholder.c index 317a01940e31bafb0af7bec42a624466e716872a..345a0277a50dfaa8b829ea77152da89c5fd0ed45 100644 --- a/src/backend/optimizer/util/placeholder.c +++ b/src/backend/optimizer/util/placeholder.c @@ -250,6 +250,8 @@ void mark_placeholder_maybe_needed(PlannerInfo *root, PlaceHolderInfo *phinfo, Relids relids) { + Relids est_eval_level; + /* Mark the PHV as possibly needed at the given syntactic level */ phinfo->ph_may_need = bms_add_members(phinfo->ph_may_need, relids); @@ -258,11 +260,18 @@ mark_placeholder_maybe_needed(PlannerInfo *root, PlaceHolderInfo *phinfo, * lower-level PHVs. We need to get those into the PlaceHolderInfo list, * but they aren't going to be needed where the outer PHV is referenced. * Rather, they'll be needed where the outer PHV is evaluated. We can - * estimate that (conservatively) as the syntactic location of the PHV's - * expression. Recurse to take care of any such PHVs. + * estimate that conservatively as the syntactic location of the PHV's + * expression, but not less than the level of any Vars it contains. + * (Normally the Vars would come from below the syntactic location anyway, + * but this might not be true if the PHV contains any LATERAL references.) */ + est_eval_level = bms_union(phinfo->ph_var->phrels, phinfo->ph_eval_at); + + /* Now recurse to take care of any such PHVs */ mark_placeholders_in_expr(root, (Node *) phinfo->ph_var->phexpr, - phinfo->ph_var->phrels); + est_eval_level); + + bms_free(est_eval_level); } /* diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 7c1ab4486151fe5ed96656c0c23fd3f0fae8f14d..914a6fd84473fa97302e3c3ca8320d35dea3fcb3 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -3465,6 +3465,46 @@ select v.* from -4567890123456789 | (20 rows) +-- case requiring nested PlaceHolderVars +explain (verbose, costs off) +select * from + int8_tbl c left join ( + int8_tbl a left join (select q1, coalesce(q2,42) as x from int8_tbl b) ss1 + on a.q2 = ss1.q1 + cross join + lateral (select q1, coalesce(ss1.x,q2) as y from int8_tbl d) ss2 + ) on c.q2 = ss2.q1, + lateral (select ss2.y) ss3; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Nested Loop + Output: c.q1, c.q2, a.q1, a.q2, b.q1, (COALESCE(b.q2, 42::bigint)), d.q1, (COALESCE((COALESCE(b.q2, 42::bigint)), d.q2)), ((COALESCE((COALESCE(b.q2, 42::bigint)), d.q2))) + -> Hash Right Join + Output: c.q1, c.q2, a.q1, a.q2, b.q1, d.q1, (COALESCE(b.q2, 42::bigint)), (COALESCE((COALESCE(b.q2, 42::bigint)), d.q2)) + Hash Cond: (d.q1 = c.q2) + -> Nested Loop + Output: a.q1, a.q2, b.q1, d.q1, (COALESCE(b.q2, 42::bigint)), COALESCE((COALESCE(b.q2, 42::bigint)), d.q2) + -> Hash Left Join + Output: a.q1, a.q2, b.q1, (COALESCE(b.q2, 42::bigint)) + Hash Cond: (a.q2 = b.q1) + -> Seq Scan on public.int8_tbl a + Output: a.q1, a.q2 + -> Hash + Output: b.q1, (COALESCE(b.q2, 42::bigint)) + -> Seq Scan on public.int8_tbl b + Output: b.q1, COALESCE(b.q2, 42::bigint) + -> Materialize + Output: d.q1, d.q2 + -> Seq Scan on public.int8_tbl d + Output: d.q1, d.q2 + -> Hash + Output: c.q1, c.q2 + -> Seq Scan on public.int8_tbl c + Output: c.q1, c.q2 + -> Result + Output: (COALESCE((COALESCE(b.q2, 42::bigint)), d.q2)) +(26 rows) + -- test some error cases where LATERAL should have been used but wasn't select f1,g from int4_tbl a, generate_series(0, f1) g; ERROR: column "f1" does not exist diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index 2213a446a3ddd7136d802fe016e4156eca02b17c..fcc6572709de95739c65760d20e07437389335fc 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -942,6 +942,17 @@ select v.* from left join int4_tbl z on z.f1 = x.q2, lateral (select x.q1,y.q1 from dual union all select x.q2,y.q2 from dual) v(vx,vy); +-- case requiring nested PlaceHolderVars +explain (verbose, costs off) +select * from + int8_tbl c left join ( + int8_tbl a left join (select q1, coalesce(q2,42) as x from int8_tbl b) ss1 + on a.q2 = ss1.q1 + cross join + lateral (select q1, coalesce(ss1.x,q2) as y from int8_tbl d) ss2 + ) on c.q2 = ss2.q1, + lateral (select ss2.y) ss3; + -- test some error cases where LATERAL should have been used but wasn't select f1,g from int4_tbl a, generate_series(0, f1) g; select f1,g from int4_tbl a, generate_series(0, a.f1) g;