create extension pg_surgery; -- create a normal heap table and insert some rows. -- use a temp table so that vacuum behavior doesn't depend on global xmin create temp table htab (a int); insert into htab values (100), (200), (300), (400), (500); -- test empty TID array select heap_force_freeze('htab'::regclass, ARRAY[]::tid[]); -- nothing should be frozen yet select * from htab where xmin = 2; -- freeze forcibly select heap_force_freeze('htab'::regclass, ARRAY['(0, 4)']::tid[]); -- now we should have one frozen tuple select ctid, xmax from htab where xmin = 2; -- kill forcibly select heap_force_kill('htab'::regclass, ARRAY['(0, 4)']::tid[]); -- should be gone now select * from htab where ctid = '(0, 4)'; -- should now be skipped because it's already dead select heap_force_kill('htab'::regclass, ARRAY['(0, 4)']::tid[]); select heap_force_freeze('htab'::regclass, ARRAY['(0, 4)']::tid[]); -- freeze two TIDs at once while skipping an out-of-range block number select heap_force_freeze('htab'::regclass, ARRAY['(0, 1)', '(0, 3)', '(1, 1)']::tid[]); -- we should now have two frozen tuples select ctid, xmax from htab where xmin = 2; -- out-of-range TIDs should be skipped select heap_force_freeze('htab'::regclass, ARRAY['(0, 0)', '(0, 6)']::tid[]); -- set up a new table with a redirected line pointer -- use a temp table so that vacuum behavior doesn't depend on global xmin create temp table htab2(a int); insert into htab2 values (100); update htab2 set a = 200; vacuum htab2; -- redirected TIDs should be skipped select heap_force_kill('htab2'::regclass, ARRAY['(0, 1)']::tid[]); -- now create an unused line pointer select ctid from htab2; update htab2 set a = 300; select ctid from htab2; vacuum freeze htab2; -- unused TIDs should be skipped select heap_force_kill('htab2'::regclass, ARRAY['(0, 2)']::tid[]); -- multidimensional TID array should be rejected select heap_force_kill('htab2'::regclass, ARRAY[['(0, 2)']]::tid[]); -- TID array with nulls should be rejected select heap_force_kill('htab2'::regclass, ARRAY[NULL]::tid[]); -- but we should be able to kill the one tuple we have select heap_force_kill('htab2'::regclass, ARRAY['(0, 3)']::tid[]); -- materialized view. -- note that we don't commit the transaction, so autovacuum can't interfere. begin; create materialized view mvw as select a from generate_series(1, 3) a; select * from mvw where xmin = 2; select heap_force_freeze('mvw'::regclass, ARRAY['(0, 3)']::tid[]); select * from mvw where xmin = 2; select heap_force_kill('mvw'::regclass, ARRAY['(0, 3)']::tid[]); select * from mvw where ctid = '(0, 3)'; rollback; -- check that it fails on an unsupported relkind create view vw as select 1; select heap_force_kill('vw'::regclass, ARRAY['(0, 1)']::tid[]); select heap_force_freeze('vw'::regclass, ARRAY['(0, 1)']::tid[]); -- cleanup. drop view vw; drop extension pg_surgery;