Hi Hackers, VACUUM FULL silently turns columns added via ALTER TABLE ... ADD COLUMN ... DEFAULT <const> into NULL on all pre-existing rows. The issue exists for other operations like CLUSTER, REPACK.
Repro: CREATE TABLE t (id int PRIMARY KEY); INSERT INTO t SELECT generate_series(1,3); ALTER TABLE t ADD COLUMN x int DEFAULT 42; SELECT * FROM t; -- (1,42),(2,42),(3,42) VACUUM FULL t; SELECT * FROM t; -- (1,NULL),(2,NULL),(3,NULL) If the column is NOT NULL, the value becomes the type's zero value instead of NULL, silently bypassing both NOT NULL and any CHECK constraint declared on it. Root Cause: fast path in reform_tuple() in heapam_handler.c returns a copy of the source tuple when no dropped columns need fixing up. The check doesn't account for short tuples (HeapTupleHeaderGetNatts(t) < relnatts) that rely on attmissingval to materialize the default. After the rewrite, finish_heap_swap() calls RelationClearMissing(), clearing the only source of those values, and the short tuples then read as NULL. Fix: force reform when the source tuple is shorter than the new tuple descriptor. Patch attached. Added a regression test in fast_default.sql covering VACUUM FULL, CLUSTER, and REPACK on a table with fast-default columns including a NOT NULL CHECK column. Thanks, Satya
0001-VACUUM-FULL-silently-NULL-out-fast-default-columns.patch
Description: Binary data
