agora inbox for pgsql-bugs@postgresql.org  
help / color / mirror / Atom feed
From: Álvaro Herrera <alvherre@kurilemu.de>
Subject: [PATCH] Fail REPACK in presence of !isready !indvalid indexes
Date: Thu, 10 Sep 2026 10:49:48 +0200

Like VACUUM FULL, non-concurrent REPACK would try to rebuild such
indexes, which can sometimes succeed.  Concurrent REPACK would however
fail.  The inconsistency is not good, so make them both throw an error
quickly to force the user to make a decision on those indexes (most
likely, drop them).

Reported-by: Zsolt Parragi <zsolt.parragi@percona.com>
Suggested-by: Kyotaro Horiguchi <horikyota.ntt@gmail.com>
Discussion: https://postgr.es/m/CAN4CZFO5A3YE0Dd-bn7eKrB20pECO3=U0wKg1z2rO=DxgWJJHQ@mail.gmail.com
---
 contrib/test_decoding/expected/repack.out | 16 ++++++
 contrib/test_decoding/sql/repack.sql      |  8 +++
 src/backend/commands/repack.c             | 69 +++++++++++++++++++++++
 3 files changed, 93 insertions(+)

diff --git a/contrib/test_decoding/expected/repack.out b/contrib/test_decoding/expected/repack.out
index ac5473137d1..3d6392def8a 100644
--- a/contrib/test_decoding/expected/repack.out
+++ b/contrib/test_decoding/expected/repack.out
@@ -51,6 +51,22 @@ SELECT * FROM rpk_missing;
 (3 rows)
 
 DROP TABLE rpk_missing;
+-- Verify handling of !valid !isready indexes
+CREATE TABLE repack_conc_invidx (i int PRIMARY KEY, j int);
+INSERT INTO repack_conc_invidx VALUES (1, 0), (2, 0);
+CREATE UNIQUE INDEX CONCURRENTLY repack_conc_invidx_uq ON repack_conc_invidx (j);
+ERROR:  could not create unique index "repack_conc_invidx_uq"
+DETAIL:  Key (j)=(0) is duplicated.
+CREATE INDEX CONCURRENTLY repack_conc_invalid_expr ON repack_conc_invidx ((1/j));
+ERROR:  division by zero
+REPACK 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.
+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.
 -- 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 e995c72d28d..9960a3de16c 100644
--- a/contrib/test_decoding/sql/repack.sql
+++ b/contrib/test_decoding/sql/repack.sql
@@ -33,6 +33,14 @@ REPACK (CONCURRENTLY) rpk_missing;
 SELECT * FROM rpk_missing;
 DROP TABLE rpk_missing;
 
+-- Verify handling of !valid !isready indexes
+CREATE TABLE repack_conc_invidx (i int PRIMARY KEY, j int);
+INSERT INTO repack_conc_invidx VALUES (1, 0), (2, 0);
+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;
+
 -- Error cases for concurrent mode
 
 -- Doesn't like partitioned tables
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 2924d884b10..c3075bae2a6 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -159,6 +159,7 @@ static bool cluster_rel_recheck(RepackCommand cmd, Relation OldHeap,
 								int options);
 static void check_concurrent_repack_requirements(Relation rel,
 												 Oid *ident_idx_p);
+static void check_repack_index_requirements(Relation rel);
 static void rebuild_relation(Relation OldHeap, Relation index, bool verbose,
 							 Oid ident_idx);
 static void copy_table_data(Relation NewHeap, Relation OldHeap, Relation OldIndex,
@@ -527,6 +528,14 @@ cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid,
 	if (concurrent)
 		check_concurrent_repack_requirements(OldHeap, &ident_idx);
 
+	/*
+	 * Also check the state of indexes; this can abort the command for REPACK.
+	 * Historically this hasn't affected CLUSTER or VACUUM FULL, so don't do
+	 * it for those commands.
+	 */
+	if (cmd == REPACK_COMMAND_REPACK)
+		check_repack_index_requirements(OldHeap);
+
 	/* Check for user-requested abort. */
 	CHECK_FOR_INTERRUPTS();
 
@@ -874,6 +883,66 @@ mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
 	table_close(pg_index, RowExclusiveLock);
 }
 
+/*
+ * Verify index state on the table being processed and throw an error if any
+ * indexes are found that are neither valid nor ready for inserts.
+ *
+ * Indexes that are neither valid nor ready for inserts, such as ones left
+ * behind by failed CREATE INDEX CONCURRENTLY, are not maintained by DML,
+ * and if they are constraint indexes, they may fail to build altogether.
+ * Throwing an error here forces the user to fix these indexes separately
+ * from REPACK.
+ */
+static void
+check_repack_index_requirements(Relation rel)
+{
+	Relation	indrel;
+	SysScanDesc indscan;
+	ScanKeyData skey;
+	HeapTuple	htup;
+	int			num_invalid_idxs = 0;
+	StringInfoData dest;
+
+	initStringInfo(&dest);
+
+	/* Prepare to scan pg_index for entries having indrelid = this rel. */
+	ScanKeyInit(&skey,
+				Anum_pg_index_indrelid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(RelationGetRelid(rel)));
+
+	indrel = table_open(IndexRelationId, AccessShareLock);
+	indscan = systable_beginscan(indrel, IndexIndrelidIndexId, true,
+								 NULL, 1, &skey);
+
+	while (HeapTupleIsValid(htup = systable_getnext(indscan)))
+	{
+		Form_pg_index index = (Form_pg_index) GETSTRUCT(htup);
+
+		if (!index->indisvalid && !index->indisready)
+		{
+			if (num_invalid_idxs == 0)
+				appendStringInfo(&dest, _("\"%s\""), get_rel_name(index->indexrelid));
+			else
+				appendStringInfo(&dest, _(", \"%s\""), get_rel_name(index->indexrelid));
+			num_invalid_idxs++;
+		}
+	}
+	systable_endscan(indscan);
+	table_close(indrel, AccessShareLock);
+
+	if (num_invalid_idxs > 0)
+		ereport(ERROR,
+				errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				errmsg("cannot execute %s on relation \"%s\"",
+					   "REPACK", RelationGetRelationName(rel)),
+				errdetail_plural("An invalid index cannot be processed correctly: %s.",
+								 "Some invalid indexes cannot be processed correctly: %s.",
+								 num_invalid_idxs,
+								 dest.data),
+				errhint("Use DROP INDEX or REINDEX."));
+}
+
 /*
  * Check if the CONCURRENTLY option is legal for the relation.
  *
-- 
2.47.3


--ih3nxo6vxmekktdf--





view thread (10+ messages)  latest in thread

Message-ID: <no-message-id-113193@localhost>
Permalink:  ../no-message-id-113193@localhost/
Also on:    postgresql.org/message-id/no-message-id-113193@localhost

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: pgsql-bugs@postgresql.org
  Cc: alvherre@kurilemu.de
  Subject: Re: [PATCH] Fail REPACK in presence of !isready !indvalid indexes
  In-Reply-To: <no-message-id-113193@localhost>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox