Skip to content

Commit

Permalink
Add grantable MAINTAIN privilege and pg_maintain role.
Browse files Browse the repository at this point in the history
Allows VACUUM, ANALYZE, REINDEX, REFRESH MATERIALIZED VIEW, CLUSTER,
and LOCK TABLE.

Effectively reverts 4441fc7. Instead of creating separate
privileges for VACUUM, ANALYZE, and other maintenance commands, group
them together under a single MAINTAIN privilege.

Author: Nathan Bossart
Discussion: https://postgr.es/m/20221212210136.GA449764@nathanxps13
Discussion: https://postgr.es/m/45224.1670476523@sss.pgh.pa.us
  • Loading branch information
jeff-davis committed Dec 14, 2022
1 parent c6f6646 commit 60684dd
Show file tree
Hide file tree
Showing 36 changed files with 340 additions and 354 deletions.
42 changes: 15 additions & 27 deletions doc/src/sgml/ddl.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -1692,8 +1692,7 @@ ALTER TABLE products RENAME TO items;
<literal>TRUNCATE</literal>, <literal>REFERENCES</literal>, <literal>TRIGGER</literal>,
<literal>CREATE</literal>, <literal>CONNECT</literal>, <literal>TEMPORARY</literal>,
<literal>EXECUTE</literal>, <literal>USAGE</literal>, <literal>SET</literal>,
<literal>ALTER SYSTEM</literal>, <literal>VACUUM</literal>, and
<literal>ANALYZE</literal>.
<literal>ALTER SYSTEM</literal>, and <literal>MAINTAIN</literal>.
The privileges applicable to a particular
object vary depending on the object's type (table, function, etc.).
More detail about the meanings of these privileges appears below.
Expand Down Expand Up @@ -1985,19 +1984,13 @@ REVOKE ALL ON accounts FROM PUBLIC;
</varlistentry>

<varlistentry>
<term><literal>VACUUM</literal></term>
<term><literal>MAINTAIN</literal></term>
<listitem>
<para>
Allows <command>VACUUM</command> on a relation.
</para>
</listitem>
</varlistentry>

<varlistentry>
<term><literal>ANALYZE</literal></term>
<listitem>
<para>
Allows <command>ANALYZE</command> on a relation.
Allows <command>VACUUM</command>, <command>ANALYZE</command>,
<command>CLUSTER</command>, <command>REFRESH MATERIALIZED VIEW</command>,
<command>REINDEX</command>, and <command>LOCK TABLE</command> on a
relation.
</para>
</listitem>
</varlistentry>
Expand Down Expand Up @@ -2151,13 +2144,8 @@ REVOKE ALL ON accounts FROM PUBLIC;
<entry><literal>PARAMETER</literal></entry>
</row>
<row>
<entry><literal>VACUUM</literal></entry>
<entry><literal>v</literal></entry>
<entry><literal>TABLE</literal></entry>
</row>
<row>
<entry><literal>ANALYZE</literal></entry>
<entry><literal>z</literal></entry>
<entry><literal>MAINTAIN</literal></entry>
<entry><literal>m</literal></entry>
<entry><literal>TABLE</literal></entry>
</row>
</tbody>
Expand Down Expand Up @@ -2250,7 +2238,7 @@ REVOKE ALL ON accounts FROM PUBLIC;
</row>
<row>
<entry><literal>TABLE</literal> (and table-like objects)</entry>
<entry><literal>arwdDxtvz</literal></entry>
<entry><literal>arwdDxtm</literal></entry>
<entry>none</entry>
<entry><literal>\dp</literal></entry>
</row>
Expand Down Expand Up @@ -2308,12 +2296,12 @@ GRANT SELECT (col1), UPDATE (col1) ON mytable TO miriam_rw;
would show:
<programlisting>
=&gt; \dp mytable
Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies
--------+---------+-------+-------------------------+-----------------------+----------
public | mytable | table | miriam=arwdDxtvz/miriam+| col1: +|
| | | =r/miriam +| miriam_rw=rw/miriam |
| | | admin=arw/miriam | |
Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies
--------+---------+-------+------------------------+-----------------------+----------
public | mytable | table | miriam=arwdDxtm/miriam+| col1: +|
| | | =r/miriam +| miriam_rw=rw/miriam |
| | | admin=arw/miriam | |
(1 row)
</programlisting>
</para>
Expand Down
3 changes: 1 addition & 2 deletions doc/src/sgml/func.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -22995,8 +22995,7 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute');
are <literal>SELECT</literal>, <literal>INSERT</literal>,
<literal>UPDATE</literal>, <literal>DELETE</literal>,
<literal>TRUNCATE</literal>, <literal>REFERENCES</literal>,
<literal>TRIGGER</literal>, <literal>VACUUM</literal> and
<literal>ANALYZE</literal>.
<literal>TRIGGER</literal>, and <literal>MAINTAIN</literal>.
</para></entry>
</row>

