From 9ee74d0b07aa81204ba3adcfa1404ba9dccd6cd3 Mon Sep 17 00:00:00 2001
From: Shayon Mukherjee <shayonj@gmail.com>
Date: Tue, 7 Oct 2025 12:43:30 -0400
Subject: [PATCH v1] Allow reads to proceed during FK/trigger drops by reducing
 relation-level lock from AccessExclusive to ShareRowExclusive

---
 src/backend/catalog/pg_constraint.c | 7 ++++---
 src/backend/commands/tablecmds.c    | 6 +++---
 src/backend/commands/trigger.c      | 6 ++++--
 3 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 6002fd0002..13b21b6ee1 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -913,10 +913,11 @@ RemoveConstraintById(Oid conId)
 		Relation	rel;
 
 		/*
-		 * If the constraint is for a relation, open and exclusive-lock the
-		 * relation it's for.
+		 * If the constraint is for a relation, open and lock the relation.
+		 * ShareRowExclusiveLock allows readers to proceed while blocking
+		 * concurrent writers during constraint removal.
 		 */
-		rel = table_open(con->conrelid, AccessExclusiveLock);
+		rel = table_open(con->conrelid, ShareRowExclusiveLock);
 
 		/*
 		 * We need to update the relchecks count if it is a check constraint
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index fc89352b66..d2f73b9c34 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -15474,11 +15474,11 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
 		/*
 		 * When rebuilding another table's constraint that references the
 		 * table we're modifying, we might not yet have any lock on the other
-		 * table, so get one now.  We'll need AccessExclusiveLock for the DROP
-		 * CONSTRAINT step, so there's no value in asking for anything weaker.
+		 * table, so get one now. Use ShareRowExclusiveLock to block writers
+		 * but allow readers during the DROP CONSTRAINT step.
 		 */
 		if (relid != tab->relid)
-			LockRelationOid(relid, AccessExclusiveLock);
+			LockRelationOid(relid, ShareRowExclusiveLock);
 
 		ATPostAlterTypeParse(oldId, relid, confrelid,
 							 (char *) lfirst(def_item),
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 579ac8d76a..fae0cc9a92 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -1315,11 +1315,13 @@ RemoveTriggerById(Oid trigOid)
 		elog(ERROR, "could not find tuple for trigger %u", trigOid);
 
 	/*
-	 * Open and exclusive-lock the relation the trigger belongs to.
+	 * Open and lock the relation the trigger belongs to.
+	 * ShareRowExclusiveLock allows reads while blocking concurrent writes
+	 * during trigger removal.
 	 */
 	relid = ((Form_pg_trigger) GETSTRUCT(tup))->tgrelid;
 
-	rel = table_open(relid, AccessExclusiveLock);
+	rel = table_open(relid, ShareRowExclusiveLock);
 
 	if (rel->rd_rel->relkind != RELKIND_RELATION &&
 		rel->rd_rel->relkind != RELKIND_VIEW &&
-- 
2.39.5 (Apple Git-154)

