The Incident
While cleaning up some obsolete user accounts, one of my DROP USER
commands failed with:
ERROR: 2BP01: role "joe" cannot be dropped because some objects depend on it
Querying the usual catalog views, I couldn’t find any objects owned by this user. That’s when I noticed the line just after the ERROR:
DETAIL: privileges for schema public
The \dn+ command confirmed that user joe had explicit USAGE privileges on the public schema:
List of schemas
Name | Owner | Access privileges | Description
----------+----------+----------------------+------------------------
public | postgres | postgres=UC/postgres+| standard public schema
| | =UC/postgres +|
| | joe=U/postgres |
I revoked this and was then able to drop that user.
> REVOKE USAGE ON SCHEMA public FROM joe;
REVOKE
> DROP USER joe;
DROP ROLE
Replicating The Problem
It’s simple enough to replicate the problem, and it isn’t limited to the special public schema (although that does bring up another question for later).
Setup
We already have the public schema, let’s create another one for testing and then create our three lab rat users:
-- Create a test schema, we'll use this in addition to public
> CREATE SCHEMA appstuff;
CREATE SCHEMA
-- Create our three users
> CREATE USER moe;
CREATE ROLE
> CREATE USER larry;
CREATE ROLE
> CREATE USER curly;
CREATE ROLE
Now let’s grant USAGE on the two schemas to two of the users:
> GRANT USAGE ON SCHEMA public TO moe;
GRANT
> GRANT USAGE ON SCHEMA appstuff TO larry;
GRANT
-- Show the access privileges on the schemas
> \dn+
List of schemas
Name | Owner | Access privileges | Description
----------+----------+----------------------+------------------------
appstuff | postgres | postgres=UC/postgres+|
| | larry=U/postgres |
public | postgres | postgres=UC/postgres+| standard public schema
| | =UC/postgres +|
| | moe=U/postgres |
DROP USER
Now that we have things set up, let’s try to drop our three stooges:
> DROP USER moe;
psql:user_drop_usage.sql:19: ERROR: 2BP01: role "moe" cannot be dropped because some objects depend on it
DETAIL: privileges for schema public
LOCATION: DropRole, user.c:1045
> DROP USER larry;
psql:user_drop_usage.sql:20: ERROR: 2BP01: role "larry" cannot be dropped because some objects depend on it
DETAIL: privileges for schema appstuff
LOCATION: DropRole, user.c:1045
> DROP USER curly;
DROP ROLE
So now we’ve duplicated the problem with the default public role as well as a new user-created role. The USAGE
ACL privilege clearly behaves differently than other privileges.
Using REVOKE
to remove the ACL clears the way for DROP USER
:
> REVOKE USAGE ON SCHEMA public FROM moe;
REVOKE
> DROP USER moe;
DROP ROLE
> REVOKE USAGE ON SCHEMA appstuff FROM larry;
REVOKE
> DROP USER larry;
DROP ROLE
So we’re all cleaned up now. You can drop the appstuff schema now to really reset things. As far as USAGE
goes, I still don’t understand why PostgreSQL can’t automatically revoke the privilege and drop the user. We don’t have to manually revoke object privileges granted to a user before revoking it. I’ll be sure to update this post if I find out more, or discus in the comments!