diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 3514028f427..abba5dfd0e7 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -3961,8 +3961,12 @@ l2: */ if (need_toast) { - /* Note we always use WAL and FSM during updates */ - heaptup = heap_toast_insert_or_update(relation, newtup, &oldtup, 0); + /* + * If logical decoding is not needed, suppress it for the TOAST + * tuples too. We never skip the FSM here. + */ + heaptup = heap_toast_insert_or_update(relation, newtup, &oldtup, + walLogical ? 0 : HEAP_INSERT_NO_LOGICAL); newtupsize = MAXALIGN(heaptup->t_len); } else diff --git a/src/test/modules/injection_points/Makefile b/src/test/modules/injection_points/Makefile index a03dae5771c..408a35c3c21 100644 --- a/src/test/modules/injection_points/Makefile +++ b/src/test/modules/injection_points/Makefile @@ -9,6 +9,8 @@ EXTENSION = injection_points DATA = injection_points--1.0.sql PGFILEDESC = "injection_points - facility for injection points" +EXTRA_INSTALL = contrib/test_decoding + REGRESS = injection_points hashagg reindex_conc vacuum REGRESS_OPTS = --dlpath=$(top_builddir)/src/test/regress @@ -16,6 +18,7 @@ ISOLATION = basic \ inplace \ reindex_concurrently_deferred \ repack \ + repack_decode \ repack_temporal \ repack_temporal_multirange \ repack_toast \ diff --git a/src/test/modules/injection_points/expected/repack_decode.out b/src/test/modules/injection_points/expected/repack_decode.out new file mode 100644 index 00000000000..0de24241bfb --- /dev/null +++ b/src/test/modules/injection_points/expected/repack_decode.out @@ -0,0 +1,36 @@ +Parsed test spec with 2 sessions + +starting permutation: s1_wait_before_lock s2_changes s2_wakeup_before_lock s1_decode +injection_points_attach +----------------------- + +(1 row) + +step s1_wait_before_lock: + REPACK (CONCURRENTLY) repack_toast; + +step s2_changes: + UPDATE repack_toast SET t = gen_external() WHERE i=1; + +step s2_wakeup_before_lock: + SELECT injection_points_wakeup('repack-concurrently-before-lock'); + +injection_points_wakeup +----------------------- + +(1 row) + +step s1_wait_before_lock: <... completed> +step s1_decode: + SELECT count(*) FROM pg_logical_slot_peek_changes('s', NULL, NULL, 'include-rewrites', '1'); + +count +----- + 9 +(1 row) + +pg_drop_replication_slot +------------------------ + +(1 row) + diff --git a/src/test/modules/injection_points/meson.build b/src/test/modules/injection_points/meson.build index 3365a28d11c..a7b40e084f6 100644 --- a/src/test/modules/injection_points/meson.build +++ b/src/test/modules/injection_points/meson.build @@ -47,6 +47,7 @@ tests += { 'inplace', 'reindex_concurrently_deferred', 'repack', + 'repack_decode', 'repack_temporal', 'repack_temporal_multirange', 'repack_toast', diff --git a/src/test/modules/injection_points/specs/repack_decode.spec b/src/test/modules/injection_points/specs/repack_decode.spec new file mode 100644 index 00000000000..89b4cbcc77e --- /dev/null +++ b/src/test/modules/injection_points/specs/repack_decode.spec @@ -0,0 +1,60 @@ +setup +{ + CREATE EXTENSION injection_points; + + BEGIN; + -- Generate a string of random characters that is not likely to be + -- compressed, but is big enough to be stored externally. + CREATE FUNCTION gen_external() + RETURNS text + LANGUAGE sql as $$ + SELECT string_agg(chr(65 + trunc(25 * random())::int), '') + FROM generate_series(1, 2048) s(x); + $$; + COMMIT; + + SELECT pg_create_logical_replication_slot('s', 'test_decoding'); + + CREATE TABLE repack_toast(i int PRIMARY KEY, t text); + INSERT INTO repack_toast(i, t) VALUES (1, gen_external()); +} + +teardown +{ + DROP TABLE repack_toast; + DROP EXTENSION injection_points; + DROP FUNCTION gen_external(); + SELECT pg_drop_replication_slot('s'); +} + +session s1 +setup +{ + SELECT injection_points_set_local(); + SELECT injection_points_attach('repack-concurrently-before-lock', 'wait'); +} +# Perform the initial load and wait for s2 to do some data changes. +step s1_wait_before_lock +{ + REPACK (CONCURRENTLY) repack_toast; +} +step s1_decode +{ + SELECT count(*) FROM pg_logical_slot_peek_changes('s', NULL, NULL, 'include-rewrites', '1'); +} + +session s2 +step s2_changes +{ + UPDATE repack_toast SET t = gen_external() WHERE i=1; +} +step s2_wakeup_before_lock +{ + SELECT injection_points_wakeup('repack-concurrently-before-lock'); +} + +permutation + s1_wait_before_lock + s2_changes + s2_wakeup_before_lock + s1_decode