Revert MAINTAIN privilege and pg_maintain predefined role.

This reverts the following commits: 4dbdb82513, c2122aae63,
5b1a879943, 9e1e9d6560, ff9618e82a, 60684dd834, 4441fc704d,
and b5d6382496.  A role with the MAINTAIN privilege may be able to
use search_path tricks to escalate privileges to the table owner.
Unfortunately, it is too late in the v16 development cycle to apply
the proposed fix, i.e., restricting search_path when running
maintenance commands.

Bumps catversion.

Reviewed-by: Jeff Davis
Discussion: https://postgr.es/m/E1q7j7Y-000z1H-Hr%40gemulon.postgresql.org
Backpatch-through: 16
This commit is contained in:
Nathan Bossart 2023-07-07 11:25:13 -07:00
parent ec99d6e9c8
commit 151c22deee
41 changed files with 179 additions and 445 deletions

View File

@ -1718,8 +1718,8 @@ ALTER TABLE products RENAME TO items;
<literal>INSERT</literal>, <literal>UPDATE</literal>, <literal>DELETE</literal>, <literal>INSERT</literal>, <literal>UPDATE</literal>, <literal>DELETE</literal>,
<literal>TRUNCATE</literal>, <literal>REFERENCES</literal>, <literal>TRIGGER</literal>, <literal>TRUNCATE</literal>, <literal>REFERENCES</literal>, <literal>TRIGGER</literal>,
<literal>CREATE</literal>, <literal>CONNECT</literal>, <literal>TEMPORARY</literal>, <literal>CREATE</literal>, <literal>CONNECT</literal>, <literal>TEMPORARY</literal>,
<literal>EXECUTE</literal>, <literal>USAGE</literal>, <literal>SET</literal>, <literal>EXECUTE</literal>, <literal>USAGE</literal>, <literal>SET</literal>
<literal>ALTER SYSTEM</literal>, and <literal>MAINTAIN</literal>. and <literal>ALTER SYSTEM</literal>.
The privileges applicable to a particular The privileges applicable to a particular
object vary depending on the object's type (table, function, etc.). object vary depending on the object's type (table, function, etc.).
More detail about the meanings of these privileges appears below. More detail about the meanings of these privileges appears below.
@ -2010,19 +2010,7 @@ REVOKE ALL ON accounts FROM PUBLIC;
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
</variablelist>
<varlistentry id="ddl-priv-maintain">
<term><literal>MAINTAIN</literal></term>
<listitem>
<para>
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>
</variablelist>
The privileges required by other commands are listed on the The privileges required by other commands are listed on the
reference page of the respective command. reference page of the respective command.
@ -2171,11 +2159,6 @@ REVOKE ALL ON accounts FROM PUBLIC;
<entry><literal>A</literal></entry> <entry><literal>A</literal></entry>
<entry><literal>PARAMETER</literal></entry> <entry><literal>PARAMETER</literal></entry>
</row> </row>
<row>
<entry><literal>MAINTAIN</literal></entry>
<entry><literal>m</literal></entry>
<entry><literal>TABLE</literal></entry>
</row>
</tbody> </tbody>
</tgroup> </tgroup>
</table> </table>
@ -2266,7 +2249,7 @@ REVOKE ALL ON accounts FROM PUBLIC;
</row> </row>
<row> <row>
<entry><literal>TABLE</literal> (and table-like objects)</entry> <entry><literal>TABLE</literal> (and table-like objects)</entry>
<entry><literal>arwdDxtm</literal></entry> <entry><literal>arwdDxt</literal></entry>
<entry>none</entry> <entry>none</entry>
<entry><literal>\dp</literal></entry> <entry><literal>\dp</literal></entry>
</row> </row>
@ -2325,11 +2308,11 @@ GRANT SELECT (col1), UPDATE (col1) ON mytable TO miriam_rw;
<programlisting> <programlisting>
=&gt; \dp mytable =&gt; \dp mytable
Access privileges Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies Schema | Name | Type | Access privileges | Column privileges | Policies
--------+---------+-------+------------------------+-----------------------+---------- --------+---------+-------+-----------------------+-----------------------+----------
public | mytable | table | miriam=arwdDxtm/miriam+| col1: +| public | mytable | table | miriam=arwdDxt/miriam+| col1: +|
| | | =r/miriam +| miriam_rw=rw/miriam | | | | =r/miriam +| miriam_rw=rw/miriam |
| | | admin=arw/miriam | | | | | admin=arw/miriam | |
(1 row) (1 row)
</programlisting> </programlisting>
</para> </para>

View File

@ -23545,7 +23545,7 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute');
are <literal>SELECT</literal>, <literal>INSERT</literal>, are <literal>SELECT</literal>, <literal>INSERT</literal>,
<literal>UPDATE</literal>, <literal>DELETE</literal>, <literal>UPDATE</literal>, <literal>DELETE</literal>,
<literal>TRUNCATE</literal>, <literal>REFERENCES</literal>, <literal>TRUNCATE</literal>, <literal>REFERENCES</literal>,
<literal>TRIGGER</literal>, and <literal>MAINTAIN</literal>. and <literal>TRIGGER</literal>.
</para></entry> </para></entry>
</row> </row>

View File

@ -28,7 +28,7 @@ ALTER DEFAULT PRIVILEGES
<phrase>where <replaceable class="parameter">abbreviated_grant_or_revoke</replaceable> is one of:</phrase> <phrase>where <replaceable class="parameter">abbreviated_grant_or_revoke</replaceable> is one of:</phrase>
GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | MAINTAIN } GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
[, ...] | ALL [ PRIVILEGES ] } [, ...] | ALL [ PRIVILEGES ] }
ON TABLES ON TABLES
TO { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ] TO { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
@ -51,7 +51,7 @@ GRANT { USAGE | CREATE | ALL [ PRIVILEGES ] }
TO { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ] TO { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
REVOKE [ GRANT OPTION FOR ] REVOKE [ GRANT OPTION FOR ]
{ { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | MAINTAIN } { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
[, ...] | ALL [ PRIVILEGES ] } [, ...] | ALL [ PRIVILEGES ] }
ON TABLES ON TABLES
FROM { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...] FROM { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...]

View File

@ -182,9 +182,11 @@ ANALYZE [ VERBOSE ] [ <replaceable class="parameter">table_and_columns</replacea
<title>Notes</title> <title>Notes</title>
<para> <para>
To analyze a table, one must ordinarily have the <literal>MAINTAIN</literal> To analyze a table, one must ordinarily be the table's owner or a
privilege on the table. However, database owners are allowed to superuser. However, database owners are allowed to
analyze all tables in their databases, except shared catalogs. 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 <command>ANALYZE</command> will skip over any tables that the calling user
does not have permission to analyze. does not have permission to analyze.
</para> </para>

View File

@ -70,8 +70,9 @@ CLUSTER [VERBOSE]
<command>CLUSTER</command> without a <command>CLUSTER</command> without a
<replaceable class="parameter">table_name</replaceable> reclusters all the <replaceable class="parameter">table_name</replaceable> reclusters all the
previously-clustered tables in the current database that the calling user previously-clustered tables in the current database that the calling user
has privileges for. This form of <command>CLUSTER</command> cannot be owns, or all such tables if called by a superuser. This
executed inside a transaction block. form of <command>CLUSTER</command> cannot be executed inside a transaction
block.
</para> </para>
<para> <para>
@ -132,11 +133,6 @@ CLUSTER [VERBOSE]
<refsect1> <refsect1>
<title>Notes</title> <title>Notes</title>
<para>
To cluster a table, one must have the <literal>MAINTAIN</literal> privilege
on the table.
</para>
<para> <para>
In cases where you are accessing single rows randomly In cases where you are accessing single rows randomly
within a table, the actual order of the data in the within a table, the actual order of the data in the

View File

@ -21,7 +21,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | MAINTAIN } GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
[, ...] | ALL [ PRIVILEGES ] } [, ...] | ALL [ PRIVILEGES ] }
ON { [ TABLE ] <replaceable class="parameter">table_name</replaceable> [, ...] ON { [ TABLE ] <replaceable class="parameter">table_name</replaceable> [, ...]
| ALL TABLES IN SCHEMA <replaceable class="parameter">schema_name</replaceable> [, ...] } | ALL TABLES IN SCHEMA <replaceable class="parameter">schema_name</replaceable> [, ...] }
@ -193,7 +193,6 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
<term><literal>USAGE</literal></term> <term><literal>USAGE</literal></term>
<term><literal>SET</literal></term> <term><literal>SET</literal></term>
<term><literal>ALTER SYSTEM</literal></term> <term><literal>ALTER SYSTEM</literal></term>
<term><literal>MAINTAIN</literal></term>
<listitem> <listitem>
<para> <para>
Specific types of privileges, as defined in <xref linkend="ddl-priv"/>. Specific types of privileges, as defined in <xref linkend="ddl-priv"/>.

View File

