From 98134b1edf9ac81868969c88ff3f76c443cd0ae5 Mon Sep 17 00:00:00 2001 From: Bharath Rupireddy Date: Tue, 1 Sep 2026 03:29:27 +0000 Subject: [PATCH v3] Restrict concurrent repack to the heap access method. Concurrent repack (28d534e2ae0) replays the data changes made during the table rewrite by decoding them from WAL, which only works if the table's access method supports logical decoding. check_concurrent_repack_requirements() didn't check for that. For an access method that doesn't support it, the concurrent changes are never decoded and are silently lost from the rewritten table. Fix by erroring out for tables that use a non-heap access method, and document the limitation. Recognizing logical-decoding support properly would need a new table AM callback, which is more than this open item warrants, so restrict it to heap for now. Backpatch to v19, where concurrent repack was introduced. Reported-by: Nathan Bossart Author: Bharath Rupireddy Reviewed-by: Nathan Bossart Reviewed-by: Masahiko Sawada Discussion: https://postgr.es/m/apBkixO4xAGFoiT3%40nathan Backpatch-through: 19 --- contrib/test_decoding/expected/repack.out | 8 ++++++++ contrib/test_decoding/sql/repack.sql | 7 +++++++ doc/src/sgml/ref/repack.sgml | 6 ++++++ src/backend/commands/repack.c | 14 ++++++++++++++ 4 files changed, 35 insertions(+) diff --git a/contrib/test_decoding/expected/repack.out b/contrib/test_decoding/expected/repack.out index 5ddc63238c5..7872d567070 100644 --- a/contrib/test_decoding/expected/repack.out +++ b/contrib/test_decoding/expected/repack.out @@ -57,6 +57,14 @@ CREATE TABLE clstrpart (a int) PARTITION BY RANGE (a); REPACK (CONCURRENTLY) clstrpart; ERROR: REPACK (CONCURRENTLY) is not supported for partitioned tables HINT: Consider running the command on individual partitions. +-- Only supported for the heap access method +CREATE ACCESS METHOD heap2 TYPE TABLE HANDLER heap_tableam_handler; +CREATE TABLE repack_conc_heap2 (i int) USING heap2; +REPACK (CONCURRENTLY) repack_conc_heap2; +ERROR: cannot execute REPACK (CONCURRENTLY) on relation "repack_conc_heap2" +HINT: REPACK (CONCURRENTLY) is only supported for the "heap" access method. +DROP TABLE repack_conc_heap2; +DROP ACCESS METHOD heap2; -- Disallowed in catalogs REPACK (CONCURRENTLY) pg_class; ERROR: cannot execute REPACK (CONCURRENTLY) on relation "pg_class" diff --git a/contrib/test_decoding/sql/repack.sql b/contrib/test_decoding/sql/repack.sql index f461f5479f4..ce8ed6c3b26 100644 --- a/contrib/test_decoding/sql/repack.sql +++ b/contrib/test_decoding/sql/repack.sql @@ -39,6 +39,13 @@ DROP TABLE rpk_missing; CREATE TABLE clstrpart (a int) PARTITION BY RANGE (a); REPACK (CONCURRENTLY) clstrpart; +-- Only supported for the heap access method +CREATE ACCESS METHOD heap2 TYPE TABLE HANDLER heap_tableam_handler; +CREATE TABLE repack_conc_heap2 (i int) USING heap2; +REPACK (CONCURRENTLY) repack_conc_heap2; +DROP TABLE repack_conc_heap2; +DROP ACCESS METHOD heap2; + -- Disallowed in catalogs REPACK (CONCURRENTLY) pg_class; diff --git a/doc/src/sgml/ref/repack.sgml b/doc/src/sgml/ref/repack.sgml index 0cb72b6b289..0496cd24af1 100644 --- a/doc/src/sgml/ref/repack.sgml +++ b/doc/src/sgml/ref/repack.sgml @@ -285,6 +285,12 @@ REPACK [ ( option [, ...] ) ] USING + + + The table uses an access method other than heap. + + + REPACK is executed inside a transaction block. diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 477c86b2ba6..05c0662a510 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -877,6 +877,20 @@ check_concurrent_repack_requirements(Relation rel, Oid *ident_idx_p) errdetail("%s requires \"wal_level\" to be set to \"replica\" or higher.", "REPACK (CONCURRENTLY)")); + /* + * A table AM that doesn't support logical decoding would cause concurrent + * repack to silently lose the changes made during the rewrite. Detecting + * such support cleanly would need a new table AM callback, so for now + * just restrict the command to heap. + */ + if (rel->rd_rel->relam != HEAP_TABLE_AM_OID) + ereport(ERROR, + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot execute %s on relation \"%s\"", + "REPACK (CONCURRENTLY)", RelationGetRelationName(rel)), + errhint("%s is only supported for the \"heap\" access method.", + "REPACK (CONCURRENTLY)")); + /* Data changes in system relations are not logically decoded. */ if (IsCatalogRelation(rel)) ereport(ERROR, -- 2.47.3