This looks like a very basic need, but I do not find any quick and suitable answer. I have a role in Postgres which has privileges to many other tables in various databases.
I need to drop this role. I have one postgres instance and then many databases on top of it.
SELECT DISTINCT 'REVOKE ALL ON TABLE ' || table_schema || '.' || table_name || ' FROM ' || r.param_role_name || ';'
FROM information_schema.table_privileges CROSS JOIN (SELECT 'some_role_name'::text AS param_role_name) r
WHERE grantee ~* r.param_role_name;
I can do like above, by going to each and every database and find all revoke statements and then drop the role. Is there any way I can find all revoke statements at one place for all the databases.
Or something like I can alter owned by this role, can I alter all privileges with one statement?
Edit1: As most of the replies are targeted to reassign owned by and drop owned by, I want to be more specific.
I can execute below commands on each database and drop the roles once all the dependencies are removed.
REASSIGN OWNED BY some_role_name TO postgres;
DROP OWNED BY some_role_name;
but there are many databases so I am looking for something forcefully flush all privileges and drop this role.
I need to drop this role.
A single REASSIGN OWNED
per database, followed by DROP OWNED
should do it:
REASSIGN OWNED BY some_role_name TO postgres;
DROP OWNED BY some_role_name;
postgres
being the default superuser, who is going to own any object the role used to own. Immediately after REASSIGN OWNED
, there are no objects left that would be owned by the same user. It may seem unintuitive to run DROP OWNED
. The wording of the command is misleading, since it also revokes all privileges and default privileges for the role in the same database. The manual:
DROP OWNED
drops all the objects within the current database that are owned by one of the specified roles. Any privileges granted to the given roles on objects in the current database and on shared objects (databases, tablespaces) will also be revoked.
Bold emphasis mine.
You still have to execute it in every single database. The manual:
Because REASSIGN OWNED
does not affect objects within other databases, it is usually necessary to execute this command in each database that contains objects owned by a role that is to be removed.
Before you can run (once!):
DROP role some_role_name;
Roles are stored in a cluster-wide system catalog, while ownership and privileges on objects are stored in database-specific system catalogs.
Detailed explanation in this related answer:
- Find objects linked to a PostgreSQL role
And @klin mentioned a very useful manual page in his comment:
- https://www.postgresql.org/docs/current/static/role-removal.html
Full automation
There is no single command to do it all. But you can let Postgres generate a complete psql script for you.
Dependencies for roles are stored in the system catalog pg_shdepend
. A quote from there:
This information allows PostgreSQL to ensure that those objects are unreferenced before attempting to delete them.
Since we (potentially) need to connect to different databases, we need a combination of psql meta-commands (\c my_database
) and SQL DDL commands as shown above. Create this function somewhere in your DB cluster once:
CREATE OR REPLACE FUNCTION f_generate_ddl_to_remove_role(dead_role_walking regrole)
RETURNS text AS
$func$
SELECT concat_ws(
E'\n'
,(SELECT string_agg(format(E'\\c %I\nREASSIGN OWNED BY %2$s TO postgres; DROP OWNED BY %2$s;'
, d.datname, dead_role_walking)
, E'\n')
FROM (
SELECT DISTINCT dbid
FROM pg_shdepend
WHERE refobjid = dead_role_walking
) s
JOIN pg_database d ON d.oid = s.dbid)
, format(E'DROP role %s;\n', dead_role_walking)
)
$func$ LANGUAGE sql;
Call:
SELECT f_generate_ddl_to_remove_role('some_role_name');
Produces a string like:
\c my_db1
REASSIGN OWNED BY some_role_name TO postgres; DROP OWNED BY some_role_name;
\c my_db2
REASSIGN OWNED BY some_role_name TO postgres; DROP OWNED BY some_role_name;
DROP role some_role_name;
Or, if the role does not own anything and has no privileges, just:
DROP role some_role_name;
If you provide a non-existing role name, you get an error.
Copy the string (without enclosing single quotes) to a psql session opened with a superuser like postgres
. Or concatenate a bash script with it. All done.
There are several related answers with more explanation for dynamic SQL:
- https://stackoverflow.com/search?q=%5Bplpgsql%5D+%5Bdynamic-sql%5D+string_agg
You cannot drop a role until there are dependent objects in any of the database.
You will have to reassign the objects owned by this user (some_role_name) to another user.
Once it is done for all objects in all the databases, you can then delete the user.
REASSIGN OWNED BY some_role_name TO some_other_role_name;
If the dependent objects are not required, you can also go ahead using DROP OWNED BY, removing all the object that are owned by some_role_name.
DROP OWNED BY some_role_name;