Thread

  1. [Bug]Vacuum full silently NULL out fast default columns

    SATYANARAYANA NARLAPURAM <satyanarlapuram@gmail.com> — 2026-05-04T09:17:08Z

    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