v14-0001-Allow-grant-and-revoke-of-privileges-on-paramete.patch
application/octet-stream
Filename: v14-0001-Allow-grant-and-revoke-of-privileges-on-paramete.patch
Type: application/octet-stream
Part: 0
Patch
Format: format-patch
Series: patch v14-0001
Subject: Allow grant and revoke of privileges on parameters
| File | + | − |
|---|---|---|
| contrib/pg_trgm/pg_trgm--1.3.sql | 4 | 0 |
| contrib/postgres_fdw/postgres_fdw--1.0.sql | 2 | 0 |
| contrib/sepgsql/sepgsql.sql.in | 1 | 0 |
| doc/src/sgml/catalogs.sgml | 70 | 5 |
| doc/src/sgml/ddl.sgml | 53 | 3 |
| doc/src/sgml/func.sgml | 18 | 2 |
| doc/src/sgml/ref/grant.sgml | 7 | 0 |
| doc/src/sgml/ref/revoke.sgml | 7 | 0 |
| doc/src/sgml/ref/set.sgml | 4 | 4 |
| src/backend/catalog/aclchk.c | 280 | 0 |
| src/backend/catalog/catalog.c | 6 | 0 |
| src/backend/catalog/dependency.c | 6 | 0 |
| src/backend/catalog/Makefile | 2 | 1 |
| src/backend/catalog/objectaddress.c | 46 | 0 |
| src/backend/catalog/pg_parameter_acl.c | 109 | 0 |
| src/backend/catalog/system_views.sql | 13 | 0 |
| src/backend/commands/alter.c | 1 | 0 |
| src/backend/commands/event_trigger.c | 6 | 0 |
| src/backend/commands/seclabel.c | 1 | 0 |
| src/backend/commands/tablecmds.c | 1 | 0 |
| src/backend/parser/gram.y | 77 | 3 |
| src/backend/utils/adt/acl.c | 307 | 0 |
| src/backend/utils/cache/lsyscache.c | 20 | 0 |
| src/backend/utils/cache/syscache.c | 23 | 0 |
| src/backend/utils/misc/guc.c | 239 | 53 |
| src/backend/utils/misc/guc-file.l | 1 | 1 |
| src/bin/pg_dump/dumputils.c | 6 | 1 |
| src/bin/pg_dump/pg_backup_archiver.c | 2 | 0 |
| src/bin/pg_dump/pg_dumpall.c | 61 | 0 |
| src/bin/pg_dump/pg_dump.c | 4 | 1 |
| src/bin/psql/tab-complete.c | 169 | 9 |
| src/include/catalog/dependency.h | 1 | 0 |
| src/include/catalog/objectaccess.h | 1 | 1 |
| src/include/catalog/pg_default_acl.h | 1 | 0 |
| src/include/catalog/pg_parameter_acl.h | 63 | 0 |
| src/include/catalog/pg_proc.dat | 23 | 0 |
| src/include/nodes/parsenodes.h | 2 | 1 |
| src/include/parser/kwlist.h | 1 | 0 |
| src/include/utils/acl.h | 8 | 1 |
| src/include/utils/guc.h | 4 | 0 |
| src/include/utils/lsyscache.h | 1 | 0 |
| src/include/utils/syscache.h | 2 | 0 |
| src/pl/plperl/plperl--1.0.sql | 2 | 0 |
| src/pl/plperl/plperlu--1.0.sql | 2 | 0 |
| src/pl/plpgsql/src/plpgsql--1.0.sql | 5 | 0 |
| src/test/modules/test_oat_hooks/expected/test_oat_hooks.out | 148 | 62 |
| src/test/modules/test_oat_hooks/sql/test_oat_hooks.sql | 44 | 1 |
| src/test/modules/test_oat_hooks/test_oat_hooks.c | 74 | 6 |
| src/test/modules/test_pg_dump/t/001_base.pl | 60 | 0 |
| src/test/modules/unsafe_tests/expected/guc_privs.out | 375 | 0 |
| src/test/modules/unsafe_tests/Makefile | 1 | 1 |
| src/test/modules/unsafe_tests/sql/guc_privs.sql | 174 | 0 |
| src/test/regress/expected/rules.out | 13 | 0 |
From cb760575df80e31a91eadbab9055ce82d8aa5716 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Mon, 28 Mar 2022 13:35:11 -0700
Subject: [PATCH v14] Allow grant and revoke of privileges on parameters
Add new SET and ALTER SYSTEM privileges for configuration parameters
(GUCs). Add new catalog pg_parameter_acl for tracking grants on
parameters, with default behavior treating parameters without
entries according to their context (userset, suset, etc.), such that
the privilege to set or reset a userset parameter is implicitly
granted to public, but may be revoked.
---
contrib/pg_trgm/pg_trgm--1.3.sql | 4 +
contrib/postgres_fdw/postgres_fdw--1.0.sql | 2 +
contrib/sepgsql/sepgsql.sql.in | 1 +
doc/src/sgml/catalogs.sgml | 75 +++-
doc/src/sgml/ddl.sgml | 56 ++-
doc/src/sgml/func.sgml | 20 +-
doc/src/sgml/ref/grant.sgml | 7 +
doc/src/sgml/ref/revoke.sgml | 7 +
doc/src/sgml/ref/set.sgml | 8 +-
src/backend/catalog/Makefile | 3 +-
src/backend/catalog/aclchk.c | 280 +++++++++++++
src/backend/catalog/catalog.c | 6 +
src/backend/catalog/dependency.c | 6 +
src/backend/catalog/objectaddress.c | 46 +++
src/backend/catalog/pg_parameter_acl.c | 109 +++++
src/backend/catalog/system_views.sql | 13 +
src/backend/commands/alter.c | 1 +
src/backend/commands/event_trigger.c | 6 +
src/backend/commands/seclabel.c | 1 +
src/backend/commands/tablecmds.c | 1 +
src/backend/parser/gram.y | 80 +++-
src/backend/utils/adt/acl.c | 307 ++++++++++++++
src/backend/utils/cache/lsyscache.c | 20 +
src/backend/utils/cache/syscache.c | 23 ++
src/backend/utils/misc/guc-file.l | 2 +-
src/backend/utils/misc/guc.c | 292 +++++++++++---
src/bin/pg_dump/dumputils.c | 7 +-
src/bin/pg_dump/pg_backup_archiver.c | 2 +
src/bin/pg_dump/pg_dump.c | 5 +-
src/bin/pg_dump/pg_dumpall.c | 61 +++
src/bin/psql/tab-complete.c | 178 ++++++++-
src/include/catalog/dependency.h | 1 +
src/include/catalog/objectaccess.h | 2 +-
src/include/catalog/pg_default_acl.h | 1 +
src/include/catalog/pg_parameter_acl.h | 63 +++
src/include/catalog/pg_proc.dat | 23 ++
src/include/nodes/parsenodes.h | 3 +-
src/include/parser/kwlist.h | 1 +
src/include/utils/acl.h | 9 +-
src/include/utils/guc.h | 4 +
src/include/utils/lsyscache.h | 1 +
src/include/utils/syscache.h | 2 +
src/pl/plperl/plperl--1.0.sql | 2 +
src/pl/plperl/plperlu--1.0.sql | 2 +
src/pl/plpgsql/src/plpgsql--1.0.sql | 5 +
.../expected/test_oat_hooks.out | 210 +++++++---
.../test_oat_hooks/sql/test_oat_hooks.sql | 45 ++-
.../modules/test_oat_hooks/test_oat_hooks.c | 80 +++-
src/test/modules/test_pg_dump/t/001_base.pl | 60 +++
src/test/modules/unsafe_tests/Makefile | 2 +-
.../unsafe_tests/expected/guc_privs.out | 375 ++++++++++++++++++
.../modules/unsafe_tests/sql/guc_privs.sql | 174 ++++++++
src/test/regress/expected/rules.out | 13 +
53 files changed, 2551 insertions(+), 156 deletions(-)
create mode 100644 src/backend/catalog/pg_parameter_acl.c
create mode 100644 src/include/catalog/pg_parameter_acl.h
create mode 100644 src/test/modules/unsafe_tests/expected/guc_privs.out
create mode 100644 src/test/modules/unsafe_tests/sql/guc_privs.sql
diff --git a/contrib/pg_trgm/pg_trgm--1.3.sql b/contrib/pg_trgm/pg_trgm--1.3.sql
index 4c6edf8c24..8aac300c7e 100644
--- a/contrib/pg_trgm/pg_trgm--1.3.sql
+++ b/contrib/pg_trgm/pg_trgm--1.3.sql
@@ -252,3 +252,7 @@ LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
ALTER OPERATOR FAMILY gin_trgm_ops USING gin ADD
OPERATOR 7 %> (text, text),
FUNCTION 6 (text,text) gin_trgm_triconsistent (internal, int2, text, int4, internal, internal, internal);
+
+GRANT SET ON PARAMETER pg_trgm.similarity_threshold TO public;
+GRANT SET ON PARAMETER pg_trgm.word_similarity_threshold TO public;
+GRANT SET ON PARAMETER pg_trgm.strict_word_similarity_threshold TO public;
diff --git a/contrib/postgres_fdw/postgres_fdw--1.0.sql b/contrib/postgres_fdw/postgres_fdw--1.0.sql
index a0f0fc1bf4..4ddf5d812c 100644
--- a/contrib/postgres_fdw/postgres_fdw--1.0.sql
+++ b/contrib/postgres_fdw/postgres_fdw--1.0.sql
@@ -16,3 +16,5 @@ LANGUAGE C STRICT;
CREATE FOREIGN DATA WRAPPER postgres_fdw
HANDLER postgres_fdw_handler
VALIDATOR postgres_fdw_validator;
+
+GRANT SET ON PARAMETER postgres_fdw.application_name TO public;
diff --git a/contrib/sepgsql/sepgsql.sql.in b/contrib/sepgsql/sepgsql.sql.in
index 917d12dbbe..9b59a80b09 100644
--- a/contrib/sepgsql/sepgsql.sql.in
+++ b/contrib/sepgsql/sepgsql.sql.in
@@ -35,3 +35,4 @@ CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_mcstrans_in(text) RETURNS text AS
CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_mcstrans_out(text) RETURNS text AS 'MODULE_PATHNAME', 'sepgsql_mcstrans_out' LANGUAGE C STRICT;
CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_restorecon(text) RETURNS bool AS 'MODULE_PATHNAME', 'sepgsql_restorecon' LANGUAGE C;
SELECT sepgsql_restorecon(NULL);
+GRANT SET ON PARAMETER sepgsql.debug_audit TO public;
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 7f4f79d1b5..0ba8523269 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -220,6 +220,11 @@
<entry>access method operator families</entry>
</row>
+ <row>
+ <entry><link linkend="catalog-pg-parameter-acl"><structname>pg_parameter_acl</structname></link></entry>
+ <entry>configuration parameters which have privileges granted to roles</entry>
+ </row>
+
<row>
<entry><link linkend="catalog-pg-partitioned-table"><structname>pg_partitioned_table</structname></link></entry>
<entry>information about partition key of tables</entry>
@@ -2432,6 +2437,64 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</para>
</sect1>
+ <sect1 id="catalog-pg-parameter-acl">
+ <title><structname>pg_parameter_acl</structname></title>
+
+ <indexterm zone="catalog-pg-parameter-acl">
+ <primary>pg_parameter_acl</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_parameter_acl</structname> records configuration
+ parameters which have had privileges to <literal>SET</literal> or
+ <literal>ALTER SYSTEM</literal> granted to one or more roles.
+ </para>
+
+ <para>
+ Unlike most system catalogs, <structname>pg_parameter_acl</structname>
+ is shared across all databases of a cluster: there is only one
+ copy of <structname>pg_parameter_acl</structname> per cluster, not
+ one per database.
+ </para>
+
+ <table>
+ <title><structname>pg_parameter_acl</structname> Columns</title>
+ <tgroup cols="1">
+ <thead>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ Column Type
+ </para>
+ <para>
+ Description
+ </para></entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>parameter</structfield> <type>text</type>
+ </para>
+ <para>
+ The name of the configuration parameter for which privileges are granted.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>setacl</structfield> <type>aclitem[]</type>
+ </para>
+ <para>
+ Access privileges; see <xref linkend="ddl-priv"/> for details
+ </para></entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
<sect1 id="catalog-pg-constraint">
<title><structname>pg_constraint</structname></title>
@@ -12779,11 +12842,13 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
<term><literal>superuser</literal></term>
<listitem>
<para>
- These settings can be set from <filename>postgresql.conf</filename>,
- or within a session via the <command>SET</command> command; but only superusers
- can change them via <command>SET</command>. Changes in
- <filename>postgresql.conf</filename> will affect existing sessions
- only if no session-local value has been established with <command>SET</command>.
+ These parameters can be set from <filename>postgresql.conf</filename>, or
+ within a session via the <command>SET</command> command; but only
+ superusers or users with <literal>SET</literal> privilege granted
+ on the parameters can change them via <command>SET</command>. Changes in
+ <filename>postgresql.conf</filename> will affect existing sessions only
+ if no session-local value has been established with
+ <command>SET</command>.
</para>
</listitem>
</varlistentry>
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 166b7a352d..8240ccb23b 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -1691,7 +1691,8 @@ ALTER TABLE products RENAME TO items;
<literal>INSERT</literal>, <literal>UPDATE</literal>, <literal>DELETE</literal>,
<literal>TRUNCATE</literal>, <literal>REFERENCES</literal>, <literal>TRIGGER</literal>,
<literal>CREATE</literal>, <literal>CONNECT</literal>, <literal>TEMPORARY</literal>,
- <literal>EXECUTE</literal>, and <literal>USAGE</literal>.
+ <literal>EXECUTE</literal>, <literal>USAGE</literal>, <literal>SET</literal>
+ and <literal>ALTER SYSTEM</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.
@@ -1959,6 +1960,26 @@ REVOKE ALL ON accounts FROM PUBLIC;
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>SET</literal></term>
+ <listitem>
+ <para>
+ Allows run-time configuration parameters to be set to a new value or
+ reset to the default value.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ALTER SYSTEM</literal></term>
+ <listitem>
+ <para>
+ Allows server configuration parameters to be configured to a new value
+ or reset to the default configuration value.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
The privileges required by other commands are listed on the
@@ -1981,8 +2002,9 @@ REVOKE ALL ON accounts FROM PUBLIC;
granted to <literal>PUBLIC</literal> are as follows:
<literal>CONNECT</literal> and <literal>TEMPORARY</literal> (create
temporary tables) privileges for databases;
- <literal>EXECUTE</literal> privilege for functions and procedures; and
- <literal>USAGE</literal> privilege for languages and data types
+ <literal>EXECUTE</literal> privilege for functions and procedures;
+ <literal>SET</literal> privilege for setting parameters locally to a
+ session; and <literal>USAGE</literal> privilege for languages and data types
(including domains).
The object owner can, of course, <command>REVOKE</command>
both default and expressly granted privileges. (For maximum
@@ -2097,6 +2119,16 @@ REVOKE ALL ON accounts FROM PUBLIC;
<literal>TYPE</literal>
</entry>
</row>
+ <row>
+ <entry><literal>SET</literal></entry>
+ <entry><literal>s</literal></entry>
+ <entry><literal>PARAMETER</literal></entry>
+ </row>
+ <row>
+ <entry><literal>ALTER SYSTEM</literal></entry>
+ <entry><literal>A</literal></entry>
+ <entry><literal>PARAMETER</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -2203,6 +2235,12 @@ REVOKE ALL ON accounts FROM PUBLIC;
<entry><literal>U</literal></entry>
<entry><literal>\dT+</literal></entry>
</row>
+ <row>
+ <entry><literal>PARAMETER</literal></entry>
+ <entry><literal>sA</literal></entry>
+ <entry><literal>s</literal> or none</entry>
+ <entry>none</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -2274,6 +2312,18 @@ GRANT SELECT (col1), UPDATE (col1) ON mytable TO miriam_rw;
access privileges display. A <literal>*</literal> will appear only when
grant options have been explicitly granted to someone.
</para>
+
+ <para>
+ The default privileges for a <literal>user</literal> parameter allow
+ <literal>PUBLIC</literal> to <command>SET</command> and
+ <command>RESET</command> the assigned value. By default,
+ <literal>PUBLIC</literal> has no privileges on
+ <literal>postmaster</literal>, <literal>superuser-backend</literal>,
+ <literal>internal</literal>, <literal>backend</literal>,
+ <literal>sighup</literal>, and <literal>superuser</literal> parameters.
+ Parameters are not explicitly owned. All parameters, including those added
+ by extensions, implicitly belong to the bootstrap superuser.
+ </para>
</sect1>
<sect1 id="ddl-rowsecurity">
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 93f11faada..34491f70d7 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -22691,8 +22691,7 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n);
privilege is held with grant option. Also, multiple privilege types can be
listed separated by commas, in which case the result will be true if any of
the listed privileges is held. (Case of the privilege string is not
- significant, and extra whitespace is allowed between but not within
- privilege names.)
+ significant, and extra whitespace is allowed between privilege names.)
Some examples:
<programlisting>
SELECT has_table_privilege('myschema.mytable', 'select');
@@ -22843,6 +22842,23 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute');
</para></entry>
</row>
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>has_parameter_privilege</primary>
+ </indexterm>
+ <function>has_parameter_privilege</function> (
+ <optional> <parameter>user</parameter> <type>name</type> or <type>oid</type>, </optional>
+ <parameter>parameter</parameter> <type>text</type> or <type>oid</type>,
+ <parameter>privilege</parameter> <type>text</type> )
+ <returnvalue>boolean</returnvalue>
+ </para>
+ <para>
+ Does user have privilege for parameter?
+ Allowable privilege types are <literal>SET</literal> and <literal>ALTER SYSTEM</literal>.
+ </para></entry>
+ </row>
+
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index 8c4edd9b0a..f86b2072f5 100644
--- a/doc/src/sgml/ref/grant.sgml
+++ b/doc/src/sgml/ref/grant.sgml
@@ -92,6 +92,11 @@ GRANT { USAGE | ALL [ PRIVILEGES ] }
TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
[ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
+GRANT { { SET | ALTER SYSTEM } [, ... ] | ALL [ PRIVILEGES ] }
+ ON PARAMETER <replaceable class="parameter">configuration_parameter</replaceable> [, ...]
+ TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+ [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
+
GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replaceable class="parameter">role_specification</replaceable> [, ...]
[ WITH ADMIN OPTION ]
[ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
@@ -185,6 +190,8 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
<term><literal>TEMPORARY</literal></term>
<term><literal>EXECUTE</literal></term>
<term><literal>USAGE</literal></term>
+ <term><literal>SET</literal></term>
+ <term><literal>ALTER SYSTEM</literal></term>
<listitem>
<para>
Specific types of privileges, as defined in <xref linkend="ddl-priv"/>.
diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml
index 3014c864ea..58455c519e 100644
--- a/doc/src/sgml/ref/revoke.sgml
+++ b/doc/src/sgml/ref/revoke.sgml
@@ -118,6 +118,13 @@ REVOKE [ GRANT OPTION FOR ]
[ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
[ CASCADE | RESTRICT ]
+REVOKE [ GRANT OPTION FOR ]
+ { { SET | ALTER SYSTEM } [, ...] | ALL [ PRIVILEGES ] }
+ ON PARAMETER <replaceable class="parameter">configuration_parameter</replaceable> [, ...]
+ FROM <replaceable class="parameter">role_specification</replaceable> [, ...]
+ [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
+ [ CASCADE | RESTRICT ]
+
REVOKE [ ADMIN OPTION FOR ]
<replaceable class="parameter">role_name</replaceable> [, ...] FROM <replaceable class="parameter">role_specification</replaceable> [, ...]
[ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
diff --git a/doc/src/sgml/ref/set.sgml b/doc/src/sgml/ref/set.sgml
index 339ee9eec9..465edcf4fd 100644
--- a/doc/src/sgml/ref/set.sgml
+++ b/doc/src/sgml/ref/set.sgml
@@ -34,10 +34,10 @@ SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="parameter">timezone</rep
parameters. Many of the run-time parameters listed in
<xref linkend="runtime-config"/> can be changed on-the-fly with
<command>SET</command>.
- (But some require superuser privileges to change, and others cannot
- be changed after server or session start.)
- <command>SET</command> only affects the value used by the current
- session.
+ (But some require either superuser privileges or granted
+ <literal>SET</literal> privileges to change, and others cannot be changed
+ after server or session start.) <command>SET</command> only affects the
+ value used by the current session.
</para>
<para>
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 87d7386e01..d14b957c14 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -29,6 +29,7 @@ OBJS = \
pg_cast.o \
pg_class.o \
pg_collation.o \
+ pg_parameter_acl.o \
pg_constraint.o \
pg_conversion.o \
pg_db_role_setting.o \
@@ -55,7 +56,7 @@ include $(top_srcdir)/src/backend/common.mk
# there are reputedly other, undocumented ordering dependencies.
CATALOG_HEADERS := \
pg_proc.h pg_type.h pg_attribute.h pg_class.h \
- pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \
+ pg_attrdef.h pg_parameter_acl.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \
pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \
pg_statistic.h pg_statistic_ext.h pg_statistic_ext_data.h \
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 1dd03a8e51..e3f1156fca 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -49,6 +49,7 @@
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_proc.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
@@ -112,6 +113,7 @@ static void ExecGrant_Largeobject(InternalGrant *grantStmt);
static void ExecGrant_Namespace(InternalGrant *grantStmt);
static void ExecGrant_Tablespace(InternalGrant *grantStmt);
static void ExecGrant_Type(InternalGrant *grantStmt);
+static void ExecGrant_Parameter(InternalGrant *grantStmt);
static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames);
static void SetDefaultACL(InternalDefaultACL *iacls);
@@ -259,6 +261,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
case OBJECT_TYPE:
whole_mask = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_PARAMETER:
+ whole_mask = ACL_ALL_RIGHTS_PARAMETER;
+ break;
default:
elog(ERROR, "unrecognized object type: %d", objtype);
/* not reached, but keep compiler quiet */
@@ -498,6 +503,10 @@ ExecuteGrantStmt(GrantStmt *stmt)
all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
errormsg = gettext_noop("invalid privilege type %s for foreign server");
break;
+ case OBJECT_PARAMETER:
+ all_privileges = ACL_ALL_RIGHTS_PARAMETER;
+ errormsg = gettext_noop("invalid privilege type %s for parameter");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
@@ -600,6 +609,9 @@ ExecGrantStmt_oids(InternalGrant *istmt)
case OBJECT_TABLESPACE:
ExecGrant_Tablespace(istmt);
break;
+ case OBJECT_PARAMETER:
+ ExecGrant_Parameter(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -759,6 +771,37 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_PARAMETER:
+ foreach(cell, objnames)
+ {
+ char *parameter = strVal(lfirst(cell));
+ Oid parameterId = get_parameter_oid(parameter, true);
+
+ if (!OidIsValid(parameterId))
+ {
+ /*
+ * Lookup the existing entry, or if missing, add a new
+ * entry for this parameter. Entries only exist for
+ * parameters which currently have, or previously have had,
+ * privileges assigned.
+ *
+ * We do not sanity-check here the given configuration
+ * parameter name against known guc names in the guc
+ * tables. Callers are responsible such checks, if needed.
+ */
+ parameterId = ParameterAclCreate(parameter, true);
+
+ /*
+ * Prevent error when processing duplicate objects, and
+ * make this new entry visible to our later selves which
+ * will need to update the Acl.
+ */
+ CommandCounterIncrement();
+ }
+
+ objects = lappend_oid(objects, parameterId);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -1494,6 +1537,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case ForeignDataWrapperRelationId:
istmt.objtype = OBJECT_FDW;
break;
+ case ParameterAclRelationId:
+ istmt.objtype = OBJECT_PARAMETER;
+ break;
default:
elog(ERROR, "unexpected object class %u", classid);
break;
@@ -3225,6 +3271,158 @@ ExecGrant_Type(InternalGrant *istmt)
table_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Parameter(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_PARAMETER;
+
+ relation = table_open(ParameterAclRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid parameterId = lfirst_oid(cell);
+ char *parameter = NULL;
+ Form_pg_parameter_acl pg_parameter_acl_tuple;
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ HeapTuple tuple;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_parameter_acl];
+ bool nulls[Natts_pg_parameter_acl];
+ bool replaces[Natts_pg_parameter_acl];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(PARAMETEROID, ObjectIdGetDatum(parameterId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for parameter %u", parameterId);
+
+ pg_parameter_acl_tuple = (Form_pg_parameter_acl) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ aclDatum = SysCacheGetAttr(PARAMETERNAME, tuple, Anum_pg_parameter_acl_setacl,
+ &isNull);
+
+ parameter = text_to_cstring(&pg_parameter_acl_tuple->parameter);
+
+ /*
+ * If the acl is null, we need to create a more permissive default acl
+ * for userset variables than for any others.
+ */
+ if (isNull)
+ {
+ GucContext context;
+
+ /*
+ * If we're processing a custom parameter that has not been created
+ * yet, we want to be conservative and avoid granting any
+ * privileges until module installation time, at which point the
+ * context will be known. We default to PGC_SUSET here, and let
+ * the module installation sql script handle updating the Acl to
+ * include grants to public. However, if this parameter already
+ * exists, we must return the appropriate default Acl for the
+ * parameter's context.
+ */
+ context = find_option_context(parameter, PGC_SUSET);
+
+ /*
+ * We only care whether the context is PGC_USERSET. All other
+ * contexts have no privileges granted to public by default.
+ */
+ old_acl = aclparameterdefault(context == PGC_USERSET);
+
+ /* There are no old member roles according to the catalogs */
+ noldmembers = 0;
+ oldmembers = NULL;
+ }
+ else
+ {
+ old_acl = DatumGetAclPCopy(aclDatum);
+ /* Get the roles mentioned in the existing ACL */
+ noldmembers = aclmembers(old_acl, &oldmembers);
+ }
+
+ /* Determine ID to do the grant as, and available grant options */
+ select_best_grantor(GetUserId(), istmt->privileges,
+ old_acl, BOOTSTRAP_SUPERUSERID,
+ &grantorId, &avail_goptions);
+
+ /*
+ * Restrict the privileges to what we can actually grant, and emit the
+ * standards-mandated warning and error messages.
+ */
+ this_privileges =
+ restrict_and_check_grant(istmt->is_grant, avail_goptions,
+ istmt->all_privs, istmt->privileges,
+ parameterId, grantorId, OBJECT_PARAMETER,
+ text_to_cstring(&pg_parameter_acl_tuple->parameter),
+ 0, NULL);
+
+ /*
+ * Generate new ACL.
+ */
+ new_acl = merge_acl_with_grant(old_acl, istmt->is_grant,
+ istmt->grant_option, istmt->behavior,
+ istmt->grantees, this_privileges,
+ grantorId, InvalidOid);
+
+ /*
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information.
+ */
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* finished building new ACL value, now insert it */
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ replaces[Anum_pg_parameter_acl_setacl - 1] = true;
+ values[Anum_pg_parameter_acl_setacl - 1] = PointerGetDatum(new_acl);
+
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values,
+ nulls, replaces);
+
+ CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
+
+ /* Update initial privileges for extensions */
+ recordExtensionInitPriv(parameterId, ParameterAclRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(ParameterAclRelationId,
+ pg_parameter_acl_tuple->oid, 0,
+ InvalidOid,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* Post alter hook called for grant and revoke */
+ InvokeObjectPostAlterHookStr(ParameterAclRelationId, parameter, istmt->privileges);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ table_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3255,6 +3453,10 @@ string_to_privilege(const char *privname)
return ACL_CREATE_TEMP;
if (strcmp(privname, "connect") == 0)
return ACL_CONNECT;
+ if (strcmp(privname, "set") == 0)
+ return ACL_SET;
+ if (strcmp(privname, "alter system") == 0)
+ return ACL_ALTER_SYSTEM;
if (strcmp(privname, "rule") == 0)
return 0; /* ignore old RULE privileges */
ereport(ERROR,
@@ -3292,6 +3494,10 @@ privilege_to_string(AclMode privilege)
return "TEMP";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET:
+ return "SET";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3328,6 +3534,13 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_COLUMN:
msg = gettext_noop("permission denied for column %s");
break;
+ case OBJECT_PARAMETER:
+ /*
+ * Quote the object name for backward compatibility
+ * with behavior before SET was handled here.
+ */
+ msg = gettext_noop("permission denied to set parameter \"%s\"");
+ break;
case OBJECT_CONVERSION:
msg = gettext_noop("permission denied for conversion %s");
break;
@@ -3564,6 +3777,7 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_AMPROC:
case OBJECT_ATTRIBUTE:
case OBJECT_CAST:
+ case OBJECT_PARAMETER:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
case OBJECT_DOMCONSTRAINT:
@@ -4000,6 +4214,59 @@ pg_database_aclmask(Oid db_oid, Oid roleid,
return result;
}
+/*
+ * Exported routine for examining a user's privileges for a configuration
+ * parameter (GUC)
+ */
+AclMode
+pg_parameter_acl_aclmask(Oid config_oid, Oid roleid,
+ AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /*
+ * Get the parameter's ACL from pg_parameter_acl
+ */
+ tuple = SearchSysCache1(PARAMETEROID, ObjectIdGetDatum(config_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("parameter with OID %u does not exist",
+ config_oid)));
+
+ aclDatum = SysCacheGetAttr(PARAMETEROID, tuple, Anum_pg_parameter_acl_setacl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_PARAMETER, InvalidOid);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, InvalidOid, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}
+
/*
* Exported routine for examining a user's privileges for a function
*/
@@ -4713,6 +4980,19 @@ pg_database_aclcheck(Oid db_oid, Oid roleid, AclMode mode)
return ACLCHECK_NO_PRIV;
}
+/*
+ * Exported routine for checking a user's access privileges to a configuration
+ * parameter
+ */
+AclResult
+pg_parameter_acl_aclcheck(Oid config_oid, Oid roleid, AclMode mode)
+{
+ if (pg_parameter_acl_aclmask(config_oid, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
/*
* Exported routine for checking a user's access privileges to a function
*/
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index dfd5fb669e..f91924cae3 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -34,6 +34,7 @@
#include "catalog/pg_largeobject.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_replication_origin.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
#include "catalog/pg_shseclabel.h"
@@ -246,6 +247,7 @@ IsSharedRelation(Oid relationId)
/* These are the shared catalogs (look for BKI_SHARED_RELATION) */
if (relationId == AuthIdRelationId ||
relationId == AuthMemRelationId ||
+ relationId == ParameterAclRelationId ||
relationId == DatabaseRelationId ||
relationId == SharedDescriptionRelationId ||
relationId == SharedDependRelationId ||
@@ -260,6 +262,8 @@ IsSharedRelation(Oid relationId)
relationId == AuthIdOidIndexId ||
relationId == AuthMemRoleMemIndexId ||
relationId == AuthMemMemRoleIndexId ||
+ relationId == ParameterAclParameterIndexId ||
+ relationId == ParameterAclOidIndexId ||
relationId == DatabaseNameIndexId ||
relationId == DatabaseOidIndexId ||
relationId == SharedDescriptionObjIndexId ||
@@ -277,6 +281,8 @@ IsSharedRelation(Oid relationId)
/* These are their toast tables and toast indexes */
if (relationId == PgAuthidToastTable ||
relationId == PgAuthidToastIndex ||
+ relationId == PgParameterAclToastTable ||
+ relationId == PgParameterAclToastIndex ||
relationId == PgDatabaseToastTable ||
relationId == PgDatabaseToastIndex ||
relationId == PgDbRoleSettingToastTable ||
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 25fe56d310..013fc6ee7d 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -52,6 +52,7 @@
#include "catalog/pg_publication_namespace.h"
#include "catalog/pg_publication_rel.h"
#include "catalog/pg_rewrite.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
@@ -178,6 +179,7 @@ static const Oid object_classes[] = {
DefaultAclRelationId, /* OCLASS_DEFACL */
ExtensionRelationId, /* OCLASS_EXTENSION */
EventTriggerRelationId, /* OCLASS_EVENT_TRIGGER */
+ ParameterAclRelationId, /* OCLASS_PARAMETER */
PolicyRelationId, /* OCLASS_POLICY */
PublicationNamespaceRelationId, /* OCLASS_PUBLICATION_NAMESPACE */
PublicationRelationId, /* OCLASS_PUBLICATION */
@@ -1503,6 +1505,7 @@ doDeletion(const ObjectAddress *object, int flags)
/*
* These global object types are not supported here.
*/
+ case OCLASS_PARAMETER:
case OCLASS_ROLE:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
@@ -2861,6 +2864,9 @@ getObjectClass(const ObjectAddress *object)
case EventTriggerRelationId:
return OCLASS_EVENT_TRIGGER;
+ case ParameterAclRelationId:
+ return OCLASS_PARAMETER;
+
case PolicyRelationId:
return OCLASS_POLICY;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 3fd17ea64f..4754d15c81 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -51,6 +51,7 @@
#include "catalog/pg_publication_namespace.h"
#include "catalog/pg_publication_rel.h"
#include "catalog/pg_rewrite.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
@@ -2285,6 +2286,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_COLUMN:
case OBJECT_ATTRIBUTE:
case OBJECT_COLLATION:
+ case OBJECT_PARAMETER:
case OBJECT_CONVERSION:
case OBJECT_STATISTIC_EXT:
case OBJECT_TSPARSER:
@@ -3880,6 +3882,22 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
break;
}
+ case OCLASS_PARAMETER:
+ {
+ char *parameter;
+
+ parameter = get_parameter_name(object->objectId);
+ if (!parameter)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for parameter %u",
+ object->objectId);
+ break;
+ }
+ appendStringInfo(&buffer, _("parameter %s"), parameter);
+ break;
+ }
+
case OCLASS_POLICY:
{
Relation policy_rel;
@@ -4547,6 +4565,10 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "event trigger");
break;
+ case OCLASS_PARAMETER:
+ appendStringInfoString(&buffer, "parameter");
+ break;
+
case OCLASS_POLICY:
appendStringInfoString(&buffer, "policy");
break;
@@ -5693,6 +5715,30 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_PARAMETER:
+ {
+ HeapTuple configTup;
+ Form_pg_parameter_acl configForm;
+ char *namestr;
+
+ configTup = SearchSysCache1(PARAMETEROID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(configTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for parameter %u",
+ object->objectId);
+ break;
+ }
+ configForm = (Form_pg_parameter_acl) GETSTRUCT(configTup);
+ namestr = text_to_cstring(&configForm->parameter);
+ appendStringInfoString(&buffer, namestr);
+ if (objname)
+ *objname = list_make1(namestr);
+ ReleaseSysCache(configTup);
+ break;
+ }
+
case OCLASS_POLICY:
{
Relation polDesc;
diff --git a/src/backend/catalog/pg_parameter_acl.c b/src/backend/catalog/pg_parameter_acl.c
new file mode 100644
index 0000000000..d5aa998a59
--- /dev/null
+++ b/src/backend/catalog/pg_parameter_acl.c
@@ -0,0 +1,109 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_parameter_acl.c
+ * routines to support manipulation of the pg_parameter_acl relation
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_parameter_acl.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/table.h"
+#include "catalog/catalog.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_parameter_acl.h"
+#include "utils/builtins.h"
+#include "utils/pg_locale.h"
+#include "utils/rel.h"
+
+
+/*
+ * ParameterAclCreate
+ *
+ * Add a new tuple to pg_parameter_acl.
+ *
+ * parameter: the parameter name to create.
+ * if_not_exists: if true, don't fail on duplicate name, but rather return
+ * the existing entry's Oid.
+ */
+Oid
+ParameterAclCreate(const char *parameter, bool if_not_exists)
+{
+ Relation rel;
+ TupleDesc tupDesc;
+ HeapTuple tuple;
+ Datum values[Natts_pg_parameter_acl];
+ bool nulls[Natts_pg_parameter_acl];
+ Oid parameterId;
+ const char *canonical;
+
+ /*
+ * Check whether a parameter ACL (by the given name or alias) already
+ * exists.
+ */
+ parameterId = get_parameter_oid(parameter, true);
+ if (OidIsValid(parameterId))
+ {
+ if (if_not_exists)
+ return parameterId;
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("parameter \"%s\" already exists",
+ parameter)));
+ }
+
+ /*
+ * Perform a basic sanity check of the parameter name, and translate old
+ * forms of known names to their canonical forms.
+ *
+ * If you deprecate a configuration name in favor of a new spelling, be
+ * sure to consider whether to also upgrade pg_parameter_acl entries.
+ */
+ if (!valid_variable_name(parameter, NULL))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid parameter name \"%s\"",
+ parameter)));
+ canonical = GetConfigOptionCanonicalName(parameter);
+ if (!canonical)
+ canonical = parameter;
+
+ /*
+ * Create and insert a new record, starting with a blank Acl.
+ *
+ * We don't take a strong enough lock to prevent concurrent insertions,
+ * relying instead on the unique index.
+ */
+ rel = table_open(ParameterAclRelationId, RowExclusiveLock);
+ tupDesc = RelationGetDescr(rel);
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ values[Anum_pg_parameter_acl_parameter - 1] =
+ DirectFunctionCall1(textin, CStringGetDatum(canonical));
+ parameterId = GetNewOidWithIndex(rel,
+ ParameterAclOidIndexId,
+ Anum_pg_parameter_acl_oid);
+ values[Anum_pg_parameter_acl_oid - 1] = ObjectIdGetDatum(parameterId);
+ nulls[Anum_pg_parameter_acl_setacl - 1] = true;
+ tuple = heap_form_tuple(tupDesc, values, nulls);
+ CatalogTupleInsert(rel, tuple);
+
+ /* Post creation hook for new parameter */
+ InvokeObjectPostCreateHookStr(ParameterAclRelationId, parameter, 0);
+
+ /*
+ * Close pg_parameter_acl, but keep lock till commit.
+ */
+ heap_freetuple(tuple);
+ table_close(rel, NoLock);
+
+ return parameterId;
+}
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 9eaa51df29..9229d56c8e 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -605,6 +605,19 @@ CREATE RULE pg_settings_n AS
GRANT SELECT, UPDATE ON pg_settings TO PUBLIC;
+CREATE VIEW pg_parameter_privileges AS
+ SELECT grantor.rolname AS grantor,
+ grantee.rolname AS grantee,
+ set_acl.parameter AS parameter,
+ acl.privilege_type AS privilege_type,
+ acl.is_grantable
+ FROM pg_catalog.pg_parameter_acl set_acl,
+ LATERAL (SELECT * FROM aclexplode(set_acl.setacl)) acl
+ LEFT JOIN pg_catalog.pg_authid grantee ON acl.grantee = grantee.oid
+ LEFT JOIN pg_catalog.pg_authid grantor ON acl.grantor = grantor.oid;
+
+GRANT SELECT ON pg_parameter_privileges TO PUBLIC;
+
CREATE VIEW pg_file_settings AS
SELECT * FROM pg_show_all_file_settings() AS A;
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 1f64c8aa51..e457cd8816 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -658,6 +658,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
case OCLASS_DEFACL:
case OCLASS_EXTENSION:
case OCLASS_EVENT_TRIGGER:
+ case OCLASS_PARAMETER:
case OCLASS_POLICY:
case OCLASS_PUBLICATION:
case OCLASS_PUBLICATION_NAMESPACE:
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 3c3fc2515b..e02b839421 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -937,6 +937,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
{
switch (obtype)
{
+ case OBJECT_PARAMETER:
case OBJECT_DATABASE:
case OBJECT_TABLESPACE:
case OBJECT_ROLE:
@@ -1012,6 +1013,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
{
switch (objclass)
{
+ case OCLASS_PARAMETER:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
@@ -2022,6 +2024,8 @@ stringify_grant_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMN";
+ case OBJECT_PARAMETER:
+ return "PARAMETER";
case OBJECT_TABLE:
return "TABLE";
case OBJECT_SEQUENCE:
@@ -2105,6 +2109,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMNS";
+ case OBJECT_PARAMETER:
+ return "PARAMETERS";
case OBJECT_TABLE:
return "TABLES";
case OBJECT_SEQUENCE:
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 7a62d547e2..73933c5d07 100644
--- a/src/backend/commands/seclabel.c
+++ b/src/backend/commands/seclabel.c
@@ -67,6 +67,7 @@ SecLabelSupportsObjectType(ObjectType objtype)
case OBJECT_ATTRIBUTE:
case OBJECT_CAST:
case OBJECT_COLLATION:
+ case OBJECT_PARAMETER:
case OBJECT_CONVERSION:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 51b4a00d50..9ab5d1b107 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -12655,6 +12655,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_DEFACL:
case OCLASS_EXTENSION:
case OCLASS_EVENT_TRIGGER:
+ case OCLASS_PARAMETER:
case OCLASS_PUBLICATION:
case OCLASS_PUBLICATION_NAMESPACE:
case OCLASS_PUBLICATION_REL:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8ad8512e4c..05d0485576 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -371,8 +371,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> foreign_server_version opt_foreign_server_version
%type <str> opt_in_database
-%type <str> OptSchemaName
-%type <list> OptSchemaEltList
+%type <str> OptSchemaName parameter_name
+%type <list> OptSchemaEltList parameter_target
%type <chr> am_type
@@ -796,7 +796,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
ORDER ORDINALITY OTHERS OUT_P OUTER_P
OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER
- PARALLEL PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS POLICY
+ PARALLEL PARAMETER PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS POLICY
POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION
@@ -7070,6 +7070,20 @@ GrantStmt: GRANT privileges ON privilege_target TO grantee_list
n->grantor = $8;
$$ = (Node*)n;
}
+ | GRANT privileges ON PARAMETER parameter_target TO grantee_list
+ opt_grant_grant_option opt_granted_by
+ {
+ GrantStmt *n = makeNode(GrantStmt);
+ n->is_grant = true;
+ n->privileges = $2;
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = OBJECT_PARAMETER;
+ n->objects = $5;
+ n->grantees = $7;
+ n->grant_option = $8;
+ n->grantor = $9;
+ $$ = (Node*)n;
+ }
;
RevokeStmt:
@@ -7103,6 +7117,36 @@ RevokeStmt:
n->behavior = $11;
$$ = (Node *)n;
}
+ | REVOKE privileges ON PARAMETER parameter_target FROM grantee_list
+ opt_granted_by opt_drop_behavior
+ {
+ GrantStmt *n = makeNode(GrantStmt);
+ n->is_grant = false;
+ n->grant_option = false;
+ n->privileges = $2;
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = OBJECT_PARAMETER;
+ n->objects = $5;
+ n->grantees = $7;
+ n->grantor = $8;
+ n->behavior = $9;
+ $$ = (Node *)n;
+ }
+ | REVOKE GRANT OPTION FOR privileges ON PARAMETER parameter_target
+ FROM grantee_list opt_granted_by opt_drop_behavior
+ {
+ GrantStmt *n = makeNode(GrantStmt);
+ n->is_grant = false;
+ n->grant_option = true;
+ n->privileges = $5;
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = OBJECT_PARAMETER;
+ n->objects = $8;
+ n->grantees = $10;
+ n->grantor = $11;
+ n->behavior = $12;
+ $$ = (Node *)n;
+ }
;
@@ -7162,6 +7206,13 @@ privilege: SELECT opt_column_list
n->cols = $2;
$$ = n;
}
+ | ALTER SYSTEM_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("alter system");
+ n->cols = NULL;
+ $$ = n;
+ }
| ColId opt_column_list
{
AccessPriv *n = makeNode(AccessPriv);
@@ -7171,6 +7222,27 @@ privilege: SELECT opt_column_list
}
;
+parameter_target:
+ parameter_name
+ {
+ $$ = list_make1(makeString($1));
+ }
+ | parameter_target ',' parameter_name
+ {
+ $$ = lappend($1, makeString($3));
+ }
+ ;
+
+parameter_name:
+ ColId
+ {
+ $$ = $1;
+ }
+ | parameter_name '.' ColId
+ {
+ $$ = psprintf("%s.%s", $1, $3);
+ }
+ ;
/* Don't bother trying to fold the first two rules into one using
* opt_table. You're going to get conflicts.
@@ -16713,6 +16785,7 @@ unreserved_keyword:
| OWNED
| OWNER
| PARALLEL
+ | PARAMETER
| PARSER
| PARTIAL
| PARTITION
@@ -17320,6 +17393,7 @@ bare_label_keyword:
| OWNED
| OWNER
| PARALLEL
+ | PARAMETER
| PARSER
| PARTIAL
| PARTITION
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 83cf7ac9ff..205bb5f805 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -23,6 +23,7 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_class.h"
#include "catalog/pg_database.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "commands/proclang.h"
@@ -36,6 +37,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
+#include "utils/guc.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
@@ -109,6 +111,7 @@ static Oid convert_tablespace_name(text *tablespacename);
static AclMode convert_tablespace_priv_string(text *priv_type_text);
static Oid convert_type_name(text *typename);
static AclMode convert_type_priv_string(text *priv_type_text);
+static AclMode convert_parameter_priv_string(text *priv_parameter_text);
static AclMode convert_role_priv_string(text *priv_type_text);
static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode);
@@ -306,6 +309,12 @@ aclparse(const char *s, AclItem *aip)
case ACL_CONNECT_CHR:
read = ACL_CONNECT;
break;
+ case ACL_SET_CHR:
+ read = ACL_SET;
+ break;
+ case ACL_ALTER_SYSTEM_CHR:
+ read = ACL_ALTER_SYSTEM;
+ break;
case 'R': /* ignore old RULE privileges */
read = 0;
break;
@@ -794,6 +803,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_PARAMETER:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_PARAMETER;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -838,6 +851,41 @@ acldefault(ObjectType objtype, Oid ownerId)
return acl;
}
+/*
+ * aclparameterdefault()
+ *
+ * Creates and returns an ACL describing the default access permissions for a
+ * parameter, taking into account whether it is a PGC_USERSET GUC parameter.
+ */
+Acl *
+aclparameterdefault(bool is_userset)
+{
+ Acl *acl;
+
+ /*
+ * Treat all parameters as belonging to the bootstrap user. This works
+ * better than passing InvalidOid, as we want the bootstrap user to retain
+ * privileges even after running a REVOKE .. FROM PUBLIC on a default ACL.
+ */
+ acl = acldefault(OBJECT_PARAMETER, BOOTSTRAP_SUPERUSERID);
+
+ /*
+ * Special case for USERSET gucs. By default, public can SET.
+ */
+ if (is_userset)
+ {
+ AclItem item;
+
+ item.ai_grantee = ACL_ID_PUBLIC;
+ item.ai_grantor = BOOTSTRAP_SUPERUSERID;
+ item.ai_privs = ACL_SET;
+
+ acl = aclupdate(acl, &item, ACL_MODECHG_ADD, BOOTSTRAP_SUPERUSERID,
+ DROP_RESTRICT);
+ }
+
+ return acl;
+}
/*
* SQL-accessible version of acldefault(). Hackish mapping from "char" type to
@@ -895,6 +943,35 @@ acldefault_sql(PG_FUNCTION_ARGS)
PG_RETURN_ACL_P(acldefault(objtype, owner));
}
+/*
+ * SQL-accessible version of aclparameterdefault().
+ */
+Datum
+aclparameterdefault_sql(PG_FUNCTION_ARGS)
+{
+ const char *parameter;
+ GucContext context;
+
+ parameter = text_to_cstring(PG_GETARG_TEXT_P(0));
+
+ /*
+ * Look up the context of the given parameter so we know what kind of
+ * default Acl to generate. If we're processing a custom parameter that
+ * has not been created yet, we want to be conservative and avoid granting
+ * any privileges until parameter creation time, at which point the context
+ * will be known. We default to PGC_SUSET here, and let the parameter
+ * creation code handle updating the Acl to include grants to public.
+ * However, if this parameter already exists, we must return the
+ * appropriate default Acl for the parameter's context.
+ */
+ context = find_option_context(parameter, PGC_SUSET);
+
+ /*
+ * We only care whether the context is PGC_USERSET. All other contexts
+ * have no privileges granted to public by default.
+ */
+ PG_RETURN_ACL_P(aclparameterdefault(context == PGC_USERSET));
+}
/*
* Update an ACL array to add or remove specified privileges.
@@ -1602,6 +1679,10 @@ convert_priv_string(text *priv_type_text)
return ACL_CREATE_TEMP;
if (pg_strcasecmp(priv_type, "CONNECT") == 0)
return ACL_CONNECT;
+ if (pg_strcasecmp(priv_type, "SET") == 0)
+ return ACL_SET;
+ if (pg_strcasecmp(priv_type, "ALTER SYSTEM") == 0)
+ return ACL_ALTER_SYSTEM;
if (pg_strcasecmp(priv_type, "RULE") == 0)
return 0; /* ignore old RULE privileges */
@@ -1698,6 +1779,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET:
+ return "SET";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
@@ -4429,6 +4514,191 @@ convert_type_priv_string(text *priv_type_text)
return convert_any_priv_string(priv_type_text, type_priv_map);
}
+/*
+ * has_parameter_privilege variants
+ * These are all named "has_parameter_privilege" at the SQL level.
+ * They take various combinations of parameter name, parameter OID,
+ * user name, user OID, or implicit user = current_user.
+ *
+ * The result is a boolean value: true if user has the indicated
+ * privilege, false if not, or NULL if object doesn't exist.
+ */
+
+/*
+ * has_parameter_privilege_name_name
+ * Check user privileges on a parameter given name username, text
+ * parameter, and text priv name.
+ */
+Datum
+has_parameter_privilege_name_name(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ text *parameter = PG_GETARG_TEXT_PP(1);
+ text *priv_parameter_text = PG_GETARG_TEXT_PP(2);
+ Oid roleid;
+ Oid parameteroid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_role_oid_or_public(NameStr(*username));
+
+ parameteroid = get_parameter_oid(text_to_cstring(parameter), true);
+ if (!OidIsValid(parameteroid))
+ PG_RETURN_NULL();
+ mode = convert_parameter_priv_string(priv_parameter_text);
+
+ aclresult = pg_parameter_acl_aclcheck(parameteroid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_parameter_privilege_name
+ * Check user privileges on a parameter given text parameter and text priv
+ * name. current_user is assumed
+ */
+Datum
+has_parameter_privilege_name(PG_FUNCTION_ARGS)
+{
+ text *parameter = PG_GETARG_TEXT_PP(0);
+ text *priv_parameter_text = PG_GETARG_TEXT_PP(1);
+ Oid roleid;
+ Oid parameteroid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ parameteroid = get_parameter_oid(text_to_cstring(parameter), true);
+ if (!OidIsValid(parameteroid))
+ PG_RETURN_NULL();
+ mode = convert_parameter_priv_string(priv_parameter_text);
+
+ aclresult = pg_parameter_acl_aclcheck(parameteroid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_parameter_privilege_name_id
+ * Check user privileges on a parameter given name usename, parameter oid,
+ * and text priv name.
+ */
+Datum
+has_parameter_privilege_name_id(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ Oid parameteroid = PG_GETARG_OID(1);
+ text *priv_parameter_text = PG_GETARG_TEXT_PP(2);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_role_oid_or_public(NameStr(*username));
+ mode = convert_parameter_priv_string(priv_parameter_text);
+
+ if (!SearchSysCacheExists1(PARAMETEROID, ObjectIdGetDatum(parameteroid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_parameter_acl_aclcheck(parameteroid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_parameter_privilege_id
+ * Check user privileges on a parameter given parameter oid, and text priv
+ * name. current_user is assumed.
+ */
+Datum
+has_parameter_privilege_id(PG_FUNCTION_ARGS)
+{
+ Oid parameteroid = PG_GETARG_OID(0);
+ text *priv_parameter_text = PG_GETARG_TEXT_PP(1);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ mode = convert_parameter_priv_string(priv_parameter_text);
+
+ if (!SearchSysCacheExists1(PARAMETEROID, ObjectIdGetDatum(parameteroid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_parameter_acl_aclcheck(parameteroid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_parameter_privilege_id_name
+ * Check user privileges on a parameter given roleid, text parameter, and
+ * text priv name.
+ */
+Datum
+has_parameter_privilege_id_name(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ text *parameter = PG_GETARG_TEXT_PP(1);
+ text *priv_parameter_text = PG_GETARG_TEXT_PP(2);
+ Oid parameteroid;
+ AclMode mode;
+ AclResult aclresult;
+
+ parameteroid = get_parameter_oid(text_to_cstring(parameter), true);
+ if (!OidIsValid(parameteroid))
+ PG_RETURN_NULL();
+ mode = convert_parameter_priv_string(priv_parameter_text);
+
+ aclresult = pg_parameter_acl_aclcheck(parameteroid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_parameter_privilege_id_id
+ * Check user privileges on a parameter given roleid, parameter oid, and
+ * text priv name.
+ */
+Datum
+has_parameter_privilege_id_id(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ Oid parameteroid = PG_GETARG_OID(1);
+ text *priv_parameter_text = PG_GETARG_TEXT_PP(2);
+ AclMode mode;
+ AclResult aclresult;
+
+ mode = convert_parameter_priv_string(priv_parameter_text);
+
+ if (!SearchSysCacheExists1(PARAMETEROID, ObjectIdGetDatum(parameteroid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_parameter_acl_aclcheck(parameteroid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * Support routines for has_parameter_privilege family.
+ */
+
+/*
+ * convert_parameter_priv_string
+ * Convert text string to AclMode value.
+ */
+static AclMode
+convert_parameter_priv_string(text *priv_parameter_text)
+{
+ static const priv_map parameter_priv_map[] = {
+ {"SET", ACL_SET},
+ {"SET WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SET)},
+ {"ALTER SYSTEM", ACL_ALTER_SYSTEM},
+ {"ALTER SYSTEM WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_ALTER_SYSTEM)},
+ {NULL, 0}
+ };
+
+ return convert_any_priv_string(priv_parameter_text, parameter_priv_map);
+}
/*
* pg_has_role variants
@@ -4665,6 +4935,43 @@ initialize_acl(void)
}
}
+/*
+ * get_parameter_oid - Given a configuration parameter name, look up the
+ * configuration parameter's OID. Note that names which are aliases for
+ * a canonical name will be translated automatically and the OID found.
+ *
+ * If missing_ok is false, throw an error if the configuration parameter name
+ * is not found.
+ *
+ * Returns the Oid of the configuration parameter.
+ */
+Oid
+get_parameter_oid(const char *parameter, bool missing_ok)
+{
+ Oid oid;
+
+ /* Check for the variable by the name we were given */
+ oid = GetSysCacheOid1(PARAMETERNAME, Anum_pg_parameter_acl_oid,
+ PointerGetDatum(cstring_to_text(parameter)));
+ if (!OidIsValid(oid))
+ {
+ const char *canonical;
+
+ /* Check if the variable has a different canonical spelling */
+ canonical = GetConfigOptionCanonicalName(parameter);
+ if (canonical != NULL)
+ oid = GetSysCacheOid1(PARAMETERNAME, Anum_pg_parameter_acl_oid,
+ PointerGetDatum(cstring_to_text(canonical)));
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("parameter \"%s\" does not exist", parameter)));
+ }
+
+ return oid;
+}
+
/*
* RoleMembershipCacheCallback
* Syscache inval callback function
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 1b7e11b93e..8c669680e3 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -32,6 +32,7 @@
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_range.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_transform.h"
#include "catalog/pg_type.h"
@@ -3314,6 +3315,25 @@ free_attstatsslot(AttStatsSlot *sslot)
pfree(sslot->numbers_arr);
}
+char *
+get_parameter_name(Oid configid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(PARAMETEROID, ObjectIdGetDatum(configid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_parameter_acl configtup = (Form_pg_parameter_acl) GETSTRUCT(tp);
+ char *result;
+
+ result = pstrdup(text_to_cstring(&configtup->parameter));
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return NULL;
+}
+
/* ---------- PG_NAMESPACE CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index a675877d19..0fe94e8339 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -57,6 +57,7 @@
#include "catalog/pg_rewrite.h"
#include "catalog/pg_seclabel.h"
#include "catalog/pg_sequence.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
#include "catalog/pg_shseclabel.h"
@@ -574,6 +575,28 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {ParameterAclRelationId, /* PARAMETERNAME */
+ ParameterAclParameterIndexId,
+ 1,
+ {
+ Anum_pg_parameter_acl_parameter,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+ {ParameterAclRelationId, /* PARAMETEROID */
+ ParameterAclOidIndexId,
+ 1,
+ {
+ Anum_pg_parameter_acl_oid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{PartitionedRelationId, /* PARTRELID */
PartitionedRelidIndexId,
1,
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index c70543fa74..a61fec4b40 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -282,7 +282,7 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
* Try to find the variable; but do not create a custom placeholder if
* it's not there already.
*/
- record = find_option(item->name, false, true, elevel);
+ record = find_option(item->name, true, elevel);
if (record)
{
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index eb3a03b976..4b49449937 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -45,6 +45,7 @@
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_authid.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/storage.h"
#include "commands/async.h"
#include "commands/prepare.h"
@@ -5069,6 +5070,10 @@ static struct config_enum ConfigureNamesEnum[] =
* the following mappings to any unrecognized name. Note that an old name
* should be mapped to a new one only if the new variable has very similar
* semantics to the old.
+ *
+ * If you deprecate a name in favor of a new spelling, be sure to consider what
+ * upgrade support will be needed, if any, for existing pg_parameter_acl
+ * entries.
*/
static const char *const map_old_guc_names[] = {
"sort_mem", "work_mem",
@@ -5468,25 +5473,29 @@ add_guc_variable(struct config_generic *var, int elevel)
}
/*
- * Decide whether a proposed custom variable name is allowed.
+ * Decide whether a proposed variable name is allowed.
*
- * It must be two or more identifiers separated by dots, where the rules
- * for what is an identifier agree with scan.l. (If you change this rule,
- * adjust the errdetail in find_option().)
+ * It must be one or more identifiers separated by zero or more dots, where the
+ * rules for what is an identifier agree with scan.l. (If you change this
+ * rule, adjust the errdetail in find_option_internal().)
+ *
+ * partcnt: returns by reference the number of dot separated identifiers.
*/
-static bool
-valid_custom_variable_name(const char *name)
+bool
+valid_variable_name(const char *name, int *partcnt)
{
- bool saw_sep = false;
+ int parts = 1;
bool name_start = true;
+ if (partcnt)
+ *partcnt = -1;
for (const char *p = name; *p; p++)
{
if (*p == GUC_QUALIFIER_SEPARATOR)
{
if (name_start)
return false; /* empty name component */
- saw_sep = true;
+ parts++;
name_start = true;
}
else if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@@ -5503,15 +5512,34 @@ valid_custom_variable_name(const char *name)
}
if (name_start)
return false; /* empty name component */
- /* OK if we found at least one separator */
- return saw_sep;
+ if (partcnt)
+ *partcnt = parts;
+ return true;
}
+
/*
- * Create and add a placeholder variable for a custom variable name.
+ * Decide whether a proposed custom variable name is allowed.
+ *
+ * It must be two or more identifiers separated by dots, where the rules
+ * for what is an identifier agree with scan.l. (If you change this rule,
+ * adjust the errdetail in find_option_internal().)
+ */
+static bool
+valid_custom_variable_name(const char *name)
+{
+ int partcnt;
+
+ return (valid_variable_name(name, &partcnt) && partcnt > 1);
+}
+
+/*
+ * Create and add a placeholder variable for a custom variable name
+ * in the given context.
*/
static struct config_generic *
-add_placeholder_variable(const char *name, int elevel)
+add_placeholder_variable(const char *name, GucContext placeholder_context,
+ int elevel)
{
size_t sz = sizeof(struct config_string) + sizeof(char *);
struct config_string *var;
@@ -5530,7 +5558,7 @@ add_placeholder_variable(const char *name, int elevel)
return NULL;
}
- gen->context = PGC_USERSET;
+ gen->context = placeholder_context;
gen->group = CUSTOM_OPTIONS;
gen->short_desc = "GUC placeholder variable";
gen->flags = GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_CUSTOM_PLACEHOLDER;
@@ -5555,11 +5583,12 @@ add_placeholder_variable(const char *name, int elevel)
/*
* Look up option "name". If it exists, return a pointer to its record.
- * Otherwise, if create_placeholders is true and name is a valid-looking
- * custom variable name, we'll create and return a placeholder record.
- * Otherwise, if skip_errors is true, then we silently return NULL for
- * an unrecognized or invalid name. Otherwise, the error is reported at
- * error level elevel (and we return NULL if that's less than ERROR).
+ * Otherwise, if create_placeholders is true and name is a valid-looking custom
+ * variable name, we'll create and return a placeholder record with the given
+ * placeholder context. Otherwise, if skip_errors is true, then we silently
+ * return NULL for an unrecognized or invalid name. Otherwise, the error is
+ * reported at error level elevel (and we return NULL if that's less than
+ * ERROR).
*
* Note: internal errors, primarily out-of-memory, draw an elevel-level
* report and NULL return regardless of skip_errors. Hence, callers must
@@ -5569,8 +5598,9 @@ add_placeholder_variable(const char *name, int elevel)
* false need not think terribly hard about this.)
*/
static struct config_generic *
-find_option(const char *name, bool create_placeholders, bool skip_errors,
- int elevel)
+find_option_internal(const char *name, bool create_placeholders,
+ GucContext placeholder_context, bool skip_errors,
+ int elevel)
{
const char **key = &name;
struct config_generic **res;
@@ -5598,8 +5628,8 @@ find_option(const char *name, bool create_placeholders, bool skip_errors,
for (i = 0; map_old_guc_names[i] != NULL; i += 2)
{
if (guc_name_compare(name, map_old_guc_names[i]) == 0)
- return find_option(map_old_guc_names[i + 1], false,
- skip_errors, elevel);
+ return find_option_internal(map_old_guc_names[i + 1], false,
+ placeholder_context, skip_errors, elevel);
}
if (create_placeholders)
@@ -5646,7 +5676,7 @@ find_option(const char *name, bool create_placeholders, bool skip_errors,
}
}
/* OK, create it */
- return add_placeholder_variable(name, elevel);
+ return add_placeholder_variable(name, placeholder_context, elevel);
}
}
@@ -5659,6 +5689,19 @@ find_option(const char *name, bool create_placeholders, bool skip_errors,
return NULL;
}
+static struct config_generic *
+find_option(const char *name, bool skip_errors, int elevel)
+{
+ return find_option_internal(name, false, PGC_USERSET, skip_errors, elevel);
+}
+
+static struct config_generic *
+find_or_create_option(const char *name, GucContext placeholder_context,
+ bool skip_errors, int elevel)
+{
+ return find_option_internal(name, true, placeholder_context, skip_errors,
+ elevel);
+}
/*
* comparator for qsorting and bsearching guc_variables array
@@ -6673,7 +6716,7 @@ ReportChangedGUCOptions(void)
{
struct config_generic *record;
- record = find_option("in_hot_standby", false, false, ERROR);
+ record = find_option("in_hot_standby", false, ERROR);
Assert(record != NULL);
record->status |= GUC_NEEDS_REPORT;
report_needed = true;
@@ -7447,7 +7490,7 @@ set_config_option(const char *name, const char *value,
(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
errmsg("cannot set parameters during a parallel operation")));
- record = find_option(name, true, false, elevel);
+ record = find_or_create_option(name, PGC_USERSET, false, elevel);
if (record == NULL)
return 0;
@@ -7558,6 +7601,24 @@ set_config_option(const char *name, const char *value,
case PGC_SUSET:
if (context == PGC_USERSET || context == PGC_BACKEND)
{
+ /*
+ * Check whether the current user has granted privilege to set
+ * this GUC.
+ */
+ Oid parameterId = get_parameter_oid(name, true);
+
+ if (OidIsValid(parameterId))
+ {
+ AclResult aclresult;
+
+ aclresult = pg_parameter_acl_aclcheck(parameterId, GetUserId(),
+ ACL_SET);
+
+ if (aclresult == ACLCHECK_OK)
+ break; /* okay */
+ }
+
+ /* No granted privilege */
ereport(elevel,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to set parameter \"%s\"",
@@ -7566,7 +7627,35 @@ set_config_option(const char *name, const char *value,
}
break;
case PGC_USERSET:
- /* always okay */
+ if (context == PGC_USERSET)
+ {
+ /*
+ * If this GUC parameter has an entry in pg_parameter_acl, then the
+ * current user must have privileges per the acl to set this guc.
+ * Otherwise, all users are implicitly allowed to set it.
+ */
+ Oid parameterId = get_parameter_oid(name, true);
+
+ if (OidIsValid(parameterId))
+ {
+ AclResult aclresult;
+
+ aclresult = pg_parameter_acl_aclcheck(parameterId, GetUserId(),
+ ACL_SET);
+
+ if (aclresult == ACLCHECK_OK)
+ break; /* okay */
+
+ /* No granted privilege */
+ ereport(elevel,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+ return 0;
+ }
+
+ /* No pg_parameter_acl entry, okay */
+ }
break;
}
@@ -8156,6 +8245,49 @@ set_config_option(const char *name, const char *value,
}
+/*
+ * Get the context required to set the parameter, or the given default context
+ * if the named parameter cannot be found.
+ */
+GucContext
+find_option_context(const char *name, GucContext default_context)
+{
+ struct config_generic *conf;
+
+ /*
+ * Find the option if it exists, but do not error nor create a placeholder
+ * for missing options.
+ */
+ conf = find_option(name, true, ERROR);
+ if (conf)
+ return conf->context;
+
+ /*
+ * If the given name does not contain a separator, then we should not treat
+ * it as a custom parameter name. Emit an error about the parameter being
+ * unrecognized.
+ */
+ if (strchr(name, GUC_QUALIFIER_SEPARATOR) == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("unrecognized configuration parameter \"%s\"",
+ name)));
+
+ /*
+ * Otherwise, assume the user intends this as a placeholder for a custom
+ * parameter name, and only insist that the name by syntactically valid.
+ */
+ if (!valid_custom_variable_name(name))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid configuration parameter name \"%s\"",
+ name),
+ errdetail("Custom parameter names must be two or more simple identifiers separated by dots.")));
+
+ return default_context;
+}
+
+
/*
* Set the fields for source file and line number the setting came from.
*/
@@ -8171,7 +8303,7 @@ set_config_sourcefile(const char *name, char *sourcefile, int sourceline)
*/
elevel = IsUnderPostmaster ? DEBUG3 : LOG;
- record = find_option(name, true, false, elevel);
+ record = find_or_create_option(name, PGC_USERSET, false, elevel);
/* should not happen */
if (record == NULL)
return;
@@ -8223,7 +8355,7 @@ GetConfigOption(const char *name, bool missing_ok, bool restrict_privileged)
struct config_generic *record;
static char buffer[256];
- record = find_option(name, false, missing_ok, ERROR);
+ record = find_option(name, missing_ok, ERROR);
if (record == NULL)
return NULL;
if (restrict_privileged &&
@@ -8272,7 +8404,7 @@ GetConfigOptionResetString(const char *name)
struct config_generic *record;
static char buffer[256];
- record = find_option(name, false, false, ERROR);
+ record = find_option(name, false, ERROR);
Assert(record != NULL);
if ((record->flags & GUC_SUPERUSER_ONLY) &&
!has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_SETTINGS))
@@ -8317,7 +8449,7 @@ GetConfigOptionFlags(const char *name, bool missing_ok)
{
struct config_generic *record;
- record = find_option(name, false, missing_ok, ERROR);
+ record = find_option(name, missing_ok, ERROR);
if (record == NULL)
return 0;
return record->flags;
@@ -8351,7 +8483,7 @@ flatten_set_variable_args(const char *name, List *args)
* Get flags for the variable; if it's not known, use default flags.
* (Caller might throw error later, but not our business to do so here.)
*/
- record = find_option(name, false, true, WARNING);
+ record = find_option(name, true, WARNING);
if (record)
flags = record->flags;
else
@@ -8600,6 +8732,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
{
char *name;
char *value;
+ Oid parameterId = InvalidOid;
bool resetall = false;
ConfigVariable *head = NULL;
ConfigVariable *tail = NULL;
@@ -8607,16 +8740,31 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
char AutoConfFileName[MAXPGPATH];
char AutoConfTmpFileName[MAXPGPATH];
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to execute ALTER SYSTEM command")));
-
/*
* Extract statement arguments
*/
name = altersysstmt->setstmt->name;
+ /*
+ * Check permission to run ALTER SYSTEM on the target variable registered.
+ */
+ if (!superuser())
+ {
+ AclResult aclresult;
+
+ parameterId = get_parameter_oid(name, true);
+ if (!OidIsValid(parameterId))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+
+ aclresult = pg_parameter_acl_aclcheck(parameterId, GetUserId(),
+ ACL_ALTER_SYSTEM);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_PARAMETER, name);
+ }
+
switch (altersysstmt->setstmt->kind)
{
case VAR_SET_VALUE:
@@ -8646,7 +8794,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
{
struct config_generic *record;
- record = find_option(name, false, false, ERROR);
+ record = find_option(name, false, ERROR);
Assert(record != NULL);
/*
@@ -8750,16 +8898,17 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
}
/*
- * Invoke the post-alter hook for altering this GUC variable.
+ * Invoke the post-alter hook for setting this GUC variable. Guc variables
+ * do not always have corresponding entries in pg_parameter_acl, so we call
+ * the hook using the name rather than an Oid that might not be assigned.
*
* We do this here rather than at the end, because ALTER SYSTEM is not
* transactional. If the hook aborts our transaction, it will be cleaner
* to do so before we touch any files.
*/
- InvokeObjectPostAlterHookArgStr(InvalidOid, name,
- ACL_ALTER_SYSTEM,
- altersysstmt->setstmt->kind,
- false);
+ InvokeObjectPostAlterHookArgStr(ParameterAclRelationId, name,
+ ACL_ALTER_SYSTEM, altersysstmt->setstmt->kind,
+ false);
/*
* To ensure crash safety, first write the new file data to a temp file,
@@ -8821,6 +8970,9 @@ void
ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
{
GucAction action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET;
+ GucContext context;
+ AclResult aclresult;
+ Oid parameterId;
/*
* Workers synchronize these parameters at the start of the parallel
@@ -8831,6 +8983,24 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
errmsg("cannot set parameters during a parallel operation")));
+ /* Get the Oid of this parameter, or InvalidOid if none. */
+ parameterId = get_parameter_oid(stmt->name, true);
+
+ /*
+ * Superusers and users who have been granted SET privilege can set with
+ * PGC_SUSET context. All others have only PGC_USERSET.
+ */
+ context = PGC_USERSET;
+ if (superuser())
+ context = PGC_SUSET;
+ else if (OidIsValid(parameterId))
+ {
+ aclresult = pg_parameter_acl_aclcheck(parameterId, GetUserId(),
+ ACL_SET);
+ if (aclresult == ACLCHECK_OK)
+ context = PGC_SUSET;
+ }
+
switch (stmt->kind)
{
case VAR_SET_VALUE:
@@ -8839,7 +9009,7 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
WarnNoTransactionBlock(isTopLevel, "SET LOCAL");
(void) set_config_option(stmt->name,
ExtractSetVariableArgs(stmt),
- (superuser() ? PGC_SUSET : PGC_USERSET),
+ context,
PGC_S_SESSION,
action, true, 0, false);
break;
@@ -8924,7 +9094,7 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
(void) set_config_option(stmt->name,
NULL,
- (superuser() ? PGC_SUSET : PGC_USERSET),
+ context,
PGC_S_SESSION,
action, true, 0, false);
break;
@@ -8933,9 +9103,9 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
break;
}
- /* Invoke the post-alter hook for setting this GUC variable. */
- InvokeObjectPostAlterHookArgStr(InvalidOid, stmt->name,
- ACL_SET_VALUE, stmt->kind, false);
+ /* Invoke the post-alter hook for setting this GUC variable, by name. */
+ InvokeObjectPostAlterHookArgStr(ParameterAclRelationId, stmt->name,
+ ACL_SET, stmt->kind, false);
}
/*
@@ -9696,6 +9866,22 @@ get_explain_guc_options(int *num)
return result;
}
+/*
+ * Return GUC variable canonical name, or NULL if no variable by the given
+ * name or alias exists.
+ */
+const char *
+GetConfigOptionCanonicalName(const char *alias)
+{
+ struct config_generic *record;
+
+ record = find_option(alias, true, LOG);
+ if (record == NULL)
+ return NULL;
+
+ return record->name;
+}
+
/*
* Return GUC variable value by name; optionally return canonical form of
* name. If the GUC is unset, then throw an error unless missing_ok is true,
@@ -9706,7 +9892,7 @@ GetConfigOptionByName(const char *name, const char **varname, bool missing_ok)
{
struct config_generic *record;
- record = find_option(name, false, missing_ok, ERROR);
+ record = find_option(name, missing_ok, ERROR);
if (record == NULL)
{
if (varname)
@@ -9742,7 +9928,7 @@ pg_settings_get_flags(PG_FUNCTION_ARGS)
Datum flags[MAX_GUC_FLAGS];
ArrayType *a;
- record = find_option(varname, false, true, ERROR);
+ record = find_option(varname, true, ERROR);
/* return NULL if no such variable */
if (record == NULL)
@@ -10553,7 +10739,7 @@ read_nondefault_variables(void)
if ((varname = read_string_with_null(fp)) == NULL)
break;
- if ((record = find_option(varname, true, false, FATAL)) == NULL)
+ if ((record = find_or_create_option(varname, PGC_USERSET, false, FATAL)) == NULL)
elog(FATAL, "failed to locate variable \"%s\" in exec config params file", varname);
if ((varvalue = read_string_with_null(fp)) == NULL)
@@ -11243,7 +11429,7 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
(void) validate_option_array_item(name, value, false);
/* normalize name (converts obsolete GUC names to modern spellings) */
- record = find_option(name, false, true, WARNING);
+ record = find_option(name, true, WARNING);
if (record)
name = record->name;
@@ -11322,7 +11508,7 @@ GUCArrayDelete(ArrayType *array, const char *name)
(void) validate_option_array_item(name, NULL, false);
/* normalize name (converts obsolete GUC names to modern spellings) */
- record = find_option(name, false, true, WARNING);
+ record = find_option(name, true, WARNING);
if (record)
name = record->name;
@@ -11478,7 +11664,7 @@ validate_option_array_item(const char *name, const char *value,
* name is not known and can't be created as a placeholder. Throw error,
* unless skipIfNoPermissions is true, in which case return false.
*/
- gconf = find_option(name, true, skipIfNoPermissions, ERROR);
+ gconf = find_or_create_option(name, PGC_USERSET, skipIfNoPermissions, ERROR);
if (!gconf)
{
/* not known, failed to make a placeholder */
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index 6086d57cf3..3e68dfc78f 100644
--- a/src/bin/pg_dump/dumputils.c
+++ b/src/bin/pg_dump/dumputils.c
@@ -37,7 +37,7 @@ static void AddAcl(PQExpBuffer aclbuf, const char *keyword,
* nspname: the namespace the object is in (NULL if none); not pre-quoted
* type: the object type (as seen in GRANT command: must be one of
* TABLE, SEQUENCE, FUNCTION, PROCEDURE, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
- * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT)
+ * FOREIGN DATA WRAPPER, SERVER, PARAMETER or LARGE OBJECT)
* acls: the ACL string fetched from the database
* baseacls: the initial ACL string for this object
* owner: username of object owner (will be passed through fmtId); can be
@@ -501,6 +501,11 @@ do { \
CONVERT_PRIV('U', "USAGE");
else if (strcmp(type, "FOREIGN TABLE") == 0)
CONVERT_PRIV('r', "SELECT");
+ else if (strcmp(type, "PARAMETER") == 0)
+ {
+ CONVERT_PRIV('s', "SET");
+ CONVERT_PRIV('A', "ALTER SYSTEM");
+ }
else if (strcmp(type, "LARGE OBJECT") == 0)
{
CONVERT_PRIV('r', "SELECT");
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index d41a99d6ea..c5ab50ad28 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -3435,6 +3435,7 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te)
strcmp(type, "SCHEMA") == 0 ||
strcmp(type, "EVENT TRIGGER") == 0 ||
strcmp(type, "FOREIGN DATA WRAPPER") == 0 ||
+ strcmp(type, "PARAMETER") == 0 ||
strcmp(type, "SERVER") == 0 ||
strcmp(type, "PUBLICATION") == 0 ||
strcmp(type, "SUBSCRIPTION") == 0 ||
@@ -3618,6 +3619,7 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, bool isData)
strcmp(te->desc, "TEXT SEARCH DICTIONARY") == 0 ||
strcmp(te->desc, "TEXT SEARCH CONFIGURATION") == 0 ||
strcmp(te->desc, "FOREIGN DATA WRAPPER") == 0 ||
+ strcmp(te->desc, "PARAMETER") == 0 ||
strcmp(te->desc, "SERVER") == 0 ||
strcmp(te->desc, "STATISTICS") == 0 ||
strcmp(te->desc, "PUBLICATION") == 0 ||
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 535b160165..139c20c155 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -14288,6 +14288,9 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
case DEFACLOBJ_NAMESPACE:
type = "SCHEMAS";
break;
+ case DEFACLOBJ_PARAMETER:
+ type = "PARAMETERS";
+ break;
default:
/* shouldn't get here */
fatal("unrecognized object type in default privileges: %d",
@@ -14331,7 +14334,7 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
* or InvalidDumpId if there is no need for a second dependency.
* 'type' must be one of
* TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
- * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
+ * FOREIGN DATA WRAPPER, SERVER, PARAMETER or LARGE OBJECT.
* 'name' is the formatted name of the object. Must be quoted etc. already.
* 'subname' is the formatted name of the sub-object, if any. Must be quoted.
* (Currently we assume that subname is only provided for table columns.)
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 9c9f7c6d63..f8c5d2efa9 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -36,6 +36,7 @@ static void help(void);
static void dropRoles(PGconn *conn);
static void dumpRoles(PGconn *conn);
static void dumpRoleMembership(PGconn *conn);
+static void dumpRoleGUCPrivs(PGconn *conn);
static void dropTablespaces(PGconn *conn);
static void dumpTablespaces(PGconn *conn);
static void dropDBs(PGconn *conn);
@@ -585,6 +586,10 @@ main(int argc, char *argv[])
/* Dump role memberships */
dumpRoleMembership(conn);
+
+ /* Dump role guc privileges */
+ if (server_version >= 150000)
+ dumpRoleGUCPrivs(conn);
}
/* Dump tablespaces */
@@ -1024,6 +1029,62 @@ dropTablespaces(PGconn *conn)
fprintf(OPF, "\n\n");
}
+/*
+ * Dump role configuration parameter privileges. This code is used for 15.0
+ * and later servers.
+ *
+ * Note: we expect dumpRoles already created all the roles, but there are
+ * no per-role configuration parameter privileges yet.
+ */
+static void
+dumpRoleGUCPrivs(PGconn *conn)
+{
+ PGresult *res;
+ int i;
+
+ /*
+ * Get all parameters which have non-default acls defined.
+ */
+ res = executeQuery(conn, "SELECT parameter, "
+ "pg_catalog.pg_get_userbyid(10) AS setowner, "
+ "setacl, aclparameterdefault(parameter) AS acldefault "
+ "FROM pg_catalog.pg_parameter_acl "
+ "ORDER BY oid");
+
+ fprintf(OPF, "--\n-- Role privileges on configuration parameters\n--\n\n");
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ PQExpBuffer buf = createPQExpBuffer();
+ char *parameter = PQgetvalue(res, i, 0);
+ char *setowner = PQgetvalue(res, i, 1);
+ char *setacl = PQgetvalue(res, i, 2);
+ char *acldefault = PQgetvalue(res, i, 3);
+ char *fparameter;
+
+ /* needed for buildACLCommands() */
+ fparameter = pg_strdup(fmtId(parameter));
+
+ if (!buildACLCommands(fparameter, NULL, NULL, "PARAMETER",
+ setacl, acldefault,
+ setowner, "", server_version, buf))
+ {
+ pg_log_error("could not parse ACL list (%s) for tablespace \"%s\"",
+ setacl, parameter);
+ PQfinish(conn);
+ exit_nicely(1);
+ }
+
+ fprintf(OPF, "%s", buf->data);
+
+ free(fparameter);
+ destroyPQExpBuffer(buf);
+ }
+
+ PQclear(res);
+ fprintf(OPF, "\n\n");
+}
+
/*
* Dump tablespaces.
*/
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 3f9dfffd57..ae7b35d142 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3736,7 +3736,11 @@ psql_completion(const char *text, int start, int end)
* ALTER DEFAULT PRIVILEGES, so use TailMatches
*/
/* Complete GRANT/REVOKE with a list of roles and privileges */
- else if (TailMatches("GRANT|REVOKE"))
+ else if (TailMatches("REVOKE", "GRANT"))
+ COMPLETE_WITH("OPTION FOR");
+ else if (TailMatches("REVOKE", "GRANT", "OPTION"))
+ COMPLETE_WITH("FOR");
+ else if (TailMatches("REVOKE"))
{
/*
* With ALTER DEFAULT PRIVILEGES, restrict completion to grantable
@@ -3748,8 +3752,11 @@ psql_completion(const char *text, int start, int end)
"EXECUTE", "USAGE", "ALL");
else
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
+ "ALTER",
"SELECT",
+ "SET",
"INSERT",
+ "GRANT",
"UPDATE",
"DELETE",
"TRUNCATE",
@@ -3762,12 +3769,139 @@ psql_completion(const char *text, int start, int end)
"USAGE",
"ALL");
}
+ else if (TailMatches("GRANT") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR"))
+ {
+ /*
+ * With ALTER DEFAULT PRIVILEGES, restrict completion to grantable
+ * privileges (can't grant roles)
+ */
+ if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES"))
+ COMPLETE_WITH("SELECT", "INSERT", "UPDATE",
+ "DELETE", "TRUNCATE", "REFERENCES", "TRIGGER",
+ "EXECUTE", "USAGE", "ALL");
+ else
+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
+ "ALTER",
+ "SELECT",
+ "SET",
+ "INSERT",
+ "UPDATE",
+ "DELETE",
+ "TRUNCATE",
+ "REFERENCES",
+ "TRIGGER",
+ "CREATE",
+ "CONNECT",
+ "TEMPORARY",
+ "EXECUTE",
+ "USAGE",
+ "ALL");
+ }
+
+ else if (TailMatches("GRANT|REVOKE", "ALTER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER"))
+ COMPLETE_WITH("SYSTEM");
+
+ else if (TailMatches("GRANT|REVOKE", "SET") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM"))
+ COMPLETE_WITH(",",
+ "ON PARAMETER");
+
+ else if (TailMatches("GRANT|REVOKE", "SET,") ||
+ TailMatches("GRANT|REVOKE", "SET", ",") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET,") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", ","))
+ COMPLETE_WITH("ALTER SYSTEM ON PARAMETER");
+ else if (TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM,") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM", ",") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM,") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM", ","))
+ COMPLETE_WITH("SET ON PARAMETER");
+
+ else if (TailMatches("GRANT|REVOKE", "SET,", "ALTER") ||
+ TailMatches("GRANT|REVOKE", "SET", ",", "ALTER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET,", "ALTER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", ",", "ALTER"))
+ COMPLETE_WITH("SYSTEM ON PARAMETER");
+ else if (TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM,", "SET") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM", ",", "SET") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM,", "SET") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM", ",", "SET") ||
+ TailMatches("GRANT|REVOKE", "SET,", "ALTER", "SYSTEM") ||
+ TailMatches("GRANT|REVOKE", "SET", ",", "ALTER", "SYSTEM") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM,", "SET") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM", ",", "SET"))
+ COMPLETE_WITH("ON PARAMETER");
+
+ else if (TailMatches("GRANT|REVOKE", "SET", "ON") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM", "ON") ||
+ TailMatches("GRANT|REVOKE", "SET,", "ALTER", "SYSTEM", "ON") ||
+ TailMatches("GRANT|REVOKE", "SET", ",", "ALTER", "SYSTEM", "ON") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM,", "SET", "ON") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM", ",", "SET", "ON") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", "ON") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM", "ON") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET,", "ALTER", "SYSTEM", "ON") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", ",", "ALTER", "SYSTEM", "ON") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM,", "SET", "ON") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM", ",", "SET", "ON"))
+ COMPLETE_WITH("PARAMETER");
+
+ else if (TailMatches("GRANT|REVOKE", "SET", "ON", "PARAMETER") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM", "ON", "PARAMETER") ||
+ TailMatches("GRANT|REVOKE", "SET,", "ALTER", "SYSTEM", "ON", "PARAMETER") ||
+ TailMatches("GRANT|REVOKE", "SET", ",", "ALTER", "SYSTEM", "ON", "PARAMETER") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM,", "SET", "ON", "PARAMETER") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM", ",", "SET", "ON", "PARAMETER") ||
+ TailMatches("GRANT|REVOKE", "ALL", "ON", "PARAMETER") ||
+ TailMatches("GRANT|REVOKE", "ALL", "PRIVILEGES", "ON", "PARAMETER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", "ON", "PARAMETER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM", "ON", "PARAMETER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET,", "ALTER", "SYSTEM", "ON", "PARAMETER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", ",", "ALTER", "SYSTEM", "ON", "PARAMETER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM,", "SET", "ON", "PARAMETER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM", ",", "SET", "ON", "PARAMETER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALL", "ON", "PARAMETER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALL", "PRIVILEGES", "ON", "PARAMETER"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_set_vars);
+
+ else if (TailMatches("GRANT", "SET", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("GRANT", "ALTER", "SYSTEM", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("GRANT", "SET,", "ALTER", "SYSTEM", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("GRANT", "SET", ",", "ALTER", "SYSTEM", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("GRANT", "ALTER", "SYSTEM,", "SET", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("GRANT", "ALTER", "SYSTEM", ",", "SET", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("GRANT", "ALL", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("GRANT", "ALL", "PRIVILEGES", "ON", "PARAMETER", MatchAny))
+ COMPLETE_WITH("TO");
+
+ else if (TailMatches("REVOKE", "SET", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "ALTER", "SYSTEM", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "SET,", "ALTER", "SYSTEM", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "SET", ",", "ALTER", "SYSTEM", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "ALL", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "ALL", "PRIVILEGES", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "ALTER", "SYSTEM,", "SET", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "ALTER", "SYSTEM", ",", "SET", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET,", "ALTER", "SYSTEM", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", ",", "ALTER", "SYSTEM", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM,", "SET", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM", ",", "SET", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALL", "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALL", "PRIVILEGES", "ON", "PARAMETER", MatchAny))
+ COMPLETE_WITH("FROM");
/*
* Complete GRANT/REVOKE <privilege> with "ON", GRANT/REVOKE <role> with
* TO/FROM
*/
- else if (TailMatches("GRANT|REVOKE", MatchAny))
+ else if (TailMatches("GRANT|REVOKE", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny))
{
if (TailMatches("SELECT|INSERT|UPDATE|DELETE|TRUNCATE|REFERENCES|TRIGGER|CREATE|CONNECT|TEMPORARY|TEMP|EXECUTE|USAGE|ALL"))
COMPLETE_WITH("ON");
@@ -3784,7 +3918,8 @@ psql_completion(const char *text, int start, int end)
* here will only work if the privilege list contains exactly one
* privilege.
*/
- else if (TailMatches("GRANT|REVOKE", MatchAny, "ON"))
+ else if (TailMatches("GRANT|REVOKE", MatchAny, "ON") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON"))
{
/*
* With ALTER DEFAULT PRIVILEGES, restrict completion to the kinds of
@@ -3814,13 +3949,15 @@ psql_completion(const char *text, int start, int end)
"TABLESPACE",
"TYPE");
}
- else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "ALL"))
+ else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "ALL") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "ALL"))
COMPLETE_WITH("FUNCTIONS IN SCHEMA",
"PROCEDURES IN SCHEMA",
"ROUTINES IN SCHEMA",
"SEQUENCES IN SCHEMA",
"TABLES IN SCHEMA");
- else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN"))
+ else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "FOREIGN"))
COMPLETE_WITH("DATA WRAPPER", "SERVER");
/*
@@ -3829,7 +3966,8 @@ psql_completion(const char *text, int start, int end)
*
* Complete "GRANT/REVOKE * ON *" with "TO/FROM".
*/
- else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", MatchAny))
+ else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", MatchAny))
{
if (TailMatches("DATABASE"))
COMPLETE_WITH_QUERY(Query_for_list_of_databases);
@@ -3867,6 +4005,25 @@ psql_completion(const char *text, int start, int end)
(HeadMatches("REVOKE") && TailMatches("FROM")))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
Keywords_for_list_of_grant_roles);
+ else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny))
+ COMPLETE_WITH("WITH ADMIN OPTION",
+ "WITH GRANT OPTION",
+ "GRANTED BY");
+ else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH"))
+ COMPLETE_WITH("ADMIN OPTION",
+ "GRANT OPTION");
+ else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH", "ADMIN"))
+ COMPLETE_WITH("OPTION");
+ else if ((HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH", "ADMIN", "OPTION")) ||
+ (HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH", "GRANT", "OPTION")))
+ COMPLETE_WITH("GRANTED BY");
+ else if ((HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH", "ADMIN", "OPTION", "GRANTED")) ||
+ (HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH", "GRANT", "OPTION", "GRANTED")))
+ COMPLETE_WITH("BY");
+ else if ((HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH", "ADMIN", "OPTION", "GRANTED", "BY")) ||
+ (HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH", "GRANT", "OPTION", "GRANTED", "BY")))
+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
+ Keywords_for_list_of_grant_roles);
/* Complete "ALTER DEFAULT PRIVILEGES ... GRANT/REVOKE ... TO/FROM */
else if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES") && TailMatches("TO|FROM"))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
@@ -3878,7 +4035,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("FROM");
/* Complete "GRANT/REVOKE * ON ALL * IN SCHEMA *" with TO/FROM */
- else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA", MatchAny))
+ else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA", MatchAny))
{
if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
COMPLETE_WITH("TO");
@@ -3887,7 +4045,8 @@ psql_completion(const char *text, int start, int end)
}
/* Complete "GRANT/REVOKE * ON FOREIGN DATA WRAPPER *" with TO/FROM */
- else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny))
+ else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny))
{
if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
COMPLETE_WITH("TO");
@@ -3896,7 +4055,8 @@ psql_completion(const char *text, int start, int end)
}
/* Complete "GRANT/REVOKE * ON FOREIGN SERVER *" with TO/FROM */
- else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny))
+ else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny))
{
if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
COMPLETE_WITH("TO");
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 344482ec87..15185b3738 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -120,6 +120,7 @@ typedef enum ObjectClass
OCLASS_DEFACL, /* pg_default_acl */
OCLASS_EXTENSION, /* pg_extension */
OCLASS_EVENT_TRIGGER, /* pg_event_trigger */
+ OCLASS_PARAMETER, /* pg_parameter_acl */
OCLASS_POLICY, /* pg_policy */
OCLASS_PUBLICATION, /* pg_publication */
OCLASS_PUBLICATION_NAMESPACE, /* pg_publication_namespace */
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index 4d54ae2a7d..ac6adcb730 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -239,7 +239,7 @@ extern void RunFunctionExecuteHookStr(const char *objectStr);
RunObjectTruncateHookStr(objectName); \
} while(0)
-#define InvokeObjectPostAlterHookStr(className,objectName,subId) \
+#define InvokeObjectPostAlterHookStr(classId,objectName,subId) \
InvokeObjectPostAlterHookArgStr((classId),(objectName),(subId), \
InvalidOid,false)
#define InvokeObjectPostAlterHookArgStr(classId,objectName,subId, \
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index 2a79155636..757fc4963b 100644
--- a/src/include/catalog/pg_default_acl.h
+++ b/src/include/catalog/pg_default_acl.h
@@ -66,6 +66,7 @@ DECLARE_UNIQUE_INDEX_PKEY(pg_default_acl_oid_index, 828, DefaultAclOidIndexId, o
#define DEFACLOBJ_FUNCTION 'f' /* function */
#define DEFACLOBJ_TYPE 'T' /* type */
#define DEFACLOBJ_NAMESPACE 'n' /* namespace */
+#define DEFACLOBJ_PARAMETER 'p' /* configuration parameter */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_parameter_acl.h b/src/include/catalog/pg_parameter_acl.h
new file mode 100644
index 0000000000..20f15063ca
--- /dev/null
+++ b/src/include/catalog/pg_parameter_acl.h
@@ -0,0 +1,63 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_parameter_acl.h
+ * definition of the "configuration parameter" system catalog
+ * (pg_parameter_acl).
+ *
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_parameter_acl.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PARAMETER_ACL_H
+#define PG_PARAMETER_ACL_H
+
+#include "catalog/genbki.h"
+#include "catalog/pg_parameter_acl_d.h"
+
+/* ----------------
+ * pg_parameter_acl definition. cpp turns this into
+ * typedef struct FormData_pg_parameter_acl
+ * ----------------
+ */
+CATALOG(pg_parameter_acl,8924,ParameterAclRelationId) BKI_SHARED_RELATION
+{
+ Oid oid; /* oid */
+
+ /*
+ * Variable-length fields start here, but we allow direct access to
+ * parameter.
+ */
+ text parameter BKI_FORCE_NOT_NULL;
+
+#ifdef CATALOG_VARLEN
+ /* Access privileges */
+ aclitem setacl[1] BKI_DEFAULT(_null_);
+#endif
+} FormData_pg_parameter_acl;
+
+
+/* ----------------
+ * Form_pg_parameter_acl corresponds to a pointer to a tuple with
+ * the format of pg_parameter_acl relation.
+ * ----------------
+ */
+typedef FormData_pg_parameter_acl *Form_pg_parameter_acl;
+
+DECLARE_TOAST(pg_parameter_acl, 8925, 8926);
+#define PgParameterAclToastTable 8925
+#define PgParameterAclToastIndex 8926
+
+DECLARE_UNIQUE_INDEX(pg_parameter_acl_parameter_index, 8927, ParameterAclParameterIndexId, on pg_parameter_acl using btree(parameter text_ops));
+DECLARE_UNIQUE_INDEX_PKEY(pg_parameter_acl_oid_index, 8928, ParameterAclOidIndexId, on pg_parameter_acl using btree(oid oid_ops));
+
+extern Oid ParameterAclCreate(const char *parameter, bool if_not_exists);
+
+#endif /* PG_PARAMETER_ACL_H */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 01e1dd4d6d..f106ff4bae 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -2085,6 +2085,10 @@
descr => 'show hardwired default privileges, primarily for use by the information schema',
proname => 'acldefault', prorettype => '_aclitem', proargtypes => 'char oid',
prosrc => 'acldefault_sql' },
+{ oid => '9806',
+ descr => 'show hardwired default privileges on parameters',
+ proname => 'aclparameterdefault', prorettype => '_aclitem', proargtypes => 'text',
+ prosrc => 'aclparameterdefault_sql' },
{ oid => '1689',
descr => 'convert ACL item array to table, primarily for use by information schema',
proname => 'aclexplode', prorows => '10', proretset => 't',
@@ -7213,6 +7217,25 @@
proname => 'has_type_privilege', provolatile => 's', prorettype => 'bool',
proargtypes => 'oid text', prosrc => 'has_type_privilege_id' },
+{ oid => '8050', descr => 'user privilege on parameter by username, parameter name',
+ proname => 'has_parameter_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'name text text', prosrc => 'has_parameter_privilege_name_name' },
+{ oid => '8051', descr => 'user privilege on parameter by username, parameter oid',
+ proname => 'has_parameter_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'name oid text', prosrc => 'has_parameter_privilege_name_id' },
+{ oid => '8052', descr => 'user privilege on parameter by user oid, parameter name',
+ proname => 'has_parameter_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid text text', prosrc => 'has_parameter_privilege_id_name' },
+{ oid => '8053', descr => 'user privilege on parameter by user oid, parameter oid',
+ proname => 'has_parameter_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid oid text', prosrc => 'has_parameter_privilege_id_id' },
+{ oid => '8054', descr => 'current user privilege on parameter by parameter name',
+ proname => 'has_parameter_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'text text', prosrc => 'has_parameter_privilege_name' },
+{ oid => '8055', descr => 'current user privilege on parameter by parameter oid',
+ proname => 'has_parameter_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid text', prosrc => 'has_parameter_privilege_id' },
+
{ oid => '2705', descr => 'user privilege on role by username, role name',
proname => 'pg_has_role', provolatile => 's', prorettype => 'bool',
proargtypes => 'name name text', prosrc => 'pg_has_role_name_name' },
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 0ff4ba0884..bb7874da3a 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -92,7 +92,7 @@ typedef uint32 AclMode; /* a bitmask of privilege bits */
#define ACL_CREATE (1<<9) /* for namespaces and databases */
#define ACL_CREATE_TEMP (1<<10) /* for databases */
#define ACL_CONNECT (1<<11) /* for databases */
-#define ACL_SET_VALUE (1<<12) /* for configuration parameters */
+#define ACL_SET (1<<12) /* for configuration parameters */
#define ACL_ALTER_SYSTEM (1<<13) /* for configuration parameters */
#define N_ACL_RIGHTS 14 /* 1 plus the last 1<<x */
#define ACL_NO_RIGHTS 0
@@ -2035,6 +2035,7 @@ typedef enum ObjectType
OBJECT_OPCLASS,
OBJECT_OPERATOR,
OBJECT_OPFAMILY,
+ OBJECT_PARAMETER,
OBJECT_POLICY,
OBJECT_PROCEDURE,
OBJECT_PUBLICATION,
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 5f3834ddf3..b15b489477 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -325,6 +325,7 @@ PG_KEYWORD("overriding", OVERRIDING, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("owned", OWNED, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("owner", OWNER, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("parallel", PARALLEL, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("parameter", PARAMETER, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("parser", PARSER, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("partial", PARTIAL, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("partition", PARTITION, UNRESERVED_KEYWORD, BARE_LABEL)
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 91ce3d8e9c..cf552d2580 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -146,7 +146,7 @@ typedef struct ArrayType Acl;
#define ACL_CREATE_CHR 'C'
#define ACL_CREATE_TEMP_CHR 'T'
#define ACL_CONNECT_CHR 'c'
-#define ACL_SET_VALUE_CHR 's'
+#define ACL_SET_CHR 's'
#define ACL_ALTER_SYSTEM_CHR 'A'
/* string holding all privilege code chars, in order by bitmask position */
@@ -167,6 +167,7 @@ typedef struct ArrayType Acl;
#define ACL_ALL_RIGHTS_SCHEMA (ACL_USAGE|ACL_CREATE)
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
#define ACL_ALL_RIGHTS_TYPE (ACL_USAGE)
+#define ACL_ALL_RIGHTS_PARAMETER (ACL_SET|ACL_ALTER_SYSTEM)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -188,6 +189,7 @@ typedef enum
* routines used internally
*/
extern Acl *acldefault(ObjectType objtype, Oid ownerId);
+extern Acl *aclparameterdefault(bool is_userset);
extern Acl *get_user_default_acl(ObjectType objtype, Oid ownerId,
Oid nsp_oid);
extern void recordDependencyOnNewAcl(Oid classId, Oid objectId, int32 objsubId,
@@ -225,6 +227,8 @@ extern void select_best_grantor(Oid roleId, AclMode privileges,
extern void initialize_acl(void);
+extern Oid get_parameter_oid(const char *parameter, bool missing_ok);
+
/*
* prototypes for functions in aclchk.c
*/
@@ -245,6 +249,8 @@ extern AclMode pg_class_aclmask_ext(Oid table_oid, Oid roleid,
bool *is_missing);
extern AclMode pg_database_aclmask(Oid db_oid, Oid roleid,
AclMode mask, AclMaskHow how);
+extern AclMode pg_parameter_acl_aclmask(Oid config_oid, Oid roleid,
+ AclMode mask, AclMaskHow how);
extern AclMode pg_proc_aclmask(Oid proc_oid, Oid roleid,
AclMode mask, AclMaskHow how);
extern AclMode pg_language_aclmask(Oid lang_oid, Oid roleid,
@@ -273,6 +279,7 @@ extern AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode);
extern AclResult pg_class_aclcheck_ext(Oid table_oid, Oid roleid,
AclMode mode, bool *is_missing);
extern AclResult pg_database_aclcheck(Oid db_oid, Oid roleid, AclMode mode);
+extern AclResult pg_parameter_acl_aclcheck(Oid config_oid, Oid roleid, AclMode mode);
extern AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode);
extern AclResult pg_language_aclcheck(Oid lang_oid, Oid roleid, AclMode mode);
extern AclResult pg_largeobject_aclcheck_snapshot(Oid lang_oid, Oid roleid,
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index ea774968f0..139e21176b 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -381,7 +381,11 @@ extern int set_config_option(const char *name, const char *value,
GucContext context, GucSource source,
GucAction action, bool changeVal, int elevel,
bool is_reload);
+extern GucContext find_option_context(const char *name,
+ GucContext default_context);
extern void AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt);
+extern bool valid_variable_name(const char *name, int *partcnt);
+extern const char *GetConfigOptionCanonicalName(const char *alias);
extern char *GetConfigOptionByName(const char *name, const char **varname,
bool missing_ok);
extern void GetConfigOptionByNum(int varnum, const char **values, bool *noshow);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index b8dd27d4a9..2191d5b6c9 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -188,6 +188,7 @@ extern int32 get_attavgwidth(Oid relid, AttrNumber attnum);
extern bool get_attstatsslot(AttStatsSlot *sslot, HeapTuple statstuple,
int reqkind, Oid reqop, int flags);
extern void free_attstatsslot(AttStatsSlot *sslot);
+extern char *get_parameter_name(Oid configid);
extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 9c1a76e8bb..267db9a914 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -72,6 +72,8 @@ enum SysCacheIdentifier
OPEROID,
OPFAMILYAMNAMENSP,
OPFAMILYOID,
+ PARAMETERNAME,
+ PARAMETEROID,
PARTRELID,
PROCNAMEARGSNSP,
PROCOID,
diff --git a/src/pl/plperl/plperl--1.0.sql b/src/pl/plperl/plperl--1.0.sql
index 5ff31e74e5..b7a432db90 100644
--- a/src/pl/plperl/plperl--1.0.sql
+++ b/src/pl/plperl/plperl--1.0.sql
@@ -18,3 +18,5 @@ CREATE TRUSTED LANGUAGE plperl
ALTER LANGUAGE plperl OWNER TO @extowner@;
COMMENT ON LANGUAGE plperl IS 'PL/Perl procedural language';
+
+GRANT SET ON PARAMETER plperl.use_strict TO public;
diff --git a/src/pl/plperl/plperlu--1.0.sql b/src/pl/plperl/plperlu--1.0.sql
index 10d75945a1..6ecdf4206d 100644
--- a/src/pl/plperl/plperlu--1.0.sql
+++ b/src/pl/plperl/plperlu--1.0.sql
@@ -15,3 +15,5 @@ CREATE LANGUAGE plperlu
VALIDATOR plperlu_validator;
COMMENT ON LANGUAGE plperlu IS 'PL/PerlU untrusted procedural language';
+
+GRANT SET ON PARAMETER plperl.use_strict TO public;
diff --git a/src/pl/plpgsql/src/plpgsql--1.0.sql b/src/pl/plpgsql/src/plpgsql--1.0.sql
index 6e5b990fcc..b500d43b88 100644
--- a/src/pl/plpgsql/src/plpgsql--1.0.sql
+++ b/src/pl/plpgsql/src/plpgsql--1.0.sql
@@ -18,3 +18,8 @@ CREATE TRUSTED LANGUAGE plpgsql
ALTER LANGUAGE plpgsql OWNER TO @extowner@;
COMMENT ON LANGUAGE plpgsql IS 'PL/pgSQL procedural language';
+
+GRANT SET ON PARAMETER plpgsql.print_strict_params TO public;
+GRANT SET ON PARAMETER plpgsql.check_asserts TO public;
+GRANT SET ON PARAMETER plpgsql.extra_warnings TO public;
+GRANT SET ON PARAMETER plpgsql.extra_errors TO public;
diff --git a/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out b/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out
index 45ff276f7e..1fcc0d7022 100644
--- a/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out
+++ b/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out
@@ -1,36 +1,77 @@
+-- Creating privileges on a placeholder GUC should create entries in the
+-- pg_parameter_acl catalog which conservatively grant no privileges to public.
+CREATE ROLE regress_role_joe;
+GRANT SET ON PARAMETER test_oat_hooks.user_var1 TO regress_role_joe;
+GRANT SET ON PARAMETER test_oat_hooks.super_var1 TO regress_role_joe;
-- SET commands fire both the ProcessUtility_hook and the
-- object_access_hook_str. Since the auditing GUC starts out false, we miss the
-- initial "attempting" audit message from the ProcessUtility_hook, but we
--- should thereafter see the audit messages
+-- should thereafter see the audit messages.
+--
+-- Module installation sql scripts *should* be written to restore SET privileges
+-- on userset variables to PUBLIC. This module does not, so the
+-- test_oat_hooks.user_var1 will remain unsettable by PUBLIC after this load.
LOAD 'test_oat_hooks';
SET test_oat_hooks.audit = true;
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [test_oat_hooks.audit]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [test_oat_hooks.audit]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [test_oat_hooks.audit]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [test_oat_hooks.audit]
NOTICE: in process utility: superuser finished set
+-- Creating privileges on an existent custom GUC should create precisely the
+-- right privileges, not overly conservative ones.
+GRANT SET ON PARAMETER test_oat_hooks.user_var2 TO regress_role_joe;
+NOTICE: in process utility: superuser attempting GrantStmt
+NOTICE: in object_access_hook_str: superuser attempting create (subId=0x0) [test_oat_hooks.user_var2]
+NOTICE: in object_access_hook_str: superuser finished create (subId=0x0) [test_oat_hooks.user_var2]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [test_oat_hooks.user_var2]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [test_oat_hooks.user_var2]
+NOTICE: in process utility: superuser finished GrantStmt
+GRANT SET ON PARAMETER test_oat_hooks.super_var2 TO regress_role_joe;
+NOTICE: in process utility: superuser attempting GrantStmt
+NOTICE: in object_access_hook_str: superuser attempting create (subId=0x0) [test_oat_hooks.super_var2]
+NOTICE: in object_access_hook_str: superuser finished create (subId=0x0) [test_oat_hooks.super_var2]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [test_oat_hooks.super_var2]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [test_oat_hooks.super_var2]
+NOTICE: in process utility: superuser finished GrantStmt
+-- Granting multiple privileges on a parameter should be reported correctly to
+-- the OAT hook, but beware that WITH GRANT OPTION is not represented.
+GRANT SET, ALTER SYSTEM ON PARAMETER none.such TO regress_role_joe;
+NOTICE: in process utility: superuser attempting GrantStmt
+NOTICE: in object_access_hook_str: superuser attempting create (subId=0x0) [none.such]
+NOTICE: in object_access_hook_str: superuser finished create (subId=0x0) [none.such]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x3000, all privileges) [none.such]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x3000, all privileges) [none.such]
+NOTICE: in process utility: superuser finished GrantStmt
+GRANT SET, ALTER SYSTEM ON PARAMETER another.bogus TO regress_role_joe WITH GRANT OPTION;
+NOTICE: in process utility: superuser attempting GrantStmt
+NOTICE: in object_access_hook_str: superuser attempting create (subId=0x0) [another.bogus]
+NOTICE: in object_access_hook_str: superuser finished create (subId=0x0) [another.bogus]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x3000, all privileges) [another.bogus]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x3000, all privileges) [another.bogus]
+NOTICE: in process utility: superuser finished GrantStmt
-- Create objects for use in the test
CREATE USER regress_test_user;
NOTICE: in process utility: superuser attempting CreateRoleStmt
-NOTICE: in object access: superuser attempting create (subId=0) [explicit]
-NOTICE: in object access: superuser finished create (subId=0) [explicit]
+NOTICE: in object access: superuser attempting create (subId=0x0) [explicit]
+NOTICE: in object access: superuser finished create (subId=0x0) [explicit]
NOTICE: in process utility: superuser finished CreateRoleStmt
CREATE TABLE regress_test_table (t text);
NOTICE: in process utility: superuser attempting CreateStmt
-NOTICE: in object access: superuser attempting namespace search (subId=0) [no report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [no report on violation, allowed]
LINE 1: CREATE TABLE regress_test_table (t text);
^
-NOTICE: in object access: superuser finished namespace search (subId=0) [no report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [no report on violation, allowed]
LINE 1: CREATE TABLE regress_test_table (t text);
^
-NOTICE: in object access: superuser attempting create (subId=0) [explicit]
-NOTICE: in object access: superuser finished create (subId=0) [explicit]
-NOTICE: in object access: superuser attempting create (subId=0) [explicit]
-NOTICE: in object access: superuser finished create (subId=0) [explicit]
-NOTICE: in object access: superuser attempting create (subId=0) [explicit]
-NOTICE: in object access: superuser finished create (subId=0) [explicit]
-NOTICE: in object access: superuser attempting create (subId=0) [internal]
-NOTICE: in object access: superuser finished create (subId=0) [internal]
-NOTICE: in object access: superuser attempting create (subId=0) [internal]
-NOTICE: in object access: superuser finished create (subId=0) [internal]
+NOTICE: in object access: superuser attempting create (subId=0x0) [explicit]
+NOTICE: in object access: superuser finished create (subId=0x0) [explicit]
+NOTICE: in object access: superuser attempting create (subId=0x0) [explicit]
+NOTICE: in object access: superuser finished create (subId=0x0) [explicit]
+NOTICE: in object access: superuser attempting create (subId=0x0) [explicit]
+NOTICE: in object access: superuser finished create (subId=0x0) [explicit]
+NOTICE: in object access: superuser attempting create (subId=0x0) [internal]
+NOTICE: in object access: superuser finished create (subId=0x0) [internal]
+NOTICE: in object access: superuser attempting create (subId=0x0) [internal]
+NOTICE: in object access: superuser finished create (subId=0x0) [internal]
NOTICE: in process utility: superuser finished CreateStmt
GRANT SELECT ON Table regress_test_table TO public;
NOTICE: in process utility: superuser attempting GrantStmt
@@ -39,8 +80,8 @@ CREATE FUNCTION regress_test_func (t text) RETURNS text AS $$
SELECT $1;
$$ LANGUAGE sql;
NOTICE: in process utility: superuser attempting CreateFunctionStmt
-NOTICE: in object access: superuser attempting create (subId=0) [explicit]
-NOTICE: in object access: superuser finished create (subId=0) [explicit]
+NOTICE: in object access: superuser attempting create (subId=0x0) [explicit]
+NOTICE: in object access: superuser finished create (subId=0x0) [explicit]
NOTICE: in process utility: superuser finished CreateFunctionStmt
GRANT EXECUTE ON FUNCTION regress_test_func (text) TO public;
NOTICE: in process utility: superuser attempting GrantStmt
@@ -63,35 +104,35 @@ NOTICE: in executor check perms: superuser finished execute
SET work_mem = 8192;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [work_mem]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [work_mem]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [work_mem]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [work_mem]
NOTICE: in process utility: superuser finished set
RESET work_mem;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [work_mem]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [work_mem]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [work_mem]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [work_mem]
NOTICE: in process utility: superuser finished set
ALTER SYSTEM SET work_mem = 8192;
NOTICE: in process utility: superuser attempting alter system
-NOTICE: in object_access_hook_str: superuser attempting alter (alter system set) [work_mem]
-NOTICE: in object_access_hook_str: superuser finished alter (alter system set) [work_mem]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x2000, alter system) [work_mem]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x2000, alter system) [work_mem]
NOTICE: in process utility: superuser finished alter system
ALTER SYSTEM RESET work_mem;
NOTICE: in process utility: superuser attempting alter system
-NOTICE: in object_access_hook_str: superuser attempting alter (alter system set) [work_mem]
-NOTICE: in object_access_hook_str: superuser finished alter (alter system set) [work_mem]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x2000, alter system) [work_mem]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x2000, alter system) [work_mem]
NOTICE: in process utility: superuser finished alter system
-- Do those same things as non-superuser
SET SESSION AUTHORIZATION regress_test_user;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: non-superuser attempting alter (set) [session_authorization]
-NOTICE: in object_access_hook_str: non-superuser finished alter (set) [session_authorization]
+NOTICE: in object_access_hook_str: non-superuser attempting alter (subId=0x1000, set) [session_authorization]
+NOTICE: in object_access_hook_str: non-superuser finished alter (subId=0x1000, set) [session_authorization]
NOTICE: in process utility: non-superuser finished set
SELECT * FROM regress_test_table;
-NOTICE: in object access: non-superuser attempting namespace search (subId=0) [no report on violation, allowed]
+NOTICE: in object access: non-superuser attempting namespace search (subId=0x0) [no report on violation, allowed]
LINE 1: SELECT * FROM regress_test_table;
^
-NOTICE: in object access: non-superuser finished namespace search (subId=0) [no report on violation, allowed]
+NOTICE: in object access: non-superuser finished namespace search (subId=0x0) [no report on violation, allowed]
LINE 1: SELECT * FROM regress_test_table;
^
NOTICE: in executor check perms: non-superuser attempting execute
@@ -110,61 +151,92 @@ NOTICE: in executor check perms: non-superuser finished execute
SET work_mem = 8192;
NOTICE: in process utility: non-superuser attempting set
-NOTICE: in object_access_hook_str: non-superuser attempting alter (set) [work_mem]
-NOTICE: in object_access_hook_str: non-superuser finished alter (set) [work_mem]
+NOTICE: in object_access_hook_str: non-superuser attempting alter (subId=0x1000, set) [work_mem]
+NOTICE: in object_access_hook_str: non-superuser finished alter (subId=0x1000, set) [work_mem]
NOTICE: in process utility: non-superuser finished set
RESET work_mem;
NOTICE: in process utility: non-superuser attempting set
-NOTICE: in object_access_hook_str: non-superuser attempting alter (set) [work_mem]
-NOTICE: in object_access_hook_str: non-superuser finished alter (set) [work_mem]
+NOTICE: in object_access_hook_str: non-superuser attempting alter (subId=0x1000, set) [work_mem]
+NOTICE: in object_access_hook_str: non-superuser finished alter (subId=0x1000, set) [work_mem]
NOTICE: in process utility: non-superuser finished set
ALTER SYSTEM SET work_mem = 8192;
NOTICE: in process utility: non-superuser attempting alter system
-ERROR: must be superuser to execute ALTER SYSTEM command
+ERROR: permission denied to set parameter "work_mem"
ALTER SYSTEM RESET work_mem;
NOTICE: in process utility: non-superuser attempting alter system
-ERROR: must be superuser to execute ALTER SYSTEM command
+ERROR: permission denied to set parameter "work_mem"
+-- Despite being a PGC_USERSET variable, user_var1 will have less permissive
+-- privileges as a consequence of having been granted to regress_role_joe prior
+-- to module load
+SET test_oat_hooks.user_var1 = true;
+NOTICE: in process utility: non-superuser attempting set
+ERROR: permission denied to set parameter "test_oat_hooks.user_var1"
+SET test_oat_hooks.super_var1 = true;
+NOTICE: in process utility: non-superuser attempting set
+ERROR: permission denied to set parameter "test_oat_hooks.super_var1"
+ALTER SYSTEM SET test_oat_hooks.user_var1 = true;
+NOTICE: in process utility: non-superuser attempting alter system
+ERROR: permission denied to set parameter "test_oat_hooks.user_var1"
+ALTER SYSTEM SET test_oat_hooks.super_var1 = true;
+NOTICE: in process utility: non-superuser attempting alter system
+ERROR: permission denied to set parameter "test_oat_hooks.super_var1"
+-- Unlike the parameters, above, these parameters were not granted to
+-- regress_role_joe until after module load
+SET test_oat_hooks.user_var2 = true;
+NOTICE: in process utility: non-superuser attempting set
+NOTICE: in object_access_hook_str: non-superuser attempting alter (subId=0x1000, set) [test_oat_hooks.user_var2]
+NOTICE: in object_access_hook_str: non-superuser finished alter (subId=0x1000, set) [test_oat_hooks.user_var2]
+NOTICE: in process utility: non-superuser finished set
+SET test_oat_hooks.super_var2 = true;
+NOTICE: in process utility: non-superuser attempting set
+ERROR: permission denied to set parameter "test_oat_hooks.super_var2"
+ALTER SYSTEM SET test_oat_hooks.user_var2 = true;
+NOTICE: in process utility: non-superuser attempting alter system
+ERROR: permission denied to set parameter "test_oat_hooks.user_var2"
+ALTER SYSTEM SET test_oat_hooks.super_var2 = true;
+NOTICE: in process utility: non-superuser attempting alter system
+ERROR: permission denied to set parameter "test_oat_hooks.super_var2"
RESET SESSION AUTHORIZATION;
NOTICE: in process utility: non-superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [session_authorization]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [session_authorization]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [session_authorization]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [session_authorization]
NOTICE: in process utility: superuser finished set
-- Turn off non-superuser permissions
SET test_oat_hooks.deny_set_variable = true;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [test_oat_hooks.deny_set_variable]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [test_oat_hooks.deny_set_variable]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [test_oat_hooks.deny_set_variable]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [test_oat_hooks.deny_set_variable]
NOTICE: in process utility: superuser finished set
SET test_oat_hooks.deny_alter_system = true;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [test_oat_hooks.deny_alter_system]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [test_oat_hooks.deny_alter_system]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [test_oat_hooks.deny_alter_system]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [test_oat_hooks.deny_alter_system]
NOTICE: in process utility: superuser finished set
SET test_oat_hooks.deny_object_access = true;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [test_oat_hooks.deny_object_access]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [test_oat_hooks.deny_object_access]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [test_oat_hooks.deny_object_access]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [test_oat_hooks.deny_object_access]
NOTICE: in process utility: superuser finished set
SET test_oat_hooks.deny_exec_perms = true;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [test_oat_hooks.deny_exec_perms]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [test_oat_hooks.deny_exec_perms]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [test_oat_hooks.deny_exec_perms]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [test_oat_hooks.deny_exec_perms]
NOTICE: in process utility: superuser finished set
SET test_oat_hooks.deny_utility_commands = true;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [test_oat_hooks.deny_utility_commands]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [test_oat_hooks.deny_utility_commands]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [test_oat_hooks.deny_utility_commands]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [test_oat_hooks.deny_utility_commands]
NOTICE: in process utility: superuser finished set
-- Try again as non-superuser with permissions denied
SET SESSION AUTHORIZATION regress_test_user;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: non-superuser attempting alter (set) [session_authorization]
+NOTICE: in object_access_hook_str: non-superuser attempting alter (subId=0x1000, set) [session_authorization]
ERROR: permission denied: set session_authorization
SELECT * FROM regress_test_table;
-NOTICE: in object access: superuser attempting namespace search (subId=0) [no report on violation, allowed]
+NOTICE: in object access: superuser attempting namespace search (subId=0x0) [no report on violation, allowed]
LINE 1: SELECT * FROM regress_test_table;
^
-NOTICE: in object access: superuser finished namespace search (subId=0) [no report on violation, allowed]
+NOTICE: in object access: superuser finished namespace search (subId=0x0) [no report on violation, allowed]
LINE 1: SELECT * FROM regress_test_table;
^
NOTICE: in executor check perms: superuser attempting execute
@@ -183,28 +255,42 @@ NOTICE: in executor check perms: superuser finished execute
SET work_mem = 8192;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [work_mem]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [work_mem]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [work_mem]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [work_mem]
NOTICE: in process utility: superuser finished set
RESET work_mem;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [work_mem]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [work_mem]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [work_mem]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [work_mem]
NOTICE: in process utility: superuser finished set
ALTER SYSTEM SET work_mem = 8192;
NOTICE: in process utility: superuser attempting alter system
-NOTICE: in object_access_hook_str: superuser attempting alter (alter system set) [work_mem]
-NOTICE: in object_access_hook_str: superuser finished alter (alter system set) [work_mem]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x2000, alter system) [work_mem]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x2000, alter system) [work_mem]
NOTICE: in process utility: superuser finished alter system
ALTER SYSTEM RESET work_mem;
NOTICE: in process utility: superuser attempting alter system
-NOTICE: in object_access_hook_str: superuser attempting alter (alter system set) [work_mem]
-NOTICE: in object_access_hook_str: superuser finished alter (alter system set) [work_mem]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x2000, alter system) [work_mem]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x2000, alter system) [work_mem]
NOTICE: in process utility: superuser finished alter system
RESET SESSION AUTHORIZATION;
NOTICE: in process utility: superuser attempting set
-NOTICE: in object_access_hook_str: superuser attempting alter (set) [session_authorization]
-NOTICE: in object_access_hook_str: superuser finished alter (set) [session_authorization]
+NOTICE: in object_access_hook_str: superuser attempting alter (subId=0x1000, set) [session_authorization]
+NOTICE: in object_access_hook_str: superuser finished alter (subId=0x1000, set) [session_authorization]
NOTICE: in process utility: superuser finished set
SET test_oat_hooks.audit = false;
NOTICE: in process utility: superuser attempting set
+DROP ROLE regress_role_joe;
+ERROR: role "regress_role_joe" cannot be dropped because some objects depend on it
+DETAIL: privileges for parameter test_oat_hooks.user_var1
+privileges for parameter test_oat_hooks.super_var1
+privileges for parameter test_oat_hooks.user_var2
+privileges for parameter test_oat_hooks.super_var2
+privileges for parameter none.such
+privileges for parameter another.bogus
+REVOKE ALL PRIVILEGES ON PARAMETER
+ none.such, another.bogus,
+ test_oat_hooks.user_var1, test_oat_hooks.super_var1,
+ test_oat_hooks.user_var2, test_oat_hooks.super_var2
+ FROM regress_role_joe;
+DROP ROLE regress_role_joe;
diff --git a/src/test/modules/test_oat_hooks/sql/test_oat_hooks.sql b/src/test/modules/test_oat_hooks/sql/test_oat_hooks.sql
index 09e61864ee..6304202970 100644
--- a/src/test/modules/test_oat_hooks/sql/test_oat_hooks.sql
+++ b/src/test/modules/test_oat_hooks/sql/test_oat_hooks.sql
@@ -1,10 +1,30 @@
+-- Creating privileges on a placeholder GUC should create entries in the
+-- pg_parameter_acl catalog which conservatively grant no privileges to public.
+CREATE ROLE regress_role_joe;
+GRANT SET ON PARAMETER test_oat_hooks.user_var1 TO regress_role_joe;
+GRANT SET ON PARAMETER test_oat_hooks.super_var1 TO regress_role_joe;
+
-- SET commands fire both the ProcessUtility_hook and the
-- object_access_hook_str. Since the auditing GUC starts out false, we miss the
-- initial "attempting" audit message from the ProcessUtility_hook, but we
--- should thereafter see the audit messages
+-- should thereafter see the audit messages.
+--
+-- Module installation sql scripts *should* be written to restore SET privileges
+-- on userset variables to PUBLIC. This module does not, so the
+-- test_oat_hooks.user_var1 will remain unsettable by PUBLIC after this load.
LOAD 'test_oat_hooks';
SET test_oat_hooks.audit = true;
+-- Creating privileges on an existent custom GUC should create precisely the
+-- right privileges, not overly conservative ones.
+GRANT SET ON PARAMETER test_oat_hooks.user_var2 TO regress_role_joe;
+GRANT SET ON PARAMETER test_oat_hooks.super_var2 TO regress_role_joe;
+
+-- Granting multiple privileges on a parameter should be reported correctly to
+-- the OAT hook, but beware that WITH GRANT OPTION is not represented.
+GRANT SET, ALTER SYSTEM ON PARAMETER none.such TO regress_role_joe;
+GRANT SET, ALTER SYSTEM ON PARAMETER another.bogus TO regress_role_joe WITH GRANT OPTION;
+
-- Create objects for use in the test
CREATE USER regress_test_user;
CREATE TABLE regress_test_table (t text);
@@ -30,6 +50,22 @@ SET work_mem = 8192;
RESET work_mem;
ALTER SYSTEM SET work_mem = 8192;
ALTER SYSTEM RESET work_mem;
+
+-- Despite being a PGC_USERSET variable, user_var1 will have less permissive
+-- privileges as a consequence of having been granted to regress_role_joe prior
+-- to module load
+SET test_oat_hooks.user_var1 = true;
+SET test_oat_hooks.super_var1 = true;
+ALTER SYSTEM SET test_oat_hooks.user_var1 = true;
+ALTER SYSTEM SET test_oat_hooks.super_var1 = true;
+
+-- Unlike the parameters, above, these parameters were not granted to
+-- regress_role_joe until after module load
+SET test_oat_hooks.user_var2 = true;
+SET test_oat_hooks.super_var2 = true;
+ALTER SYSTEM SET test_oat_hooks.user_var2 = true;
+ALTER SYSTEM SET test_oat_hooks.super_var2 = true;
+
RESET SESSION AUTHORIZATION;
-- Turn off non-superuser permissions
@@ -51,3 +87,10 @@ ALTER SYSTEM RESET work_mem;
RESET SESSION AUTHORIZATION;
SET test_oat_hooks.audit = false;
+DROP ROLE regress_role_joe;
+REVOKE ALL PRIVILEGES ON PARAMETER
+ none.such, another.bogus,
+ test_oat_hooks.user_var1, test_oat_hooks.super_var1,
+ test_oat_hooks.user_var2, test_oat_hooks.super_var2
+ FROM regress_role_joe;
+DROP ROLE regress_role_joe;
diff --git a/src/test/modules/test_oat_hooks/test_oat_hooks.c b/src/test/modules/test_oat_hooks/test_oat_hooks.c
index eb7564ce22..34f18992e9 100644
--- a/src/test/modules/test_oat_hooks/test_oat_hooks.c
+++ b/src/test/modules/test_oat_hooks/test_oat_hooks.c
@@ -34,6 +34,15 @@ static bool REGRESS_deny_exec_perms = false;
static bool REGRESS_deny_utility_commands = false;
static bool REGRESS_audit = false;
+/*
+ * GUC for testing privileges on USERSET and SUSET variables,
+ * with and without privileges granted prior to module load.
+ */
+static bool REGRESS_userset_variable1 = false;
+static bool REGRESS_userset_variable2 = false;
+static bool REGRESS_suset_variable1 = false;
+static bool REGRESS_suset_variable2 = false;
+
/* Saved hook values in case of unload */
static object_access_hook_type next_object_access_hook = NULL;
static object_access_hook_type_str next_object_access_hook_str = NULL;
@@ -153,6 +162,56 @@ _PG_init(void)
NULL,
NULL);
+ /*
+ * test_oat_hooks.user_var{1,2} = (on|off)
+ */
+ DefineCustomBoolVariable("test_oat_hooks.user_var1",
+ "Dummy parameter settable by public",
+ NULL,
+ ®RESS_userset_variable1,
+ false,
+ PGC_USERSET,
+ GUC_NOT_IN_SAMPLE,
+ NULL,
+ NULL,
+ NULL);
+
+ DefineCustomBoolVariable("test_oat_hooks.user_var2",
+ "Dummy parameter settable by public",
+ NULL,
+ ®RESS_userset_variable2,
+ false,
+ PGC_USERSET,
+ GUC_NOT_IN_SAMPLE,
+ NULL,
+ NULL,
+ NULL);
+
+ /*
+ * test_oat_hooks.super_var{1,2} = (on|off)
+ */
+ DefineCustomBoolVariable("test_oat_hooks.super_var1",
+ "Dummy parameter settable by superuser",
+ NULL,
+ ®RESS_suset_variable1,
+ false,
+ PGC_SUSET,
+ GUC_NOT_IN_SAMPLE,
+ NULL,
+ NULL,
+ NULL);
+
+ DefineCustomBoolVariable("test_oat_hooks.super_var2",
+ "Dummy parameter settable by superuser",
+ NULL,
+ ®RESS_suset_variable2,
+ false,
+ PGC_SUSET,
+ GUC_NOT_IN_SAMPLE,
+ NULL,
+ NULL,
+ NULL);
+
MarkGUCPrefixReserved("test_oat_hooks");
/* Object access hook */
@@ -250,7 +309,14 @@ REGRESS_object_access_hook_str(ObjectAccessType access, Oid classId, const char
switch (access)
{
case OAT_POST_ALTER:
- if (subId & ACL_SET_VALUE)
+ if ((subId & ACL_SET) && (subId & ACL_ALTER_SYSTEM))
+ {
+ if (REGRESS_deny_set_variable && !superuser_arg(GetUserId()))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied: all privileges %s", objName)));
+ }
+ else if (subId & ACL_SET)
{
if (REGRESS_deny_set_variable && !superuser_arg(GetUserId()))
ereport(ERROR,
@@ -265,7 +331,7 @@ REGRESS_object_access_hook_str(ObjectAccessType access, Oid classId, const char
errmsg("permission denied: alter system set %s", objName)));
}
else
- elog(ERROR, "Unknown SettingAclRelationId subId: %d", subId);
+ elog(ERROR, "Unknown ParameterAclRelationId subId: %d", subId);
break;
default:
break;
@@ -860,12 +926,14 @@ accesstype_to_string(ObjectAccessType access, int subId)
type = "UNRECOGNIZED ObjectAccessType";
}
- if (subId & ACL_SET_VALUE)
- return psprintf("%s (set)", type);
+ if ((subId & ACL_SET) && (subId & ACL_ALTER_SYSTEM))
+ return psprintf("%s (subId=0x%x, all privileges)", type, subId);
+ if (subId & ACL_SET)
+ return psprintf("%s (subId=0x%x, set)", type, subId);
if (subId & ACL_ALTER_SYSTEM)
- return psprintf("%s (alter system set)", type);
+ return psprintf("%s (subId=0x%x, alter system)", type, subId);
- return psprintf("%s (subId=%d)", type, subId);
+ return psprintf("%s (subId=0x%x)", type, subId);
}
static char *
diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl
index 84a35590b7..1108391a6b 100644
--- a/src/test/modules/test_pg_dump/t/001_base.pl
+++ b/src/test/modules/test_pg_dump/t/001_base.pl
@@ -316,6 +316,66 @@ my %tests = (
like => { pg_dumpall_globals => 1, },
},
+ 'GRANT ALTER SYSTEM ON PARAMETER full_page_writes TO regress_dump_test_role' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT ALTER SYSTEM ON PARAMETER full_page_writes TO regress_dump_test_role;',
+ regexp =>
+
+ qr/^GRANT ALTER SYSTEM ON PARAMETER full_page_writes TO regress_dump_test_role;/m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALL ON PARAMETER fsync TO regress_dump_test_role WITH GRANT OPTION' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET, ALTER SYSTEM ON PARAMETER fsync TO regress_dump_test_role WITH GRANT OPTION;',
+ regexp =>
+ # "set" plus "alter system" is "all" privileges on parameters
+ qr/^GRANT ALL ON PARAMETER fsync TO regress_dump_test_role WITH GRANT OPTION;/m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALTER SYSTEM ON PARAMETER zero_damaged_pages" TO regress_dump_test_role' => {
+ create_order => 2,
+ create_sql =>
+ # configuration parameters get cased folded
+ 'GRANT ALTER SYSTEM ON PARAMETER ZERO_DAMAGED_PAGES TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM ON PARAMETER zero_damaged_pages TO regress_dump_test_role;/m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALL ON PARAMETER ignore_checksum_failure TO regress_dump_test_role' => {
+ create_order => 2,
+ create_sql =>
+ # GRANTED BY CURRENT_ROLE is allowed for SQL compatibility, but is ignored
+ 'GRANT ALTER SYSTEM, SET ON PARAMETER ignore_checksum_failure TO regress_dump_test_role GRANTED BY CURRENT_ROLE;',
+ regexp =>
+ qr/^GRANT ALL ON PARAMETER ignore_checksum_failure TO regress_dump_test_role/m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'REVOKE SET ON PARAMETER work_mem FROM PUBLIC' => {
+ create_order => 2,
+ create_sql =>
+ 'REVOKE ALL PRIVILEGES ON PARAMETER work_mem FROM PUBLIC;',
+ regexp =>
+ # Pubilc only has "set" by default, so revoking "all privileges" is simplified to revoking "set"
+ qr/^REVOKE SET ON PARAMETER work_mem FROM PUBLIC;/m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALL ON PARAMETER "DateStyle" TO regress_dump_test_role' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT ALL ON PARAMETER DateStyle TO regress_dump_test_role WITH GRANT OPTION; REVOKE GRANT OPTION FOR ALL ON PARAMETER DateStyle FROM regress_dump_test_role;',
+ regexp =>
+ # The revoke simplifies the ultimate grant so as to not include "with grant option"
+ qr/^GRANT ALL ON PARAMETER "DateStyle" TO regress_dump_test_role;/m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
'CREATE SCHEMA public' => {
regexp => qr/^CREATE SCHEMA public;/m,
like => {
diff --git a/src/test/modules/unsafe_tests/Makefile b/src/test/modules/unsafe_tests/Makefile
index 3ecf5fcfc5..df58273688 100644
--- a/src/test/modules/unsafe_tests/Makefile
+++ b/src/test/modules/unsafe_tests/Makefile
@@ -1,6 +1,6 @@
# src/test/modules/unsafe_tests/Makefile
-REGRESS = rolenames alter_system_table
+REGRESS = rolenames alter_system_table guc_privs
ifdef USE_PGXS
PG_CONFIG = pg_config
diff --git a/src/test/modules/unsafe_tests/expected/guc_privs.out b/src/test/modules/unsafe_tests/expected/guc_privs.out
new file mode 100644
index 0000000000..c43c19d5e5
--- /dev/null
+++ b/src/test/modules/unsafe_tests/expected/guc_privs.out
@@ -0,0 +1,375 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform operations as user 'regress_admin' --
+SET SESSION AUTHORIZATION regress_admin;
+-- PGC_BACKEND
+SET ignore_system_indexes = OFF; -- fail, cannot be set after connection start
+ERROR: parameter "ignore_system_indexes" cannot be set after connection start
+RESET ignore_system_indexes; -- fail, cannot be set after connection start
+ERROR: parameter "ignore_system_indexes" cannot be set after connection start
+ALTER SYSTEM SET ignore_system_indexes = OFF; -- ok
+ALTER SYSTEM RESET ignore_system_indexes; -- ok
+-- PGC_INTERNAL
+SET block_size = 50; -- fail, cannot be changed
+ERROR: parameter "block_size" cannot be changed
+RESET block_size; -- fail, cannot be changed
+ERROR: parameter "block_size" cannot be changed
+ALTER SYSTEM SET block_size = 50; -- fail, cannot be changed
+ERROR: parameter "block_size" cannot be changed
+ALTER SYSTEM RESET block_size; -- fail, cannot be changed
+ERROR: parameter "block_size" cannot be changed
+-- PGC_POSTMASTER
+SET autovacuum_freeze_max_age = 1000050000; -- fail, requires restart
+ERROR: parameter "autovacuum_freeze_max_age" cannot be changed without restarting the server
+RESET autovacuum_freeze_max_age; -- fail, requires restart
+ERROR: parameter "autovacuum_freeze_max_age" cannot be changed without restarting the server
+ALTER SYSTEM SET autovacuum_freeze_max_age = 1000050000; -- ok
+ALTER SYSTEM RESET autovacuum_freeze_max_age; -- ok
+ALTER SYSTEM SET config_file = '/usr/local/data/postgresql.conf'; -- fail, cannot be changed
+ERROR: parameter "config_file" cannot be changed
+ALTER SYSTEM RESET config_file; -- fail, cannot be changed
+ERROR: parameter "config_file" cannot be changed
+-- PGC_SIGHUP
+SET autovacuum = OFF; -- fail, requires reload
+ERROR: parameter "autovacuum" cannot be changed now
+RESET autovacuum; -- fail, requires reload
+ERROR: parameter "autovacuum" cannot be changed now
+ALTER SYSTEM SET autovacuum = OFF; -- ok
+ALTER SYSTEM RESET autovacuum; -- ok
+-- PGC_SUSET
+SET lc_messages = 'C'; -- ok
+RESET lc_messages; -- ok
+ALTER SYSTEM SET lc_messages = 'C'; -- ok
+ALTER SYSTEM RESET lc_messages; -- ok
+-- PGC_SU_BACKEND
+SET jit_debugging_support = OFF; -- fail, cannot be set after connection start
+ERROR: parameter "jit_debugging_support" cannot be set after connection start
+RESET jit_debugging_support; -- fail, cannot be set after connection start
+ERROR: parameter "jit_debugging_support" cannot be set after connection start
+ALTER SYSTEM SET jit_debugging_support = OFF; -- ok
+ALTER SYSTEM RESET jit_debugging_support; -- ok
+-- PGC_USERSET
+SET DateStyle = 'ISO, MDY'; -- ok
+RESET DateStyle; -- ok
+ALTER SYSTEM SET DateStyle = 'ISO, MDY'; -- ok
+ALTER SYSTEM RESET DateStyle; -- ok
+ALTER SYSTEM SET ssl_renegotiation_limit = 0; -- fail, cannot be changed
+ERROR: parameter "ssl_renegotiation_limit" cannot be changed
+ALTER SYSTEM RESET ssl_renegotiation_limit; -- fail, cannot be changed
+ERROR: parameter "ssl_renegotiation_limit" cannot be changed
+-- Finished testing superuser
+RESET statement_timeout;
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+-- Revoke privileges not yet granted
+REVOKE SET, ALTER SYSTEM ON PARAMETER work_mem FROM regress_host_resource_admin;
+REVOKE SET, ALTER SYSTEM ON PARAMETER zero_damaged_pages FROM regress_host_resource_admin;
+-- Check the new role does not yet have privileges on parameters
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+-- Check inappropriate and nonsense privilege types
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+-- Revoke, grant, and revoke again a SUSET parameter not yet granted
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+REVOKE SET ON PARAMETER zero_damaged_pages FROM regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+GRANT SET ON PARAMETER zero_damaged_pages TO regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+REVOKE SET ON PARAMETER zero_damaged_pages FROM regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+-- Revoke, grant, and revoke again a USERSET parameter not yet granted
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+REVOKE SET ON PARAMETER work_mem FROM regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+GRANT SET ON PARAMETER work_mem TO regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+REVOKE SET ON PARAMETER work_mem FROM regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+-- Grant privileges on parameters to the new non-superuser role
+GRANT SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+REVOKE ALL ON PARAMETER temp_buffers FROM PUBLIC;
+-- Check the new role now has privilges on parameters
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET WITH GRANT OPTION, ALTER SYSTEM WITH GRANT OPTION');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+-- Check again the inappropriate and nonsense privilege types. The prior similar check
+-- was performed before any entry for work_mem existed.
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER WITH GRANT OPTION');
+ERROR: unrecognized privilege type: "WHATEVER WITH GRANT OPTION"
+-- Check other function signatures
+SELECT has_parameter_privilege('regress_host_resource_admin',
+ (SELECT oid FROM pg_catalog.pg_parameter_acl WHERE parameter = 'max_stack_depth'),
+ 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ (SELECT oid FROM pg_catalog.pg_parameter_acl WHERE parameter = 'max_stack_depth'),
+ 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('hash_mem_multiplier', 'set');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege((SELECT oid FROM pg_catalog.pg_parameter_acl WHERE parameter = 'work_mem'),
+ 'alter system with grant option');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+-- Perform all operations as user 'regress_host_resource_admin' --
+SET SESSION AUTHORIZATION regress_host_resource_admin;
+ALTER SYSTEM SET autovacuum_work_mem = 32; -- ok, privileges have been granted
+ALTER SYSTEM SET ignore_system_indexes = OFF; -- fail, insufficient privileges
+ERROR: permission denied to set parameter "ignore_system_indexes"
+ALTER SYSTEM RESET autovacuum_multixact_freeze_max_age; -- fail, insufficient privileges
+ERROR: permission denied to set parameter "autovacuum_multixact_freeze_max_age"
+SET jit_provider = 'llvmjit'; -- fail, insufficient privileges
+ERROR: parameter "jit_provider" cannot be changed without restarting the server
+SELECT set_config ('jit_provider', 'llvmjit', true); -- fail, insufficient privileges
+ERROR: parameter "jit_provider" cannot be changed without restarting the server
+ALTER SYSTEM SET shared_buffers = 50; -- ok
+ALTER SYSTEM RESET shared_buffers; -- ok
+SET autovacuum_work_mem = 50; -- cannot be changed now
+ERROR: parameter "autovacuum_work_mem" cannot be changed now
+ALTER SYSTEM RESET temp_file_limit; -- ok
+SET TimeZone = 'Europe/Helsinki'; -- ok
+SELECT set_config ('temp_buffers', '8192', false); -- fail, privileges have been revoked
+ERROR: permission denied to set parameter "temp_buffers"
+ALTER SYSTEM RESET autovacuum_work_mem; -- ok, privileges have been granted
+RESET TimeZone; -- ok
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_host_resource_admin; -- fail, privileges remain
+ERROR: role "regress_host_resource_admin" cannot be dropped because some objects depend on it
+DETAIL: privileges for parameter work_mem
+privileges for parameter autovacuum_work_mem
+privileges for parameter hash_mem_multiplier
+privileges for parameter logical_decoding_work_mem
+privileges for parameter maintenance_work_mem
+privileges for parameter max_stack_depth
+privileges for parameter min_dynamic_shared_memory
+privileges for parameter shared_buffers
+privileges for parameter temp_file_limit
+-- Use "revoke" to remove the privileges and allow the role to be dropped
+REVOKE SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_file_limit, work_mem
+FROM regress_host_resource_admin;
+DROP ROLE regress_host_resource_admin; -- ok
+-- Try that again, but use "drop owned by" instead of "revoke"
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+SET SESSION AUTHORIZATION regress_host_resource_admin;
+ALTER SYSTEM SET autovacuum_work_mem = 32; -- fail, privileges not yet granted
+ERROR: permission denied to set parameter "autovacuum_work_mem"
+SET SESSION AUTHORIZATION regress_admin;
+GRANT SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+DROP ROLE regress_host_resource_admin; -- fail, privileges remain
+ERROR: role "regress_host_resource_admin" cannot be dropped because some objects depend on it
+DETAIL: privileges for parameter work_mem
+privileges for parameter autovacuum_work_mem
+privileges for parameter hash_mem_multiplier
+privileges for parameter logical_decoding_work_mem
+privileges for parameter maintenance_work_mem
+privileges for parameter max_stack_depth
+privileges for parameter min_dynamic_shared_memory
+privileges for parameter shared_buffers
+privileges for parameter temp_file_limit
+DROP OWNED BY regress_host_resource_admin RESTRICT; -- cascade should not be needed
+SET SESSION AUTHORIZATION regress_host_resource_admin;
+ALTER SYSTEM SET autovacuum_work_mem = 32; -- fail, "drop owned" has dropped privileges
+ERROR: permission denied to set parameter "autovacuum_work_mem"
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_host_resource_admin; -- ok
+-- Try that again, but use "reassign owned by" this time
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+CREATE ROLE regress_host_resource_newadmin NOSUPERUSER;
+GRANT SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+REASSIGN OWNED BY regress_host_resource_admin TO regress_host_resource_newadmin;
+SET SESSION AUTHORIZATION regress_host_resource_admin;
+ALTER SYSTEM SET autovacuum_work_mem = 32; -- ok, "reassign owned" did not change privileges
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_host_resource_admin; -- fail, privileges remain
+ERROR: role "regress_host_resource_admin" cannot be dropped because some objects depend on it
+DETAIL: privileges for parameter work_mem
+privileges for parameter autovacuum_work_mem
+privileges for parameter hash_mem_multiplier
+privileges for parameter logical_decoding_work_mem
+privileges for parameter maintenance_work_mem
+privileges for parameter max_stack_depth
+privileges for parameter min_dynamic_shared_memory
+privileges for parameter shared_buffers
+privileges for parameter temp_file_limit
+DROP ROLE regress_host_resource_newadmin; -- ok, nothing was transferred
+-- Use "drop owned by" so we can drop the role
+DROP OWNED BY regress_host_resource_admin; -- ok
+DROP ROLE regress_host_resource_admin; -- ok
+-- Drop the test superuser
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_admin; -- ok
diff --git a/src/test/modules/unsafe_tests/sql/guc_privs.sql b/src/test/modules/unsafe_tests/sql/guc_privs.sql
new file mode 100644
index 0000000000..feae920379
--- /dev/null
+++ b/src/test/modules/unsafe_tests/sql/guc_privs.sql
@@ -0,0 +1,174 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform operations as user 'regress_admin' --
+SET SESSION AUTHORIZATION regress_admin;
+-- PGC_BACKEND
+SET ignore_system_indexes = OFF; -- fail, cannot be set after connection start
+RESET ignore_system_indexes; -- fail, cannot be set after connection start
+ALTER SYSTEM SET ignore_system_indexes = OFF; -- ok
+ALTER SYSTEM RESET ignore_system_indexes; -- ok
+-- PGC_INTERNAL
+SET block_size = 50; -- fail, cannot be changed
+RESET block_size; -- fail, cannot be changed
+ALTER SYSTEM SET block_size = 50; -- fail, cannot be changed
+ALTER SYSTEM RESET block_size; -- fail, cannot be changed
+-- PGC_POSTMASTER
+SET autovacuum_freeze_max_age = 1000050000; -- fail, requires restart
+RESET autovacuum_freeze_max_age; -- fail, requires restart
+ALTER SYSTEM SET autovacuum_freeze_max_age = 1000050000; -- ok
+ALTER SYSTEM RESET autovacuum_freeze_max_age; -- ok
+ALTER SYSTEM SET config_file = '/usr/local/data/postgresql.conf'; -- fail, cannot be changed
+ALTER SYSTEM RESET config_file; -- fail, cannot be changed
+-- PGC_SIGHUP
+SET autovacuum = OFF; -- fail, requires reload
+RESET autovacuum; -- fail, requires reload
+ALTER SYSTEM SET autovacuum = OFF; -- ok
+ALTER SYSTEM RESET autovacuum; -- ok
+-- PGC_SUSET
+SET lc_messages = 'C'; -- ok
+RESET lc_messages; -- ok
+ALTER SYSTEM SET lc_messages = 'C'; -- ok
+ALTER SYSTEM RESET lc_messages; -- ok
+-- PGC_SU_BACKEND
+SET jit_debugging_support = OFF; -- fail, cannot be set after connection start
+RESET jit_debugging_support; -- fail, cannot be set after connection start
+ALTER SYSTEM SET jit_debugging_support = OFF; -- ok
+ALTER SYSTEM RESET jit_debugging_support; -- ok
+-- PGC_USERSET
+SET DateStyle = 'ISO, MDY'; -- ok
+RESET DateStyle; -- ok
+ALTER SYSTEM SET DateStyle = 'ISO, MDY'; -- ok
+ALTER SYSTEM RESET DateStyle; -- ok
+ALTER SYSTEM SET ssl_renegotiation_limit = 0; -- fail, cannot be changed
+ALTER SYSTEM RESET ssl_renegotiation_limit; -- fail, cannot be changed
+-- Finished testing superuser
+RESET statement_timeout;
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+-- Revoke privileges not yet granted
+REVOKE SET, ALTER SYSTEM ON PARAMETER work_mem FROM regress_host_resource_admin;
+REVOKE SET, ALTER SYSTEM ON PARAMETER zero_damaged_pages FROM regress_host_resource_admin;
+-- Check the new role does not yet have privileges on parameters
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET, ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+-- Check inappropriate and nonsense privilege types
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+-- Revoke, grant, and revoke again a SUSET parameter not yet granted
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+REVOKE SET ON PARAMETER zero_damaged_pages FROM regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+GRANT SET ON PARAMETER zero_damaged_pages TO regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+REVOKE SET ON PARAMETER zero_damaged_pages FROM regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+-- Revoke, grant, and revoke again a USERSET parameter not yet granted
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+REVOKE SET ON PARAMETER work_mem FROM regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+GRANT SET ON PARAMETER work_mem TO regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+REVOKE SET ON PARAMETER work_mem FROM regress_host_resource_admin;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+-- Grant privileges on parameters to the new non-superuser role
+GRANT SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+REVOKE ALL ON PARAMETER temp_buffers FROM PUBLIC;
+-- Check the new role now has privilges on parameters
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET, ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET WITH GRANT OPTION, ALTER SYSTEM WITH GRANT OPTION');
+-- Check again the inappropriate and nonsense privilege types. The prior similar check
+-- was performed before any entry for work_mem existed.
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER WITH GRANT OPTION');
+-- Check other function signatures
+SELECT has_parameter_privilege('regress_host_resource_admin',
+ (SELECT oid FROM pg_catalog.pg_parameter_acl WHERE parameter = 'max_stack_depth'),
+ 'SET');
+SELECT has_parameter_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET');
+SELECT has_parameter_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ (SELECT oid FROM pg_catalog.pg_parameter_acl WHERE parameter = 'max_stack_depth'),
+ 'SET');
+SELECT has_parameter_privilege('hash_mem_multiplier', 'set');
+SELECT has_parameter_privilege((SELECT oid FROM pg_catalog.pg_parameter_acl WHERE parameter = 'work_mem'),
+ 'alter system with grant option');
+-- Perform all operations as user 'regress_host_resource_admin' --
+SET SESSION AUTHORIZATION regress_host_resource_admin;
+ALTER SYSTEM SET autovacuum_work_mem = 32; -- ok, privileges have been granted
+ALTER SYSTEM SET ignore_system_indexes = OFF; -- fail, insufficient privileges
+ALTER SYSTEM RESET autovacuum_multixact_freeze_max_age; -- fail, insufficient privileges
+SET jit_provider = 'llvmjit'; -- fail, insufficient privileges
+SELECT set_config ('jit_provider', 'llvmjit', true); -- fail, insufficient privileges
+ALTER SYSTEM SET shared_buffers = 50; -- ok
+ALTER SYSTEM RESET shared_buffers; -- ok
+SET autovacuum_work_mem = 50; -- cannot be changed now
+ALTER SYSTEM RESET temp_file_limit; -- ok
+SET TimeZone = 'Europe/Helsinki'; -- ok
+SELECT set_config ('temp_buffers', '8192', false); -- fail, privileges have been revoked
+ALTER SYSTEM RESET autovacuum_work_mem; -- ok, privileges have been granted
+RESET TimeZone; -- ok
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_host_resource_admin; -- fail, privileges remain
+-- Use "revoke" to remove the privileges and allow the role to be dropped
+REVOKE SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_file_limit, work_mem
+FROM regress_host_resource_admin;
+DROP ROLE regress_host_resource_admin; -- ok
+-- Try that again, but use "drop owned by" instead of "revoke"
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+SET SESSION AUTHORIZATION regress_host_resource_admin;
+ALTER SYSTEM SET autovacuum_work_mem = 32; -- fail, privileges not yet granted
+SET SESSION AUTHORIZATION regress_admin;
+GRANT SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+DROP ROLE regress_host_resource_admin; -- fail, privileges remain
+DROP OWNED BY regress_host_resource_admin RESTRICT; -- cascade should not be needed
+SET SESSION AUTHORIZATION regress_host_resource_admin;
+ALTER SYSTEM SET autovacuum_work_mem = 32; -- fail, "drop owned" has dropped privileges
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_host_resource_admin; -- ok
+-- Try that again, but use "reassign owned by" this time
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+CREATE ROLE regress_host_resource_newadmin NOSUPERUSER;
+GRANT SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+REASSIGN OWNED BY regress_host_resource_admin TO regress_host_resource_newadmin;
+SET SESSION AUTHORIZATION regress_host_resource_admin;
+ALTER SYSTEM SET autovacuum_work_mem = 32; -- ok, "reassign owned" did not change privileges
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_host_resource_admin; -- fail, privileges remain
+DROP ROLE regress_host_resource_newadmin; -- ok, nothing was transferred
+-- Use "drop owned by" so we can drop the role
+DROP OWNED BY regress_host_resource_admin; -- ok
+DROP ROLE regress_host_resource_admin; -- ok
+-- Drop the test superuser
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_admin; -- ok
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 423b9b99fb..5322039237 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1392,6 +1392,19 @@ pg_matviews| SELECT n.nspname AS schemaname,
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace)))
WHERE (c.relkind = 'm'::"char");
+pg_parameter_privileges| SELECT grantor.rolname AS grantor,
+ grantee.rolname AS grantee,
+ set_acl.parameter,
+ acl.privilege_type,
+ acl.is_grantable
+ FROM pg_parameter_acl set_acl,
+ ((LATERAL ( SELECT aclexplode.grantor,
+ aclexplode.grantee,
+ aclexplode.privilege_type,
+ aclexplode.is_grantable
+ FROM aclexplode(set_acl.setacl) aclexplode(grantor, grantee, privilege_type, is_grantable)) acl
+ LEFT JOIN pg_authid grantee ON ((acl.grantee = grantee.oid)))
+ LEFT JOIN pg_authid grantor ON ((acl.grantor = grantor.oid)));
pg_policies| SELECT n.nspname AS schemaname,
c.relname AS tablename,
pol.polname AS policyname,
--
2.35.1