Skip to content

Commit 4441fc7

Browse files
committed
Provide non-superuser predefined roles for vacuum and analyze
This provides two new predefined roles: pg_vacuum_all_tables and pg_analyze_all_tables. Roles which have been granted these roles can perform vacuum or analyse respectively on any or all tables as if they were a superuser. This removes the need to grant superuser privilege to roles just so they can perform vacuum and/or analyze. Nathan Bossart Reviewed by: Bharath Rupireddy, Kyotaro Horiguchi, Stephen Frost, Robert Haas, Mark Dilger, Tom Lane, Corey Huinker, David G. Johnston, Michael Paquier. Discussion: https://postgr.es/m/20220722203735.GB3996698@nathanxps13
1 parent b5d6382 commit 4441fc7

File tree

8 files changed

+106
-7
lines changed

8 files changed

+106
-7
lines changed

doc/src/sgml/ref/analyze.sgml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,16 @@ ANALYZE [ VERBOSE ] [ <replaceable class="parameter">table_and_columns</replacea
148148
<title>Notes</title>
149149

150150
<para>
151-
To analyze a table, one must ordinarily be the table's owner or a
152-
superuser or have the <literal>ANALYZE</literal> privilege on the table.
151+
To analyze a table, one must ordinarily have the <literal>ANALYZE</literal>
152+
privilege on the table or be the table's owner, a superuser, or a role with
153+
privileges of the
154+
<link linkend="predefined-roles-table"><literal>pg_analyze_all_tables</literal></link>
155+
role.
153156
However, database owners are allowed to
154157
analyze all tables in their databases, except shared catalogs.
155158
(The restriction for shared catalogs means that a true database-wide
156-
<command>ANALYZE</command> can only be performed by a superuser.)
159+
<command>ANALYZE</command> can only be performed by superusers and roles
160+
with privileges of <literal>pg_analyze_all_tables</literal>.)
157161
<command>ANALYZE</command> will skip over any tables that the calling user
158162
does not have permission to analyze.
159163
</para>

doc/src/sgml/ref/vacuum.sgml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -356,12 +356,16 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="paramet
356356
<title>Notes</title>
357357

358358
<para>
359-
To vacuum a table, one must ordinarily be the table's owner or a
360-
superuser or have the <literal>VACUUM</literal> privilege on the table.
359+
To vacuum a table, one must ordinarily have the <literal>VACUUM</literal>
360+
privilege on the table or be the table's owner, a superuser, or a role with
361+
privileges of the
362+
<link linkend="predefined-roles-table"><literal>pg_vacuum_all_tables</literal></link>
363+
role.
361364
However, database owners are allowed to
362365
vacuum all tables in their databases, except shared catalogs.
363366
(The restriction for shared catalogs means that a true database-wide
364-
<command>VACUUM</command> can only be performed by a superuser.)
367+
<command>VACUUM</command> can only be performed by superusers and roles
368+
with privileges of <literal>pg_vacuum_all_tables</literal>.)
365369
<command>VACUUM</command> will skip over any tables that the calling user
366370
does not have permission to vacuum.
367371
</para>

doc/src/sgml/user-manag.sgml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,18 @@ DROP ROLE doomed_role;
635635
the <link linkend="sql-checkpoint"><command>CHECKPOINT</command></link>
636636
command.</entry>
637637
</row>
638+
<row>
639+
<entry>pg_vacuum_all_tables</entry>
640+
<entry>Allow executing the
641+
<link linkend="sql-vacuum"><command>VACUUM</command></link> command on
642+
all tables.</entry>
643+
</row>
644+
<row>
645+
<entry>pg_analyze_all_tables</entry>
646+
<entry>Allow executing the
647+
<link linkend="sql-analyze"><command>ANALYZE</command></link> command on
648+
all tables.</entry>
649+
</row>
638650
</tbody>
639651
</tgroup>
640652
</table>

src/backend/catalog/aclchk.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4202,6 +4202,26 @@ pg_class_aclmask_ext(Oid table_oid, Oid roleid, AclMode mask,
42024202
has_privs_of_role(roleid, ROLE_PG_WRITE_ALL_DATA))
42034203
result |= (mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE));
42044204

4205+
/*
4206+
* Check if ACL_VACUUM is being checked and, if so, and not already set as
4207+
* part of the result, then check if the user is a member of the
4208+
* pg_vacuum_all_tables role, which allows VACUUM on all relations.
4209+
*/
4210+
if (mask & ACL_VACUUM &&
4211+
!(result & ACL_VACUUM) &&
4212+
has_privs_of_role(roleid, ROLE_PG_VACUUM_ALL_TABLES))
4213+
result |= ACL_VACUUM;
4214+
4215+
/*
4216+
* Check if ACL_ANALYZE is being checked and, if so, and not already set as
4217+
* part of the result, then check if the user is a member of the
4218+
* pg_analyze_all_tables role, which allows ANALYZE on all relations.
4219+
*/
4220+
if (mask & ACL_ANALYZE &&
4221+
!(result & ACL_ANALYZE) &&
4222+
has_privs_of_role(roleid, ROLE_PG_ANALYZE_ALL_TABLES))
4223+
result |= ACL_ANALYZE;
4224+
42054225
return result;
42064226
}
42074227

src/include/catalog/catversion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,6 @@
5757
*/
5858

5959
/* yyyymmddN */
60-
#define CATALOG_VERSION_NO 202211241
60+
#define CATALOG_VERSION_NO 202211281
6161

6262
#endif

