Add new predefined role pg_maintenance, which can issue VACUUM,
ANALYZE, CHECKPOINT.

Patch attached.

Regards,
        Jeff Davis

From 7777e615ad4b4a8c390a4b937dbb5721de5daad5 Mon Sep 17 00:00:00 2001
From: Jeff Davis <j...@j-davis.com>
Date: Sat, 23 Oct 2021 13:41:41 -0700
Subject: [PATCH] Predefined role pg_maintenance for VACUUM, ANALYZE,
 CHECKPOINT.

---
 doc/src/sgml/ref/analyze.sgml     | 14 +++++++-------
 doc/src/sgml/ref/checkpoint.sgml  |  3 ++-
 doc/src/sgml/ref/vacuum.sgml      | 14 +++++++-------
 src/backend/commands/vacuum.c     | 12 +++++++-----
 src/backend/tcop/utility.c        |  5 +++--
 src/include/catalog/catversion.h  |  2 +-
 src/include/catalog/pg_authid.dat |  5 +++++
 7 files changed, 32 insertions(+), 23 deletions(-)

diff --git a/doc/src/sgml/ref/analyze.sgml b/doc/src/sgml/ref/analyze.sgml
index c8fcebc1612..38e3d8916f0 100644
--- a/doc/src/sgml/ref/analyze.sgml
+++ b/doc/src/sgml/ref/analyze.sgml
@@ -148,13 +148,13 @@ 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
-   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> will skip over any tables that the calling user
-   does not have permission to analyze.
+   To analyze a table, one must ordinarily be the table's owner, a superuser,
+   or a member of the <literal>pg_maintenance</literal> 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> will skip over any tables that the
+   calling user does not have permission to analyze.
   </para>
 
   <para>
diff --git a/doc/src/sgml/ref/checkpoint.sgml b/doc/src/sgml/ref/checkpoint.sgml
index 2afee6d7b59..461ccd603e5 100644
--- a/doc/src/sgml/ref/checkpoint.sgml
+++ b/doc/src/sgml/ref/checkpoint.sgml
@@ -52,7 +52,8 @@ CHECKPOINT
   </para>
 
   <para>
-   Only superusers can call <command>CHECKPOINT</command>.
+   Only superusers or members of <literal>pg_maintenance</literal> can call
+   <command>CHECKPOINT</command>.
   </para>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml
index 3df32b58ee6..72d5b56feb5 100644
--- a/doc/src/sgml/ref/vacuum.sgml
+++ b/doc/src/sgml/ref/vacuum.sgml
@@ -356,13 +356,13 @@ 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
-    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> will skip over any tables that the calling user
-    does not have permission to vacuum.
+    To vacuum a table, one must ordinarily be the table's owner, a superuser,
+    or a member of the <literal>pg_maintenance</literal> 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> will skip over any tables that the
+    calling user does not have permission to vacuum.
    </para>
 
    <para>
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 5c4bc15b441..d22726ac006 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -32,6 +32,7 @@
 #include "access/transam.h"
 #include "access/xact.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_database.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_namespace.h"
@@ -565,15 +566,16 @@ vacuum_is_relation_owner(Oid relid, Form_pg_class reltuple, bits32 options)
 	/*
 	 * Check permissions.
 	 *
-	 * We allow the user to vacuum or analyze a table if he is superuser, the
-	 * table owner, or the database owner (but in the latter case, only if
-	 * it's not a shared relation).  pg_class_ownercheck includes the
-	 * superuser case.
+	 * We allow the user to vacuum or analyze a table if he is a member of
+	 * pg_maintenance, the table owner, or the database owner (but in the
+	 * latter case, only if it's not a shared relation).  pg_class_ownercheck
+	 * includes the superuser case.
 	 *
 	 * Note we choose to treat permissions failure as a WARNING and keep
 	 * trying to vacuum or analyze the rest of the DB --- is this appropriate?
 	 */
-	if (pg_class_ownercheck(relid, GetUserId()) ||
+	if (has_privs_of_role(GetUserId(), ROLE_PG_MAINTENANCE) ||
+		pg_class_ownercheck(relid, GetUserId()) ||
 		(pg_database_ownercheck(MyDatabaseId, GetUserId()) && !reltuple->relisshared))
 		return true;
 
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index bf085aa93b2..43607eb84af 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -24,6 +24,7 @@
 #include "catalog/catalog.h"
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
 #include "commands/alter.h"
@@ -939,10 +940,10 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 			break;
 
 		case T_CheckPointStmt:
-			if (!superuser())
+			if (!has_privs_of_role(GetUserId(), ROLE_PG_MAINTENANCE))
 				ereport(ERROR,
 						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-						 errmsg("must be superuser to do CHECKPOINT")));
+						 errmsg("must be member of pg_maintenance to do CHECKPOINT")));
 
 			RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_WAIT |
 							  (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 3253b8751b1..1bc331a9a39 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	202109101
+#define CATALOG_VERSION_NO	202110231
 
 #endif
diff --git a/src/include/catalog/pg_authid.dat b/src/include/catalog/pg_authid.dat
index 3da68016b61..886969dc646 100644
--- a/src/include/catalog/pg_authid.dat
+++ b/src/include/catalog/pg_authid.dat
@@ -79,5 +79,10 @@
   rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
   rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1',
   rolpassword => '_null_', rolvaliduntil => '_null_' },
+{ oid => '4544', oid_symbol => 'ROLE_PG_MAINTENANCE',
+  rolname => 'pg_maintenance', rolsuper => 'f', rolinherit => 't',
+  rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
+  rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1',
+  rolpassword => '_null_', rolvaliduntil => '_null_' },
 
 ]
-- 
2.17.1

Reply via email to