From 2835bd62e2779e7b12bd5dbfe4ad09a8edbf96d9 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Wed, 20 Oct 2021 07:48:38 -0700
Subject: [PATCH v1 03/12] Adding role pg_manage_vacuum_settings

This new role can SET and ALTER SYSTEM SET any GUC variable which
controls vacuum behavior or resource usage.
---
 doc/src/sgml/user-manag.sgml            | 11 +++-
 src/backend/utils/misc/guc.c            | 26 ++++----
 src/include/catalog/pg_authid.dat       |  5 ++
 src/include/utils/guc.h                 |  1 +
 src/test/regress/expected/guc_privs.out | 81 +++++++++++++++++++++++++
 src/test/regress/sql/guc_privs.sql      | 51 ++++++++++++++++
 6 files changed, 161 insertions(+), 14 deletions(-)

diff --git a/doc/src/sgml/user-manag.sgml b/doc/src/sgml/user-manag.sgml
index 25c97e4e5b..1d91d419e3 100644
--- a/doc/src/sgml/user-manag.sgml
+++ b/doc/src/sgml/user-manag.sgml
@@ -586,6 +586,10 @@ DROP ROLE doomed_role;
        <entry>pg_manage_host_resource_settings</entry>
        <entry>Allow changing settings which control host resource usage.</entry>
       </row>
+      <row>
+       <entry>pg_manage_vacuum_settings</entry>
+       <entry>Allow changing settings which control vacuum behavior.</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
@@ -631,9 +635,10 @@ DROP ROLE doomed_role;
   </para>
 
   <para>
-  The <literal>pg_manage_host_resource_settings</literal> role is intended to
-  allow administrators to have a trusted, but non-superuser, role to configure
-  memory, cpu, disk and filehandle usage.
+  The <literal>pg_manage_host_resource_settings</literal> and
+  <literal>pg_manage_vacuum_settings</literal> roles are intended to allow
+  administrators to have trusted, but non-superuser, roles to configure the
+  server.
   </para>
 
   <para>
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index be3fb60e9e..80265559b0 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -2634,7 +2634,7 @@ static struct config_int ConfigureNamesInt[] =
 		{"vacuum_cost_page_hit", PGC_USERSET, RESOURCES_VACUUM_DELAY,
 			gettext_noop("Vacuum cost for a page found in the buffer cache."),
 			NULL,
-			GUC_SUPERUSER_ONLY
+			GUC_MANAGE_VACUUM_SETTINGS
 		},
 		&VacuumCostPageHit,
 		1, 0, 10000,
@@ -2645,7 +2645,7 @@ static struct config_int ConfigureNamesInt[] =
 		{"vacuum_cost_page_miss", PGC_USERSET, RESOURCES_VACUUM_DELAY,
 			gettext_noop("Vacuum cost for a page not found in the buffer cache."),
 			NULL,
-			GUC_SUPERUSER_ONLY
+			GUC_MANAGE_VACUUM_SETTINGS
 		},
 		&VacuumCostPageMiss,
 		2, 0, 10000,
@@ -2656,7 +2656,7 @@ static struct config_int ConfigureNamesInt[] =
 		{"vacuum_cost_page_dirty", PGC_USERSET, RESOURCES_VACUUM_DELAY,
 			gettext_noop("Vacuum cost for a page dirtied by vacuum."),
 			NULL,
-			GUC_SUPERUSER_ONLY
+			GUC_MANAGE_VACUUM_SETTINGS
 		},
 		&VacuumCostPageDirty,
 		20, 0, 10000,
@@ -2667,7 +2667,7 @@ static struct config_int ConfigureNamesInt[] =
 		{"vacuum_cost_limit", PGC_USERSET, RESOURCES_VACUUM_DELAY,
 			gettext_noop("Vacuum cost amount available before napping."),
 			NULL,
-			GUC_SUPERUSER_ONLY
+			GUC_MANAGE_VACUUM_SETTINGS
 		},
 		&VacuumCostLimit,
 		200, 1, 10000,
