Hi hackers, The previous attempt to add a predefined role for VACUUM and ANALYZE [0] resulted in the new pg_checkpoint role in v15. I'd like to try again to add a new role (or multiple new roles) for VACUUM and ANALYZE.
The primary motivation for this is to continue chipping away at things that require special privileges or even superuser. VACUUM and ANALYZE typically require table ownership, database ownership, or superuser. And only superusers can VACUUM/ANALYZE shared catalogs. A predefined role for these operations would allow delegating such tasks (e.g., a nightly VACUUM scheduled with pg_cron) to a role with fewer privileges. The attached patch adds a pg_vacuum_analyze role that allows VACUUM and ANALYZE commands on all relations. I started by trying to introduce separate pg_vacuum and pg_analyze roles, but that quickly became complicated because the VACUUM and ANALYZE code is intertwined. To initiate the discussion, here's the simplest thing I could think of. An alternate approach might be to allow using GRANT to manage these privileges, as suggested in the previous thread [1]. Thoughts? [0] https://postgr.es/m/67a1d667e8ec228b5e07f232184c80348c5d93f4.camel%40j-davis.com [1] https://postgr.es/m/20211104224636.5qg6cfyjkw52r...@alap3.anarazel.de -- Nathan Bossart Amazon Web Services: https://aws.amazon.com
commit 0b1397397c0f490525d3a15f5e9d5eb8f6023aa9 Author: Nathan Bossart <nathandboss...@gmail.com> Date: Fri Jul 22 12:21:16 2022 -0700 introduce pg_vacuum_analyze diff --git a/doc/src/sgml/ref/analyze.sgml b/doc/src/sgml/ref/analyze.sgml index b968f740cb..203b713a4e 100644 --- a/doc/src/sgml/ref/analyze.sgml +++ b/doc/src/sgml/ref/analyze.sgml @@ -148,11 +148,14 @@ ANALYZE [ VERBOSE ] [ <replaceable class="parameter">table_and_columns</replacea <title>Notes</title> <para> - To analyze a table, one must ordinarily be the table's owner or a - superuser. However, database owners are allowed to + To analyze a table, one must ordinarily be the table's owner, a superuser, or + a role with privileges of the + <link linkend="predefined-roles-table"><literal>pg_vacuum_analyze</literal></link> + role. However, database owners are allowed to analyze all tables in their databases, except shared catalogs. (The restriction for shared catalogs means that a true database-wide - <command>ANALYZE</command> can only be performed by a superuser.) + <command>ANALYZE</command> can only be performed by superusers and roles with + privileges of <literal>pg_vacuum_analyze</literal>.) <command>ANALYZE</command> will skip over any tables that the calling user does not have permission to analyze. </para> diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml index c582021d29..12d7b96fee 100644 --- a/doc/src/sgml/ref/vacuum.sgml +++ b/doc/src/sgml/ref/vacuum.sgml @@ -356,11 +356,14 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="paramet <title>Notes</title> <para> - To vacuum a table, one must ordinarily be the table's owner or a - superuser. However, database owners are allowed to + To vacuum a table, one must ordinarily be the table's owner, a superuser, or + a role with privileges of the + <link linkend="predefined-roles-table"><literal>pg_vacuum_analyze</literal></link> + role. However, database owners are allowed to vacuum all tables in their databases, except shared catalogs. (The restriction for shared catalogs means that a true database-wide - <command>VACUUM</command> can only be performed by a superuser.) + <command>VACUUM</command> can only be performed by superusers and roles with + privileges of <literal>pg_vacuum_analyze</literal>.) <command>VACUUM</command> will skip over any tables that the calling user does not have permission to vacuum. </para> diff --git a/doc/src/sgml/user-manag.sgml b/doc/src/sgml/user-manag.sgml index 6eaaaa36b8..b09aa7aed9 100644 --- a/doc/src/sgml/user-manag.sgml +++ b/doc/src/sgml/user-manag.sgml @@ -588,6 +588,13 @@ DROP ROLE doomed_role; the <link linkend="sql-checkpoint"><command>CHECKPOINT</command></link> command.</entry> </row> + <row> + <entry>pg_vacuum_analyze</entry> + <entry>Allow executing the + <link linkend="sql-vacuum"><command>VACUUM</command></link></entry> and + <link linkend="sql-analyze"><command>ANALYZE</command></link></entry> + commands on all tables.</entry> + </row> </tbody> </tgroup> </table> diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 8df25f59d8..b3eb41a8cc 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -36,6 +36,7 @@ #include "access/xact.h" #include "catalog/namespace.h" #include "catalog/index.h" +#include "catalog/pg_authid.h" #include "catalog/pg_database.h" #include "catalog/pg_inherits.h" #include "catalog/pg_namespace.h" @@ -574,7 +575,8 @@ vacuum_is_relation_owner(Oid relid, Form_pg_class reltuple, bits32 options) * trying to vacuum or analyze the rest of the DB --- is this appropriate? */ if (pg_class_ownercheck(relid, GetUserId()) || - (pg_database_ownercheck(MyDatabaseId, GetUserId()) && !reltuple->relisshared)) + (pg_database_ownercheck(MyDatabaseId, GetUserId()) && !reltuple->relisshared) || + has_privs_of_role(GetUserId(), ROLE_PG_VACUUM_ANALYZE)) return true; relname = NameStr(reltuple->relname); @@ -583,11 +585,14 @@ vacuum_is_relation_owner(Oid relid, Form_pg_class reltuple, bits32 options) { if (reltuple->relisshared) ereport(WARNING, - (errmsg("skipping \"%s\" --- only superuser can vacuum it", + (errmsg("skipping \"%s\" --- only superusers and roles with " + "privileges of pg_vacuum_analyze can vacuum it", relname))); else if (reltuple->relnamespace == PG_CATALOG_NAMESPACE) ereport(WARNING, - (errmsg("skipping \"%s\" --- only superuser or database owner can vacuum it", + (errmsg("skipping \"%s\" --- only superusers, roles with " + "privileges of pg_vacuum_analyze, or the database " + "owner can vacuum it", relname))); else ereport(WARNING, @@ -606,11 +611,14 @@ vacuum_is_relation_owner(Oid relid, Form_pg_class reltuple, bits32 options) { if (reltuple->relisshared) ereport(WARNING, - (errmsg("skipping \"%s\" --- only superuser can analyze it", + (errmsg("skipping \"%s\" --- only superusers and roles with " + "privileges of pg_vacuum_analyze can analyze it", relname))); else if (reltuple->relnamespace == PG_CATALOG_NAMESPACE) ereport(WARNING, - (errmsg("skipping \"%s\" --- only superuser or database owner can analyze it", + (errmsg("skipping \"%s\" --- only superusers, roles with " + "privileges of pg_vacuum_analyze, or the database " + "owner can analyze it", relname))); else ereport(WARNING, diff --git a/src/include/catalog/pg_authid.dat b/src/include/catalog/pg_authid.dat index 3343a69ddb..f067fe1c57 100644 --- a/src/include/catalog/pg_authid.dat +++ b/src/include/catalog/pg_authid.dat @@ -84,5 +84,10 @@ rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', rolpassword => '_null_', rolvaliduntil => '_null_' }, +{ oid => '4549', oid_symbol => 'ROLE_PG_VACUUM_ANALYZE', + rolname => 'pg_vacuum_analyze', rolsuper => 'f', rolinherit => 't', + rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', + rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', + rolpassword => '_null_', rolvaliduntil => '_null_' }, ] diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out index c63a157e5f..859be4c13e 100644 --- a/src/test/regress/expected/vacuum.out +++ b/src/test/regress/expected/vacuum.out @@ -302,18 +302,18 @@ VACUUM (ANALYZE) vacowned; WARNING: skipping "vacowned" --- only table or database owner can vacuum it -- Catalog VACUUM pg_catalog.pg_class; -WARNING: skipping "pg_class" --- only superuser or database owner can vacuum it +WARNING: skipping "pg_class" --- only superusers, roles with privileges of pg_vacuum_analyze, or the database owner can vacuum it ANALYZE pg_catalog.pg_class; -WARNING: skipping "pg_class" --- only superuser or database owner can analyze it +WARNING: skipping "pg_class" --- only superusers, roles with privileges of pg_vacuum_analyze, or the database owner can analyze it VACUUM (ANALYZE) pg_catalog.pg_class; -WARNING: skipping "pg_class" --- only superuser or database owner can vacuum it +WARNING: skipping "pg_class" --- only superusers, roles with privileges of pg_vacuum_analyze, or the database owner can vacuum it -- Shared catalog VACUUM pg_catalog.pg_authid; -WARNING: skipping "pg_authid" --- only superuser can vacuum it +WARNING: skipping "pg_authid" --- only superusers and roles with privileges of pg_vacuum_analyze can vacuum it ANALYZE pg_catalog.pg_authid; -WARNING: skipping "pg_authid" --- only superuser can analyze it +WARNING: skipping "pg_authid" --- only superusers and roles with privileges of pg_vacuum_analyze can analyze it VACUUM (ANALYZE) pg_catalog.pg_authid; -WARNING: skipping "pg_authid" --- only superuser can vacuum it +WARNING: skipping "pg_authid" --- only superusers and roles with privileges of pg_vacuum_analyze can vacuum it -- Partitioned table and its partitions, nothing owned by other user. -- Relations are not listed in a single command to test ownership -- independently.