Re: pgsql: Don't trust NOT NULL on virtual generated columns of partitioned

From: Manu <manuelreyesbravo(at)gmail(dot)com>
To: shihao zhong <zhong950419(at)gmail(dot)com>
Cc: Álvaro Herrera <alvherre(at)kurilemu(dot)de>, Richard Guo <guofenglinux(at)gmail(dot)com>, Richard Guo <rguo(at)postgresql(dot)org>, pgsql-hackers(at)lists(dot)postgresql(dot)org
Subject: Re: pgsql: Don't trust NOT NULL on virtual generated columns of partitioned
Date: 2026-10-26 17:40:53
Message-ID: 179303645383.2287648.3804356080420844661@gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-committers pgsql-hackers

Hi Shihao,

On Tue, Oct 13, 2026 at 6:00 PM Álvaro Herrera <alvherre(at)kurilemu(dot)de>
wrote:
> I suspect we don't really know what other
> things this misfeature can break.

One more: with constraint_exclusion = on (not the default), a query
on the parent returns wrong results, on 18.6 too:

create table p (a int,
v int generated always as (nullif(a, 0)) virtual not null)
partition by list (a);
create table p1 partition of p
(v generated always as (a + 1) virtual) for values in (0);
insert into p values (0);
set constraint_exclusion = on;
select count(*) from p where v is null; -- 0, should be 1

The plan is "One-Time Filter: false": the parent's NOT NULL refutes
the qual, but the row read through p has v = NULL. A CHECK (v > 0)
on the parent does the same: count(*) where v < 0 gives 0, not 1.
Both on 18.6, 19beta4, 19rc1 and master (dfd7ede529f), so
d8444163d22 does not change it; with the default setting all give 1.
repro-ce-on.sql.txt is attached.

On Wed, Oct 14, 2026 at 12:39 AM shihao zhong <zhong950419(at)gmail(dot)com>
wrote:
> With the patch, a virtual generated column of a partition must use the
> same expression as the parent. CREATE TABLE PARTITION OF and ATTACH
> PARTITION check this, and SET EXPRESSION can only change the whole
> partition tree. Stored columns and plain inheritance are unchanged.

I ran v1 on master and REL_19_STABLE, and the 18 version on
REL_18_STABLE, against 13 statements that create a divergent
expression: PARTITION OF (LIST, RANGE, HASH, DEFAULT), ATTACH, SET
EXPRESSION on a partition or on ONLY the parent, four multi-level
cases and two foreign-table partitions (paths.sql.txt). All 13 fail
on the three branches. The controls with the parent's expression,
STORED and INHERITS still work. Your query finds 7 mismatches
unpatched and 0 patched. make check passes on the three, and the
pg_upgrade TAP tests on master. Without the ATTACH check, 3 of the
13 get through, so the script catches that. On REL_19_STABLE v1
needs "git am -3" (check_for_pg_role_prefix is still in check.c).

> For existing databases, the attached query lists the partition columns
> that differ, and pg_upgrade on master and 19 runs the same check. Users
> can fix them with SET EXPRESSION on the parent, unless existing rows
> break NOT NULL under the parent's expression.

pg_upgrade --check from an 18.6 cluster to master and to 19 (both
patched) lists all four divergent columns I created. On patched 18
the existing ones keep the wrong result above until they are fixed.

One case where the fix fails on 18: SET EXPRESSION on a virtual
column errors out if the table has any CHECK constraint, even one
that does not involve the column, and also after the rows are fixed:

ERROR: ALTER TABLE / SET EXPRESSION is not supported for virtual
generated columns in tables with check constraints

f80bedd52b1 lifted this in 19; 18 still has it. So the pg_upgrade
hint fails for such tables in an 18 cluster. What worked on patched
18, in one transaction: drop the CHECK, SET EXPRESSION on the parent,
add the CHECK back. After that your query is empty and pg_upgrade
--check passes (legacy.sh.txt).

All of this is on one machine, cassert builds; 17 and older not run,
no check-world. Details are in measurements.txt.

Regards,
Manu

Attachment Content-Type Size
repro-ce-on.sql.txt text/plain 1.5 KB
paths.sql.txt text/plain 5.4 KB
legacy.sh.txt text/plain 6.9 KB
measurements.txt text/plain 5.3 KB

In response to

Browse pgsql-committers by date

  From Date Subject
Next Message Tom Lane 2026-10-26 18:03:48 pgsql: Fix PL/pgSQL's handling of anycompatiblemultirange.
Previous Message Tom Lane 2026-10-26 17:02:06 pgsql: Avoid waiting for a safe snapshot in notify-reading transactions

Browse pgsql-hackers by date

  From Date Subject
Next Message Taras Kloba 2026-10-26 17:53:56 doc: pg_restore --filter refers to nonexistent --indexes option
Previous Message Manu 2026-10-26 17:33:40 Re: UNDO with FILEOPS, and the FLUX table AM