@ -166,8 +166,8 @@ LOCK [ TABLE ] [ ONLY ] <replaceable class="parameter">name</replaceable> [ * ]
<para> <para>
To lock a table, the user must have the right privilege for the specified To lock a table, the user must have the right privilege for the specified
<replaceable class="parameter">lockmode</replaceable>. <replaceable class="parameter">lockmode</replaceable>, or be the table's
If the user has <literal>MAINTAIN</literal>, owner or a superuser. If the user has
<literal>UPDATE</literal>, <literal>DELETE</literal>, or <literal>UPDATE</literal>, <literal>DELETE</literal>, or
<literal>TRUNCATE</literal> privileges on the table, any <replaceable <literal>TRUNCATE</literal> privileges on the table, any <replaceable
class="parameter">lockmode</replaceable> is permitted. If the user has class="parameter">lockmode</replaceable> is permitted. If the user has

View File

@ -31,9 +31,8 @@ REFRESH MATERIALIZED VIEW [ CONCURRENTLY ] <replaceable class="parameter">name</
<para> <para>
<command>REFRESH MATERIALIZED VIEW</command> completely replaces the <command>REFRESH MATERIALIZED VIEW</command> completely replaces the
contents of a materialized view. To execute this command you must have the contents of a materialized view. To execute this command you must be the
<literal>MAINTAIN</literal> owner of the materialized view. The old contents are discarded. If
privilege on the materialized view. The old contents are discarded. If
<literal>WITH DATA</literal> is specified (or defaults) the backing query <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 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 scannable state. If <literal>WITH NO DATA</literal> is specified no new

View File

@ -292,21 +292,16 @@ REINDEX [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] { DA
</para> </para>
<para> <para>
Reindexing a single index or table requires Reindexing a single index or table requires being the owner of that
having the <literal>MAINTAIN</literal> privilege on the index or table. Reindexing a schema or database requires being the
table. Note that while <command>REINDEX</command> on a partitioned index or owner of that schema or database. Note specifically that it's thus
table requires having the <literal>MAINTAIN</literal> privilege on the
partitioned table, such commands skip the privilege checks when processing
the individual partitions. Reindexing a schema or database requires being the
owner of that schema or database or having privileges of the
<link linkend="predefined-roles-table"><literal>pg_maintain</literal></link>
role. Note specifically that it's thus
possible for non-superusers to rebuild indexes of tables owned by possible for non-superusers to rebuild indexes of tables owned by
other users. However, as a special exception, other users. However, as a special exception, when
<command>REINDEX DATABASE</command>, <command>REINDEX SCHEMA</command>, <command>REINDEX DATABASE</command>, <command>REINDEX SCHEMA</command>
and <command>REINDEX SYSTEM</command> will skip indexes on shared catalogs or <command>REINDEX SYSTEM</command> is issued by a non-superuser,
unless the user has the <literal>MAINTAIN</literal> privilege on the indexes on shared catalogs will be skipped unless the user owns the
catalog. catalog (which typically won't be the case). Of course, superusers
can always reindex anything.
</para> </para>
<para> <para>

View File

@ -22,7 +22,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
REVOKE [ GRANT OPTION FOR ] REVOKE [ GRANT OPTION FOR ]
{ { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER | MAINTAIN } { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
[, ...] | ALL [ PRIVILEGES ] } [, ...] | ALL [ PRIVILEGES ] }
ON { [ TABLE ] <replaceable class="parameter">table_name</replaceable> [, ...] ON { [ TABLE ] <replaceable class="parameter">table_name</replaceable> [, ...]
| ALL TABLES IN SCHEMA <replaceable>schema_name</replaceable> [, ...] } | ALL TABLES IN SCHEMA <replaceable>schema_name</replaceable> [, ...] }

View File

@ -444,9 +444,11 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="paramet
<title>Notes</title> <title>Notes</title>
<para> <para>
To vacuum a table, one must ordinarily have the <literal>MAINTAIN</literal> To vacuum a table, one must ordinarily be the table's owner or a
privilege on the table. However, database owners are allowed to superuser. However, database owners are allowed to
vacuum all tables in their databases, except shared catalogs. 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 <command>VACUUM</command> will skip over any tables that the calling user
does not have permission to vacuum. does not have permission to vacuum.
</para> </para>

View File

@ -683,18 +683,6 @@ DROP ROLE doomed_role;
the <link linkend="sql-checkpoint"><command>CHECKPOINT</command></link> the <link linkend="sql-checkpoint"><command>CHECKPOINT</command></link>
command.</entry> command.</entry>
</row> </row>
<row>
<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, as if having <literal>MAINTAIN</literal> rights on those
objects, even without having it explicitly.</entry>
</row>
<row> <row>
<entry>pg_use_reserved_connections</entry> <entry>pg_use_reserved_connections</entry>
<entry>Allow use of connection slots reserved via <entry>Allow use of connection slots reserved via

View File

@ -2612,8 +2612,6 @@ string_to_privilege(const char *privname)
return ACL_SET; return ACL_SET;
if (strcmp(privname, "alter system") == 0) if (strcmp(privname, "alter system") == 0)
return ACL_ALTER_SYSTEM; return ACL_ALTER_SYSTEM;
if (strcmp(privname, "maintain") == 0)
return ACL_MAINTAIN;
if (strcmp(privname, "rule") == 0) if (strcmp(privname, "rule") == 0)
return 0; /* ignore old RULE privileges */ return 0; /* ignore old RULE privileges */
ereport(ERROR, ereport(ERROR,
@ -2655,8 +2653,6 @@ privilege_to_string(AclMode privilege)
return "SET"; return "SET";
case ACL_ALTER_SYSTEM: case ACL_ALTER_SYSTEM:
return "ALTER SYSTEM"; return "ALTER SYSTEM";
case ACL_MAINTAIN:
return "MAINTAIN";
default: default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege); elog(ERROR, "unrecognized privilege: %d", (int) privilege);
} }
@ -3388,17 +3384,6 @@ pg_class_aclmask_ext(Oid table_oid, Oid roleid, AclMode mask,
has_privs_of_role(roleid, ROLE_PG_WRITE_ALL_DATA)) has_privs_of_role(roleid, ROLE_PG_WRITE_ALL_DATA))
result |= (mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE)); result |= (mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE));
/*
* 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_maintain role, which allows VACUUM, ANALYZE, CLUSTER, REFRESH
* MATERIALIZED VIEW, and REINDEX on all relations.
*/
if (mask & ACL_MAINTAIN &&
!(result & ACL_MAINTAIN) &&
has_privs_of_role(roleid, ROLE_PG_MAINTAIN))
result |= ACL_MAINTAIN;
return result; return result;
} }

View File