@@ -2787,7 +2787,7 @@ static struct config_int ConfigureNamesInt[] =
 		{"vacuum_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Minimum age at which VACUUM should freeze a table row."),
 			NULL,
-			GUC_SUPERUSER_ONLY
+			GUC_MANAGE_VACUUM_SETTINGS
 		},
 		&vacuum_freeze_min_age,
 		50000000, 0, 1000000000,
@@ -2798,7 +2798,7 @@ static struct config_int ConfigureNamesInt[] =
 		{"vacuum_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Age at which VACUUM should scan whole table to freeze tuples."),
 			NULL,
-			GUC_SUPERUSER_ONLY
+			GUC_MANAGE_VACUUM_SETTINGS
 		},
 		&vacuum_freeze_table_age,
 		150000000, 0, 2000000000,
@@ -2809,7 +2809,7 @@ static struct config_int ConfigureNamesInt[] =
 		{"vacuum_multixact_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Minimum age at which VACUUM should freeze a MultiXactId in a table row."),
 			NULL,
-			GUC_SUPERUSER_ONLY
+			GUC_MANAGE_VACUUM_SETTINGS
 		},
 		&vacuum_multixact_freeze_min_age,
 		5000000, 0, 1000000000,
@@ -2820,7 +2820,7 @@ static struct config_int ConfigureNamesInt[] =
 		{"vacuum_multixact_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Multixact age at which VACUUM should scan whole table to freeze tuples."),
 			NULL,
-			GUC_SUPERUSER_ONLY
+			GUC_MANAGE_VACUUM_SETTINGS
 		},
 		&vacuum_multixact_freeze_table_age,
 		150000000, 0, 2000000000,
@@ -2841,7 +2841,7 @@ static struct config_int ConfigureNamesInt[] =
 		{"vacuum_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
 			NULL,
-			GUC_SUPERUSER_ONLY
+			GUC_MANAGE_VACUUM_SETTINGS
 		},
 		&vacuum_failsafe_age,
 		1600000000, 0, 2100000000,
@@ -2851,7 +2851,7 @@ static struct config_int ConfigureNamesInt[] =
 		{"vacuum_multixact_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Multixact age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
 			NULL,
-			GUC_SUPERUSER_ONLY
+			GUC_MANAGE_VACUUM_SETTINGS
 		},
 		&vacuum_multixact_failsafe_age,
 		1600000000, 0, 2100000000,
@@ -4012,7 +4012,7 @@ static struct config_real ConfigureNamesReal[] =
 		{"vacuum_cost_delay", PGC_USERSET, RESOURCES_VACUUM_DELAY,
 			gettext_noop("Vacuum cost delay in milliseconds."),
 			NULL,
-			GUC_SUPERUSER_ONLY,
+			GUC_MANAGE_VACUUM_SETTINGS,
 			GUC_UNIT_MS
 		},
 		&VacuumCostDelay,
@@ -7631,6 +7631,10 @@ role_has_privileges(Oid roleid, int privileges)
 	if ((privileges & GUC_SUPERUSER_ONLY) && !superuser_arg(roleid))
 		return false;
 
+	if ((privileges & GUC_MANAGE_VACUUM_SETTINGS) &&
+		! has_privs_of_role(roleid, ROLE_PG_MANAGE_VACUUM_SETTINGS))
+		return false;
+
 	return true;
 }
 
diff --git a/src/include/catalog/pg_authid.dat b/src/include/catalog/pg_authid.dat
index daf62c2061..cef9b7f015 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 => 't', rolconnlimit => '-1',
   rolpassword => '_null_', rolvaliduntil => '_null_' },
