From aa2f2cd51d3d95708fe56167064ca697c7e39970 Mon Sep 17 00:00:00 2001
From: ChangAo Chen <cca5507@qq.com>
Date: Tue, 23 Dec 2025 10:59:28 +0800
Subject: [PATCH v3] Use ROLERECURSE_PRIVS in is_admin_of_role().

Currently we use different role recurse methods in is_admin_of_role()
and select_best_admin(), this could lead to an unexpected behavior.
For example, when is_admin_of_role() return true, select_best_admin()
can still return InvalidOid. To fix it, we use ROLERECURSE_PRIVS in
is_admin_of_role(), making it consistent with select_best_admin().
---
 src/backend/utils/adt/acl.c               | 2 +-
 src/test/regress/expected/create_role.out | 8 ++++++++
 src/test/regress/sql/create_role.sql      | 7 +++++++
 3 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 05d48412f82..d22e696a46a 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -5422,7 +5422,7 @@ is_admin_of_role(Oid member, Oid role)
 	if (member == role)
 		return false;
 
-	(void) roles_is_member_of(member, ROLERECURSE_MEMBERS, role, &admin_role);
+	(void) roles_is_member_of(member, ROLERECURSE_PRIVS, role, &admin_role);
 	return OidIsValid(admin_role);
 }
 
diff --git a/src/test/regress/expected/create_role.out b/src/test/regress/expected/create_role.out
index 46d4f9efe99..fe36586e49e 100644
--- a/src/test/regress/expected/create_role.out
+++ b/src/test/regress/expected/create_role.out
@@ -218,6 +218,14 @@ DROP ROLE regress_nosuch_recursive;
 ERROR:  role "regress_nosuch_recursive" does not exist
 DROP ROLE regress_nosuch_admin_recursive;
 ERROR:  role "regress_nosuch_admin_recursive" does not exist
+-- fail, regress_role_admin is not admin of regress_plainrole
+DROP ROLE regress_plainrole;
+ERROR:  permission denied to drop role
+DETAIL:  Only roles with the CREATEROLE attribute and the ADMIN option on role "regress_plainrole" may drop this role.
+-- ok, regress_role_admin is admin of regress_createrole
+GRANT regress_createrole TO regress_role_admin WITH INHERIT TRUE;
+-- ok, regress_createrole is admin of regress_plainrole and regress_role_admin
+-- inherits permissions from regress_createrole
 DROP ROLE regress_plainrole;
 -- must revoke privileges before dropping role
 REVOKE CREATE ON DATABASE regression FROM regress_createrole CASCADE;
diff --git a/src/test/regress/sql/create_role.sql b/src/test/regress/sql/create_role.sql
index 4491a28a8ae..9247a90b4b4 100644
--- a/src/test/regress/sql/create_role.sql
+++ b/src/test/regress/sql/create_role.sql
@@ -174,6 +174,13 @@ DROP ROLE regress_nosuch_super;
 DROP ROLE regress_nosuch_dbowner;
 DROP ROLE regress_nosuch_recursive;
 DROP ROLE regress_nosuch_admin_recursive;
+
+-- fail, regress_role_admin is not admin of regress_plainrole
+DROP ROLE regress_plainrole;
+-- ok, regress_role_admin is admin of regress_createrole
+GRANT regress_createrole TO regress_role_admin WITH INHERIT TRUE;
+-- ok, regress_createrole is admin of regress_plainrole and regress_role_admin
+-- inherits permissions from regress_createrole
 DROP ROLE regress_plainrole;
 
 -- must revoke privileges before dropping role
-- 
2.34.1

