/*
* 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));
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);
* 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);
}
/*
-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
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;