@ -159,15 +159,16 @@ analyze_rel(Oid relid, RangeVar *relation,
return; return;
/* /*
* Check if relation needs to be skipped based on privileges. This check * Check if relation needs to be skipped based on ownership. This check
* happens also when building the relation list to analyze for a manual * happens also when building the relation list to analyze for a manual
* operation, and needs to be done additionally here as ANALYZE could * operation, and needs to be done additionally here as ANALYZE could
* happen across multiple transactions where privileges could have changed * happen across multiple transactions where relation ownership could have
* in-between. Make sure to generate only logs for ANALYZE in this case. * changed in-between. Make sure to generate only logs for ANALYZE in
* this case.
*/ */
if (!vacuum_is_permitted_for_relation(RelationGetRelid(onerel), if (!vacuum_is_relation_owner(RelationGetRelid(onerel),
onerel->rd_rel, onerel->rd_rel,
params->options & ~VACOPT_VACUUM)) params->options & VACOPT_ANALYZE))
{ {
relation_close(onerel, ShareUpdateExclusiveLock); relation_close(onerel, ShareUpdateExclusiveLock);
return; return;

View File

@ -80,7 +80,6 @@ static void copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
static List *get_tables_to_cluster(MemoryContext cluster_context); static List *get_tables_to_cluster(MemoryContext cluster_context);
static List *get_tables_to_cluster_partitioned(MemoryContext cluster_context, static List *get_tables_to_cluster_partitioned(MemoryContext cluster_context,
Oid indexOid); Oid indexOid);
static bool cluster_is_permitted_for_relation(Oid relid, Oid userid);
/*--------------------------------------------------------------------------- /*---------------------------------------------------------------------------
@ -148,8 +147,7 @@ cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel)
tableOid = RangeVarGetRelidExtended(stmt->relation, tableOid = RangeVarGetRelidExtended(stmt->relation,
AccessExclusiveLock, AccessExclusiveLock,
0, 0,
RangeVarCallbackMaintainsTable, RangeVarCallbackOwnsTable, NULL);
NULL);
rel = table_open(tableOid, NoLock); rel = table_open(tableOid, NoLock);
/* /*
@ -366,8 +364,8 @@ cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params)
*/ */
if (recheck) if (recheck)
{ {
/* Check that the user still has privileges for the relation */ /* Check that the user still owns the relation */
if (!cluster_is_permitted_for_relation(tableOid, save_userid)) if (!object_ownercheck(RelationRelationId, tableOid, save_userid))
{ {
relation_close(OldHeap, AccessExclusiveLock); relation_close(OldHeap, AccessExclusiveLock);
goto out; goto out;
@ -1642,7 +1640,7 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
/* /*
* Get a list of tables that the current user has privileges on and * Get a list of tables that the current user owns and
* have indisclustered set. Return the list in a List * of RelToCluster * have indisclustered set. Return the list in a List * of RelToCluster
* (stored in the specified memory context), each one giving the tableOid * (stored in the specified memory context), each one giving the tableOid
* and the indexOid on which the table is already clustered. * and the indexOid on which the table is already clustered.
@ -1659,8 +1657,8 @@ get_tables_to_cluster(MemoryContext cluster_context)
List *rtcs = NIL; List *rtcs = NIL;
/* /*
* Get all indexes that have indisclustered set and that the current user * Get all indexes that have indisclustered set and are owned by
* has the appropriate privileges for. * appropriate user.
*/ */
indRelation = table_open(IndexRelationId, AccessShareLock); indRelation = table_open(IndexRelationId, AccessShareLock);
ScanKeyInit(&entry, ScanKeyInit(&entry,
@ -1674,7 +1672,7 @@ get_tables_to_cluster(MemoryContext cluster_context)
index = (Form_pg_index) GETSTRUCT(indexTuple); index = (Form_pg_index) GETSTRUCT(indexTuple);
if (!cluster_is_permitted_for_relation(index->indrelid, GetUserId())) if (!object_ownercheck(RelationRelationId, index->indrelid, GetUserId()))
continue; continue;
/* Use a permanent memory context for the result list */ /* Use a permanent memory context for the result list */
@ -1722,13 +1720,10 @@ get_tables_to_cluster_partitioned(MemoryContext cluster_context, Oid indexOid)
if (get_rel_relkind(indexrelid) != RELKIND_INDEX) if (get_rel_relkind(indexrelid) != RELKIND_INDEX)
continue; continue;
/* /* Silently skip partitions which the user has no access to. */
* It's possible that the user does not have privileges to CLUSTER the if (!object_ownercheck(RelationRelationId, relid, GetUserId()) &&
* leaf partition despite having such privileges on the partitioned (!object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId()) ||
* table. We skip any partitions which the user is not permitted to IsSharedRelation(relid)))
* CLUSTER.
*/
if (!cluster_is_permitted_for_relation(relid, GetUserId()))
continue; continue;
/* Use a permanent memory context for the result list */ /* Use a permanent memory context for the result list */
@ -1744,19 +1739,3 @@ get_tables_to_cluster_partitioned(MemoryContext cluster_context, Oid indexOid)
return rtcs; return rtcs;
} }
/*
* Return whether userid has privileges to CLUSTER relid. If not, this
* function emits a WARNING.
*/
static bool
cluster_is_permitted_for_relation(Oid relid, Oid userid)
{
if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
return true;
ereport(WARNING,
(errmsg("permission denied to cluster \"%s\", skipping it",
get_rel_name(relid))));
return false;
}

View File

@ -26,7 +26,6 @@
#include "catalog/index.h" #include "catalog/index.h"
#include "catalog/indexing.h" #include "catalog/indexing.h"
#include "catalog/pg_am.h" #include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_constraint.h" #include "catalog/pg_constraint.h"
#include "catalog/pg_database.h" #include "catalog/pg_database.h"
#include "catalog/pg_inherits.h" #include "catalog/pg_inherits.h"
@ -2829,7 +2828,6 @@ RangeVarCallbackForReindexIndex(const RangeVar *relation,
char relkind; char relkind;
struct ReindexIndexCallbackState *state = arg; struct ReindexIndexCallbackState *state = arg;
LOCKMODE table_lockmode; LOCKMODE table_lockmode;
Oid table_oid;
/* /*
* Lock level here should match table lock in reindex_index() for * Lock level here should match table lock in reindex_index() for
@ -2869,19 +2867,14 @@ RangeVarCallbackForReindexIndex(const RangeVar *relation,
errmsg("\"%s\" is not an index", relation->relname))); errmsg("\"%s\" is not an index", relation->relname)));
/* Check permissions */ /* Check permissions */
table_oid = IndexGetRelation(relId, true); if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
if (OidIsValid(table_oid)) aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, relation->relname);
{
AclResult aclresult;
aclresult = pg_class_aclcheck(table_oid, GetUserId(), ACL_MAINTAIN);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_INDEX, relation->relname);
}
/* Lock heap before index to avoid deadlock. */ /* Lock heap before index to avoid deadlock. */
if (relId != oldRelId) if (relId != oldRelId)
{ {
Oid table_oid = IndexGetRelation(relId, true);
/* /*
* If the OID isn't valid, it means the index was concurrently * If the OID isn't valid, it means the index was concurrently
* dropped, which is not a problem for us; just return normally. * dropped, which is not a problem for us; just return normally.
@ -2916,7 +2909,7 @@ ReindexTable(RangeVar *relation, ReindexParams *params, bool isTopLevel)
(params->options & REINDEXOPT_CONCURRENTLY) != 0 ? (params->options & REINDEXOPT_CONCURRENTLY) != 0 ?
ShareUpdateExclusiveLock : ShareLock, ShareUpdateExclusiveLock : ShareLock,
0, 0,
RangeVarCallbackMaintainsTable, NULL); RangeVarCallbackOwnsTable, NULL);
if (get_rel_relkind(heapOid) == RELKIND_PARTITIONED_TABLE) if (get_rel_relkind(heapOid) == RELKIND_PARTITIONED_TABLE)
ReindexPartitions(heapOid, params, isTopLevel); ReindexPartitions(heapOid, params, isTopLevel);
@ -2998,8 +2991,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
{ {
objectOid = get_namespace_oid(objectName, false); objectOid = get_namespace_oid(objectName, false);
if (!object_ownercheck(NamespaceRelationId, objectOid, GetUserId()) && if (!object_ownercheck(NamespaceRelationId, objectOid, GetUserId()))
!has_privs_of_role(GetUserId(), ROLE_PG_MAINTAIN))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SCHEMA, aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SCHEMA,
objectName); objectName);
} }
@ -3011,8 +3003,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("can only reindex the currently open database"))); errmsg("can only reindex the currently open database")));
if (!object_ownercheck(DatabaseRelationId, objectOid, GetUserId()) && if (!object_ownercheck(DatabaseRelationId, objectOid, GetUserId()))
!has_privs_of_role(GetUserId(), ROLE_PG_MAINTAIN))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_DATABASE, aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_DATABASE,
get_database_name(objectOid)); get_database_name(objectOid));
} }
@ -3084,12 +3075,15 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
continue; continue;
/* /*
* We already checked privileges on the database or schema, but we * The table can be reindexed if the user is superuser, the table
* further restrict reindexing shared catalogs to roles with the * owner, or the database/schema owner (but in the latter case, only
* MAINTAIN privilege on the relation. * if it's not a shared relation). object_ownercheck includes the
* superuser case, and depending on objectKind we already know that
* the user has permission to run REINDEX on this database or schema
* per the permission checks at the beginning of this routine.
*/ */
if (classtuple->relisshared && if (classtuple->relisshared &&
pg_class_aclcheck(relid, GetUserId(), ACL_MAINTAIN) != ACLCHECK_OK) !object_ownercheck(RelationRelationId, relid, GetUserId()))
continue; continue;
/* /*

View File

@ -284,7 +284,7 @@ LockTableAclCheck(Oid reloid, LOCKMODE lockmode, Oid userid)
AclMode aclmask; AclMode aclmask;
/* any of these privileges permit any lock mode */ /* any of these privileges permit any lock mode */
aclmask = ACL_MAINTAIN | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE; aclmask = ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE;
/* SELECT privileges also permit ACCESS SHARE and below */ /* SELECT privileges also permit ACCESS SHARE and below */
if (lockmode <= AccessShareLock) if (lockmode <= AccessShareLock)

View File

@ -165,8 +165,7 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
*/ */
matviewOid = RangeVarGetRelidExtended(stmt->relation, matviewOid = RangeVarGetRelidExtended(stmt->relation,
lockmode, 0, lockmode, 0,
RangeVarCallbackMaintainsTable, RangeVarCallbackOwnsTable, NULL);
NULL);
matviewRel = table_open(matviewOid, NoLock); matviewRel = table_open(matviewOid, NoLock);
relowner = matviewRel->rd_rel->relowner; relowner = matviewRel->rd_rel->relowner;

View File

@ -16978,16 +16978,15 @@ AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
* This is intended as a callback for RangeVarGetRelidExtended(). It allows * This is intended as a callback for RangeVarGetRelidExtended(). It allows
* the relation to be locked only if (1) it's a plain or partitioned table, * the relation to be locked only if (1) it's a plain or partitioned table,
* materialized view, or TOAST table and (2) the current user is the owner (or * materialized view, or TOAST table and (2) the current user is the owner (or
* the superuser) or has been granted MAINTAIN. This meets the * the superuser). This meets the permission-checking needs of CLUSTER,
* permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH * REINDEX TABLE, and REFRESH MATERIALIZED VIEW; we expose it here so that it
* MATERIALIZED VIEW; we expose it here so that it can be used by all. * can be used by all.
*/ */
void void
RangeVarCallbackMaintainsTable(const RangeVar *relation, RangeVarCallbackOwnsTable(const RangeVar *relation,
Oid relId, Oid oldRelId, void *arg) Oid relId, Oid oldRelId, void *arg)
{ {
char relkind; char relkind;
AclResult aclresult;
/* Nothing to do if the relation was not found. */ /* Nothing to do if the relation was not found. */
if (!OidIsValid(relId)) if (!OidIsValid(relId))
@ -17008,9 +17007,8 @@ RangeVarCallbackMaintainsTable(const RangeVar *relation,
errmsg("\"%s\" is not a table or materialized view", relation->relname))); errmsg("\"%s\" is not a table or materialized view", relation->relname)));
/* Check permissions */ /* Check permissions */
aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN); if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
if (aclresult != ACLCHECK_OK) aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)), relation->relname);
aclcheck_error(aclresult, OBJECT_TABLE, relation->relname);
} }
/* /*

View File

@ -697,35 +697,32 @@ vacuum(List *relations, VacuumParams *params, BufferAccessStrategy bstrategy,
} }
/* /*
* Check if the current user has privileges to vacuum or analyze the relation. * Check if a given relation can be safely vacuumed or analyzed. If the
* If not, issue a WARNING log message and return false to let the caller * user is not the relation owner, issue a WARNING log message and return
* decide what to do with this relation. This routine is used to decide if a * false to let the caller decide what to do with this relation. This
* relation can be processed for VACUUM or ANALYZE. * routine is used to decide if a relation can be processed for VACUUM or
* ANALYZE.
*/ */
bool bool
vacuum_is_permitted_for_relation(Oid relid, Form_pg_class reltuple, vacuum_is_relation_owner(Oid relid, Form_pg_class reltuple, bits32 options)
bits32 options)
{ {
char *relname; char *relname;
Assert((options & (VACOPT_VACUUM | VACOPT_ANALYZE)) != 0); Assert((options & (VACOPT_VACUUM | VACOPT_ANALYZE)) != 0);
/* /*
* Privilege checks are bypassed in some cases (e.g., when recursing to a * Check permissions.
* relation's TOAST table). *
* 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). object_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 (options & VACOPT_SKIP_PRIVS) if (object_ownercheck(RelationRelationId, relid, GetUserId()) ||
return true; (object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId()) && !reltuple->relisshared))
/*----------
* A role has privileges to vacuum or analyze the relation if any of the
* following are true:
* - the role owns the current database and the relation is not shared
* - the role has the MAINTAIN privilege on the relation
*----------
*/
if ((object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId()) && !reltuple->relisshared) ||
pg_class_aclcheck(relid, GetUserId(), ACL_MAINTAIN) == ACLCHECK_OK)
return true; return true;
relname = NameStr(reltuple->relname); relname = NameStr(reltuple->relname);
@ -941,10 +938,10 @@ expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context,
classForm = (Form_pg_class) GETSTRUCT(tuple); classForm = (Form_pg_class) GETSTRUCT(tuple);
/* /*
* Make a returnable VacuumRelation for this rel if the user has the * Make a returnable VacuumRelation for this rel if user is a proper
* required privileges. * owner.
*/ */
if (vacuum_is_permitted_for_relation(relid, classForm, options)) if (vacuum_is_relation_owner(relid, classForm, options))
{ {
oldcontext = MemoryContextSwitchTo(vac_context); oldcontext = MemoryContextSwitchTo(vac_context);
vacrels = lappend(vacrels, makeVacuumRelation(vrel->relation, vacrels = lappend(vacrels, makeVacuumRelation(vrel->relation,
@ -1041,7 +1038,7 @@ get_all_vacuum_rels(MemoryContext vac_context, int options)
continue; continue;
/* check permissions of relation */ /* check permissions of relation */
if (!vacuum_is_permitted_for_relation(relid, classForm, options)) if (!vacuum_is_relation_owner(relid, classForm, options))
continue; continue;
/* /*
@ -2034,15 +2031,16 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params,
} }
/* /*
* Check if relation needs to be skipped based on privileges. This check * Check if relation needs to be skipped based on ownership. This check
* happens also when building the relation list to vacuum for a manual * happens also when building the relation list to vacuum for a manual
* operation, and needs to be done additionally here as VACUUM could * operation, and needs to be done additionally here as VACUUM could
* happen across multiple transactions where privileges could have changed * happen across multiple transactions where relation ownership could have
* in-between. Make sure to only generate logs for VACUUM in this case. * changed in-between. Make sure to only generate logs for VACUUM in this
* case.
*/ */
if (!vacuum_is_permitted_for_relation(RelationGetRelid(rel), if (!vacuum_is_relation_owner(RelationGetRelid(rel),
rel->rd_rel, rel->rd_rel,
params->options & ~VACOPT_ANALYZE)) params->options & VACOPT_VACUUM))
{ {
relation_close(rel, lmode); relation_close(rel, lmode);
PopActiveSnapshot(); PopActiveSnapshot();
@ -2228,14 +2226,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params,
{ {
VacuumParams toast_vacuum_params; VacuumParams toast_vacuum_params;
/* /* force VACOPT_PROCESS_MAIN so vacuum_rel() processes it */
* Force VACOPT_PROCESS_MAIN so vacuum_rel() processes it. Likewise,
* set VACOPT_SKIP_PRIVS since privileges on the main relation are
* sufficient to process it.
*/
memcpy(&toast_vacuum_params, params, sizeof(VacuumParams)); memcpy(&toast_vacuum_params, params, sizeof(VacuumParams));
toast_vacuum_params.options |= VACOPT_PROCESS_MAIN; toast_vacuum_params.options |= VACOPT_PROCESS_MAIN;
toast_vacuum_params.options |= VACOPT_SKIP_PRIVS;
vacuum_rel(toast_relid, NULL, &toast_vacuum_params, bstrategy); vacuum_rel(toast_relid, NULL, &toast_vacuum_params, bstrategy);
} }

View File

@ -332,9 +332,6 @@ aclparse(const char *s, AclItem *aip, Node *escontext)
case ACL_ALTER_SYSTEM_CHR: case ACL_ALTER_SYSTEM_CHR:
read = ACL_ALTER_SYSTEM; read = ACL_ALTER_SYSTEM;
break; break;
case ACL_MAINTAIN_CHR:
read = ACL_MAINTAIN;
break;
case 'R': /* ignore old RULE privileges */ case 'R': /* ignore old RULE privileges */
read = 0; read = 0;
break; break;
@ -1626,7 +1623,6 @@ makeaclitem(PG_FUNCTION_ARGS)
{"CONNECT", ACL_CONNECT}, {"CONNECT", ACL_CONNECT},
{"SET", ACL_SET}, {"SET", ACL_SET},
{"ALTER SYSTEM", ACL_ALTER_SYSTEM}, {"ALTER SYSTEM", ACL_ALTER_SYSTEM},
{"MAINTAIN", ACL_MAINTAIN},
{"RULE", 0}, /* ignore old RULE privileges */ {"RULE", 0}, /* ignore old RULE privileges */
{NULL, 0} {NULL, 0}
}; };
@ -1735,8 +1731,6 @@ convert_aclright_to_string(int aclright)
return "SET"; return "SET";
case ACL_ALTER_SYSTEM: case ACL_ALTER_SYSTEM:
return "ALTER SYSTEM"; return "ALTER SYSTEM";
case ACL_MAINTAIN:
return "MAINTAIN";
default: default:
elog(ERROR, "unrecognized aclright: %d", aclright); elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL; return NULL;
@ -2046,8 +2040,6 @@ convert_table_priv_string(text *priv_type_text)
{"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)}, {"REFERENCES WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_REFERENCES)},
{"TRIGGER", ACL_TRIGGER}, {"TRIGGER", ACL_TRIGGER},
{"TRIGGER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRIGGER)}, {"TRIGGER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_TRIGGER)},
{"MAINTAIN", ACL_MAINTAIN},
{"MAINTAIN WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_MAINTAIN)},
{"RULE", 0}, /* ignore old RULE privileges */ {"RULE", 0}, /* ignore old RULE privileges */
{"RULE WITH GRANT OPTION", 0}, {"RULE WITH GRANT OPTION", 0},
{NULL, 0} {NULL, 0}

View File

@ -463,7 +463,6 @@ do { \
CONVERT_PRIV('d', "DELETE"); CONVERT_PRIV('d', "DELETE");
CONVERT_PRIV('t', "TRIGGER"); CONVERT_PRIV('t', "TRIGGER");
CONVERT_PRIV('D', "TRUNCATE"); CONVERT_PRIV('D', "TRUNCATE");
CONVERT_PRIV('m', "MAINTAIN");
} }
} }

View File

@ -794,7 +794,7 @@ my %tests = (
\QREVOKE ALL ON TABLES FROM regress_dump_test_role;\E\n \QREVOKE ALL ON TABLES FROM regress_dump_test_role;\E\n
\QALTER DEFAULT PRIVILEGES \E \QALTER DEFAULT PRIVILEGES \E
\QFOR ROLE regress_dump_test_role \E \QFOR ROLE regress_dump_test_role \E
\QGRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,MAINTAIN,UPDATE ON TABLES TO regress_dump_test_role;\E \QGRANT INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLES TO regress_dump_test_role;\E
/xm, /xm,
like => { %full_runs, section_post_data => 1, }, like => { %full_runs, section_post_data => 1, },
unlike => { no_privs => 1, }, unlike => { no_privs => 1, },

View File

@ -1151,7 +1151,7 @@ Keywords_for_list_of_owner_roles, "PUBLIC"
#define Privilege_options_of_grant_and_revoke \ #define Privilege_options_of_grant_and_revoke \
"SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", "REFERENCES", "TRIGGER", \ "SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", "REFERENCES", "TRIGGER", \
"CREATE", "CONNECT", "TEMPORARY", "EXECUTE", "USAGE", "SET", "ALTER SYSTEM", \ "CREATE", "CONNECT", "TEMPORARY", "EXECUTE", "USAGE", "SET", "ALTER SYSTEM", \
"MAINTAIN", "ALL" "ALL"
/* ALTER PROCEDURE options */ /* ALTER PROCEDURE options */
#define Alter_procedure_options \ #define Alter_procedure_options \
@ -3881,7 +3881,7 @@ psql_completion(const char *text, int start, int end)
if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES")) if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES"))
COMPLETE_WITH("SELECT", "INSERT", "UPDATE", COMPLETE_WITH("SELECT", "INSERT", "UPDATE",
"DELETE", "TRUNCATE", "REFERENCES", "TRIGGER", "DELETE", "TRUNCATE", "REFERENCES", "TRIGGER",
"CREATE", "EXECUTE", "USAGE", "MAINTAIN", "ALL"); "CREATE", "EXECUTE", "USAGE", "ALL");
else if (TailMatches("GRANT")) else if (TailMatches("GRANT"))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles, COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
Privilege_options_of_grant_and_revoke); Privilege_options_of_grant_and_revoke);
@ -3933,7 +3933,7 @@ psql_completion(const char *text, int start, int end)
else if (TailMatches("GRANT|REVOKE", MatchAny) || else if (TailMatches("GRANT|REVOKE", MatchAny) ||
TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny)) TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny))
{ {
if (TailMatches("SELECT|INSERT|UPDATE|DELETE|TRUNCATE|REFERENCES|TRIGGER|CREATE|CONNECT|TEMPORARY|TEMP|EXECUTE|USAGE|MAINTAIN|ALL")) if (TailMatches("SELECT|INSERT|UPDATE|DELETE|TRUNCATE|REFERENCES|TRIGGER|CREATE|CONNECT|TEMPORARY|TEMP|EXECUTE|USAGE|ALL"))
COMPLETE_WITH("ON"); COMPLETE_WITH("ON");
else if (TailMatches("GRANT", MatchAny)) else if (TailMatches("GRANT", MatchAny))
COMPLETE_WITH("TO"); COMPLETE_WITH("TO");

View File

@ -57,6 +57,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 202307031 #define CATALOG_VERSION_NO 202307072
#endif #endif

View File

@ -84,11 +84,6 @@
rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1', rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1',
rolpassword => '_null_', rolvaliduntil => '_null_' }, rolpassword => '_null_', rolvaliduntil => '_null_' },
{ oid => '4549', oid_symbol => 'ROLE_PG_MAINTAIN',
rolname => 'pg_maintain', rolsuper => 'f', rolinherit => 't',
rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1',
rolpassword => '_null_', rolvaliduntil => '_null_' },
{ oid => '4550', oid_symbol => 'ROLE_PG_USE_RESERVED_CONNECTIONS', { oid => '4550', oid_symbol => 'ROLE_PG_USE_RESERVED_CONNECTIONS',
rolname => 'pg_use_reserved_connections', rolsuper => 'f', rolinherit => 't', rolname => 'pg_use_reserved_connections', rolsuper => 'f', rolinherit => 't',
rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f', rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',

View File

@ -96,9 +96,8 @@ extern void AtEOSubXact_on_commit_actions(bool isCommit,
SubTransactionId mySubid, SubTransactionId mySubid,
SubTransactionId parentSubid); SubTransactionId parentSubid);
extern void RangeVarCallbackMaintainsTable(const RangeVar *relation, extern void RangeVarCallbackOwnsTable(const RangeVar *relation,
Oid relId, Oid oldRelId, Oid relId, Oid oldRelId, void *arg);
void *arg);
extern void RangeVarCallbackOwnsRelation(const RangeVar *relation, extern void RangeVarCallbackOwnsRelation(const RangeVar *relation,
Oid relId, Oid oldRelId, void *arg); Oid relId, Oid oldRelId, void *arg);

View File

@ -187,7 +187,6 @@ typedef struct VacAttrStats
#define VACOPT_DISABLE_PAGE_SKIPPING 0x100 /* don't skip any pages */ #define VACOPT_DISABLE_PAGE_SKIPPING 0x100 /* don't skip any pages */
#define VACOPT_SKIP_DATABASE_STATS 0x200 /* skip vac_update_datfrozenxid() */ #define VACOPT_SKIP_DATABASE_STATS 0x200 /* skip vac_update_datfrozenxid() */
#define VACOPT_ONLY_DATABASE_STATS 0x400 /* only vac_update_datfrozenxid() */ #define VACOPT_ONLY_DATABASE_STATS 0x400 /* only vac_update_datfrozenxid() */
#define VACOPT_SKIP_PRIVS 0x800 /* skip privilege checks */
/* /*
* Values used by index_cleanup and truncate params. * Values used by index_cleanup and truncate params.
@ -344,8 +343,8 @@ extern bool vacuum_get_cutoffs(Relation rel, const VacuumParams *params,
extern bool vacuum_xid_failsafe_check(const struct VacuumCutoffs *cutoffs); extern bool vacuum_xid_failsafe_check(const struct VacuumCutoffs *cutoffs);
extern void vac_update_datfrozenxid(void); extern void vac_update_datfrozenxid(void);
extern void vacuum_delay_point(void); extern void vacuum_delay_point(void);
extern bool vacuum_is_permitted_for_relation(Oid relid, Form_pg_class reltuple, extern bool vacuum_is_relation_owner(Oid relid, Form_pg_class reltuple,
bits32 options); bits32 options);
extern Relation vacuum_open_relation(Oid relid, RangeVar *relation, extern Relation vacuum_open_relation(Oid relid, RangeVar *relation,
bits32 options, bool verbose, bits32 options, bool verbose,
LOCKMODE lmode); LOCKMODE lmode);

View File

@ -94,8 +94,7 @@ typedef uint64 AclMode; /* a bitmask of privilege bits */
#define ACL_CONNECT (1<<11) /* for databases */ #define ACL_CONNECT (1<<11) /* for databases */
#define ACL_SET (1<<12) /* for configuration parameters */ #define ACL_SET (1<<12) /* for configuration parameters */
#define ACL_ALTER_SYSTEM (1<<13) /* for configuration parameters */ #define ACL_ALTER_SYSTEM (1<<13) /* for configuration parameters */
#define ACL_MAINTAIN (1<<14) /* for relations */ #define N_ACL_RIGHTS 14 /* 1 plus the last 1<<x */
#define N_ACL_RIGHTS 15 /* 1 plus the last 1<<x */
#define ACL_NO_RIGHTS 0 #define ACL_NO_RIGHTS 0
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */ /* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE #define ACL_SELECT_FOR_UPDATE ACL_UPDATE

View File

@ -148,16 +148,15 @@ typedef struct ArrayType Acl;
#define ACL_CONNECT_CHR 'c' #define ACL_CONNECT_CHR 'c'
#define ACL_SET_CHR 's' #define ACL_SET_CHR 's'
#define ACL_ALTER_SYSTEM_CHR 'A' #define ACL_ALTER_SYSTEM_CHR 'A'
#define ACL_MAINTAIN_CHR 'm'
/* string holding all privilege code chars, in order by bitmask position */ /* string holding all privilege code chars, in order by bitmask position */
#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcsAm" #define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcsA"
/* /*
* Bitmasks defining "all rights" for each supported object type * Bitmasks defining "all rights" for each supported object type
*/ */
#define ACL_ALL_RIGHTS_COLUMN (ACL_INSERT|ACL_SELECT|ACL_UPDATE|ACL_REFERENCES) #define ACL_ALL_RIGHTS_COLUMN (ACL_INSERT|ACL_SELECT|ACL_UPDATE|ACL_REFERENCES)
#define ACL_ALL_RIGHTS_RELATION (ACL_INSERT|ACL_SELECT|ACL_UPDATE|ACL_DELETE|ACL_TRUNCATE|ACL_REFERENCES|ACL_TRIGGER|ACL_MAINTAIN) #define ACL_ALL_RIGHTS_RELATION (ACL_INSERT|ACL_SELECT|ACL_UPDATE|ACL_DELETE|ACL_TRUNCATE|ACL_REFERENCES|ACL_TRIGGER)
#define ACL_ALL_RIGHTS_SEQUENCE (ACL_USAGE|ACL_SELECT|ACL_UPDATE) #define ACL_ALL_RIGHTS_SEQUENCE (ACL_USAGE|ACL_SELECT|ACL_UPDATE)
#define ACL_ALL_RIGHTS_DATABASE (ACL_CREATE|ACL_CREATE_TEMP|ACL_CONNECT) #define ACL_ALL_RIGHTS_DATABASE (ACL_CREATE|ACL_CREATE_TEMP|ACL_CONNECT)
#define ACL_ALL_RIGHTS_FDW (ACL_USAGE) #define ACL_ALL_RIGHTS_FDW (ACL_USAGE)

View File

@ -3,7 +3,7 @@ Parsed test spec with 2 sessions
starting permutation: s1_begin s1_lock_parent s2_auth s2_cluster s1_commit s2_reset starting permutation: s1_begin s1_lock_parent s2_auth s2_cluster s1_commit s2_reset
step s1_begin: BEGIN; step s1_begin: BEGIN;
step s1_lock_parent: LOCK cluster_part_tab IN SHARE UPDATE EXCLUSIVE MODE; step s1_lock_parent: LOCK cluster_part_tab IN SHARE UPDATE EXCLUSIVE MODE;
step s2_auth: SET ROLE regress_cluster_part; SET client_min_messages = ERROR; step s2_auth: SET ROLE regress_cluster_part;
step s2_cluster: CLUSTER cluster_part_tab USING cluster_part_ind; <waiting ...> step s2_cluster: CLUSTER cluster_part_tab USING cluster_part_ind; <waiting ...>
step s1_commit: COMMIT; step s1_commit: COMMIT;
step s2_cluster: <... completed> step s2_cluster: <... completed>
@ -11,7 +11,7 @@ step s2_reset: RESET ROLE;
starting permutation: s1_begin s2_auth s1_lock_parent s2_cluster s1_commit s2_reset starting permutation: s1_begin s2_auth s1_lock_parent s2_cluster s1_commit s2_reset
step s1_begin: BEGIN; step s1_begin: BEGIN;
step s2_auth: SET ROLE regress_cluster_part; SET client_min_messages = ERROR; step s2_auth: SET ROLE regress_cluster_part;
step s1_lock_parent: LOCK cluster_part_tab IN SHARE UPDATE EXCLUSIVE MODE; step s1_lock_parent: LOCK cluster_part_tab IN SHARE UPDATE EXCLUSIVE MODE;
step s2_cluster: CLUSTER cluster_part_tab USING cluster_part_ind; <waiting ...> step s2_cluster: CLUSTER cluster_part_tab USING cluster_part_ind; <waiting ...>
step s1_commit: COMMIT; step s1_commit: COMMIT;
@ -21,14 +21,14 @@ step s2_reset: RESET ROLE;
starting permutation: s1_begin s1_lock_child s2_auth s2_cluster s1_commit s2_reset starting permutation: s1_begin s1_lock_child s2_auth s2_cluster s1_commit s2_reset
step s1_begin: BEGIN; step s1_begin: BEGIN;
step s1_lock_child: LOCK cluster_part_tab1 IN SHARE UPDATE EXCLUSIVE MODE; step s1_lock_child: LOCK cluster_part_tab1 IN SHARE UPDATE EXCLUSIVE MODE;
step s2_auth: SET ROLE regress_cluster_part; SET client_min_messages = ERROR; step s2_auth: SET ROLE regress_cluster_part;
step s2_cluster: CLUSTER cluster_part_tab USING cluster_part_ind; step s2_cluster: CLUSTER cluster_part_tab USING cluster_part_ind;
step s1_commit: COMMIT; step s1_commit: COMMIT;
step s2_reset: RESET ROLE; step s2_reset: RESET ROLE;
starting permutation: s1_begin s2_auth s1_lock_child s2_cluster s1_commit s2_reset starting permutation: s1_begin s2_auth s1_lock_child s2_cluster s1_commit s2_reset
step s1_begin: BEGIN; step s1_begin: BEGIN;
step s2_auth: SET ROLE regress_cluster_part; SET client_min_messages = ERROR; step s2_auth: SET ROLE regress_cluster_part;
step s1_lock_child: LOCK cluster_part_tab1 IN SHARE UPDATE EXCLUSIVE MODE; step s1_lock_child: LOCK cluster_part_tab1 IN SHARE UPDATE EXCLUSIVE MODE;
step s2_cluster: CLUSTER cluster_part_tab USING cluster_part_ind; step s2_cluster: CLUSTER cluster_part_tab USING cluster_part_ind;
step s1_commit: COMMIT; step s1_commit: COMMIT;

View File

@ -23,7 +23,7 @@ step s1_lock_child { LOCK cluster_part_tab1 IN SHARE UPDATE EXCLUSIVE MODE;
step s1_commit { COMMIT; } step s1_commit { COMMIT; }
session s2 session s2
step s2_auth { SET ROLE regress_cluster_part; SET client_min_messages = ERROR; } step s2_auth { SET ROLE regress_cluster_part; }
step s2_cluster { CLUSTER cluster_part_tab USING cluster_part_ind; } step s2_cluster { CLUSTER cluster_part_tab USING cluster_part_ind; }
step s2_reset { RESET ROLE; } step s2_reset { RESET ROLE; }

View File

@ -274,17 +274,6 @@ sub adjust_old_dumpfile
$dump = _mash_view_qualifiers($dump); $dump = _mash_view_qualifiers($dump);
} }
if ($old_version >= 14 && $old_version < 16)
{
# Fix up some privilege-set discrepancies.
$dump =~
s {^REVOKE SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE}
{REVOKE ALL ON TABLE}mg;
$dump =~
s {^(GRANT SELECT,INSERT,REFERENCES,TRIGGER,TRUNCATE),UPDATE ON TABLE}
{$1,MAINTAIN,UPDATE ON TABLE}mg;
}
if ($old_version < 14) if ($old_version < 14)
{ {
# Remove mentions of extended hash functions. # Remove mentions of extended hash functions.

View File

@ -352,9 +352,7 @@ INSERT INTO clstr_3 VALUES (1);
-- this user can only cluster clstr_1 and clstr_3, but the latter -- this user can only cluster clstr_1 and clstr_3, but the latter
-- has not been clustered -- has not been clustered
SET SESSION AUTHORIZATION regress_clstr_user; SET SESSION AUTHORIZATION regress_clstr_user;
SET client_min_messages = ERROR; -- order of "skipping" warnings may vary
CLUSTER; CLUSTER;
RESET client_min_messages;
SELECT * FROM clstr_1 UNION ALL SELECT * FROM clstr_1 UNION ALL
SELECT * FROM clstr_2 UNION ALL SELECT * FROM clstr_2 UNION ALL
SELECT * FROM clstr_3; SELECT * FROM clstr_3;
@ -502,17 +500,12 @@ CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
CREATE ROLE regress_ptnowner; CREATE ROLE regress_ptnowner;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
ERROR: permission denied for table ptnowner
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner; ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS CREATE TEMP TABLE ptnowner_oldnodes AS
SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree
JOIN pg_class AS c ON c.oid=tree.relid; JOIN pg_class AS c ON c.oid=tree.relid;
SET SESSION AUTHORIZATION regress_ptnowner; SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx; CLUSTER ptnowner USING ptnowner_i_idx;
WARNING: permission denied to cluster "ptnowner2", skipping it
RESET SESSION AUTHORIZATION; RESET SESSION AUTHORIZATION;
SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a SELECT a.relname, a.relfilenode=b.relfilenode FROM pg_class a
JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C"; JOIN ptnowner_oldnodes b USING (oid) ORDER BY a.relname COLLATE "C";

View File

@ -2831,9 +2831,9 @@ RESET ROLE;
GRANT USAGE ON SCHEMA pg_toast TO regress_reindexuser; GRANT USAGE ON SCHEMA pg_toast TO regress_reindexuser;
SET SESSION ROLE regress_reindexuser; SET SESSION ROLE regress_reindexuser;
REINDEX TABLE pg_toast.pg_toast_1260; REINDEX TABLE pg_toast.pg_toast_1260;
ERROR: permission denied for table pg_toast_1260 ERROR: must be owner of table pg_toast_1260
REINDEX INDEX pg_toast.pg_toast_1260_index; REINDEX INDEX pg_toast.pg_toast_1260_index;
ERROR: permission denied for index pg_toast_1260_index ERROR: must be owner of index pg_toast_1260_index
-- Clean up -- Clean up
RESET ROLE; RESET ROLE;
REVOKE USAGE ON SCHEMA pg_toast FROM regress_reindexuser; REVOKE USAGE ON SCHEMA pg_toast FROM regress_reindexuser;

View File

@ -19,7 +19,7 @@ DETAIL: privileges for table deptest
REVOKE SELECT ON deptest FROM GROUP regress_dep_group; REVOKE SELECT ON deptest FROM GROUP regress_dep_group;
DROP GROUP regress_dep_group; DROP GROUP regress_dep_group;
-- can't drop the user if we revoke the privileges partially -- can't drop the user if we revoke the privileges partially
REVOKE SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, MAINTAIN ON deptest FROM regress_dep_user; REVOKE SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES ON deptest FROM regress_dep_user;
DROP USER regress_dep_user; DROP USER regress_dep_user;
ERROR: role "regress_dep_user" cannot be dropped because some objects depend on it ERROR: role "regress_dep_user" cannot be dropped because some objects depend on it
DETAIL: privileges for table deptest DETAIL: privileges for table deptest
@ -67,21 +67,21 @@ CREATE TABLE deptest (a serial primary key, b text);
GRANT ALL ON deptest1 TO regress_dep_user2; GRANT ALL ON deptest1 TO regress_dep_user2;
RESET SESSION AUTHORIZATION; RESET SESSION AUTHORIZATION;
\z deptest1 \z deptest1
Access privileges Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies Schema | Name | Type | Access privileges | Column privileges | Policies
--------+----------+-------+------------------------------------------------------+-------------------+---------- --------+----------+-------+----------------------------------------------------+-------------------+----------
public | deptest1 | table | regress_dep_user0=arwdDxtm/regress_dep_user0 +| | public | deptest1 | table | regress_dep_user0=arwdDxt/regress_dep_user0 +| |
| | | regress_dep_user1=a*r*w*d*D*x*t*m*/regress_dep_user0+| | | | | regress_dep_user1=a*r*w*d*D*x*t*/regress_dep_user0+| |
| | | regress_dep_user2=arwdDxtm/regress_dep_user1 | | | | | regress_dep_user2=arwdDxt/regress_dep_user1 | |
(1 row) (1 row)
DROP OWNED BY regress_dep_user1; DROP OWNED BY regress_dep_user1;
-- all grants revoked -- all grants revoked
\z deptest1 \z deptest1
Access privileges Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies Schema | Name | Type | Access privileges | Column privileges | Policies
--------+----------+-------+----------------------------------------------+-------------------+---------- --------+----------+-------+---------------------------------------------+-------------------+----------
public | deptest1 | table | regress_dep_user0=arwdDxtm/regress_dep_user0 | | public | deptest1 | table | regress_dep_user0=arwdDxt/regress_dep_user0 | |
(1 row) (1 row)
-- table was dropped -- table was dropped

View File

@ -2278,9 +2278,9 @@ SELECT pg_input_is_valid('regress_priv_user1=rY', 'aclitem');
(1 row) (1 row)
SELECT * FROM pg_input_error_info('regress_priv_user1=rY', 'aclitem'); SELECT * FROM pg_input_error_info('regress_priv_user1=rY', 'aclitem');
message | detail | hint | sql_error_code message | detail | hint | sql_error_code
----------------------------------------------------------+--------+------+---------------- ---------------------------------------------------------+--------+------+----------------
invalid mode character: must be one of "arwdDxtXUCTcsAm" | | | 22P02 invalid mode character: must be one of "arwdDxtXUCTcsA" | | | 22P02
(1 row) (1 row)
-- --
@ -2621,38 +2621,38 @@ set session role regress_priv_user4;
grant select on dep_priv_test to regress_priv_user5; grant select on dep_priv_test to regress_priv_user5;
\dp dep_priv_test \dp dep_priv_test
Access privileges Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies Schema | Name | Type | Access privileges | Column privileges | Policies
--------+---------------+-------+------------------------------------------------+-------------------+---------- --------+---------------+-------+-----------------------------------------------+-------------------+----------
public | dep_priv_test | table | regress_priv_user1=arwdDxtm/regress_priv_user1+| | public | dep_priv_test | table | regress_priv_user1=arwdDxt/regress_priv_user1+| |
| | | regress_priv_user2=r*/regress_priv_user1 +| | | | | regress_priv_user2=r*/regress_priv_user1 +| |
| | | regress_priv_user3=r*/regress_priv_user1 +| | | | | regress_priv_user3=r*/regress_priv_user1 +| |
| | | regress_priv_user4=r*/regress_priv_user2 +| | | | | regress_priv_user4=r*/regress_priv_user2 +| |
| | | regress_priv_user4=r*/regress_priv_user3 +| | | | | regress_priv_user4=r*/regress_priv_user3 +| |
| | | regress_priv_user5=r/regress_priv_user4 | | | | | regress_priv_user5=r/regress_priv_user4 | |
(1 row) (1 row)
set session role regress_priv_user2; set session role regress_priv_user2;
revoke select on dep_priv_test from regress_priv_user4 cascade; revoke select on dep_priv_test from regress_priv_user4 cascade;
\dp dep_priv_test \dp dep_priv_test
Access privileges Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies Schema | Name | Type | Access privileges | Column privileges | Policies
--------+---------------+-------+------------------------------------------------+-------------------+---------- --------+---------------+-------+-----------------------------------------------+-------------------+----------
public | dep_priv_test | table | regress_priv_user1=arwdDxtm/regress_priv_user1+| | public | dep_priv_test | table | regress_priv_user1=arwdDxt/regress_priv_user1+| |
| | | regress_priv_user2=r*/regress_priv_user1 +| | | | | regress_priv_user2=r*/regress_priv_user1 +| |
| | | regress_priv_user3=r*/regress_priv_user1 +| | | | | regress_priv_user3=r*/regress_priv_user1 +| |
| | | regress_priv_user4=r*/regress_priv_user3 +| | | | | regress_priv_user4=r*/regress_priv_user3 +| |
| | | regress_priv_user5=r/regress_priv_user4 | | | | | regress_priv_user5=r/regress_priv_user4 | |
(1 row) (1 row)
set session role regress_priv_user3; set session role regress_priv_user3;
revoke select on dep_priv_test from regress_priv_user4 cascade; revoke select on dep_priv_test from regress_priv_user4 cascade;
\dp dep_priv_test \dp dep_priv_test
Access privileges Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies Schema | Name | Type | Access privileges | Column privileges | Policies
--------+---------------+-------+------------------------------------------------+-------------------+---------- --------+---------------+-------+-----------------------------------------------+-------------------+----------
public | dep_priv_test | table | regress_priv_user1=arwdDxtm/regress_priv_user1+| | public | dep_priv_test | table | regress_priv_user1=arwdDxt/regress_priv_user1+| |
| | | regress_priv_user2=r*/regress_priv_user1 +| | | | | regress_priv_user2=r*/regress_priv_user1 +| |
| | | regress_priv_user3=r*/regress_priv_user1 | | | | | regress_priv_user3=r*/regress_priv_user1 | |
(1 row) (1 row)
set session role regress_priv_user1; set session role regress_priv_user1;
@ -2782,20 +2782,6 @@ LOCK TABLE lock_table IN ACCESS EXCLUSIVE MODE; -- should pass
COMMIT; COMMIT;
\c \c
REVOKE TRUNCATE ON lock_table FROM regress_locktable_user; REVOKE TRUNCATE ON lock_table FROM regress_locktable_user;
-- LOCK TABLE and MAINTAIN permission
GRANT MAINTAIN ON lock_table TO regress_locktable_user;
SET SESSION AUTHORIZATION regress_locktable_user;
BEGIN;
LOCK TABLE lock_table IN ACCESS SHARE MODE; -- should pass
ROLLBACK;
BEGIN;
LOCK TABLE lock_table IN ROW EXCLUSIVE MODE; -- should pass
COMMIT;
BEGIN;
LOCK TABLE lock_table IN ACCESS EXCLUSIVE MODE; -- should pass
COMMIT;
\c
REVOKE MAINTAIN ON lock_table FROM regress_locktable_user;
-- clean up -- clean up
DROP TABLE lock_table; DROP TABLE lock_table;
DROP USER regress_locktable_user; DROP USER regress_locktable_user;
@ -2909,59 +2895,3 @@ DROP SCHEMA regress_roleoption;
DROP ROLE regress_roleoption_protagonist; DROP ROLE regress_roleoption_protagonist;
DROP ROLE regress_roleoption_donor; DROP ROLE regress_roleoption_donor;
DROP ROLE regress_roleoption_recipient; DROP ROLE regress_roleoption_recipient;
-- MAINTAIN
CREATE ROLE regress_no_maintain;
CREATE ROLE regress_maintain;
CREATE ROLE regress_maintain_all IN ROLE pg_maintain;
CREATE TABLE maintain_test (a INT);
CREATE INDEX ON maintain_test (a);
GRANT MAINTAIN ON maintain_test TO regress_maintain;
CREATE MATERIALIZED VIEW refresh_test AS SELECT 1;
GRANT MAINTAIN ON refresh_test TO regress_maintain;
CREATE SCHEMA reindex_test;
-- negative tests; should fail
SET ROLE regress_no_maintain;
VACUUM maintain_test;
WARNING: permission denied to vacuum "maintain_test", skipping it
ANALYZE maintain_test;
WARNING: permission denied to analyze "maintain_test", skipping it
VACUUM (ANALYZE) maintain_test;
WARNING: permission denied to vacuum "maintain_test", skipping it
CLUSTER maintain_test USING maintain_test_a_idx;
ERROR: permission denied for table maintain_test
REFRESH MATERIALIZED VIEW refresh_test;
ERROR: permission denied for table refresh_test
REINDEX TABLE maintain_test;
ERROR: permission denied for table maintain_test
REINDEX INDEX maintain_test_a_idx;
ERROR: permission denied for index maintain_test_a_idx
REINDEX SCHEMA reindex_test;
ERROR: must be owner of schema reindex_test
RESET ROLE;
SET ROLE regress_maintain;
VACUUM maintain_test;
ANALYZE maintain_test;
VACUUM (ANALYZE) maintain_test;
CLUSTER maintain_test USING maintain_test_a_idx;
REFRESH MATERIALIZED VIEW refresh_test;
REINDEX TABLE maintain_test;
REINDEX INDEX maintain_test_a_idx;
REINDEX SCHEMA reindex_test;
ERROR: must be owner of schema reindex_test
RESET ROLE;
SET ROLE regress_maintain_all;
VACUUM maintain_test;
ANALYZE maintain_test;
VACUUM (ANALYZE) maintain_test;
CLUSTER maintain_test USING maintain_test_a_idx;
REFRESH MATERIALIZED VIEW refresh_test;
REINDEX TABLE maintain_test;
REINDEX INDEX maintain_test_a_idx;
REINDEX SCHEMA reindex_test;
RESET ROLE;
DROP TABLE maintain_test;
DROP MATERIALIZED VIEW refresh_test;
DROP SCHEMA reindex_test;
DROP ROLE regress_no_maintain;
DROP ROLE regress_maintain;
DROP ROLE regress_maintain_all;

View File

@ -93,23 +93,23 @@ CREATE POLICY p2r ON document AS RESTRICTIVE TO regress_rls_dave
CREATE POLICY p1r ON document AS RESTRICTIVE TO regress_rls_dave CREATE POLICY p1r ON document AS RESTRICTIVE TO regress_rls_dave
USING (cid <> 44); USING (cid <> 44);
\dp \dp
Access privileges Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies Schema | Name | Type | Access privileges | Column privileges | Policies
--------------------+----------+-------+----------------------------------------------+-------------------+-------------------------------------------- --------------------+----------+-------+---------------------------------------------+-------------------+--------------------------------------------
regress_rls_schema | category | table | regress_rls_alice=arwdDxtm/regress_rls_alice+| | regress_rls_schema | category | table | regress_rls_alice=arwdDxt/regress_rls_alice+| |
| | | =arwdDxtm/regress_rls_alice | | | | | =arwdDxt/regress_rls_alice | |
regress_rls_schema | document | table | regress_rls_alice=arwdDxtm/regress_rls_alice+| | p1: + regress_rls_schema | document | table | regress_rls_alice=arwdDxt/regress_rls_alice+| | p1: +
| | | =arwdDxtm/regress_rls_alice | | (u): (dlevel <= ( SELECT uaccount.seclv + | | | =arwdDxt/regress_rls_alice | | (u): (dlevel <= ( SELECT uaccount.seclv +
| | | | | FROM uaccount + | | | | | FROM uaccount +
| | | | | WHERE (uaccount.pguser = CURRENT_USER)))+ | | | | | WHERE (uaccount.pguser = CURRENT_USER)))+
| | | | | p2r (RESTRICTIVE): + | | | | | p2r (RESTRICTIVE): +
| | | | | (u): ((cid <> 44) AND (cid < 50)) + | | | | | (u): ((cid <> 44) AND (cid < 50)) +
| | | | | to: regress_rls_dave + | | | | | to: regress_rls_dave +
| | | | | p1r (RESTRICTIVE): + | | | | | p1r (RESTRICTIVE): +
| | | | | (u): (cid <> 44) + | | | | | (u): (cid <> 44) +
| | | | | to: regress_rls_dave | | | | | to: regress_rls_dave
regress_rls_schema | uaccount | table | regress_rls_alice=arwdDxtm/regress_rls_alice+| | regress_rls_schema | uaccount | table | regress_rls_alice=arwdDxt/regress_rls_alice+| |
| | | =r/regress_rls_alice | | | | | =r/regress_rls_alice | |
(3 rows) (3 rows)
\d document \d document

View File

@ -145,9 +145,7 @@ INSERT INTO clstr_3 VALUES (1);
-- this user can only cluster clstr_1 and clstr_3, but the latter -- this user can only cluster clstr_1 and clstr_3, but the latter
-- has not been clustered -- has not been clustered
SET SESSION AUTHORIZATION regress_clstr_user; SET SESSION AUTHORIZATION regress_clstr_user;
SET client_min_messages = ERROR; -- order of "skipping" warnings may vary
CLUSTER; CLUSTER;
RESET client_min_messages;
SELECT * FROM clstr_1 UNION ALL SELECT * FROM clstr_1 UNION ALL
SELECT * FROM clstr_2 UNION ALL SELECT * FROM clstr_2 UNION ALL
SELECT * FROM clstr_3; SELECT * FROM clstr_3;
@ -238,9 +236,6 @@ CREATE TABLE ptnowner1 PARTITION OF ptnowner FOR VALUES IN (1);
CREATE ROLE regress_ptnowner; CREATE ROLE regress_ptnowner;
CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2); CREATE TABLE ptnowner2 PARTITION OF ptnowner FOR VALUES IN (2);
ALTER TABLE ptnowner1 OWNER TO regress_ptnowner; ALTER TABLE ptnowner1 OWNER TO regress_ptnowner;
SET SESSION AUTHORIZATION regress_ptnowner;
CLUSTER ptnowner USING ptnowner_i_idx;
RESET SESSION AUTHORIZATION;
ALTER TABLE ptnowner OWNER TO regress_ptnowner; ALTER TABLE ptnowner OWNER TO regress_ptnowner;
CREATE TEMP TABLE ptnowner_oldnodes AS CREATE TEMP TABLE ptnowner_oldnodes AS
SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree SELECT oid, relname, relfilenode FROM pg_partition_tree('ptnowner') AS tree

View File

@ -21,7 +21,7 @@ REVOKE SELECT ON deptest FROM GROUP regress_dep_group;
DROP GROUP regress_dep_group; DROP GROUP regress_dep_group;
-- can't drop the user if we revoke the privileges partially -- can't drop the user if we revoke the privileges partially
REVOKE SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, MAINTAIN ON deptest FROM regress_dep_user; REVOKE SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES ON deptest FROM regress_dep_user;
DROP USER regress_dep_user; DROP USER regress_dep_user;
-- now we are OK to drop him -- now we are OK to drop him

View File

@ -1778,21 +1778,6 @@ COMMIT;
\c \c
REVOKE TRUNCATE ON lock_table FROM regress_locktable_user; REVOKE TRUNCATE ON lock_table FROM regress_locktable_user;
-- LOCK TABLE and MAINTAIN permission
GRANT MAINTAIN ON lock_table TO regress_locktable_user;
SET SESSION AUTHORIZATION regress_locktable_user;
BEGIN;
LOCK TABLE lock_table IN ACCESS SHARE MODE; -- should pass
ROLLBACK;
BEGIN;
LOCK TABLE lock_table IN ROW EXCLUSIVE MODE; -- should pass
COMMIT;
BEGIN;
LOCK TABLE lock_table IN ACCESS EXCLUSIVE MODE; -- should pass
COMMIT;
\c
REVOKE MAINTAIN ON lock_table FROM regress_locktable_user;
-- clean up -- clean up
DROP TABLE lock_table; DROP TABLE lock_table;
DROP USER regress_locktable_user; DROP USER regress_locktable_user;
@ -1876,56 +1861,3 @@ DROP SCHEMA regress_roleoption;
DROP ROLE regress_roleoption_protagonist; DROP ROLE regress_roleoption_protagonist;
DROP ROLE regress_roleoption_donor; DROP ROLE regress_roleoption_donor;
DROP ROLE regress_roleoption_recipient; DROP ROLE regress_roleoption_recipient;
-- MAINTAIN
CREATE ROLE regress_no_maintain;
CREATE ROLE regress_maintain;
CREATE ROLE regress_maintain_all IN ROLE pg_maintain;
CREATE TABLE maintain_test (a INT);
CREATE INDEX ON maintain_test (a);
GRANT MAINTAIN ON maintain_test TO regress_maintain;
CREATE MATERIALIZED VIEW refresh_test AS SELECT 1;
GRANT MAINTAIN ON refresh_test TO regress_maintain;
CREATE SCHEMA reindex_test;
-- negative tests; should fail
SET ROLE regress_no_maintain;
VACUUM maintain_test;
ANALYZE maintain_test;
VACUUM (ANALYZE) maintain_test;
CLUSTER maintain_test USING maintain_test_a_idx;
REFRESH MATERIALIZED VIEW refresh_test;
REINDEX TABLE maintain_test;
REINDEX INDEX maintain_test_a_idx;
REINDEX SCHEMA reindex_test;
RESET ROLE;
SET ROLE regress_maintain;
VACUUM maintain_test;
ANALYZE maintain_test;
VACUUM (ANALYZE) maintain_test;
CLUSTER maintain_test USING maintain_test_a_idx;
REFRESH MATERIALIZED VIEW refresh_test;
REINDEX TABLE maintain_test;
REINDEX INDEX maintain_test_a_idx;
REINDEX SCHEMA reindex_test;
RESET ROLE;
SET ROLE regress_maintain_all;
VACUUM maintain_test;
ANALYZE maintain_test;
VACUUM (ANALYZE) maintain_test;
CLUSTER maintain_test USING maintain_test_a_idx;
REFRESH MATERIALIZED VIEW refresh_test;
REINDEX TABLE maintain_test;
REINDEX INDEX maintain_test_a_idx;
REINDEX SCHEMA reindex_test;
RESET ROLE;
DROP TABLE maintain_test;
DROP MATERIALIZED VIEW refresh_test;
DROP SCHEMA reindex_test;
DROP ROLE regress_no_maintain;
DROP ROLE regress_maintain;
DROP ROLE regress_maintain_all;