Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Fix DROP ROLE when specifying duplicated roles
authorMichael Paquier <michael@paquier.xyz>
Sun, 28 Jan 2024 23:05:59 +0000 (08:05 +0900)
committerMichael Paquier <michael@paquier.xyz>
Sun, 28 Jan 2024 23:05:59 +0000 (08:05 +0900)
This commit fixes failures with "tuple already updated by self" when
listing twice the same role and in a DROP ROLE query.

This is an oversight in 6566133c5f52, that has introduced a two-phase
logic in DropRole() where dependencies of all the roles to drop are
removed in a first phase, with the roles themselves removed from
pg_authid in a second phase.

The code is simplified to not rely on a List of ObjectAddress built in
the first phase used to remove the pg_authid entries in the second
phase, switching to a list of OIDs.  Duplicated OIDs can be simply
avoided in the first phase thanks to that.  Using ObjectAddress was not
necessary for the roles as they are not used for anything specific to
dependency.c, building all the ObjectAddress in the List with
AuthIdRelationId as class ID.

In 15 and older versions, where a single phase is used, DROP ROLE with
duplicated role names would fail on "role \"blah\" does not exist" for
the second entry after the CCI() done by the first deletion.  This is
not really incorrect, but it does not seem worth changing based on a
lack of complaints.

Reported-by: Alexander Lakhin
Reviewed-by: Tender Wang
Discussion: https://postgr.es/m/18310-1eb233c5908189c8@postgresql.org
Backpatch-through: 16

src/backend/commands/user.c
src/test/regress/expected/create_role.out
src/test/regress/sql/create_role.sql

index 7a9c177b21c82c4fd43dac31d0d28cf8be99230b..6839d44a2f5e06d2f3132b68dee677185c9d680e 100644 (file)
@@ -1093,7 +1093,7 @@ DropRole(DropRoleStmt *stmt)
    Relation    pg_authid_rel,
                pg_auth_members_rel;
    ListCell   *item;
-   List       *role_addresses = NIL;
+   List       *role_oids = NIL;
 
    if (!have_createrole_privilege())
        ereport(ERROR,
@@ -1119,7 +1119,6 @@ DropRole(DropRoleStmt *stmt)
        ScanKeyData scankey;
        SysScanDesc sscan;
        Oid         roleid;
-       ObjectAddress *role_address;
 
        if (rolspec->roletype != ROLESPEC_CSTRING)
            ereport(ERROR,
@@ -1260,21 +1259,16 @@ DropRole(DropRoleStmt *stmt)
         */
        CommandCounterIncrement();
 
-       /* Looks tentatively OK, add it to the list. */
-       role_address = palloc(sizeof(ObjectAddress));
-       role_address->classId = AuthIdRelationId;
-       role_address->objectId = roleid;
-       role_address->objectSubId = 0;
-       role_addresses = lappend(role_addresses, role_address);
+       /* Looks tentatively OK, add it to the list if not there yet. */
+       role_oids = list_append_unique_oid(role_oids, roleid);
    }
 
    /*
     * Second pass over the roles to be removed.
     */
-   foreach(item, role_addresses)
+   foreach(item, role_oids)
    {
-       ObjectAddress *role_address = lfirst(item);
-       Oid         roleid = role_address->objectId;
+       Oid         roleid = lfirst_oid(item);
        HeapTuple   tuple;
        Form_pg_authid roleform;
        char       *detail;
index 7117e943c2096a32d5832cba7e0b9f86838066ca..46d4f9efe997ebe3c4c8516d04d12c820c8502ae 100644 (file)
@@ -251,7 +251,8 @@ DROP INDEX tenant_idx;
 DROP TABLE tenant_table;
 DROP VIEW tenant_view;
 DROP SCHEMA regress_tenant2_schema;
-DROP ROLE regress_tenant;
+-- check for duplicated drop
+DROP ROLE regress_tenant, regress_tenant;
 DROP ROLE regress_tenant2;
 DROP ROLE regress_rolecreator;
 DROP ROLE regress_role_admin;
index 12582a3cc29df740f8edb593d5aec4eca583fbbb..4491a28a8ae99894e846a0dcd0fc0aa1a43389b6 100644 (file)
@@ -206,7 +206,8 @@ DROP INDEX tenant_idx;
 DROP TABLE tenant_table;
 DROP VIEW tenant_view;
 DROP SCHEMA regress_tenant2_schema;
-DROP ROLE regress_tenant;
+-- check for duplicated drop
+DROP ROLE regress_tenant, regress_tenant;
 DROP ROLE regress_tenant2;
 DROP ROLE regress_rolecreator;
 DROP ROLE regress_role_admin;