From 0ace3fd2f1b7ab35b3b5ffdf4f0aae7f3a53e9bd Mon Sep 17 00:00:00 2001 From: Shihao Zhong Date: Sun, 13 Sep 2026 22:04:14 -0400 Subject: [PATCH] Make REPACK reject invalid indexes, not only !indisready ones Commit 0c5d6269614 made REPACK fail on indexes that are not ready. An index left behind by CREATE INDEX CONCURRENTLY that failed during validation is ready but not valid, and the table may contain duplicates for a unique index. Non-concurrent REPACK rebuilds it without checking constraints and leaves it invalid, while REPACK (CONCURRENTLY) copies the whole table and then fails to build the unique index. Check indisvalid instead, so both fail quickly as intended. --- contrib/test_decoding/expected/repack.out | 15 +++++++++++++++ contrib/test_decoding/sql/repack.sql | 9 +++++++++ src/backend/commands/repack.c | 16 ++++++++-------- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/contrib/test_decoding/expected/repack.out b/contrib/test_decoding/expected/repack.out index e7060a7e577..2ad4755998c 100644 --- a/contrib/test_decoding/expected/repack.out +++ b/contrib/test_decoding/expected/repack.out @@ -67,6 +67,21 @@ REPACK (CONCURRENTLY) repack_conc_invidx; ERROR: cannot execute REPACK on relation "repack_conc_invidx" DETAIL: Some invalid indexes cannot be processed correctly: "repack_conc_invidx_uq", "repack_conc_invalid_expr". HINT: Use DROP INDEX or REINDEX. +DROP INDEX repack_conc_invidx_uq, repack_conc_invalid_expr; +-- An index that is ready but not valid, as left behind by CREATE INDEX +-- CONCURRENTLY failing during validation, must be rejected as well. +CREATE INDEX repack_conc_invidx_ready ON repack_conc_invidx (j); +UPDATE pg_index SET indisvalid = false + WHERE indexrelid = 'repack_conc_invidx_ready'::regclass; +REPACK repack_conc_invidx; +ERROR: cannot execute REPACK on relation "repack_conc_invidx" +DETAIL: An invalid index cannot be processed correctly: "repack_conc_invidx_ready". +HINT: Use DROP INDEX or REINDEX. +REPACK (CONCURRENTLY) repack_conc_invidx; +ERROR: cannot execute REPACK on relation "repack_conc_invidx" +DETAIL: An invalid index cannot be processed correctly: "repack_conc_invidx_ready". +HINT: Use DROP INDEX or REINDEX. +DROP TABLE repack_conc_invidx; -- Error cases for concurrent mode -- Doesn't like partitioned tables CREATE TABLE clstrpart (a int) PARTITION BY RANGE (a); diff --git a/contrib/test_decoding/sql/repack.sql b/contrib/test_decoding/sql/repack.sql index 0107e0bf89d..2462967bd59 100644 --- a/contrib/test_decoding/sql/repack.sql +++ b/contrib/test_decoding/sql/repack.sql @@ -40,6 +40,15 @@ CREATE UNIQUE INDEX CONCURRENTLY repack_conc_invidx_uq ON repack_conc_invidx (j) CREATE INDEX CONCURRENTLY repack_conc_invalid_expr ON repack_conc_invidx ((1/j)); REPACK repack_conc_invidx; REPACK (CONCURRENTLY) repack_conc_invidx; +DROP INDEX repack_conc_invidx_uq, repack_conc_invalid_expr; +-- An index that is ready but not valid, as left behind by CREATE INDEX +-- CONCURRENTLY failing during validation, must be rejected as well. +CREATE INDEX repack_conc_invidx_ready ON repack_conc_invidx (j); +UPDATE pg_index SET indisvalid = false + WHERE indexrelid = 'repack_conc_invidx_ready'::regclass; +REPACK repack_conc_invidx; +REPACK (CONCURRENTLY) repack_conc_invidx; +DROP TABLE repack_conc_invidx; -- Error cases for concurrent mode diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 558ba955dc3..d2a20bd95f2 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -886,13 +886,14 @@ mark_index_clustered(Relation rel, Oid indexOid, bool is_internal) /* * check_index_requirements: verify index state on relation being processed * - * Throw an error if any !indisready indexes are found. + * Throw an error if any !indisvalid indexes are found. * - * Indexes that are not ready for inserts, such as ones left behind by failed - * CREATE INDEX CONCURRENTLY, are not maintained by DML. In some cases they - * may fail to build altogether. Throwing an error here forces the user to - * take action on these indexes separately from the table reconstruction, - * which prevents perpetuating them for no reason. + * Invalid indexes, such as ones left behind by failed CREATE INDEX + * CONCURRENTLY, may fail to build altogether: a unique index that failed + * during validation is ready for inserts, but the table contains + * duplicates. Throwing an error here forces the user to take action on + * these indexes separately from the table reconstruction, which prevents + * perpetuating them for no reason. */ static void check_index_requirements(Relation rel, RepackCommand cmd) @@ -920,9 +921,8 @@ check_index_requirements(Relation rel, RepackCommand cmd) { Form_pg_index index = (Form_pg_index) GETSTRUCT(htup); - if (!index->indisready) + if (!index->indisvalid) { - Assert(!index->indisvalid); if (num_invalid_idxs == 0) appendStringInfo(&dest, _("\"%s\""), get_rel_name(index->indexrelid)); else -- 2.37.1 (Apple Git-137.1)