-- P2: TRUNCATE followed, in the same transaction, by work that is undone or -- by an ANALYZE, leaves wrong live/dead counts. Deterministic: no -- dependency on flush timing. Runs on 14 and later. \set ON_ERROR_STOP 1 \pset footer off SELECT current_setting('server_version_num')::int >= 150000 AS has_force_flush \gset \if :has_force_flush \set flush 'SELECT pg_stat_force_next_flush()' \else \set flush 'SELECT pg_sleep(1.1)' \endif \echo == P2a: BEGIN; TRUNCATE; SAVEPOINT; INSERT 100; ROLLBACK TO; COMMIT CREATE TABLE p2a (id int) WITH (autovacuum_enabled = off); BEGIN; TRUNCATE p2a; SAVEPOINT s; INSERT INTO p2a SELECT generate_series(1, 100); ROLLBACK TO s; COMMIT; :flush \g /dev/null SELECT n_live_tup, n_dead_tup FROM pg_stat_user_tables WHERE relname = 'p2a'; \echo expected n_dead_tup = 100 (the aborted rows are in the new relfilenode); bug: 0 \echo VACUUM VERBOSE shows the 100 dead rows it removes: VACUUM (VERBOSE) p2a; \echo == P2a control: same without the TRUNCATE CREATE TABLE p2a_ctl (id int) WITH (autovacuum_enabled = off); BEGIN; SAVEPOINT s; INSERT INTO p2a_ctl SELECT generate_series(1, 100); ROLLBACK TO s; COMMIT; :flush \g /dev/null SELECT n_live_tup, n_dead_tup FROM pg_stat_user_tables WHERE relname = 'p2a_ctl'; \echo control gives n_dead_tup = 100 \echo == P2b: 288 rows; BEGIN; SAVEPOINT; TRUNCATE; ANALYZE; ROLLBACK TO; COMMIT CREATE TABLE p2b (id int) WITH (autovacuum_enabled = off); INSERT INTO p2b SELECT generate_series(1, 288); ANALYZE p2b; :flush \g /dev/null BEGIN; SAVEPOINT s; TRUNCATE p2b; ANALYZE p2b; ROLLBACK TO s; COMMIT; :flush \g /dev/null SELECT s.n_live_tup, c.reltuples, (SELECT count(*) FROM p2b) AS count FROM pg_stat_user_tables s JOIN pg_class c ON c.oid = s.relid WHERE s.relname = 'p2b'; \echo expected n_live_tup = 288; bug: 0. pg_class.reltuples keeps the right value \echo == P2b, top-level variant: BEGIN; TRUNCATE; ANALYZE; ROLLBACK CREATE TABLE p2c (id int) WITH (autovacuum_enabled = off); INSERT INTO p2c SELECT generate_series(1, 288); ANALYZE p2c; :flush \g /dev/null BEGIN; TRUNCATE p2c; ANALYZE p2c; ROLLBACK; :flush \g /dev/null SELECT s.n_live_tup, c.reltuples, (SELECT count(*) FROM p2c) AS count FROM pg_stat_user_tables s JOIN pg_class c ON c.oid = s.relid WHERE s.relname = 'p2c'; DROP TABLE p2a, p2a_ctl, p2b, p2c;