+{ oid => '9280', oid_symbol => 'ROLE_PG_MANAGE_VACUUM_SETTINGS',
+  rolname => 'pg_manage_vacuum_settings', rolsuper => 'f', rolinherit => 't',
+  rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
+  rolreplication => 'f', rolbypassrls => 't', rolconnlimit => '-1',
+  rolpassword => '_null_', rolvaliduntil => '_null_' },
 
 ]
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 7b0aac2216..8555e3f7b8 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -202,6 +202,7 @@ typedef enum
  * privilege bits required to modify a GUC variable
  */
 #define GUC_MANAGE_HOST_RESOURCE_SETTINGS	0x0001
+#define GUC_MANAGE_VACUUM_SETTINGS			0x0002
 
 /*
  * bit values in "flags" of a GUC variable
diff --git a/src/test/regress/expected/guc_privs.out b/src/test/regress/expected/guc_privs.out
index 34a0dcfc0b..6887a6664a 100644
--- a/src/test/regress/expected/guc_privs.out
+++ b/src/test/regress/expected/guc_privs.out
@@ -151,3 +151,84 @@ ALTER SYSTEM RESET backend_flush_after;  -- ok
 RESET statement_timeout;
 RESET SESSION AUTHORIZATION;
 DROP ROLE regress_host_resource_admin;
+-- Test role pg_manage_vacuum_settings
+-- Non-superuser with privileges to configure vacuum processes
+CREATE ROLE regress_vacuum_admin NOSUPERUSER;
+GRANT pg_manage_vacuum_settings TO regress_vacuum_admin;
+-- Perform all operations as user 'regress_vacuum_admin' --
+SET SESSION AUTHORIZATION regress_vacuum_admin;
+-- PGC_BACKEND
+SET ignore_system_indexes = OFF;  -- fail, cannot be set after connection start
+ERROR:  parameter "ignore_system_indexes" cannot be set after connection start
+RESET ignore_system_indexes;  -- fail, cannot be set after connection start
+ERROR:  parameter "ignore_system_indexes" cannot be set after connection start
+ALTER SYSTEM SET ignore_system_indexes = OFF;  -- fail, regress_vacuum_admin has insufficient privileges
+ERROR:  permission denied to set parameter "ignore_system_indexes"
+ALTER SYSTEM RESET ignore_system_indexes;  -- fail, regress_vacuum_admin has insufficient privileges
+ERROR:  permission denied to set parameter "ignore_system_indexes"
+-- PGC_INTERNAL
+SET block_size = 50;  -- fail, cannot be changed
+ERROR:  parameter "block_size" cannot be changed
+RESET block_size;  -- fail, cannot be changed
+ERROR:  parameter "block_size" cannot be changed
+ALTER SYSTEM SET block_size = 50;  -- fail, cannot be changed
+ERROR:  parameter "block_size" cannot be changed
+ALTER SYSTEM RESET block_size;  -- fail, cannot be changed
+ERROR:  parameter "block_size" cannot be changed
+-- PGC_POSTMASTER
+SET autovacuum_freeze_max_age = 1000050000;  -- fail, requires restart
+ERROR:  parameter "autovacuum_freeze_max_age" cannot be changed without restarting the server
+RESET autovacuum_freeze_max_age;  -- fail, requires restart
+ERROR:  parameter "autovacuum_freeze_max_age" cannot be changed without restarting the server
+ALTER SYSTEM SET autovacuum_freeze_max_age = 1000050000;  -- fail, regress_vacuum_admin has insufficient privileges
+ERROR:  permission denied to set parameter "autovacuum_freeze_max_age"
+ALTER SYSTEM RESET autovacuum_freeze_max_age;  -- fail, regress_vacuum_admin has insufficient privileges
+ERROR:  permission denied to set parameter "autovacuum_freeze_max_age"
+ALTER SYSTEM SET config_file = '/usr/local/data/postgresql.conf';  -- fail, cannot be changed
+ERROR:  parameter "config_file" cannot be changed
+ALTER SYSTEM RESET config_file;  -- fail, cannot be changed
+ERROR:  parameter "config_file" cannot be changed
+-- PGC_SIGHUP
+SET autovacuum = OFF;  -- fail, requires reload
+ERROR:  parameter "autovacuum" cannot be changed now
+RESET autovacuum;  -- fail, requires reload
+ERROR:  parameter "autovacuum" cannot be changed now
+ALTER SYSTEM SET autovacuum = OFF;  -- fail, regress_vacuum_admin has insufficient privileges
+ERROR:  permission denied to set parameter "autovacuum"
+ALTER SYSTEM RESET autovacuum;  -- fail, regress_vacuum_admin has insufficient privileges
+ERROR:  permission denied to set parameter "autovacuum"
+-- PGC_SUSET
+SET lc_messages = 'en_US.UTF-8';  -- fail, regress_vacuum_admin has insufficient privileges
+ERROR:  permission denied to set parameter "lc_messages"
+RESET lc_messages;  -- fail, regress_vacuum_admin has insufficient privileges
+ERROR:  permission denied to set parameter "lc_messages"
+ALTER SYSTEM SET lc_messages = 'en_US.UTF-8';  -- fail, regress_vacuum_admin has insufficient privileges
+ERROR:  permission denied to set parameter "lc_messages"
+ALTER SYSTEM RESET lc_messages;  -- fail, regress_vacuum_admin has insufficient privileges
+ERROR:  permission denied to set parameter "lc_messages"
+-- PGC_SU_BACKEND
+SET jit_debugging_support = OFF;  -- fail, cannot be set after connection start
+ERROR:  parameter "jit_debugging_support" cannot be set after connection start
+RESET jit_debugging_support;  -- fail, cannot be set after connection start
+ERROR:  parameter "jit_debugging_support" cannot be set after connection start
+ALTER SYSTEM SET jit_debugging_support = OFF;  -- fail, regress_vacuum_admin has insufficient privileges
+ERROR:  permission denied to set parameter "jit_debugging_support"
+ALTER SYSTEM RESET jit_debugging_support;  -- fail, regress_vacuum_admin has insufficient privileges
+ERROR:  permission denied to set parameter "jit_debugging_support"
+-- PGC_USERSET
+SET DateStyle = 'ISO, MDY';  -- ok
+RESET DateStyle;  -- ok
+ALTER SYSTEM SET DateStyle = 'ISO, MDY';  -- fail, regress_vacuum_admin has insufficient privileges
+ERROR:  permission denied to set parameter "datestyle"
+ALTER SYSTEM RESET DateStyle;  -- fail, regress_vacuum_admin has insufficient privileges
+ERROR:  permission denied to set parameter "datestyle"
+ALTER SYSTEM SET vacuum_failsafe_age = 50;  -- ok
+ALTER SYSTEM RESET vacuum_failsafe_age;  -- ok
+ALTER SYSTEM SET ssl_renegotiation_limit = 0;  -- fail, cannot be changed
+ERROR:  parameter "ssl_renegotiation_limit" cannot be changed
+ALTER SYSTEM RESET ssl_renegotiation_limit;  -- fail, cannot be changed
+ERROR:  parameter "ssl_renegotiation_limit" cannot be changed
+-- Finished testing role pg_manage_vacuum_settings
+RESET statement_timeout;
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_vacuum_admin;
diff --git a/src/test/regress/sql/guc_privs.sql b/src/test/regress/sql/guc_privs.sql
index a8a78e61de..6e7494cb3e 100644
--- a/src/test/regress/sql/guc_privs.sql
+++ b/src/test/regress/sql/guc_privs.sql
@@ -105,3 +105,54 @@ ALTER SYSTEM RESET backend_flush_after;  -- ok
 RESET statement_timeout;
 RESET SESSION AUTHORIZATION;
 DROP ROLE regress_host_resource_admin;
+-- Test role pg_manage_vacuum_settings
+-- Non-superuser with privileges to configure vacuum processes
+CREATE ROLE regress_vacuum_admin NOSUPERUSER;
+GRANT pg_manage_vacuum_settings TO regress_vacuum_admin;
+-- Perform all operations as user 'regress_vacuum_admin' --
+SET SESSION AUTHORIZATION regress_vacuum_admin;
+-- PGC_BACKEND
+SET ignore_system_indexes = OFF;  -- fail, cannot be set after connection start
+RESET ignore_system_indexes;  -- fail, cannot be set after connection start
+ALTER SYSTEM SET ignore_system_indexes = OFF;  -- fail, regress_vacuum_admin has insufficient privileges
+ALTER SYSTEM RESET ignore_system_indexes;  -- fail, regress_vacuum_admin has insufficient privileges
+-- PGC_INTERNAL
+SET block_size = 50;  -- fail, cannot be changed
+RESET block_size;  -- fail, cannot be changed
+ALTER SYSTEM SET block_size = 50;  -- fail, cannot be changed
+ALTER SYSTEM RESET block_size;  -- fail, cannot be changed
+-- PGC_POSTMASTER
+SET autovacuum_freeze_max_age = 1000050000;  -- fail, requires restart
+RESET autovacuum_freeze_max_age;  -- fail, requires restart
+ALTER SYSTEM SET autovacuum_freeze_max_age = 1000050000;  -- fail, regress_vacuum_admin has insufficient privileges
+ALTER SYSTEM RESET autovacuum_freeze_max_age;  -- fail, regress_vacuum_admin has insufficient privileges
+ALTER SYSTEM SET config_file = '/usr/local/data/postgresql.conf';  -- fail, cannot be changed
+ALTER SYSTEM RESET config_file;  -- fail, cannot be changed
+-- PGC_SIGHUP
+SET autovacuum = OFF;  -- fail, requires reload
+RESET autovacuum;  -- fail, requires reload
+ALTER SYSTEM SET autovacuum = OFF;  -- fail, regress_vacuum_admin has insufficient privileges
+ALTER SYSTEM RESET autovacuum;  -- fail, regress_vacuum_admin has insufficient privileges
+-- PGC_SUSET
+SET lc_messages = 'en_US.UTF-8';  -- fail, regress_vacuum_admin has insufficient privileges
+RESET lc_messages;  -- fail, regress_vacuum_admin has insufficient privileges
+ALTER SYSTEM SET lc_messages = 'en_US.UTF-8';  -- fail, regress_vacuum_admin has insufficient privileges
+ALTER SYSTEM RESET lc_messages;  -- fail, regress_vacuum_admin has insufficient privileges
+-- PGC_SU_BACKEND
+SET jit_debugging_support = OFF;  -- fail, cannot be set after connection start
+RESET jit_debugging_support;  -- fail, cannot be set after connection start
+ALTER SYSTEM SET jit_debugging_support = OFF;  -- fail, regress_vacuum_admin has insufficient privileges
+ALTER SYSTEM RESET jit_debugging_support;  -- fail, regress_vacuum_admin has insufficient privileges
+-- PGC_USERSET
+SET DateStyle = 'ISO, MDY';  -- ok
+RESET DateStyle;  -- ok
+ALTER SYSTEM SET DateStyle = 'ISO, MDY';  -- fail, regress_vacuum_admin has insufficient privileges
+ALTER SYSTEM RESET DateStyle;  -- fail, regress_vacuum_admin has insufficient privileges
+ALTER SYSTEM SET vacuum_failsafe_age = 50;  -- ok
+ALTER SYSTEM RESET vacuum_failsafe_age;  -- ok
+ALTER SYSTEM SET ssl_renegotiation_limit = 0;  -- fail, cannot be changed
+ALTER SYSTEM RESET ssl_renegotiation_limit;  -- fail, cannot be changed
+-- Finished testing role pg_manage_vacuum_settings
+RESET statement_timeout;
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_vacuum_admin;
-- 
2.21.1 (Apple Git-122.3)

