From d29cc80d400e206e13566748d9c20903725a5211 Mon Sep 17 00:00:00 2001
From: Vignesh C <c_vigneshwaran@apple.com>
Date: Wed, 9 Sep 2026 09:21:27 +0530
Subject: [PATCH v1 2/5] Fix ALTER PUBLICATION validation race

AlterPublication() calls CheckAlterPublication() before acquiring the
publication lock. A concurrent ALTER PUBLICATION can modify
pg_publication_rel while the command waits for the lock, allowing the
validation to pass based on stale state.

The command then re-fetches the publication tuple after acquiring the
lock, but does not revalidate the publication relations. This can leave
pg_publication_rel containing a mixture of inclusion and exclusion rows.

Re-run CheckAlterPublication() after acquiring the lock so that the
validation uses the current publication state.
---
 src/backend/commands/publicationcmds.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c
index 79276c842cc..96d5c0453a1 100644
--- a/src/backend/commands/publicationcmds.c
+++ b/src/backend/commands/publicationcmds.c
@@ -1695,8 +1695,6 @@ AlterPublication(ParseState *pstate, AlterPublicationStmt *stmt)
 		ObjectsInPublicationToOids(stmt->pubobjects, pstate, &relations,
 								   &exceptrelations, &schemaidlist);
 
-		CheckAlterPublication(stmt, tup, relations, schemaidlist);
-
 		heap_freetuple(tup);
 
 		/* Lock the publication so nobody else can do anything with it. */
@@ -1716,6 +1714,17 @@ AlterPublication(ParseState *pstate, AlterPublicationStmt *stmt)
 					errmsg("publication \"%s\" does not exist",
 						   stmt->pubname));
 
+		/*
+		 * Validate after acquiring the lock so that the checks see the
+		 * publication state we are about to modify. A concurrent ALTER
+		 * PUBLICATION may otherwise change the state while we wait for the
+		 * lock, causing both commands to pass their checks independently.
+		 * LockDatabaseObject() accepts invalidation messages after acquiring
+		 * the lock, so the catalog scans below see concurrently committed
+		 * changes.
+		 */
+		CheckAlterPublication(stmt, tup, relations, schemaidlist);
+
 		relations = list_concat(relations, exceptrelations);
 		AlterPublicationTables(stmt, tup, relations, pstate->p_sourcetext,
 							   schemaidlist != NIL);
-- 
2.55.0

