diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c index f456b3b0a44..a72704da425 100644 --- a/src/backend/optimizer/util/appendinfo.c +++ b/src/backend/optimizer/util/appendinfo.c @@ -233,8 +233,9 @@ adjust_appendrel_attrs_mutator(Node *node, * You might think we need to adjust var->varnullingrels, but that * shouldn't need any changes. It will contain outer-join relids, * while the transformation we are making affects only baserels. - * Below, we just propagate var->varnullingrels into the translated - * Var. + * Below, we just merge var->varnullingrels into the translated Var. + * (We must merge not just copy: the child Var could have some + * nullingrel bits set already, and we mustn't drop those.) * * If var->varnullingrels isn't empty, and the translation wouldn't be * a Var, we have to fail. One could imagine wrapping the translated @@ -279,7 +280,12 @@ adjust_appendrel_attrs_mutator(Node *node, elog(ERROR, "attribute %d of relation \"%s\" does not exist", var->varattno, get_rel_name(appinfo->parent_reloid)); if (IsA(newnode, Var)) - ((Var *) newnode)->varnullingrels = var->varnullingrels; + { + Var *newvar = (Var *) newnode; + + newvar->varnullingrels = bms_add_members(newvar->varnullingrels, + var->varnullingrels); + } else if (var->varnullingrels != NULL) elog(ERROR, "failed to apply nullingrels to a non-Var"); return newnode; diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 84e35981ed8..b6f86d9aaf0 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -4020,6 +4020,58 @@ where ss.x is null; Output: 'bar'::text (12 rows) +-- Test computation of varnullingrels when translating appendrel Var +begin; +create temp table t_append (a int not null, b int); +insert into t_append values (1, 1); +insert into t_append values (2, 3); +explain (verbose, costs off) +select t1.a, s.a from t_append t1 + left join t_append t2 on t1.a = t2.b + join lateral ( + select t1.a as a union all select t2.a as a + ) s on true +where s.a is not null; + QUERY PLAN +--------------------------------------------------- + Nested Loop + Output: t1.a, (t1.a) + -> Merge Left Join + Output: t1.a, t2.a + Merge Cond: (t1.a = t2.b) + -> Sort + Output: t1.a + Sort Key: t1.a + -> Seq Scan on pg_temp.t_append t1 + Output: t1.a + -> Sort + Output: t2.a, t2.b + Sort Key: t2.b + -> Seq Scan on pg_temp.t_append t2 + Output: t2.a, t2.b + -> Append + -> Result + Output: t1.a + One-Time Filter: (t1.a IS NOT NULL) + -> Result + Output: t2.a + One-Time Filter: (t2.a IS NOT NULL) +(22 rows) + +select t1.a, s.a from t_append t1 + left join t_append t2 on t1.a = t2.b + join lateral ( + select t1.a as a union all select t2.a as a + ) s on true +where s.a is not null; + a | a +---+--- + 1 | 1 + 1 | 1 + 2 | 2 +(3 rows) + +rollback; -- -- test inlining of immutable functions -- diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index d6f646a1d50..dc4c5a9628f 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -1329,6 +1329,30 @@ select * from int4_tbl left join ( ) ss(x) on true where ss.x is null; +-- Test computation of varnullingrels when translating appendrel Var +begin; + +create temp table t_append (a int not null, b int); +insert into t_append values (1, 1); +insert into t_append values (2, 3); + +explain (verbose, costs off) +select t1.a, s.a from t_append t1 + left join t_append t2 on t1.a = t2.b + join lateral ( + select t1.a as a union all select t2.a as a + ) s on true +where s.a is not null; + +select t1.a, s.a from t_append t1 + left join t_append t2 on t1.a = t2.b + join lateral ( + select t1.a as a union all select t2.a as a + ) s on true +where s.a is not null; + +rollback; + -- -- test inlining of immutable functions --