src/include/catalog/pg_authid.dat

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,5 +84,15 @@
8484
rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
8585
rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1',
8686
rolpassword => '_null_', rolvaliduntil => '_null_' },
87+
{ oid => '4549', oid_symbol => 'ROLE_PG_VACUUM_ALL_TABLES',
88+
rolname => 'pg_vacuum_all_tables', rolsuper => 'f', rolinherit => 't',
89+
rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
90+
rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1',
91+
rolpassword => '_null_', rolvaliduntil => '_null_' },
92+
{ oid => '4550', oid_symbol => 'ROLE_PG_ANALYZE_ALL_TABLES',
93+
rolname => 'pg_analyze_all_tables', rolsuper => 'f', rolinherit => 't',
94+
rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
95+
rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1',
96+
rolpassword => '_null_', rolvaliduntil => '_null_' },
8797

8898
]

src/test/regress/expected/privileges.out

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2854,6 +2854,9 @@ CREATE ROLE regress_no_priv;
28542854
CREATE ROLE regress_only_vacuum;
28552855
CREATE ROLE regress_only_analyze;
28562856
CREATE ROLE regress_both;
2857+
CREATE ROLE regress_only_vacuum_all IN ROLE pg_vacuum_all_tables;
2858+
CREATE ROLE regress_only_analyze_all IN ROLE pg_analyze_all_tables;
2859+
CREATE ROLE regress_both_all IN ROLE pg_vacuum_all_tables, pg_analyze_all_tables;
28572860
CREATE TABLE vacanalyze_test (a INT);
28582861
GRANT VACUUM ON vacanalyze_test TO regress_only_vacuum, regress_both;
28592862
GRANT ANALYZE ON vacanalyze_test TO regress_only_analyze, regress_both;
@@ -2884,8 +2887,30 @@ VACUUM vacanalyze_test;
28842887
ANALYZE vacanalyze_test;
28852888
VACUUM (ANALYZE) vacanalyze_test;
28862889
RESET ROLE;
2890+
SET ROLE regress_only_vacuum_all;
2891+
VACUUM vacanalyze_test;
2892+
ANALYZE vacanalyze_test;
2893+
WARNING: permission denied to analyze "vacanalyze_test", skipping it
2894+
VACUUM (ANALYZE) vacanalyze_test;
2895+
WARNING: permission denied to analyze "vacanalyze_test", skipping it
2896+
RESET ROLE;
2897+
SET ROLE regress_only_analyze_all;
2898+
VACUUM vacanalyze_test;
2899+
WARNING: permission denied to vacuum "vacanalyze_test", skipping it
2900+
ANALYZE vacanalyze_test;
2901+
VACUUM (ANALYZE) vacanalyze_test;
2902+
WARNING: permission denied to vacuum "vacanalyze_test", skipping it
2903+
RESET ROLE;
2904+
SET ROLE regress_both_all;
2905+
VACUUM vacanalyze_test;
2906+
ANALYZE vacanalyze_test;
2907+
VACUUM (ANALYZE) vacanalyze_test;
2908+
RESET ROLE;
28872909
DROP TABLE vacanalyze_test;
28882910
DROP ROLE regress_no_priv;
28892911
DROP ROLE regress_only_vacuum;
28902912
DROP ROLE regress_only_analyze;
28912913
DROP ROLE regress_both;
2914+
DROP ROLE regress_only_vacuum_all;
2915+
DROP ROLE regress_only_analyze_all;
2916+
DROP ROLE regress_both_all;

src/test/regress/sql/privileges.sql

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1858,6 +1858,9 @@ CREATE ROLE regress_no_priv;
18581858
CREATE ROLE regress_only_vacuum;
18591859
CREATE ROLE regress_only_analyze;
18601860
CREATE ROLE regress_both;
1861+
CREATE ROLE regress_only_vacuum_all IN ROLE pg_vacuum_all_tables;
1862+
CREATE ROLE regress_only_analyze_all IN ROLE pg_analyze_all_tables;
1863+
CREATE ROLE regress_both_all IN ROLE pg_vacuum_all_tables, pg_analyze_all_tables;
18611864

18621865
CREATE TABLE vacanalyze_test (a INT);
18631866
GRANT VACUUM ON vacanalyze_test TO regress_only_vacuum, regress_both;
@@ -1887,8 +1890,29 @@ ANALYZE vacanalyze_test;
18871890
VACUUM (ANALYZE) vacanalyze_test;
18881891
RESET ROLE;
18891892

1893+
SET ROLE regress_only_vacuum_all;
1894+
VACUUM vacanalyze_test;
1895+
ANALYZE vacanalyze_test;
1896+
VACUUM (ANALYZE) vacanalyze_test;
1897+
RESET ROLE;
1898+
1899+
SET ROLE regress_only_analyze_all;
1900+
VACUUM vacanalyze_test;
1901+
ANALYZE vacanalyze_test;
1902+
VACUUM (ANALYZE) vacanalyze_test;
1903+
RESET ROLE;
1904+
1905+
SET ROLE regress_both_all;
1906+
VACUUM vacanalyze_test;
1907+
ANALYZE vacanalyze_test;
1908+
VACUUM (ANALYZE) vacanalyze_test;
1909+
RESET ROLE;
1910+
18901911
DROP TABLE vacanalyze_test;
18911912
DROP ROLE regress_no_priv;
18921913
DROP ROLE regress_only_vacuum;
18931914
DROP ROLE regress_only_analyze;
18941915
DROP ROLE regress_both;
1916+
DROP ROLE regress_only_vacuum_all;
1917+
DROP ROLE regress_only_analyze_all;
1918+
DROP ROLE regress_both_all;

0 commit comments

Comments
 (0)