| From: | shihao zhong <zhong950419(at)gmail(dot)com> |
|---|---|
| To: | pgsql-hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org> |
| Cc: | David Rowley <dgrowleyml(at)gmail(dot)com>, Guofenglinux <guofenglinux(at)gmail(dot)com>, Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us> |
| Subject: | [PG19] NOT NULL constraint is trusted by snapshots older than it |
| Date: | 2026-10-29 02:03:10 |
| Message-ID: | CAGRkXqTKYDxETk+5-Jtd_aHSiPRBHiHbq_r9sz3GuG8-k-L5UA@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Hi hackers,
PG19 can return wrong results from a plain SELECT after another session
adds a NOT NULL constraint.
CREATE TABLE t (id int PRIMARY KEY, a int, b int, c text);
INSERT INTO t SELECT g, CASE WHEN g <= 5 THEN NULL ELSE g * 10 END,
g + 100, 'c' || g FROM generate_series(1, 10) g;
ALTER TABLE t ADD CONSTRAINT t_a_nn NOT NULL a NOT VALID;
-- session A
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT count(*) FROM t WHERE a IS NULL; -- 5
-- session B, does not block on A
UPDATE t SET a = 0 WHERE a IS NULL;
ALTER TABLE t VALIDATE CONSTRAINT t_a_nn;
-- session A, same transaction
SELECT count(*) FROM t WHERE a IS NULL; -- 0
SELECT id, a, b FROM t WHERE id <= 5; -- a shows 101..105
SELECT * FROM t WHERE id <= 5; -- invalid memory alloc
A still sees the five rows with NULLs, but they come back wrong. With
JIT on, column a shows the value of b as well. SET NOT NULL instead of
VALIDATE does the same.
The planner [1], the tuple deform fast path [2] and JIT deforming
trust the constraint as soon as it is committed. They do that even for
a snapshot that still sees the rows with NULLs. Bug 19783 [3] comes
from the same commit but has a different cause.
0001 stores the xmin of the newest constraint in the relcache.
The planner uses the constraints only when the active
snapshot sees that xmin as committed, else the plan is marked
transient like with indcheckxmin. The scan slot gets the not-null
flag only when the executor snapshot sees it. This is based on
existing indcheckxmin design.
0002 adds an isolation test with the VALIDATE and the SET NOT NULL
case.
0003 makes JIT deforming skip null checks only when the scan slot has
that flag. Inner and outer slots of a join never get it.
JIT has trusted attnotnull since 11, so 18 and older return wrong
values with JIT on too....
[1] https://git.postgresql.org/pg/commitdiff/e2debb64380
[2] https://git.postgresql.org/pg/commitdiff/c456e391138
[3]
https://www.postgresql.org/message-id/19783-070e4bf0c73c1076@postgresql.org
Thanks,
Shihao
| Attachment | Content-Type | Size |
|---|---|---|
| v1-0002-Add-an-isolation-test-for-not-null-constraints-se.patch | application/octet-stream | 4.9 KB |
| v1-0001-Do-not-trust-not-null-constraints-newer-than-the-.patch | application/octet-stream | 13.9 KB |
| v1-0003-Let-JIT-deforming-trust-not-null-constraints-only.patch | application/octet-stream | 7.1 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | shihao zhong | 2026-10-29 02:11:20 | Re: [PG19] pg_upgrade --check passes, then the upgrade fails |
| Previous Message | shihao zhong | 2026-10-29 01:55:34 | Re: [PG19] Wrong results from Memoize with a nondeterministic collation |