Expand Down
4 changes: 2 additions & 2 deletions doc/src/sgml/ref/alter_default_privileges.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ ALTER DEFAULT PRIVILEGES

<phrase>where <replaceable class="parameter">abbreviated_grant_or_revoke</replaceable> is one of:</phrase>

GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | VACUUM | ANALYZE }
GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | MAINTAIN }
[, ...] | ALL [ PRIVILEGES ] }
ON TABLES
TO { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
Expand All @@ -51,7 +51,7 @@ GRANT { USAGE | CREATE | ALL [ PRIVILEGES ] }
TO { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]

REVOKE [ GRANT OPTION FOR ]
{ { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | VACUUM | ANALYZE }
{ { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | MAINTAIN }
[, ...] | ALL [ PRIVILEGES ] }
ON TABLES
FROM { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...]
Expand Down
9 changes: 4 additions & 5 deletions doc/src/sgml/ref/analyze.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -148,16 +148,15 @@ ANALYZE [ VERBOSE ] [ <replaceable class="parameter">table_and_columns</replacea
<title>Notes</title>

<para>
To analyze a table, one must ordinarily have the <literal>ANALYZE</literal>
To analyze a table, one must ordinarily have the <literal>MAINTAIN</literal>
privilege on the table or be the table's owner, a superuser, or a role with
privileges of the
<link linkend="predefined-roles-table"><literal>pg_analyze_all_tables</literal></link>
role.
However, database owners are allowed to
<link linkend="predefined-roles-table"><literal>pg_maintain</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 superusers and roles
with privileges of <literal>pg_analyze_all_tables</literal>.)
with privileges of <literal>pg_maintain</literal>.)
<command>ANALYZE</command> will skip over any tables that the calling user
does not have permission to analyze.
</para>
Expand Down
8 changes: 5 additions & 3 deletions doc/src/sgml/ref/cluster.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,11 @@ CLUSTER [VERBOSE]
<para>
<command>CLUSTER</command> without any parameter reclusters all the
previously-clustered tables in the current database that the calling user
owns, or all such tables if called by a superuser. This
form of <command>CLUSTER</command> cannot be executed inside a transaction
block.
owns or has the <literal>MAINTAIN</literal> privilege for, or all such tables
if called by a superuser or a role with privileges of the
<link linkend="predefined-roles-table"><literal>pg_maintain</literal></link>
role. This form of <command>CLUSTER</command> cannot be
executed inside a transaction block.
</para>

<para>
Expand Down
5 changes: 2 additions & 3 deletions doc/src/sgml/ref/grant.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ PostgreSQL documentation

<refsynopsisdiv>
<synopsis>
GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | VACUUM | ANALYZE }
GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | MAINTAIN }
[, ...] | ALL [ PRIVILEGES ] }
ON { [ TABLE ] <replaceable class="parameter">table_name</replaceable> [, ...]
| ALL TABLES IN SCHEMA <replaceable class="parameter">schema_name</replaceable> [, ...] }
Expand Down Expand Up @@ -193,8 +193,7 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
<term><literal>USAGE</literal></term>
<term><literal>SET</literal></term>
<term><literal>ALTER SYSTEM</literal></term>
<term><literal>VACUUM</literal></term>
<term><literal>ANALYZE</literal></term>
<term><literal>MAINTAIN</literal></term>
<listitem>
<para>
Specific types of privileges, as defined in <xref linkend="ddl-priv"/>.
Expand Down
16 changes: 11 additions & 5 deletions doc/src/sgml/ref/lock.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,17 @@ LOCK [ TABLE ] [ ONLY ] <replaceable class="parameter">name</replaceable> [ * ]
<title>Notes</title>

