From 1e1097db3bf1edd2e0399732a645d68baf1e3952 Mon Sep 17 00:00:00 2001 From: shihao zhong Date: Tue, 6 Oct 2026 22:05:23 -0400 Subject: [PATCH v2 2/2] Test dropped-column values in inserts replayed by REPACK Add repack_dropped_insert. A BEFORE INSERT trigger makes the new row a copy of an existing one, so it carries the value of the dropped column. The new heap has no TOAST table, so REPACK (CONCURRENTLY) fails if that value is kept. Discussion: https://postgr.es/m/CAJgoLkK2UBzB1J9buCsSUbjf7bOqz-o_0=CeiTruU789BhBw-Q@mail.gmail.com --- src/test/modules/injection_points/Makefile | 1 + .../expected/repack_dropped_insert.out | 46 +++++++++++++ src/test/modules/injection_points/meson.build | 1 + .../specs/repack_dropped_insert.spec | 65 +++++++++++++++++++ 4 files changed, 113 insertions(+) create mode 100644 src/test/modules/injection_points/expected/repack_dropped_insert.out create mode 100644 src/test/modules/injection_points/specs/repack_dropped_insert.spec diff --git a/src/test/modules/injection_points/Makefile b/src/test/modules/injection_points/Makefile index 2463d6e0fb9..9a9682b7faa 100644 --- a/src/test/modules/injection_points/Makefile +++ b/src/test/modules/injection_points/Makefile @@ -26,6 +26,7 @@ ISOLATION = basic \ repack_commit_race \ repack_decode \ repack_dropped \ + repack_dropped_insert \ repack_missingval \ repack_temporal \ repack_temporal_multirange \ diff --git a/src/test/modules/injection_points/expected/repack_dropped_insert.out b/src/test/modules/injection_points/expected/repack_dropped_insert.out new file mode 100644 index 00000000000..a2a1d3c1ec3 --- /dev/null +++ b/src/test/modules/injection_points/expected/repack_dropped_insert.out @@ -0,0 +1,46 @@ +Parsed test spec with 2 sessions + +starting permutation: s1_repack s2_insert s2_size s2_unlock s1_noop s2_size +injection_points_attach +----------------------- + +(1 row) + +step s1_repack: + REPACK (CONCURRENTLY) repack_dropped_insert; + +step s2_insert: + INSERT INTO repack_dropped_insert (id, n) VALUES (2, 2); + +step s2_size: + SELECT id, pg_column_size(repack_dropped_insert) < current_setting('block_size')::int + AS fits_in_one_block + FROM repack_dropped_insert ORDER BY id; + +id|fits_in_one_block +--+----------------- + 1|f + 2|f +(2 rows) + +step s2_unlock: + SELECT injection_points_wakeup('repack-concurrently-before-lock'); + +injection_points_wakeup +----------------------- + +(1 row) + +step s1_repack: <... completed> +step s1_noop: +step s2_size: + SELECT id, pg_column_size(repack_dropped_insert) < current_setting('block_size')::int + AS fits_in_one_block + FROM repack_dropped_insert ORDER BY id; + +id|fits_in_one_block +--+----------------- + 1|t + 2|t +(2 rows) + diff --git a/src/test/modules/injection_points/meson.build b/src/test/modules/injection_points/meson.build index b4a0079484b..9605e433430 100644 --- a/src/test/modules/injection_points/meson.build +++ b/src/test/modules/injection_points/meson.build @@ -34,6 +34,7 @@ injection_points_isolation = [ 'repack_commit_race', 'repack_decode', 'repack_dropped', + 'repack_dropped_insert', 'repack_missingval', 'repack_temporal', 'repack_temporal_multirange', diff --git a/src/test/modules/injection_points/specs/repack_dropped_insert.spec b/src/test/modules/injection_points/specs/repack_dropped_insert.spec new file mode 100644 index 00000000000..66e9c9d2d15 --- /dev/null +++ b/src/test/modules/injection_points/specs/repack_dropped_insert.spec @@ -0,0 +1,65 @@ +# Test REPACK (CONCURRENTLY) replaying an INSERT that carries the value of a +# dropped column. +# +# The trigger makes the new tuple a copy of tuple 1, dropped column included. +# The columns that are left are ints, so the new heap has no TOAST table to +# put that value in. +setup { + CREATE EXTENSION injection_points; + + CREATE TABLE repack_dropped_insert (id int PRIMARY KEY, n int, b text); + ALTER TABLE repack_dropped_insert ALTER COLUMN b SET STORAGE EXTERNAL; + INSERT INTO repack_dropped_insert (id, n, b) VALUES (1, 1, + repeat('x', 2 * current_setting('block_size')::int)); + CREATE FUNCTION repack_dropped_insert_f() RETURNS trigger LANGUAGE plpgsql AS + $$ DECLARE r repack_dropped_insert; + BEGIN + r := (SELECT t FROM repack_dropped_insert t WHERE id = 1); + r.id := NEW.id; + RETURN r; + END $$; + CREATE TRIGGER repack_dropped_insert_t BEFORE INSERT ON repack_dropped_insert FOR EACH ROW EXECUTE FUNCTION repack_dropped_insert_f(); + ALTER TABLE repack_dropped_insert DROP COLUMN b; +} + +teardown { + DROP TABLE repack_dropped_insert; + DROP FUNCTION repack_dropped_insert_f; + DROP EXTENSION injection_points; +} + +session s1 +setup +{ + SELECT injection_points_set_local(); + SELECT injection_points_attach('repack-concurrently-before-lock', 'wait'); +} +step s1_repack +{ + REPACK (CONCURRENTLY) repack_dropped_insert; +} +step s1_noop { } + +session s2 +step s2_insert +{ + INSERT INTO repack_dropped_insert (id, n) VALUES (2, 2); +} +step s2_size +{ + SELECT id, pg_column_size(repack_dropped_insert) < current_setting('block_size')::int + AS fits_in_one_block + FROM repack_dropped_insert ORDER BY id; +} +step s2_unlock +{ + SELECT injection_points_wakeup('repack-concurrently-before-lock'); +} + +permutation + s1_repack + s2_insert + s2_size + s2_unlock + s1_noop + s2_size -- 2.37.1 (Apple Git-137.1)