32

I want to GRANT USAGE to a user/role for a given database. The database has many schemas.

I know there's an ON ALL TABLES IN SCHEMA, but I want "all schemas". I tried GRANT USAGE .. ON DATABASE, but that's obviously wrong (it doesn't actually exist).

This is for Postgres 9.3 or 9.4, it happens to be a server that is on AWS RDS.

András Váczi
  • 31,778
  • 13
  • 102
  • 151
300D7309EF17
  • 431
  • 1
  • 4
  • 6

4 Answers4

28

You have at least two options.

The first one makes use of a small query and a text editor. We have to collect the schemata of our interest:

SELECT nspname
  FROM pg_namespace;

You can add a WHERE clause if you want to limit the scope. Copy the output and amend it, so you get a number of GRANT USAGE ON SCHEMA ... TO your_role; commands. Then just feed it to psql, for example:

psql -f multigrant.sql

A usual variant of this could be a shell script that loops over the collected names and calls psql, passing the constructed GRANT statement to the -c option.

The other solution does basically the same in one pl/pgsql block, building a dynamic query. The core is the same - we have to collect the schemata. Then we loop over all of them, granting the permissions schema by schema:

DO $do$
DECLARE
    sch text;
BEGIN
    FOR sch IN SELECT nspname FROM pg_namespace
    LOOP
        EXECUTE format($$ GRANT USAGE ON SCHEMA %I TO your_role $$, sch);
    END LOOP;
END;
$do$;

Notes:

  • unlike for tables, sequences, functions and types, one cannot set default privileges for schemata (as of 9.4). You will have to grant this privilege for any newly added schema manually.
  • here I am using dollar quoting when building the dynamic query. This allows me to use 'normal' syntax, as opposed to multiplicating single quotes, for example (not present in this example). This way most editors will highlight the statements nicely.
  • I also use format() with the %I format specifier to have the object name properly quoted if necessary. This approach is far more readable than building the query with concatenation of string constants and some quote_ident() calls.
  • pg_namespace can be found in the pg_catalog schema. Check out the other objects in there - they store every aspect of your schemas, tables and so on.
András Váczi
  • 31,778
  • 13
  • 102
  • 151
6

You can use too.

DO $do$
DECLARE
    sch text;
BEGIN
    FOR sch IN SELECT nspname FROM pg_namespace where nspname != 'pg_toast' 
    and nspname != 'pg_temp_1' and nspname != 'pg_toast_temp_1'
    and nspname != 'pg_statistic' and nspname != 'pg_catalog'
    and nspname != 'information_schema'
    LOOP
        EXECUTE format($$ GRANT USAGE ON SCHEMA %I TO your_role $$, sch);
        EXECUTE format($$ GRANT SELECT ON ALL SEQUENCES IN SCHEMA %I TO your_role $$, sch);
        EXECUTE format($$ GRANT SELECT ON ALL TABLES IN SCHEMA %I TO your_role $$, sch);
    EXECUTE format($$ ALTER DEFAULT PRIVILEGES IN SCHEMA %I GRANT SELECT ON TABLES TO your_role $$, sch);
    EXECUTE format($$ ALTER DEFAULT PRIVILEGES IN SCHEMA %I GRANT SELECT ON SEQUENCES TO your_role $$, sch);
END LOOP;

END; $do$;

Mateus Padua
  • 61
  • 1
  • 1
2

Try pg_read_all_data in PostgreSQL 14 and later. It will grant SELECT and USAGE.

Read all data (tables, views, sequences), as if having SELECT rights on those objects, and USAGE rights on all schemas, even without having it explicitly.

Paul White
  • 94,921
  • 30
  • 437
  • 687
yegorski
  • 121
  • 3
1

That's my variation, adapted from the other answers, for giving all privileges:

create or replace function grant_all_privileges_on_database_to_role(
    role_name text,
    database_name text
)
    returns void
    language plpgsql as
$function$
declare
    row record;
begin
    execute format('grant all on database %1$s to %2$s', database_name, role_name);
for row in (
    select
        nspname as name
    from
        pg_namespace
    where
        nspname not in (
            'information_schema',
            'pg_catalog',
            'pg_toast'
        )
) loop
    execute format('grant all on schema %1$s to %2$s', row.name, role_name);
    execute format('grant all on all tables in schema %1$s to %2$s', row.name, role_name);
    execute format('grant all on all sequences in schema %1$s to %2$s', row.name, role_name);
    execute format('grant all on all functions in schema %1$s to %2$s', row.name, role_name);
    execute format('grant all on all procedures in schema %1$s to %2$s', row.name, role_name);
    execute format('grant all on all routines in schema %1$s to %2$s', row.name, role_name);
end loop;

end; $function$;

-- select grant_all_privileges_on_database_to_role('<my_user>', '<my_database>');

or, for a schema:

create or replace function grant_all_privileges_on_schema_to_role(
    role_name text,
    schema_name text
)
    returns void
    language plpgsql
as $function$
begin
    execute format('grant all on schema %1$s to %2$s', schema_name, role_name);
    execute format('grant all on all tables in schema %1$s to %2$s', schema_name, role_name);
    execute format('grant all on all sequences in schema %1$s to %2$s', schema_name, role_name);
    execute format('grant all on all functions in schema %1$s to %2$s', schema_name, role_name);
    execute format('grant all on all procedures in schema %1$s to %2$s', schema_name, role_name);
    execute format('grant all on all routines in schema %1$s to %2$s', schema_name, role_name);
end;
$function$;

-- select grant_all_privileges_on_schema_to_role('<my_user>', '<my_schema>');

GarouDan
  • 131
  • 5