<para>
<literal>LOCK TABLE ... IN ACCESS SHARE MODE</literal> requires <literal>SELECT</literal>
privileges on the target table. <literal>LOCK TABLE ... IN ROW EXCLUSIVE
MODE</literal> requires <literal>INSERT</literal>, <literal>UPDATE</literal>, <literal>DELETE</literal>,
or <literal>TRUNCATE</literal> privileges on the target table. All other forms of
<command>LOCK</command> require table-level <literal>UPDATE</literal>, <literal>DELETE</literal>,
To lock a table, one must ordinarily have the <literal>MAINTAIN</literal>
privilege on the table or be the table's owner, a superuser, or a role
with privileges of the
<link linkend="predefined-roles-table"><literal>pg_maintain</literal></link>
role. <literal>LOCK TABLE ... IN ACCESS SHARE MODE</literal> is allowed
with <literal>SELECT</literal> privileges on the target
table. <literal>LOCK TABLE ... IN ROW EXCLUSIVE MODE</literal> is allowed
with <literal>INSERT</literal>, <literal>UPDATE</literal>, <literal>DELETE</literal>,
or <literal>TRUNCATE</literal> privileges on the target table. All other
forms of <command>LOCK</command> are allowed with
table-level <literal>UPDATE</literal>, <literal>DELETE</literal>,
or <literal>TRUNCATE</literal> privileges.
</para>

