-- VACUUM of a posting whose repacked sparsemap would exceed its original -- inline slot must NOT error -- it migrates the posting out-of-line. -- -- Removing a TID from the interior of an RLE run can split it, so a -- sparsemap with fewer members can serialize larger than the original. -- Before 2.0.4 this ERROR'd ("inline vacuum repack overflow") and aborted -- VACUUM; 2.0.4 kept the original blob (no removal); 2.1.0 MIGRATES the -- survivors to a dedicated posting leaf so the dead TIDs are actually -- reclaimed (self-healing). Builds are payload-free as of 3.0.0 (no -- inline slack), so we build, DELETE + VACUUM twice, and assert the -- index keeps agreeing with a sequential scan. Single-token robust style. SET client_min_messages = warning; SET CREATE EXTENSION IF NOT EXISTS pg_tre; CREATE EXTENSION DROP TABLE IF EXISTS vr_t; DROP TABLE CREATE TABLE vr_t (id serial PRIMARY KEY, body text); CREATE TABLE INSERT INTO vr_t(body) SELECT 'noise' || g || (CASE WHEN (g * 2654435761) % 3 = 0 THEN ' midtoken' ELSE '' END) FROM generate_series(1, 5000) g; INSERT 0 5000 SET pg_tre.coalesce_enable = on; SET CREATE INDEX vr_idx ON vr_t USING tre (body); CREATE INDEX SET pg_tre.coalesce_enable = off; SET -- delete a scattered chunk + VACUUM (the path that used to overflow), -- then confirm index == seq-scan. DELETE FROM vr_t WHERE id % 5 = 0; DELETE 1000 VACUUM vr_t; VACUUM SELECT CASE WHEN (SELECT count(*) FROM vr_t WHERE body %~~ tre_pattern('midtoken', 0)) = (SELECT count(*) FROM vr_t WHERE body LIKE '%midtoken%') THEN 'vacuum1_ok' ELSE 'vacuum1_bad' END AS r1; r1 ------------ vacuum1_ok (1 row) -- a second, heavier churn + VACUUM. DELETE FROM vr_t WHERE id % 3 = 0; DELETE 1333 VACUUM vr_t; VACUUM SELECT CASE WHEN (SELECT count(*) FROM vr_t WHERE body %~~ tre_pattern('midtoken', 0)) = (SELECT count(*) FROM vr_t WHERE body LIKE '%midtoken%') THEN 'vacuum2_ok' ELSE 'vacuum2_bad' END AS r2; r2 ------------ vacuum2_ok (1 row) DROP TABLE vr_t; DROP TABLE