Re: Wrong result for whole-row IS NOT NULL with dropped columns

From: "Matheus Alcantara" <matheusssilv97(at)gmail(dot)com>
To: "Joao Rito" <joaovictor(dot)rito(at)gmail(dot)com>, "Sho Ito" <i(dot)sho(dot)0628(at)gmail(dot)com>
Cc: <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: Re: Wrong result for whole-row IS NOT NULL with dropped columns
Date: 2026-11-11 14:54:36
Message-ID: DMSGUXT2T9MW.2Z13MYYFO2E08@gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Thanks Joao and Sho Ito for testing the reviewing the patch!

On Tue Nov 10, 2026 at 10:01 AM -03, Joao Rito wrote:
> 4. A stored view. This one is a little different:
>
> CREATE TABLE src (x int, y int, z int);
> INSERT INTO src VALUES (1, NULL, 3), (1, 2, 3);
> CREATE TYPE ct3 AS (x int, y int, z int);
> CREATE VIEW v AS SELECT ROW(x, y, z)::ct3 IS NOT NULL AS nn FROM src;
> CREATE VIEW v2 AS SELECT ROW(x, y, z)::ct3 AS r FROM src;
> ALTER TYPE ct3 DROP ATTRIBUTE y;
> SELECT nn FROM v; -- f, t
> SELECT r IS NOT NULL FROM (SELECT r FROM v2 OFFSET 0) s; -- t, t
>
> Here the stored RowExpr still has the old Var src.y for the dropped
> attribute, not a null Const, so the planner and the executor disagree for
> the first row. With v1 both give t, t, and EXPLAIN shows "((src.x IS NOT
> NULL) AND (src.z IS NOT NULL))". So checking attisdropped in the tuple
> descriptor, as v1 does, is the right approach; looking for a null Const
> would not cover this case.
>

Nice catch, I was not aware of this case. I think that it's worth to
have a test for it too, since it makes sure that we don't move to a
approach that only looks for the null Const in the future, so I've
added it on rowtypes.sql.

> - The "i < tupdesc->natts" check looks defensive only, since a RowExpr of a
> named type has one argument per attribute, dropped ones included. Fine to
> keep.

Yeah, I've kept it just to be safe.

> - Tests: the two new tests cover the generated column and the cast. Maybe
> add the WHERE case from item 1 (the plan with One-Time Filter: false is
> easy to check with EXPLAIN), and the ALTER TYPE ... DROP ATTRIBUTE case.
>

Done in the attached v2. The WHERE case is on generated_virtual.sql and
the ALTER TYPE ... DROP ATTRIBUTE case is on rowtypes.sql.

> Backpatch
> - v1 does not apply to REL_18_STABLE or REL_17_STABLE. The conflict is in
> the third clauses.c hunk, which touches code that exists only on master.
> - I think the same fix works on older branches, since the ROW()::type path
> from item 2 is not related to virtual generated columns. On 17,
> TupleDescAttr() has to be used instead of TupleDescCompactAttr(), and the
> generated_virtual test applies only to 18 and later. I prepared that port
> for REL_17_STABLE but could not finish building and testing it, so I
> cannot confirm yet that it works there.
>

I agree. I'm also attaching patches for each back branch down to 14.
Some notes about the differences:

On REL_18 the fix is the same but without the expr_is_nonnullable()
check, which only exists on master.

On REL_17 and older there are no virtual generated columns, so only the
rowtypes.sql tests are included, and TupleDescAttr() is used instead of
TupleDescCompactAttr(), as you mentioned.

On REL_14 the rowtypes.sql tests use regular tables instead of temp
tables, because EXPLAIN VERBOSE on 14 shows the real temp schema name
instead of pg_temp, which make the expected output unstable.

--
Matheus Alcantara
EDB: https://www.enterprisedb.com

Attachment Content-Type Size
v2-0001-Ignore-dropped-columns-when-simplifying-ROW-.-IS-.patch text/plain 11.7 KB
v2-REL_18-0001-Ignore-dropped-columns-when-simplifying-ROW-.-IS-.patch.nocfbot text/plain 11.4 KB
v2-REL_17-0001-Ignore-dropped-columns-when-simplifying-ROW-.-IS-.patch.nocfbot text/plain 7.6 KB
v2-REL_16-0001-Ignore-dropped-columns-when-simplifying-ROW-.-IS-.patch.nocfbot text/plain 7.6 KB
v2-REL_15-0001-Ignore-dropped-columns-when-simplifying-ROW-.-IS-.patch.nocfbot text/plain 7.6 KB
v2-REL_14-0001-Ignore-dropped-columns-when-simplifying-ROW-.-IS-.patch.nocfbot text/plain 7.7 KB

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Zhijie Hou 2026-11-11 15:13:13 Re: Assertion failure in update_local_synced_slot(), slotsync.c:364
Previous Message Baji Shaik 2026-11-11 14:47:10 Autovacuum freeze scores wrap around for just-vacuumed tables