Expand Down
5 changes: 4 additions & 1 deletion doc/src/sgml/ref/refresh_materialized_view.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ REFRESH MATERIALIZED VIEW [ CONCURRENTLY ] <replaceable class="parameter">name</
<para>
<command>REFRESH MATERIALIZED VIEW</command> completely replaces the
contents of a materialized view. To execute this command you must be the
owner of the materialized view. The old contents are discarded. If
owner of the materialized view, have privileges of the
<link linkend="predefined-roles-table"><literal>pg_maintain</literal></link>
role, or have the <literal>MAINTAIN</literal>
privilege on the materialized view. The old contents are discarded. If
<literal>WITH DATA</literal> is specified (or defaults) the backing query
is executed to provide the new data, and the materialized view is left in a
scannable state. If <literal>WITH NO DATA</literal> is specified no new
Expand Down
13 changes: 9 additions & 4 deletions doc/src/sgml/ref/reindex.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -293,15 +293,20 @@ REINDEX [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] { DA

<para>
Reindexing a single index or table requires being the owner of that
index or table. Reindexing a schema or database requires being the
owner of that schema or database. Note specifically that it's thus
index or table, having privileges of the
<link linkend="predefined-roles-table"><literal>pg_maintain</literal></link>
role, or having the <literal>MAINTAIN</literal> privilege on the
table. Reindexing a schema or database requires being the
owner of that schema or database or having privileges of the
<literal>pg_maintain</literal> role. Note specifically that it's thus
possible for non-superusers to rebuild indexes of tables owned by
other users. However, as a special exception, when
<command>REINDEX DATABASE</command>, <command>REINDEX SCHEMA</command>
or <command>REINDEX SYSTEM</command> is issued by a non-superuser,
indexes on shared catalogs will be skipped unless the user owns the
catalog (which typically won't be the case). Of course, superusers
can always reindex anything.
catalog (which typically won't be the case), has privileges of the
<literal>pg_maintain</literal> role, or has the <literal>MAINTAIN</literal>
privilege on the catalog. Of course, superusers can always reindex anything.
</para>

<para>
Expand Down
2 changes: 1 addition & 1 deletion doc/src/sgml/ref/revoke.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
REVOKE [ GRANT OPTION FOR ]
{ { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | VACUUM | ANALYZE }
{ { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | MAINTAIN }
[, ...] | ALL [ PRIVILEGES ] }
ON { [ TABLE ] <replaceable class="parameter">table_name</replaceable> [, ...]
| ALL TABLES IN SCHEMA <replaceable>schema_name</replaceable> [, ...] }
Expand Down
9 changes: 4 additions & 5 deletions doc/src/sgml/ref/vacuum.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -356,16 +356,15 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="paramet
<title>Notes</title>

<para>
To vacuum a table, one must ordinarily have the <literal>VACUUM</literal>
To vacuum a table, one must ordinarily have the <literal>MAINTAIN</literal>
privilege on the table or be the table's owner, a superuser, or a role with
privileges of the
<link linkend="predefined-roles-table"><literal>pg_vacuum_all_tables</literal></link>
role.
However, database owners are allowed to
<link linkend="predefined-roles-table"><literal>pg_maintain</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 superusers and roles
with privileges of <literal>pg_vacuum_all_tables</literal>.)
with privileges of <literal>pg_maintain</literal>.)
<command>VACUUM</command> will skip over any tables that the calling user
does not have permission to vacuum.
</para>
Expand Down
19 changes: 9 additions & 10 deletions doc/src/sgml/user-manag.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -636,16 +636,15 @@ DROP ROLE doomed_role;
command.</entry>
</row>
<row>
<entry>pg_vacuum_all_tables</entry>
<entry>Allow executing the
<link linkend="sql-vacuum"><command>VACUUM</command></link> command on
all tables.</entry>
</row>
<row>
<entry>pg_analyze_all_tables</entry>
<entry>Allow executing the
<link linkend="sql-analyze"><command>ANALYZE</command></link> command on
all tables.</entry>
<entry>pg_maintain</entry>
<entry>Allow executing
<link linkend="sql-vacuum"><command>VACUUM</command></link>,
<link linkend="sql-analyze"><command>ANALYZE</command></link>,
<link linkend="sql-cluster"><command>CLUSTER</command></link>,
<link linkend="sql-refreshmaterializedview"><command>REFRESH MATERIALIZED VIEW</command></link>,
<link linkend="sql-reindex"><command>REINDEX</command></link>,
and <link linkend="sql-lock"><command>LOCK TABLE</command></link> on all
relations.</entry>
</row>
</tbody>
</tgroup>
Expand Down
35 changes: 11 additions & 24 deletions src/backend/catalog/aclchk.c
Original file line number Diff line number Diff line change
Expand Up @@ -2618,10 +2618,8 @@ string_to_privilege(const char *privname)
return ACL_SET;
if (strcmp(privname, "alter system") == 0)
return ACL_ALTER_SYSTEM;
if (strcmp(privname, "vacuum") == 0)
return ACL_VACUUM;
if (strcmp(privname, "analyze") == 0)
return ACL_ANALYZE;
if (strcmp(privname, "maintain") == 0)
return ACL_MAINTAIN;
if (strcmp(privname, "rule") == 0)
return 0; /* ignore old RULE privileges */
ereport(ERROR,
Expand Down Expand Up @@ -2663,10 +2661,8 @@ privilege_to_string(AclMode privilege)
return "SET";
case ACL_ALTER_SYSTEM:
return "ALTER SYSTEM";
case ACL_VACUUM:
return "VACUUM";
case ACL_ANALYZE:
return "ANALYZE";
case ACL_MAINTAIN:
return "MAINTAIN";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
Expand Down Expand Up @@ -3401,24 +3397,15 @@ pg_class_aclmask_ext(Oid table_oid, Oid roleid, AclMode mask,
result |= (mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE));

/*
* Check if ACL_VACUUM is being checked and, if so, and not already set as
* Check if ACL_MAINTAIN is being checked and, if so, and not already set as
* part of the result, then check if the user is a member of the
* pg_vacuum_all_tables role, which allows VACUUM on all relations.
* pg_maintain role, which allows VACUUM, ANALYZE, CLUSTER, REFRESH
* MATERIALIZED VIEW, and REINDEX on all relations.
*/
if (mask & ACL_VACUUM &&
!(result & ACL_VACUUM) &&
has_privs_of_role(roleid, ROLE_PG_VACUUM_ALL_TABLES))
result |= ACL_VACUUM;

/*
* Check if ACL_ANALYZE is being checked and, if so, and not already set as
* part of the result, then check if the user is a member of the
* pg_analyze_all_tables role, which allows ANALYZE on all relations.
*/
if (mask & ACL_ANALYZE &&
!(result & ACL_ANALYZE) &&
has_privs_of_role(roleid, ROLE_PG_ANALYZE_ALL_TABLES))
result |= ACL_ANALYZE;
if (mask & ACL_MAINTAIN &&
!(result & ACL_MAINTAIN) &&
has_privs_of_role(roleid, ROLE_PG_MAINTAIN))
result |= ACL_MAINTAIN;

return result;
}
Expand Down
2 changes: 1 addition & 1 deletion src/backend/commands/analyze.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ analyze_rel(Oid relid, RangeVar *relation,
*/
if (!vacuum_is_permitted_for_relation(RelationGetRelid(onerel),
onerel->rd_rel,
VACOPT_ANALYZE))
params->options & VACOPT_ANALYZE))
{
relation_close(onerel, ShareUpdateExclusiveLock);
return;
Expand Down
Loading

0 comments on commit 60684dd

Please sign in to comment.