diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 794b33c4295..f749c2b2bbc 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -1126,13 +1126,22 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, * for the child. This saves some effort while processing the child * rel, and it also avoids an implementation restriction in * adjust_appendrel_attrs (it can't apply nullingrels to a non-Var). + * + * clause_relids does not reflect references to this rel that appear + * within PlaceHolderVars, but those are lateral references, so we + * also skip quals that use any rel laterally referencing this rel. + * This is safe since such quals are useless for the child: enforcing + * one at a scan of this rel would require the scan to be + * parameterized by a rel that laterally references this rel, which is + * impossible since that rel needs this rel's output first. */ childrinfos = NIL; foreach(lc, rel->joininfo) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); - if (!bms_overlap(rinfo->clause_relids, rel->nulling_relids)) + if (!bms_overlap(rinfo->clause_relids, rel->nulling_relids) && + !bms_overlap(rinfo->clause_relids, rel->lateral_referencers)) childrinfos = lappend(childrinfos, adjust_appendrel_attrs(root, (Node *) rinfo, diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index d289e53a039..56f526a47b3 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -4858,6 +4858,84 @@ where ss.x is null; Output: 'bar'::text (12 rows) +-- Test a join qual referencing a nulled appendrel output only within a PHV +explain (verbose, costs off) +select 1 from int4_tbl t1 + left join (select 1 as x union all select 2) s on true + left join lateral (select s.x as y from int4_tbl t2) ss on true + left join int4_tbl t3 on ss.y is null; + QUERY PLAN +-------------------------------------------------- + Nested Loop Left Join + Output: 1 + Join Filter: (((1)) IS NULL) + -> Nested Loop Left Join + Output: ((1)) + -> Nested Loop Left Join + Output: (1) + -> Seq Scan on public.int4_tbl t1 + Output: t1.f1 + -> Materialize + Output: (1) + -> Append + -> Result + Output: 1 + -> Result + Output: 2 + -> Memoize + Output: ((1)) + Cache Key: (1) + Cache Mode: binary + -> Seq Scan on public.int4_tbl t2 + Output: (1) + -> Materialize + -> Seq Scan on public.int4_tbl t3 +(24 rows) + +-- Same, but the join qual's required_relids don't include the nulling OJ +explain (verbose, costs off) +select 1 from ((int4_tbl t1 left join (select 1 as x union all select 2) s on t1.f1 < s.x) + join lateral (select coalesce(s.x, t2.f1) as y from int4_tbl t2) ss on true) + full join int4_tbl t3 on t3.f1 = t1.f1 and ss.y = t3.f1 + right join int4_tbl t4 on t3.f1 = t4.f1; + QUERY PLAN +--------------------------------------------------------------------------- + Hash Right Join + Output: 1 + Hash Cond: (t3.f1 = t4.f1) + -> Hash Right Join + Output: t3.f1 + Hash Cond: ((t1.f1 = t3.f1) AND ((COALESCE((1), t2.f1)) = t3.f1)) + -> Nested Loop + Output: t1.f1, (COALESCE((1), t2.f1)) + -> Nested Loop Left Join + Output: t1.f1, (1) + Join Filter: (t1.f1 < (1)) + -> Seq Scan on public.int4_tbl t1 + Output: t1.f1 + -> Materialize + Output: (1) + -> Append + -> Result + Output: 1 + -> Result + Output: 2 + -> Memoize + Output: (COALESCE((1), t2.f1)) + Cache Key: (1) + Cache Mode: binary + -> Seq Scan on public.int4_tbl t2 + Output: COALESCE((1), t2.f1) + -> Hash + Output: t3.f1 + -> Seq Scan on public.int4_tbl t3 + Output: t3.f1 + -> Hash + Output: t4.f1 + -> Seq Scan on public.int4_tbl t4 + Output: t4.f1 +(34 rows) + -- Test computation of varnullingrels when translating appendrel Var begin; create temp table t_append (a int not null, b int); diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index 4dcf185a6eb..634edcd67d8 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -1648,6 +1648,20 @@ select * from int4_tbl left join ( ) ss(x) on true where ss.x is null; +-- Test a join qual referencing a nulled appendrel output only within a PHV +explain (verbose, costs off) +select 1 from int4_tbl t1 + left join (select 1 as x union all select 2) s on true + left join lateral (select s.x as y from int4_tbl t2) ss on true + left join int4_tbl t3 on ss.y is null; + +-- Same, but the join qual's required_relids don't include the nulling OJ +explain (verbose, costs off) +select 1 from ((int4_tbl t1 left join (select 1 as x union all select 2) s on t1.f1 < s.x) + join lateral (select coalesce(s.x, t2.f1) as y from int4_tbl t2) ss on true) + full join int4_tbl t3 on t3.f1 = t1.f1 and ss.y = t3.f1 + right join int4_tbl t4 on t3.f1 = t4.f1; + -- Test computation of varnullingrels when translating appendrel Var begin;