Hello postgres hackers: I recently came across a scenario involving system catalog "pg_default_acl" where a tuple contains a NULL value for the "defaclacl" attribute. This can cause confusion while dropping a role whose default ACL has been changed. Here is a way to reproduce that:
``` example postgres=# create user adminuser; CREATE ROLE postgres=# create user normaluser; CREATE ROLE postgres=# alter default privileges for role adminuser grant all on tables to normaluser; ALTER DEFAULT PRIVILEGES postgres=# alter default privileges for role adminuser revoke all ON tables from adminuser; ALTER DEFAULT PRIVILEGES postgres=# alter default privileges for role adminuser revoke all ON tables from normaluser; ALTER DEFAULT PRIVILEGES postgres=# select * from pg_default_acl where pg_get_userbyid(defaclrole) = 'adminuser'; oid | defaclrole | defaclnamespace | defaclobjtype | defaclacl -------+------------+-----------------+---------------+----------- 16396 | 16394 | 0 | r | {} (1 row) postgres=# drop user adminuser ; ERROR: role "adminuser" cannot be dropped because some objects depend on it DETAIL: owner of default privileges on new relations belonging to role adminuser ``` I believe this is a bug since the tuple can be deleted if we revoke from "normaluser" first. Besides, according to the document: https://www.postgresql.org/docs/current/sql-alterdefaultprivileges.html > If you wish to drop a role for which the default privileges have been altered, > it is necessary to reverse the changes in its default privileges or use DROP > OWNED BY > to get rid of the default privileges entry for the role. There must be a way to "reverse the changes", but NULL value of "defaclacl" prevents it. Luckily, "DROP OWNED BY" works well. The code-level reason could be that the function "SetDefaultACL" doesn't handle the situation where "new_acl" is NULL. So I present a simple patch here. diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index 01ff575b093..0e313526b28 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -1335,7 +1335,7 @@ SetDefaultACL(InternalDefaultACL *iacls) */ aclitemsort(new_acl); aclitemsort(def_acl); - if (aclequal(new_acl, def_acl)) + if (aclequal(new_acl, def_acl) || ACL_NUM(new_acl) == 0) { /* delete old entry, if indeed there is one */ if (!isNew) Best regards, Boyu Yang