-- The strings of an index's options (point_tid_columns, order_array_columns, ...) live in its relcache entry, which a lock taken after they were read reloads -- and frees: the planner's check for a point lookup read them after its catalog lookups, and the choices of a build read them after the leaf statistics. -- sift.test_col_rel_inval overwrites the options and the cached state and reloads the entry at those points, so a caller that kept them reads garbage. CREATE EXTENSION pg_sift; CREATE EXTENSION pg_sift_test; SET client_min_messages = warning; SET max_parallel_workers_per_gather = 0; SET max_parallel_maintenance_workers = 0; SET sift.async_maintenance = off; CREATE TABLE ro (id int, k int, g int, v numeric(10, 2)) WITH (autovacuum_enabled = off); INSERT INTO ro SELECT i, (i * 7919) % 9000, i % 7, (i % 1000) / 10.0 FROM generate_series(1, 30000) i; CREATE INDEX ro_c ON ro USING sift_col (k, g, id, v) WITH (point_tid_columns = 'k'); -- no directory of heap addresses: the order array is the first the choices read statistics for CREATE INDEX ro_o ON ro USING sift_col (k, g, id, v) WITH (point_tid_columns = 'off', order_columns = 'g', order_array_columns = 'g'); VACUUM (FREEZE, ANALYZE) ro; CREATE FUNCTION plan_of(q text) RETURNS text LANGUAGE plpgsql AS $$ DECLARE l text; r text := 'normal'; BEGIN FOR l IN EXECUTE 'EXPLAIN (COSTS OFF) ' || q LOOP IF l LIKE '%Custom Scan (SiftAgg)%' THEN r := 'SiftAgg'; END IF; END LOOP; RETURN r; END $$; -- an equality and an IN list on the point-lookup column keep the normal plan, with the option strings read in place or after a reload SELECT plan_of('SELECT count(*), sum(v) FROM ro WHERE k = 7919') AS equality, plan_of('SELECT count(*), sum(v) FROM ro WHERE k IN (1, 2, 3)') AS in_list; SET sift.test_col_rel_inval = on; SELECT plan_of('SELECT count(*), sum(v) FROM ro WHERE k = 7919') AS equality, plan_of('SELECT count(*), sum(v) FROM ro WHERE k IN (1, 2, 3)') AS in_list; RESET sift.test_col_rel_inval; -- the choices the build says, as a set: the same with the entry reloaded in the middle CREATE TABLE ch_off AS SELECT choice, value FROM sift.index_choices('ro_o'); SET sift.test_col_rel_inval = on; CREATE TABLE ch_on AS SELECT choice, value FROM sift.index_choices('ro_o'); RESET sift.test_col_rel_inval; SELECT (SELECT count(*) FROM ch_off) > 0 AS says_something, (SELECT count(*) FROM ((TABLE ch_off EXCEPT TABLE ch_on) UNION ALL (TABLE ch_on EXCEPT TABLE ch_off)) d) AS differences; DROP TABLE ch_off, ch_on, ro; DROP FUNCTION plan_of(text); DROP EXTENSION pg_sift_test; DROP EXTENSION pg_sift;