From a0150ddf088a622d38eac247ebd7ff862e7fb7a8 Mon Sep 17 00:00:00 2001
From: Jeff Davis <jeff@j-davis.com>
Date: Tue, 28 Jul 2026 14:50:45 -0700
Subject: [PATCH v3 01/11] Fix lock release for role membership grants in DROP
 OWNED BY.

Commit 6566133c5f5 added a case for AuthMemRelationId in
AcquireDeletionLock(), but not ReleaseDeletionLock(). The fall-through
case would go to UnlockDatabaseObject(), which would raise a WARNING;
and the lock would be retained until the end of the transaction.

Add the missing branch.

Discussion: https://postgr.es/m/2487ddcd737d4fc8e408e87aa9ad4365eed3bbb3.camel@j-davis.com
Backpatch-through: 16
---
 src/backend/catalog/dependency.c              |  3 ++
 .../isolation/expected/drop-owned-grant.out   |  8 +++++
 src/test/isolation/isolation_schedule         |  1 +
 .../isolation/specs/drop-owned-grant.spec     | 30 +++++++++++++++++++
 4 files changed, 42 insertions(+)
 create mode 100644 src/test/isolation/expected/drop-owned-grant.out
 create mode 100644 src/test/isolation/specs/drop-owned-grant.spec

diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index c54774b3275..52cd2caf9d4 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -1600,6 +1600,9 @@ ReleaseDeletionLock(const ObjectAddress *object)
 {
 	if (object->classId == RelationRelationId)
 		UnlockRelationOid(object->objectId, AccessExclusiveLock);
+	else if (object->classId == AuthMemRelationId)
+		UnlockSharedObject(object->classId, object->objectId, 0,
+						   AccessExclusiveLock);
 	else
 		/* assume we should lock the whole object not a sub-object */
 		UnlockDatabaseObject(object->classId, object->objectId, 0,
diff --git a/src/test/isolation/expected/drop-owned-grant.out b/src/test/isolation/expected/drop-owned-grant.out
new file mode 100644
index 00000000000..ea6cca277b6
--- /dev/null
+++ b/src/test/isolation/expected/drop-owned-grant.out
@@ -0,0 +1,8 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1b s1d s2d s1c
+step s1b: BEGIN;
+step s1d: DROP OWNED BY regress_dropowned_grantor;
+step s2d: DROP OWNED BY regress_dropowned_grantor; <waiting ...>
+step s1c: COMMIT;
+step s2d: <... completed>
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 26abed9f9f0..df8ce44ede6 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -129,3 +129,4 @@ test: lock-nowait
 test: for-portion-of
 test: ddl-dependency-locking
 test: pub-concurrent-drop
+test: drop-owned-grant
diff --git a/src/test/isolation/specs/drop-owned-grant.spec b/src/test/isolation/specs/drop-owned-grant.spec
new file mode 100644
index 00000000000..636cc213a6b
--- /dev/null
+++ b/src/test/isolation/specs/drop-owned-grant.spec
@@ -0,0 +1,30 @@
+# Test locking of role membership grants during concurrent DROP OWNED BY.
+
+setup
+{
+	CREATE ROLE regress_dropowned_role;
+	CREATE ROLE regress_dropowned_member;
+	CREATE ROLE regress_dropowned_grantor;
+	GRANT regress_dropowned_role TO regress_dropowned_grantor
+		WITH ADMIN OPTION;
+	SET ROLE regress_dropowned_grantor;
+	GRANT regress_dropowned_role TO regress_dropowned_member;
+	RESET ROLE;
+}
+
+teardown
+{
+	DROP ROLE regress_dropowned_member;
+	DROP ROLE regress_dropowned_grantor;
+	DROP ROLE regress_dropowned_role;
+}
+
+session s1
+step s1b	{ BEGIN; }
+step s1d	{ DROP OWNED BY regress_dropowned_grantor; }
+step s1c	{ COMMIT; }
+
+session s2
+step s2d	{ DROP OWNED BY regress_dropowned_grantor; }
+
+permutation s1b s1d s2d s1c
-- 
2.43.0

