From a7bdbea9fdbfe4d24e0e971ebfab20d854a2ca44 Mon Sep 17 00:00:00 2001
From: Ewan Young <kdbase.hack@gmail.com>
Date: Fri, 28 Aug 2026 23:40:01 +0800
Subject: [PATCH v2] Repack: Fix replica identity index check

check_concurrent_repack_requirements() obtained the identity index with
GetRelationIdentityOrPK(), which falls back to the primary key when there
is no replica identity index.  Logical decoding, however, uses
RelationGetReplicaIndex() and does not fall back to the primary key.  So a
table with REPLICA IDENTITY USING INDEX whose index has since been dropped
(relreplident still 'i', but the index gone) passes the check via the PK
fallback, lets the repack start, and then fails during catch-up with
"incomplete delete info" once a delete has to be decoded.

Use RelationGetReplicaIndex() so the check matches what decoding needs, and
keep the existing error structure: the deferrable-primary-key case stays a
reason for "no identity index" (inside the !OidIsValid branch).  This avoids
wrongly rejecting a table that has an explicit replica identity index but no
primary key, and keeps the deferrable-specific message for the case it is
meant for.
---
 src/backend/commands/repack.c | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 477c86b2ba6..cc8c52662be 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -924,13 +924,14 @@ check_concurrent_repack_requirements(Relation rel, Oid *ident_idx_p)
 						  "REPLICA IDENTITY NOTHING" : "REPLICA IDENTITY FULL"));
 
 	/*
-	 * Obtain the replica identity index -- either one that has been set
-	 * explicitly, or a non-deferrable primary key.  If none of these cases
-	 * apply, the table cannot be repacked concurrently.  It might be possible
-	 * to have repack work with a FULL replica identity; however that requires
-	 * more work and is not implemented yet.
-	 */
-	ident_idx = GetRelationIdentityOrPK(rel);
+	 * Obtain the replica identity index -- the one set explicitly, or the
+	 * default non-deferrable primary key.  Use RelationGetReplicaIndex(), not
+	 * GetRelationIdentityOrPK(): decoding does not fall back to the primary
+	 * key, so neither may we.  If there is no such index, the table cannot be
+	 * repacked concurrently; a FULL replica identity might be workable but is
+	 * not implemented yet.
+	 */
+	ident_idx = RelationGetReplicaIndex(rel);
 	if (!OidIsValid(ident_idx))
 	{
 		/* This special case warrants its own error message */
-- 
2.47.3

