Granting SET and ALTER SYSTE privileges for GUCs
Hackers,
In the ongoing effort [1] to reduce the number of tasks which require operating under superuser privileges, this patch extends the system to allow, per GUC variable, the ability to SET or ALTER SYSTEM for the variable. A previous patch set was submitted [2] which created hard-coded privileged roles with the authority to manage associated hard-coded sets of GUC variables. This current patch appears superior in several ways:
- It allows much greater flexibility in how roles and GUCs are associated
- Custom GUC variables defined by extensions can be covered by this approach
and perhaps most importantly,
- It's what Andrew suggested
Granting SET privilege on a USERSET variable makes no practical difference, but for SUSET variables it does, and granting ALTER SYSTEM is meaningful for all variables. The patch does not mandate that non-login roles be created for this, but as a usage suggestion, one could define a non-login role and assign privileges for a set of GUCs, such as:
CREATE ROLE regress_host_resource_admin NOSUPERUSER;
GRANT SET VALUE, ALTER SYSTEM ON
autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
shared_buffers, temp_buffers, temp_file_limit, work_mem
TO regress_host_resource_admin;
and then delegate authority to manage the set of GUCs to a non-superuser by granting membership in non-login role:
CREATE ROLE regress_admin_member IN ROLE regress_host_resource_admin;
One disadvantage of this approach is that the GUC variables are represented both in the list of C structures in guc.c and in the new system catalog pg_config_param's .dat file. Failure to enter a GUC in the .dat file will result in the inability to grant privileges on the GUC, at least unless/until you run CREATE CONFIGURATION PARAMETER on the GUC. (This is, in fact, how extension scripts deal with the issue.) It would perhaps be better if the list of GUCs were not duplicated, but I wasn't clever enough to find a clean way to do that without greatly expanding the patch (nor did I complete prototyping any such thing.)
Attachments:
v2-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-for-variables.patchapplication/octet-stream; name=v2-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-for-variables.patch; x-unix-mode=0644Download
From 0ff4364bd4dcd1c9e5ed40b047112be4a7cb9311 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Mon, 15 Nov 2021 09:19:22 -0800
Subject: [PATCH v2] Allow GRANT of SET and ALTER SYSTEM for variables
Allow granting of privilege to set or alter system set variables
which otherwise can only be managed by superusers. Each
(role,variable,privilege) triple is independently grantable, so a
user may be granted privilege to SET but not to ALTER SYSTEM SET on
a variable, or vice versa. The privilege to SET a userset variable
may be granted, though doing so has no practical effect, since any
role can set userset variables anyway.
Addition custom variables defined by extensions can be registered
with the system using a new command, CREATE CONFIGURATION PARAMETER,
from within the extension install script. For upgrades which need
to drop an older version's configuration parameters, a DROP
CONFIGURATION PARAMETER command is supplied. There is no obvious
other use for these new commands.
---
contrib/pg_prewarm/Makefile | 3 +-
contrib/pg_prewarm/pg_prewarm--1.2--1.3.sql | 7 +
contrib/pg_prewarm/pg_prewarm.control | 2 +-
contrib/pg_stat_statements/Makefile | 3 +-
.../expected/oldextversions.out | 51 ++++
.../expected/pg_stat_statements.out | 17 ++
.../pg_stat_statements--1.9--1.10.sql | 10 +
.../pg_stat_statements/pg_stat_statements.c | 14 +-
.../pg_stat_statements.control | 2 +-
.../pg_stat_statements/sql/oldextversions.sql | 5 +
.../sql/pg_stat_statements.sql | 11 +
contrib/pg_trgm/Makefile | 6 +-
contrib/pg_trgm/pg_trgm--1.6--1.7.sql | 8 +
contrib/pg_trgm/pg_trgm.control | 2 +-
contrib/postgres_fdw/Makefile | 3 +-
.../postgres_fdw/postgres_fdw--1.1--1.2.sql | 6 +
contrib/postgres_fdw/postgres_fdw.control | 2 +-
doc/src/sgml/ddl.sgml | 39 ++-
doc/src/sgml/ref/grant.sgml | 7 +
doc/src/sgml/ref/set.sgml | 5 +-
src/backend/catalog/Makefile | 5 +-
src/backend/catalog/aclchk.c | 274 ++++++++++++++++++
src/backend/catalog/catalog.c | 6 +
src/backend/catalog/dependency.c | 6 +
src/backend/catalog/objectaddress.c | 49 ++++
src/backend/catalog/pg_config_param.c | 143 +++++++++
src/backend/commands/Makefile | 1 +
src/backend/commands/alter.c | 1 +
src/backend/commands/configcmds.c | 80 +++++
src/backend/commands/dropcmds.c | 7 +
src/backend/commands/event_trigger.c | 6 +
src/backend/commands/seclabel.c | 1 +
src/backend/commands/tablecmds.c | 1 +
src/backend/nodes/copyfuncs.c | 28 ++
src/backend/nodes/equalfuncs.c | 24 ++
src/backend/parser/gram.y | 138 ++++++++-
src/backend/tcop/utility.c | 32 ++
src/backend/utils/adt/acl.c | 58 ++++
src/backend/utils/cache/lsyscache.c | 20 ++
src/backend/utils/cache/syscache.c | 23 ++
src/backend/utils/misc/README | 11 +
src/backend/utils/misc/guc.c | 63 +++-
src/bin/pg_dump/pg_dump.c | 3 +
src/include/catalog/dependency.h | 1 +
src/include/catalog/pg_config_param.dat | 211 ++++++++++++++
src/include/catalog/pg_config_param.h | 61 ++++
src/include/catalog/pg_default_acl.h | 1 +
src/include/commands/configcmds.h | 17 ++
src/include/nodes/nodes.h | 2 +
src/include/nodes/parsenodes.h | 27 +-
src/include/parser/kwlist.h | 1 +
src/include/tcop/cmdtaglist.h | 2 +
src/include/utils/acl.h | 11 +-
src/include/utils/guc.h | 1 +
src/include/utils/lsyscache.h | 1 +
src/include/utils/syscache.h | 2 +
src/test/modules/test_extensions/Makefile | 3 +-
.../test_extensions/test_ext9--1.0--1.1.sql | 12 +
.../test_extensions/test_ext9--1.0.sql | 12 +
.../modules/test_extensions/test_ext9.control | 4 +
src/test/regress/expected/guc_privs.out | 144 +++++++++
src/test/regress/expected/oidjoins.out | 1 +
src/test/regress/expected/privileges.out | 40 ++-
src/test/regress/expected/sanity_check.out | 1 +
src/test/regress/parallel_schedule | 2 +-
src/test/regress/sql/guc_privs.sql | 106 +++++++
src/test/regress/sql/privileges.sql | 37 ++-
src/tools/pgindent/typedefs.list | 1 +
68 files changed, 1847 insertions(+), 37 deletions(-)
create mode 100644 contrib/pg_prewarm/pg_prewarm--1.2--1.3.sql
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
create mode 100644 contrib/pg_trgm/pg_trgm--1.6--1.7.sql
create mode 100644 contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql
create mode 100644 src/backend/catalog/pg_config_param.c
create mode 100644 src/backend/commands/configcmds.c
create mode 100644 src/include/catalog/pg_config_param.dat
create mode 100644 src/include/catalog/pg_config_param.h
create mode 100644 src/include/commands/configcmds.h
create mode 100644 src/test/modules/test_extensions/test_ext9--1.0--1.1.sql
create mode 100644 src/test/modules/test_extensions/test_ext9--1.0.sql
create mode 100644 src/test/modules/test_extensions/test_ext9.control
create mode 100644 src/test/regress/expected/guc_privs.out
create mode 100644 src/test/regress/sql/guc_privs.sql
diff --git a/contrib/pg_prewarm/Makefile b/contrib/pg_prewarm/Makefile
index b13ac3c813..1f4bccb807 100644
--- a/contrib/pg_prewarm/Makefile
+++ b/contrib/pg_prewarm/Makefile
@@ -7,7 +7,8 @@ OBJS = \
pg_prewarm.o
EXTENSION = pg_prewarm
-DATA = pg_prewarm--1.1--1.2.sql pg_prewarm--1.1.sql pg_prewarm--1.0--1.1.sql
+DATA = pg_prewarm--1.2--1.3.sql pg_prewarm--1.1--1.2.sql pg_prewarm--1.1.sql \
+ pg_prewarm--1.0--1.1.sql
PGFILEDESC = "pg_prewarm - preload relation data into system buffer cache"
ifdef USE_PGXS
diff --git a/contrib/pg_prewarm/pg_prewarm--1.2--1.3.sql b/contrib/pg_prewarm/pg_prewarm--1.2--1.3.sql
new file mode 100644
index 0000000000..87ca3fd637
--- /dev/null
+++ b/contrib/pg_prewarm/pg_prewarm--1.2--1.3.sql
@@ -0,0 +1,7 @@
+/* contrib/pg_prewarm/pg_prewarm-1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_prewarm UPDATE TO '1.3'" to load this file. \quit
+
+CREATE CONFIGURATION PARAMETER IF NOT EXISTS 'pg_prewarm.autoprewarm_interval';
+CREATE CONFIGURATION PARAMETER IF NOT EXISTS 'pg_prewarm.autoprewarm';
diff --git a/contrib/pg_prewarm/pg_prewarm.control b/contrib/pg_prewarm/pg_prewarm.control
index 40e3add481..cb97cf6f13 100644
--- a/contrib/pg_prewarm/pg_prewarm.control
+++ b/contrib/pg_prewarm/pg_prewarm.control
@@ -1,5 +1,5 @@
# pg_prewarm extension
comment = 'prewarm relation data'
-default_version = '1.2'
+default_version = '1.3'
module_pathname = '$libdir/pg_prewarm'
relocatable = true
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 7fabd96f38..3d01fdb410 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,8 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.8--1.9.sql \
+DATA = pg_stat_statements--1.4.sql \
+ pg_stat_statements--1.8--1.9.sql pg_stat_statements--1.9--1.10.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
pg_stat_statements--1.3--1.4.sql pg_stat_statements--1.2--1.3.sql \
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index f18c08838f..f206a0f0ef 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -136,4 +136,55 @@ SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
(1 row)
+-- Grantable privileges on GUCs for pg_stat_statements in 1.10
+AlTER EXTENSION pg_stat_statements UPDATE TO '1.10';
+\d pg_stat_statements
+ View "public.pg_stat_statements"
+ Column | Type | Collation | Nullable | Default
+---------------------+------------------+-----------+----------+---------
+ userid | oid | | |
+ dbid | oid | | |
+ toplevel | boolean | | |
+ queryid | bigint | | |
+ query | text | | |
+ plans | bigint | | |
+ total_plan_time | double precision | | |
+ min_plan_time | double precision | | |
+ max_plan_time | double precision | | |
+ mean_plan_time | double precision | | |
+ stddev_plan_time | double precision | | |
+ calls | bigint | | |
+ total_exec_time | double precision | | |
+ min_exec_time | double precision | | |
+ max_exec_time | double precision | | |
+ mean_exec_time | double precision | | |
+ stddev_exec_time | double precision | | |
+ rows | bigint | | |
+ shared_blks_hit | bigint | | |
+ shared_blks_read | bigint | | |
+ shared_blks_dirtied | bigint | | |
+ shared_blks_written | bigint | | |
+ local_blks_hit | bigint | | |
+ local_blks_read | bigint | | |
+ local_blks_dirtied | bigint | | |
+ local_blks_written | bigint | | |
+ temp_blks_read | bigint | | |
+ temp_blks_written | bigint | | |
+ blk_read_time | double precision | | |
+ blk_write_time | double precision | | |
+ wal_records | bigint | | |
+ wal_fpi | bigint | | |
+ wal_bytes | numeric | | |
+
+SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
+ pg_get_functiondef
+--------------------------------------------------------------------------------------------------------------------------------
+ CREATE OR REPLACE FUNCTION public.pg_stat_statements_reset(userid oid DEFAULT 0, dbid oid DEFAULT 0, queryid bigint DEFAULT 0)+
+ RETURNS void +
+ LANGUAGE c +
+ PARALLEL SAFE STRICT +
+ AS '$libdir/pg_stat_statements', $function$pg_stat_statements_reset_1_7$function$ +
+
+(1 row)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index b52d187722..580a1e3aef 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -1077,4 +1077,21 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
+-- CUSTOM GUCS FROM THIS EXTENSION
+SHOW pg_stat_statements.max;
+ pg_stat_statements.max
+------------------------
+ 5000
+(1 row)
+
+CREATE ROLE regress_stat_admin;
+GRANT ALTER SYSTEM ON pg_stat_statements.max TO regress_stat_admin;
+SET SESSION AUTHORIZATION regress_stat_admin;
+ALTER SYSTEM SET pg_stat_statements.max = 3000;
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_stat_admin;
+ERROR: role "regress_stat_admin" cannot be dropped because some objects depend on it
+DETAIL: privileges for configuration parameter pg_stat_statements.max
+REVOKE ALTER SYSTEM ON pg_stat_statements.max FROM regress_stat_admin;
+DROP ROLE regress_stat_admin;
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
new file mode 100644
index 0000000000..5e2c525297
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
@@ -0,0 +1,10 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.10'" to load this file. \quit
+
+CREATE CONFIGURATION PARAMETER IF NOT EXISTS 'pg_stat_statements.max';
+CREATE CONFIGURATION PARAMETER IF NOT EXISTS 'pg_stat_statements.track';
+CREATE CONFIGURATION PARAMETER IF NOT EXISTS 'pg_stat_statements.track_utility';
+CREATE CONFIGURATION PARAMETER IF NOT EXISTS 'pg_stat_statements.track_planning';
+CREATE CONFIGURATION PARAMETER IF NOT EXISTS 'pg_stat_statements.save';
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 726ba59e2b..db6e7c54a9 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -121,7 +121,8 @@ typedef enum pgssVersion
PGSS_V1_2,
PGSS_V1_3,
PGSS_V1_8,
- PGSS_V1_9
+ PGSS_V1_9,
+ PGSS_V1_10
} pgssVersion;
typedef enum pgssStoreKind
@@ -302,6 +303,7 @@ PG_FUNCTION_INFO_V1(pg_stat_statements_1_2);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_3);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_8);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -1438,6 +1440,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_10(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_10, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_9(PG_FUNCTION_ARGS)
{
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 2f1ce6ed50..0747e48138 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.9'
+default_version = '1.10'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index f2e822acd3..ea1f1cdd19 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -36,4 +36,9 @@ AlTER EXTENSION pg_stat_statements UPDATE TO '1.8';
\d pg_stat_statements
SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
+-- Grantable privileges on GUCs for pg_stat_statements in 1.10
+AlTER EXTENSION pg_stat_statements UPDATE TO '1.10';
+\d pg_stat_statements
+SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index dffd2c8c18..a762136ce6 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -442,4 +442,15 @@ SELECT (
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
+-- CUSTOM GUCS FROM THIS EXTENSION
+SHOW pg_stat_statements.max;
+CREATE ROLE regress_stat_admin;
+GRANT ALTER SYSTEM ON pg_stat_statements.max TO regress_stat_admin;
+SET SESSION AUTHORIZATION regress_stat_admin;
+ALTER SYSTEM SET pg_stat_statements.max = 3000;
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_stat_admin;
+REVOKE ALTER SYSTEM ON pg_stat_statements.max FROM regress_stat_admin;
+DROP ROLE regress_stat_admin;
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_trgm/Makefile b/contrib/pg_trgm/Makefile
index 1fbdc9ec1e..8fac4f6289 100644
--- a/contrib/pg_trgm/Makefile
+++ b/contrib/pg_trgm/Makefile
@@ -9,9 +9,9 @@ OBJS = \
trgm_regexp.o
EXTENSION = pg_trgm
-DATA = pg_trgm--1.5--1.6.sql pg_trgm--1.4--1.5.sql pg_trgm--1.3--1.4.sql \
- pg_trgm--1.3.sql pg_trgm--1.2--1.3.sql pg_trgm--1.1--1.2.sql \
- pg_trgm--1.0--1.1.sql
+DATA = pg_trgm--1.6--1.7.sql pg_trgm--1.5--1.6.sql pg_trgm--1.4--1.5.sql \
+ pg_trgm--1.3--1.4.sql pg_trgm--1.3.sql pg_trgm--1.2--1.3.sql \
+ pg_trgm--1.1--1.2.sql pg_trgm--1.0--1.1.sql
PGFILEDESC = "pg_trgm - trigram matching"
REGRESS = pg_trgm pg_word_trgm pg_strict_word_trgm
diff --git a/contrib/pg_trgm/pg_trgm--1.6--1.7.sql b/contrib/pg_trgm/pg_trgm--1.6--1.7.sql
new file mode 100644
index 0000000000..509f9c98ff
--- /dev/null
+++ b/contrib/pg_trgm/pg_trgm--1.6--1.7.sql
@@ -0,0 +1,8 @@
+/* contrib/pg_trgm/pg_trgm--1.6--1.7.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_trgm UPDATE TO '1.7'" to load this file. \quit
+
+CREATE CONFIGURATION PARAMETER IF NOT EXISTS 'pg_trgm.similarity_threshold';
+CREATE CONFIGURATION PARAMETER IF NOT EXISTS 'pg_trgm.word_similarity_threshold';
+CREATE CONFIGURATION PARAMETER IF NOT EXISTS 'pg_trgm.strict_word_similarity_threshold';
diff --git a/contrib/pg_trgm/pg_trgm.control b/contrib/pg_trgm/pg_trgm.control
index 1d6a9ddf25..6e3ee43c51 100644
--- a/contrib/pg_trgm/pg_trgm.control
+++ b/contrib/pg_trgm/pg_trgm.control
@@ -1,6 +1,6 @@
# pg_trgm extension
comment = 'text similarity measurement and index searching based on trigrams'
-default_version = '1.6'
+default_version = '1.7'
module_pathname = '$libdir/pg_trgm'
relocatable = true
trusted = true
diff --git a/contrib/postgres_fdw/Makefile b/contrib/postgres_fdw/Makefile
index c1b0cad453..3564015ee8 100644
--- a/contrib/postgres_fdw/Makefile
+++ b/contrib/postgres_fdw/Makefile
@@ -14,7 +14,8 @@ PG_CPPFLAGS = -I$(libpq_srcdir)
SHLIB_LINK_INTERNAL = $(libpq)
EXTENSION = postgres_fdw
-DATA = postgres_fdw--1.0.sql postgres_fdw--1.0--1.1.sql
+DATA = postgres_fdw--1.0.sql postgres_fdw--1.0--1.1.sql \
+ postgres_fdw--1.1--1.2.sql
REGRESS = postgres_fdw
diff --git a/contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql b/contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql
new file mode 100644
index 0000000000..e8fbe245f3
--- /dev/null
+++ b/contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql
@@ -0,0 +1,6 @@
+/* contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION postgres_fdw UPDATE TO '1.2'" to load this file. \quit
+
+CREATE CONFIGURATION PARAMETER IF NOT EXISTS 'postgres_fdw.application_name';
diff --git a/contrib/postgres_fdw/postgres_fdw.control b/contrib/postgres_fdw/postgres_fdw.control
index d489382064..a4b800be4f 100644
--- a/contrib/postgres_fdw/postgres_fdw.control
+++ b/contrib/postgres_fdw/postgres_fdw.control
@@ -1,5 +1,5 @@
# postgres_fdw extension
comment = 'foreign-data wrapper for remote PostgreSQL servers'
-default_version = '1.1'
+default_version = '1.2'
module_pathname = '$libdir/postgres_fdw'
relocatable = true
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 94f745aed0..d862ab8c0e 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -1620,7 +1620,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 VALUE</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.
@@ -1888,6 +1889,26 @@ REVOKE ALL ON accounts FROM PUBLIC;
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>SET VALUE</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>SET</command> command to
+ change run-time configuration parameters.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ALTER SYSTEM</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>ALTER SYSTEM
+ SET</command> command to change server configuration parameters.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
The privileges required by other commands are listed on the
@@ -2026,6 +2047,16 @@ REVOKE ALL ON accounts FROM PUBLIC;
<literal>TYPE</literal>
</entry>
</row>
+ <row>
+ <entry><literal>SET VALUE</literal></entry>
+ <entry><literal>s</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
+ <row>
+ <entry><literal>ALTER SYSTEM</literal></entry>
+ <entry><literal>A</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -2132,6 +2163,12 @@ REVOKE ALL ON accounts FROM PUBLIC;
<entry><literal>U</literal></entry>
<entry><literal>\dT+</literal></entry>
</row>
+ <row>
+ <entry><literal>Configuration parameter</literal></entry>
+ <entry><literal>sA</literal></entry>
+ <entry>none</entry>
+ <entry><literal></literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index a897712de2..21ec320a8d 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 VALUE | ALTER SYSTEM }
+ ON <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 VALUE</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/set.sgml b/doc/src/sgml/ref/set.sgml
index 339ee9eec9..a08057d1d1 100644
--- a/doc/src/sgml/ref/set.sgml
+++ b/doc/src/sgml/ref/set.sgml
@@ -34,8 +34,9 @@ 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.)
+ (But some require either superuser privileges or granted <literal>SET
+ VALUE</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>
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 4e6efda97f..eb6d044da0 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -28,6 +28,7 @@ OBJS = \
pg_cast.o \
pg_class.o \
pg_collation.o \
+ pg_config_param.o \
pg_constraint.o \
pg_conversion.o \
pg_db_role_setting.o \
@@ -54,7 +55,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_config_param.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 \
@@ -78,7 +79,7 @@ POSTGRES_BKI_SRCS := $(addprefix $(top_srcdir)/src/include/catalog/, $(CATALOG_H
# The .dat files we need can just be listed alphabetically.
POSTGRES_BKI_DATA = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_aggregate.dat pg_am.dat pg_amop.dat pg_amproc.dat pg_authid.dat \
- pg_cast.dat pg_class.dat pg_collation.dat pg_conversion.dat \
+ pg_cast.dat pg_class.dat pg_collation.dat pg_config_param.dat pg_conversion.dat \
pg_database.dat pg_language.dat \
pg_namespace.dat pg_opclass.dat pg_operator.dat pg_opfamily.dat \
pg_proc.dat pg_range.dat pg_tablespace.dat \
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ce0a4ff14e..4ceb088034 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -33,6 +33,7 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_config_param.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/pg_default_acl.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_ConfigParam(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_CONFIG_PARAM:
+ whole_mask = ACL_ALL_RIGHTS_CONFIG_PARAM;
+ 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_CONFIG_PARAM:
+ all_privileges = ACL_ALL_RIGHTS_CONFIG_PARAM;
+ errormsg = gettext_noop("invalid privilege type %s for configuration 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_CONFIG_PARAM:
+ ExecGrant_ConfigParam(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -759,6 +771,15 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_CONFIG_PARAM:
+ foreach(cell, objnames)
+ {
+ char *cfgname = strVal(lfirst(cell));
+ Oid cfgid = get_cfgparam_oid(cfgname, false);
+
+ objects = lappend_oid(objects, cfgid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -1007,6 +1028,10 @@ ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *s
all_privileges = ACL_ALL_RIGHTS_SCHEMA;
errormsg = gettext_noop("invalid privilege type %s for schema");
break;
+ case OBJECT_CONFIG_PARAM:
+ all_privileges = ACL_ALL_RIGHTS_CONFIG_PARAM;
+ errormsg = gettext_noop("invalid privilege type %s for configuration parameter");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) action->objtype);
@@ -1204,6 +1229,12 @@ SetDefaultACL(InternalDefaultACL *iacls)
this_privileges = ACL_ALL_RIGHTS_SCHEMA;
break;
+ case OBJECT_CONFIG_PARAM:
+ objtype = DEFACLOBJ_CONFIG_PARAM;
+ if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+ this_privileges = ACL_ALL_RIGHTS_CONFIG_PARAM;
+ break;
+
default:
elog(ERROR, "unrecognized objtype: %d",
(int) iacls->objtype);
@@ -1437,6 +1468,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case DEFACLOBJ_NAMESPACE:
iacls.objtype = OBJECT_SCHEMA;
break;
+ case DEFACLOBJ_CONFIG_PARAM:
+ iacls.objtype = OBJECT_CONFIG_PARAM;
+ break;
default:
/* Shouldn't get here */
elog(ERROR, "unexpected default ACL type: %d",
@@ -1494,6 +1528,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case ForeignDataWrapperRelationId:
istmt.objtype = OBJECT_FDW;
break;
+ case ConfigParamRelationId:
+ istmt.objtype = OBJECT_CONFIG_PARAM;
+ break;
default:
elog(ERROR, "unexpected object class %u", classid);
break;
@@ -3225,6 +3262,130 @@ ExecGrant_Type(InternalGrant *istmt)
table_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_ConfigParam(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_CONFIG_PARAM;
+
+ relation = table_open(ConfigParamRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid cfgId = lfirst_oid(cell);
+ Form_pg_config_param pg_config_param_tuple;
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ Oid ownerId;
+ HeapTuple tuple;
+ HeapTuple newtuple;
+ Datum values[Natts_pg_config_param];
+ bool nulls[Natts_pg_config_param];
+ bool replaces[Natts_pg_config_param];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(CONFIGOID, ObjectIdGetDatum(cfgId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for configuration parameter %u", cfgId);
+
+ pg_config_param_tuple = (Form_pg_config_param) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ ownerId = pg_config_param_tuple->configowner;
+ aclDatum = SysCacheGetAttr(CONFIGNAME, tuple, Anum_pg_config_param_cfgacl,
+ &isNull);
+ if (isNull)
+ {
+ old_acl = acldefault(OBJECT_CONFIG_PARAM, ownerId);
+ /* 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, ownerId,
+ &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,
+ cfgId, grantorId, OBJECT_CONFIG_PARAM,
+ NameStr(pg_config_param_tuple->configname),
+ 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, ownerId);
+
+ /*
+ * 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_config_param_cfgacl - 1] = true;
+ values[Anum_pg_config_param_cfgacl - 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(cfgId, ConfigParamRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(ConfigParamRelationId,
+ pg_config_param_tuple->oid, 0,
+ ownerId,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ table_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3255,6 +3416,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 +3457,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 +3497,13 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_COLUMN:
msg = gettext_noop("permission denied for column %s");
break;
+ case OBJECT_CONFIG_PARAM:
+ /*
+ * 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;
@@ -3457,6 +3633,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_COLLATION:
msg = gettext_noop("must be owner of collation %s");
break;
+ case OBJECT_CONFIG_PARAM:
+ msg = gettext_noop("must be owner of configuration parameter %s");
+ break;
case OBJECT_CONVERSION:
msg = gettext_noop("must be owner of conversion %s");
break;
@@ -4000,6 +4179,62 @@ 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_config_param_aclmask(Oid config_oid, Oid roleid,
+ AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /*
+ * Get the configuration parameter's ACL from pg_config_param
+ */
+ tuple = SearchSysCache1(CONFIGOID, ObjectIdGetDatum(config_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("configuration parameter with OID %u does not exist",
+ config_oid)));
+
+ ownerId = ((Form_pg_config_param) GETSTRUCT(tuple))->configowner;
+
+ aclDatum = SysCacheGetAttr(CONFIGOID, tuple, Anum_pg_config_param_cfgacl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_CONFIG_PARAM, ownerId);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, ownerId, 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 +4948,18 @@ 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 database
+ */
+AclResult
+pg_config_param_aclcheck(Oid config_oid, Oid roleid, AclMode mode)
+{
+ if (pg_config_param_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
*/
@@ -5283,6 +5530,33 @@ pg_collation_ownercheck(Oid coll_oid, Oid roleid)
return has_privs_of_role(roleid, ownerId);
}
+/*
+ * Ownership check for a collation (specified by OID).
+ */
+bool
+pg_config_param_ownercheck(Oid config_oid, Oid roleid)
+{
+ HeapTuple tuple;
+ Oid ownerId;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return true;
+
+ tuple = SearchSysCache1(CONFIGOID, ObjectIdGetDatum(config_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("configuration parameter with OID %u does not exist",
+ config_oid)));
+
+ ownerId = ((Form_pg_config_param) GETSTRUCT(tuple))->configowner;
+
+ ReleaseSysCache(tuple);
+
+ return has_privs_of_role(roleid, ownerId);
+}
+
/*
* Ownership check for a conversion (specified by OID).
*/
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index aa7d4d5456..578bac485c 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -29,6 +29,7 @@
#include "catalog/namespace.h"
#include "catalog/pg_auth_members.h"
#include "catalog/pg_authid.h"
+#include "catalog/pg_config_param.h"
#include "catalog/pg_database.h"
#include "catalog/pg_db_role_setting.h"
#include "catalog/pg_largeobject.h"
@@ -246,6 +247,7 @@ IsSharedRelation(Oid relationId)
/* These are the shared catalogs (look for BKI_SHARED_RELATION) */
if (relationId == AuthIdRelationId ||
relationId == AuthMemRelationId ||
+ relationId == ConfigParamRelationId ||
relationId == DatabaseRelationId ||
relationId == SharedDescriptionRelationId ||
relationId == SharedDependRelationId ||
@@ -260,6 +262,8 @@ IsSharedRelation(Oid relationId)
relationId == AuthIdOidIndexId ||
relationId == AuthMemRoleMemIndexId ||
relationId == AuthMemMemRoleIndexId ||
+ relationId == ConfigParamNameIndexId ||
+ relationId == ConfigParamOidIndexId ||
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 == PgConfigParamToastTable ||
+ relationId == PgConfigParamToastIndex ||
relationId == PgDatabaseToastTable ||
relationId == PgDatabaseToastIndex ||
relationId == PgDbRoleSettingToastTable ||
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index fe9c714257..65cf4fbd0b 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -30,6 +30,7 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_config_param.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
@@ -150,6 +151,7 @@ static const Oid object_classes[] = {
TypeRelationId, /* OCLASS_TYPE */
CastRelationId, /* OCLASS_CAST */
CollationRelationId, /* OCLASS_COLLATION */
+ ConfigParamRelationId, /* OCLASS_CONFIG_PARAM */
ConstraintRelationId, /* OCLASS_CONSTRAINT */
ConversionRelationId, /* OCLASS_CONVERSION */
AttrDefaultRelationId, /* OCLASS_DEFAULT */
@@ -1504,6 +1506,7 @@ doDeletion(const ObjectAddress *object, int flags)
/*
* These global object types are not supported here.
*/
+ case OCLASS_CONFIG_PARAM:
case OCLASS_ROLE:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
@@ -2778,6 +2781,9 @@ getObjectClass(const ObjectAddress *object)
case CollationRelationId:
return OCLASS_COLLATION;
+ case ConfigParamRelationId:
+ return OCLASS_CONFIG_PARAM;
+
case ConstraintRelationId:
return OCLASS_CONSTRAINT;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 2bae3fbb17..a4d4f3b3c7 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -29,6 +29,7 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_config_param.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
@@ -2309,6 +2310,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_COLUMN:
case OBJECT_ATTRIBUTE:
case OBJECT_COLLATION:
+ case OBJECT_CONFIG_PARAM:
case OBJECT_CONVERSION:
case OBJECT_STATISTIC_EXT:
case OBJECT_TSPARSER:
@@ -2496,6 +2498,11 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
NameListToString(castNode(List, object)));
break;
+ case OBJECT_CONFIG_PARAM:
+ if (!pg_config_param_ownercheck(address.objectId, roleid))
+ aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
+ NameListToString(castNode(List, object)));
+ break;
case OBJECT_CONVERSION:
if (!pg_conversion_ownercheck(address.objectId, roleid))
aclcheck_error(ACLCHECK_NOT_OWNER, objtype,
@@ -3510,6 +3517,22 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
break;
}
+ case OCLASS_CONFIG_PARAM:
+ {
+ char *configname;
+
+ configname = get_cfgparam_name(object->objectId);
+ if (!configname)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for configuration parameter %u",
+ object->objectId);
+ break;
+ }
+ appendStringInfo(&buffer, _("configuration parameter %s"), configname);
+ break;
+ }
+
case OCLASS_SCHEMA:
{
char *nspname;
@@ -4473,6 +4496,10 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "collation");
break;
+ case OCLASS_CONFIG_PARAM:
+ appendStringInfoString(&buffer, "configuration parameter");
+ break;
+
case OCLASS_CONSTRAINT:
getConstraintTypeDescription(&buffer, object->objectId,
missing_ok);
@@ -4977,6 +5004,28 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_CONFIG_PARAM:
+ {
+ HeapTuple configTup;
+ Form_pg_config_param configForm;
+
+ configTup = SearchSysCache1(CONFIGOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(configTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for configuration parameter %u",
+ object->objectId);
+ break;
+ }
+ configForm = (Form_pg_config_param) GETSTRUCT(configTup);
+ appendStringInfoString(&buffer, NameStr(configForm->configname));
+ if (objname)
+ *objname = list_make1(pstrdup(NameStr(configForm->configname)));
+ ReleaseSysCache(configTup);
+ break;
+ }
+
case OCLASS_CONVERSION:
{
HeapTuple conTup;
diff --git a/src/backend/catalog/pg_config_param.c b/src/backend/catalog/pg_config_param.c
new file mode 100644
index 0000000000..9589f1aa41
--- /dev/null
+++ b/src/backend/catalog/pg_config_param.c
@@ -0,0 +1,143 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_config_param.c
+ * routines to support manipulation of the pg_config_param 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_config_param.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/sysattr.h"
+#include "access/table.h"
+#include "access/tableam.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_config_param.h"
+#include "catalog/pg_namespace.h"
+#include "mb/pg_wchar.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/pg_locale.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * ConfigParamCreate
+ *
+ * Add a new tuple to pg_config_param. It is the caller's responsibility to
+ * check that a GUC variable of the given name exists, that the name is the
+ * canonical name of the variable rather than an alias, and that the name will
+ * fit within a NameData. (Passing an alias and if_not_exists will work to
+ * look up the Oid, but callers should not rely on that, as the alias could
+ * accidentally be inserted.)
+ *
+ * configname: the configuration parameter name to create.
+ * configowner: oid of role which will own this configuration parameter.
+ * if_not_exists: if true, don't fail on duplicate name, but rather return
+ * the existing entry's Oid.
+ */
+Oid
+ConfigParamCreate(const char *configname,
+ Oid configowner,
+ bool if_not_exists)
+{
+ Relation rel;
+ TupleDesc tupDesc;
+ HeapTuple tuple;
+ Datum values[Natts_pg_config_param];
+ bool nulls[Natts_pg_config_param];
+ Oid configParamId;
+
+ /*
+ * Check whether the configuration parameter (by the given name or alias)
+ * already exists.
+ */
+ configParamId = get_cfgparam_oid(configname, true);
+ if (OidIsValid(configParamId))
+ {
+ if (if_not_exists)
+ return configParamId;
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("configuration parameter \"%s\" already exists",
+ configname)));
+ }
+
+ /*
+ * 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(ConfigParamRelationId, RowExclusiveLock);
+ tupDesc = RelationGetDescr(rel);
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ values[Anum_pg_config_param_configname - 1] =
+ DirectFunctionCall1(namein, CStringGetDatum(configname));
+ configParamId = GetNewOidWithIndex(rel,
+ ConfigParamOidIndexId,
+ Anum_pg_config_param_oid);
+ values[Anum_pg_config_param_oid - 1] = ObjectIdGetDatum(configParamId);
+ values[Anum_pg_config_param_configowner - 1] = ObjectIdGetDatum(configowner);
+ nulls[Anum_pg_config_param_cfgacl - 1] = true;
+ tuple = heap_form_tuple(tupDesc, values, nulls);
+ CatalogTupleInsert(rel, tuple);
+
+ /* Post creation hook for new configuration parameter */
+ InvokeObjectPostCreateHook(ConfigParamRelationId, configParamId, 0);
+
+ /*
+ * Close pg_config_param, but keep lock till commit.
+ */
+ heap_freetuple(tuple);
+ table_close(rel, NoLock);
+
+ return configParamId;
+}
+
+void
+ConfigParamDrop(const char *configname, bool missing_ok)
+{
+ Oid configParamId;
+ Relation rel;
+ TableScanDesc scan;
+ ScanKeyData keys[1];
+ HeapTuple tup;
+ int numkeys = 0;
+
+ configParamId = get_cfgparam_oid(configname, missing_ok);
+ if (!OidIsValid(configParamId))
+ return;
+
+ rel = table_open(ConfigParamRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&keys[numkeys],
+ Anum_pg_config_param_oid,
+ BTEqualStrategyNumber,
+ F_OIDEQ,
+ ObjectIdGetDatum(configParamId));
+ numkeys++;
+
+ scan = table_beginscan_catalog(rel, numkeys, keys);
+ while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
+ {
+ CatalogTupleDelete(rel, &tup->t_self);
+ }
+ table_endscan(scan);
+
+ table_close(rel, RowExclusiveLock);
+}
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index e8504f0ae4..3f41ecc4fc 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -21,6 +21,7 @@ OBJS = \
cluster.o \
collationcmds.o \
comment.o \
+ configcmds.o \
constraint.o \
conversioncmds.o \
copy.o \
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 40044070cf..1f8c3f78c2 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -639,6 +639,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
break;
case OCLASS_CAST:
+ case OCLASS_CONFIG_PARAM:
case OCLASS_CONSTRAINT:
case OCLASS_DEFAULT:
case OCLASS_LANGUAGE:
diff --git a/src/backend/commands/configcmds.c b/src/backend/commands/configcmds.c
new file mode 100644
index 0000000000..ae9e4c75fc
--- /dev/null
+++ b/src/backend/commands/configcmds.c
@@ -0,0 +1,80 @@
+/*-------------------------------------------------------------------------
+ *
+ * configcmds.c
+ * Commands for manipulating configuration parameters.
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/commands/configcmds.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/table.h"
+#include "access/xact.h"
+#include "catalog/binary_upgrade.h"
+#include "catalog/catalog.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_auth_members.h"
+#include "catalog/pg_authid.h"
+#include "catalog/pg_config_param.h"
+#include "catalog/pg_database.h"
+#include "commands/comment.h"
+#include "commands/dbcommands.h"
+#include "commands/defrem.h"
+#include "commands/seclabel.h"
+#include "commands/configcmds.h"
+#include "libpq/crypt.h"
+#include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/syscache.h"
+#include "utils/timestamp.h"
+
+/*
+ * CREATE CONFIGURATION PARAMETER
+ */
+Oid
+CreateConfigParam(CreateConfigParamStmt *stmt)
+{
+ /*
+ * Check some permissions first.
+ *
+ * There are currently no callers except extensions which register their
+ * custom variables.
+ */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to create configuration parameters")));
+
+ /*
+ * For now the owner cannot be specified on create. This command is only
+ * useful for extension authors.
+ */
+ return ConfigParamCreate(stmt->cfgparam_name, BOOTSTRAP_SUPERUSERID,
+ stmt->if_not_exists);
+}
+
+/*
+ * DROP CONFIGURATION PARAMETER
+ */
+void
+DropConfigParam(DropConfigParamStmt *stmt)
+{
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to drop configuration parameter")));
+
+ ConfigParamDrop(stmt->cfgparam_name, stmt->missing_ok);
+}
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 4e545adf95..cd98976e07 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -276,6 +276,13 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
name = NameListToString(castNode(List, object));
}
break;
+ case OBJECT_CONFIG_PARAM:
+ if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
+ {
+ msg = gettext_noop("configuration parameter \"%s\" does not exist, skipping");
+ name = NameListToString(castNode(List, object));
+ }
+ break;
case OBJECT_CONVERSION:
if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
{
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index df264329d8..419a7a4fad 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_CONFIG_PARAM:
case OBJECT_DATABASE:
case OBJECT_TABLESPACE:
case OBJECT_ROLE:
@@ -1012,6 +1013,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
{
switch (objclass)
{
+ case OCLASS_CONFIG_PARAM:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
@@ -2078,6 +2080,8 @@ stringify_grant_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMN";
+ case OBJECT_CONFIG_PARAM:
+ return "CONFIGURATION PARAMETER";
case OBJECT_TABLE:
return "TABLE";
case OBJECT_SEQUENCE:
@@ -2161,6 +2165,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMNS";
+ case OBJECT_CONFIG_PARAM:
+ return "CONFIGURATION PARAMETERS";
case OBJECT_TABLE:
return "TABLES";
case OBJECT_SEQUENCE:
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 53c18628a7..6a0fe325cf 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_CONFIG_PARAM:
case OBJECT_CONVERSION:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 857cc5ce6e..7b6c93d8a1 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -12262,6 +12262,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_TYPE:
case OCLASS_CAST:
case OCLASS_COLLATION:
+ case OCLASS_CONFIG_PARAM:
case OCLASS_CONVERSION:
case OCLASS_LANGUAGE:
case OCLASS_LARGEOBJECT:
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index ad1ea2ff2f..3a53a4c14d 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -4604,6 +4604,28 @@ _copyCreateSchemaStmt(const CreateSchemaStmt *from)
return newnode;
}
+static CreateConfigParamStmt *
+_copyCreateConfigParamStmt(const CreateConfigParamStmt *from)
+{
+ CreateConfigParamStmt *newnode = makeNode(CreateConfigParamStmt);
+
+ COPY_NODE_FIELD(cfgparam_name);
+ COPY_SCALAR_FIELD(if_not_exists);
+
+ return newnode;
+}
+
+static DropConfigParamStmt *
+_copyDropConfigParamStmt(const DropConfigParamStmt *from)
+{
+ DropConfigParamStmt *newnode = makeNode(DropConfigParamStmt);
+
+ COPY_NODE_FIELD(cfgparam_name);
+ COPY_SCALAR_FIELD(missing_ok);
+
+ return newnode;
+}
+
static CreateConversionStmt *
_copyCreateConversionStmt(const CreateConversionStmt *from)
{
@@ -5700,6 +5722,12 @@ copyObjectImpl(const void *from)
case T_CreateSchemaStmt:
retval = _copyCreateSchemaStmt(from);
break;
+ case T_CreateConfigParamStmt:
+ retval = _copyCreateConfigParamStmt(from);
+ break;
+ case T_DropConfigParamStmt:
+ retval = _copyDropConfigParamStmt(from);
+ break;
case T_CreateConversionStmt:
retval = _copyCreateConversionStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index f537d3eb96..37ff9b2c9e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2203,6 +2203,24 @@ _equalCreateSchemaStmt(const CreateSchemaStmt *a, const CreateSchemaStmt *b)
return true;
}
+static bool
+_equalCreateConfigParamStmt(const CreateConfigParamStmt *a, const CreateConfigParamStmt *b)
+{
+ COMPARE_NODE_FIELD(cfgparam_name);
+ COMPARE_SCALAR_FIELD(if_not_exists);
+
+ return true;
+}
+
+static bool
+_equalDropConfigParamStmt(const DropConfigParamStmt *a, const DropConfigParamStmt *b)
+{
+ COMPARE_NODE_FIELD(cfgparam_name);
+ COMPARE_SCALAR_FIELD(missing_ok);
+
+ return true;
+}
+
static bool
_equalCreateConversionStmt(const CreateConversionStmt *a, const CreateConversionStmt *b)
{
@@ -3705,6 +3723,12 @@ equal(const void *a, const void *b)
case T_CreateSchemaStmt:
retval = _equalCreateSchemaStmt(a, b);
break;
+ case T_CreateConfigParamStmt:
+ retval = _equalCreateConfigParamStmt(a, b);
+ break;
+ case T_DropConfigParamStmt:
+ retval = _equalDropConfigParamStmt(a, b);
+ break;
case T_CreateConversionStmt:
retval = _equalCreateConversionStmt(a, b);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a6d0cefa6b..e22e4a9959 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -299,7 +299,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
SecLabelStmt SelectStmt TransactionStmt TransactionStmtLegacy TruncateStmt
UnlistenStmt UpdateStmt VacuumStmt
VariableResetStmt VariableSetStmt VariableShowStmt
- ViewStmt CheckPointStmt CreateConversionStmt
+ ViewStmt CheckPointStmt CreateConfigParamStmt DropConfigParamStmt CreateConversionStmt
DeallocateStmt PrepareStmt ExecuteStmt
DropOwnedStmt ReassignOwnedStmt
AlterTSConfigurationStmt AlterTSDictionaryStmt
@@ -348,8 +348,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 cfgparam_name
+%type <list> OptSchemaEltList cfgparam_target
%type <chr> am_type
@@ -387,8 +387,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> iso_level opt_encoding
%type <rolespec> grantee
%type <list> grantee_list
-%type <accesspriv> privilege
-%type <list> privileges privilege_list
+%type <accesspriv> privilege guc_priv
+%type <list> privileges privilege_list guc_priv_list
%type <privtarget> privilege_target
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
@@ -698,7 +698,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
@@ -951,6 +951,8 @@ stmt:
| CreateAsStmt
| CreateAssertionStmt
| CreateCastStmt
+ | CreateConfigParamStmt
+ | DropConfigParamStmt
| CreateConversionStmt
| CreateDomainStmt
| CreateExtensionStmt
@@ -6884,6 +6886,31 @@ GrantStmt: GRANT privileges ON privilege_target TO grantee_list
n->grantor = $8;
$$ = (Node*)n;
}
+ | GRANT guc_priv_list ON cfgparam_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_CONFIG_PARAM;
+ n->objects = $4;
+ n->grantees = $6;
+ n->grant_option = $7;
+ n->grantor = $8;
+ $$ = (Node*)n;
+ }
+ ;
+
+cfgparam_target:
+ cfgparam_name
+ {
+ $$ = list_make1(makeString($1));
+ }
+ | cfgparam_target ',' cfgparam_name
+ {
+ $$ = lappend($1, makeString($3));
+ }
;
RevokeStmt:
@@ -6917,6 +6944,36 @@ RevokeStmt:
n->behavior = $11;
$$ = (Node *)n;
}
+ | REVOKE guc_priv_list ON cfgparam_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_CONFIG_PARAM;
+ n->objects = $4;
+ n->grantees = $6;
+ n->grantor = $7;
+ n->behavior = $8;
+ $$ = (Node *)n;
+ }
+ | REVOKE GRANT OPTION FOR guc_priv_list ON cfgparam_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_CONFIG_PARAM;
+ n->objects = $7;
+ n->grantees = $9;
+ n->grantor = $10;
+ n->behavior = $11;
+ $$ = (Node *)n;
+ }
;
@@ -6985,6 +7042,27 @@ privilege: SELECT opt_column_list
}
;
+guc_priv_list: guc_priv { $$ = list_make1($1); }
+ | guc_priv_list ',' guc_priv { $$ = lappend($1, $3); }
+ ;
+
+guc_priv:
+ ALTER SYSTEM_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("alter system");
+ n->cols = NIL;
+ $$ = n;
+ }
+ | SET VALUE_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("set");
+ n->cols = NIL;
+ $$ = n;
+ }
+ ;
+
/* Don't bother trying to fold the first two rules into one using
* opt_table. You're going to get conflicts.
@@ -10658,6 +10736,52 @@ any_with: WITH
;
+/*****************************************************************************
+ *
+ * CONFIGURATION PARAMETER
+ *
+ *****************************************************************************/
+
+CreateConfigParamStmt:
+ CREATE CONFIGURATION PARAMETER opt_if_not_exists Sconst
+ {
+ CreateConfigParamStmt *n = makeNode(CreateConfigParamStmt);
+ n->if_not_exists = $4;
+ n->cfgparam_name = $5;
+ $$ = (Node *)n;
+ }
+
+ ;
+
+DropConfigParamStmt:
+ DROP CONFIGURATION PARAMETER opt_if_exists Sconst
+ {
+ DropConfigParamStmt *n = makeNode(DropConfigParamStmt);
+ n->missing_ok = $4;
+ n->cfgparam_name = $5;
+ $$ = (Node *)n;
+ }
+ ;
+
+cfgparam_name:
+ ColId
+ {
+ $$ = $1;
+ }
+ | cfgparam_name '.' ColId
+ {
+ char *str = psprintf("%s.%s", $1, $3);
+
+ if (strlen(str) >= NAMEDATALEN)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("configuration parameter too long"),
+ parser_errposition(@1)));
+
+ $$ = str;
+ }
+ ;
+
/*****************************************************************************
*
* Manipulate a conversion
@@ -15728,6 +15852,7 @@ unreserved_keyword:
| OWNED
| OWNER
| PARALLEL
+ | PARAMETER
| PARSER
| PARTIAL
| PARTITION
@@ -16306,6 +16431,7 @@ bare_label_keyword:
| OWNED
| OWNER
| PARALLEL
+ | PARAMETER
| PARSER
| PARTIAL
| PARTITION
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 1fbc387d47..832e6ddc31 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -32,6 +32,7 @@
#include "commands/cluster.h"
#include "commands/collationcmds.h"
#include "commands/comment.h"
+#include "commands/configcmds.h"
#include "commands/conversioncmds.h"
#include "commands/copy.h"
#include "commands/createas.h"
@@ -170,6 +171,8 @@ ClassifyUtilityCommandAsReadOnly(Node *parsetree)
case T_CompositeTypeStmt:
case T_CreateAmStmt:
case T_CreateCastStmt:
+ case T_CreateConfigParamStmt:
+ case T_DropConfigParamStmt:
case T_CreateConversionStmt:
case T_CreateDomainStmt:
case T_CreateEnumStmt:
@@ -896,6 +899,19 @@ standard_ProcessUtility(PlannedStmt *pstmt,
AlterEventTrigger((AlterEventTrigStmt *) parsetree);
break;
+ /*
+ * CONFIGURATION PARAMETER statements
+ */
+ case T_CreateConfigParamStmt:
+ /* no event triggers for global objects */
+ CreateConfigParam((CreateConfigParamStmt *) parsetree);
+ break;
+
+ case T_DropConfigParamStmt:
+ /* no event triggers for global objects */
+ DropConfigParam((DropConfigParamStmt *) parsetree);
+ break;
+
/*
* ******************************** ROLE statements ****
*/
@@ -2974,6 +2990,14 @@ CreateCommandTag(Node *parsetree)
tag = CMDTAG_REINDEX;
break;
+ case T_CreateConfigParamStmt:
+ tag = CMDTAG_CREATE_CONFIG_PARAM;
+ break;
+
+ case T_DropConfigParamStmt:
+ tag = CMDTAG_DROP_CONFIG_PARAM;
+ break;
+
case T_CreateConversionStmt:
tag = CMDTAG_CREATE_CONVERSION;
break;
@@ -3590,6 +3614,14 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_ALL; /* should this be DDL? */
break;
+ case T_CreateConfigParamStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
+ case T_DropConfigParamStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_CreateConversionStmt:
lev = LOGSTMT_DDL;
break;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 67f8b29434..595bd95230 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -22,6 +22,7 @@
#include "catalog/pg_auth_members.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_class.h"
+#include "catalog/pg_config_param.h"
#include "catalog/pg_database.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.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"
@@ -306,6 +308,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 +802,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_CONFIG_PARAM:
+ world_default = ACL_USAGE;
+ owner_default = ACL_ALL_RIGHTS_CONFIG_PARAM;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -1602,6 +1614,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 +1714,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;
@@ -4670,6 +4690,44 @@ initialize_acl(void)
}
}
+/*
+ * get_cfgparam_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_cfgparam_oid(const char *cfgname, bool missing_ok)
+{
+ Oid oid;
+
+ /* Check for the variable by the name we were given */
+ oid = GetSysCacheOid1(CONFIGNAME, Anum_pg_config_param_oid,
+ CStringGetDatum(cfgname));
+ if (!OidIsValid(oid))
+ {
+ const char *canonical;
+
+ /* Check if the variable has a different canonical spelling */
+ canonical = GetConfigOptionCanonicalName(cfgname);
+ if (canonical != NULL)
+ oid = GetSysCacheOid1(CONFIGNAME, Anum_pg_config_param_oid,
+ CStringGetDatum(canonical));
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("configuration parameter \"%s\" does not exist",
+ cfgname)));
+ }
+
+ return oid;
+}
+
/*
* RoleMembershipCacheCallback
* Syscache inval callback function
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 4ebaa552a2..d0b927418a 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -25,6 +25,7 @@
#include "catalog/pg_amproc.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_config_param.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
@@ -3304,6 +3305,25 @@ free_attstatsslot(AttStatsSlot *sslot)
pfree(sslot->numbers_arr);
}
+char *
+get_cfgparam_name(Oid configid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(CONFIGOID, ObjectIdGetDatum(configid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_config_param configtup = (Form_pg_config_param) GETSTRUCT(tp);
+ char *result;
+
+ result = pstrdup(NameStr(configtup->configname));
+ 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 56870b46e4..3d2b2ff531 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -30,6 +30,7 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_config_param.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
@@ -310,6 +311,28 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {ConfigParamRelationId, /* CONFIGNAME */
+ ConfigParamNameIndexId,
+ 1,
+ {
+ Anum_pg_config_param_configname,
+ 0,
+ 0,
+ 0
+ },
+ 256
+ },
+ {ConfigParamRelationId, /* CONFIGOID */
+ ConfigParamOidIndexId,
+ 1,
+ {
+ Anum_pg_config_param_oid,
+ 0,
+ 0,
+ 0
+ },
+ 256
+ },
{ConversionRelationId, /* CONDEFAULT */
ConversionDefaultIndexId,
4,
diff --git a/src/backend/utils/misc/README b/src/backend/utils/misc/README
index 6e294386f7..7ad2a780d9 100644
--- a/src/backend/utils/misc/README
+++ b/src/backend/utils/misc/README
@@ -252,6 +252,17 @@ actual variable, and are not directly aware of the additional values
maintained by GUC.
+Per-Variable Grantable Privileges
+---------------------------------
+
+For each GUC variable, if the name of the variable is entered into
+catalog/pg_config_param.dat, or alternatively if CREATE CONFIGURATION PARAMETER
+is run to add a catalog entry for the variable, then permission to SET or ALTER
+SYSTEM on the guc can be assigned to roles. Otherwise, these commands can
+still be run by users or superusers as normal, but grants on the variable
+cannot be performed.
+
+
GUC Memory Handling
-------------------
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index e91d5a3cfd..73c5808930 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -8533,16 +8533,32 @@ 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;
+ Oid cfgId;
+
+ cfgId = get_cfgparam_oid(name, true);
+ if (!OidIsValid(cfgId))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+
+ aclresult = pg_config_param_aclcheck(cfgId, GetUserId(),
+ ACL_ALTER_SYSTEM);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_CONFIG_PARAM, name);
+ }
+
switch (altersysstmt->setstmt->kind)
{
case VAR_SET_VALUE:
@@ -8735,6 +8751,9 @@ void
ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
{
GucAction action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET;
+ GucContext context;
+ AclResult aclresult;
+ Oid cfgId;
/*
* Workers synchronize these parameters at the start of the parallel
@@ -8745,6 +8764,20 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
errmsg("cannot set parameters during a parallel operation")));
+ /*
+ * 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(cfgId = get_cfgparam_oid(stmt->name, true)))
+ {
+ aclresult = pg_config_param_aclcheck(cfgId, GetUserId(), ACL_SET);
+ if (aclresult == ACLCHECK_OK)
+ context = PGC_SUSET;
+ }
+
switch (stmt->kind)
{
case VAR_SET_VALUE:
@@ -8753,7 +8786,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;
@@ -8838,7 +8871,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;
@@ -9578,6 +9611,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, false, 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,
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 7e98371d25..eae9ba27c2 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -15043,6 +15043,9 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
case DEFACLOBJ_NAMESPACE:
type = "SCHEMAS";
break;
+ case DEFACLOBJ_CONFIG_PARAM:
+ type = "CONFIGURATION PARAMETERS";
+ break;
default:
/* shouldn't get here */
fatal("unrecognized object type in default privileges: %d",
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 3eca295ff4..ab92782d15 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -92,6 +92,7 @@ typedef enum ObjectClass
OCLASS_TYPE, /* pg_type */
OCLASS_CAST, /* pg_cast */
OCLASS_COLLATION, /* pg_collation */
+ OCLASS_CONFIG_PARAM, /* pg_config_param */
OCLASS_CONSTRAINT, /* pg_constraint */
OCLASS_CONVERSION, /* pg_conversion */
OCLASS_DEFAULT, /* pg_attrdef */
diff --git a/src/include/catalog/pg_config_param.dat b/src/include/catalog/pg_config_param.dat
new file mode 100644
index 0000000000..5285cf54d3
--- /dev/null
+++ b/src/include/catalog/pg_config_param.dat
@@ -0,0 +1,211 @@
+{ configname => 'DateStyle' },
+{ configname => 'IntervalStyle' },
+{ configname => 'TimeZone' },
+{ configname => 'allow_system_table_mods' },
+{ configname => 'application_name' },
+{ configname => 'archive_timeout' },
+{ configname => 'authentication_timeout' },
+{ configname => 'autovacuum_naptime' },
+{ configname => 'autovacuum_vacuum_cost_delay' },
+{ configname => 'autovacuum_work_mem' },
+{ configname => 'backend_flush_after' },
+{ configname => 'backtrace_functions' },
+{ configname => 'bgwriter_delay' },
+{ configname => 'bgwriter_flush_after' },
+{ configname => 'block_size' },
+{ configname => 'checkpoint_flush_after' },
+{ configname => 'checkpoint_timeout' },
+{ configname => 'checkpoint_warning' },
+{ configname => 'client_connection_check_interval' },
+{ configname => 'client_encoding' },
+{ configname => 'cluster_name' },
+{ configname => 'config_file' },
+{ configname => 'constraint_exclusion' },
+{ configname => 'cpu_index_tuple_cost' },
+{ configname => 'cpu_operator_cost' },
+{ configname => 'cpu_tuple_cost' },
+{ configname => 'cursor_tuple_fraction' },
+{ configname => 'data_checksums' },
+{ configname => 'data_directory' },
+{ configname => 'data_directory_mode' },
+{ configname => 'deadlock_timeout' },
+{ configname => 'debug_assertions' },
+{ configname => 'debug_deadlocks' },
+{ configname => 'debug_discard_caches' },
+{ configname => 'default_table_access_method' },
+{ configname => 'default_tablespace' },
+{ configname => 'default_transaction_read_only' },
+{ configname => 'default_with_oids' },
+{ configname => 'dynamic_library_path' },
+{ configname => 'effective_cache_size' },
+{ configname => 'effective_io_concurrency' },
+{ configname => 'enable_async_append' },
+{ configname => 'enable_bitmapscan' },
+{ configname => 'enable_gathermerge' },
+{ configname => 'enable_hashagg' },
+{ configname => 'enable_hashjoin' },
+{ configname => 'enable_indexonlyscan' },
+{ configname => 'enable_indexscan' },
+{ configname => 'enable_material' },
+{ configname => 'enable_memoize' },
+{ configname => 'enable_mergejoin' },
+{ configname => 'enable_nestloop' },
+{ configname => 'enable_parallel_append' },
+{ configname => 'enable_parallel_hash' },
+{ configname => 'enable_partition_pruning' },
+{ configname => 'enable_partitionwise_aggregate' },
+{ configname => 'enable_partitionwise_join' },
+{ configname => 'enable_seqscan' },
+{ configname => 'enable_sort' },
+{ configname => 'enable_tidscan' },
+{ configname => 'external_pid_file' },
+{ configname => 'force_parallel_mode' },
+{ configname => 'from_collapse_limit' },
+{ configname => 'geqo' },
+{ configname => 'geqo_effort' },
+{ configname => 'geqo_generations' },
+{ configname => 'geqo_pool_size' },
+{ configname => 'geqo_seed' },
+{ configname => 'geqo_selection_bias' },
+{ configname => 'geqo_threshold' },
+{ configname => 'gin_pending_list_limit' },
+{ configname => 'hash_mem_multiplier' },
+{ configname => 'huge_page_size' },
+{ configname => 'idle_in_transaction_session_timeout' },
+{ configname => 'idle_session_timeout' },
+{ configname => 'ignore_checksum_failure' },
+{ configname => 'ignore_invalid_pages' },
+{ configname => 'ignore_system_indexes' },
+{ configname => 'in_hot_standby' },
+{ configname => 'integer_datetimes' },
+{ configname => 'is_superuser' },
+{ configname => 'jit' },
+{ configname => 'jit_above_cost' },
+{ configname => 'jit_debugging_support' },
+{ configname => 'jit_dump_bitcode' },
+{ configname => 'jit_expressions' },
+{ configname => 'jit_inline_above_cost' },
+{ configname => 'jit_optimize_above_cost' },
+{ configname => 'jit_profiling_support' },
+{ configname => 'jit_provider' },
+{ configname => 'jit_tuple_deforming' },
+{ configname => 'join_collapse_limit' },
+{ configname => 'krb_server_keyfile' },
+{ configname => 'lc_collate' },
+{ configname => 'lc_ctype' },
+{ configname => 'listen_addresses' },
+{ configname => 'local_preload_libraries' },
+{ configname => 'lock_timeout' },
+{ configname => 'log_autovacuum_min_duration' },
+{ configname => 'log_btree_build_stats' },
+{ configname => 'log_directory' },
+{ configname => 'log_filename' },
+{ configname => 'log_min_duration_sample' },
+{ configname => 'log_min_duration_statement' },
+{ configname => 'log_parameter_max_length' },
+{ configname => 'log_parameter_max_length_on_error' },
+{ configname => 'log_rotation_age' },
+{ configname => 'log_rotation_size' },
+{ configname => 'log_startup_progress_interval' },
+{ configname => 'log_temp_files' },
+{ configname => 'logical_decoding_work_mem' },
+{ configname => 'maintenance_io_concurrency' },
+{ configname => 'maintenance_work_mem' },
+{ configname => 'max_function_args' },
+{ configname => 'max_identifier_length' },
+{ configname => 'max_index_keys' },
+{ configname => 'max_parallel_workers' },
+{ configname => 'max_parallel_workers_per_gather' },
+{ configname => 'max_slot_wal_keep_size' },
+{ configname => 'max_stack_depth' },
+{ configname => 'max_standby_archive_delay' },
+{ configname => 'max_standby_streaming_delay' },
+{ configname => 'max_wal_size' },
+{ configname => 'min_dynamic_shared_memory' },
+{ configname => 'min_parallel_index_scan_size' },
+{ configname => 'min_parallel_table_scan_size' },
+{ configname => 'min_wal_size' },
+{ configname => 'old_snapshot_threshold' },
+{ configname => 'optimize_bounded_sort' },
+{ configname => 'parallel_leader_participation' },
+{ configname => 'parallel_setup_cost' },
+{ configname => 'parallel_tuple_cost' },
+{ configname => 'plan_cache_mode' },
+{ configname => 'plperl.on_init' },
+{ configname => 'plperl.on_plperl_init' },
+{ configname => 'plperl.on_plperlu_init' },
+{ configname => 'plperl.use_strict' },
+{ configname => 'plpgsql.check_asserts' },
+{ configname => 'plpgsql.extra_errors' },
+{ configname => 'plpgsql.extra_warnings' },
+{ configname => 'plpgsql.print_strict_params' },
+{ configname => 'plpgsql.variable_conflict' },
+{ configname => 'pltcl.start_proc' },
+{ configname => 'pltclu.start_proc' },
+{ configname => 'post_auth_delay' },
+{ configname => 'pre_auth_delay' },
+{ configname => 'primary_conninfo' },
+{ configname => 'random_page_cost' },
+{ configname => 'recovery_min_apply_delay' },
+{ configname => 'remove_temp_files_after_crash' },
+{ configname => 'role' },
+{ configname => 'search_path' },
+{ configname => 'seed' },
+{ configname => 'segment_size' },
+{ configname => 'seq_page_cost' },
+{ configname => 'server_encoding' },
+{ configname => 'server_version' },
+{ configname => 'server_version_num' },
+{ configname => 'session_authorization' },
+{ configname => 'session_preload_libraries' },
+{ configname => 'shared_buffers' },
+{ configname => 'shared_memory_size' },
+{ configname => 'shared_memory_size_in_huge_pages' },
+{ configname => 'shared_preload_libraries' },
+{ configname => 'ssl_ciphers' },
+{ configname => 'ssl_dh_params_file' },
+{ configname => 'ssl_ecdh_curve' },
+{ configname => 'ssl_library' },
+{ configname => 'ssl_max_protocol_version' },
+{ configname => 'ssl_min_protocol_version' },
+{ configname => 'ssl_passphrase_command' },
+{ configname => 'ssl_renegotiation_limit' },
+{ configname => 'standard_conforming_strings' },
+{ configname => 'statement_timeout' },
+{ configname => 'stats_temp_directory' },
+{ configname => 'synchronous_standby_names' },
+{ configname => 'tcp_keepalives_idle' },
+{ configname => 'tcp_keepalives_interval' },
+{ configname => 'tcp_user_timeout' },
+{ configname => 'temp_buffers' },
+{ configname => 'temp_file_limit' },
+{ configname => 'temp_tablespaces' },
+{ configname => 'trace_lock_oidmin' },
+{ configname => 'trace_lock_table' },
+{ configname => 'trace_locks' },
+{ configname => 'trace_lwlocks' },
+{ configname => 'trace_notify' },
+{ configname => 'trace_sort' },
+{ configname => 'trace_syncscan' },
+{ configname => 'trace_userlocks' },
+{ configname => 'track_activity_query_size' },
+{ configname => 'transaction_deferrable' },
+{ configname => 'transaction_isolation' },
+{ configname => 'transaction_read_only' },
+{ configname => 'unix_socket_directories' },
+{ configname => 'vacuum_cost_delay' },
+{ configname => 'wal_block_size' },
+{ configname => 'wal_buffers' },
+{ configname => 'wal_consistency_checking' },
+{ configname => 'wal_debug' },
+{ configname => 'wal_keep_size' },
+{ configname => 'wal_receiver_status_interval' },
+{ configname => 'wal_receiver_timeout' },
+{ configname => 'wal_retrieve_retry_interval' },
+{ configname => 'wal_segment_size' },
+{ configname => 'wal_sender_timeout' },
+{ configname => 'wal_skip_threshold' },
+{ configname => 'wal_writer_delay' },
+{ configname => 'wal_writer_flush_after' },
+{ configname => 'work_mem' },
+{ configname => 'zero_damaged_pages' },
diff --git a/src/include/catalog/pg_config_param.h b/src/include/catalog/pg_config_param.h
new file mode 100644
index 0000000000..d8e640bb77
--- /dev/null
+++ b/src/include/catalog/pg_config_param.h
@@ -0,0 +1,61 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_config_param.h
+ * definition of the "configuration parameter" system catalog
+ * (pg_config_param).
+ *
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_config_param.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_CONFIG_PARAM_H
+#define PG_CONFIG_PARAM_H
+
+#include "catalog/genbki.h"
+#include "catalog/pg_config_param_d.h"
+
+/* ----------------
+ * pg_config_param definition. cpp turns this into
+ * typedef struct FormData_pg_config_param
+ * ----------------
+ */
+CATALOG(pg_config_param,8923,ConfigParamRelationId) BKI_SHARED_RELATION
+{
+ Oid oid; /* oid */
+ NameData configname; /* configuration parameter name */
+ Oid configowner BKI_DEFAULT(POSTGRES) BKI_LOOKUP(pg_authid);
+
+#ifdef CATALOG_VARLEN /* variable-length fields start here */
+ /* Access privileges */
+ aclitem cfgacl[1] BKI_DEFAULT(_null_);
+#endif
+} FormData_pg_config_param;
+
+
+/* ----------------
+ * Form_pg_config_param corresponds to a pointer to a tuple with
+ * the format of pg_config_param relation.
+ * ----------------
+ */
+typedef FormData_pg_config_param *Form_pg_config_param;
+
+DECLARE_TOAST(pg_config_param, 8924, 8925);
+#define PgConfigParamToastTable 8924
+#define PgConfigParamToastIndex 8925
+
+DECLARE_UNIQUE_INDEX(pg_cfgparam_name_index, 8926, ConfigParamNameIndexId, on pg_config_param using btree(configname name_ops));
+DECLARE_UNIQUE_INDEX_PKEY(pg_config_param_oid_index, 8927, ConfigParamOidIndexId, on pg_config_param using btree(oid oid_ops));
+
+extern Oid ConfigParamCreate(const char *configname, Oid configowner,
+ bool if_not_exists);
+extern void ConfigParamDrop(const char *configname, bool missing_ok);
+
+#endif /* PG_CONFIG_PARAM_H */
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index eb72dd6293..4ca71b2258 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_CONFIG_PARAM 'c' /* configuration parameter */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/commands/configcmds.h b/src/include/commands/configcmds.h
new file mode 100644
index 0000000000..4c9b80a4dc
--- /dev/null
+++ b/src/include/commands/configcmds.h
@@ -0,0 +1,17 @@
+/*-------------------------------------------------------------------------
+ *
+ * configcmds.h
+ * Commands for manipulating configuration parameters (GUC variables).
+ *
+ *
+ * src/include/commands/configcmds.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CONFIGCMDS_H
+#define CONFIGCMDS_H
+
+extern Oid CreateConfigParam(CreateConfigParamStmt *stmt);
+extern void DropConfigParam(DropConfigParamStmt *stmt);
+
+#endif /* CONFIGCMDS_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 7c657c1241..3d19dfb285 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -371,6 +371,8 @@ typedef enum NodeTag
T_AlterDatabaseStmt,
T_AlterDatabaseSetStmt,
T_AlterRoleSetStmt,
+ T_CreateConfigParamStmt,
+ T_DropConfigParamStmt,
T_CreateConversionStmt,
T_CreateCastStmt,
T_CreateOpClassStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 067138e6b5..5d282623ff 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -92,7 +92,9 @@ 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 N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */
+#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
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
@@ -1794,6 +1796,7 @@ typedef enum ObjectType
OBJECT_CAST,
OBJECT_COLUMN,
OBJECT_COLLATION,
+ OBJECT_CONFIG_PARAM,
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DEFAULT,
@@ -3499,6 +3502,28 @@ typedef struct ReindexStmt
List *params; /* list of DefElem nodes */
} ReindexStmt;
+/* ----------------------
+ * CREATE CONFIGURATION PARAMETER Statement
+ * ----------------------
+ */
+typedef struct CreateConfigParamStmt
+{
+ NodeTag type;
+ char *cfgparam_name; /* Name of the configuration parameter */
+ bool if_not_exists; /* just do nothing if it already exists? */
+} CreateConfigParamStmt;
+
+/* ----------------------
+ * DROP CONFIGURATION PARAMETER Statement
+ * ----------------------
+ */
+typedef struct DropConfigParamStmt
+{
+ NodeTag type;
+ char *cfgparam_name; /* Name of the configuration parameter */
+ bool missing_ok; /* skip error if a configuration parameter is missing? */
+} DropConfigParamStmt;
+
/* ----------------------
* CREATE CONVERSION Statement
* ----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f836acf876..527e723b39 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -307,6 +307,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/tcop/cmdtaglist.h b/src/include/tcop/cmdtaglist.h
index 9ba24d4ca9..203b916fec 100644
--- a/src/include/tcop/cmdtaglist.h
+++ b/src/include/tcop/cmdtaglist.h
@@ -86,6 +86,7 @@ PG_CMDTAG(CMDTAG_CREATE_ACCESS_METHOD, "CREATE ACCESS METHOD", true, false, fals
PG_CMDTAG(CMDTAG_CREATE_AGGREGATE, "CREATE AGGREGATE", true, false, false)
PG_CMDTAG(CMDTAG_CREATE_CAST, "CREATE CAST", true, false, false)
PG_CMDTAG(CMDTAG_CREATE_COLLATION, "CREATE COLLATION", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_CONFIG_PARAM, "CREATE CONFIGURATION PARAMETER", false, false, false)
PG_CMDTAG(CMDTAG_CREATE_CONSTRAINT, "CREATE CONSTRAINT", true, false, false)
PG_CMDTAG(CMDTAG_CREATE_CONVERSION, "CREATE CONVERSION", true, false, false)
PG_CMDTAG(CMDTAG_CREATE_DATABASE, "CREATE DATABASE", false, false, false)
@@ -138,6 +139,7 @@ PG_CMDTAG(CMDTAG_DROP_ACCESS_METHOD, "DROP ACCESS METHOD", true, false, false)
PG_CMDTAG(CMDTAG_DROP_AGGREGATE, "DROP AGGREGATE", true, false, false)
PG_CMDTAG(CMDTAG_DROP_CAST, "DROP CAST", true, false, false)
PG_CMDTAG(CMDTAG_DROP_COLLATION, "DROP COLLATION", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_CONFIG_PARAM, "DROP CONFIGURATION PARAMETER", false, false, false)
PG_CMDTAG(CMDTAG_DROP_CONSTRAINT, "DROP CONSTRAINT", true, false, false)
PG_CMDTAG(CMDTAG_DROP_CONVERSION, "DROP CONVERSION", true, false, false)
PG_CMDTAG(CMDTAG_DROP_DATABASE, "DROP DATABASE", false, false, false)
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index af771c901d..8e88fd0672 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -146,9 +146,11 @@ typedef struct ArrayType Acl;
#define ACL_CREATE_CHR 'C'
#define ACL_CREATE_TEMP_CHR 'T'
#define ACL_CONNECT_CHR 'c'
+#define ACL_SET_CHR 's'
+#define ACL_ALTER_SYSTEM_CHR 'A'
/* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTc"
+#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcsA"
/*
* Bitmasks defining "all rights" for each supported object type
@@ -165,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_CONFIG_PARAM (ACL_SET|ACL_ALTER_SYSTEM)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -223,6 +226,8 @@ extern void select_best_grantor(Oid roleId, AclMode privileges,
extern void initialize_acl(void);
+extern Oid get_cfgparam_oid(const char *cfgname, bool missing_ok);
+
/*
* prototypes for functions in aclchk.c
*/
@@ -243,6 +248,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_config_param_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,
@@ -271,6 +278,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_config_param_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,
@@ -306,6 +314,7 @@ extern bool pg_opclass_ownercheck(Oid opc_oid, Oid roleid);
extern bool pg_opfamily_ownercheck(Oid opf_oid, Oid roleid);
extern bool pg_database_ownercheck(Oid db_oid, Oid roleid);
extern bool pg_collation_ownercheck(Oid coll_oid, Oid roleid);
+extern bool pg_config_param_ownercheck(Oid config_oid, Oid roleid);
extern bool pg_conversion_ownercheck(Oid conv_oid, Oid roleid);
extern bool pg_ts_dict_ownercheck(Oid dict_oid, Oid roleid);
extern bool pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid);
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index aa18d304ac..c35e552bb0 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -379,6 +379,7 @@ extern int set_config_option(const char *name, const char *value,
GucAction action, bool changeVal, int elevel,
bool is_reload);
extern void AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt);
+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 77871aaefc..33c21f7102 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_cfgparam_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 c8cfbc30f6..14d803a6dc 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -48,6 +48,8 @@ enum SysCacheIdentifier
CLAOID,
COLLNAMEENCNSP,
COLLOID,
+ CONFIGNAME,
+ CONFIGOID,
CONDEFAULT,
CONNAMENSP,
CONSTROID,
diff --git a/src/test/modules/test_extensions/Makefile b/src/test/modules/test_extensions/Makefile
index 77ee4d5d9e..e1e47b29ab 100644
--- a/src/test/modules/test_extensions/Makefile
+++ b/src/test/modules/test_extensions/Makefile
@@ -4,11 +4,12 @@ MODULE = test_extensions
PGFILEDESC = "test_extensions - regression testing for EXTENSION support"
EXTENSION = test_ext1 test_ext2 test_ext3 test_ext4 test_ext5 test_ext6 \
- test_ext7 test_ext8 test_ext_cyclic1 test_ext_cyclic2 \
+ test_ext7 test_ext8 test_ext9 test_ext_cyclic1 test_ext_cyclic2 \
test_ext_evttrig
DATA = test_ext1--1.0.sql test_ext2--1.0.sql test_ext3--1.0.sql \
test_ext4--1.0.sql test_ext5--1.0.sql test_ext6--1.0.sql \
test_ext7--1.0.sql test_ext7--1.0--2.0.sql test_ext8--1.0.sql \
+ test_ext9--1.0.sql test_ext9--1.0--1.1.sql \
test_ext_cyclic1--1.0.sql test_ext_cyclic2--1.0.sql \
test_ext_evttrig--1.0.sql test_ext_evttrig--1.0--2.0.sql
diff --git a/src/test/modules/test_extensions/test_ext9--1.0--1.1.sql b/src/test/modules/test_extensions/test_ext9--1.0--1.1.sql
new file mode 100644
index 0000000000..3aa1295108
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext9--1.0--1.1.sql
@@ -0,0 +1,12 @@
+/* src/test/modules/test_extensions/test_ext8--1.0--1.1.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_ext9" to load this file. \quit
+
+-- create some custom extenion configuration parameters
+drop configuration parameter if exists 'test_ext9_param1';
+create configuration parameter if not exists 'test_ext9_param2';
+drop configuration parameter if exists 'test_ext9_param2';
+create configuration parameter if not exists 'test_ext9_param2';
+
+-- leave configuration parameter test_ext9_param2
diff --git a/src/test/modules/test_extensions/test_ext9--1.0.sql b/src/test/modules/test_extensions/test_ext9--1.0.sql
new file mode 100644
index 0000000000..87537ae4f8
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext9--1.0.sql
@@ -0,0 +1,12 @@
+/* src/test/modules/test_extensions/test_ext8--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_ext9" to load this file. \quit
+
+-- create some custom extenion configuration parameters
+create configuration parameter if not exists 'test_ext9_param1';
+create configuration parameter if not exists 'test_ext9_param2';
+drop configuration parameter if exists 'test_ext9_param2';
+drop configuration parameter if exists 'test_ext9_param3';
+
+-- leave configuration parameter test_ext9_param1
diff --git a/src/test/modules/test_extensions/test_ext9.control b/src/test/modules/test_extensions/test_ext9.control
new file mode 100644
index 0000000000..3482a220cc
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext9.control
@@ -0,0 +1,4 @@
+comment = 'Test extension 9'
+default_version = '1.1'
+schema = 'public'
+relocatable = false
diff --git a/src/test/regress/expected/guc_privs.out b/src/test/regress/expected/guc_privs.out
new file mode 100644
index 0000000000..9100de10ab
--- /dev/null
+++ b/src/test/regress/expected/guc_privs.out
@@ -0,0 +1,144 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform all 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 = 'en_US.UTF-8'; -- ok
+RESET lc_messages; -- ok
+ALTER SYSTEM SET lc_messages = 'en_US.UTF-8'; -- 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;
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_admin;
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Perform all operations as user 'regress_host_resource_admin' --
+SET SESSION AUTHORIZATION regress_host_resource_admin;
+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
+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
+RESET TimeZone; -- ok
+RESET SESSION AUTHORIZATION;
+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 configuration parameter autovacuum_work_mem
+privileges for configuration parameter hash_mem_multiplier
+privileges for configuration parameter logical_decoding_work_mem
+privileges for configuration parameter maintenance_work_mem
+privileges for configuration parameter max_stack_depth
+privileges for configuration parameter min_dynamic_shared_memory
+privileges for configuration parameter shared_buffers
+privileges for configuration parameter temp_buffers
+privileges for configuration parameter temp_file_limit
+privileges for configuration parameter work_mem
+REVOKE SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+FROM regress_host_resource_admin;
+DROP ROLE regress_host_resource_admin; -- ok
+-- Create non-superuser with privileges to configure plgsql custom variables
+CREATE ROLE regress_plpgsql_admin NOSUPERUSER;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin;
+SET plpgsql.extra_errors TO 'all';
+SET plpgsql.extra_warnings TO 'all';
+RESET plpgsql.extra_errors;
+RESET plpgsql.extra_warnings;
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all';
+ERROR: permission denied to set parameter "plpgsql.extra_errors"
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all';
+ERROR: permission denied to set parameter "plpgsql.extra_warnings"
+ALTER SYSTEM RESET plpgsql.extra_errors;
+ERROR: permission denied to set parameter "plpgsql.extra_errors"
+ALTER SYSTEM RESET plpgsql.extra_warnings;
+ERROR: permission denied to set parameter "plpgsql.extra_warnings"
+RESET SESSION AUTHORIZATION;
+GRANT SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+TO regress_plpgsql_admin;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin; -- ok
+SET plpgsql.extra_errors TO 'all'; -- ok
+SET plpgsql.extra_warnings TO 'all'; -- ok
+RESET plpgsql.extra_errors; -- ok
+RESET plpgsql.extra_warnings; -- ok
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all'; -- ok
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all'; -- ok
+ALTER SYSTEM RESET plpgsql.extra_errors; -- ok
+ALTER SYSTEM RESET plpgsql.extra_warnings; -- ok
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_plpgsql_admin; -- fail, privileges remain
+ERROR: role "regress_plpgsql_admin" cannot be dropped because some objects depend on it
+DETAIL: privileges for configuration parameter plpgsql.extra_errors
+privileges for configuration parameter plpgsql.extra_warnings
+REVOKE SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+FROM regress_plpgsql_admin;
+DROP ROLE regress_plpgsql_admin; -- ok
diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out
index 215eb899be..050d8b4f62 100644
--- a/src/test/regress/expected/oidjoins.out
+++ b/src/test/regress/expected/oidjoins.out
@@ -84,6 +84,7 @@ NOTICE: checking pg_class {reltoastrelid} => pg_class {oid}
NOTICE: checking pg_class {relrewrite} => pg_class {oid}
NOTICE: checking pg_attrdef {adrelid} => pg_class {oid}
NOTICE: checking pg_attrdef {adrelid,adnum} => pg_attribute {attrelid,attnum}
+NOTICE: checking pg_config_param {configowner} => pg_authid {oid}
NOTICE: checking pg_constraint {connamespace} => pg_namespace {oid}
NOTICE: checking pg_constraint {conrelid} => pg_class {oid}
NOTICE: checking pg_constraint {contypid} => pg_type {oid}
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 9b91865dcc..8574f6bc32 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -31,6 +31,24 @@ CREATE USER regress_priv_user6;
CREATE USER regress_priv_user7;
GRANT pg_read_all_data TO regress_priv_user6;
GRANT pg_write_all_data TO regress_priv_user7;
+GRANT SET VALUE ON enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON enable_nestloop TO regress_priv_user6;
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+GRANT ALTER SYSTEM ON enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_param TO regress_priv_user7; -- does not exist
+ERROR: configuration parameter "no_such_param" does not exist
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
ALTER GROUP regress_priv_group1 ADD USER regress_priv_user4;
@@ -2326,10 +2344,28 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+ERROR: role "regress_priv_user6" cannot be dropped because some objects depend on it
+DETAIL: privileges for configuration parameter enable_memoize
+privileges for configuration parameter enable_nestloop
+DROP USER regress_priv_user7; -- privileges remain
+ERROR: role "regress_priv_user7" cannot be dropped because some objects depend on it
+DETAIL: privileges for configuration parameter enable_seqscan
+privileges for configuration parameter maintenance_work_mem
+privileges for configuration parameter work_mem
+privileges for table persons2
DROP USER regress_priv_user8; -- does not exist
ERROR: role "regress_priv_user8" does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON no_such_param FROM regress_priv_user7; -- does not exist
+ERROR: configuration parameter "no_such_param" does not exist
+DROP USER regress_priv_user7; -- ok
+REVOKE SET VALUE ON enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON enable_nestloop FROM regress_priv_user6;
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
CREATE TABLE lock_table (a int);
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index d04dc66db9..308c25daff 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -113,6 +113,7 @@ pg_authid|t
pg_cast|t
pg_class|t
pg_collation|t
+pg_config_param|t
pg_constraint|t
pg_conversion|t
pg_database|t
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 017e962fed..b45a203eb6 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -86,7 +86,7 @@ test: brin_bloom brin_multi
# ----------
# Another group of parallel tests
# ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort guc_privs
# rules cannot run concurrently with any test that creates
# a view or rule in the public schema
diff --git a/src/test/regress/sql/guc_privs.sql b/src/test/regress/sql/guc_privs.sql
new file mode 100644
index 0000000000..0456517274
--- /dev/null
+++ b/src/test/regress/sql/guc_privs.sql
@@ -0,0 +1,106 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform all 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 = 'en_US.UTF-8'; -- ok
+RESET lc_messages; -- ok
+ALTER SYSTEM SET lc_messages = 'en_US.UTF-8'; -- 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;
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_admin;
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Perform all operations as user 'regress_host_resource_admin' --
+SET SESSION AUTHORIZATION regress_host_resource_admin;
+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
+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
+RESET TimeZone; -- ok
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_host_resource_admin; -- fail, privileges remain
+REVOKE SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+FROM regress_host_resource_admin;
+DROP ROLE regress_host_resource_admin; -- ok
+-- Create non-superuser with privileges to configure plgsql custom variables
+CREATE ROLE regress_plpgsql_admin NOSUPERUSER;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin;
+SET plpgsql.extra_errors TO 'all';
+SET plpgsql.extra_warnings TO 'all';
+RESET plpgsql.extra_errors;
+RESET plpgsql.extra_warnings;
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all';
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all';
+ALTER SYSTEM RESET plpgsql.extra_errors;
+ALTER SYSTEM RESET plpgsql.extra_warnings;
+RESET SESSION AUTHORIZATION;
+GRANT SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+TO regress_plpgsql_admin;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin; -- ok
+SET plpgsql.extra_errors TO 'all'; -- ok
+SET plpgsql.extra_warnings TO 'all'; -- ok
+RESET plpgsql.extra_errors; -- ok
+RESET plpgsql.extra_warnings; -- ok
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all'; -- ok
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all'; -- ok
+ALTER SYSTEM RESET plpgsql.extra_errors; -- ok
+ALTER SYSTEM RESET plpgsql.extra_warnings; -- ok
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_plpgsql_admin; -- fail, privileges remain
+REVOKE SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+FROM regress_plpgsql_admin;
+DROP ROLE regress_plpgsql_admin; -- ok
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index 6353a1cb8c..11f41032f2 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -36,6 +36,27 @@ CREATE USER regress_priv_user7;
GRANT pg_read_all_data TO regress_priv_user6;
GRANT pg_write_all_data TO regress_priv_user7;
+GRANT SET VALUE ON enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON enable_nestloop TO regress_priv_user6;
+
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+
+GRANT ALTER SYSTEM ON enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_param TO regress_priv_user7; -- does not exist
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
+
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
@@ -1389,10 +1410,22 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+DROP USER regress_priv_user7; -- privileges remain
DROP USER regress_priv_user8; -- does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON no_such_param FROM regress_priv_user7; -- does not exist
+
+DROP USER regress_priv_user7; -- ok
+
+REVOKE SET VALUE ON enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON enable_nestloop FROM regress_priv_user6;
+
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index da6ac8ed83..a1ee568b61 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -459,6 +459,7 @@ CoverExt
CoverPos
CreateAmStmt
CreateCastStmt
+CreateConfigParamStmt
CreateConversionStmt
CreateDomainStmt
CreateEnumStmt
--
2.21.1 (Apple Git-122.3)
On Mon, Nov 15, 2021 at 3:37 PM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:
One disadvantage of this approach is that the GUC variables are represented both in the list of C structures in guc.c and in the new system catalog pg_config_param's .dat file. Failure to enter a GUC in the .dat file will result in the inability to grant privileges on the GUC, at least unless/until you run CREATE CONFIGURATION PARAMETER on the GUC. (This is, in fact, how extension scripts deal with the issue.) It would perhaps be better if the list of GUCs were not duplicated, but I wasn't clever enough to find a clean way to do that without greatly expanding the patch (nor did I complete prototyping any such thing.)
I think this imposes an unnecessary burden on developers. It seems
like it would be easy to write some code that lives inside guc.c and
runs during bootstrap, and it could just iterate over each
ConfigureNamesWhatever array and insert catalog entries for everything
it finds.
It's also going to be important to think about what happens with
extension GUCs. If somebody installs an extension, we can't ask them
to perform a manual step in order to be able to grant privileges. And
if somebody then loads up a different .so for that extension, the set
of GUCs that it provides can change without any DDL being executed.
New GUCs could appear, and old GUCs could vanish.
So I wonder if we should instead not do what I proposed above, and
instead just adjust the GRANT command to automatically insert a new
row into the relevant catalog if there isn't one already. That seems
nicer for extensions, and also nicer for core GUCs, since it avoids
bloating the catalog with a bunch of entries that aren't needed.
--
Robert Haas
EDB: http://www.enterprisedb.com
Robert Haas <robertmhaas@gmail.com> writes:
It's also going to be important to think about what happens with
extension GUCs. If somebody installs an extension, we can't ask them
to perform a manual step in order to be able to grant privileges. And
if somebody then loads up a different .so for that extension, the set
of GUCs that it provides can change without any DDL being executed.
New GUCs could appear, and old GUCs could vanish.
Right. I think that any design that involves per-GUC catalog entries
is going to be an abysmal failure, precisely because the set of GUCs
is not stable enough. So I'm suspicious of this entire proposal.
Maybe there's a way to make it work, but that way isn't how.
regards, tom lane
On Tue, Nov 16, 2021 at 10:17 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Right. I think that any design that involves per-GUC catalog entries
is going to be an abysmal failure, precisely because the set of GUCs
is not stable enough.
In practice it's pretty stable. I think it's just a matter of having a
plan that covers the cases where it isn't stable reasonably elegantly.
We already embed GUC names in catalog entries when someone runs ALTER
USER SET or ALTER DATABASE SET, so this proposal doesn't seem to be
moving the goalposts in any meaningful way.
--
Robert Haas
EDB: http://www.enterprisedb.com
On Nov 16, 2021, at 7:03 AM, Robert Haas <robertmhaas@gmail.com> wrote:
It's also going to be important to think about what happens with
extension GUCs. If somebody installs an extension, we can't ask them
to perform a manual step in order to be able to grant privileges.
The burden isn't on the installer of an extension. As implemented, it's the extension's installation .sql file that sets it up, and the upgrade .sql files that make adjustments, if necessary.
And
if somebody then loads up a different .so for that extension, the set
of GUCs that it provides can change without any DDL being executed.
New GUCs could appear, and old GUCs could vanish.
Well, the same is true for functions, right? If you add, remove, or redefine functions in the extension, you need an upgrade script that defines the new functions, removes the old ones, changes function signatures, or whatever. The same is true here for GUCs.
I don't think we support using a .so that is mismatched against the version of the extension that is installed.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
On Tue, Nov 16, 2021 at 10:17 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Right. I think that any design that involves per-GUC catalog entries
is going to be an abysmal failure, precisely because the set of GUCs
is not stable enough.
In practice it's pretty stable. I think it's just a matter of having a
plan that covers the cases where it isn't stable reasonably elegantly.
Um. Really the point comes down to having sane default behavior
when there's no entry, which ought to eliminate any need to do the
sort of "run over all the entries at startup" processing that you
seemed to be proposing. So I guess I don't understand why such a
thing would be needed.
We already embed GUC names in catalog entries when someone runs ALTER
USER SET or ALTER DATABASE SET, so this proposal doesn't seem to be
moving the goalposts in any meaningful way.
True; as long as the expectation is that entries will exist for only
a tiny subset of GUCs, it's probably fine.
regards, tom lane
Mark Dilger <mark.dilger@enterprisedb.com> writes:
I don't think we support using a .so that is mismatched against the
version of the extension that is installed.
You are entirely mistaken. That's not only "supported", it's *required*.
Consider binary upgrades, where the SQL objects are transferred as-is
but the receiving installation may have a different (hopefully newer)
version of the .so. That .so has to cope with the older SQL object
definitions; it doesn't get to assume that the upgrade script has been
run yet.
regards, tom lane
On Nov 16, 2021, at 7:32 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Mark Dilger <mark.dilger@enterprisedb.com> writes:
I don't think we support using a .so that is mismatched against the
version of the extension that is installed.You are entirely mistaken. That's not only "supported", it's *required*.
Consider binary upgrades, where the SQL objects are transferred as-is
but the receiving installation may have a different (hopefully newer)
version of the .so. That .so has to cope with the older SQL object
definitions; it doesn't get to assume that the upgrade script has been
run yet.
You are talking about mismatches in the other direction, aren't you? I was responding to Robert's point that new gucs could appear, and old gucs disappear. That seems analogous to new functions appearing and old functions disappearing. If you upgrade (not downgrade) the .so, the new gucs and functions will be in the .so, but won't be callable/grantable from sql until the upgrade script runs. The old gucs and functions will be missing from the .so, and attempts to call them/grant them from sql before the upgrade will fail. What am I missing here?
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Nov 16, 2021, at 7:28 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
True; as long as the expectation is that entries will exist for only
a tiny subset of GUCs, it's probably fine.
I understand that bloating a frequently used catalog can be pretty harmful to performance. I wasn't aware that the size of an infrequently used catalog was critical. This new catalog would be used during GRANT SET ... and GRANT ALTER SYSTEM commands, which should be rare, and potentially consulted when SET or ALTER SYSTEM commands are issued. Is there a more substantial performance impact to this than I'm aware? It can be a bit challenging to run performance tests on such things, given the way everything interacts with everything else.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Tue, Nov 16, 2021 at 10:45 AM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:
You are talking about mismatches in the other direction, aren't you? I was responding to Robert's point that new gucs could appear, and old gucs disappear. That seems analogous to new functions appearing and old functions disappearing. If you upgrade (not downgrade) the .so, the new gucs and functions will be in the .so, but won't be callable/grantable from sql until the upgrade script runs. The old gucs and functions will be missing from the .so, and attempts to call them/grant them from sql before the upgrade will fail. What am I missing here?
It's true that we could impose such a restriction, but I don't think
we should. If I install a different .so, I want the new GUCs to be
grantable immediately, without running any separate DDL.
I also don't think we should burden extension authors with putting
stuff in their upgrade scripts for this. We should solve the problem
in our code rather than forcing them to do so in theirs.
--
Robert Haas
EDB: http://www.enterprisedb.com
Mark Dilger <mark.dilger@enterprisedb.com> writes:
You are talking about mismatches in the other direction, aren't you? I was responding to Robert's point that new gucs could appear, and old gucs disappear. That seems analogous to new functions appearing and old functions disappearing. If you upgrade (not downgrade) the .so, the new gucs and functions will be in the .so, but won't be callable/grantable from sql until the upgrade script runs.
True, but in general users might not care about getting access to new
features (or at least, they might not care until much later, at which
point they'd bother to run ALTER EXTENSION UPDATE).
The old gucs and functions will be missing from the .so, and attempts to call them/grant them from sql before the upgrade will fail. What am I missing here?
Here, you are describing behavior that's against project policy and would
be rejected immediately if anyone submitted a patch that made an extension
do that. Newer .so versions are expected to run successfully, not fail,
with an older version of their SQL objects. Were this not so, it's almost
inevitable that a binary upgrade would fail, because the extension is
likely to be called into action somewhere before there is any opportunity
to issue ALTER EXTENSION UPDATE. Even in-place updates of extensions
would become problematic: you can't assume that a rollout of new
executables will be instantly followed by ALTER EXTENSION UPDATE.
Basically, I think the proposed new system catalog is both unworkable
and entirely unnecessary. Maybe it would have been okay if Peter had
designed GUCs like that a couple of decades ago, but at this point
we have a ton of infrastructure --- much of it not under the core
project's control --- that assumes that GUCs can be created with just
a bit of code. I do not think that this feature is worth the cost of
changing that. Or IOW: ALTER SYSTEM SET has gotten along fine without
such a catalog; why can't this feature?
I also notice that the patch seems to intend to introduce tracking
of which user "owns" a GUC, which I think is even more unworkable.
They are all going to wind up owned by the bootstrap superuser
(extension GUCs too), so why bother?
regards, tom lane
Mark Dilger <mark.dilger@enterprisedb.com> writes:
On Nov 16, 2021, at 7:28 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
True; as long as the expectation is that entries will exist for only
a tiny subset of GUCs, it's probably fine.
I understand that bloating a frequently used catalog can be pretty
harmful to performance. I wasn't aware that the size of an infrequently
used catalog was critical.
My concern is not about performance, it's about the difficulty of
maintaining a catalog that expects to be a more-or-less exhaustive
list of GUCs. I think you need to get rid of that expectation.
In the analogy to ALTER DATABASE/USER SET, we don't expect that
pg_db_role_setting catalog entries will exist for all, or even
very many, GUCs. Also, the fact that pg_db_role_setting entries
aren't tied very hard to the actual existence of a GUC is a good
thing from the maintenance and upgrade standpoint.
BTW, if we did create such a catalog, there would need to be
pg_dump and pg_upgrade support for its entries, and you'd have to
think about (e.g.) whether pg_upgrade would attempt to preserve
the same OIDs. I don't see any indication that the patch has
addressed that infrastructure ... which is probably just as well,
since it's work that I'd be wanting to reject. (Hm, but actually,
doesn't pg_dump need work anyway to dump this new type of GRANT?)
regards, tom lane
On Nov 16, 2021, at 8:44 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
My concern is not about performance, it's about the difficulty of
maintaining a catalog that expects to be a more-or-less exhaustive
list of GUCs. I think you need to get rid of that expectation.
I'm preparing a new version of the patch that has the catalog empty to begin with, and only adds values in response to GRANT commands. That also handles the issues of extension upgrades, which I think the old patch handled better than the discussion on this thread suggested, but no matter. The new behavior will allow granting privileges on non-existent gucs, just as ALTER ROLE..SET allows registering settings on non-existent gucs.
The reason I had resisted allowing grants of privileges on non-existent gucs is that you can get the following sort of behavior (note the last two lines):
DROP USER regress_priv_user7;
ERROR: role "regress_priv_user7" cannot be dropped because some objects depend on it
DETAIL: privileges for table persons2
privileges for configuration parameter sort_mem
privileges for configuration parameter no_such_param
Rejecting "no_such_param" in the original GRANT statement seemed cleaner to me, but this discussion suggests pretty strongly that I can't do it that way. Changing the historical "sort_mem" to the canonical "work_mem" name also seems better to me, as otherwise you could have different grants on the same GUC under different names. I'm inclined to keep the canonicalization of names where known, but maybe that runs afoul the rule that these grants should not be tied very hard to the GUC?
In the analogy to ALTER DATABASE/USER SET, we don't expect that
pg_db_role_setting catalog entries will exist for all, or even
very many, GUCs. Also, the fact that pg_db_role_setting entries
aren't tied very hard to the actual existence of a GUC is a good
thing from the maintenance and upgrade standpoint.
Doing it that way....
BTW, if we did create such a catalog, there would need to be
pg_dump and pg_upgrade support for its entries, and you'd have to
think about (e.g.) whether pg_upgrade would attempt to preserve
the same OIDs. I don't see any indication that the patch has
addressed that infrastructure ... which is probably just as well,
since it's work that I'd be wanting to reject.
Yeah, that's why I didn't write it. I wanted feedback on the basic implementation before doing that work.
(Hm, but actually,
doesn't pg_dump need work anyway to dump this new type of GRANT?)
Yes, if the idea of this kind of grant isn't being outright rejected, then I'll need to write that.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 11/16/21 14:48, Mark Dilger wrote:
On Nov 16, 2021, at 8:44 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
My concern is not about performance, it's about the difficulty of
maintaining a catalog that expects to be a more-or-less exhaustive
list of GUCs. I think you need to get rid of that expectation.I'm preparing a new version of the patch that has the catalog empty to begin with, and only adds values in response to GRANT commands. That also handles the issues of extension upgrades, which I think the old patch handled better than the discussion on this thread suggested, but no matter. The new behavior will allow granting privileges on non-existent gucs, just as ALTER ROLE..SET allows registering settings on non-existent gucs.
Your original and fairly simple set of patches used hardcoded role names
and sets of GUCs they could update via ALTER SYSTEM. I suggested to you
privately that a more flexible approach would be to drive this from a
catalog table. I had in mind a table of more or less <roleid, guc_name>.
You could prepopulate it with the roles / GUCs from your original patch
set. I don't think it needs to be initially empty. But DBAs would be
able to modify and extend the settings. I agree with Tom that we
shouldn't try to cover all GUCs in the table - any GUC without an entry
can only be updated by a superuser.
To support pg_dump and pg_upgrade, it might be better to have an
enabled/disabled flag rather than to delete rows.
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
On Nov 16, 2021, at 12:38 PM, Andrew Dunstan <andrew@dunslane.net> wrote:
Your original and fairly simple set of patches used hardcoded role names
and sets of GUCs they could update via ALTER SYSTEM. I suggested to you
privately that a more flexible approach would be to drive this from a
catalog table. I had in mind a table of more or less <roleid, guc_name>.
Right. I prefer a table of <guc_name, acl> matching the structure of most of the rest of the grantable objects, so that entries from pg_depend or pg_shdepend can track the dependencies in the usual way and prevent dangling references when DROP ROLE is executed. Is there a reason to prefer an ad hoc tables structure?
You could prepopulate it with the roles / GUCs from your original patch
set. I don't think it needs to be initially empty. But DBAs would be
able to modify and extend the settings. I agree with Tom that we
shouldn't try to cover all GUCs in the table - any GUC without an entry
can only be updated by a superuser.
The patch already posted on this thread already works that way. Robert and Tom seemed to infer that all gucs need to be present in the catalog, but in fact, not entering them in the catalog simply means that they aren't grantable. I think the confusion arose from the fact that I created a mechanism for extension authors to enter additional gucs into the catalog, which gave the false impression that such entries were required. They are not. I didn't bother explaining that before, because Robert and Tom both seem to expect all gucs to be grantable, an expectation you do not appear to share.
To support pg_dump and pg_upgrade, it might be better to have an
enabled/disabled flag rather than to delete rows.
I'm not really sure what this means. Are you suggesting that the <guc_name, acl> (or in your formulation <roleid, guc_name>) should have a "bool enabled" field, and when the guc gets dropped, the "enabled" field gets set to false?
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Mark Dilger <mark.dilger@enterprisedb.com> writes:
The patch already posted on this thread already works that way. Robert and Tom seemed to infer that all gucs need to be present in the catalog, but in fact, not entering them in the catalog simply means that they aren't grantable. I think the confusion arose from the fact that I created a mechanism for extension authors to enter additional gucs into the catalog, which gave the false impression that such entries were required. They are not. I didn't bother explaining that before, because Robert and Tom both seem to expect all gucs to be grantable, an expectation you do not appear to share.
The question is why you need pg_config_param at all, then.
AFAICS it just adds maintenance complexity we could do without.
I think we'd be better off with a catalog modeled on the design of
pg_db_role_setting, which would have entries for roles and lists
of GUC names that those roles could set.
BTW, another objection to pg_config_param as designed here is that
a "name" is not an appropriate way to store possibly-qualified
custom GUC names. It's not long enough (cf. valid_custom_variable_name).
To support pg_dump and pg_upgrade, it might be better to have an
enabled/disabled flag rather than to delete rows.
I'm not really sure what this means.
I didn't see the point of this either. We really need to KISS here.
Every bit of added complexity in the catalog representation is another
opportunity for bugs-of-omission, not to mention a detail that you
have to provide mechanisms to dump and restore.
regards, tom lane
On Nov 16, 2021, at 2:12 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
The question is why you need pg_config_param at all, then.
AFAICS it just adds maintenance complexity we could do without.
I think we'd be better off with a catalog modeled on the design of
pg_db_role_setting, which would have entries for roles and lists
of GUC names that those roles could set.
Originally, I was trying to have dependency linkage between two proper types of objects, so that DROP ROLE and DROP CONFIGURATION PARAMETER would behave as expected, complaining about privileges remaining rather than dropping an object and leaving a dangling reference.
I've deleted the whole CREATE CONFIGURATION PARAMETER and DROP CONFIGURATION PARAMETER syntax and implementation, but it still seems odd to me that:
CREATE ROLE somebody;
GRANT SELECT ON TABLE sometable TO ROLE somebody;
GRANT ALTER SYSTEM ON someguc TO ROLE somebody;
DROP ROLE somebody;
ERROR: role "somebody" cannot be dropped because some objects depend on it
DETAIL: privileges for table sometable
would not mention privileges for "someguc" as well. That's why I want configuration parameters to be proper objects with OIDs and with entries in pg_depend and/or pg_shdepend. Maybe there is some better way to do it, but that's why I've been doing this.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Nov 16, 2021, at 2:12 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
BTW, another objection to pg_config_param as designed here is that
a "name" is not an appropriate way to store possibly-qualified
custom GUC names. It's not long enough (cf. valid_custom_variable_name).
I was aware of that, but figured not all GUCs have to be grantable. If it doesn't fit in a NameData, you can't grant on it.
If we want to be more accommodating than that, we can store it as text, just like pg_db_role_names does, but then we need more code complexity to look it up and to verify that it is unique. (We wouldn't want multiple records for the same <role,guc> pair.)
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Tue, Nov 16, 2021 at 2:48 PM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:
I'm preparing a new version of the patch that has the catalog empty to begin with, and only adds values in response to GRANT commands. That also handles the issues of extension upgrades, which I think the old patch handled better than the discussion on this thread suggested, but no matter. The new behavior will allow granting privileges on non-existent gucs, just as ALTER ROLE..SET allows registering settings on non-existent gucs.
The reason I had resisted allowing grants of privileges on non-existent gucs is that you can get the following sort of behavior (note the last two lines):
DROP USER regress_priv_user7;
ERROR: role "regress_priv_user7" cannot be dropped because some objects depend on it
DETAIL: privileges for table persons2
privileges for configuration parameter sort_mem
privileges for configuration parameter no_such_paramRejecting "no_such_param" in the original GRANT statement seemed cleaner to me, but this discussion suggests pretty strongly that I can't do it that way.
I think it's perfectly fine to refuse a GRANT statement on a GUC that
doesn't exist, and I don't believe I ever said otherwise. What I don't
think is OK is to require a preparatory statement like CREATE
CONFIGURATION PARAMETER to be executed before you can grant
permissions on it. There's no reason for that. If somebody tries to
grant privileges on a GUC that does not exist, fail. If the GUC does
exist but there's no catalog entry, make one. If the GUC exists and
the catalog entry also exists, update it.
At REVOKE time, don't check whether the GUC exists - only check the
catalog. That way people can remove privileges on GUCs that don't
exist any more. If somebody issues a REVOKE and there's no catalog
entry, do nothing. If somebody issues a REVOKE and there is a catalog
entry, remove stuff from it; but if that would leave it completely
empty, instead delete it.
Whenever you create a catalog entry, also add dependency entries
pointing to it. When you delete one, remove those entries.
Changing the historical "sort_mem" to the canonical "work_mem" name also seems better to me, as otherwise you could have different grants on the same GUC under different names. I'm inclined to keep the canonicalization of names where known, but maybe that runs afoul the rule that these grants should not be tied very hard to the GUC?
No. If somebody grants privileges on an old name, record the grant
under the canonical name.
--
Robert Haas
EDB: http://www.enterprisedb.com
On Tue, Nov 16, 2021 at 3:38 PM Andrew Dunstan <andrew@dunslane.net> wrote:
Your original and fairly simple set of patches used hardcoded role names
and sets of GUCs they could update via ALTER SYSTEM. I suggested to you
privately that a more flexible approach would be to drive this from a
catalog table. I had in mind a table of more or less <roleid, guc_name>.
You could prepopulate it with the roles / GUCs from your original patch
set. I don't think it needs to be initially empty. But DBAs would be
able to modify and extend the settings. I agree with Tom that we
shouldn't try to cover all GUCs in the table - any GUC without an entry
can only be updated by a superuser.
I simply can't understand the point of this. You're basically
proposing that somebody has to execute one SQL statement to make a GUC
grantable, and then a second SQL statement to actually grant access to
it. What is the value in that? It is the same person doing both
things, and the system can work out automatically what needs to be
done.
--
Robert Haas
EDB: http://www.enterprisedb.com
On Tue, Nov 16, 2021 at 5:45 PM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:
I was aware of that, but figured not all GUCs have to be grantable. If it doesn't fit in a NameData, you can't grant on it.
Such restrictions are rather counterintuitive for users, and here it
doesn't even buy anything. Using 'text' rather than 'name' as the data
type isn't going to cost any meaningful amount of performance.
If we want to be more accommodating than that, we can store it as text, just like pg_db_role_names does, but then we need more code complexity to look it up and to verify that it is unique. (We wouldn't want multiple records for the same <role,guc> pair.)
If you're verifying that it's unique in any way other than using a
unique index, I think you're doing it wrong.
Also, maybe I'm confused here, but why isn't the schema:
gucoid
gucname
gucacl
IOW, I don't understand why this table has <role,guc> as the primary
key rather than just guc. Everywhere else, we have a single ACL array
for the all privileges on an object. Why wouldn't we do the same thing
here?
--
Robert Haas
EDB: http://www.enterprisedb.com
On 11/17/21 08:32, Robert Haas wrote:
On Tue, Nov 16, 2021 at 5:45 PM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:I was aware of that, but figured not all GUCs have to be grantable. If it doesn't fit in a NameData, you can't grant on it.
Such restrictions are rather counterintuitive for users, and here it
doesn't even buy anything. Using 'text' rather than 'name' as the data
type isn't going to cost any meaningful amount of performance.
indeed
If we want to be more accommodating than that, we can store it as text, just like pg_db_role_names does, but then we need more code complexity to look it up and to verify that it is unique. (We wouldn't want multiple records for the same <role,guc> pair.)
If you're verifying that it's unique in any way other than using a
unique index, I think you're doing it wrong.
yeah
Also, maybe I'm confused here, but why isn't the schema:
gucoid
gucname
gucaclIOW, I don't understand why this table has <role,guc> as the primary
key rather than just guc. Everywhere else, we have a single ACL array
for the all privileges on an object. Why wouldn't we do the same thing
here?
Yes, that should work, It seems like a better scheme.
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
On 11/16/21 17:12, Tom Lane wrote:
To support pg_dump and pg_upgrade, it might be better to have an
enabled/disabled flag rather than to delete rows.I'm not really sure what this means.
I didn't see the point of this either. We really need to KISS here.
Every bit of added complexity in the catalog representation is another
opportunity for bugs-of-omission, not to mention a detail that you
have to provide mechanisms to dump and restore.
Well, I was trying (perhaps not very well) to imagine how to deal with
someone modifying the permissions of one of the predefined roles. Say
pg_foo has initial permission to set bar and baz, and the DBA removes
permission to set baz. How is pg_dump going to emit the right commands
to allow a safe pg_upgrade? Maybe we should say that the permissions for
the predefined roles are immutable, so only permissions sets for user
defined roles are mutable.
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
On Nov 17, 2021, at 5:32 AM, Robert Haas <robertmhaas@gmail.com> wrote:
I was aware of that, but figured not all GUCs have to be grantable. If it doesn't fit in a NameData, you can't grant on it.
Such restrictions are rather counterintuitive for users, and here it
doesn't even buy anything. Using 'text' rather than 'name' as the data
type isn't going to cost any meaningful amount of performance.
That sounds fine.
If we want to be more accommodating than that, we can store it as text, just like pg_db_role_names does, but then we need more code complexity to look it up and to verify that it is unique. (We wouldn't want multiple records for the same <role,guc> pair.)
If you're verifying that it's unique in any way other than using a
unique index, I think you're doing it wrong.
No, I'm using a unique index. I was overthinking it, concerned about changing from name_ops to text_ops and needing the toast table, but that's silly, because I need one for the acl anyway.
Also, maybe I'm confused here, but why isn't the schema:
gucoid
gucname
gucacl
It is, both in v2 already posted, and in the v3, written but not yet posted, as I haven't finished the pg_dump work, and also I'm waiting to see how this discussion gets resolved before asking for a review of v3.
IOW, I don't understand why this table has <role,guc> as the primary
key rather than just guc.
I was responding to Tom's recommendation that I follow the pattern in pg_db_role_setting, and speculating how that would work. I was not proposing to do it that way.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Nov 17, 2021, at 6:31 AM, Andrew Dunstan <andrew@dunslane.net> wrote:
Well, I was trying (perhaps not very well) to imagine how to deal with
someone modifying the permissions of one of the predefined roles. Say
pg_foo has initial permission to set bar and baz, and the DBA removes
permission to set baz. How is pg_dump going to emit the right commands
to allow a safe pg_upgrade? Maybe we should say that the permissions for
the predefined roles are immutable, so only permissions sets for user
defined roles are mutable.
I find this somewhat amusing. When you suggested off-list that I make gucs individually grantable rather than creating predefined roles with privileges, that was a great idea precisely because sites could define their own security policies using their own site-defined roles:
CREATE ROLE admin_type_a NOLOGIN NOSUPERUSER;
CREATE ROLE admin_type_b NOLOGIN NOSUPERUSER;
...
GRANT ALTER SYSTEM ON guc_a1, guc_a2, guc_a3, ... TO admin_type_a;
GRANT ALTER SYSTEM ON guc_b1, guc_b2, guc_b3, ... TO admin_type_b;
...
That has all the power of a system based on predefined roles, but with site-specific flexibility, which is better. So it amuses me that we'd now be talking about granting some of these to predefined roles, as that is a regression in flexibility. (How would a site revoke it from one of those predefined roles if they wanted a different policy?)
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Wed, Nov 17, 2021 at 9:31 AM Andrew Dunstan <andrew@dunslane.net> wrote:
Well, I was trying (perhaps not very well) to imagine how to deal with
someone modifying the permissions of one of the predefined roles. Say
pg_foo has initial permission to set bar and baz, and the DBA removes
permission to set baz. How is pg_dump going to emit the right commands
to allow a safe pg_upgrade? Maybe we should say that the permissions for
the predefined roles are immutable, so only permissions sets for user
defined roles are mutable.
That's a great question, but it isn't a new problem. If I create a
brand new database and do thIs:
rhaas=# revoke execute on function pg_ls_waldir() from pg_monitor;
REVOKE
And then I do this:
[rhaas pgsql]$ pg_dump
Then the output includes this:
REVOKE ALL ON FUNCTION pg_catalog.pg_ls_waldir(OUT name text, OUT size
bigint, OUT modification timestamp with time zone) FROM pg_monitor;
I recommend looking at how that works and making this work the same way.
--
Robert Haas
EDB: http://www.enterprisedb.com
On 11/17/21 11:07, Mark Dilger wrote:
On Nov 17, 2021, at 6:31 AM, Andrew Dunstan <andrew@dunslane.net> wrote:
Well, I was trying (perhaps not very well) to imagine how to deal with
someone modifying the permissions of one of the predefined roles. Say
pg_foo has initial permission to set bar and baz, and the DBA removes
permission to set baz. How is pg_dump going to emit the right commands
to allow a safe pg_upgrade? Maybe we should say that the permissions for
the predefined roles are immutable, so only permissions sets for user
defined roles are mutable.I find this somewhat amusing. When you suggested off-list that I make gucs individually grantable rather than creating predefined roles with privileges, that was a great idea precisely because sites could define their own security policies using their own site-defined roles:
CREATE ROLE admin_type_a NOLOGIN NOSUPERUSER;
CREATE ROLE admin_type_b NOLOGIN NOSUPERUSER;
...
GRANT ALTER SYSTEM ON guc_a1, guc_a2, guc_a3, ... TO admin_type_a;
GRANT ALTER SYSTEM ON guc_b1, guc_b2, guc_b3, ... TO admin_type_b;
...That has all the power of a system based on predefined roles, but with site-specific flexibility, which is better. So it amuses me that we'd now be talking about granting some of these to predefined roles, as that is a regression in flexibility. (How would a site revoke it from one of those predefined roles if they wanted a different policy?)
I agree it's not ideal. At the time I suggested a more flexible approach
I hadn't really thought about the problems of upgrading. If you can come
up with something that works there then I'll be all ears.
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
On Nov 17, 2021, at 9:06 AM, Andrew Dunstan <andrew@dunslane.net> wrote:
I agree it's not ideal. At the time I suggested a more flexible approach
I hadn't really thought about the problems of upgrading. If you can come
up with something that works there then I'll be all ears.
Are you talking about upgrades preserving revocations of privileges on gucs from predefined roles, or merely preserving grants and revocation of privileges on gucs to regular roles? I think the former problem is easily handled by not shipping any predefined roles with such privileges. The latter problem would seem to be a mere matter of programming, something I'm working on but don't have finished. (But maybe you see dragons ahead for me that I'm not seeing yet?)
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 11/17/21 12:12, Mark Dilger wrote:
On Nov 17, 2021, at 9:06 AM, Andrew Dunstan <andrew@dunslane.net> wrote:
I agree it's not ideal. At the time I suggested a more flexible approach
I hadn't really thought about the problems of upgrading. If you can come
up with something that works there then I'll be all ears.Are you talking about upgrades preserving revocations of privileges on gucs from predefined roles, or merely preserving grants and revocation of privileges on gucs to regular roles? I think the former problem is easily handled by not shipping any predefined roles with such privileges. The latter problem would seem to be a mere matter of programming, something I'm working on but don't have finished. (But maybe you see dragons ahead for me that I'm not seeing yet?)
Yes, if we don't ship with any preset privileges then the problem I was
thinking about disappears.
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
On Nov 16, 2021, at 7:03 AM, Robert Haas <robertmhaas@gmail.com> wrote:
It's also going to be important to think about what happens with
extension GUCs. If somebody installs an extension, we can't ask them
to perform a manual step in order to be able to grant privileges. And
if somebody then loads up a different .so for that extension, the set
of GUCs that it provides can change without any DDL being executed.
New GUCs could appear, and old GUCs could vanish.
The v3 patch allows grants on unrecognized guc names. This should allow a grant statement to precede the loading of a new .so which provides the named guc.
instead just adjust the GRANT command to automatically insert a new
row into the relevant catalog if there isn't one already. That seems
nicer for extensions, and also nicer for core GUCs, since it avoids
bloating the catalog with a bunch of entries that aren't needed.
Grants on GUCs create a new catalog entry if necessary, or update the existing catalog entry if found.
There is a new information_schema.guc_privileges view, not present in v2.
Attachments:
v3-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-for-variables.patchapplication/octet-stream; name=v3-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-for-variables.patch; x-unix-mode=0644Download
From 15c1c6fb86e2569fb498defa1e77ca25a770d074 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Mon, 15 Nov 2021 09:19:22 -0800
Subject: [PATCH v3] Allow GRANT of SET and ALTER SYSTEM for variables
Allow granting of privilege to set or alter system set variables
which otherwise can only be managed by superusers. Each
(role,variable,privilege) triple is independently grantable, so a
user may be granted privilege to SET but not to ALTER SYSTEM SET on
a variable, or vice versa. The privilege to SET a userset variable
may be granted, though doing so has no practical effect, since any
role can set userset variables anyway.
---
doc/src/sgml/catalogs.sgml | 63 +++++
doc/src/sgml/ddl.sgml | 39 ++-
doc/src/sgml/ref/grant.sgml | 7 +
doc/src/sgml/ref/set.sgml | 5 +-
src/backend/catalog/Makefile | 3 +-
src/backend/catalog/aclchk.c | 251 ++++++++++++++++++++
src/backend/catalog/catalog.c | 6 +
src/backend/catalog/dependency.c | 6 +
src/backend/catalog/information_schema.sql | 19 ++
src/backend/catalog/objectaddress.c | 46 ++++
src/backend/catalog/pg_config_param.c | 126 ++++++++++
src/backend/commands/alter.c | 1 +
src/backend/commands/dropcmds.c | 7 +
src/backend/commands/event_trigger.c | 6 +
src/backend/commands/seclabel.c | 1 +
src/backend/commands/tablecmds.c | 1 +
src/backend/parser/gram.y | 99 +++++++-
src/backend/utils/adt/acl.c | 58 +++++
src/backend/utils/cache/lsyscache.c | 20 ++
src/backend/utils/cache/syscache.c | 23 ++
src/backend/utils/misc/guc.c | 107 +++++++--
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 | 71 ++++++
src/include/catalog/dependency.h | 1 +
src/include/catalog/pg_config_param.h | 63 +++++
src/include/catalog/pg_default_acl.h | 1 +
src/include/nodes/parsenodes.h | 5 +-
src/include/parser/kwlist.h | 1 +
src/include/tcop/cmdtaglist.h | 2 +
src/include/utils/acl.h | 10 +-
src/include/utils/guc.h | 2 +
src/include/utils/lsyscache.h | 1 +
src/include/utils/syscache.h | 2 +
src/test/modules/test_pg_dump/t/001_base.pl | 54 ++++-
src/test/regress/expected/guc_privs.out | 204 ++++++++++++++++
src/test/regress/expected/privileges.out | 54 ++++-
src/test/regress/expected/sanity_check.out | 1 +
src/test/regress/parallel_schedule | 2 +-
src/test/regress/sql/guc_privs.sql | 142 +++++++++++
src/test/regress/sql/privileges.sql | 44 +++-
42 files changed, 1532 insertions(+), 36 deletions(-)
create mode 100644 src/backend/catalog/pg_config_param.c
create mode 100644 src/include/catalog/pg_config_param.h
create mode 100644 src/test/regress/expected/guc_privs.out
create mode 100644 src/test/regress/sql/guc_privs.sql
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index c1d11be73f..fa085549b8 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -105,6 +105,11 @@
<entry>collations (locale information)</entry>
</row>
+ <row>
+ <entry><link linkend="catalog-pg-config-param"><structname>pg_config_param</structname></link></entry>
+ <entry>configuration parameters which have privileges granted to roles</entry>
+ </row>
+
<row>
<entry><link linkend="catalog-pg-constraint"><structname>pg_constraint</structname></link></entry>
<entry>check constraints, unique constraints, primary key constraints, foreign key constraints</entry>
@@ -2423,6 +2428,64 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</para>
</sect1>
+ <sect1 id="catalog-pg-config-param">
+ <title><structname>pg_config_param</structname></title>
+
+ <indexterm zone="catalog-pg-config-param">
+ <primary>pg_config_param</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_config_param</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_config_param</structname>
+ is shared across all databases of a cluster: there is only one
+ copy of <structname>pg_config_param</structname> per cluster, not
+ one per database.
+ </para>
+
+ <table>
+ <title><structname>pg_config_param</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>configname</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>cfgacl</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>
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 94f745aed0..d862ab8c0e 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -1620,7 +1620,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 VALUE</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.
@@ -1888,6 +1889,26 @@ REVOKE ALL ON accounts FROM PUBLIC;
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>SET VALUE</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>SET</command> command to
+ change run-time configuration parameters.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ALTER SYSTEM</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>ALTER SYSTEM
+ SET</command> command to change server configuration parameters.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
The privileges required by other commands are listed on the
@@ -2026,6 +2047,16 @@ REVOKE ALL ON accounts FROM PUBLIC;
<literal>TYPE</literal>
</entry>
</row>
+ <row>
+ <entry><literal>SET VALUE</literal></entry>
+ <entry><literal>s</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
+ <row>
+ <entry><literal>ALTER SYSTEM</literal></entry>
+ <entry><literal>A</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -2132,6 +2163,12 @@ REVOKE ALL ON accounts FROM PUBLIC;
<entry><literal>U</literal></entry>
<entry><literal>\dT+</literal></entry>
</row>
+ <row>
+ <entry><literal>Configuration parameter</literal></entry>
+ <entry><literal>sA</literal></entry>
+ <entry>none</entry>
+ <entry><literal></literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index a897712de2..21ec320a8d 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 VALUE | ALTER SYSTEM }
+ ON <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 VALUE</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/set.sgml b/doc/src/sgml/ref/set.sgml
index 339ee9eec9..a08057d1d1 100644
--- a/doc/src/sgml/ref/set.sgml
+++ b/doc/src/sgml/ref/set.sgml
@@ -34,8 +34,9 @@ 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.)
+ (But some require either superuser privileges or granted <literal>SET
+ VALUE</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>
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 4e6efda97f..bec208db98 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -28,6 +28,7 @@ OBJS = \
pg_cast.o \
pg_class.o \
pg_collation.o \
+ pg_config_param.o \
pg_constraint.o \
pg_conversion.o \
pg_db_role_setting.o \
@@ -54,7 +55,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_config_param.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 ce0a4ff14e..d309375a31 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -33,6 +33,7 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_config_param.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/pg_default_acl.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_ConfigParam(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_CONFIG_PARAM:
+ whole_mask = ACL_ALL_RIGHTS_CONFIG_PARAM;
+ 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_CONFIG_PARAM:
+ all_privileges = ACL_ALL_RIGHTS_CONFIG_PARAM;
+ errormsg = gettext_noop("invalid privilege type %s for configuration 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_CONFIG_PARAM:
+ ExecGrant_ConfigParam(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -759,6 +771,38 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_CONFIG_PARAM:
+ foreach(cell, objnames)
+ {
+ char *cfgname = strVal(lfirst(cell));
+ Oid cfgid = get_cfgparam_oid(cfgname, true);
+
+ if (!OidIsValid(cfgid))
+ {
+ /*
+ * Lookup the existing entry, or if necessary, add a new
+ * entry for this parameter. Entries only exist for
+ * parameters which currently have, or previously have had,
+ * privileges assigned.
+ *
+ * It is tempting to sanity-check the given configuration
+ * parameter name against known guc names in the guc
+ * tables, but for handling upgrades we need to accept
+ * configuration parameter names that do not yet exist.
+ */
+ cfgid = ConfigParamCreate(cfgname, 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, cfgid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -1494,6 +1538,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case ForeignDataWrapperRelationId:
istmt.objtype = OBJECT_FDW;
break;
+ case ConfigParamRelationId:
+ istmt.objtype = OBJECT_CONFIG_PARAM;
+ break;
default:
elog(ERROR, "unexpected object class %u", classid);
break;
@@ -3225,6 +3272,128 @@ ExecGrant_Type(InternalGrant *istmt)
table_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_ConfigParam(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_CONFIG_PARAM;
+
+ relation = table_open(ConfigParamRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid cfgId = lfirst_oid(cell);
+ Form_pg_config_param pg_config_param_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_config_param];
+ bool nulls[Natts_pg_config_param];
+ bool replaces[Natts_pg_config_param];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(CONFIGOID, ObjectIdGetDatum(cfgId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for configuration parameter %u", cfgId);
+
+ pg_config_param_tuple = (Form_pg_config_param) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ aclDatum = SysCacheGetAttr(CONFIGNAME, tuple, Anum_pg_config_param_cfgacl,
+ &isNull);
+ if (isNull)
+ {
+ old_acl = acldefault(OBJECT_CONFIG_PARAM, InvalidOid);
+ /* 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, GetUserId(),
+ &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,
+ cfgId, grantorId, OBJECT_CONFIG_PARAM,
+ text_to_cstring(&pg_config_param_tuple->configname),
+ 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_config_param_cfgacl - 1] = true;
+ values[Anum_pg_config_param_cfgacl - 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(cfgId, ConfigParamRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(ConfigParamRelationId,
+ pg_config_param_tuple->oid, 0,
+ InvalidOid,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ table_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3255,6 +3424,10 @@ string_to_privilege(const char *privname)
return ACL_CREATE_TEMP;
if (strcmp(privname, "connect") == 0)
return ACL_CONNECT;
+ if (strcmp(privname, "set value") == 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 +3465,10 @@ privilege_to_string(AclMode privilege)
return "TEMP";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET:
+ return "SET VALUE";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3328,6 +3505,13 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_COLUMN:
msg = gettext_noop("permission denied for column %s");
break;
+ case OBJECT_CONFIG_PARAM:
+ /*
+ * 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 +3748,7 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_AMPROC:
case OBJECT_ATTRIBUTE:
case OBJECT_CAST:
+ case OBJECT_CONFIG_PARAM:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
case OBJECT_DOMCONSTRAINT:
@@ -4000,6 +4185,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_config_param_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 configuration parameter's ACL from pg_config_param
+ */
+ tuple = SearchSysCache1(CONFIGOID, ObjectIdGetDatum(config_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("configuration parameter with OID %u does not exist",
+ config_oid)));
+
+ aclDatum = SysCacheGetAttr(CONFIGOID, tuple, Anum_pg_config_param_cfgacl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_CONFIG_PARAM, 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 +4951,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_config_param_aclcheck(Oid config_oid, Oid roleid, AclMode mode)
+{
+ if (pg_config_param_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 aa7d4d5456..578bac485c 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -29,6 +29,7 @@
#include "catalog/namespace.h"
#include "catalog/pg_auth_members.h"
#include "catalog/pg_authid.h"
+#include "catalog/pg_config_param.h"
#include "catalog/pg_database.h"
#include "catalog/pg_db_role_setting.h"
#include "catalog/pg_largeobject.h"
@@ -246,6 +247,7 @@ IsSharedRelation(Oid relationId)
/* These are the shared catalogs (look for BKI_SHARED_RELATION) */
if (relationId == AuthIdRelationId ||
relationId == AuthMemRelationId ||
+ relationId == ConfigParamRelationId ||
relationId == DatabaseRelationId ||
relationId == SharedDescriptionRelationId ||
relationId == SharedDependRelationId ||
@@ -260,6 +262,8 @@ IsSharedRelation(Oid relationId)
relationId == AuthIdOidIndexId ||
relationId == AuthMemRoleMemIndexId ||
relationId == AuthMemMemRoleIndexId ||
+ relationId == ConfigParamNameIndexId ||
+ relationId == ConfigParamOidIndexId ||
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 == PgConfigParamToastTable ||
+ relationId == PgConfigParamToastIndex ||
relationId == PgDatabaseToastTable ||
relationId == PgDatabaseToastIndex ||
relationId == PgDbRoleSettingToastTable ||
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index fe9c714257..65cf4fbd0b 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -30,6 +30,7 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_config_param.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
@@ -150,6 +151,7 @@ static const Oid object_classes[] = {
TypeRelationId, /* OCLASS_TYPE */
CastRelationId, /* OCLASS_CAST */
CollationRelationId, /* OCLASS_COLLATION */
+ ConfigParamRelationId, /* OCLASS_CONFIG_PARAM */
ConstraintRelationId, /* OCLASS_CONSTRAINT */
ConversionRelationId, /* OCLASS_CONVERSION */
AttrDefaultRelationId, /* OCLASS_DEFAULT */
@@ -1504,6 +1506,7 @@ doDeletion(const ObjectAddress *object, int flags)
/*
* These global object types are not supported here.
*/
+ case OCLASS_CONFIG_PARAM:
case OCLASS_ROLE:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
@@ -2778,6 +2781,9 @@ getObjectClass(const ObjectAddress *object)
case CollationRelationId:
return OCLASS_COLLATION;
+ case ConfigParamRelationId:
+ return OCLASS_CONFIG_PARAM;
+
case ConstraintRelationId:
return OCLASS_CONSTRAINT;
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index 11d9dd60c2..38dd62c398 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -2429,6 +2429,25 @@ CREATE VIEW usage_privileges AS
GRANT SELECT ON usage_privileges TO PUBLIC;
+/*
+ * GUC_PRIVILEGES view
+ */
+CREATE VIEW guc_privileges AS
+ SELECT CAST(grantor.rolname AS sql_identifier) AS grantor,
+ CAST(grantee.rolname AS sql_identifier) aS grantee,
+ CAST(cfg.configname AS character_data) AS guc,
+ CAST(acl.privilege_type AS character_data) AS privilege_type,
+ CAST(CASE WHEN acl.is_grantable THEN 'YES' ELSE 'NO' END AS yes_or_no) AS is_grantable
+ FROM pg_catalog.pg_config_param cfg,
+ LATERAL (SELECT * FROM aclexplode(cfg.cfgacl)) acl
+ JOIN pg_catalog.pg_authid grantee
+ ON acl.grantee = grantee.oid
+ LEFT JOIN pg_catalog.pg_authid grantor ON acl.grantor = grantor.oid;
+ -- WHERE acl.grantee > 0;
+
+GRANT SELECT ON guc_privileges TO PUBLIC;
+
+
/*
* 5.45
* ROLE_USAGE_GRANTS view
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 2bae3fbb17..7355c7403d 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -29,6 +29,7 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_config_param.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
@@ -2309,6 +2310,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_COLUMN:
case OBJECT_ATTRIBUTE:
case OBJECT_COLLATION:
+ case OBJECT_CONFIG_PARAM:
case OBJECT_CONVERSION:
case OBJECT_STATISTIC_EXT:
case OBJECT_TSPARSER:
@@ -3510,6 +3512,22 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
break;
}
+ case OCLASS_CONFIG_PARAM:
+ {
+ char *configname;
+
+ configname = get_cfgparam_name(object->objectId);
+ if (!configname)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for configuration parameter %u",
+ object->objectId);
+ break;
+ }
+ appendStringInfo(&buffer, _("configuration parameter %s"), configname);
+ break;
+ }
+
case OCLASS_SCHEMA:
{
char *nspname;
@@ -4473,6 +4491,10 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "collation");
break;
+ case OCLASS_CONFIG_PARAM:
+ appendStringInfoString(&buffer, "configuration parameter");
+ break;
+
case OCLASS_CONSTRAINT:
getConstraintTypeDescription(&buffer, object->objectId,
missing_ok);
@@ -4977,6 +4999,30 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_CONFIG_PARAM:
+ {
+ HeapTuple configTup;
+ Form_pg_config_param configForm;
+ char *namestr;
+
+ configTup = SearchSysCache1(CONFIGOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(configTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for configuration parameter %u",
+ object->objectId);
+ break;
+ }
+ configForm = (Form_pg_config_param) GETSTRUCT(configTup);
+ namestr = text_to_cstring(&configForm->configname);
+ appendStringInfoString(&buffer, namestr);
+ if (objname)
+ *objname = list_make1(namestr);
+ ReleaseSysCache(configTup);
+ break;
+ }
+
case OCLASS_CONVERSION:
{
HeapTuple conTup;
diff --git a/src/backend/catalog/pg_config_param.c b/src/backend/catalog/pg_config_param.c
new file mode 100644
index 0000000000..f71de8aa25
--- /dev/null
+++ b/src/backend/catalog/pg_config_param.c
@@ -0,0 +1,126 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_config_param.c
+ * routines to support manipulation of the pg_config_param 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_config_param.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/sysattr.h"
+#include "access/table.h"
+#include "access/tableam.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_config_param.h"
+#include "catalog/pg_namespace.h"
+#include "mb/pg_wchar.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/guc.h"
+#include "utils/pg_locale.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * ConfigParamCreate
+ *
+ * Add a new tuple to pg_config_param.
+ *
+ * configname: the configuration parameter name to create.
+ * if_not_exists: if true, don't fail on duplicate name, but rather return
+ * the existing entry's Oid.
+ */
+Oid
+ConfigParamCreate(const char *configname, bool if_not_exists)
+{
+ Relation rel;
+ TupleDesc tupDesc;
+ HeapTuple tuple;
+ Datum values[Natts_pg_config_param];
+ bool nulls[Natts_pg_config_param];
+ Oid configParamId;
+ const char *canonical;
+
+ /*
+ * Check whether the configuration parameter (by the given name or alias)
+ * already exists.
+ */
+ configParamId = get_cfgparam_oid(configname, true);
+ if (OidIsValid(configParamId))
+ {
+ if (if_not_exists)
+ return configParamId;
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("configuration parameter \"%s\" already exists",
+ configname)));
+ }
+
+ /*
+ * We must not require the configname to be in the list of existent GUCs,
+ * as we may be called at different points during upgrades or the
+ * installation or removal of extensions. Instead, perform a basic sanity
+ * check of the configname, 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_config_param entries.
+ */
+ if (!valid_variable_name(configname, NULL))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid configuration parameter name \"%s\"",
+ configname)));
+ canonical = GetConfigOptionCanonicalName(configname);
+ if (!canonical)
+ canonical = configname;
+ else if (strcmp(canonical, configname) != 0)
+ ereport(NOTICE,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("granting privileges on canonical configuration parameter name \"%s\"", canonical)));
+
+ /*
+ * 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(ConfigParamRelationId, RowExclusiveLock);
+ tupDesc = RelationGetDescr(rel);
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ values[Anum_pg_config_param_configname - 1] =
+ DirectFunctionCall1(textin, CStringGetDatum(canonical));
+ configParamId = GetNewOidWithIndex(rel,
+ ConfigParamOidIndexId,
+ Anum_pg_config_param_oid);
+ values[Anum_pg_config_param_oid - 1] = ObjectIdGetDatum(configParamId);
+ nulls[Anum_pg_config_param_cfgacl - 1] = true;
+ tuple = heap_form_tuple(tupDesc, values, nulls);
+ CatalogTupleInsert(rel, tuple);
+
+ /* Post creation hook for new configuration parameter */
+ InvokeObjectPostCreateHook(ConfigParamRelationId, configParamId, 0);
+
+ /*
+ * Close pg_config_param, but keep lock till commit.
+ */
+ heap_freetuple(tuple);
+ table_close(rel, NoLock);
+
+ return configParamId;
+}
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 40044070cf..1f8c3f78c2 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -639,6 +639,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
break;
case OCLASS_CAST:
+ case OCLASS_CONFIG_PARAM:
case OCLASS_CONSTRAINT:
case OCLASS_DEFAULT:
case OCLASS_LANGUAGE:
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 4e545adf95..cd98976e07 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -276,6 +276,13 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
name = NameListToString(castNode(List, object));
}
break;
+ case OBJECT_CONFIG_PARAM:
+ if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
+ {
+ msg = gettext_noop("configuration parameter \"%s\" does not exist, skipping");
+ name = NameListToString(castNode(List, object));
+ }
+ break;
case OBJECT_CONVERSION:
if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
{
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index df264329d8..419a7a4fad 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_CONFIG_PARAM:
case OBJECT_DATABASE:
case OBJECT_TABLESPACE:
case OBJECT_ROLE:
@@ -1012,6 +1013,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
{
switch (objclass)
{
+ case OCLASS_CONFIG_PARAM:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
@@ -2078,6 +2080,8 @@ stringify_grant_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMN";
+ case OBJECT_CONFIG_PARAM:
+ return "CONFIGURATION PARAMETER";
case OBJECT_TABLE:
return "TABLE";
case OBJECT_SEQUENCE:
@@ -2161,6 +2165,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMNS";
+ case OBJECT_CONFIG_PARAM:
+ return "CONFIGURATION PARAMETERS";
case OBJECT_TABLE:
return "TABLES";
case OBJECT_SEQUENCE:
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 53c18628a7..6a0fe325cf 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_CONFIG_PARAM:
case OBJECT_CONVERSION:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 785b282e69..c600169cbd 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -12262,6 +12262,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_TYPE:
case OCLASS_CAST:
case OCLASS_COLLATION:
+ case OCLASS_CONFIG_PARAM:
case OCLASS_CONVERSION:
case OCLASS_LANGUAGE:
case OCLASS_LARGEOBJECT:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a6d0cefa6b..2adadaa6e4 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -348,8 +348,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 cfgparam_name
+%type <list> OptSchemaEltList cfgparam_target
%type <chr> am_type
@@ -387,8 +387,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> iso_level opt_encoding
%type <rolespec> grantee
%type <list> grantee_list
-%type <accesspriv> privilege
-%type <list> privileges privilege_list
+%type <accesspriv> privilege guc_priv
+%type <list> privileges privilege_list guc_priv_list
%type <privtarget> privilege_target
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
@@ -698,7 +698,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
@@ -6884,6 +6884,20 @@ GrantStmt: GRANT privileges ON privilege_target TO grantee_list
n->grantor = $8;
$$ = (Node*)n;
}
+ | GRANT guc_priv_list ON cfgparam_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_CONFIG_PARAM;
+ n->objects = $4;
+ n->grantees = $6;
+ n->grant_option = $7;
+ n->grantor = $8;
+ $$ = (Node*)n;
+ }
;
RevokeStmt:
@@ -6917,6 +6931,36 @@ RevokeStmt:
n->behavior = $11;
$$ = (Node *)n;
}
+ | REVOKE guc_priv_list ON cfgparam_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_CONFIG_PARAM;
+ n->objects = $4;
+ n->grantees = $6;
+ n->grantor = $7;
+ n->behavior = $8;
+ $$ = (Node *)n;
+ }
+ | REVOKE GRANT OPTION FOR guc_priv_list ON cfgparam_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_CONFIG_PARAM;
+ n->objects = $7;
+ n->grantees = $9;
+ n->grantor = $10;
+ n->behavior = $11;
+ $$ = (Node *)n;
+ }
;
@@ -6985,6 +7029,27 @@ privilege: SELECT opt_column_list
}
;
+guc_priv_list: guc_priv { $$ = list_make1($1); }
+ | guc_priv_list ',' guc_priv { $$ = lappend($1, $3); }
+ ;
+
+guc_priv:
+ ALTER SYSTEM_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("alter system");
+ n->cols = NIL;
+ $$ = n;
+ }
+ | SET VALUE_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("set value");
+ n->cols = NIL;
+ $$ = n;
+ }
+ ;
+
/* Don't bother trying to fold the first two rules into one using
* opt_table. You're going to get conflicts.
@@ -10658,6 +10723,28 @@ any_with: WITH
;
+cfgparam_target:
+ cfgparam_name
+ {
+ $$ = list_make1(makeString($1));
+ }
+ | cfgparam_target ',' cfgparam_name
+ {
+ $$ = lappend($1, makeString($3));
+ }
+ ;
+
+cfgparam_name:
+ ColId
+ {
+ $$ = $1;
+ }
+ | cfgparam_name '.' ColId
+ {
+ $$ = psprintf("%s.%s", $1, $3);
+ }
+ ;
+
/*****************************************************************************
*
* Manipulate a conversion
@@ -15728,6 +15815,7 @@ unreserved_keyword:
| OWNED
| OWNER
| PARALLEL
+ | PARAMETER
| PARSER
| PARTIAL
| PARTITION
@@ -16306,6 +16394,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 67f8b29434..895b542916 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -22,6 +22,7 @@
#include "catalog/pg_auth_members.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_class.h"
+#include "catalog/pg_config_param.h"
#include "catalog/pg_database.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.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"
@@ -306,6 +308,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 +802,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_CONFIG_PARAM:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_NO_RIGHTS;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -1602,6 +1614,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 VALUE") == 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 +1714,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET:
+ return "SET VALUE";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
@@ -4670,6 +4690,44 @@ initialize_acl(void)
}
}
+/*
+ * get_cfgparam_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_cfgparam_oid(const char *cfgname, bool missing_ok)
+{
+ Oid oid;
+
+ /* Check for the variable by the name we were given */
+ oid = GetSysCacheOid1(CONFIGNAME, Anum_pg_config_param_oid,
+ PointerGetDatum(cstring_to_text(cfgname)));
+ if (!OidIsValid(oid))
+ {
+ const char *canonical;
+
+ /* Check if the variable has a different canonical spelling */
+ canonical = GetConfigOptionCanonicalName(cfgname);
+ if (canonical != NULL)
+ oid = GetSysCacheOid1(CONFIGNAME, Anum_pg_config_param_oid,
+ PointerGetDatum(cstring_to_text(canonical)));
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("configuration parameter \"%s\" does not exist",
+ cfgname)));
+ }
+
+ return oid;
+}
+
/*
* RoleMembershipCacheCallback
* Syscache inval callback function
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 4ebaa552a2..b8768f7506 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -25,6 +25,7 @@
#include "catalog/pg_amproc.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_config_param.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
@@ -3304,6 +3305,25 @@ free_attstatsslot(AttStatsSlot *sslot)
pfree(sslot->numbers_arr);
}
+char *
+get_cfgparam_name(Oid configid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(CONFIGOID, ObjectIdGetDatum(configid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_config_param configtup = (Form_pg_config_param) GETSTRUCT(tp);
+ char *result;
+
+ result = pstrdup(text_to_cstring(&configtup->configname));
+ 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 56870b46e4..e66158d584 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -30,6 +30,7 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_config_param.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
@@ -310,6 +311,28 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {ConfigParamRelationId, /* CONFIGNAME */
+ ConfigParamNameIndexId,
+ 1,
+ {
+ Anum_pg_config_param_configname,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+ {ConfigParamRelationId, /* CONFIGOID */
+ ConfigParamOidIndexId,
+ 1,
+ {
+ Anum_pg_config_param_oid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{ConversionRelationId, /* CONDEFAULT */
ConversionDefaultIndexId,
4,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index e91d5a3cfd..446e42d320 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -5021,6 +5021,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_config_param
+ * entries.
*/
static const char *const map_old_guc_names[] = {
"sort_mem", "work_mem",
@@ -5420,25 +5424,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().)
+ *
+ * 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"
@@ -5455,8 +5463,24 @@ 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;
+}
+
+/*
+ * 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().)
+ */
+static bool
+valid_custom_variable_name(const char *name)
+{
+ int partcnt;
+
+ return (valid_variable_name(name, &partcnt) && partcnt > 1);
}
/*
@@ -8533,16 +8557,32 @@ 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;
+ Oid cfgId;
+
+ cfgId = get_cfgparam_oid(name, true);
+ if (!OidIsValid(cfgId))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+
+ aclresult = pg_config_param_aclcheck(cfgId, GetUserId(),
+ ACL_ALTER_SYSTEM);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_CONFIG_PARAM, name);
+ }
+
switch (altersysstmt->setstmt->kind)
{
case VAR_SET_VALUE:
@@ -8735,6 +8775,9 @@ void
ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
{
GucAction action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET;
+ GucContext context;
+ AclResult aclresult;
+ Oid cfgId;
/*
* Workers synchronize these parameters at the start of the parallel
@@ -8745,6 +8788,20 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
errmsg("cannot set parameters during a parallel operation")));
+ /*
+ * 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(cfgId = get_cfgparam_oid(stmt->name, true)))
+ {
+ aclresult = pg_config_param_aclcheck(cfgId, GetUserId(), ACL_SET);
+ if (aclresult == ACLCHECK_OK)
+ context = PGC_SUSET;
+ }
+
switch (stmt->kind)
{
case VAR_SET_VALUE:
@@ -8753,7 +8810,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;
@@ -8838,7 +8895,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;
@@ -9578,6 +9635,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, false, 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,
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index ea67e52a3f..07298198e3 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, CONFIGURATION PARAMETER or LARGE OBJECT)
* acls: the ACL string fetched from the database
* racls: the ACL string of any initial-but-now-revoked privileges
* owner: username of object owner (will be passed through fmtId); can be
@@ -573,6 +573,11 @@ do { \
CONVERT_PRIV('U', "USAGE");
else if (strcmp(type, "FOREIGN TABLE") == 0)
CONVERT_PRIV('r', "SELECT");
+ else if (strcmp(type, "CONFIGURATION PARAMETER") == 0)
+ {
+ CONVERT_PRIV('s', "SET VALUE");
+ 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 59f4fbb2cc..aaadac8e32 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -3413,6 +3413,7 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te)
strcmp(type, "SCHEMA") == 0 ||
strcmp(type, "EVENT TRIGGER") == 0 ||
strcmp(type, "FOREIGN DATA WRAPPER") == 0 ||
+ strcmp(type, "CONFIGURATION PARAMETER") == 0 ||
strcmp(type, "SERVER") == 0 ||
strcmp(type, "PUBLICATION") == 0 ||
strcmp(type, "SUBSCRIPTION") == 0 ||
@@ -3596,6 +3597,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, "CONFIGURATION 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 5a2094de9f..9e79fd129f 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -15078,6 +15078,9 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
case DEFACLOBJ_NAMESPACE:
type = "SCHEMAS";
break;
+ case DEFACLOBJ_CONFIG_PARAM:
+ type = "CONFIGURATION PARAMETERS";
+ break;
default:
/* shouldn't get here */
fatal("unrecognized object type in default privileges: %d",
@@ -15123,7 +15126,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, CONFIGURATION 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 c29101704a..2f398c8094 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 dumpGroups(PGconn *conn);
static void dropTablespaces(PGconn *conn);
static void dumpTablespaces(PGconn *conn);
@@ -586,6 +587,10 @@ main(int argc, char *argv[])
dumpRoleMembership(conn);
else
dumpGroups(conn);
+
+ /* Dump role guc privileges */
+ if (server_version >= 150000)
+ dumpRoleGUCPrivs(conn);
}
/* Dump tablespaces */
@@ -1049,6 +1054,72 @@ dumpRoleMembership(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)
+{
+ PQExpBuffer buf = createPQExpBuffer();
+ PGresult *res;
+ int i;
+
+ printfPQExpBuffer(buf, "SELECT string_agg(acl.privilege_type, ', ' ORDER BY acl.privilege_type), "
+ "cfg.configname, "
+ "grantee.rolname AS grantee, "
+ "acl.is_grantable, "
+ "grantor.rolname AS grantor "
+ "FROM pg_catalog.pg_config_param cfg, "
+ "LATERAL (SELECT * FROM aclexplode(cfg.cfgacl)) acl "
+ "JOIN pg_catalog.pg_authid grantee "
+ "ON acl.grantee = grantee.oid "
+ "LEFT JOIN pg_catalog.pg_authid grantor ON "
+ "acl.grantor = grantor.oid "
+ "WHERE acl.grantee > 0 "
+ "GROUP BY configname, grantee.rolname, is_grantable, grantor.rolname"
+ );
+
+ res = executeQuery(conn, buf->data);
+
+ if (PQntuples(res) > 0)
+ fprintf(OPF, "--\n-- Role privileges on configuration parameters\n--\n\n");
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ char *privilege = PQgetvalue(res, i, 0);
+ char *cfgname = PQgetvalue(res, i, 1);
+ char *grantee = PQgetvalue(res, i, 2);
+ char *grantable = PQgetvalue(res, i, 3);
+
+ fprintf(OPF, "GRANT %s", privilege);
+ fprintf(OPF, " ON %s", cfgname);
+ fprintf(OPF, " TO %s", fmtId(grantee));
+ if (*grantable == 't')
+ fprintf(OPF, " WITH GRANT OPTION");
+
+ /*
+ * We don't track the grantor very carefully in the backend, so cope
+ * with the possibility that it has been dropped.
+ */
+ if (!PQgetisnull(res, i, 4))
+ {
+ char *grantor = PQgetvalue(res, i, 4);
+
+ fprintf(OPF, " GRANTED BY %s", fmtId(grantor));
+ }
+ fprintf(OPF, ";\n");
+ }
+
+ PQclear(res);
+ destroyPQExpBuffer(buf);
+
+ fprintf(OPF, "\n\n");
+}
+
/*
* Dump group memberships from a pre-8.1 server. It's annoying that we
* can't share any useful amount of code with the post-8.1 case, but
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 3eca295ff4..ab92782d15 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -92,6 +92,7 @@ typedef enum ObjectClass
OCLASS_TYPE, /* pg_type */
OCLASS_CAST, /* pg_cast */
OCLASS_COLLATION, /* pg_collation */
+ OCLASS_CONFIG_PARAM, /* pg_config_param */
OCLASS_CONSTRAINT, /* pg_constraint */
OCLASS_CONVERSION, /* pg_conversion */
OCLASS_DEFAULT, /* pg_attrdef */
diff --git a/src/include/catalog/pg_config_param.h b/src/include/catalog/pg_config_param.h
new file mode 100644
index 0000000000..6b92a0150e
--- /dev/null
+++ b/src/include/catalog/pg_config_param.h
@@ -0,0 +1,63 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_config_param.h
+ * definition of the "configuration parameter" system catalog
+ * (pg_config_param).
+ *
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_config_param.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_CONFIG_PARAM_H
+#define PG_CONFIG_PARAM_H
+
+#include "catalog/genbki.h"
+#include "catalog/pg_config_param_d.h"
+
+/* ----------------
+ * pg_config_param definition. cpp turns this into
+ * typedef struct FormData_pg_config_param
+ * ----------------
+ */
+CATALOG(pg_config_param,8924,ConfigParamRelationId) BKI_SHARED_RELATION
+{
+ Oid oid; /* oid */
+ /*
+
+ * Variable-length fields start here, but we allow direct access to
+ * configname.
+ */
+ text configname BKI_FORCE_NOT_NULL;
+
+#ifdef CATALOG_VARLEN
+ /* Access privileges */
+ aclitem cfgacl[1] BKI_DEFAULT(_null_);
+#endif
+} FormData_pg_config_param;
+
+
+/* ----------------
+ * Form_pg_config_param corresponds to a pointer to a tuple with
+ * the format of pg_config_param relation.
+ * ----------------
+ */
+typedef FormData_pg_config_param *Form_pg_config_param;
+
+DECLARE_TOAST(pg_config_param, 8925, 8926);
+#define PgConfigParamToastTable 8925
+#define PgConfigParamToastIndex 8926
+
+DECLARE_UNIQUE_INDEX(pg_cfgparam_name_index, 8927, ConfigParamNameIndexId, on pg_config_param using btree(configname text_ops));
+DECLARE_UNIQUE_INDEX_PKEY(pg_config_param_oid_index, 8928, ConfigParamOidIndexId, on pg_config_param using btree(oid oid_ops));
+
+extern Oid ConfigParamCreate(const char *configname, bool if_not_exists);
+
+#endif /* PG_CONFIG_PARAM_H */
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index eb72dd6293..4ca71b2258 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_CONFIG_PARAM 'c' /* configuration parameter */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 067138e6b5..f717c6d024 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -92,7 +92,9 @@ 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 N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */
+#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
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
@@ -1794,6 +1796,7 @@ typedef enum ObjectType
OBJECT_CAST,
OBJECT_COLUMN,
OBJECT_COLLATION,
+ OBJECT_CONFIG_PARAM,
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DEFAULT,
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f836acf876..527e723b39 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -307,6 +307,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/tcop/cmdtaglist.h b/src/include/tcop/cmdtaglist.h
index 9ba24d4ca9..203b916fec 100644
--- a/src/include/tcop/cmdtaglist.h
+++ b/src/include/tcop/cmdtaglist.h
@@ -86,6 +86,7 @@ PG_CMDTAG(CMDTAG_CREATE_ACCESS_METHOD, "CREATE ACCESS METHOD", true, false, fals
PG_CMDTAG(CMDTAG_CREATE_AGGREGATE, "CREATE AGGREGATE", true, false, false)
PG_CMDTAG(CMDTAG_CREATE_CAST, "CREATE CAST", true, false, false)
PG_CMDTAG(CMDTAG_CREATE_COLLATION, "CREATE COLLATION", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_CONFIG_PARAM, "CREATE CONFIGURATION PARAMETER", false, false, false)
PG_CMDTAG(CMDTAG_CREATE_CONSTRAINT, "CREATE CONSTRAINT", true, false, false)
PG_CMDTAG(CMDTAG_CREATE_CONVERSION, "CREATE CONVERSION", true, false, false)
PG_CMDTAG(CMDTAG_CREATE_DATABASE, "CREATE DATABASE", false, false, false)
@@ -138,6 +139,7 @@ PG_CMDTAG(CMDTAG_DROP_ACCESS_METHOD, "DROP ACCESS METHOD", true, false, false)
PG_CMDTAG(CMDTAG_DROP_AGGREGATE, "DROP AGGREGATE", true, false, false)
PG_CMDTAG(CMDTAG_DROP_CAST, "DROP CAST", true, false, false)
PG_CMDTAG(CMDTAG_DROP_COLLATION, "DROP COLLATION", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_CONFIG_PARAM, "DROP CONFIGURATION PARAMETER", false, false, false)
PG_CMDTAG(CMDTAG_DROP_CONSTRAINT, "DROP CONSTRAINT", true, false, false)
PG_CMDTAG(CMDTAG_DROP_CONVERSION, "DROP CONVERSION", true, false, false)
PG_CMDTAG(CMDTAG_DROP_DATABASE, "DROP DATABASE", false, false, false)
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index af771c901d..afc36ab257 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -146,9 +146,11 @@ typedef struct ArrayType Acl;
#define ACL_CREATE_CHR 'C'
#define ACL_CREATE_TEMP_CHR 'T'
#define ACL_CONNECT_CHR 'c'
+#define ACL_SET_CHR 's'
+#define ACL_ALTER_SYSTEM_CHR 'A'
/* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTc"
+#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcsA"
/*
* Bitmasks defining "all rights" for each supported object type
@@ -165,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_CONFIG_PARAM (ACL_SET|ACL_ALTER_SYSTEM)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -223,6 +226,8 @@ extern void select_best_grantor(Oid roleId, AclMode privileges,
extern void initialize_acl(void);
+extern Oid get_cfgparam_oid(const char *cfgname, bool missing_ok);
+
/*
* prototypes for functions in aclchk.c
*/
@@ -243,6 +248,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_config_param_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,
@@ -271,6 +278,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_config_param_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 aa18d304ac..fb2fe193c5 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -379,6 +379,8 @@ extern int set_config_option(const char *name, const char *value,
GucAction action, bool changeVal, int elevel,
bool is_reload);
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 77871aaefc..33c21f7102 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_cfgparam_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 c8cfbc30f6..14d803a6dc 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -48,6 +48,8 @@ enum SysCacheIdentifier
CLAOID,
COLLNAMEENCNSP,
COLLOID,
+ CONFIGNAME,
+ CONFIGOID,
CONDEFAULT,
CONNAMENSP,
CONSTROID,
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 16f7610883..7fbf2d871b 100644
--- a/src/test/modules/test_pg_dump/t/001_base.pl
+++ b/src/test/modules/test_pg_dump/t/001_base.pl
@@ -9,7 +9,12 @@ use PostgreSQL::Test::Cluster;
use PostgreSQL::Test::Utils;
use Test::More;
-my $tempdir = PostgreSQL::Test::Utils::tempdir;
+# my $tempdir = PostgreSQL::Test::Utils::tempdir;
+my $tempbase = '/tmp/test_pg_dump';
+my $subdir = 0;
+$subdir++ while (-e "$tempbase/$subdir");
+my $tempdir = "$tempbase/$subdir";
+system("mkdir $tempdir");
###############################################################
# This structure is based off of the src/bin/pg_dump/t test
@@ -317,6 +322,53 @@ my %tests = (
like => { pg_dumpall_globals => 1, },
},
+ 'GRANT ALTER SYSTEM ON ignore_checksum_failure' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT ALTER SYSTEM ON ignore_checksum_failure TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM ON ignore_checksum_failure TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT SET VALUE ON my.missing.guc' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET VALUE ON my.missing.guc TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT SET VALUE ON my\.missing\.guc TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT SET VALUE, ALTER SYSTEM ON something WITH GRANT OPTION' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET VALUE, ALTER SYSTEM ON something TO regress_dump_test_role WITH GRANT OPTION;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM, SET VALUE ON something TO regress_dump_test_role WITH GRANT OPTION GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALTER SYSTEM ON MyReallyLong.ButValidCustom.GucNameThatCannotFit.InNamedata64Byte.Format' => {
+ create_order => 2,
+ create_sql =>
+ # configuration parameters get cased folded
+ 'GRANT ALTER SYSTEM ON MyReallyLong.ButValidCustom.GucNameThatCannotFit.InNamedata64Byte.Format TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM ON myreallylong\.butvalidcustom\.gucnamethatcannotfit\.innamedata64byte\.format TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALTER SYSTEM, SET VALUE ON my.guc TO regress_dump_test_role GRANTED BY CURRENT_ROLE' => {
+ create_order => 2,
+ create_sql =>
+ # GRANTED BY CURRENT_ROLE is allowed for SQL compatibility, but is ignored
+ 'GRANT ALTER SYSTEM, SET VALUE ON my.guc TO regress_dump_test_role GRANTED BY CURRENT_ROLE;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM, SET VALUE ON my\.guc TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
'CREATE SCHEMA public' => {
regexp => qr/^CREATE SCHEMA public;/m,
like => {
diff --git a/src/test/regress/expected/guc_privs.out b/src/test/regress/expected/guc_privs.out
new file mode 100644
index 0000000000..ae02b3fe77
--- /dev/null
+++ b/src/test/regress/expected/guc_privs.out
@@ -0,0 +1,204 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform all 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 = 'en_US.UTF-8'; -- ok
+RESET lc_messages; -- ok
+ALTER SYSTEM SET lc_messages = 'en_US.UTF-8'; -- 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;
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_admin;
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- 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
+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
+ALTER SYSTEM RESET autovacuum_work_mem; -- ok, privileges have been granted
+RESET TimeZone; -- ok
+RESET SESSION AUTHORIZATION;
+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 configuration parameter work_mem
+privileges for configuration parameter maintenance_work_mem
+privileges for configuration parameter autovacuum_work_mem
+privileges for configuration parameter hash_mem_multiplier
+privileges for configuration parameter logical_decoding_work_mem
+privileges for configuration parameter max_stack_depth
+privileges for configuration parameter min_dynamic_shared_memory
+privileges for configuration parameter shared_buffers
+privileges for configuration parameter temp_buffers
+privileges for configuration parameter temp_file_limit
+-- Use "revoke" to remove the privileges and allow the role to be dropped
+REVOKE SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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"
+RESET SESSION AUTHORIZATION;
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 configuration parameter work_mem
+privileges for configuration parameter maintenance_work_mem
+privileges for configuration parameter autovacuum_work_mem
+privileges for configuration parameter hash_mem_multiplier
+privileges for configuration parameter logical_decoding_work_mem
+privileges for configuration parameter max_stack_depth
+privileges for configuration parameter min_dynamic_shared_memory
+privileges for configuration parameter shared_buffers
+privileges for configuration parameter temp_buffers
+privileges for configuration 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"
+RESET SESSION AUTHORIZATION;
+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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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
+RESET SESSION AUTHORIZATION;
+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 configuration parameter work_mem
+privileges for configuration parameter maintenance_work_mem
+privileges for configuration parameter autovacuum_work_mem
+privileges for configuration parameter hash_mem_multiplier
+privileges for configuration parameter logical_decoding_work_mem
+privileges for configuration parameter max_stack_depth
+privileges for configuration parameter min_dynamic_shared_memory
+privileges for configuration parameter shared_buffers
+privileges for configuration parameter temp_buffers
+privileges for configuration 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
+-- Create non-superuser with privileges to configure plgsql custom variables
+CREATE ROLE regress_plpgsql_admin NOSUPERUSER;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin;
+SET plpgsql.extra_errors TO 'all';
+SET plpgsql.extra_warnings TO 'all';
+RESET plpgsql.extra_errors;
+RESET plpgsql.extra_warnings;
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all';
+ERROR: permission denied to set parameter "plpgsql.extra_errors"
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all';
+ERROR: permission denied to set parameter "plpgsql.extra_warnings"
+ALTER SYSTEM RESET plpgsql.extra_errors;
+ERROR: permission denied to set parameter "plpgsql.extra_errors"
+ALTER SYSTEM RESET plpgsql.extra_warnings;
+ERROR: permission denied to set parameter "plpgsql.extra_warnings"
+RESET SESSION AUTHORIZATION;
+GRANT SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+TO regress_plpgsql_admin;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin; -- ok
+SET plpgsql.extra_errors TO 'all'; -- ok
+SET plpgsql.extra_warnings TO 'all'; -- ok
+RESET plpgsql.extra_errors; -- ok
+RESET plpgsql.extra_warnings; -- ok
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all'; -- ok
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all'; -- ok
+ALTER SYSTEM RESET plpgsql.extra_errors; -- ok
+ALTER SYSTEM RESET plpgsql.extra_warnings; -- ok
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_plpgsql_admin; -- fail, privileges remain
+ERROR: role "regress_plpgsql_admin" cannot be dropped because some objects depend on it
+DETAIL: privileges for configuration parameter plpgsql.extra_warnings
+privileges for configuration parameter plpgsql.extra_errors
+REVOKE SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+FROM regress_plpgsql_admin;
+DROP ROLE regress_plpgsql_admin; -- ok
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 9b91865dcc..ef4197c0f3 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -31,6 +31,34 @@ CREATE USER regress_priv_user6;
CREATE USER regress_priv_user7;
GRANT pg_read_all_data TO regress_priv_user6;
GRANT pg_write_all_data TO regress_priv_user7;
+GRANT SET VALUE ON enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON enable_nestloop TO regress_priv_user6;
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+GRANT ALTER SYSTEM ON enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON sort_mem TO regress_priv_user7; -- old name for "work_mem"
+NOTICE: granting privileges on canonical configuration parameter name "work_mem"
+GRANT ALTER SYSTEM ON maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON "" TO regress_priv_user7; -- bad name
+ERROR: zero-length delimited identifier at or near """"
+LINE 1: GRANT ALTER SYSTEM ON "" TO regress_priv_user7;
+ ^
+GRANT ALTER SYSTEM ON " " TO regress_priv_user7; -- bad name
+ERROR: invalid configuration parameter name " "
+GRANT ALTER SYSTEM ON " foo " TO regress_priv_user7; -- bad name
+ERROR: invalid configuration parameter name " foo "
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
ALTER GROUP regress_priv_group1 ADD USER regress_priv_user4;
@@ -2326,10 +2354,32 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+ERROR: role "regress_priv_user6" cannot be dropped because some objects depend on it
+DETAIL: privileges for configuration parameter enable_memoize
+privileges for configuration parameter enable_nestloop
+DROP USER regress_priv_user7; -- privileges remain
+ERROR: role "regress_priv_user7" cannot be dropped because some objects depend on it
+DETAIL: privileges for table persons2
+privileges for configuration parameter enable_seqscan
+privileges for configuration parameter work_mem
+privileges for configuration parameter maintenance_work_mem
+privileges for configuration parameter no_such_param
+privileges for configuration parameter no_such_extension.no_such_param
+privileges for configuration parameter no_such_extension.no_such_param.longer.than.maximum.namedata.length
DROP USER regress_priv_user8; -- does not exist
ERROR: role "regress_priv_user8" does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+DROP USER regress_priv_user7; -- ok
+REVOKE SET VALUE ON enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON enable_nestloop FROM regress_priv_user6;
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
CREATE TABLE lock_table (a int);
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 63706a28cc..aff31f0d24 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -113,6 +113,7 @@ pg_authid|t
pg_cast|t
pg_class|t
pg_collation|t
+pg_config_param|t
pg_constraint|t
pg_conversion|t
pg_database|t
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 017e962fed..b45a203eb6 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -86,7 +86,7 @@ test: brin_bloom brin_multi
# ----------
# Another group of parallel tests
# ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort guc_privs
# rules cannot run concurrently with any test that creates
# a view or rule in the public schema
diff --git a/src/test/regress/sql/guc_privs.sql b/src/test/regress/sql/guc_privs.sql
new file mode 100644
index 0000000000..e5dbc7ad3b
--- /dev/null
+++ b/src/test/regress/sql/guc_privs.sql
@@ -0,0 +1,142 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform all 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 = 'en_US.UTF-8'; -- ok
+RESET lc_messages; -- ok
+ALTER SYSTEM SET lc_messages = 'en_US.UTF-8'; -- 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;
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_admin;
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- 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
+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
+ALTER SYSTEM RESET autovacuum_work_mem; -- ok, privileges have been granted
+RESET TimeZone; -- ok
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_host_resource_admin; -- fail, privileges remain
+-- Use "revoke" to remove the privileges and allow the role to be dropped
+REVOKE SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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
+RESET SESSION AUTHORIZATION;
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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
+RESET SESSION AUTHORIZATION;
+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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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
+RESET SESSION AUTHORIZATION;
+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
+-- Create non-superuser with privileges to configure plgsql custom variables
+CREATE ROLE regress_plpgsql_admin NOSUPERUSER;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin;
+SET plpgsql.extra_errors TO 'all';
+SET plpgsql.extra_warnings TO 'all';
+RESET plpgsql.extra_errors;
+RESET plpgsql.extra_warnings;
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all';
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all';
+ALTER SYSTEM RESET plpgsql.extra_errors;
+ALTER SYSTEM RESET plpgsql.extra_warnings;
+RESET SESSION AUTHORIZATION;
+GRANT SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+TO regress_plpgsql_admin;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin; -- ok
+SET plpgsql.extra_errors TO 'all'; -- ok
+SET plpgsql.extra_warnings TO 'all'; -- ok
+RESET plpgsql.extra_errors; -- ok
+RESET plpgsql.extra_warnings; -- ok
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all'; -- ok
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all'; -- ok
+ALTER SYSTEM RESET plpgsql.extra_errors; -- ok
+ALTER SYSTEM RESET plpgsql.extra_warnings; -- ok
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_plpgsql_admin; -- fail, privileges remain
+REVOKE SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+FROM regress_plpgsql_admin;
+DROP ROLE regress_plpgsql_admin; -- ok
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index 6353a1cb8c..eee18e7ba3 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -36,6 +36,33 @@ CREATE USER regress_priv_user7;
GRANT pg_read_all_data TO regress_priv_user6;
GRANT pg_write_all_data TO regress_priv_user7;
+GRANT SET VALUE ON enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON enable_nestloop TO regress_priv_user6;
+
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+
+GRANT ALTER SYSTEM ON enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON "" TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON " " TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON " foo " TO regress_priv_user7; -- bad name
+
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
+
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
@@ -1389,10 +1416,23 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+DROP USER regress_priv_user7; -- privileges remain
DROP USER regress_priv_user8; -- does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+DROP USER regress_priv_user7; -- ok
+
+REVOKE SET VALUE ON enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON enable_nestloop FROM regress_priv_user6;
+
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
--
2.21.1 (Apple Git-122.3)
On Mon, Nov 22, 2021 at 7:21 PM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:
There is a new information_schema.guc_privileges view, not present in v2.
It's my impression that information_schema is a child of the SQL
standard, and that inventions specific to PG go in pg_catalog.
Also, I think the user-facing name for GUCs is "settings".
--
Robert Haas
EDB: http://www.enterprisedb.com
On Nov 23, 2021, at 8:07 AM, Robert Haas <robertmhaas@gmail.com> wrote:
It's my impression that information_schema is a child of the SQL
standard, and that inventions specific to PG go in pg_catalog.Also, I think the user-facing name for GUCs is "settings".
Thanks. These issues should be fixed in v4.
Along the way, I also added has_setting_privilege() functions overlooked in v3 and before.
Attachments:
v4-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-for-variables.patchapplication/octet-stream; name=v4-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-for-variables.patch; x-unix-mode=0644Download
From 0ecf153befc88b171b0a2d700995340095e693c0 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Mon, 15 Nov 2021 09:19:22 -0800
Subject: [PATCH v4] Allow GRANT of SET and ALTER SYSTEM for variables
Allow granting of privilege to set or alter system set variables
which otherwise can only be managed by superusers. Each
(role,variable,privilege) triple is independently grantable, so a
user may be granted privilege to SET but not to ALTER SYSTEM SET on
a variable, or vice versa. The privilege to SET a userset variable
may be granted, though doing so has no practical effect, since any
role can set userset variables anyway.
---
doc/src/sgml/catalogs.sgml | 63 ++++
doc/src/sgml/ddl.sgml | 39 ++-
doc/src/sgml/func.sgml | 20 +-
doc/src/sgml/ref/grant.sgml | 7 +
doc/src/sgml/ref/set.sgml | 5 +-
src/backend/catalog/Makefile | 3 +-
src/backend/catalog/aclchk.c | 251 ++++++++++++++
src/backend/catalog/catalog.c | 6 +
src/backend/catalog/dependency.c | 6 +
src/backend/catalog/objectaddress.c | 46 +++
src/backend/catalog/pg_setting_acl.c | 126 +++++++
src/backend/catalog/system_views.sql | 14 +
src/backend/commands/alter.c | 1 +
src/backend/commands/dropcmds.c | 7 +
src/backend/commands/event_trigger.c | 6 +
src/backend/commands/seclabel.c | 1 +
src/backend/commands/tablecmds.c | 1 +
src/backend/parser/gram.y | 99 +++++-
src/backend/utils/adt/acl.c | 258 ++++++++++++++
src/backend/utils/cache/lsyscache.c | 20 ++
src/backend/utils/cache/syscache.c | 23 ++
src/backend/utils/misc/guc.c | 108 +++++-
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 | 71 ++++
src/include/catalog/dependency.h | 1 +
src/include/catalog/pg_default_acl.h | 1 +
src/include/catalog/pg_proc.dat | 19 ++
src/include/catalog/pg_setting_acl.h | 63 ++++
src/include/nodes/parsenodes.h | 5 +-
src/include/parser/kwlist.h | 1 +
src/include/utils/acl.h | 10 +-
src/include/utils/guc.h | 2 +
src/include/utils/lsyscache.h | 1 +
src/include/utils/syscache.h | 2 +
src/test/modules/test_pg_dump/t/001_base.pl | 54 ++-
src/test/regress/expected/guc_privs.out | 360 ++++++++++++++++++++
src/test/regress/expected/privileges.out | 54 ++-
src/test/regress/expected/rules.out | 13 +
src/test/regress/expected/sanity_check.out | 1 +
src/test/regress/parallel_schedule | 2 +-
src/test/regress/sql/guc_privs.sql | 179 ++++++++++
src/test/regress/sql/privileges.sql | 44 ++-
44 files changed, 1969 insertions(+), 38 deletions(-)
create mode 100644 src/backend/catalog/pg_setting_acl.c
create mode 100644 src/include/catalog/pg_setting_acl.h
create mode 100644 src/test/regress/expected/guc_privs.out
create mode 100644 src/test/regress/sql/guc_privs.sql
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index c1d11be73f..54cd837f37 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -105,6 +105,11 @@
<entry>collations (locale information)</entry>
</row>
+ <row>
+ <entry><link linkend="catalog-pg-setting-acl"><structname>pg_setting_acl</structname></link></entry>
+ <entry>configuration parameters which have privileges granted to roles</entry>
+ </row>
+
<row>
<entry><link linkend="catalog-pg-constraint"><structname>pg_constraint</structname></link></entry>
<entry>check constraints, unique constraints, primary key constraints, foreign key constraints</entry>
@@ -2423,6 +2428,64 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</para>
</sect1>
+ <sect1 id="catalog-pg-setting-acl">
+ <title><structname>pg_setting_acl</structname></title>
+
+ <indexterm zone="catalog-pg-setting-acl">
+ <primary>pg_setting_acl</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_setting_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_setting_acl</structname>
+ is shared across all databases of a cluster: there is only one
+ copy of <structname>pg_setting_acl</structname> per cluster, not
+ one per database.
+ </para>
+
+ <table>
+ <title><structname>pg_setting_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>setting</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>
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 94f745aed0..d862ab8c0e 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -1620,7 +1620,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 VALUE</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.
@@ -1888,6 +1889,26 @@ REVOKE ALL ON accounts FROM PUBLIC;
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>SET VALUE</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>SET</command> command to
+ change run-time configuration parameters.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ALTER SYSTEM</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>ALTER SYSTEM
+ SET</command> command to change server configuration parameters.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
The privileges required by other commands are listed on the
@@ -2026,6 +2047,16 @@ REVOKE ALL ON accounts FROM PUBLIC;
<literal>TYPE</literal>
</entry>
</row>
+ <row>
+ <entry><literal>SET VALUE</literal></entry>
+ <entry><literal>s</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
+ <row>
+ <entry><literal>ALTER SYSTEM</literal></entry>
+ <entry><literal>A</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -2132,6 +2163,12 @@ REVOKE ALL ON accounts FROM PUBLIC;
<entry><literal>U</literal></entry>
<entry><literal>\dT+</literal></entry>
</row>
+ <row>
+ <entry><literal>Configuration parameter</literal></entry>
+ <entry><literal>sA</literal></entry>
+ <entry>none</entry>
+ <entry><literal></literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 74d3087a72..6574eadcd7 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -22614,8 +22614,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');
@@ -22880,6 +22879,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_setting_privilege</primary>
+ </indexterm>
+ <function>has_setting_privilege</function> (
+ <optional> <parameter>user</parameter> <type>name</type> or <type>oid</type>, </optional>
+ <parameter>setting</parameter> <type>text</type> or <type>oid</type>,
+ <parameter>privilege</parameter> <type>text</type> )
+ <returnvalue>boolean</returnvalue>
+ </para>
+ <para>
+ Does user have privilege for setting?
+ Allowable privilege types are <literal>SET VALUE</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 a897712de2..21ec320a8d 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 VALUE | ALTER SYSTEM }
+ ON <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 VALUE</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/set.sgml b/doc/src/sgml/ref/set.sgml
index 339ee9eec9..a08057d1d1 100644
--- a/doc/src/sgml/ref/set.sgml
+++ b/doc/src/sgml/ref/set.sgml
@@ -34,8 +34,9 @@ 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.)
+ (But some require either superuser privileges or granted <literal>SET
+ VALUE</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>
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 4e6efda97f..b88f69e3a4 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -28,6 +28,7 @@ OBJS = \
pg_cast.o \
pg_class.o \
pg_collation.o \
+ pg_setting_acl.o \
pg_constraint.o \
pg_conversion.o \
pg_db_role_setting.o \
@@ -54,7 +55,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_setting_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 ce0a4ff14e..02c15853e5 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_setting_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_Setting(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_SETTING:
+ whole_mask = ACL_ALL_RIGHTS_SETTING;
+ 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_SETTING:
+ all_privileges = ACL_ALL_RIGHTS_SETTING;
+ errormsg = gettext_noop("invalid privilege type %s for setting");
+ 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_SETTING:
+ ExecGrant_Setting(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -759,6 +771,38 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_SETTING:
+ foreach(cell, objnames)
+ {
+ char *setting = strVal(lfirst(cell));
+ Oid settingid = get_setting_oid(setting, true);
+
+ if (!OidIsValid(settingid))
+ {
+ /*
+ * Lookup the existing entry, or if necessary, add a new
+ * entry for this parameter. Entries only exist for
+ * parameters which currently have, or previously have had,
+ * privileges assigned.
+ *
+ * It is tempting to sanity-check the given configuration
+ * parameter name against known guc names in the guc
+ * tables, but for handling upgrades we need to accept
+ * setting names that do not yet exist.
+ */
+ settingid = SettingAclCreate(setting, 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, settingid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -1494,6 +1538,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case ForeignDataWrapperRelationId:
istmt.objtype = OBJECT_FDW;
break;
+ case SettingAclRelationId:
+ istmt.objtype = OBJECT_SETTING;
+ break;
default:
elog(ERROR, "unexpected object class %u", classid);
break;
@@ -3225,6 +3272,128 @@ ExecGrant_Type(InternalGrant *istmt)
table_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Setting(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_SETTING;
+
+ relation = table_open(SettingAclRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid settingId = lfirst_oid(cell);
+ Form_pg_setting_acl pg_setting_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_setting_acl];
+ bool nulls[Natts_pg_setting_acl];
+ bool replaces[Natts_pg_setting_acl];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(settingId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for setting %u", settingId);
+
+ pg_setting_acl_tuple = (Form_pg_setting_acl) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ aclDatum = SysCacheGetAttr(SETTINGNAME, tuple, Anum_pg_setting_acl_setacl,
+ &isNull);
+ if (isNull)
+ {
+ old_acl = acldefault(OBJECT_SETTING, InvalidOid);
+ /* 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, GetUserId(),
+ &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,
+ settingId, grantorId, OBJECT_SETTING,
+ text_to_cstring(&pg_setting_acl_tuple->setting),
+ 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_setting_acl_setacl - 1] = true;
+ values[Anum_pg_setting_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(settingId, SettingAclRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(SettingAclRelationId,
+ pg_setting_acl_tuple->oid, 0,
+ InvalidOid,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ table_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3255,6 +3424,10 @@ string_to_privilege(const char *privname)
return ACL_CREATE_TEMP;
if (strcmp(privname, "connect") == 0)
return ACL_CONNECT;
+ if (strcmp(privname, "set value") == 0)
+ return ACL_SET_VALUE;
+ 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 +3465,10 @@ privilege_to_string(AclMode privilege)
return "TEMP";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET_VALUE:
+ return "SET VALUE";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3328,6 +3505,13 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_COLUMN:
msg = gettext_noop("permission denied for column %s");
break;
+ case OBJECT_SETTING:
+ /*
+ * 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 +3748,7 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_AMPROC:
case OBJECT_ATTRIBUTE:
case OBJECT_CAST:
+ case OBJECT_SETTING:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
case OBJECT_DOMCONSTRAINT:
@@ -4000,6 +4185,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_setting_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 setting's ACL from pg_setting_acl
+ */
+ tuple = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(config_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("setting with OID %u does not exist",
+ config_oid)));
+
+ aclDatum = SysCacheGetAttr(SETTINGOID, tuple, Anum_pg_setting_acl_setacl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_SETTING, 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 +4951,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_setting_acl_aclcheck(Oid config_oid, Oid roleid, AclMode mode)
+{
+ if (pg_setting_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 aa7d4d5456..a069c2fb39 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_setting_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 == SettingAclRelationId ||
relationId == DatabaseRelationId ||
relationId == SharedDescriptionRelationId ||
relationId == SharedDependRelationId ||
@@ -260,6 +262,8 @@ IsSharedRelation(Oid relationId)
relationId == AuthIdOidIndexId ||
relationId == AuthMemRoleMemIndexId ||
relationId == AuthMemMemRoleIndexId ||
+ relationId == SettingAclSettingIndexId ||
+ relationId == SettingAclOidIndexId ||
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 == PgSettingAclToastTable ||
+ relationId == PgSettingAclToastIndex ||
relationId == PgDatabaseToastTable ||
relationId == PgDatabaseToastIndex ||
relationId == PgDbRoleSettingToastTable ||
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index fe9c714257..57174b7c19 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_setting_acl.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
@@ -150,6 +151,7 @@ static const Oid object_classes[] = {
TypeRelationId, /* OCLASS_TYPE */
CastRelationId, /* OCLASS_CAST */
CollationRelationId, /* OCLASS_COLLATION */
+ SettingAclRelationId, /* OCLASS_SETTING */
ConstraintRelationId, /* OCLASS_CONSTRAINT */
ConversionRelationId, /* OCLASS_CONVERSION */
AttrDefaultRelationId, /* OCLASS_DEFAULT */
@@ -1504,6 +1506,7 @@ doDeletion(const ObjectAddress *object, int flags)
/*
* These global object types are not supported here.
*/
+ case OCLASS_SETTING:
case OCLASS_ROLE:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
@@ -2778,6 +2781,9 @@ getObjectClass(const ObjectAddress *object)
case CollationRelationId:
return OCLASS_COLLATION;
+ case SettingAclRelationId:
+ return OCLASS_SETTING;
+
case ConstraintRelationId:
return OCLASS_CONSTRAINT;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 2bae3fbb17..682ae5e390 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_setting_acl.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
@@ -2309,6 +2310,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_COLUMN:
case OBJECT_ATTRIBUTE:
case OBJECT_COLLATION:
+ case OBJECT_SETTING:
case OBJECT_CONVERSION:
case OBJECT_STATISTIC_EXT:
case OBJECT_TSPARSER:
@@ -3510,6 +3512,22 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
break;
}
+ case OCLASS_SETTING:
+ {
+ char *setting;
+
+ setting = get_setting_name(object->objectId);
+ if (!setting)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for setting %u",
+ object->objectId);
+ break;
+ }
+ appendStringInfo(&buffer, _("setting %s"), setting);
+ break;
+ }
+
case OCLASS_SCHEMA:
{
char *nspname;
@@ -4473,6 +4491,10 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "collation");
break;
+ case OCLASS_SETTING:
+ appendStringInfoString(&buffer, "setting");
+ break;
+
case OCLASS_CONSTRAINT:
getConstraintTypeDescription(&buffer, object->objectId,
missing_ok);
@@ -4977,6 +4999,30 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_SETTING:
+ {
+ HeapTuple configTup;
+ Form_pg_setting_acl configForm;
+ char *namestr;
+
+ configTup = SearchSysCache1(SETTINGOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(configTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for setting %u",
+ object->objectId);
+ break;
+ }
+ configForm = (Form_pg_setting_acl) GETSTRUCT(configTup);
+ namestr = text_to_cstring(&configForm->setting);
+ appendStringInfoString(&buffer, namestr);
+ if (objname)
+ *objname = list_make1(namestr);
+ ReleaseSysCache(configTup);
+ break;
+ }
+
case OCLASS_CONVERSION:
{
HeapTuple conTup;
diff --git a/src/backend/catalog/pg_setting_acl.c b/src/backend/catalog/pg_setting_acl.c
new file mode 100644
index 0000000000..671f390897
--- /dev/null
+++ b/src/backend/catalog/pg_setting_acl.c
@@ -0,0 +1,126 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_setting_acl.c
+ * routines to support manipulation of the pg_setting_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_setting_acl.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/sysattr.h"
+#include "access/table.h"
+#include "access/tableam.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_setting_acl.h"
+#include "mb/pg_wchar.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/guc.h"
+#include "utils/pg_locale.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * SettingAclCreate
+ *
+ * Add a new tuple to pg_setting_acl.
+ *
+ * setting: the setting name to create.
+ * if_not_exists: if true, don't fail on duplicate name, but rather return
+ * the existing entry's Oid.
+ */
+Oid
+SettingAclCreate(const char *setting, bool if_not_exists)
+{
+ Relation rel;
+ TupleDesc tupDesc;
+ HeapTuple tuple;
+ Datum values[Natts_pg_setting_acl];
+ bool nulls[Natts_pg_setting_acl];
+ Oid settingId;
+ const char *canonical;
+
+ /*
+ * Check whether the setting (by the given name or alias)
+ * already exists.
+ */
+ settingId = get_setting_oid(setting, true);
+ if (OidIsValid(settingId))
+ {
+ if (if_not_exists)
+ return settingId;
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("setting \"%s\" already exists",
+ setting)));
+ }
+
+ /*
+ * We must not require the setting to be in the list of existent GUCs,
+ * as we may be called at different points during upgrades or the
+ * installation or removal of extensions. Instead, perform a basic sanity
+ * check of the setting, 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_setting_acl entries.
+ */
+ if (!valid_variable_name(setting, NULL))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid setting name \"%s\"",
+ setting)));
+ canonical = GetConfigOptionCanonicalName(setting);
+ if (!canonical)
+ canonical = setting;
+ else if (strcmp(canonical, setting) != 0)
+ ereport(NOTICE,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("granting privileges on canonical setting name \"%s\"", canonical)));
+
+ /*
+ * 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(SettingAclRelationId, RowExclusiveLock);
+ tupDesc = RelationGetDescr(rel);
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ values[Anum_pg_setting_acl_setting - 1] =
+ DirectFunctionCall1(textin, CStringGetDatum(canonical));
+ settingId = GetNewOidWithIndex(rel,
+ SettingAclOidIndexId,
+ Anum_pg_setting_acl_oid);
+ values[Anum_pg_setting_acl_oid - 1] = ObjectIdGetDatum(settingId);
+ nulls[Anum_pg_setting_acl_setacl - 1] = true;
+ tuple = heap_form_tuple(tupDesc, values, nulls);
+ CatalogTupleInsert(rel, tuple);
+
+ /* Post creation hook for new setting */
+ InvokeObjectPostCreateHook(SettingAclRelationId, settingId, 0);
+
+ /*
+ * Close pg_setting_acl, but keep lock till commit.
+ */
+ heap_freetuple(tuple);
+ table_close(rel, NoLock);
+
+ return settingId;
+}
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index eb560955cd..781ba185de 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -593,6 +593,20 @@ CREATE RULE pg_settings_n AS
GRANT SELECT, UPDATE ON pg_settings TO PUBLIC;
+CREATE VIEW pg_setting_privileges AS
+ SELECT grantor.rolname AS grantor,
+ grantee.rolname AS grantee,
+ set_acl.setting AS setting,
+ acl.privilege_type AS privilege_type,
+ acl.is_grantable
+ FROM pg_catalog.pg_setting_acl set_acl,
+ LATERAL (SELECT * FROM aclexplode(set_acl.setacl)) acl
+ 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_setting_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 40044070cf..7da2e62683 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -639,6 +639,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
break;
case OCLASS_CAST:
+ case OCLASS_SETTING:
case OCLASS_CONSTRAINT:
case OCLASS_DEFAULT:
case OCLASS_LANGUAGE:
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 4e545adf95..45bc1c1948 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -276,6 +276,13 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
name = NameListToString(castNode(List, object));
}
break;
+ case OBJECT_SETTING:
+ if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
+ {
+ msg = gettext_noop("setting \"%s\" does not exist, skipping");
+ name = NameListToString(castNode(List, object));
+ }
+ break;
case OBJECT_CONVERSION:
if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
{
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index df264329d8..d6df5b3ad9 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_SETTING:
case OBJECT_DATABASE:
case OBJECT_TABLESPACE:
case OBJECT_ROLE:
@@ -1012,6 +1013,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
{
switch (objclass)
{
+ case OCLASS_SETTING:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
@@ -2078,6 +2080,8 @@ stringify_grant_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMN";
+ case OBJECT_SETTING:
+ return "SETTING";
case OBJECT_TABLE:
return "TABLE";
case OBJECT_SEQUENCE:
@@ -2161,6 +2165,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMNS";
+ case OBJECT_SETTING:
+ return "SETTINGS";
case OBJECT_TABLE:
return "TABLES";
case OBJECT_SEQUENCE:
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 53c18628a7..8f6f9bd7a0 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_SETTING:
case OBJECT_CONVERSION:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 785b282e69..3cd49c5232 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -12262,6 +12262,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_TYPE:
case OCLASS_CAST:
case OCLASS_COLLATION:
+ case OCLASS_SETTING:
case OCLASS_CONVERSION:
case OCLASS_LANGUAGE:
case OCLASS_LARGEOBJECT:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a6d0cefa6b..31e25df9d4 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -348,8 +348,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 setting_name
+%type <list> OptSchemaEltList setting_target
%type <chr> am_type
@@ -387,8 +387,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> iso_level opt_encoding
%type <rolespec> grantee
%type <list> grantee_list
-%type <accesspriv> privilege
-%type <list> privileges privilege_list
+%type <accesspriv> privilege setting_priv
+%type <list> privileges privilege_list setting_priv_list
%type <privtarget> privilege_target
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
@@ -698,7 +698,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
@@ -6884,6 +6884,20 @@ GrantStmt: GRANT privileges ON privilege_target TO grantee_list
n->grantor = $8;
$$ = (Node*)n;
}
+ | GRANT setting_priv_list ON setting_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_SETTING;
+ n->objects = $4;
+ n->grantees = $6;
+ n->grant_option = $7;
+ n->grantor = $8;
+ $$ = (Node*)n;
+ }
;
RevokeStmt:
@@ -6917,6 +6931,36 @@ RevokeStmt:
n->behavior = $11;
$$ = (Node *)n;
}
+ | REVOKE setting_priv_list ON setting_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_SETTING;
+ n->objects = $4;
+ n->grantees = $6;
+ n->grantor = $7;
+ n->behavior = $8;
+ $$ = (Node *)n;
+ }
+ | REVOKE GRANT OPTION FOR setting_priv_list ON setting_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_SETTING;
+ n->objects = $7;
+ n->grantees = $9;
+ n->grantor = $10;
+ n->behavior = $11;
+ $$ = (Node *)n;
+ }
;
@@ -6985,6 +7029,49 @@ privilege: SELECT opt_column_list
}
;
+setting_priv_list: setting_priv { $$ = list_make1($1); }
+ | setting_priv_list ',' setting_priv { $$ = lappend($1, $3); }
+ ;
+
+setting_priv:
+ ALTER SYSTEM_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("alter system");
+ n->cols = NIL;
+ $$ = n;
+ }
+ | SET VALUE_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("set value");
+ n->cols = NIL;
+ $$ = n;
+ }
+ ;
+
+
+setting_target:
+ setting_name
+ {
+ $$ = list_make1(makeString($1));
+ }
+ | setting_target ',' setting_name
+ {
+ $$ = lappend($1, makeString($3));
+ }
+ ;
+
+setting_name:
+ ColId
+ {
+ $$ = $1;
+ }
+ | setting_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.
@@ -15728,6 +15815,7 @@ unreserved_keyword:
| OWNED
| OWNER
| PARALLEL
+ | PARAMETER
| PARSER
| PARTIAL
| PARTITION
@@ -16306,6 +16394,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 67f8b29434..f0dd3e52ed 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_setting_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,8 @@ 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 Oid convert_setting_name(text *setting);
+static AclMode convert_setting_priv_string(text *priv_setting_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 +310,12 @@ aclparse(const char *s, AclItem *aip)
case ACL_CONNECT_CHR:
read = ACL_CONNECT;
break;
+ case ACL_SET_VALUE_CHR:
+ read = ACL_SET_VALUE;
+ break;
+ case ACL_ALTER_SYSTEM_CHR:
+ read = ACL_ALTER_SYSTEM;
+ break;
case 'R': /* ignore old RULE privileges */
read = 0;
break;
@@ -794,6 +804,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_SETTING:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_NO_RIGHTS;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -1602,6 +1616,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 VALUE") == 0)
+ return ACL_SET_VALUE;
+ 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 +1716,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET_VALUE:
+ return "SET VALUE";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
@@ -4429,6 +4451,205 @@ convert_type_priv_string(text *priv_type_text)
return convert_any_priv_string(priv_type_text, type_priv_map);
}
+/*
+ * has_setting_privilege variants
+ * These are all named "has_setting_privilege" at the SQL level.
+ * They take various combinations of setting name, setting 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_setting_privilege_name_name
+ * Check user privileges on a setting given
+ * name username, text setting, and text priv name.
+ */
+Datum
+has_setting_privilege_name_name(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ text *setting = PG_GETARG_TEXT_PP(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid roleid;
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_role_oid_or_public(NameStr(*username));
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_name
+ * Check user privileges on a setting given
+ * text setting and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_setting_privilege_name(PG_FUNCTION_ARGS)
+{
+ text *setting = PG_GETARG_TEXT_PP(0);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(1);
+ Oid roleid;
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_name_id
+ * Check user privileges on a setting given
+ * name usename, setting oid, and text priv name.
+ */
+Datum
+has_setting_privilege_name_id(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ Oid settingoid = PG_GETARG_OID(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_role_oid_or_public(NameStr(*username));
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id
+ * Check user privileges on a setting given
+ * setting oid, and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_setting_privilege_id(PG_FUNCTION_ARGS)
+{
+ Oid settingoid = PG_GETARG_OID(0);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(1);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id_name
+ * Check user privileges on a setting given
+ * roleid, text setting, and text priv name.
+ */
+Datum
+has_setting_privilege_id_name(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ text *setting = PG_GETARG_TEXT_PP(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id_id
+ * Check user privileges on a setting given
+ * roleid, setting oid, and text priv name.
+ */
+Datum
+has_setting_privilege_id_id(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ Oid settingoid = PG_GETARG_OID(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ AclMode mode;
+ AclResult aclresult;
+
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * Support routines for has_setting_privilege family.
+ */
+
+/*
+ * Given a setting name expressed as a string, look it up and return Oid
+ */
+static Oid
+convert_setting_name(text *settingname)
+{
+ Oid oid;
+ char *setting = text_to_cstring(settingname);
+
+ oid = get_setting_oid(setting, true);
+
+ if (!OidIsValid(oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("setting \"%s\" does not exist", setting)));
+
+ return oid;
+}
+
+/*
+ * convert_setting_priv_string
+ * Convert text string to AclMode value.
+ */
+static AclMode
+convert_setting_priv_string(text *priv_setting_text)
+{
+ static const priv_map setting_priv_map[] = {
+ {"SET VALUE", ACL_SET_VALUE},
+ {"SET VALUE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SET_VALUE)},
+ {"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_setting_text, setting_priv_map);
+}
/*
* pg_has_role variants
@@ -4670,6 +4891,43 @@ initialize_acl(void)
}
}
+/*
+ * get_setting_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_setting_oid(const char *setting, bool missing_ok)
+{
+ Oid oid;
+
+ /* Check for the variable by the name we were given */
+ oid = GetSysCacheOid1(SETTINGNAME, Anum_pg_setting_acl_oid,
+ PointerGetDatum(cstring_to_text(setting)));
+ if (!OidIsValid(oid))
+ {
+ const char *canonical;
+
+ /* Check if the variable has a different canonical spelling */
+ canonical = GetConfigOptionCanonicalName(setting);
+ if (canonical != NULL)
+ oid = GetSysCacheOid1(SETTINGNAME, Anum_pg_setting_acl_oid,
+ PointerGetDatum(cstring_to_text(canonical)));
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("setting \"%s\" does not exist", setting)));
+ }
+
+ return oid;
+}
+
/*
* RoleMembershipCacheCallback
* Syscache inval callback function
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 4ebaa552a2..4979808ce7 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_setting_acl.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_transform.h"
#include "catalog/pg_type.h"
@@ -3304,6 +3305,25 @@ free_attstatsslot(AttStatsSlot *sslot)
pfree(sslot->numbers_arr);
}
+char *
+get_setting_name(Oid configid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(configid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_setting_acl configtup = (Form_pg_setting_acl) GETSTRUCT(tp);
+ char *result;
+
+ result = pstrdup(text_to_cstring(&configtup->setting));
+ 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 56870b46e4..00fbe5218d 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_setting_acl.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
#include "catalog/pg_shseclabel.h"
@@ -762,6 +763,28 @@ static const struct cachedesc cacheinfo[] = {
},
32
},
+ {SettingAclRelationId, /* SETTINGNAME */
+ SettingAclSettingIndexId,
+ 1,
+ {
+ Anum_pg_setting_acl_setting,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+ {SettingAclRelationId, /* SETTINGOID */
+ SettingAclOidIndexId,
+ 1,
+ {
+ Anum_pg_setting_acl_oid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{StatisticExtDataRelationId, /* STATEXTDATASTXOID */
StatisticExtDataStxoidIndexId,
1,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index e91d5a3cfd..69fe16a64c 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -5021,6 +5021,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_setting_acl
+ * entries.
*/
static const char *const map_old_guc_names[] = {
"sort_mem", "work_mem",
@@ -5420,25 +5424,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().)
+ *
+ * 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"
@@ -5455,8 +5463,24 @@ 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;
+}
+
+/*
+ * 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().)
+ */
+static bool
+valid_custom_variable_name(const char *name)
+{
+ int partcnt;
+
+ return (valid_variable_name(name, &partcnt) && partcnt > 1);
}
/*
@@ -8533,16 +8557,32 @@ 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;
+ Oid settingId;
+
+ settingId = get_setting_oid(name, true);
+ if (!OidIsValid(settingId))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+
+ aclresult = pg_setting_acl_aclcheck(settingId, GetUserId(),
+ ACL_ALTER_SYSTEM);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_SETTING, name);
+ }
+
switch (altersysstmt->setstmt->kind)
{
case VAR_SET_VALUE:
@@ -8735,6 +8775,9 @@ void
ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
{
GucAction action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET;
+ GucContext context;
+ AclResult aclresult;
+ Oid settingId;
/*
* Workers synchronize these parameters at the start of the parallel
@@ -8745,6 +8788,21 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
errmsg("cannot set parameters during a parallel operation")));
+ /*
+ * 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(settingId = get_setting_oid(stmt->name, true)))
+ {
+ aclresult = pg_setting_acl_aclcheck(settingId, GetUserId(),
+ ACL_SET_VALUE);
+ if (aclresult == ACLCHECK_OK)
+ context = PGC_SUSET;
+ }
+
switch (stmt->kind)
{
case VAR_SET_VALUE:
@@ -8753,7 +8811,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;
@@ -8838,7 +8896,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;
@@ -9578,6 +9636,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, false, 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,
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index ea67e52a3f..4ddd7cc2e5 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, SETTING or LARGE OBJECT)
* acls: the ACL string fetched from the database
* racls: the ACL string of any initial-but-now-revoked privileges
* owner: username of object owner (will be passed through fmtId); can be
@@ -573,6 +573,11 @@ do { \
CONVERT_PRIV('U', "USAGE");
else if (strcmp(type, "FOREIGN TABLE") == 0)
CONVERT_PRIV('r', "SELECT");
+ else if (strcmp(type, "SETTING") == 0)
+ {
+ CONVERT_PRIV('s', "SET VALUE");
+ 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 59f4fbb2cc..0950d159c2 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -3413,6 +3413,7 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te)
strcmp(type, "SCHEMA") == 0 ||
strcmp(type, "EVENT TRIGGER") == 0 ||
strcmp(type, "FOREIGN DATA WRAPPER") == 0 ||
+ strcmp(type, "SETTING") == 0 ||
strcmp(type, "SERVER") == 0 ||
strcmp(type, "PUBLICATION") == 0 ||
strcmp(type, "SUBSCRIPTION") == 0 ||
@@ -3596,6 +3597,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, "SETTING") == 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 5a2094de9f..6a4d423d16 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -15078,6 +15078,9 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
case DEFACLOBJ_NAMESPACE:
type = "SCHEMAS";
break;
+ case DEFACLOBJ_SETTING:
+ type = "SETTINGS";
+ break;
default:
/* shouldn't get here */
fatal("unrecognized object type in default privileges: %d",
@@ -15123,7 +15126,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, SETTING 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 c29101704a..48a8e79cc0 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 dumpGroups(PGconn *conn);
static void dropTablespaces(PGconn *conn);
static void dumpTablespaces(PGconn *conn);
@@ -586,6 +587,10 @@ main(int argc, char *argv[])
dumpRoleMembership(conn);
else
dumpGroups(conn);
+
+ /* Dump role guc privileges */
+ if (server_version >= 150000)
+ dumpRoleGUCPrivs(conn);
}
/* Dump tablespaces */
@@ -1049,6 +1054,72 @@ dumpRoleMembership(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)
+{
+ PQExpBuffer buf = createPQExpBuffer();
+ PGresult *res;
+ int i;
+
+ printfPQExpBuffer(buf, "SELECT string_agg(acl.privilege_type, ', ' ORDER BY acl.privilege_type), "
+ "set_acl.setting, "
+ "grantee.rolname AS grantee, "
+ "acl.is_grantable, "
+ "grantor.rolname AS grantor "
+ "FROM pg_catalog.pg_setting_acl set_acl, "
+ "LATERAL (SELECT * FROM aclexplode(set_acl.setacl)) acl "
+ "JOIN pg_catalog.pg_authid grantee "
+ "ON acl.grantee = grantee.oid "
+ "LEFT JOIN pg_catalog.pg_authid grantor ON "
+ "acl.grantor = grantor.oid "
+ "WHERE acl.grantee > 0 "
+ "GROUP BY setting, grantee.rolname, is_grantable, grantor.rolname"
+ );
+
+ res = executeQuery(conn, buf->data);
+
+ if (PQntuples(res) > 0)
+ fprintf(OPF, "--\n-- Role privileges on configuration parameters\n--\n\n");
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ char *privilege = PQgetvalue(res, i, 0);
+ char *setting = PQgetvalue(res, i, 1);
+ char *grantee = PQgetvalue(res, i, 2);
+ char *grantable = PQgetvalue(res, i, 3);
+
+ fprintf(OPF, "GRANT %s", privilege);
+ fprintf(OPF, " ON %s", setting);
+ fprintf(OPF, " TO %s", fmtId(grantee));
+ if (*grantable == 't')
+ fprintf(OPF, " WITH GRANT OPTION");
+
+ /*
+ * We don't track the grantor very carefully in the backend, so cope
+ * with the possibility that it has been dropped.
+ */
+ if (!PQgetisnull(res, i, 4))
+ {
+ char *grantor = PQgetvalue(res, i, 4);
+
+ fprintf(OPF, " GRANTED BY %s", fmtId(grantor));
+ }
+ fprintf(OPF, ";\n");
+ }
+
+ PQclear(res);
+ destroyPQExpBuffer(buf);
+
+ fprintf(OPF, "\n\n");
+}
+
/*
* Dump group memberships from a pre-8.1 server. It's annoying that we
* can't share any useful amount of code with the post-8.1 case, but
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 3eca295ff4..a800f0b544 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -92,6 +92,7 @@ typedef enum ObjectClass
OCLASS_TYPE, /* pg_type */
OCLASS_CAST, /* pg_cast */
OCLASS_COLLATION, /* pg_collation */
+ OCLASS_SETTING, /* pg_setting_acl */
OCLASS_CONSTRAINT, /* pg_constraint */
OCLASS_CONVERSION, /* pg_conversion */
OCLASS_DEFAULT, /* pg_attrdef */
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index eb72dd6293..08a2dcee41 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_SETTING 'c' /* configuration parameter */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index e934361dc3..98aed5de58 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7196,6 +7196,25 @@
proname => 'has_type_privilege', provolatile => 's', prorettype => 'bool',
proargtypes => 'oid text', prosrc => 'has_type_privilege_id' },
+{ oid => '8050', descr => 'user privilege on setting by username, setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'name text text', prosrc => 'has_setting_privilege_name_name' },
+{ oid => '8051', descr => 'user privilege on setting by username, setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'name oid text', prosrc => 'has_setting_privilege_name_id' },
+{ oid => '8052', descr => 'user privilege on setting by user oid, setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid text text', prosrc => 'has_setting_privilege_id_name' },
+{ oid => '8053', descr => 'user privilege on setting by user oid, setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid oid text', prosrc => 'has_setting_privilege_id_id' },
+{ oid => '8054', descr => 'current user privilege on setting by setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'text text', prosrc => 'has_setting_privilege_name' },
+{ oid => '8055', descr => 'current user privilege on setting by setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid text', prosrc => 'has_setting_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/catalog/pg_setting_acl.h b/src/include/catalog/pg_setting_acl.h
new file mode 100644
index 0000000000..b7dee55e5c
--- /dev/null
+++ b/src/include/catalog/pg_setting_acl.h
@@ -0,0 +1,63 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_setting_acl.h
+ * definition of the "configuration parameter" system catalog
+ * (pg_setting_acl).
+ *
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_setting_acl.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SETTING_ACL_H
+#define PG_SETTING_ACL_H
+
+#include "catalog/genbki.h"
+#include "catalog/pg_setting_acl_d.h"
+
+/* ----------------
+ * pg_setting_acl definition. cpp turns this into
+ * typedef struct FormData_pg_setting_acl
+ * ----------------
+ */
+CATALOG(pg_setting_acl,8924,SettingAclRelationId) BKI_SHARED_RELATION
+{
+ Oid oid; /* oid */
+ /*
+
+ * Variable-length fields start here, but we allow direct access to
+ * setting.
+ */
+ text setting BKI_FORCE_NOT_NULL;
+
+#ifdef CATALOG_VARLEN
+ /* Access privileges */
+ aclitem setacl[1] BKI_DEFAULT(_null_);
+#endif
+} FormData_pg_setting_acl;
+
+
+/* ----------------
+ * Form_pg_setting_acl corresponds to a pointer to a tuple with
+ * the format of pg_setting_acl relation.
+ * ----------------
+ */
+typedef FormData_pg_setting_acl *Form_pg_setting_acl;
+
+DECLARE_TOAST(pg_setting_acl, 8925, 8926);
+#define PgSettingAclToastTable 8925
+#define PgSettingAclToastIndex 8926
+
+DECLARE_UNIQUE_INDEX(pg_setting_acl_setting_index, 8927, SettingAclSettingIndexId, on pg_setting_acl using btree(setting text_ops));
+DECLARE_UNIQUE_INDEX_PKEY(pg_setting_acl_oid_index, 8928, SettingAclOidIndexId, on pg_setting_acl using btree(oid oid_ops));
+
+extern Oid SettingAclCreate(const char *setting, bool if_not_exists);
+
+#endif /* PG_SETTING_ACL_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 067138e6b5..d87c5b512e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -92,7 +92,9 @@ 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 N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */
+#define ACL_SET_VALUE (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
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
@@ -1794,6 +1796,7 @@ typedef enum ObjectType
OBJECT_CAST,
OBJECT_COLUMN,
OBJECT_COLLATION,
+ OBJECT_SETTING,
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DEFAULT,
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f836acf876..527e723b39 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -307,6 +307,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 af771c901d..70a6d83336 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -146,9 +146,11 @@ 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_ALTER_SYSTEM_CHR 'A'
/* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTc"
+#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcsA"
/*
* Bitmasks defining "all rights" for each supported object type
@@ -165,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_SETTING (ACL_SET_VALUE|ACL_ALTER_SYSTEM)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -223,6 +226,8 @@ extern void select_best_grantor(Oid roleId, AclMode privileges,
extern void initialize_acl(void);
+extern Oid get_setting_oid(const char *setting, bool missing_ok);
+
/*
* prototypes for functions in aclchk.c
*/
@@ -243,6 +248,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_setting_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,
@@ -271,6 +278,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_setting_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 aa18d304ac..fb2fe193c5 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -379,6 +379,8 @@ extern int set_config_option(const char *name, const char *value,
GucAction action, bool changeVal, int elevel,
bool is_reload);
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 77871aaefc..a620eb6dca 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_setting_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 c8cfbc30f6..27cf1c6088 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -89,6 +89,8 @@ enum SysCacheIdentifier
REPLORIGNAME,
RULERELNAME,
SEQRELID,
+ SETTINGNAME,
+ SETTINGOID,
STATEXTDATASTXOID,
STATEXTNAMENSP,
STATEXTOID,
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 16f7610883..7fbf2d871b 100644
--- a/src/test/modules/test_pg_dump/t/001_base.pl
+++ b/src/test/modules/test_pg_dump/t/001_base.pl
@@ -9,7 +9,12 @@ use PostgreSQL::Test::Cluster;
use PostgreSQL::Test::Utils;
use Test::More;
-my $tempdir = PostgreSQL::Test::Utils::tempdir;
+# my $tempdir = PostgreSQL::Test::Utils::tempdir;
+my $tempbase = '/tmp/test_pg_dump';
+my $subdir = 0;
+$subdir++ while (-e "$tempbase/$subdir");
+my $tempdir = "$tempbase/$subdir";
+system("mkdir $tempdir");
###############################################################
# This structure is based off of the src/bin/pg_dump/t test
@@ -317,6 +322,53 @@ my %tests = (
like => { pg_dumpall_globals => 1, },
},
+ 'GRANT ALTER SYSTEM ON ignore_checksum_failure' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT ALTER SYSTEM ON ignore_checksum_failure TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM ON ignore_checksum_failure TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT SET VALUE ON my.missing.guc' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET VALUE ON my.missing.guc TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT SET VALUE ON my\.missing\.guc TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT SET VALUE, ALTER SYSTEM ON something WITH GRANT OPTION' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET VALUE, ALTER SYSTEM ON something TO regress_dump_test_role WITH GRANT OPTION;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM, SET VALUE ON something TO regress_dump_test_role WITH GRANT OPTION GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALTER SYSTEM ON MyReallyLong.ButValidCustom.GucNameThatCannotFit.InNamedata64Byte.Format' => {
+ create_order => 2,
+ create_sql =>
+ # configuration parameters get cased folded
+ 'GRANT ALTER SYSTEM ON MyReallyLong.ButValidCustom.GucNameThatCannotFit.InNamedata64Byte.Format TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM ON myreallylong\.butvalidcustom\.gucnamethatcannotfit\.innamedata64byte\.format TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALTER SYSTEM, SET VALUE ON my.guc TO regress_dump_test_role GRANTED BY CURRENT_ROLE' => {
+ create_order => 2,
+ create_sql =>
+ # GRANTED BY CURRENT_ROLE is allowed for SQL compatibility, but is ignored
+ 'GRANT ALTER SYSTEM, SET VALUE ON my.guc TO regress_dump_test_role GRANTED BY CURRENT_ROLE;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM, SET VALUE ON my\.guc TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
'CREATE SCHEMA public' => {
regexp => qr/^CREATE SCHEMA public;/m,
like => {
diff --git a/src/test/regress/expected/guc_privs.out b/src/test/regress/expected/guc_privs.out
new file mode 100644
index 0000000000..370ef41d17
--- /dev/null
+++ b/src/test/regress/expected/guc_privs.out
@@ -0,0 +1,360 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform all 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 = 'en_US.UTF-8'; -- ok
+RESET lc_messages; -- ok
+ALTER SYSTEM SET lc_messages = 'en_US.UTF-8'; -- 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;
+-- Check setting privileges prior to creating any
+SELECT * FROM pg_catalog.pg_setting_privileges;
+ grantor | grantee | setting | privilege_type | is_grantable
+---------+---------+---------+----------------+--------------
+(0 rows)
+
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+-- Check the new role does not yet have privileges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ f
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ f
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ f
+(1 row)
+
+-- Check inappropriate and nonsense privilege types
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+-- Grant privileges on settings to the new non-superuser role
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Check setting privileges after creating some
+SELECT * FROM pg_catalog.pg_setting_privileges;
+ grantor | grantee | setting | privilege_type | is_grantable
+---------------+-----------------------------+---------------------------+----------------+--------------
+ regress_admin | regress_host_resource_admin | autovacuum_work_mem | SET VALUE | f
+ regress_admin | regress_host_resource_admin | autovacuum_work_mem | ALTER SYSTEM | f
+ regress_admin | regress_host_resource_admin | hash_mem_multiplier | SET VALUE | f
+ regress_admin | regress_host_resource_admin | hash_mem_multiplier | ALTER SYSTEM | f
+ regress_admin | regress_host_resource_admin | logical_decoding_work_mem | SET VALUE | f
+ regress_admin | regress_host_resource_admin | logical_decoding_work_mem | ALTER SYSTEM | f
+ regress_admin | regress_host_resource_admin | maintenance_work_mem | SET VALUE | f
+ regress_admin | regress_host_resource_admin | maintenance_work_mem | ALTER SYSTEM | f
+ regress_admin | regress_host_resource_admin | max_stack_depth | SET VALUE | f
+ regress_admin | regress_host_resource_admin | max_stack_depth | ALTER SYSTEM | f
+ regress_admin | regress_host_resource_admin | min_dynamic_shared_memory | SET VALUE | f
+ regress_admin | regress_host_resource_admin | min_dynamic_shared_memory | ALTER SYSTEM | f
+ regress_admin | regress_host_resource_admin | shared_buffers | SET VALUE | f
+ regress_admin | regress_host_resource_admin | shared_buffers | ALTER SYSTEM | f
+ regress_admin | regress_host_resource_admin | temp_buffers | SET VALUE | f
+ regress_admin | regress_host_resource_admin | temp_buffers | ALTER SYSTEM | f
+ regress_admin | regress_host_resource_admin | temp_file_limit | SET VALUE | f
+ regress_admin | regress_host_resource_admin | temp_file_limit | ALTER SYSTEM | f
+ regress_admin | regress_host_resource_admin | work_mem | SET VALUE | f
+ regress_admin | regress_host_resource_admin | work_mem | ALTER SYSTEM | f
+(20 rows)
+
+-- Check the new role now has privilges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE WITH GRANT OPTION, ALTER SYSTEM WITH GRANT OPTION');
+ has_setting_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_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+SELECT has_setting_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_setting_privilege('regress_host_resource_admin',
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('hash_mem_multiplier', 'set value');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'work_mem'),
+ 'alter system with grant option');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+-- Check setting privileges show up in view
+SELECT * FROM pg_catalog.pg_setting_privileges;
+ grantor | grantee | setting | privilege_type | is_grantable
+---------------+-----------------------------+---------------------------+----------------+--------------
+ regress_admin | regress_host_resource_admin | autovacuum_work_mem | SET VALUE | f
+ regress_admin | regress_host_resource_admin | autovacuum_work_mem | ALTER SYSTEM | f
+ regress_admin | regress_host_resource_admin | hash_mem_multiplier | SET VALUE | f
+ regress_admin | regress_host_resource_admin | hash_mem_multiplier | ALTER SYSTEM | f
+ regress_admin | regress_host_resource_admin | logical_decoding_work_mem | SET VALUE | f
+ regress_admin | regress_host_resource_admin | logical_decoding_work_mem | ALTER SYSTEM | f
+ regress_admin | regress_host_resource_admin | maintenance_work_mem | SET VALUE | f
+ regress_admin | regress_host_resource_admin | maintenance_work_mem | ALTER SYSTEM | f
+ regress_admin | regress_host_resource_admin | max_stack_depth | SET VALUE | f
+ regress_admin | regress_host_resource_admin | max_stack_depth | ALTER SYSTEM | f
+ regress_admin | regress_host_resource_admin | min_dynamic_shared_memory | SET VALUE | f
+ regress_admin | regress_host_resource_admin | min_dynamic_shared_memory | ALTER SYSTEM | f
+ regress_admin | regress_host_resource_admin | shared_buffers | SET VALUE | f
+ regress_admin | regress_host_resource_admin | shared_buffers | ALTER SYSTEM | f
+ regress_admin | regress_host_resource_admin | temp_buffers | SET VALUE | f
+ regress_admin | regress_host_resource_admin | temp_buffers | ALTER SYSTEM | f
+ regress_admin | regress_host_resource_admin | temp_file_limit | SET VALUE | f
+ regress_admin | regress_host_resource_admin | temp_file_limit | ALTER SYSTEM | f
+ regress_admin | regress_host_resource_admin | work_mem | SET VALUE | f
+ regress_admin | regress_host_resource_admin | work_mem | ALTER SYSTEM | f
+(20 rows)
+
+-- 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
+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
+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 setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting autovacuum_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting logical_decoding_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting temp_buffers
+privileges for setting temp_file_limit
+-- Use "revoke" to remove the privileges and allow the role to be dropped
+REVOKE SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting autovacuum_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting logical_decoding_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting temp_buffers
+privileges for setting 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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting autovacuum_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting logical_decoding_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting temp_buffers
+privileges for setting 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
+-- Create non-superuser with privileges to configure plgsql custom variables
+CREATE ROLE regress_plpgsql_admin NOSUPERUSER;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin;
+SET plpgsql.extra_errors TO 'all';
+SET plpgsql.extra_warnings TO 'all';
+RESET plpgsql.extra_errors;
+RESET plpgsql.extra_warnings;
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all';
+ERROR: permission denied to set parameter "plpgsql.extra_errors"
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all';
+ERROR: permission denied to set parameter "plpgsql.extra_warnings"
+ALTER SYSTEM RESET plpgsql.extra_errors;
+ERROR: permission denied to set parameter "plpgsql.extra_errors"
+ALTER SYSTEM RESET plpgsql.extra_warnings;
+ERROR: permission denied to set parameter "plpgsql.extra_warnings"
+SET SESSION AUTHORIZATION regress_admin;
+GRANT SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+TO regress_plpgsql_admin;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin; -- ok
+SET plpgsql.extra_errors TO 'all'; -- ok
+SET plpgsql.extra_warnings TO 'all'; -- ok
+RESET plpgsql.extra_errors; -- ok
+RESET plpgsql.extra_warnings; -- ok
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all'; -- ok
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all'; -- ok
+ALTER SYSTEM RESET plpgsql.extra_errors; -- ok
+ALTER SYSTEM RESET plpgsql.extra_warnings; -- ok
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_plpgsql_admin; -- fail, privileges remain
+ERROR: role "regress_plpgsql_admin" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting plpgsql.extra_warnings
+privileges for setting plpgsql.extra_errors
+REVOKE SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+FROM regress_plpgsql_admin;
+DROP ROLE regress_plpgsql_admin; -- ok
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 9b91865dcc..d0ddf1a8be 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -31,6 +31,34 @@ CREATE USER regress_priv_user6;
CREATE USER regress_priv_user7;
GRANT pg_read_all_data TO regress_priv_user6;
GRANT pg_write_all_data TO regress_priv_user7;
+GRANT SET VALUE ON enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON enable_nestloop TO regress_priv_user6;
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+GRANT ALTER SYSTEM ON enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON sort_mem TO regress_priv_user7; -- old name for "work_mem"
+NOTICE: granting privileges on canonical setting name "work_mem"
+GRANT ALTER SYSTEM ON maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON "" TO regress_priv_user7; -- bad name
+ERROR: zero-length delimited identifier at or near """"
+LINE 1: GRANT ALTER SYSTEM ON "" TO regress_priv_user7;
+ ^
+GRANT ALTER SYSTEM ON " " TO regress_priv_user7; -- bad name
+ERROR: invalid setting name " "
+GRANT ALTER SYSTEM ON " foo " TO regress_priv_user7; -- bad name
+ERROR: invalid setting name " foo "
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
ALTER GROUP regress_priv_group1 ADD USER regress_priv_user4;
@@ -2326,10 +2354,32 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+ERROR: role "regress_priv_user6" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting enable_memoize
+privileges for setting enable_nestloop
+DROP USER regress_priv_user7; -- privileges remain
+ERROR: role "regress_priv_user7" cannot be dropped because some objects depend on it
+DETAIL: privileges for table persons2
+privileges for setting enable_seqscan
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting no_such_param
+privileges for setting no_such_extension.no_such_param
+privileges for setting no_such_extension.no_such_param.longer.than.maximum.namedata.length
DROP USER regress_priv_user8; -- does not exist
ERROR: role "regress_priv_user8" does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+DROP USER regress_priv_user7; -- ok
+REVOKE SET VALUE ON enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON enable_nestloop FROM regress_priv_user6;
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
CREATE TABLE lock_table (a int);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2fa00a3c29..d10d71e09a 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1708,6 +1708,19 @@ pg_sequences| SELECT n.nspname AS schemaname,
JOIN pg_class c ON ((c.oid = s.seqrelid)))
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char"));
+pg_setting_privileges| SELECT grantor.rolname AS grantor,
+ grantee.rolname AS grantee,
+ set_acl.setting,
+ acl.privilege_type,
+ acl.is_grantable
+ FROM pg_setting_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
+ JOIN pg_authid grantee ON ((acl.grantee = grantee.oid)))
+ LEFT JOIN pg_authid grantor ON ((acl.grantor = grantor.oid)));
pg_settings| SELECT a.name,
a.setting,
a.unit,
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 63706a28cc..4a8208c789 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -147,6 +147,7 @@ pg_replication_origin|t
pg_rewrite|t
pg_seclabel|t
pg_sequence|t
+pg_setting_acl|t
pg_shdepend|t
pg_shdescription|t
pg_shseclabel|t
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 017e962fed..b45a203eb6 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -86,7 +86,7 @@ test: brin_bloom brin_multi
# ----------
# Another group of parallel tests
# ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort guc_privs
# rules cannot run concurrently with any test that creates
# a view or rule in the public schema
diff --git a/src/test/regress/sql/guc_privs.sql b/src/test/regress/sql/guc_privs.sql
new file mode 100644
index 0000000000..a3cfa99437
--- /dev/null
+++ b/src/test/regress/sql/guc_privs.sql
@@ -0,0 +1,179 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform all 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 = 'en_US.UTF-8'; -- ok
+RESET lc_messages; -- ok
+ALTER SYSTEM SET lc_messages = 'en_US.UTF-8'; -- 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;
+-- Check setting privileges prior to creating any
+SELECT * FROM pg_catalog.pg_setting_privileges;
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+-- Check the new role does not yet have privileges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+-- Check inappropriate and nonsense privilege types
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+-- Grant privileges on settings to the new non-superuser role
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Check setting privileges after creating some
+SELECT * FROM pg_catalog.pg_setting_privileges;
+-- Check the new role now has privilges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE 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_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER WITH GRANT OPTION');
+-- Check other function signatures
+SELECT has_setting_privilege('regress_host_resource_admin',
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET VALUE');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+SELECT has_setting_privilege('hash_mem_multiplier', 'set value');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'work_mem'),
+ 'alter system with grant option');
+-- Check setting privileges show up in view
+SELECT * FROM pg_catalog.pg_setting_privileges;
+-- 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
+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
+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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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
+-- Create non-superuser with privileges to configure plgsql custom variables
+CREATE ROLE regress_plpgsql_admin NOSUPERUSER;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin;
+SET plpgsql.extra_errors TO 'all';
+SET plpgsql.extra_warnings TO 'all';
+RESET plpgsql.extra_errors;
+RESET plpgsql.extra_warnings;
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all';
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all';
+ALTER SYSTEM RESET plpgsql.extra_errors;
+ALTER SYSTEM RESET plpgsql.extra_warnings;
+SET SESSION AUTHORIZATION regress_admin;
+GRANT SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+TO regress_plpgsql_admin;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin; -- ok
+SET plpgsql.extra_errors TO 'all'; -- ok
+SET plpgsql.extra_warnings TO 'all'; -- ok
+RESET plpgsql.extra_errors; -- ok
+RESET plpgsql.extra_warnings; -- ok
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all'; -- ok
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all'; -- ok
+ALTER SYSTEM RESET plpgsql.extra_errors; -- ok
+ALTER SYSTEM RESET plpgsql.extra_warnings; -- ok
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_plpgsql_admin; -- fail, privileges remain
+REVOKE SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+FROM regress_plpgsql_admin;
+DROP ROLE regress_plpgsql_admin; -- ok
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index 6353a1cb8c..eee18e7ba3 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -36,6 +36,33 @@ CREATE USER regress_priv_user7;
GRANT pg_read_all_data TO regress_priv_user6;
GRANT pg_write_all_data TO regress_priv_user7;
+GRANT SET VALUE ON enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON enable_nestloop TO regress_priv_user6;
+
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+
+GRANT ALTER SYSTEM ON enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON "" TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON " " TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON " foo " TO regress_priv_user7; -- bad name
+
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
+
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
@@ -1389,10 +1416,23 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+DROP USER regress_priv_user7; -- privileges remain
DROP USER regress_priv_user8; -- does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+DROP USER regress_priv_user7; -- ok
+
+REVOKE SET VALUE ON enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON enable_nestloop FROM regress_priv_user6;
+
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
--
2.21.1 (Apple Git-122.3)
On 11/23/21 21:14, Mark Dilger wrote:
On Nov 23, 2021, at 8:07 AM, Robert Haas <robertmhaas@gmail.com> wrote:
It's my impression that information_schema is a child of the SQL
standard, and that inventions specific to PG go in pg_catalog.Also, I think the user-facing name for GUCs is "settings".
Thanks. These issues should be fixed in v4.
Along the way, I also added has_setting_privilege() functions overlooked in v3 and before.
This patch had bit-rotted slightly, and I was attempting to remedy it.
However, I got a failure running the TAP tests because of this change:
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 16f7610883..7fbf2d871b 100644
--- a/src/test/modules/test_pg_dump/t/001_base.pl
+++ b/src/test/modules/test_pg_dump/t/001_base.pl
@@ -9,7 +9,12 @@ use PostgreSQL::Test::Cluster;
use PostgreSQL::Test::Utils;
use Test::More;
-my $tempdir = PostgreSQL::Test::Utils::tempdir;
+# my $tempdir = PostgreSQL::Test::Utils::tempdir;
+my $tempbase = '/tmp/test_pg_dump';
+my $subdir = 0;
+$subdir++ while (-e "$tempbase/$subdir");
+my $tempdir = "$tempbase/$subdir";
+system("mkdir $tempdir");
What's going on here?
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
On Dec 13, 2021, at 12:56 PM, Andrew Dunstan <andrew@dunslane.net> wrote:
This patch had bit-rotted slightly, and I was attempting to remedy it.
I have that already, and getting ready to post. Give me a few minutes and I'll repost.
However, I got a failure running the TAP tests because of this change:
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 16f7610883..7fbf2d871b 100644 --- a/src/test/modules/test_pg_dump/t/001_base.pl +++ b/src/test/modules/test_pg_dump/t/001_base.pl @@ -9,7 +9,12 @@ use PostgreSQL::Test::Cluster; use PostgreSQL::Test::Utils; use Test::More;-my $tempdir = PostgreSQL::Test::Utils::tempdir; +# my $tempdir = PostgreSQL::Test::Utils::tempdir; +my $tempbase = '/tmp/test_pg_dump'; +my $subdir = 0; +$subdir++ while (-e "$tempbase/$subdir"); +my $tempdir = "$tempbase/$subdir"; +system("mkdir $tempdir");What's going on here?
Yeah, I hit that, too. That was an accidentally committed bit of local testing. Please ignore it for now.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Dec 13, 2021, at 12:58 PM, Mark Dilger <mark.dilger@enterprisedb.com> wrote:
Give me a few minutes and I'll repost.
Ok, this new version is rebased, cleaned up, and has the ability to revoke SET VALUE on most settings which were previously userset, but are now suset with initdb time granted SET VALUE privilege to public.
I have not yet altered gucs defined by contrib modules, but will repost in a few hours, or tomorrow, with those changes.
Attachments:
v4-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-SET-for-gucs.patchapplication/octet-stream; name=v4-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-SET-for-gucs.patch; x-unix-mode=0644Download
From 2a62acb69e157cb2f79dc835b75de1493bb2ca53 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Mon, 13 Dec 2021 07:01:18 -0800
Subject: [PATCH v4] Allow GRANT of SET and ALTER SYSTEM SET for gucs
Allow granting of privilege to set or alter system set variables
which otherwise can only be managed by superusers. Each
(role,variable,privilege) triple is independently grantable, so a
user may be granted privilege to SET but not to ALTER SYSTEM SET on
a variable, or vice versa. The privilege to SET a userset variable
may be granted, though doing so has no practical effect, since any
role can set userset variables anyway. Worse, there is no way to
revoke the privilege to SET a userset variable. To remedy that,
most core userset variables have been changed to suset, with
explicit grants to set the variable to public.
---
doc/src/sgml/catalogs.sgml | 63 ++
doc/src/sgml/ddl.sgml | 39 +-
doc/src/sgml/func.sgml | 20 +-
doc/src/sgml/ref/grant.sgml | 7 +
doc/src/sgml/ref/set.sgml | 5 +-
src/backend/catalog/Makefile | 6 +-
src/backend/catalog/aclchk.c | 251 +++++++
src/backend/catalog/catalog.c | 6 +
src/backend/catalog/dependency.c | 6 +
src/backend/catalog/objectaddress.c | 46 ++
src/backend/catalog/pg_setting_acl.c | 122 +++
src/backend/catalog/setting_privileges.sql | 62 ++
src/backend/catalog/system_views.sql | 13 +
src/backend/commands/alter.c | 1 +
src/backend/commands/dropcmds.c | 7 +
src/backend/commands/event_trigger.c | 6 +
src/backend/commands/seclabel.c | 1 +
src/backend/commands/tablecmds.c | 1 +
src/backend/parser/gram.y | 99 ++-
src/backend/utils/adt/acl.c | 258 +++++++
src/backend/utils/cache/lsyscache.c | 20 +
src/backend/utils/cache/syscache.c | 23 +
src/backend/utils/misc/guc.c | 402 ++++++----
src/bin/initdb/initdb.c | 5 +
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 | 71 ++
src/include/catalog/dependency.h | 1 +
src/include/catalog/pg_default_acl.h | 1 +
src/include/catalog/pg_proc.dat | 19 +
src/include/catalog/pg_setting_acl.h | 63 ++
src/include/nodes/parsenodes.h | 5 +-
src/include/parser/kwlist.h | 1 +
src/include/utils/acl.h | 10 +-
src/include/utils/guc.h | 2 +
src/include/utils/lsyscache.h | 1 +
src/include/utils/syscache.h | 2 +
src/test/modules/test_pg_dump/t/001_base.pl | 47 ++
src/test/regress/expected/guc_privs.out | 780 ++++++++++++++++++++
src/test/regress/expected/privileges.out | 53 +-
src/test/regress/expected/rules.out | 13 +
src/test/regress/expected/sanity_check.out | 1 +
src/test/regress/parallel_schedule | 2 +-
src/test/regress/sql/guc_privs.sql | 185 +++++
src/test/regress/sql/privileges.sql | 44 +-
46 files changed, 2608 insertions(+), 176 deletions(-)
create mode 100644 src/backend/catalog/pg_setting_acl.c
create mode 100644 src/backend/catalog/setting_privileges.sql
create mode 100644 src/include/catalog/pg_setting_acl.h
create mode 100644 src/test/regress/expected/guc_privs.out
create mode 100644 src/test/regress/sql/guc_privs.sql
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 03e2537b07..5442b8689f 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -105,6 +105,11 @@
<entry>collations (locale information)</entry>
</row>
+ <row>
+ <entry><link linkend="catalog-pg-setting-acl"><structname>pg_setting_acl</structname></link></entry>
+ <entry>configuration parameters which have privileges granted to roles</entry>
+ </row>
+
<row>
<entry><link linkend="catalog-pg-constraint"><structname>pg_constraint</structname></link></entry>
<entry>check constraints, unique constraints, primary key constraints, foreign key constraints</entry>
@@ -2423,6 +2428,64 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</para>
</sect1>
+ <sect1 id="catalog-pg-setting-acl">
+ <title><structname>pg_setting_acl</structname></title>
+
+ <indexterm zone="catalog-pg-setting-acl">
+ <primary>pg_setting_acl</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_setting_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_setting_acl</structname>
+ is shared across all databases of a cluster: there is only one
+ copy of <structname>pg_setting_acl</structname> per cluster, not
+ one per database.
+ </para>
+
+ <table>
+ <title><structname>pg_setting_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>setting</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>
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 64d9030652..dc2898ca9a 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -1672,7 +1672,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 VALUE</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.
@@ -1940,6 +1941,26 @@ REVOKE ALL ON accounts FROM PUBLIC;
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>SET VALUE</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>SET</command> command to
+ change run-time configuration parameters.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ALTER SYSTEM</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>ALTER SYSTEM
+ SET</command> command to change server configuration parameters.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
The privileges required by other commands are listed on the
@@ -2078,6 +2099,16 @@ REVOKE ALL ON accounts FROM PUBLIC;
<literal>TYPE</literal>
</entry>
</row>
+ <row>
+ <entry><literal>SET VALUE</literal></entry>
+ <entry><literal>s</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
+ <row>
+ <entry><literal>ALTER SYSTEM</literal></entry>
+ <entry><literal>A</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -2184,6 +2215,12 @@ REVOKE ALL ON accounts FROM PUBLIC;
<entry><literal>U</literal></entry>
<entry><literal>\dT+</literal></entry>
</row>
+ <row>
+ <entry><literal>Configuration parameter</literal></entry>
+ <entry><literal>sA</literal></entry>
+ <entry>none</entry>
+ <entry><literal></literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 0a725a6711..ec846cd4e3 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -22627,8 +22627,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');
@@ -22893,6 +22892,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_setting_privilege</primary>
+ </indexterm>
+ <function>has_setting_privilege</function> (
+ <optional> <parameter>user</parameter> <type>name</type> or <type>oid</type>, </optional>
+ <parameter>setting</parameter> <type>text</type> or <type>oid</type>,
+ <parameter>privilege</parameter> <type>text</type> )
+ <returnvalue>boolean</returnvalue>
+ </para>
+ <para>
+ Does user have privilege for setting?
+ Allowable privilege types are <literal>SET VALUE</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 a897712de2..21ec320a8d 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 VALUE | ALTER SYSTEM }
+ ON <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 VALUE</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/set.sgml b/doc/src/sgml/ref/set.sgml
index 339ee9eec9..a08057d1d1 100644
--- a/doc/src/sgml/ref/set.sgml
+++ b/doc/src/sgml/ref/set.sgml
@@ -34,8 +34,9 @@ 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.)
+ (But some require either superuser privileges or granted <literal>SET
+ VALUE</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>
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 4e6efda97f..45e5330c12 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -28,6 +28,7 @@ OBJS = \
pg_cast.o \
pg_class.o \
pg_collation.o \
+ pg_setting_acl.o \
pg_constraint.o \
pg_conversion.o \
pg_db_role_setting.o \
@@ -54,7 +55,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_setting_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 \
@@ -126,6 +127,7 @@ install-data: bki-stamp installdirs
$(INSTALL_DATA) $(srcdir)/system_functions.sql '$(DESTDIR)$(datadir)/system_functions.sql'
$(INSTALL_DATA) $(srcdir)/system_views.sql '$(DESTDIR)$(datadir)/system_views.sql'
$(INSTALL_DATA) $(srcdir)/information_schema.sql '$(DESTDIR)$(datadir)/information_schema.sql'
+ $(INSTALL_DATA) $(srcdir)/setting_privileges.sql '$(DESTDIR)$(datadir)/setting_privileges.sql'
$(INSTALL_DATA) $(srcdir)/sql_features.txt '$(DESTDIR)$(datadir)/sql_features.txt'
installdirs:
@@ -133,7 +135,7 @@ installdirs:
.PHONY: uninstall-data
uninstall-data:
- rm -f $(addprefix '$(DESTDIR)$(datadir)'/, postgres.bki system_constraints.sql system_functions.sql system_views.sql information_schema.sql sql_features.txt)
+ rm -f $(addprefix '$(DESTDIR)$(datadir)'/, postgres.bki system_constraints.sql system_functions.sql system_views.sql information_schema.sql setting_privileges.sql sql_features.txt)
# postgres.bki, system_constraints.sql, and the generated headers are
# in the distribution tarball, so they are not cleaned here.
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ce0a4ff14e..02c15853e5 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_setting_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_Setting(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_SETTING:
+ whole_mask = ACL_ALL_RIGHTS_SETTING;
+ 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_SETTING:
+ all_privileges = ACL_ALL_RIGHTS_SETTING;
+ errormsg = gettext_noop("invalid privilege type %s for setting");
+ 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_SETTING:
+ ExecGrant_Setting(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -759,6 +771,38 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_SETTING:
+ foreach(cell, objnames)
+ {
+ char *setting = strVal(lfirst(cell));
+ Oid settingid = get_setting_oid(setting, true);
+
+ if (!OidIsValid(settingid))
+ {
+ /*
+ * Lookup the existing entry, or if necessary, add a new
+ * entry for this parameter. Entries only exist for
+ * parameters which currently have, or previously have had,
+ * privileges assigned.
+ *
+ * It is tempting to sanity-check the given configuration
+ * parameter name against known guc names in the guc
+ * tables, but for handling upgrades we need to accept
+ * setting names that do not yet exist.
+ */
+ settingid = SettingAclCreate(setting, 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, settingid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -1494,6 +1538,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case ForeignDataWrapperRelationId:
istmt.objtype = OBJECT_FDW;
break;
+ case SettingAclRelationId:
+ istmt.objtype = OBJECT_SETTING;
+ break;
default:
elog(ERROR, "unexpected object class %u", classid);
break;
@@ -3225,6 +3272,128 @@ ExecGrant_Type(InternalGrant *istmt)
table_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Setting(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_SETTING;
+
+ relation = table_open(SettingAclRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid settingId = lfirst_oid(cell);
+ Form_pg_setting_acl pg_setting_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_setting_acl];
+ bool nulls[Natts_pg_setting_acl];
+ bool replaces[Natts_pg_setting_acl];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(settingId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for setting %u", settingId);
+
+ pg_setting_acl_tuple = (Form_pg_setting_acl) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ aclDatum = SysCacheGetAttr(SETTINGNAME, tuple, Anum_pg_setting_acl_setacl,
+ &isNull);
+ if (isNull)
+ {
+ old_acl = acldefault(OBJECT_SETTING, InvalidOid);
+ /* 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, GetUserId(),
+ &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,
+ settingId, grantorId, OBJECT_SETTING,
+ text_to_cstring(&pg_setting_acl_tuple->setting),
+ 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_setting_acl_setacl - 1] = true;
+ values[Anum_pg_setting_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(settingId, SettingAclRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(SettingAclRelationId,
+ pg_setting_acl_tuple->oid, 0,
+ InvalidOid,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ table_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3255,6 +3424,10 @@ string_to_privilege(const char *privname)
return ACL_CREATE_TEMP;
if (strcmp(privname, "connect") == 0)
return ACL_CONNECT;
+ if (strcmp(privname, "set value") == 0)
+ return ACL_SET_VALUE;
+ 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 +3465,10 @@ privilege_to_string(AclMode privilege)
return "TEMP";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET_VALUE:
+ return "SET VALUE";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3328,6 +3505,13 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_COLUMN:
msg = gettext_noop("permission denied for column %s");
break;
+ case OBJECT_SETTING:
+ /*
+ * 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 +3748,7 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_AMPROC:
case OBJECT_ATTRIBUTE:
case OBJECT_CAST:
+ case OBJECT_SETTING:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
case OBJECT_DOMCONSTRAINT:
@@ -4000,6 +4185,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_setting_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 setting's ACL from pg_setting_acl
+ */
+ tuple = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(config_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("setting with OID %u does not exist",
+ config_oid)));
+
+ aclDatum = SysCacheGetAttr(SETTINGOID, tuple, Anum_pg_setting_acl_setacl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_SETTING, 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 +4951,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_setting_acl_aclcheck(Oid config_oid, Oid roleid, AclMode mode)
+{
+ if (pg_setting_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 aa7d4d5456..a069c2fb39 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_setting_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 == SettingAclRelationId ||
relationId == DatabaseRelationId ||
relationId == SharedDescriptionRelationId ||
relationId == SharedDependRelationId ||
@@ -260,6 +262,8 @@ IsSharedRelation(Oid relationId)
relationId == AuthIdOidIndexId ||
relationId == AuthMemRoleMemIndexId ||
relationId == AuthMemMemRoleIndexId ||
+ relationId == SettingAclSettingIndexId ||
+ relationId == SettingAclOidIndexId ||
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 == PgSettingAclToastTable ||
+ relationId == PgSettingAclToastIndex ||
relationId == PgDatabaseToastTable ||
relationId == PgDatabaseToastIndex ||
relationId == PgDbRoleSettingToastTable ||
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index fe9c714257..57174b7c19 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_setting_acl.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
@@ -150,6 +151,7 @@ static const Oid object_classes[] = {
TypeRelationId, /* OCLASS_TYPE */
CastRelationId, /* OCLASS_CAST */
CollationRelationId, /* OCLASS_COLLATION */
+ SettingAclRelationId, /* OCLASS_SETTING */
ConstraintRelationId, /* OCLASS_CONSTRAINT */
ConversionRelationId, /* OCLASS_CONVERSION */
AttrDefaultRelationId, /* OCLASS_DEFAULT */
@@ -1504,6 +1506,7 @@ doDeletion(const ObjectAddress *object, int flags)
/*
* These global object types are not supported here.
*/
+ case OCLASS_SETTING:
case OCLASS_ROLE:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
@@ -2778,6 +2781,9 @@ getObjectClass(const ObjectAddress *object)
case CollationRelationId:
return OCLASS_COLLATION;
+ case SettingAclRelationId:
+ return OCLASS_SETTING;
+
case ConstraintRelationId:
return OCLASS_CONSTRAINT;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 2bae3fbb17..682ae5e390 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_setting_acl.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
@@ -2309,6 +2310,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_COLUMN:
case OBJECT_ATTRIBUTE:
case OBJECT_COLLATION:
+ case OBJECT_SETTING:
case OBJECT_CONVERSION:
case OBJECT_STATISTIC_EXT:
case OBJECT_TSPARSER:
@@ -3510,6 +3512,22 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
break;
}
+ case OCLASS_SETTING:
+ {
+ char *setting;
+
+ setting = get_setting_name(object->objectId);
+ if (!setting)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for setting %u",
+ object->objectId);
+ break;
+ }
+ appendStringInfo(&buffer, _("setting %s"), setting);
+ break;
+ }
+
case OCLASS_SCHEMA:
{
char *nspname;
@@ -4473,6 +4491,10 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "collation");
break;
+ case OCLASS_SETTING:
+ appendStringInfoString(&buffer, "setting");
+ break;
+
case OCLASS_CONSTRAINT:
getConstraintTypeDescription(&buffer, object->objectId,
missing_ok);
@@ -4977,6 +4999,30 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_SETTING:
+ {
+ HeapTuple configTup;
+ Form_pg_setting_acl configForm;
+ char *namestr;
+
+ configTup = SearchSysCache1(SETTINGOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(configTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for setting %u",
+ object->objectId);
+ break;
+ }
+ configForm = (Form_pg_setting_acl) GETSTRUCT(configTup);
+ namestr = text_to_cstring(&configForm->setting);
+ appendStringInfoString(&buffer, namestr);
+ if (objname)
+ *objname = list_make1(namestr);
+ ReleaseSysCache(configTup);
+ break;
+ }
+
case OCLASS_CONVERSION:
{
HeapTuple conTup;
diff --git a/src/backend/catalog/pg_setting_acl.c b/src/backend/catalog/pg_setting_acl.c
new file mode 100644
index 0000000000..3c6594da03
--- /dev/null
+++ b/src/backend/catalog/pg_setting_acl.c
@@ -0,0 +1,122 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_setting_acl.c
+ * routines to support manipulation of the pg_setting_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_setting_acl.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/sysattr.h"
+#include "access/table.h"
+#include "access/tableam.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_setting_acl.h"
+#include "mb/pg_wchar.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/guc.h"
+#include "utils/pg_locale.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * SettingAclCreate
+ *
+ * Add a new tuple to pg_setting_acl.
+ *
+ * setting: the setting name to create.
+ * if_not_exists: if true, don't fail on duplicate name, but rather return
+ * the existing entry's Oid.
+ */
+Oid
+SettingAclCreate(const char *setting, bool if_not_exists)
+{
+ Relation rel;
+ TupleDesc tupDesc;
+ HeapTuple tuple;
+ Datum values[Natts_pg_setting_acl];
+ bool nulls[Natts_pg_setting_acl];
+ Oid settingId;
+ const char *canonical;
+
+ /*
+ * Check whether the setting (by the given name or alias)
+ * already exists.
+ */
+ settingId = get_setting_oid(setting, true);
+ if (OidIsValid(settingId))
+ {
+ if (if_not_exists)
+ return settingId;
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("setting \"%s\" already exists",
+ setting)));
+ }
+
+ /*
+ * We must not require the setting to be in the list of existent GUCs,
+ * as we may be called at different points during upgrades or the
+ * installation or removal of extensions. Instead, perform a basic sanity
+ * check of the setting, 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_setting_acl entries.
+ */
+ if (!valid_variable_name(setting, NULL))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid setting name \"%s\"",
+ setting)));
+ canonical = GetConfigOptionCanonicalName(setting);
+ if (!canonical)
+ canonical = setting;
+
+ /*
+ * 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(SettingAclRelationId, RowExclusiveLock);
+ tupDesc = RelationGetDescr(rel);
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ values[Anum_pg_setting_acl_setting - 1] =
+ DirectFunctionCall1(textin, CStringGetDatum(canonical));
+ settingId = GetNewOidWithIndex(rel,
+ SettingAclOidIndexId,
+ Anum_pg_setting_acl_oid);
+ values[Anum_pg_setting_acl_oid - 1] = ObjectIdGetDatum(settingId);
+ nulls[Anum_pg_setting_acl_setacl - 1] = true;
+ tuple = heap_form_tuple(tupDesc, values, nulls);
+ CatalogTupleInsert(rel, tuple);
+
+ /* Post creation hook for new setting */
+ InvokeObjectPostCreateHook(SettingAclRelationId, settingId, 0);
+
+ /*
+ * Close pg_setting_acl, but keep lock till commit.
+ */
+ heap_freetuple(tuple);
+ table_close(rel, NoLock);
+
+ return settingId;
+}
diff --git a/src/backend/catalog/setting_privileges.sql b/src/backend/catalog/setting_privileges.sql
new file mode 100644
index 0000000000..b788ebee7f
--- /dev/null
+++ b/src/backend/catalog/setting_privileges.sql
@@ -0,0 +1,62 @@
+/*
+ * PostgreSQL User SET variables
+ *
+ * Copyright (c) 2021, PostgreSQL Global Development Group
+ *
+ * src/backend/catalog/setting_privileges.sql
+ *
+ * Note: this file is read in single-user -j mode, which means that the
+ * command terminator is semicolon-newline-newline; whenever the backend
+ * sees that, it stops and executes what it's got. If you write a lot of
+ * statements without empty lines between, they'll all get quoted to you
+ * in any error message about one of them, so don't do that. Also, you
+ * cannot write a semicolon immediately followed by an empty line in a
+ * string literal (including a function body!) or a multiline comment.
+ */
+
+GRANT SET VALUE ON
+ enable_seqscan, enable_indexscan, enable_indexonlyscan, enable_bitmapscan,
+ enable_tidscan, enable_sort, enable_incremental_sort, enable_hashagg,
+ enable_material, enable_memoize, enable_nestloop, enable_mergejoin,
+ enable_hashjoin, enable_gathermerge, enable_partitionwise_join,
+ enable_partitionwise_aggregate, enable_parallel_append,
+ enable_parallel_hash, enable_partition_pruning, enable_async_append, geqo,
+ exit_on_error, debug_print_parse, debug_print_rewritten, debug_print_plan,
+ debug_pretty_print, trace_notify, transform_null_equals,
+ default_transaction_read_only, transaction_read_only,
+ default_transaction_deferrable, transaction_deferrable, row_security,
+ check_function_bodies, array_nulls, default_with_oids, trace_sort,
+ trace_syncscan, optimize_bounded_sort, escape_string_warning,
+ standard_conforming_strings, synchronize_seqscans, quote_all_identifiers,
+ parallel_leader_participation, jit, jit_expressions, jit_tuple_deforming,
+ default_statistics_target, from_collapse_limit, join_collapse_limit,
+ geqo_threshold, geqo_effort, geqo_pool_size, geqo_generations,
+ temp_buffers, work_mem, maintenance_work_mem, logical_decoding_work_mem,
+ vacuum_cost_page_hit, vacuum_cost_page_miss, vacuum_cost_page_dirty,
+ vacuum_cost_limit, statement_timeout, lock_timeout,
+ idle_in_transaction_session_timeout, idle_session_timeout,
+ vacuum_freeze_min_age, vacuum_freeze_table_age,
+ vacuum_multixact_freeze_min_age, vacuum_multixact_freeze_table_age,
+ vacuum_failsafe_age, vacuum_multixact_failsafe_age, wal_skip_threshold,
+ wal_sender_timeout, commit_siblings, extra_float_digits,
+ log_parameter_max_length_on_error, effective_io_concurrency,
+ maintenance_io_concurrency, backend_flush_after,
+ max_parallel_maintenance_workers, max_parallel_workers_per_gather,
+ max_parallel_workers, tcp_keepalives_idle, tcp_keepalives_interval,
+ ssl_renegotiation_limit, tcp_keepalives_count, gin_fuzzy_search_limit,
+ effective_cache_size, min_parallel_table_scan_size,
+ min_parallel_index_scan_size, gin_pending_list_limit, tcp_user_timeout,
+ client_connection_check_interval, seq_page_cost, random_page_cost,
+ cpu_tuple_cost, cpu_index_tuple_cost, cpu_operator_cost,
+ parallel_tuple_cost, parallel_setup_cost, jit_above_cost,
+ jit_optimize_above_cost, jit_inline_above_cost, cursor_tuple_fraction,
+ geqo_selection_bias, geqo_seed, hash_mem_multiplier, seed,
+ vacuum_cost_delay, DateStyle, default_table_access_method,
+ default_tablespace, temp_tablespaces, lc_monetary, lc_numeric, lc_time,
+ local_preload_libraries, search_path, role, TimeZone,
+ timezone_abbreviations, default_text_search_config, application_name,
+ backslash_quote, bytea_output, client_min_messages, constraint_exclusion,
+ default_toast_compression, default_transaction_isolation,
+ transaction_isolation, IntervalStyle, synchronous_commit, xmlbinary,
+ xmloption, force_parallel_mode, password_encryption, plan_cache_mode
+ TO public;
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 61b515cdb8..bd772dc9c5 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -593,6 +593,19 @@ CREATE RULE pg_settings_n AS
GRANT SELECT, UPDATE ON pg_settings TO PUBLIC;
+CREATE VIEW pg_setting_privileges AS
+ SELECT grantor.rolname AS grantor,
+ grantee.rolname AS grantee,
+ set_acl.setting AS setting,
+ acl.privilege_type AS privilege_type,
+ acl.is_grantable
+ FROM pg_catalog.pg_setting_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_setting_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 40044070cf..7da2e62683 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -639,6 +639,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
break;
case OCLASS_CAST:
+ case OCLASS_SETTING:
case OCLASS_CONSTRAINT:
case OCLASS_DEFAULT:
case OCLASS_LANGUAGE:
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 4e545adf95..45bc1c1948 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -276,6 +276,13 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
name = NameListToString(castNode(List, object));
}
break;
+ case OBJECT_SETTING:
+ if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
+ {
+ msg = gettext_noop("setting \"%s\" does not exist, skipping");
+ name = NameListToString(castNode(List, object));
+ }
+ break;
case OBJECT_CONVERSION:
if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
{
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index df264329d8..d6df5b3ad9 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_SETTING:
case OBJECT_DATABASE:
case OBJECT_TABLESPACE:
case OBJECT_ROLE:
@@ -1012,6 +1013,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
{
switch (objclass)
{
+ case OCLASS_SETTING:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
@@ -2078,6 +2080,8 @@ stringify_grant_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMN";
+ case OBJECT_SETTING:
+ return "SETTING";
case OBJECT_TABLE:
return "TABLE";
case OBJECT_SEQUENCE:
@@ -2161,6 +2165,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMNS";
+ case OBJECT_SETTING:
+ return "SETTINGS";
case OBJECT_TABLE:
return "TABLES";
case OBJECT_SEQUENCE:
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 53c18628a7..8f6f9bd7a0 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_SETTING:
case OBJECT_CONVERSION:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 47b29001d5..5672e9d1e4 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -12359,6 +12359,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_TYPE:
case OCLASS_CAST:
case OCLASS_COLLATION:
+ case OCLASS_SETTING:
case OCLASS_CONVERSION:
case OCLASS_LANGUAGE:
case OCLASS_LARGEOBJECT:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 3d4dd43e47..132b6907c5 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -363,8 +363,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 setting_name
+%type <list> OptSchemaEltList setting_target
%type <chr> am_type
@@ -402,8 +402,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> iso_level opt_encoding
%type <rolespec> grantee
%type <list> grantee_list
-%type <accesspriv> privilege
-%type <list> privileges privilege_list
+%type <accesspriv> privilege setting_priv
+%type <list> privileges privilege_list setting_priv_list
%type <privtarget> privilege_target
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
@@ -715,7 +715,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
@@ -6972,6 +6972,20 @@ GrantStmt: GRANT privileges ON privilege_target TO grantee_list
n->grantor = $8;
$$ = (Node*)n;
}
+ | GRANT setting_priv_list ON setting_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_SETTING;
+ n->objects = $4;
+ n->grantees = $6;
+ n->grant_option = $7;
+ n->grantor = $8;
+ $$ = (Node*)n;
+ }
;
RevokeStmt:
@@ -7005,6 +7019,36 @@ RevokeStmt:
n->behavior = $11;
$$ = (Node *)n;
}
+ | REVOKE setting_priv_list ON setting_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_SETTING;
+ n->objects = $4;
+ n->grantees = $6;
+ n->grantor = $7;
+ n->behavior = $8;
+ $$ = (Node *)n;
+ }
+ | REVOKE GRANT OPTION FOR setting_priv_list ON setting_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_SETTING;
+ n->objects = $7;
+ n->grantees = $9;
+ n->grantor = $10;
+ n->behavior = $11;
+ $$ = (Node *)n;
+ }
;
@@ -7073,6 +7117,49 @@ privilege: SELECT opt_column_list
}
;
+setting_priv_list: setting_priv { $$ = list_make1($1); }
+ | setting_priv_list ',' setting_priv { $$ = lappend($1, $3); }
+ ;
+
+setting_priv:
+ ALTER SYSTEM_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("alter system");
+ n->cols = NIL;
+ $$ = n;
+ }
+ | SET VALUE_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("set value");
+ n->cols = NIL;
+ $$ = n;
+ }
+ ;
+
+
+setting_target:
+ setting_name
+ {
+ $$ = list_make1(makeString($1));
+ }
+ | setting_target ',' setting_name
+ {
+ $$ = lappend($1, makeString($3));
+ }
+ ;
+
+setting_name:
+ ColId
+ {
+ $$ = $1;
+ }
+ | setting_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.
@@ -15818,6 +15905,7 @@ unreserved_keyword:
| OWNED
| OWNER
| PARALLEL
+ | PARAMETER
| PARSER
| PARTIAL
| PARTITION
@@ -16396,6 +16484,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 67f8b29434..f0dd3e52ed 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_setting_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,8 @@ 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 Oid convert_setting_name(text *setting);
+static AclMode convert_setting_priv_string(text *priv_setting_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 +310,12 @@ aclparse(const char *s, AclItem *aip)
case ACL_CONNECT_CHR:
read = ACL_CONNECT;
break;
+ case ACL_SET_VALUE_CHR:
+ read = ACL_SET_VALUE;
+ break;
+ case ACL_ALTER_SYSTEM_CHR:
+ read = ACL_ALTER_SYSTEM;
+ break;
case 'R': /* ignore old RULE privileges */
read = 0;
break;
@@ -794,6 +804,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_SETTING:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_NO_RIGHTS;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -1602,6 +1616,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 VALUE") == 0)
+ return ACL_SET_VALUE;
+ 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 +1716,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET_VALUE:
+ return "SET VALUE";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
@@ -4429,6 +4451,205 @@ convert_type_priv_string(text *priv_type_text)
return convert_any_priv_string(priv_type_text, type_priv_map);
}
+/*
+ * has_setting_privilege variants
+ * These are all named "has_setting_privilege" at the SQL level.
+ * They take various combinations of setting name, setting 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_setting_privilege_name_name
+ * Check user privileges on a setting given
+ * name username, text setting, and text priv name.
+ */
+Datum
+has_setting_privilege_name_name(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ text *setting = PG_GETARG_TEXT_PP(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid roleid;
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_role_oid_or_public(NameStr(*username));
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_name
+ * Check user privileges on a setting given
+ * text setting and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_setting_privilege_name(PG_FUNCTION_ARGS)
+{
+ text *setting = PG_GETARG_TEXT_PP(0);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(1);
+ Oid roleid;
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_name_id
+ * Check user privileges on a setting given
+ * name usename, setting oid, and text priv name.
+ */
+Datum
+has_setting_privilege_name_id(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ Oid settingoid = PG_GETARG_OID(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_role_oid_or_public(NameStr(*username));
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id
+ * Check user privileges on a setting given
+ * setting oid, and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_setting_privilege_id(PG_FUNCTION_ARGS)
+{
+ Oid settingoid = PG_GETARG_OID(0);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(1);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id_name
+ * Check user privileges on a setting given
+ * roleid, text setting, and text priv name.
+ */
+Datum
+has_setting_privilege_id_name(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ text *setting = PG_GETARG_TEXT_PP(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id_id
+ * Check user privileges on a setting given
+ * roleid, setting oid, and text priv name.
+ */
+Datum
+has_setting_privilege_id_id(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ Oid settingoid = PG_GETARG_OID(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ AclMode mode;
+ AclResult aclresult;
+
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * Support routines for has_setting_privilege family.
+ */
+
+/*
+ * Given a setting name expressed as a string, look it up and return Oid
+ */
+static Oid
+convert_setting_name(text *settingname)
+{
+ Oid oid;
+ char *setting = text_to_cstring(settingname);
+
+ oid = get_setting_oid(setting, true);
+
+ if (!OidIsValid(oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("setting \"%s\" does not exist", setting)));
+
+ return oid;
+}
+
+/*
+ * convert_setting_priv_string
+ * Convert text string to AclMode value.
+ */
+static AclMode
+convert_setting_priv_string(text *priv_setting_text)
+{
+ static const priv_map setting_priv_map[] = {
+ {"SET VALUE", ACL_SET_VALUE},
+ {"SET VALUE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SET_VALUE)},
+ {"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_setting_text, setting_priv_map);
+}
/*
* pg_has_role variants
@@ -4670,6 +4891,43 @@ initialize_acl(void)
}
}
+/*
+ * get_setting_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_setting_oid(const char *setting, bool missing_ok)
+{
+ Oid oid;
+
+ /* Check for the variable by the name we were given */
+ oid = GetSysCacheOid1(SETTINGNAME, Anum_pg_setting_acl_oid,
+ PointerGetDatum(cstring_to_text(setting)));
+ if (!OidIsValid(oid))
+ {
+ const char *canonical;
+
+ /* Check if the variable has a different canonical spelling */
+ canonical = GetConfigOptionCanonicalName(setting);
+ if (canonical != NULL)
+ oid = GetSysCacheOid1(SETTINGNAME, Anum_pg_setting_acl_oid,
+ PointerGetDatum(cstring_to_text(canonical)));
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("setting \"%s\" does not exist", setting)));
+ }
+
+ return oid;
+}
+
/*
* RoleMembershipCacheCallback
* Syscache inval callback function
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 9176514a96..0a0cbfd9f0 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_setting_acl.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_transform.h"
#include "catalog/pg_type.h"
@@ -3304,6 +3305,25 @@ free_attstatsslot(AttStatsSlot *sslot)
pfree(sslot->numbers_arr);
}
+char *
+get_setting_name(Oid configid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(configid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_setting_acl configtup = (Form_pg_setting_acl) GETSTRUCT(tp);
+ char *result;
+
+ result = pstrdup(text_to_cstring(&configtup->setting));
+ 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 56870b46e4..00fbe5218d 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_setting_acl.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
#include "catalog/pg_shseclabel.h"
@@ -762,6 +763,28 @@ static const struct cachedesc cacheinfo[] = {
},
32
},
+ {SettingAclRelationId, /* SETTINGNAME */
+ SettingAclSettingIndexId,
+ 1,
+ {
+ Anum_pg_setting_acl_setting,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+ {SettingAclRelationId, /* SETTINGOID */
+ SettingAclOidIndexId,
+ 1,
+ {
+ Anum_pg_setting_acl_oid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{StatisticExtDataRelationId, /* STATEXTDATASTXOID */
StatisticExtDataStxoidIndexId,
1,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index ee6a838b3a..6ac9c306e8 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -974,7 +974,7 @@ static const unit_conversion time_unit_conversion_table[] =
static struct config_bool ConfigureNamesBool[] =
{
{
- {"enable_seqscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_seqscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of sequential-scan plans."),
NULL,
GUC_EXPLAIN
@@ -984,7 +984,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_indexscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_indexscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of index-scan plans."),
NULL,
GUC_EXPLAIN
@@ -994,7 +994,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_indexonlyscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_indexonlyscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of index-only-scan plans."),
NULL,
GUC_EXPLAIN
@@ -1004,7 +1004,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_bitmapscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_bitmapscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of bitmap-scan plans."),
NULL,
GUC_EXPLAIN
@@ -1014,7 +1014,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_tidscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_tidscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of TID scan plans."),
NULL,
GUC_EXPLAIN
@@ -1024,7 +1024,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_sort", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_sort", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of explicit sort steps."),
NULL,
GUC_EXPLAIN
@@ -1034,7 +1034,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_incremental_sort", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_incremental_sort", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of incremental sort steps."),
NULL,
GUC_EXPLAIN
@@ -1044,7 +1044,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_hashagg", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_hashagg", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of hashed aggregation plans."),
NULL,
GUC_EXPLAIN
@@ -1054,7 +1054,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_material", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_material", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of materialization."),
NULL,
GUC_EXPLAIN
@@ -1064,7 +1064,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_memoize", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_memoize", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of memoization."),
NULL,
GUC_EXPLAIN
@@ -1074,7 +1074,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_nestloop", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_nestloop", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of nested-loop join plans."),
NULL,
GUC_EXPLAIN
@@ -1084,7 +1084,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_mergejoin", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_mergejoin", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of merge join plans."),
NULL,
GUC_EXPLAIN
@@ -1094,7 +1094,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_hashjoin", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_hashjoin", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of hash join plans."),
NULL,
GUC_EXPLAIN
@@ -1104,7 +1104,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_gathermerge", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_gathermerge", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of gather merge plans."),
NULL,
GUC_EXPLAIN
@@ -1114,7 +1114,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_partitionwise_join", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_partitionwise_join", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables partitionwise join."),
NULL,
GUC_EXPLAIN
@@ -1124,7 +1124,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_partitionwise_aggregate", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_partitionwise_aggregate", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables partitionwise aggregation and grouping."),
NULL,
GUC_EXPLAIN
@@ -1134,7 +1134,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_parallel_append", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_parallel_append", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of parallel append plans."),
NULL,
GUC_EXPLAIN
@@ -1144,7 +1144,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_parallel_hash", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_parallel_hash", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of parallel hash plans."),
NULL,
GUC_EXPLAIN
@@ -1154,7 +1154,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_partition_pruning", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_partition_pruning", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables plan-time and execution-time partition pruning."),
gettext_noop("Allows the query planner and executor to compare partition "
"bounds to conditions in the query to determine which "
@@ -1166,7 +1166,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_async_append", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_async_append", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of async append plans."),
NULL,
GUC_EXPLAIN
@@ -1176,7 +1176,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("Enables genetic query optimization."),
gettext_noop("This algorithm attempts to do planning without "
"exhaustive searching."),
@@ -1398,7 +1398,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"exit_on_error", PGC_USERSET, ERROR_HANDLING_OPTIONS,
+ {"exit_on_error", PGC_SUSET, ERROR_HANDLING_OPTIONS,
gettext_noop("Terminate session on any error."),
NULL
},
@@ -1436,7 +1436,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_print_parse", PGC_USERSET, LOGGING_WHAT,
+ {"debug_print_parse", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs each query's parse tree."),
NULL
},
@@ -1445,7 +1445,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_print_rewritten", PGC_USERSET, LOGGING_WHAT,
+ {"debug_print_rewritten", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs each query's rewritten parse tree."),
NULL
},
@@ -1454,7 +1454,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_print_plan", PGC_USERSET, LOGGING_WHAT,
+ {"debug_print_plan", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs each query's execution plan."),
NULL
},
@@ -1463,7 +1463,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_pretty_print", PGC_USERSET, LOGGING_WHAT,
+ {"debug_pretty_print", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Indents parse and plan tree displays."),
NULL
},
@@ -1584,7 +1584,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"trace_notify", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"trace_notify", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Generates debugging output for LISTEN and NOTIFY."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -1668,7 +1668,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"transform_null_equals", PGC_USERSET, COMPAT_OPTIONS_CLIENT,
+ {"transform_null_equals", PGC_SUSET, COMPAT_OPTIONS_CLIENT,
gettext_noop("Treats \"expr=NULL\" as \"expr IS NULL\"."),
gettext_noop("When turned on, expressions of the form expr = NULL "
"(or NULL = expr) are treated as expr IS NULL, that is, they "
@@ -1690,7 +1690,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"default_transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_transaction_read_only", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default read-only status of new transactions."),
NULL,
GUC_REPORT
@@ -1700,7 +1700,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"transaction_read_only", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the current transaction's read-only status."),
NULL,
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -1710,7 +1710,7 @@ static struct config_bool ConfigureNamesBool[] =
check_transaction_read_only, NULL, NULL
},
{
- {"default_transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_transaction_deferrable", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default deferrable status of new transactions."),
NULL
},
@@ -1719,7 +1719,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"transaction_deferrable", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Whether to defer a read-only serializable transaction until it can be executed with no possible serialization failures."),
NULL,
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -1729,7 +1729,7 @@ static struct config_bool ConfigureNamesBool[] =
check_transaction_deferrable, NULL, NULL
},
{
- {"row_security", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"row_security", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Enable row security."),
gettext_noop("When enabled, row security will be applied to all users.")
},
@@ -1738,7 +1738,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"check_function_bodies", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"check_function_bodies", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Check routine bodies during CREATE FUNCTION and CREATE PROCEDURE."),
NULL
},
@@ -1747,7 +1747,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"array_nulls", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"array_nulls", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Enable input of NULL elements in arrays."),
gettext_noop("When turned on, unquoted NULL in an array input "
"value means a null value; "
@@ -1764,7 +1764,7 @@ static struct config_bool ConfigureNamesBool[] =
* avoid unnecessarily breaking older dump files.
*/
{
- {"default_with_oids", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"default_with_oids", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("WITH OIDS is no longer supported; this can only be false."),
NULL,
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE
@@ -1794,7 +1794,7 @@ static struct config_bool ConfigureNamesBool[] =
#ifdef TRACE_SORT
{
- {"trace_sort", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"trace_sort", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Emit information about resource usage in sorting."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -1808,7 +1808,7 @@ static struct config_bool ConfigureNamesBool[] =
#ifdef TRACE_SYNCSCAN
/* this is undocumented because not exposed in a standard build */
{
- {"trace_syncscan", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"trace_syncscan", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Generate debugging output for synchronized scanning."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -1823,7 +1823,7 @@ static struct config_bool ConfigureNamesBool[] =
/* this is undocumented because not exposed in a standard build */
{
{
- "optimize_bounded_sort", PGC_USERSET, QUERY_TUNING_METHOD,
+ "optimize_bounded_sort", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enable bounded sorting using heap sort."),
NULL,
GUC_NOT_IN_SAMPLE | GUC_EXPLAIN
@@ -1869,7 +1869,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"escape_string_warning", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"escape_string_warning", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Warn about backslash escapes in ordinary string literals."),
NULL
},
@@ -1879,7 +1879,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"standard_conforming_strings", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"standard_conforming_strings", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Causes '...' strings to treat backslashes literally."),
NULL,
GUC_REPORT
@@ -1890,7 +1890,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"synchronize_seqscans", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"synchronize_seqscans", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Enable synchronized sequential scans."),
NULL
},
@@ -1975,7 +1975,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"quote_all_identifiers", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"quote_all_identifiers", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("When generating SQL fragments, quote all identifiers."),
NULL,
},
@@ -2016,7 +2016,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"parallel_leader_participation", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"parallel_leader_participation", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Controls whether Gather and Gather Merge also run subplans."),
gettext_noop("Should gather nodes also run subplans or just gather tuples?"),
GUC_EXPLAIN
@@ -2027,7 +2027,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"jit", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"jit", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Allow JIT compilation."),
NULL,
GUC_EXPLAIN
@@ -2066,7 +2066,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"jit_expressions", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"jit_expressions", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Allow JIT compilation of expressions."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -2094,7 +2094,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"jit_tuple_deforming", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"jit_tuple_deforming", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Allow JIT compilation of tuple deforming."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -2154,7 +2154,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"default_statistics_target", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"default_statistics_target", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the default statistics target."),
gettext_noop("This applies to table columns that have not had a "
"column-specific target set via ALTER TABLE SET STATISTICS.")
@@ -2164,7 +2164,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"from_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"from_collapse_limit", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the FROM-list size beyond which subqueries "
"are not collapsed."),
gettext_noop("The planner will merge subqueries into upper "
@@ -2177,7 +2177,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"join_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"join_collapse_limit", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the FROM-list size beyond which JOIN "
"constructs are not flattened."),
gettext_noop("The planner will flatten explicit JOIN "
@@ -2190,7 +2190,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_threshold", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_threshold", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("Sets the threshold of FROM items beyond which GEQO is used."),
NULL,
GUC_EXPLAIN
@@ -2200,7 +2200,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_effort", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_effort", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: effort is used to set the default for other GEQO parameters."),
NULL,
GUC_EXPLAIN
@@ -2210,7 +2210,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_pool_size", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_pool_size", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: number of individuals in the population."),
gettext_noop("Zero selects a suitable default value."),
GUC_EXPLAIN
@@ -2220,7 +2220,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_generations", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_generations", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: number of iterations of the algorithm."),
gettext_noop("Zero selects a suitable default value."),
GUC_EXPLAIN
@@ -2367,7 +2367,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"temp_buffers", PGC_USERSET, RESOURCES_MEM,
+ {"temp_buffers", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum number of temporary buffers used by each session."),
NULL,
GUC_UNIT_BLOCKS | GUC_EXPLAIN
@@ -2432,7 +2432,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"work_mem", PGC_USERSET, RESOURCES_MEM,
+ {"work_mem", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum memory to be used for query workspaces."),
gettext_noop("This much memory can be used by each internal "
"sort operation and hash table before switching to "
@@ -2445,7 +2445,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"maintenance_work_mem", PGC_USERSET, RESOURCES_MEM,
+ {"maintenance_work_mem", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum memory to be used for maintenance operations."),
gettext_noop("This includes operations such as VACUUM and CREATE INDEX."),
GUC_UNIT_KB
@@ -2456,7 +2456,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"logical_decoding_work_mem", PGC_USERSET, RESOURCES_MEM,
+ {"logical_decoding_work_mem", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum memory to be used for logical decoding."),
gettext_noop("This much memory can be used by each internal "
"reorder buffer before spilling to disk."),
@@ -2495,7 +2495,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_page_hit", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_page_hit", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost for a page found in the buffer cache."),
NULL
},
@@ -2505,7 +2505,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_page_miss", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_page_miss", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost for a page not found in the buffer cache."),
NULL
},
@@ -2515,7 +2515,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_page_dirty", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_page_dirty", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost for a page dirtied by vacuum."),
NULL
},
@@ -2525,7 +2525,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_limit", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_limit", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost amount available before napping."),
NULL
},
@@ -2591,7 +2591,7 @@ static struct config_int ConfigureNamesInt[] =
#endif
{
- {"statement_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"statement_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed duration of any statement."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2602,7 +2602,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"lock_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"lock_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed duration of any wait for a lock."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2613,7 +2613,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"idle_in_transaction_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"idle_in_transaction_session_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed idle time between queries, when in a transaction."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2624,7 +2624,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"idle_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"idle_session_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed idle time between queries, when not in a transaction."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2635,7 +2635,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_freeze_min_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Minimum age at which VACUUM should freeze a table row."),
NULL
},
@@ -2645,7 +2645,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_freeze_table_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Age at which VACUUM should scan whole table to freeze tuples."),
NULL
},
@@ -2655,7 +2655,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_multixact_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_multixact_freeze_min_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Minimum age at which VACUUM should freeze a MultiXactId in a table row."),
NULL
},
@@ -2665,7 +2665,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_multixact_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_multixact_freeze_table_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Multixact age at which VACUUM should scan whole table to freeze tuples."),
NULL
},
@@ -2684,7 +2684,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"vacuum_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_failsafe_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
NULL
},
@@ -2693,7 +2693,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"vacuum_multixact_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_multixact_failsafe_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Multixact age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
NULL
},
@@ -2881,7 +2881,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"wal_skip_threshold", PGC_USERSET, WAL_SETTINGS,
+ {"wal_skip_threshold", PGC_SUSET, WAL_SETTINGS,
gettext_noop("Minimum size of new file to fsync instead of writing WAL."),
NULL,
GUC_UNIT_KB
@@ -2926,7 +2926,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"wal_sender_timeout", PGC_USERSET, REPLICATION_SENDING,
+ {"wal_sender_timeout", PGC_SUSET, REPLICATION_SENDING,
gettext_noop("Sets the maximum time to wait for WAL replication."),
NULL,
GUC_UNIT_MS
@@ -2949,7 +2949,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"commit_siblings", PGC_USERSET, WAL_SETTINGS,
+ {"commit_siblings", PGC_SUSET, WAL_SETTINGS,
gettext_noop("Sets the minimum number of concurrent open transactions "
"required before performing commit_delay."),
NULL
@@ -2960,7 +2960,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"extra_float_digits", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"extra_float_digits", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the number of digits displayed for floating-point values."),
gettext_noop("This affects real, double precision, and geometric data types. "
"A zero or negative parameter value is added to the standard "
@@ -3022,7 +3022,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"log_parameter_max_length_on_error", PGC_USERSET, LOGGING_WHAT,
+ {"log_parameter_max_length_on_error", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Sets the maximum length in bytes of data logged for bind "
"parameter values when logging statements, on error."),
gettext_noop("-1 to print values in full."),
@@ -3067,7 +3067,7 @@ static struct config_int ConfigureNamesInt[] =
{
{"effective_io_concurrency",
- PGC_USERSET,
+ PGC_SUSET,
RESOURCES_ASYNCHRONOUS,
gettext_noop("Number of simultaneous requests that can be handled efficiently by the disk subsystem."),
NULL,
@@ -3085,7 +3085,7 @@ static struct config_int ConfigureNamesInt[] =
{
{"maintenance_io_concurrency",
- PGC_USERSET,
+ PGC_SUSET,
RESOURCES_ASYNCHRONOUS,
gettext_noop("A variant of effective_io_concurrency that is used for maintenance work."),
NULL,
@@ -3102,7 +3102,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"backend_flush_after", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"backend_flush_after", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Number of pages after which previously performed writes are flushed to disk."),
NULL,
GUC_UNIT_BLOCKS
@@ -3334,7 +3334,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"max_parallel_maintenance_workers", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"max_parallel_maintenance_workers", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Sets the maximum number of parallel processes per maintenance operation."),
NULL
},
@@ -3344,7 +3344,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"max_parallel_workers_per_gather", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"max_parallel_workers_per_gather", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Sets the maximum number of parallel processes per executor node."),
NULL,
GUC_EXPLAIN
@@ -3355,7 +3355,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"max_parallel_workers", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"max_parallel_workers", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Sets the maximum number of parallel workers that can be active at one time."),
NULL,
GUC_EXPLAIN
@@ -3388,7 +3388,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_keepalives_idle", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_keepalives_idle", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Time between issuing TCP keepalives."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_S
@@ -3399,7 +3399,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_keepalives_interval", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_keepalives_interval", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Time between TCP keepalive retransmits."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_S
@@ -3410,7 +3410,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"ssl_renegotiation_limit", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"ssl_renegotiation_limit", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("SSL renegotiation is no longer supported; this can only be 0."),
NULL,
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE,
@@ -3421,7 +3421,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_keepalives_count", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_keepalives_count", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Maximum number of TCP keepalive retransmits."),
gettext_noop("This controls the number of consecutive keepalive retransmits that can be "
"lost before a connection is considered dead. A value of 0 uses the "
@@ -3433,7 +3433,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"gin_fuzzy_search_limit", PGC_USERSET, CLIENT_CONN_OTHER,
+ {"gin_fuzzy_search_limit", PGC_SUSET, CLIENT_CONN_OTHER,
gettext_noop("Sets the maximum allowed result for exact search by GIN."),
NULL,
0
@@ -3444,7 +3444,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"effective_cache_size", PGC_USERSET, QUERY_TUNING_COST,
+ {"effective_cache_size", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's assumption about the total size of the data caches."),
gettext_noop("That is, the total size of the caches (kernel cache and shared buffers) used for PostgreSQL data files. "
"This is measured in disk pages, which are normally 8 kB each."),
@@ -3456,7 +3456,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"min_parallel_table_scan_size", PGC_USERSET, QUERY_TUNING_COST,
+ {"min_parallel_table_scan_size", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the minimum amount of table data for a parallel scan."),
gettext_noop("If the planner estimates that it will read a number of table pages too small to reach this limit, a parallel scan will not be considered."),
GUC_UNIT_BLOCKS | GUC_EXPLAIN,
@@ -3467,7 +3467,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"min_parallel_index_scan_size", PGC_USERSET, QUERY_TUNING_COST,
+ {"min_parallel_index_scan_size", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the minimum amount of index data for a parallel scan."),
gettext_noop("If the planner estimates that it will read a number of index pages too small to reach this limit, a parallel scan will not be considered."),
GUC_UNIT_BLOCKS | GUC_EXPLAIN,
@@ -3512,7 +3512,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"gin_pending_list_limit", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"gin_pending_list_limit", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum size of the pending list for GIN index."),
NULL,
GUC_UNIT_KB
@@ -3523,7 +3523,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_user_timeout", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_user_timeout", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("TCP user timeout."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_MS
@@ -3568,7 +3568,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"client_connection_check_interval", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"client_connection_check_interval", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Sets the time interval between checks for disconnection while running queries."),
NULL,
GUC_UNIT_MS
@@ -3600,7 +3600,7 @@ static struct config_int ConfigureNamesInt[] =
static struct config_real ConfigureNamesReal[] =
{
{
- {"seq_page_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"seq_page_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of a "
"sequentially fetched disk page."),
NULL,
@@ -3611,7 +3611,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"random_page_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"random_page_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of a "
"nonsequentially fetched disk page."),
NULL,
@@ -3622,7 +3622,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"cpu_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"cpu_tuple_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"processing each tuple (row)."),
NULL,
@@ -3633,7 +3633,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"cpu_index_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"cpu_index_tuple_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"processing each index entry during an index scan."),
NULL,
@@ -3644,7 +3644,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"cpu_operator_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"cpu_operator_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"processing each operator or function call."),
NULL,
@@ -3655,7 +3655,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"parallel_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"parallel_tuple_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"passing each tuple (row) from worker to leader backend."),
NULL,
@@ -3666,7 +3666,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"parallel_setup_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"parallel_setup_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"starting up worker processes for parallel query."),
NULL,
@@ -3678,7 +3678,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"jit_above_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"jit_above_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Perform JIT compilation if query is more expensive."),
gettext_noop("-1 disables JIT compilation."),
GUC_EXPLAIN
@@ -3689,7 +3689,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"jit_optimize_above_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"jit_optimize_above_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Optimize JIT-compiled functions if query is more expensive."),
gettext_noop("-1 disables optimization."),
GUC_EXPLAIN
@@ -3700,7 +3700,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"jit_inline_above_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"jit_inline_above_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Perform JIT inlining if query is more expensive."),
gettext_noop("-1 disables inlining."),
GUC_EXPLAIN
@@ -3711,7 +3711,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"cursor_tuple_fraction", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"cursor_tuple_fraction", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the planner's estimate of the fraction of "
"a cursor's rows that will be retrieved."),
NULL,
@@ -3723,7 +3723,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"geqo_selection_bias", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_selection_bias", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: selective pressure within the population."),
NULL,
GUC_EXPLAIN
@@ -3734,7 +3734,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"geqo_seed", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_seed", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: seed for random path selection."),
NULL,
GUC_EXPLAIN
@@ -3745,7 +3745,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"hash_mem_multiplier", PGC_USERSET, RESOURCES_MEM,
+ {"hash_mem_multiplier", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Multiple of work_mem to use for hash tables."),
NULL,
GUC_EXPLAIN
@@ -3766,7 +3766,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"seed", PGC_USERSET, UNGROUPED,
+ {"seed", PGC_SUSET, UNGROUPED,
gettext_noop("Sets the seed for random-number generation."),
NULL,
GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -3777,7 +3777,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"vacuum_cost_delay", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_delay", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost delay in milliseconds."),
NULL,
GUC_UNIT_MS
@@ -4027,7 +4027,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"DateStyle", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"DateStyle", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the display format for date and time values."),
gettext_noop("Also controls interpretation of ambiguous "
"date inputs."),
@@ -4039,7 +4039,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"default_table_access_method", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_table_access_method", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default table access method for new tables."),
NULL,
GUC_IS_NAME
@@ -4050,7 +4050,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"default_tablespace", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_tablespace", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default tablespace to create tables and indexes in."),
gettext_noop("An empty string selects the database's default tablespace."),
GUC_IS_NAME
@@ -4061,7 +4061,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"temp_tablespaces", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE
@@ -4141,7 +4141,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"lc_monetary", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"lc_monetary", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the locale for formatting monetary amounts."),
NULL
},
@@ -4151,7 +4151,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"lc_numeric", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"lc_numeric", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the locale for formatting numbers."),
NULL
},
@@ -4161,7 +4161,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"lc_time", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"lc_time", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the locale for formatting date and time values."),
NULL
},
@@ -4193,7 +4193,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"local_preload_libraries", PGC_USERSET, CLIENT_CONN_PRELOAD,
+ {"local_preload_libraries", PGC_SUSET, CLIENT_CONN_PRELOAD,
gettext_noop("Lists unprivileged shared libraries to preload into each backend."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE
@@ -4204,7 +4204,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"search_path", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"search_path", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the schema search order for names that are not schema-qualified."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE | GUC_EXPLAIN
@@ -4240,7 +4240,7 @@ static struct config_string ConfigureNamesString[] =
{
/* Not for general use --- used by SET ROLE */
- {"role", PGC_USERSET, UNGROUPED,
+ {"role", PGC_SUSET, UNGROUPED,
gettext_noop("Sets the current role."),
NULL,
GUC_IS_NAME | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_NOT_WHILE_SEC_REST
@@ -4319,7 +4319,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"TimeZone", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"TimeZone", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the time zone for displaying and interpreting time stamps."),
NULL,
GUC_REPORT
@@ -4329,7 +4329,7 @@ static struct config_string ConfigureNamesString[] =
check_timezone, assign_timezone, show_timezone
},
{
- {"timezone_abbreviations", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"timezone_abbreviations", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Selects a file of time zone abbreviations."),
NULL
},
@@ -4522,7 +4522,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"default_text_search_config", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"default_text_search_config", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets default text search configuration."),
NULL
},
@@ -4584,7 +4584,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"application_name", PGC_USERSET, LOGGING_WHAT,
+ {"application_name", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Sets the application name to be reported in statistics and logs."),
NULL,
GUC_IS_NAME | GUC_REPORT | GUC_NOT_IN_SAMPLE
@@ -4648,7 +4648,7 @@ static struct config_string ConfigureNamesString[] =
static struct config_enum ConfigureNamesEnum[] =
{
{
- {"backslash_quote", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"backslash_quote", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Sets whether \"\\'\" is allowed in string literals."),
NULL
},
@@ -4658,7 +4658,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"bytea_output", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"bytea_output", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the output format for bytea."),
NULL
},
@@ -4668,7 +4668,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"client_min_messages", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"client_min_messages", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the message levels that are sent to the client."),
gettext_noop("Each level includes all the levels that follow it. The later"
" the level, the fewer messages are sent.")
@@ -4689,7 +4689,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"constraint_exclusion", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"constraint_exclusion", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Enables the planner to use constraints to optimize queries."),
gettext_noop("Table scans will be skipped if their constraints"
" guarantee that no rows match the query."),
@@ -4701,7 +4701,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"default_toast_compression", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_toast_compression", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default compression method for compressible values."),
NULL
},
@@ -4712,7 +4712,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"default_transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_transaction_isolation", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the transaction isolation level of each new transaction."),
NULL
},
@@ -4722,7 +4722,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"transaction_isolation", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the current transaction's isolation level."),
NULL,
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -4733,7 +4733,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"IntervalStyle", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"IntervalStyle", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the display format for interval values."),
NULL,
GUC_REPORT
@@ -4811,7 +4811,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"synchronous_commit", PGC_USERSET, WAL_SETTINGS,
+ {"synchronous_commit", PGC_SUSET, WAL_SETTINGS,
gettext_noop("Sets the current transaction's synchronization level."),
NULL
},
@@ -4918,7 +4918,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"xmlbinary", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"xmlbinary", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets how binary values are to be encoded in XML."),
NULL
},
@@ -4928,7 +4928,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"xmloption", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"xmloption", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets whether XML data in implicit parsing and serialization "
"operations is to be considered as documents or content fragments."),
NULL
@@ -4949,7 +4949,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"force_parallel_mode", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"force_parallel_mode", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Forces use of parallel query facilities."),
gettext_noop("If possible, run query using a parallel worker and with parallel restrictions."),
GUC_NOT_IN_SAMPLE | GUC_EXPLAIN
@@ -4960,7 +4960,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"password_encryption", PGC_USERSET, CONN_AUTH_AUTH,
+ {"password_encryption", PGC_SUSET, CONN_AUTH_AUTH,
gettext_noop("Chooses the algorithm for encrypting passwords."),
NULL
},
@@ -4970,7 +4970,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"plan_cache_mode", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"plan_cache_mode", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Controls the planner's selection of custom or generic plan."),
gettext_noop("Prepared statements can have custom and generic plans, and the planner "
"will attempt to choose which is better. This can be set to override "
@@ -5029,6 +5029,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_setting_acl
+ * entries.
*/
static const char *const map_old_guc_names[] = {
"sort_mem", "work_mem",
@@ -5428,25 +5432,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().)
+ *
+ * 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"
@@ -5463,8 +5471,24 @@ 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;
+}
+
+/*
+ * 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().)
+ */
+static bool
+valid_custom_variable_name(const char *name)
+{
+ int partcnt;
+
+ return (valid_variable_name(name, &partcnt) && partcnt > 1);
}
/*
@@ -7492,6 +7516,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 settingid = get_setting_oid(name, true);
+
+ if (OidIsValid(settingid))
+ {
+ AclResult aclresult;
+
+ aclresult = pg_setting_acl_aclcheck(settingid, GetUserId(),
+ ACL_SET_VALUE);
+
+ if (aclresult == ACLCHECK_OK)
+ break; /* okay */
+ }
+
+ /* No granted privilege */
ereport(elevel,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to set parameter \"%s\"",
@@ -8541,16 +8583,32 @@ 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;
+ Oid settingId;
+
+ settingId = get_setting_oid(name, true);
+ if (!OidIsValid(settingId))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+
+ aclresult = pg_setting_acl_aclcheck(settingId, GetUserId(),
+ ACL_ALTER_SYSTEM);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_SETTING, name);
+ }
+
switch (altersysstmt->setstmt->kind)
{
case VAR_SET_VALUE:
@@ -8743,6 +8801,9 @@ void
ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
{
GucAction action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET;
+ GucContext context;
+ AclResult aclresult;
+ Oid settingId;
/*
* Workers synchronize these parameters at the start of the parallel
@@ -8753,6 +8814,21 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
errmsg("cannot set parameters during a parallel operation")));
+ /*
+ * 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(settingId = get_setting_oid(stmt->name, true)))
+ {
+ aclresult = pg_setting_acl_aclcheck(settingId, GetUserId(),
+ ACL_SET_VALUE);
+ if (aclresult == ACLCHECK_OK)
+ context = PGC_SUSET;
+ }
+
switch (stmt->kind)
{
case VAR_SET_VALUE:
@@ -8761,7 +8837,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);
check_reserved_prefixes(stmt->name);
@@ -8847,7 +8923,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);
@@ -9631,6 +9707,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, false, 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,
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 403adb0ee7..39fbbabe03 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -163,6 +163,7 @@ static char *features_file;
static char *system_constraints_file;
static char *system_functions_file;
static char *system_views_file;
+static char *setting_privileges_file;
static bool success = false;
static bool made_new_pgdata = false;
static bool found_existing_pgdata = false;
@@ -2441,6 +2442,7 @@ setup_data_file_paths(void)
set_input(&system_constraints_file, "system_constraints.sql");
set_input(&system_functions_file, "system_functions.sql");
set_input(&system_views_file, "system_views.sql");
+ set_input(&setting_privileges_file, "setting_privileges.sql");
if (show_setting || debug)
{
@@ -2469,6 +2471,7 @@ setup_data_file_paths(void)
check_input(system_constraints_file);
check_input(system_functions_file);
check_input(system_views_file);
+ check_input(setting_privileges_file);
}
@@ -2816,6 +2819,8 @@ initialize_data_directory(void)
setup_run_file(cmdfd, system_views_file);
+ setup_run_file(cmdfd, setting_privileges_file);
+
setup_description(cmdfd);
setup_collation(cmdfd);
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index 6e216313c6..6552ef345c 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, SETTING or LARGE OBJECT)
* acls: the ACL string fetched from the database
* baseacls: the initial ACL string for this object; can be
* NULL or empty string to indicate "not available from server"
@@ -561,6 +561,11 @@ do { \
CONVERT_PRIV('U', "USAGE");
else if (strcmp(type, "FOREIGN TABLE") == 0)
CONVERT_PRIV('r', "SELECT");
+ else if (strcmp(type, "SETTING") == 0)
+ {
+ CONVERT_PRIV('s', "SET VALUE");
+ 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 59f4fbb2cc..0950d159c2 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -3413,6 +3413,7 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te)
strcmp(type, "SCHEMA") == 0 ||
strcmp(type, "EVENT TRIGGER") == 0 ||
strcmp(type, "FOREIGN DATA WRAPPER") == 0 ||
+ strcmp(type, "SETTING") == 0 ||
strcmp(type, "SERVER") == 0 ||
strcmp(type, "PUBLICATION") == 0 ||
strcmp(type, "SUBSCRIPTION") == 0 ||
@@ -3596,6 +3597,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, "SETTING") == 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 10a86f9810..5fd8923ce8 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -15236,6 +15236,9 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
case DEFACLOBJ_NAMESPACE:
type = "SCHEMAS";
break;
+ case DEFACLOBJ_SETTING:
+ type = "SETTINGS";
+ break;
default:
/* shouldn't get here */
fatal("unrecognized object type in default privileges: %d",
@@ -15279,7 +15282,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, SETTING 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 27b95732c8..62a5f612b0 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 dumpGroups(PGconn *conn);
static void dropTablespaces(PGconn *conn);
static void dumpTablespaces(PGconn *conn);
@@ -586,6 +587,10 @@ main(int argc, char *argv[])
dumpRoleMembership(conn);
else
dumpGroups(conn);
+
+ /* Dump role guc privileges */
+ if (server_version >= 150000)
+ dumpRoleGUCPrivs(conn);
}
/* Dump tablespaces */
@@ -1049,6 +1054,72 @@ dumpRoleMembership(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)
+{
+ PQExpBuffer buf = createPQExpBuffer();
+ PGresult *res;
+ int i;
+
+ printfPQExpBuffer(buf, "SELECT string_agg(acl.privilege_type, ', ' ORDER BY acl.privilege_type), "
+ "set_acl.setting, "
+ "grantee.rolname AS grantee, "
+ "acl.is_grantable, "
+ "grantor.rolname AS grantor "
+ "FROM pg_catalog.pg_setting_acl set_acl, "
+ "LATERAL (SELECT * FROM aclexplode(set_acl.setacl)) acl "
+ "JOIN pg_catalog.pg_authid grantee "
+ "ON acl.grantee = grantee.oid "
+ "LEFT JOIN pg_catalog.pg_authid grantor ON "
+ "acl.grantor = grantor.oid "
+ "WHERE acl.grantee > 0 "
+ "GROUP BY setting, grantee.rolname, is_grantable, grantor.rolname"
+ );
+
+ res = executeQuery(conn, buf->data);
+
+ if (PQntuples(res) > 0)
+ fprintf(OPF, "--\n-- Role privileges on configuration parameters\n--\n\n");
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ char *privilege = PQgetvalue(res, i, 0);
+ char *setting = PQgetvalue(res, i, 1);
+ char *grantee = PQgetvalue(res, i, 2);
+ char *grantable = PQgetvalue(res, i, 3);
+
+ fprintf(OPF, "GRANT %s", privilege);
+ fprintf(OPF, " ON %s", setting);
+ fprintf(OPF, " TO %s", fmtId(grantee));
+ if (*grantable == 't')
+ fprintf(OPF, " WITH GRANT OPTION");
+
+ /*
+ * We don't track the grantor very carefully in the backend, so cope
+ * with the possibility that it has been dropped.
+ */
+ if (!PQgetisnull(res, i, 4))
+ {
+ char *grantor = PQgetvalue(res, i, 4);
+
+ fprintf(OPF, " GRANTED BY %s", fmtId(grantor));
+ }
+ fprintf(OPF, ";\n");
+ }
+
+ PQclear(res);
+ destroyPQExpBuffer(buf);
+
+ fprintf(OPF, "\n\n");
+}
+
/*
* Dump group memberships from a pre-8.1 server. It's annoying that we
* can't share any useful amount of code with the post-8.1 case, but
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 3eca295ff4..a800f0b544 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -92,6 +92,7 @@ typedef enum ObjectClass
OCLASS_TYPE, /* pg_type */
OCLASS_CAST, /* pg_cast */
OCLASS_COLLATION, /* pg_collation */
+ OCLASS_SETTING, /* pg_setting_acl */
OCLASS_CONSTRAINT, /* pg_constraint */
OCLASS_CONVERSION, /* pg_conversion */
OCLASS_DEFAULT, /* pg_attrdef */
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index eb72dd6293..08a2dcee41 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_SETTING 'c' /* configuration parameter */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 79d787cd26..b6f1159931 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7214,6 +7214,25 @@
proname => 'has_type_privilege', provolatile => 's', prorettype => 'bool',
proargtypes => 'oid text', prosrc => 'has_type_privilege_id' },
+{ oid => '8050', descr => 'user privilege on setting by username, setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'name text text', prosrc => 'has_setting_privilege_name_name' },
+{ oid => '8051', descr => 'user privilege on setting by username, setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'name oid text', prosrc => 'has_setting_privilege_name_id' },
+{ oid => '8052', descr => 'user privilege on setting by user oid, setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid text text', prosrc => 'has_setting_privilege_id_name' },
+{ oid => '8053', descr => 'user privilege on setting by user oid, setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid oid text', prosrc => 'has_setting_privilege_id_id' },
+{ oid => '8054', descr => 'current user privilege on setting by setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'text text', prosrc => 'has_setting_privilege_name' },
+{ oid => '8055', descr => 'current user privilege on setting by setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid text', prosrc => 'has_setting_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/catalog/pg_setting_acl.h b/src/include/catalog/pg_setting_acl.h
new file mode 100644
index 0000000000..b7dee55e5c
--- /dev/null
+++ b/src/include/catalog/pg_setting_acl.h
@@ -0,0 +1,63 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_setting_acl.h
+ * definition of the "configuration parameter" system catalog
+ * (pg_setting_acl).
+ *
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_setting_acl.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SETTING_ACL_H
+#define PG_SETTING_ACL_H
+
+#include "catalog/genbki.h"
+#include "catalog/pg_setting_acl_d.h"
+
+/* ----------------
+ * pg_setting_acl definition. cpp turns this into
+ * typedef struct FormData_pg_setting_acl
+ * ----------------
+ */
+CATALOG(pg_setting_acl,8924,SettingAclRelationId) BKI_SHARED_RELATION
+{
+ Oid oid; /* oid */
+ /*
+
+ * Variable-length fields start here, but we allow direct access to
+ * setting.
+ */
+ text setting BKI_FORCE_NOT_NULL;
+
+#ifdef CATALOG_VARLEN
+ /* Access privileges */
+ aclitem setacl[1] BKI_DEFAULT(_null_);
+#endif
+} FormData_pg_setting_acl;
+
+
+/* ----------------
+ * Form_pg_setting_acl corresponds to a pointer to a tuple with
+ * the format of pg_setting_acl relation.
+ * ----------------
+ */
+typedef FormData_pg_setting_acl *Form_pg_setting_acl;
+
+DECLARE_TOAST(pg_setting_acl, 8925, 8926);
+#define PgSettingAclToastTable 8925
+#define PgSettingAclToastIndex 8926
+
+DECLARE_UNIQUE_INDEX(pg_setting_acl_setting_index, 8927, SettingAclSettingIndexId, on pg_setting_acl using btree(setting text_ops));
+DECLARE_UNIQUE_INDEX_PKEY(pg_setting_acl_oid_index, 8928, SettingAclOidIndexId, on pg_setting_acl using btree(oid oid_ops));
+
+extern Oid SettingAclCreate(const char *setting, bool if_not_exists);
+
+#endif /* PG_SETTING_ACL_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 4c5a8a39bf..1cd57d5552 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -92,7 +92,9 @@ 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 N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */
+#define ACL_SET_VALUE (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
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
@@ -1794,6 +1796,7 @@ typedef enum ObjectType
OBJECT_CAST,
OBJECT_COLUMN,
OBJECT_COLLATION,
+ OBJECT_SETTING,
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DEFAULT,
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f836acf876..527e723b39 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -307,6 +307,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 af771c901d..70a6d83336 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -146,9 +146,11 @@ 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_ALTER_SYSTEM_CHR 'A'
/* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTc"
+#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcsA"
/*
* Bitmasks defining "all rights" for each supported object type
@@ -165,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_SETTING (ACL_SET_VALUE|ACL_ALTER_SYSTEM)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -223,6 +226,8 @@ extern void select_best_grantor(Oid roleId, AclMode privileges,
extern void initialize_acl(void);
+extern Oid get_setting_oid(const char *setting, bool missing_ok);
+
/*
* prototypes for functions in aclchk.c
*/
@@ -243,6 +248,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_setting_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,
@@ -271,6 +278,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_setting_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 aa18d304ac..fb2fe193c5 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -379,6 +379,8 @@ extern int set_config_option(const char *name, const char *value,
GucAction action, bool changeVal, int elevel,
bool is_reload);
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 77871aaefc..a620eb6dca 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_setting_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 c8cfbc30f6..27cf1c6088 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -89,6 +89,8 @@ enum SysCacheIdentifier
REPLORIGNAME,
RULERELNAME,
SEQRELID,
+ SETTINGNAME,
+ SETTINGOID,
STATEXTDATASTXOID,
STATEXTNAMENSP,
STATEXTOID,
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 16f7610883..f8071eca00 100644
--- a/src/test/modules/test_pg_dump/t/001_base.pl
+++ b/src/test/modules/test_pg_dump/t/001_base.pl
@@ -317,6 +317,53 @@ my %tests = (
like => { pg_dumpall_globals => 1, },
},
+ 'GRANT ALTER SYSTEM ON ignore_checksum_failure' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT ALTER SYSTEM ON ignore_checksum_failure TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM ON ignore_checksum_failure TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT SET VALUE ON my.missing.guc' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET VALUE ON my.missing.guc TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT SET VALUE ON my\.missing\.guc TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT SET VALUE, ALTER SYSTEM ON something WITH GRANT OPTION' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET VALUE, ALTER SYSTEM ON something TO regress_dump_test_role WITH GRANT OPTION;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM, SET VALUE ON something TO regress_dump_test_role WITH GRANT OPTION GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALTER SYSTEM ON MyReallyLong.ButValidCustom.GucNameThatCannotFit.InNamedata64Byte.Format' => {
+ create_order => 2,
+ create_sql =>
+ # configuration parameters get cased folded
+ 'GRANT ALTER SYSTEM ON MyReallyLong.ButValidCustom.GucNameThatCannotFit.InNamedata64Byte.Format TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM ON myreallylong\.butvalidcustom\.gucnamethatcannotfit\.innamedata64byte\.format TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALTER SYSTEM, SET VALUE ON my.guc TO regress_dump_test_role GRANTED BY CURRENT_ROLE' => {
+ create_order => 2,
+ create_sql =>
+ # GRANTED BY CURRENT_ROLE is allowed for SQL compatibility, but is ignored
+ 'GRANT ALTER SYSTEM, SET VALUE ON my.guc TO regress_dump_test_role GRANTED BY CURRENT_ROLE;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM, SET VALUE ON my\.guc TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
'CREATE SCHEMA public' => {
regexp => qr/^CREATE SCHEMA public;/m,
like => {
diff --git a/src/test/regress/expected/guc_privs.out b/src/test/regress/expected/guc_privs.out
new file mode 100644
index 0000000000..f8bc8e7276
--- /dev/null
+++ b/src/test/regress/expected/guc_privs.out
@@ -0,0 +1,780 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform all 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 = 'en_US.UTF-8'; -- ok
+RESET lc_messages; -- ok
+ALTER SYSTEM SET lc_messages = 'en_US.UTF-8'; -- 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;
+-- Check setting privileges prior to creating any
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+ grantee | setting | privilege_type | is_grantable
+---------+-------------------------------------+----------------+--------------
+ | DateStyle | SET VALUE | f
+ | IntervalStyle | SET VALUE | f
+ | TimeZone | SET VALUE | f
+ | application_name | SET VALUE | f
+ | array_nulls | SET VALUE | f
+ | backend_flush_after | SET VALUE | f
+ | backslash_quote | SET VALUE | f
+ | bytea_output | SET VALUE | f
+ | check_function_bodies | SET VALUE | f
+ | client_connection_check_interval | SET VALUE | f
+ | client_min_messages | SET VALUE | f
+ | commit_siblings | SET VALUE | f
+ | constraint_exclusion | SET VALUE | f
+ | cpu_index_tuple_cost | SET VALUE | f
+ | cpu_operator_cost | SET VALUE | f
+ | cpu_tuple_cost | SET VALUE | f
+ | cursor_tuple_fraction | SET VALUE | f
+ | debug_pretty_print | SET VALUE | f
+ | debug_print_parse | SET VALUE | f
+ | debug_print_plan | SET VALUE | f
+ | debug_print_rewritten | SET VALUE | f
+ | default_statistics_target | SET VALUE | f
+ | default_table_access_method | SET VALUE | f
+ | default_tablespace | SET VALUE | f
+ | default_text_search_config | SET VALUE | f
+ | default_toast_compression | SET VALUE | f
+ | default_transaction_deferrable | SET VALUE | f
+ | default_transaction_isolation | SET VALUE | f
+ | default_transaction_read_only | SET VALUE | f
+ | default_with_oids | SET VALUE | f
+ | effective_cache_size | SET VALUE | f
+ | effective_io_concurrency | SET VALUE | f
+ | enable_async_append | SET VALUE | f
+ | enable_bitmapscan | SET VALUE | f
+ | enable_gathermerge | SET VALUE | f
+ | enable_hashagg | SET VALUE | f
+ | enable_hashjoin | SET VALUE | f
+ | enable_incremental_sort | SET VALUE | f
+ | enable_indexonlyscan | SET VALUE | f
+ | enable_indexscan | SET VALUE | f
+ | enable_material | SET VALUE | f
+ | enable_memoize | SET VALUE | f
+ | enable_mergejoin | SET VALUE | f
+ | enable_nestloop | SET VALUE | f
+ | enable_parallel_append | SET VALUE | f
+ | enable_parallel_hash | SET VALUE | f
+ | enable_partition_pruning | SET VALUE | f
+ | enable_partitionwise_aggregate | SET VALUE | f
+ | enable_partitionwise_join | SET VALUE | f
+ | enable_seqscan | SET VALUE | f
+ | enable_sort | SET VALUE | f
+ | enable_tidscan | SET VALUE | f
+ | escape_string_warning | SET VALUE | f
+ | exit_on_error | SET VALUE | f
+ | extra_float_digits | SET VALUE | f
+ | force_parallel_mode | SET VALUE | f
+ | from_collapse_limit | SET VALUE | f
+ | geqo | SET VALUE | f
+ | geqo_effort | SET VALUE | f
+ | geqo_generations | SET VALUE | f
+ | geqo_pool_size | SET VALUE | f
+ | geqo_seed | SET VALUE | f
+ | geqo_selection_bias | SET VALUE | f
+ | geqo_threshold | SET VALUE | f
+ | gin_fuzzy_search_limit | SET VALUE | f
+ | gin_pending_list_limit | SET VALUE | f
+ | hash_mem_multiplier | SET VALUE | f
+ | idle_in_transaction_session_timeout | SET VALUE | f
+ | idle_session_timeout | SET VALUE | f
+ | jit | SET VALUE | f
+ | jit_above_cost | SET VALUE | f
+ | jit_expressions | SET VALUE | f
+ | jit_inline_above_cost | SET VALUE | f
+ | jit_optimize_above_cost | SET VALUE | f
+ | jit_tuple_deforming | SET VALUE | f
+ | join_collapse_limit | SET VALUE | f
+ | lc_monetary | SET VALUE | f
+ | lc_numeric | SET VALUE | f
+ | lc_time | SET VALUE | f
+ | local_preload_libraries | SET VALUE | f
+ | lock_timeout | SET VALUE | f
+ | log_parameter_max_length_on_error | SET VALUE | f
+ | logical_decoding_work_mem | SET VALUE | f
+ | maintenance_io_concurrency | SET VALUE | f
+ | maintenance_work_mem | SET VALUE | f
+ | max_parallel_maintenance_workers | SET VALUE | f
+ | max_parallel_workers | SET VALUE | f
+ | max_parallel_workers_per_gather | SET VALUE | f
+ | min_parallel_index_scan_size | SET VALUE | f
+ | min_parallel_table_scan_size | SET VALUE | f
+ | optimize_bounded_sort | SET VALUE | f
+ | parallel_leader_participation | SET VALUE | f
+ | parallel_setup_cost | SET VALUE | f
+ | parallel_tuple_cost | SET VALUE | f
+ | password_encryption | SET VALUE | f
+ | plan_cache_mode | SET VALUE | f
+ | quote_all_identifiers | SET VALUE | f
+ | random_page_cost | SET VALUE | f
+ | role | SET VALUE | f
+ | row_security | SET VALUE | f
+ | search_path | SET VALUE | f
+ | seed | SET VALUE | f
+ | seq_page_cost | SET VALUE | f
+ | ssl_renegotiation_limit | SET VALUE | f
+ | standard_conforming_strings | SET VALUE | f
+ | statement_timeout | SET VALUE | f
+ | synchronize_seqscans | SET VALUE | f
+ | synchronous_commit | SET VALUE | f
+ | tcp_keepalives_count | SET VALUE | f
+ | tcp_keepalives_idle | SET VALUE | f
+ | tcp_keepalives_interval | SET VALUE | f
+ | tcp_user_timeout | SET VALUE | f
+ | temp_buffers | SET VALUE | f
+ | temp_tablespaces | SET VALUE | f
+ | timezone_abbreviations | SET VALUE | f
+ | trace_notify | SET VALUE | f
+ | trace_sort | SET VALUE | f
+ | trace_syncscan | SET VALUE | f
+ | transaction_deferrable | SET VALUE | f
+ | transaction_isolation | SET VALUE | f
+ | transaction_read_only | SET VALUE | f
+ | transform_null_equals | SET VALUE | f
+ | vacuum_cost_delay | SET VALUE | f
+ | vacuum_cost_limit | SET VALUE | f
+ | vacuum_cost_page_dirty | SET VALUE | f
+ | vacuum_cost_page_hit | SET VALUE | f
+ | vacuum_cost_page_miss | SET VALUE | f
+ | vacuum_failsafe_age | SET VALUE | f
+ | vacuum_freeze_min_age | SET VALUE | f
+ | vacuum_freeze_table_age | SET VALUE | f
+ | vacuum_multixact_failsafe_age | SET VALUE | f
+ | vacuum_multixact_freeze_min_age | SET VALUE | f
+ | vacuum_multixact_freeze_table_age | SET VALUE | f
+ | wal_sender_timeout | SET VALUE | f
+ | wal_skip_threshold | SET VALUE | f
+ | work_mem | SET VALUE | f
+ | xmlbinary | SET VALUE | f
+ | xmloption | SET VALUE | f
+(138 rows)
+
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+-- Check the new role does not yet have privileges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ f
+(1 row)
+
+-- Check inappropriate and nonsense privilege types
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+-- Grant privileges on settings to the new non-superuser role
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Check setting privileges after creating some
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+ grantee | setting | privilege_type | is_grantable
+-----------------------------+-------------------------------------+----------------+--------------
+ regress_host_resource_admin | autovacuum_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | autovacuum_work_mem | SET VALUE | f
+ regress_host_resource_admin | hash_mem_multiplier | ALTER SYSTEM | f
+ regress_host_resource_admin | hash_mem_multiplier | SET VALUE | f
+ regress_host_resource_admin | logical_decoding_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | logical_decoding_work_mem | SET VALUE | f
+ regress_host_resource_admin | maintenance_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | maintenance_work_mem | SET VALUE | f
+ regress_host_resource_admin | max_stack_depth | ALTER SYSTEM | f
+ regress_host_resource_admin | max_stack_depth | SET VALUE | f
+ regress_host_resource_admin | min_dynamic_shared_memory | ALTER SYSTEM | f
+ regress_host_resource_admin | min_dynamic_shared_memory | SET VALUE | f
+ regress_host_resource_admin | shared_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | shared_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_file_limit | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_file_limit | SET VALUE | f
+ regress_host_resource_admin | work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | work_mem | SET VALUE | f
+ | DateStyle | SET VALUE | f
+ | IntervalStyle | SET VALUE | f
+ | TimeZone | SET VALUE | f
+ | application_name | SET VALUE | f
+ | array_nulls | SET VALUE | f
+ | backend_flush_after | SET VALUE | f
+ | backslash_quote | SET VALUE | f
+ | bytea_output | SET VALUE | f
+ | check_function_bodies | SET VALUE | f
+ | client_connection_check_interval | SET VALUE | f
+ | client_min_messages | SET VALUE | f
+ | commit_siblings | SET VALUE | f
+ | constraint_exclusion | SET VALUE | f
+ | cpu_index_tuple_cost | SET VALUE | f
+ | cpu_operator_cost | SET VALUE | f
+ | cpu_tuple_cost | SET VALUE | f
+ | cursor_tuple_fraction | SET VALUE | f
+ | debug_pretty_print | SET VALUE | f
+ | debug_print_parse | SET VALUE | f
+ | debug_print_plan | SET VALUE | f
+ | debug_print_rewritten | SET VALUE | f
+ | default_statistics_target | SET VALUE | f
+ | default_table_access_method | SET VALUE | f
+ | default_tablespace | SET VALUE | f
+ | default_text_search_config | SET VALUE | f
+ | default_toast_compression | SET VALUE | f
+ | default_transaction_deferrable | SET VALUE | f
+ | default_transaction_isolation | SET VALUE | f
+ | default_transaction_read_only | SET VALUE | f
+ | default_with_oids | SET VALUE | f
+ | effective_cache_size | SET VALUE | f
+ | effective_io_concurrency | SET VALUE | f
+ | enable_async_append | SET VALUE | f
+ | enable_bitmapscan | SET VALUE | f
+ | enable_gathermerge | SET VALUE | f
+ | enable_hashagg | SET VALUE | f
+ | enable_hashjoin | SET VALUE | f
+ | enable_incremental_sort | SET VALUE | f
+ | enable_indexonlyscan | SET VALUE | f
+ | enable_indexscan | SET VALUE | f
+ | enable_material | SET VALUE | f
+ | enable_memoize | SET VALUE | f
+ | enable_mergejoin | SET VALUE | f
+ | enable_nestloop | SET VALUE | f
+ | enable_parallel_append | SET VALUE | f
+ | enable_parallel_hash | SET VALUE | f
+ | enable_partition_pruning | SET VALUE | f
+ | enable_partitionwise_aggregate | SET VALUE | f
+ | enable_partitionwise_join | SET VALUE | f
+ | enable_seqscan | SET VALUE | f
+ | enable_sort | SET VALUE | f
+ | enable_tidscan | SET VALUE | f
+ | escape_string_warning | SET VALUE | f
+ | exit_on_error | SET VALUE | f
+ | extra_float_digits | SET VALUE | f
+ | force_parallel_mode | SET VALUE | f
+ | from_collapse_limit | SET VALUE | f
+ | geqo | SET VALUE | f
+ | geqo_effort | SET VALUE | f
+ | geqo_generations | SET VALUE | f
+ | geqo_pool_size | SET VALUE | f
+ | geqo_seed | SET VALUE | f
+ | geqo_selection_bias | SET VALUE | f
+ | geqo_threshold | SET VALUE | f
+ | gin_fuzzy_search_limit | SET VALUE | f
+ | gin_pending_list_limit | SET VALUE | f
+ | hash_mem_multiplier | SET VALUE | f
+ | idle_in_transaction_session_timeout | SET VALUE | f
+ | idle_session_timeout | SET VALUE | f
+ | jit | SET VALUE | f
+ | jit_above_cost | SET VALUE | f
+ | jit_expressions | SET VALUE | f
+ | jit_inline_above_cost | SET VALUE | f
+ | jit_optimize_above_cost | SET VALUE | f
+ | jit_tuple_deforming | SET VALUE | f
+ | join_collapse_limit | SET VALUE | f
+ | lc_monetary | SET VALUE | f
+ | lc_numeric | SET VALUE | f
+ | lc_time | SET VALUE | f
+ | local_preload_libraries | SET VALUE | f
+ | lock_timeout | SET VALUE | f
+ | log_parameter_max_length_on_error | SET VALUE | f
+ | logical_decoding_work_mem | SET VALUE | f
+ | maintenance_io_concurrency | SET VALUE | f
+ | maintenance_work_mem | SET VALUE | f
+ | max_parallel_maintenance_workers | SET VALUE | f
+ | max_parallel_workers | SET VALUE | f
+ | max_parallel_workers_per_gather | SET VALUE | f
+ | min_parallel_index_scan_size | SET VALUE | f
+ | min_parallel_table_scan_size | SET VALUE | f
+ | optimize_bounded_sort | SET VALUE | f
+ | parallel_leader_participation | SET VALUE | f
+ | parallel_setup_cost | SET VALUE | f
+ | parallel_tuple_cost | SET VALUE | f
+ | password_encryption | SET VALUE | f
+ | plan_cache_mode | SET VALUE | f
+ | quote_all_identifiers | SET VALUE | f
+ | random_page_cost | SET VALUE | f
+ | role | SET VALUE | f
+ | row_security | SET VALUE | f
+ | search_path | SET VALUE | f
+ | seed | SET VALUE | f
+ | seq_page_cost | SET VALUE | f
+ | ssl_renegotiation_limit | SET VALUE | f
+ | standard_conforming_strings | SET VALUE | f
+ | statement_timeout | SET VALUE | f
+ | synchronize_seqscans | SET VALUE | f
+ | synchronous_commit | SET VALUE | f
+ | tcp_keepalives_count | SET VALUE | f
+ | tcp_keepalives_idle | SET VALUE | f
+ | tcp_keepalives_interval | SET VALUE | f
+ | tcp_user_timeout | SET VALUE | f
+ | temp_buffers | SET VALUE | f
+ | temp_tablespaces | SET VALUE | f
+ | timezone_abbreviations | SET VALUE | f
+ | trace_notify | SET VALUE | f
+ | trace_sort | SET VALUE | f
+ | trace_syncscan | SET VALUE | f
+ | transaction_deferrable | SET VALUE | f
+ | transaction_isolation | SET VALUE | f
+ | transaction_read_only | SET VALUE | f
+ | transform_null_equals | SET VALUE | f
+ | vacuum_cost_delay | SET VALUE | f
+ | vacuum_cost_limit | SET VALUE | f
+ | vacuum_cost_page_dirty | SET VALUE | f
+ | vacuum_cost_page_hit | SET VALUE | f
+ | vacuum_cost_page_miss | SET VALUE | f
+ | vacuum_failsafe_age | SET VALUE | f
+ | vacuum_freeze_min_age | SET VALUE | f
+ | vacuum_freeze_table_age | SET VALUE | f
+ | vacuum_multixact_failsafe_age | SET VALUE | f
+ | vacuum_multixact_freeze_min_age | SET VALUE | f
+ | vacuum_multixact_freeze_table_age | SET VALUE | f
+ | wal_sender_timeout | SET VALUE | f
+ | wal_skip_threshold | SET VALUE | f
+ | work_mem | SET VALUE | f
+ | xmlbinary | SET VALUE | f
+ | xmloption | SET VALUE | f
+(158 rows)
+
+-- Check the new role now has privilges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE WITH GRANT OPTION, ALTER SYSTEM WITH GRANT OPTION');
+ has_setting_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_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+SELECT has_setting_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_setting_privilege('regress_host_resource_admin',
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('hash_mem_multiplier', 'set value');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'work_mem'),
+ 'alter system with grant option');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+-- Check setting privileges show up in view
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+ grantee | setting | privilege_type | is_grantable
+-----------------------------+-------------------------------------+----------------+--------------
+ regress_host_resource_admin | autovacuum_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | autovacuum_work_mem | SET VALUE | f
+ regress_host_resource_admin | hash_mem_multiplier | ALTER SYSTEM | f
+ regress_host_resource_admin | hash_mem_multiplier | SET VALUE | f
+ regress_host_resource_admin | logical_decoding_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | logical_decoding_work_mem | SET VALUE | f
+ regress_host_resource_admin | maintenance_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | maintenance_work_mem | SET VALUE | f
+ regress_host_resource_admin | max_stack_depth | ALTER SYSTEM | f
+ regress_host_resource_admin | max_stack_depth | SET VALUE | f
+ regress_host_resource_admin | min_dynamic_shared_memory | ALTER SYSTEM | f
+ regress_host_resource_admin | min_dynamic_shared_memory | SET VALUE | f
+ regress_host_resource_admin | shared_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | shared_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_file_limit | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_file_limit | SET VALUE | f
+ regress_host_resource_admin | work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | work_mem | SET VALUE | f
+ | DateStyle | SET VALUE | f
+ | IntervalStyle | SET VALUE | f
+ | TimeZone | SET VALUE | f
+ | application_name | SET VALUE | f
+ | array_nulls | SET VALUE | f
+ | backend_flush_after | SET VALUE | f
+ | backslash_quote | SET VALUE | f
+ | bytea_output | SET VALUE | f
+ | check_function_bodies | SET VALUE | f
+ | client_connection_check_interval | SET VALUE | f
+ | client_min_messages | SET VALUE | f
+ | commit_siblings | SET VALUE | f
+ | constraint_exclusion | SET VALUE | f
+ | cpu_index_tuple_cost | SET VALUE | f
+ | cpu_operator_cost | SET VALUE | f
+ | cpu_tuple_cost | SET VALUE | f
+ | cursor_tuple_fraction | SET VALUE | f
+ | debug_pretty_print | SET VALUE | f
+ | debug_print_parse | SET VALUE | f
+ | debug_print_plan | SET VALUE | f
+ | debug_print_rewritten | SET VALUE | f
+ | default_statistics_target | SET VALUE | f
+ | default_table_access_method | SET VALUE | f
+ | default_tablespace | SET VALUE | f
+ | default_text_search_config | SET VALUE | f
+ | default_toast_compression | SET VALUE | f
+ | default_transaction_deferrable | SET VALUE | f
+ | default_transaction_isolation | SET VALUE | f
+ | default_transaction_read_only | SET VALUE | f
+ | default_with_oids | SET VALUE | f
+ | effective_cache_size | SET VALUE | f
+ | effective_io_concurrency | SET VALUE | f
+ | enable_async_append | SET VALUE | f
+ | enable_bitmapscan | SET VALUE | f
+ | enable_gathermerge | SET VALUE | f
+ | enable_hashagg | SET VALUE | f
+ | enable_hashjoin | SET VALUE | f
+ | enable_incremental_sort | SET VALUE | f
+ | enable_indexonlyscan | SET VALUE | f
+ | enable_indexscan | SET VALUE | f
+ | enable_material | SET VALUE | f
+ | enable_memoize | SET VALUE | f
+ | enable_mergejoin | SET VALUE | f
+ | enable_nestloop | SET VALUE | f
+ | enable_parallel_append | SET VALUE | f
+ | enable_parallel_hash | SET VALUE | f
+ | enable_partition_pruning | SET VALUE | f
+ | enable_partitionwise_aggregate | SET VALUE | f
+ | enable_partitionwise_join | SET VALUE | f
+ | enable_seqscan | SET VALUE | f
+ | enable_sort | SET VALUE | f
+ | enable_tidscan | SET VALUE | f
+ | escape_string_warning | SET VALUE | f
+ | exit_on_error | SET VALUE | f
+ | extra_float_digits | SET VALUE | f
+ | force_parallel_mode | SET VALUE | f
+ | from_collapse_limit | SET VALUE | f
+ | geqo | SET VALUE | f
+ | geqo_effort | SET VALUE | f
+ | geqo_generations | SET VALUE | f
+ | geqo_pool_size | SET VALUE | f
+ | geqo_seed | SET VALUE | f
+ | geqo_selection_bias | SET VALUE | f
+ | geqo_threshold | SET VALUE | f
+ | gin_fuzzy_search_limit | SET VALUE | f
+ | gin_pending_list_limit | SET VALUE | f
+ | hash_mem_multiplier | SET VALUE | f
+ | idle_in_transaction_session_timeout | SET VALUE | f
+ | idle_session_timeout | SET VALUE | f
+ | jit | SET VALUE | f
+ | jit_above_cost | SET VALUE | f
+ | jit_expressions | SET VALUE | f
+ | jit_inline_above_cost | SET VALUE | f
+ | jit_optimize_above_cost | SET VALUE | f
+ | jit_tuple_deforming | SET VALUE | f
+ | join_collapse_limit | SET VALUE | f
+ | lc_monetary | SET VALUE | f
+ | lc_numeric | SET VALUE | f
+ | lc_time | SET VALUE | f
+ | local_preload_libraries | SET VALUE | f
+ | lock_timeout | SET VALUE | f
+ | log_parameter_max_length_on_error | SET VALUE | f
+ | logical_decoding_work_mem | SET VALUE | f
+ | maintenance_io_concurrency | SET VALUE | f
+ | maintenance_work_mem | SET VALUE | f
+ | max_parallel_maintenance_workers | SET VALUE | f
+ | max_parallel_workers | SET VALUE | f
+ | max_parallel_workers_per_gather | SET VALUE | f
+ | min_parallel_index_scan_size | SET VALUE | f
+ | min_parallel_table_scan_size | SET VALUE | f
+ | optimize_bounded_sort | SET VALUE | f
+ | parallel_leader_participation | SET VALUE | f
+ | parallel_setup_cost | SET VALUE | f
+ | parallel_tuple_cost | SET VALUE | f
+ | password_encryption | SET VALUE | f
+ | plan_cache_mode | SET VALUE | f
+ | quote_all_identifiers | SET VALUE | f
+ | random_page_cost | SET VALUE | f
+ | role | SET VALUE | f
+ | row_security | SET VALUE | f
+ | search_path | SET VALUE | f
+ | seed | SET VALUE | f
+ | seq_page_cost | SET VALUE | f
+ | ssl_renegotiation_limit | SET VALUE | f
+ | standard_conforming_strings | SET VALUE | f
+ | statement_timeout | SET VALUE | f
+ | synchronize_seqscans | SET VALUE | f
+ | synchronous_commit | SET VALUE | f
+ | tcp_keepalives_count | SET VALUE | f
+ | tcp_keepalives_idle | SET VALUE | f
+ | tcp_keepalives_interval | SET VALUE | f
+ | tcp_user_timeout | SET VALUE | f
+ | temp_buffers | SET VALUE | f
+ | temp_tablespaces | SET VALUE | f
+ | timezone_abbreviations | SET VALUE | f
+ | trace_notify | SET VALUE | f
+ | trace_sort | SET VALUE | f
+ | trace_syncscan | SET VALUE | f
+ | transaction_deferrable | SET VALUE | f
+ | transaction_isolation | SET VALUE | f
+ | transaction_read_only | SET VALUE | f
+ | transform_null_equals | SET VALUE | f
+ | vacuum_cost_delay | SET VALUE | f
+ | vacuum_cost_limit | SET VALUE | f
+ | vacuum_cost_page_dirty | SET VALUE | f
+ | vacuum_cost_page_hit | SET VALUE | f
+ | vacuum_cost_page_miss | SET VALUE | f
+ | vacuum_failsafe_age | SET VALUE | f
+ | vacuum_freeze_min_age | SET VALUE | f
+ | vacuum_freeze_table_age | SET VALUE | f
+ | vacuum_multixact_failsafe_age | SET VALUE | f
+ | vacuum_multixact_freeze_min_age | SET VALUE | f
+ | vacuum_multixact_freeze_table_age | SET VALUE | f
+ | wal_sender_timeout | SET VALUE | f
+ | wal_skip_threshold | SET VALUE | f
+ | work_mem | SET VALUE | f
+ | xmlbinary | SET VALUE | f
+ | xmloption | SET VALUE | f
+(158 rows)
+
+-- 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
+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
+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 setting temp_buffers
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting logical_decoding_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting autovacuum_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting temp_file_limit
+-- Use "revoke" to remove the privileges and allow the role to be dropped
+REVOKE SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 setting temp_buffers
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting logical_decoding_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting autovacuum_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting 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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 setting temp_buffers
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting logical_decoding_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting autovacuum_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting 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
+-- Create non-superuser with privileges to configure plgsql custom variables
+CREATE ROLE regress_plpgsql_admin NOSUPERUSER;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin;
+SET plpgsql.extra_errors TO 'all';
+SET plpgsql.extra_warnings TO 'all';
+RESET plpgsql.extra_errors;
+RESET plpgsql.extra_warnings;
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all';
+ERROR: permission denied to set parameter "plpgsql.extra_errors"
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all';
+ERROR: permission denied to set parameter "plpgsql.extra_warnings"
+ALTER SYSTEM RESET plpgsql.extra_errors;
+ERROR: permission denied to set parameter "plpgsql.extra_errors"
+ALTER SYSTEM RESET plpgsql.extra_warnings;
+ERROR: permission denied to set parameter "plpgsql.extra_warnings"
+SET SESSION AUTHORIZATION regress_admin;
+GRANT SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+TO regress_plpgsql_admin;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin; -- ok
+SET plpgsql.extra_errors TO 'all'; -- ok
+SET plpgsql.extra_warnings TO 'all'; -- ok
+RESET plpgsql.extra_errors; -- ok
+RESET plpgsql.extra_warnings; -- ok
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all'; -- ok
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all'; -- ok
+ALTER SYSTEM RESET plpgsql.extra_errors; -- ok
+ALTER SYSTEM RESET plpgsql.extra_warnings; -- ok
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_plpgsql_admin; -- fail, privileges remain
+ERROR: role "regress_plpgsql_admin" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting plpgsql.extra_warnings
+privileges for setting plpgsql.extra_errors
+REVOKE SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+FROM regress_plpgsql_admin;
+DROP ROLE regress_plpgsql_admin; -- ok
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 0bc79be03d..6c8afdba57 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -54,6 +54,33 @@ REVOKE pg_read_all_settings FROM regress_priv_user8;
DROP USER regress_priv_user10;
DROP USER regress_priv_user9;
DROP USER regress_priv_user8;
+GRANT SET VALUE ON enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON enable_nestloop TO regress_priv_user6;
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+GRANT ALTER SYSTEM ON enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON "" TO regress_priv_user7; -- bad name
+ERROR: zero-length delimited identifier at or near """"
+LINE 1: GRANT ALTER SYSTEM ON "" TO regress_priv_user7;
+ ^
+GRANT ALTER SYSTEM ON " " TO regress_priv_user7; -- bad name
+ERROR: invalid setting name " "
+GRANT ALTER SYSTEM ON " foo " TO regress_priv_user7; -- bad name
+ERROR: invalid setting name " foo "
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
ALTER GROUP regress_priv_group1 ADD USER regress_priv_user4;
@@ -2357,10 +2384,32 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+ERROR: role "regress_priv_user6" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting enable_memoize
+privileges for setting enable_nestloop
+DROP USER regress_priv_user7; -- privileges remain
+ERROR: role "regress_priv_user7" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting enable_seqscan
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for table persons2
+privileges for setting no_such_param
+privileges for setting no_such_extension.no_such_param
+privileges for setting no_such_extension.no_such_param.longer.than.maximum.namedata.length
DROP USER regress_priv_user8; -- does not exist
ERROR: role "regress_priv_user8" does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+DROP USER regress_priv_user7; -- ok
+REVOKE SET VALUE ON enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON enable_nestloop FROM regress_priv_user6;
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
CREATE TABLE lock_table (a int);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index b58b062b10..e262035269 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1708,6 +1708,19 @@ pg_sequences| SELECT n.nspname AS schemaname,
JOIN pg_class c ON ((c.oid = s.seqrelid)))
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char"));
+pg_setting_privileges| SELECT grantor.rolname AS grantor,
+ grantee.rolname AS grantee,
+ set_acl.setting,
+ acl.privilege_type,
+ acl.is_grantable
+ FROM pg_setting_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_settings| SELECT a.name,
a.setting,
a.unit,
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 63706a28cc..4a8208c789 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -147,6 +147,7 @@ pg_replication_origin|t
pg_rewrite|t
pg_seclabel|t
pg_sequence|t
+pg_setting_acl|t
pg_shdepend|t
pg_shdescription|t
pg_shseclabel|t
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 017e962fed..b45a203eb6 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -86,7 +86,7 @@ test: brin_bloom brin_multi
# ----------
# Another group of parallel tests
# ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort guc_privs
# rules cannot run concurrently with any test that creates
# a view or rule in the public schema
diff --git a/src/test/regress/sql/guc_privs.sql b/src/test/regress/sql/guc_privs.sql
new file mode 100644
index 0000000000..31728c5be8
--- /dev/null
+++ b/src/test/regress/sql/guc_privs.sql
@@ -0,0 +1,185 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform all 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 = 'en_US.UTF-8'; -- ok
+RESET lc_messages; -- ok
+ALTER SYSTEM SET lc_messages = 'en_US.UTF-8'; -- 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;
+-- Check setting privileges prior to creating any
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+-- Check the new role does not yet have privileges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+-- Check inappropriate and nonsense privilege types
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+-- Grant privileges on settings to the new non-superuser role
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Check setting privileges after creating some
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+-- Check the new role now has privilges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE 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_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER WITH GRANT OPTION');
+-- Check other function signatures
+SELECT has_setting_privilege('regress_host_resource_admin',
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET VALUE');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+SELECT has_setting_privilege('hash_mem_multiplier', 'set value');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'work_mem'),
+ 'alter system with grant option');
+-- Check setting privileges show up in view
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+-- 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
+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
+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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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
+-- Create non-superuser with privileges to configure plgsql custom variables
+CREATE ROLE regress_plpgsql_admin NOSUPERUSER;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin;
+SET plpgsql.extra_errors TO 'all';
+SET plpgsql.extra_warnings TO 'all';
+RESET plpgsql.extra_errors;
+RESET plpgsql.extra_warnings;
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all';
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all';
+ALTER SYSTEM RESET plpgsql.extra_errors;
+ALTER SYSTEM RESET plpgsql.extra_warnings;
+SET SESSION AUTHORIZATION regress_admin;
+GRANT SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+TO regress_plpgsql_admin;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin; -- ok
+SET plpgsql.extra_errors TO 'all'; -- ok
+SET plpgsql.extra_warnings TO 'all'; -- ok
+RESET plpgsql.extra_errors; -- ok
+RESET plpgsql.extra_warnings; -- ok
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all'; -- ok
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all'; -- ok
+ALTER SYSTEM RESET plpgsql.extra_errors; -- ok
+ALTER SYSTEM RESET plpgsql.extra_warnings; -- ok
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_plpgsql_admin; -- fail, privileges remain
+REVOKE SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+FROM regress_plpgsql_admin;
+DROP ROLE regress_plpgsql_admin; -- ok
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index c8c545b64c..ddbf6afa44 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -66,6 +66,33 @@ DROP USER regress_priv_user10;
DROP USER regress_priv_user9;
DROP USER regress_priv_user8;
+GRANT SET VALUE ON enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON enable_nestloop TO regress_priv_user6;
+
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+
+GRANT ALTER SYSTEM ON enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON "" TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON " " TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON " foo " TO regress_priv_user7; -- bad name
+
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
+
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
@@ -1426,10 +1453,23 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+DROP USER regress_priv_user7; -- privileges remain
DROP USER regress_priv_user8; -- does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+DROP USER regress_priv_user7; -- ok
+
+REVOKE SET VALUE ON enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON enable_nestloop FROM regress_priv_user6;
+
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
--
2.21.1 (Apple Git-122.3)
On Dec 13, 2021, at 1:33 PM, Mark Dilger <mark.dilger@enterprisedb.com> wrote:
but will repost in a few hours
... and here it is:
Attachments:
v5-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-SET-for-gucs.patchapplication/octet-stream; name=v5-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-SET-for-gucs.patch; x-unix-mode=0644Download
From cb693948b22a81d58e1593502b2ca8042b480ceb Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Mon, 13 Dec 2021 07:01:18 -0800
Subject: [PATCH v5] Allow GRANT of SET and ALTER SYSTEM SET for gucs
Allow granting of privilege to set or alter system set variables
which otherwise can only be managed by superusers. Each
(role,variable,privilege) triple is independently grantable, so a
user may be granted privilege to SET but not to ALTER SYSTEM SET on
a variable, or vice versa. The privilege to SET a userset variable
may be granted, though doing so has no practical effect, since any
role can set userset variables anyway. Worse, there is no way to
revoke the privilege to SET a userset variable. To remedy that,
most core userset variables have been changed to suset, with
explicit grants to set the variable to public.
---
contrib/pg_trgm/Makefile | 6 +-
contrib/pg_trgm/pg_trgm--1.6--1.7.sql | 8 +
contrib/pg_trgm/pg_trgm.control | 2 +-
contrib/postgres_fdw/Makefile | 3 +-
contrib/postgres_fdw/option.c | 2 +-
.../postgres_fdw/postgres_fdw--1.1--1.2.sql | 6 +
contrib/postgres_fdw/postgres_fdw.control | 2 +-
contrib/sepgsql/hooks.c | 2 +-
contrib/sepgsql/sepgsql.sql.in | 1 +
doc/src/sgml/catalogs.sgml | 75 +-
doc/src/sgml/ddl.sgml | 41 +-
doc/src/sgml/func.sgml | 20 +-
doc/src/sgml/ref/grant.sgml | 7 +
doc/src/sgml/ref/set.sgml | 5 +-
src/backend/catalog/Makefile | 6 +-
src/backend/catalog/aclchk.c | 251 ++++++
src/backend/catalog/catalog.c | 6 +
src/backend/catalog/dependency.c | 6 +
src/backend/catalog/objectaddress.c | 46 ++
src/backend/catalog/pg_setting_acl.c | 122 +++
src/backend/catalog/setting_privileges.sql | 62 ++
src/backend/catalog/system_views.sql | 13 +
src/backend/commands/alter.c | 1 +
src/backend/commands/dropcmds.c | 7 +
src/backend/commands/event_trigger.c | 6 +
src/backend/commands/seclabel.c | 1 +
src/backend/commands/tablecmds.c | 1 +
src/backend/parser/gram.y | 99 ++-
src/backend/utils/adt/acl.c | 258 ++++++
src/backend/utils/cache/lsyscache.c | 20 +
src/backend/utils/cache/syscache.c | 23 +
src/backend/utils/misc/guc.c | 402 +++++----
src/bin/initdb/initdb.c | 5 +
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 | 71 ++
src/include/catalog/dependency.h | 1 +
src/include/catalog/pg_default_acl.h | 1 +
src/include/catalog/pg_proc.dat | 19 +
src/include/catalog/pg_setting_acl.h | 63 ++
src/include/nodes/parsenodes.h | 5 +-
src/include/parser/kwlist.h | 1 +
src/include/utils/acl.h | 10 +-
src/include/utils/guc.h | 2 +
src/include/utils/lsyscache.h | 1 +
src/include/utils/syscache.h | 2 +
src/test/modules/test_pg_dump/t/001_base.pl | 47 ++
src/test/regress/expected/guc_privs.out | 780 ++++++++++++++++++
src/test/regress/expected/privileges.out | 53 +-
src/test/regress/expected/rules.out | 13 +
src/test/regress/expected/sanity_check.out | 1 +
src/test/regress/parallel_schedule | 2 +-
src/test/regress/sql/guc_privs.sql | 185 +++++
src/test/regress/sql/privileges.sql | 44 +-
55 files changed, 2641 insertions(+), 189 deletions(-)
create mode 100644 contrib/pg_trgm/pg_trgm--1.6--1.7.sql
create mode 100644 contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql
create mode 100644 src/backend/catalog/pg_setting_acl.c
create mode 100644 src/backend/catalog/setting_privileges.sql
create mode 100644 src/include/catalog/pg_setting_acl.h
create mode 100644 src/test/regress/expected/guc_privs.out
create mode 100644 src/test/regress/sql/guc_privs.sql
diff --git a/contrib/pg_trgm/Makefile b/contrib/pg_trgm/Makefile
index 1fbdc9ec1e..8fac4f6289 100644
--- a/contrib/pg_trgm/Makefile
+++ b/contrib/pg_trgm/Makefile
@@ -9,9 +9,9 @@ OBJS = \
trgm_regexp.o
EXTENSION = pg_trgm
-DATA = pg_trgm--1.5--1.6.sql pg_trgm--1.4--1.5.sql pg_trgm--1.3--1.4.sql \
- pg_trgm--1.3.sql pg_trgm--1.2--1.3.sql pg_trgm--1.1--1.2.sql \
- pg_trgm--1.0--1.1.sql
+DATA = pg_trgm--1.6--1.7.sql pg_trgm--1.5--1.6.sql pg_trgm--1.4--1.5.sql \
+ pg_trgm--1.3--1.4.sql pg_trgm--1.3.sql pg_trgm--1.2--1.3.sql \
+ pg_trgm--1.1--1.2.sql pg_trgm--1.0--1.1.sql
PGFILEDESC = "pg_trgm - trigram matching"
REGRESS = pg_trgm pg_word_trgm pg_strict_word_trgm
diff --git a/contrib/pg_trgm/pg_trgm--1.6--1.7.sql b/contrib/pg_trgm/pg_trgm--1.6--1.7.sql
new file mode 100644
index 0000000000..06b783acbd
--- /dev/null
+++ b/contrib/pg_trgm/pg_trgm--1.6--1.7.sql
@@ -0,0 +1,8 @@
+/* contrib/pg_trgm/pg_trgm--1.6--1.7.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_trgm UPDATE TO '1.7'" to load this file. \quit
+
+GRANT SET VALUE ON "pg_trgm.similarity_threshold" TO public;
+GRANT SET VALUE ON "pg_trgm.word_similarity_threshold" TO public;
+GRANT SET VALUE ON "pg_trgm.strict_word_similarity_threshold" TO public;
diff --git a/contrib/pg_trgm/pg_trgm.control b/contrib/pg_trgm/pg_trgm.control
index 1d6a9ddf25..6e3ee43c51 100644
--- a/contrib/pg_trgm/pg_trgm.control
+++ b/contrib/pg_trgm/pg_trgm.control
@@ -1,6 +1,6 @@
# pg_trgm extension
comment = 'text similarity measurement and index searching based on trigrams'
-default_version = '1.6'
+default_version = '1.7'
module_pathname = '$libdir/pg_trgm'
relocatable = true
trusted = true
diff --git a/contrib/postgres_fdw/Makefile b/contrib/postgres_fdw/Makefile
index c1b0cad453..3564015ee8 100644
--- a/contrib/postgres_fdw/Makefile
+++ b/contrib/postgres_fdw/Makefile
@@ -14,7 +14,8 @@ PG_CPPFLAGS = -I$(libpq_srcdir)
SHLIB_LINK_INTERNAL = $(libpq)
EXTENSION = postgres_fdw
-DATA = postgres_fdw--1.0.sql postgres_fdw--1.0--1.1.sql
+DATA = postgres_fdw--1.0.sql postgres_fdw--1.0--1.1.sql \
+ postgres_fdw--1.1--1.2.sql
REGRESS = postgres_fdw
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 48c7417e6e..5d30345fcf 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -464,7 +464,7 @@ _PG_init(void)
NULL,
&pgfdw_application_name,
NULL,
- PGC_USERSET,
+ PGC_SUSET,
0,
NULL,
NULL,
diff --git a/contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql b/contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql
new file mode 100644
index 0000000000..b174e04283
--- /dev/null
+++ b/contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql
@@ -0,0 +1,6 @@
+/* contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION postgres_fdw UPDATE TO '1.2'" to load this file. \quit
+
+GRANT SET VALUE ON "postgres_fdw.application_name" TO public;
diff --git a/contrib/postgres_fdw/postgres_fdw.control b/contrib/postgres_fdw/postgres_fdw.control
index d489382064..a4b800be4f 100644
--- a/contrib/postgres_fdw/postgres_fdw.control
+++ b/contrib/postgres_fdw/postgres_fdw.control
@@ -1,5 +1,5 @@
# postgres_fdw extension
comment = 'foreign-data wrapper for remote PostgreSQL servers'
-default_version = '1.1'
+default_version = '1.2'
module_pathname = '$libdir/postgres_fdw'
relocatable = true
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 19a3ffb7ff..f7ffd3d486 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -449,7 +449,7 @@ _PG_init(void)
NULL,
&sepgsql_debug_audit,
false,
- PGC_USERSET,
+ PGC_SUSET,
GUC_NOT_IN_SAMPLE,
NULL,
NULL,
diff --git a/contrib/sepgsql/sepgsql.sql.in b/contrib/sepgsql/sepgsql.sql.in
index 917d12dbbe..4dce769306 100644
--- a/contrib/sepgsql/sepgsql.sql.in
+++ b/contrib/sepgsql/sepgsql.sql.in
@@ -34,4 +34,5 @@ CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_setcon(text) RETURNS bool AS 'MODU
CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_mcstrans_in(text) RETURNS text AS 'MODULE_PATHNAME', 'sepgsql_mcstrans_in' LANGUAGE C STRICT;
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;
+GRANT SET VALUE ON "sepgsql.debug_audit" TO public;
SELECT sepgsql_restorecon(NULL);
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 03e2537b07..3f7e55c3b9 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -105,6 +105,11 @@
<entry>collations (locale information)</entry>
</row>
+ <row>
+ <entry><link linkend="catalog-pg-setting-acl"><structname>pg_setting_acl</structname></link></entry>
+ <entry>configuration parameters which have privileges granted to roles</entry>
+ </row>
+
<row>
<entry><link linkend="catalog-pg-constraint"><structname>pg_constraint</structname></link></entry>
<entry>check constraints, unique constraints, primary key constraints, foreign key constraints</entry>
@@ -2423,6 +2428,64 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</para>
</sect1>
+ <sect1 id="catalog-pg-setting-acl">
+ <title><structname>pg_setting_acl</structname></title>
+
+ <indexterm zone="catalog-pg-setting-acl">
+ <primary>pg_setting_acl</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_setting_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_setting_acl</structname>
+ is shared across all databases of a cluster: there is only one
+ copy of <structname>pg_setting_acl</structname> per cluster, not
+ one per database.
+ </para>
+
+ <table>
+ <title><structname>pg_setting_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>setting</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>
@@ -12494,11 +12557,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 settings 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 VALUE</literal> privilege granted
+ on the setting 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 64d9030652..3b48e8006e 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -1672,7 +1672,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 VALUE</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.
@@ -1940,6 +1941,28 @@ REVOKE ALL ON accounts FROM PUBLIC;
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>SET VALUE</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>SET</command> command to
+ change <literal>superuser</literal> run-time configuration parameters.
+ (Any user can set <literal>user</literal> run-time configuration
+ parameters.)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ALTER SYSTEM</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>ALTER SYSTEM
+ SET</command> command to change server configuration parameters.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
The privileges required by other commands are listed on the
@@ -2078,6 +2101,16 @@ REVOKE ALL ON accounts FROM PUBLIC;
<literal>TYPE</literal>
</entry>
</row>
+ <row>
+ <entry><literal>SET VALUE</literal></entry>
+ <entry><literal>s</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
+ <row>
+ <entry><literal>ALTER SYSTEM</literal></entry>
+ <entry><literal>A</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -2184,6 +2217,12 @@ REVOKE ALL ON accounts FROM PUBLIC;
<entry><literal>U</literal></entry>
<entry><literal>\dT+</literal></entry>
</row>
+ <row>
+ <entry><literal>Configuration parameter</literal></entry>
+ <entry><literal>sA</literal></entry>
+ <entry>none</entry>
+ <entry><literal></literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 0a725a6711..ec846cd4e3 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -22627,8 +22627,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');
@@ -22893,6 +22892,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_setting_privilege</primary>
+ </indexterm>
+ <function>has_setting_privilege</function> (
+ <optional> <parameter>user</parameter> <type>name</type> or <type>oid</type>, </optional>
+ <parameter>setting</parameter> <type>text</type> or <type>oid</type>,
+ <parameter>privilege</parameter> <type>text</type> )
+ <returnvalue>boolean</returnvalue>
+ </para>
+ <para>
+ Does user have privilege for setting?
+ Allowable privilege types are <literal>SET VALUE</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 a897712de2..21ec320a8d 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 VALUE | ALTER SYSTEM }
+ ON <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 VALUE</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/set.sgml b/doc/src/sgml/ref/set.sgml
index 339ee9eec9..a08057d1d1 100644
--- a/doc/src/sgml/ref/set.sgml
+++ b/doc/src/sgml/ref/set.sgml
@@ -34,8 +34,9 @@ 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.)
+ (But some require either superuser privileges or granted <literal>SET
+ VALUE</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>
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 4e6efda97f..45e5330c12 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -28,6 +28,7 @@ OBJS = \
pg_cast.o \
pg_class.o \
pg_collation.o \
+ pg_setting_acl.o \
pg_constraint.o \
pg_conversion.o \
pg_db_role_setting.o \
@@ -54,7 +55,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_setting_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 \
@@ -126,6 +127,7 @@ install-data: bki-stamp installdirs
$(INSTALL_DATA) $(srcdir)/system_functions.sql '$(DESTDIR)$(datadir)/system_functions.sql'
$(INSTALL_DATA) $(srcdir)/system_views.sql '$(DESTDIR)$(datadir)/system_views.sql'
$(INSTALL_DATA) $(srcdir)/information_schema.sql '$(DESTDIR)$(datadir)/information_schema.sql'
+ $(INSTALL_DATA) $(srcdir)/setting_privileges.sql '$(DESTDIR)$(datadir)/setting_privileges.sql'
$(INSTALL_DATA) $(srcdir)/sql_features.txt '$(DESTDIR)$(datadir)/sql_features.txt'
installdirs:
@@ -133,7 +135,7 @@ installdirs:
.PHONY: uninstall-data
uninstall-data:
- rm -f $(addprefix '$(DESTDIR)$(datadir)'/, postgres.bki system_constraints.sql system_functions.sql system_views.sql information_schema.sql sql_features.txt)
+ rm -f $(addprefix '$(DESTDIR)$(datadir)'/, postgres.bki system_constraints.sql system_functions.sql system_views.sql information_schema.sql setting_privileges.sql sql_features.txt)
# postgres.bki, system_constraints.sql, and the generated headers are
# in the distribution tarball, so they are not cleaned here.
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ce0a4ff14e..02c15853e5 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_setting_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_Setting(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_SETTING:
+ whole_mask = ACL_ALL_RIGHTS_SETTING;
+ 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_SETTING:
+ all_privileges = ACL_ALL_RIGHTS_SETTING;
+ errormsg = gettext_noop("invalid privilege type %s for setting");
+ 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_SETTING:
+ ExecGrant_Setting(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -759,6 +771,38 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_SETTING:
+ foreach(cell, objnames)
+ {
+ char *setting = strVal(lfirst(cell));
+ Oid settingid = get_setting_oid(setting, true);
+
+ if (!OidIsValid(settingid))
+ {
+ /*
+ * Lookup the existing entry, or if necessary, add a new
+ * entry for this parameter. Entries only exist for
+ * parameters which currently have, or previously have had,
+ * privileges assigned.
+ *
+ * It is tempting to sanity-check the given configuration
+ * parameter name against known guc names in the guc
+ * tables, but for handling upgrades we need to accept
+ * setting names that do not yet exist.
+ */
+ settingid = SettingAclCreate(setting, 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, settingid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -1494,6 +1538,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case ForeignDataWrapperRelationId:
istmt.objtype = OBJECT_FDW;
break;
+ case SettingAclRelationId:
+ istmt.objtype = OBJECT_SETTING;
+ break;
default:
elog(ERROR, "unexpected object class %u", classid);
break;
@@ -3225,6 +3272,128 @@ ExecGrant_Type(InternalGrant *istmt)
table_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Setting(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_SETTING;
+
+ relation = table_open(SettingAclRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid settingId = lfirst_oid(cell);
+ Form_pg_setting_acl pg_setting_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_setting_acl];
+ bool nulls[Natts_pg_setting_acl];
+ bool replaces[Natts_pg_setting_acl];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(settingId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for setting %u", settingId);
+
+ pg_setting_acl_tuple = (Form_pg_setting_acl) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ aclDatum = SysCacheGetAttr(SETTINGNAME, tuple, Anum_pg_setting_acl_setacl,
+ &isNull);
+ if (isNull)
+ {
+ old_acl = acldefault(OBJECT_SETTING, InvalidOid);
+ /* 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, GetUserId(),
+ &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,
+ settingId, grantorId, OBJECT_SETTING,
+ text_to_cstring(&pg_setting_acl_tuple->setting),
+ 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_setting_acl_setacl - 1] = true;
+ values[Anum_pg_setting_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(settingId, SettingAclRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(SettingAclRelationId,
+ pg_setting_acl_tuple->oid, 0,
+ InvalidOid,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ table_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3255,6 +3424,10 @@ string_to_privilege(const char *privname)
return ACL_CREATE_TEMP;
if (strcmp(privname, "connect") == 0)
return ACL_CONNECT;
+ if (strcmp(privname, "set value") == 0)
+ return ACL_SET_VALUE;
+ 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 +3465,10 @@ privilege_to_string(AclMode privilege)
return "TEMP";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET_VALUE:
+ return "SET VALUE";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3328,6 +3505,13 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_COLUMN:
msg = gettext_noop("permission denied for column %s");
break;
+ case OBJECT_SETTING:
+ /*
+ * 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 +3748,7 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_AMPROC:
case OBJECT_ATTRIBUTE:
case OBJECT_CAST:
+ case OBJECT_SETTING:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
case OBJECT_DOMCONSTRAINT:
@@ -4000,6 +4185,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_setting_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 setting's ACL from pg_setting_acl
+ */
+ tuple = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(config_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("setting with OID %u does not exist",
+ config_oid)));
+
+ aclDatum = SysCacheGetAttr(SETTINGOID, tuple, Anum_pg_setting_acl_setacl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_SETTING, 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 +4951,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_setting_acl_aclcheck(Oid config_oid, Oid roleid, AclMode mode)
+{
+ if (pg_setting_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 aa7d4d5456..a069c2fb39 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_setting_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 == SettingAclRelationId ||
relationId == DatabaseRelationId ||
relationId == SharedDescriptionRelationId ||
relationId == SharedDependRelationId ||
@@ -260,6 +262,8 @@ IsSharedRelation(Oid relationId)
relationId == AuthIdOidIndexId ||
relationId == AuthMemRoleMemIndexId ||
relationId == AuthMemMemRoleIndexId ||
+ relationId == SettingAclSettingIndexId ||
+ relationId == SettingAclOidIndexId ||
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 == PgSettingAclToastTable ||
+ relationId == PgSettingAclToastIndex ||
relationId == PgDatabaseToastTable ||
relationId == PgDatabaseToastIndex ||
relationId == PgDbRoleSettingToastTable ||
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index fe9c714257..57174b7c19 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_setting_acl.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
@@ -150,6 +151,7 @@ static const Oid object_classes[] = {
TypeRelationId, /* OCLASS_TYPE */
CastRelationId, /* OCLASS_CAST */
CollationRelationId, /* OCLASS_COLLATION */
+ SettingAclRelationId, /* OCLASS_SETTING */
ConstraintRelationId, /* OCLASS_CONSTRAINT */
ConversionRelationId, /* OCLASS_CONVERSION */
AttrDefaultRelationId, /* OCLASS_DEFAULT */
@@ -1504,6 +1506,7 @@ doDeletion(const ObjectAddress *object, int flags)
/*
* These global object types are not supported here.
*/
+ case OCLASS_SETTING:
case OCLASS_ROLE:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
@@ -2778,6 +2781,9 @@ getObjectClass(const ObjectAddress *object)
case CollationRelationId:
return OCLASS_COLLATION;
+ case SettingAclRelationId:
+ return OCLASS_SETTING;
+
case ConstraintRelationId:
return OCLASS_CONSTRAINT;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 2bae3fbb17..682ae5e390 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_setting_acl.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
@@ -2309,6 +2310,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_COLUMN:
case OBJECT_ATTRIBUTE:
case OBJECT_COLLATION:
+ case OBJECT_SETTING:
case OBJECT_CONVERSION:
case OBJECT_STATISTIC_EXT:
case OBJECT_TSPARSER:
@@ -3510,6 +3512,22 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
break;
}
+ case OCLASS_SETTING:
+ {
+ char *setting;
+
+ setting = get_setting_name(object->objectId);
+ if (!setting)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for setting %u",
+ object->objectId);
+ break;
+ }
+ appendStringInfo(&buffer, _("setting %s"), setting);
+ break;
+ }
+
case OCLASS_SCHEMA:
{
char *nspname;
@@ -4473,6 +4491,10 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "collation");
break;
+ case OCLASS_SETTING:
+ appendStringInfoString(&buffer, "setting");
+ break;
+
case OCLASS_CONSTRAINT:
getConstraintTypeDescription(&buffer, object->objectId,
missing_ok);
@@ -4977,6 +4999,30 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_SETTING:
+ {
+ HeapTuple configTup;
+ Form_pg_setting_acl configForm;
+ char *namestr;
+
+ configTup = SearchSysCache1(SETTINGOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(configTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for setting %u",
+ object->objectId);
+ break;
+ }
+ configForm = (Form_pg_setting_acl) GETSTRUCT(configTup);
+ namestr = text_to_cstring(&configForm->setting);
+ appendStringInfoString(&buffer, namestr);
+ if (objname)
+ *objname = list_make1(namestr);
+ ReleaseSysCache(configTup);
+ break;
+ }
+
case OCLASS_CONVERSION:
{
HeapTuple conTup;
diff --git a/src/backend/catalog/pg_setting_acl.c b/src/backend/catalog/pg_setting_acl.c
new file mode 100644
index 0000000000..3c6594da03
--- /dev/null
+++ b/src/backend/catalog/pg_setting_acl.c
@@ -0,0 +1,122 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_setting_acl.c
+ * routines to support manipulation of the pg_setting_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_setting_acl.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/sysattr.h"
+#include "access/table.h"
+#include "access/tableam.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_setting_acl.h"
+#include "mb/pg_wchar.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/guc.h"
+#include "utils/pg_locale.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * SettingAclCreate
+ *
+ * Add a new tuple to pg_setting_acl.
+ *
+ * setting: the setting name to create.
+ * if_not_exists: if true, don't fail on duplicate name, but rather return
+ * the existing entry's Oid.
+ */
+Oid
+SettingAclCreate(const char *setting, bool if_not_exists)
+{
+ Relation rel;
+ TupleDesc tupDesc;
+ HeapTuple tuple;
+ Datum values[Natts_pg_setting_acl];
+ bool nulls[Natts_pg_setting_acl];
+ Oid settingId;
+ const char *canonical;
+
+ /*
+ * Check whether the setting (by the given name or alias)
+ * already exists.
+ */
+ settingId = get_setting_oid(setting, true);
+ if (OidIsValid(settingId))
+ {
+ if (if_not_exists)
+ return settingId;
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("setting \"%s\" already exists",
+ setting)));
+ }
+
+ /*
+ * We must not require the setting to be in the list of existent GUCs,
+ * as we may be called at different points during upgrades or the
+ * installation or removal of extensions. Instead, perform a basic sanity
+ * check of the setting, 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_setting_acl entries.
+ */
+ if (!valid_variable_name(setting, NULL))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid setting name \"%s\"",
+ setting)));
+ canonical = GetConfigOptionCanonicalName(setting);
+ if (!canonical)
+ canonical = setting;
+
+ /*
+ * 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(SettingAclRelationId, RowExclusiveLock);
+ tupDesc = RelationGetDescr(rel);
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ values[Anum_pg_setting_acl_setting - 1] =
+ DirectFunctionCall1(textin, CStringGetDatum(canonical));
+ settingId = GetNewOidWithIndex(rel,
+ SettingAclOidIndexId,
+ Anum_pg_setting_acl_oid);
+ values[Anum_pg_setting_acl_oid - 1] = ObjectIdGetDatum(settingId);
+ nulls[Anum_pg_setting_acl_setacl - 1] = true;
+ tuple = heap_form_tuple(tupDesc, values, nulls);
+ CatalogTupleInsert(rel, tuple);
+
+ /* Post creation hook for new setting */
+ InvokeObjectPostCreateHook(SettingAclRelationId, settingId, 0);
+
+ /*
+ * Close pg_setting_acl, but keep lock till commit.
+ */
+ heap_freetuple(tuple);
+ table_close(rel, NoLock);
+
+ return settingId;
+}
diff --git a/src/backend/catalog/setting_privileges.sql b/src/backend/catalog/setting_privileges.sql
new file mode 100644
index 0000000000..b788ebee7f
--- /dev/null
+++ b/src/backend/catalog/setting_privileges.sql
@@ -0,0 +1,62 @@
+/*
+ * PostgreSQL User SET variables
+ *
+ * Copyright (c) 2021, PostgreSQL Global Development Group
+ *
+ * src/backend/catalog/setting_privileges.sql
+ *
+ * Note: this file is read in single-user -j mode, which means that the
+ * command terminator is semicolon-newline-newline; whenever the backend
+ * sees that, it stops and executes what it's got. If you write a lot of
+ * statements without empty lines between, they'll all get quoted to you
+ * in any error message about one of them, so don't do that. Also, you
+ * cannot write a semicolon immediately followed by an empty line in a
+ * string literal (including a function body!) or a multiline comment.
+ */
+
+GRANT SET VALUE ON
+ enable_seqscan, enable_indexscan, enable_indexonlyscan, enable_bitmapscan,
+ enable_tidscan, enable_sort, enable_incremental_sort, enable_hashagg,
+ enable_material, enable_memoize, enable_nestloop, enable_mergejoin,
+ enable_hashjoin, enable_gathermerge, enable_partitionwise_join,
+ enable_partitionwise_aggregate, enable_parallel_append,
+ enable_parallel_hash, enable_partition_pruning, enable_async_append, geqo,
+ exit_on_error, debug_print_parse, debug_print_rewritten, debug_print_plan,
+ debug_pretty_print, trace_notify, transform_null_equals,
+ default_transaction_read_only, transaction_read_only,
+ default_transaction_deferrable, transaction_deferrable, row_security,
+ check_function_bodies, array_nulls, default_with_oids, trace_sort,
+ trace_syncscan, optimize_bounded_sort, escape_string_warning,
+ standard_conforming_strings, synchronize_seqscans, quote_all_identifiers,
+ parallel_leader_participation, jit, jit_expressions, jit_tuple_deforming,
+ default_statistics_target, from_collapse_limit, join_collapse_limit,
+ geqo_threshold, geqo_effort, geqo_pool_size, geqo_generations,
+ temp_buffers, work_mem, maintenance_work_mem, logical_decoding_work_mem,
+ vacuum_cost_page_hit, vacuum_cost_page_miss, vacuum_cost_page_dirty,
+ vacuum_cost_limit, statement_timeout, lock_timeout,
+ idle_in_transaction_session_timeout, idle_session_timeout,
+ vacuum_freeze_min_age, vacuum_freeze_table_age,
+ vacuum_multixact_freeze_min_age, vacuum_multixact_freeze_table_age,
+ vacuum_failsafe_age, vacuum_multixact_failsafe_age, wal_skip_threshold,
+ wal_sender_timeout, commit_siblings, extra_float_digits,
+ log_parameter_max_length_on_error, effective_io_concurrency,
+ maintenance_io_concurrency, backend_flush_after,
+ max_parallel_maintenance_workers, max_parallel_workers_per_gather,
+ max_parallel_workers, tcp_keepalives_idle, tcp_keepalives_interval,
+ ssl_renegotiation_limit, tcp_keepalives_count, gin_fuzzy_search_limit,
+ effective_cache_size, min_parallel_table_scan_size,
+ min_parallel_index_scan_size, gin_pending_list_limit, tcp_user_timeout,
+ client_connection_check_interval, seq_page_cost, random_page_cost,
+ cpu_tuple_cost, cpu_index_tuple_cost, cpu_operator_cost,
+ parallel_tuple_cost, parallel_setup_cost, jit_above_cost,
+ jit_optimize_above_cost, jit_inline_above_cost, cursor_tuple_fraction,
+ geqo_selection_bias, geqo_seed, hash_mem_multiplier, seed,
+ vacuum_cost_delay, DateStyle, default_table_access_method,
+ default_tablespace, temp_tablespaces, lc_monetary, lc_numeric, lc_time,
+ local_preload_libraries, search_path, role, TimeZone,
+ timezone_abbreviations, default_text_search_config, application_name,
+ backslash_quote, bytea_output, client_min_messages, constraint_exclusion,
+ default_toast_compression, default_transaction_isolation,
+ transaction_isolation, IntervalStyle, synchronous_commit, xmlbinary,
+ xmloption, force_parallel_mode, password_encryption, plan_cache_mode
+ TO public;
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 61b515cdb8..bd772dc9c5 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -593,6 +593,19 @@ CREATE RULE pg_settings_n AS
GRANT SELECT, UPDATE ON pg_settings TO PUBLIC;
+CREATE VIEW pg_setting_privileges AS
+ SELECT grantor.rolname AS grantor,
+ grantee.rolname AS grantee,
+ set_acl.setting AS setting,
+ acl.privilege_type AS privilege_type,
+ acl.is_grantable
+ FROM pg_catalog.pg_setting_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_setting_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 40044070cf..7da2e62683 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -639,6 +639,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
break;
case OCLASS_CAST:
+ case OCLASS_SETTING:
case OCLASS_CONSTRAINT:
case OCLASS_DEFAULT:
case OCLASS_LANGUAGE:
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 4e545adf95..45bc1c1948 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -276,6 +276,13 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
name = NameListToString(castNode(List, object));
}
break;
+ case OBJECT_SETTING:
+ if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
+ {
+ msg = gettext_noop("setting \"%s\" does not exist, skipping");
+ name = NameListToString(castNode(List, object));
+ }
+ break;
case OBJECT_CONVERSION:
if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
{
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index df264329d8..d6df5b3ad9 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_SETTING:
case OBJECT_DATABASE:
case OBJECT_TABLESPACE:
case OBJECT_ROLE:
@@ -1012,6 +1013,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
{
switch (objclass)
{
+ case OCLASS_SETTING:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
@@ -2078,6 +2080,8 @@ stringify_grant_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMN";
+ case OBJECT_SETTING:
+ return "SETTING";
case OBJECT_TABLE:
return "TABLE";
case OBJECT_SEQUENCE:
@@ -2161,6 +2165,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMNS";
+ case OBJECT_SETTING:
+ return "SETTINGS";
case OBJECT_TABLE:
return "TABLES";
case OBJECT_SEQUENCE:
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 53c18628a7..8f6f9bd7a0 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_SETTING:
case OBJECT_CONVERSION:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 47b29001d5..5672e9d1e4 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -12359,6 +12359,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_TYPE:
case OCLASS_CAST:
case OCLASS_COLLATION:
+ case OCLASS_SETTING:
case OCLASS_CONVERSION:
case OCLASS_LANGUAGE:
case OCLASS_LARGEOBJECT:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 3d4dd43e47..132b6907c5 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -363,8 +363,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 setting_name
+%type <list> OptSchemaEltList setting_target
%type <chr> am_type
@@ -402,8 +402,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> iso_level opt_encoding
%type <rolespec> grantee
%type <list> grantee_list
-%type <accesspriv> privilege
-%type <list> privileges privilege_list
+%type <accesspriv> privilege setting_priv
+%type <list> privileges privilege_list setting_priv_list
%type <privtarget> privilege_target
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
@@ -715,7 +715,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
@@ -6972,6 +6972,20 @@ GrantStmt: GRANT privileges ON privilege_target TO grantee_list
n->grantor = $8;
$$ = (Node*)n;
}
+ | GRANT setting_priv_list ON setting_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_SETTING;
+ n->objects = $4;
+ n->grantees = $6;
+ n->grant_option = $7;
+ n->grantor = $8;
+ $$ = (Node*)n;
+ }
;
RevokeStmt:
@@ -7005,6 +7019,36 @@ RevokeStmt:
n->behavior = $11;
$$ = (Node *)n;
}
+ | REVOKE setting_priv_list ON setting_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_SETTING;
+ n->objects = $4;
+ n->grantees = $6;
+ n->grantor = $7;
+ n->behavior = $8;
+ $$ = (Node *)n;
+ }
+ | REVOKE GRANT OPTION FOR setting_priv_list ON setting_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_SETTING;
+ n->objects = $7;
+ n->grantees = $9;
+ n->grantor = $10;
+ n->behavior = $11;
+ $$ = (Node *)n;
+ }
;
@@ -7073,6 +7117,49 @@ privilege: SELECT opt_column_list
}
;
+setting_priv_list: setting_priv { $$ = list_make1($1); }
+ | setting_priv_list ',' setting_priv { $$ = lappend($1, $3); }
+ ;
+
+setting_priv:
+ ALTER SYSTEM_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("alter system");
+ n->cols = NIL;
+ $$ = n;
+ }
+ | SET VALUE_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("set value");
+ n->cols = NIL;
+ $$ = n;
+ }
+ ;
+
+
+setting_target:
+ setting_name
+ {
+ $$ = list_make1(makeString($1));
+ }
+ | setting_target ',' setting_name
+ {
+ $$ = lappend($1, makeString($3));
+ }
+ ;
+
+setting_name:
+ ColId
+ {
+ $$ = $1;
+ }
+ | setting_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.
@@ -15818,6 +15905,7 @@ unreserved_keyword:
| OWNED
| OWNER
| PARALLEL
+ | PARAMETER
| PARSER
| PARTIAL
| PARTITION
@@ -16396,6 +16484,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 67f8b29434..f0dd3e52ed 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_setting_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,8 @@ 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 Oid convert_setting_name(text *setting);
+static AclMode convert_setting_priv_string(text *priv_setting_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 +310,12 @@ aclparse(const char *s, AclItem *aip)
case ACL_CONNECT_CHR:
read = ACL_CONNECT;
break;
+ case ACL_SET_VALUE_CHR:
+ read = ACL_SET_VALUE;
+ break;
+ case ACL_ALTER_SYSTEM_CHR:
+ read = ACL_ALTER_SYSTEM;
+ break;
case 'R': /* ignore old RULE privileges */
read = 0;
break;
@@ -794,6 +804,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_SETTING:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_NO_RIGHTS;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -1602,6 +1616,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 VALUE") == 0)
+ return ACL_SET_VALUE;
+ 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 +1716,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET_VALUE:
+ return "SET VALUE";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
@@ -4429,6 +4451,205 @@ convert_type_priv_string(text *priv_type_text)
return convert_any_priv_string(priv_type_text, type_priv_map);
}
+/*
+ * has_setting_privilege variants
+ * These are all named "has_setting_privilege" at the SQL level.
+ * They take various combinations of setting name, setting 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_setting_privilege_name_name
+ * Check user privileges on a setting given
+ * name username, text setting, and text priv name.
+ */
+Datum
+has_setting_privilege_name_name(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ text *setting = PG_GETARG_TEXT_PP(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid roleid;
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_role_oid_or_public(NameStr(*username));
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_name
+ * Check user privileges on a setting given
+ * text setting and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_setting_privilege_name(PG_FUNCTION_ARGS)
+{
+ text *setting = PG_GETARG_TEXT_PP(0);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(1);
+ Oid roleid;
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_name_id
+ * Check user privileges on a setting given
+ * name usename, setting oid, and text priv name.
+ */
+Datum
+has_setting_privilege_name_id(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ Oid settingoid = PG_GETARG_OID(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_role_oid_or_public(NameStr(*username));
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id
+ * Check user privileges on a setting given
+ * setting oid, and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_setting_privilege_id(PG_FUNCTION_ARGS)
+{
+ Oid settingoid = PG_GETARG_OID(0);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(1);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id_name
+ * Check user privileges on a setting given
+ * roleid, text setting, and text priv name.
+ */
+Datum
+has_setting_privilege_id_name(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ text *setting = PG_GETARG_TEXT_PP(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id_id
+ * Check user privileges on a setting given
+ * roleid, setting oid, and text priv name.
+ */
+Datum
+has_setting_privilege_id_id(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ Oid settingoid = PG_GETARG_OID(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ AclMode mode;
+ AclResult aclresult;
+
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * Support routines for has_setting_privilege family.
+ */
+
+/*
+ * Given a setting name expressed as a string, look it up and return Oid
+ */
+static Oid
+convert_setting_name(text *settingname)
+{
+ Oid oid;
+ char *setting = text_to_cstring(settingname);
+
+ oid = get_setting_oid(setting, true);
+
+ if (!OidIsValid(oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("setting \"%s\" does not exist", setting)));
+
+ return oid;
+}
+
+/*
+ * convert_setting_priv_string
+ * Convert text string to AclMode value.
+ */
+static AclMode
+convert_setting_priv_string(text *priv_setting_text)
+{
+ static const priv_map setting_priv_map[] = {
+ {"SET VALUE", ACL_SET_VALUE},
+ {"SET VALUE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SET_VALUE)},
+ {"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_setting_text, setting_priv_map);
+}
/*
* pg_has_role variants
@@ -4670,6 +4891,43 @@ initialize_acl(void)
}
}
+/*
+ * get_setting_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_setting_oid(const char *setting, bool missing_ok)
+{
+ Oid oid;
+
+ /* Check for the variable by the name we were given */
+ oid = GetSysCacheOid1(SETTINGNAME, Anum_pg_setting_acl_oid,
+ PointerGetDatum(cstring_to_text(setting)));
+ if (!OidIsValid(oid))
+ {
+ const char *canonical;
+
+ /* Check if the variable has a different canonical spelling */
+ canonical = GetConfigOptionCanonicalName(setting);
+ if (canonical != NULL)
+ oid = GetSysCacheOid1(SETTINGNAME, Anum_pg_setting_acl_oid,
+ PointerGetDatum(cstring_to_text(canonical)));
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("setting \"%s\" does not exist", setting)));
+ }
+
+ return oid;
+}
+
/*
* RoleMembershipCacheCallback
* Syscache inval callback function
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 9176514a96..0a0cbfd9f0 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_setting_acl.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_transform.h"
#include "catalog/pg_type.h"
@@ -3304,6 +3305,25 @@ free_attstatsslot(AttStatsSlot *sslot)
pfree(sslot->numbers_arr);
}
+char *
+get_setting_name(Oid configid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(configid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_setting_acl configtup = (Form_pg_setting_acl) GETSTRUCT(tp);
+ char *result;
+
+ result = pstrdup(text_to_cstring(&configtup->setting));
+ 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 56870b46e4..00fbe5218d 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_setting_acl.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
#include "catalog/pg_shseclabel.h"
@@ -762,6 +763,28 @@ static const struct cachedesc cacheinfo[] = {
},
32
},
+ {SettingAclRelationId, /* SETTINGNAME */
+ SettingAclSettingIndexId,
+ 1,
+ {
+ Anum_pg_setting_acl_setting,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+ {SettingAclRelationId, /* SETTINGOID */
+ SettingAclOidIndexId,
+ 1,
+ {
+ Anum_pg_setting_acl_oid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{StatisticExtDataRelationId, /* STATEXTDATASTXOID */
StatisticExtDataStxoidIndexId,
1,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index f736e8d872..e2615138b6 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -974,7 +974,7 @@ static const unit_conversion time_unit_conversion_table[] =
static struct config_bool ConfigureNamesBool[] =
{
{
- {"enable_seqscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_seqscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of sequential-scan plans."),
NULL,
GUC_EXPLAIN
@@ -984,7 +984,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_indexscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_indexscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of index-scan plans."),
NULL,
GUC_EXPLAIN
@@ -994,7 +994,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_indexonlyscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_indexonlyscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of index-only-scan plans."),
NULL,
GUC_EXPLAIN
@@ -1004,7 +1004,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_bitmapscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_bitmapscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of bitmap-scan plans."),
NULL,
GUC_EXPLAIN
@@ -1014,7 +1014,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_tidscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_tidscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of TID scan plans."),
NULL,
GUC_EXPLAIN
@@ -1024,7 +1024,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_sort", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_sort", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of explicit sort steps."),
NULL,
GUC_EXPLAIN
@@ -1034,7 +1034,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_incremental_sort", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_incremental_sort", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of incremental sort steps."),
NULL,
GUC_EXPLAIN
@@ -1044,7 +1044,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_hashagg", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_hashagg", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of hashed aggregation plans."),
NULL,
GUC_EXPLAIN
@@ -1054,7 +1054,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_material", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_material", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of materialization."),
NULL,
GUC_EXPLAIN
@@ -1064,7 +1064,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_memoize", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_memoize", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of memoization."),
NULL,
GUC_EXPLAIN
@@ -1074,7 +1074,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_nestloop", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_nestloop", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of nested-loop join plans."),
NULL,
GUC_EXPLAIN
@@ -1084,7 +1084,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_mergejoin", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_mergejoin", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of merge join plans."),
NULL,
GUC_EXPLAIN
@@ -1094,7 +1094,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_hashjoin", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_hashjoin", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of hash join plans."),
NULL,
GUC_EXPLAIN
@@ -1104,7 +1104,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_gathermerge", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_gathermerge", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of gather merge plans."),
NULL,
GUC_EXPLAIN
@@ -1114,7 +1114,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_partitionwise_join", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_partitionwise_join", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables partitionwise join."),
NULL,
GUC_EXPLAIN
@@ -1124,7 +1124,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_partitionwise_aggregate", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_partitionwise_aggregate", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables partitionwise aggregation and grouping."),
NULL,
GUC_EXPLAIN
@@ -1134,7 +1134,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_parallel_append", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_parallel_append", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of parallel append plans."),
NULL,
GUC_EXPLAIN
@@ -1144,7 +1144,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_parallel_hash", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_parallel_hash", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of parallel hash plans."),
NULL,
GUC_EXPLAIN
@@ -1154,7 +1154,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_partition_pruning", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_partition_pruning", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables plan-time and execution-time partition pruning."),
gettext_noop("Allows the query planner and executor to compare partition "
"bounds to conditions in the query to determine which "
@@ -1166,7 +1166,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_async_append", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_async_append", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of async append plans."),
NULL,
GUC_EXPLAIN
@@ -1176,7 +1176,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("Enables genetic query optimization."),
gettext_noop("This algorithm attempts to do planning without "
"exhaustive searching."),
@@ -1398,7 +1398,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"exit_on_error", PGC_USERSET, ERROR_HANDLING_OPTIONS,
+ {"exit_on_error", PGC_SUSET, ERROR_HANDLING_OPTIONS,
gettext_noop("Terminate session on any error."),
NULL
},
@@ -1436,7 +1436,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_print_parse", PGC_USERSET, LOGGING_WHAT,
+ {"debug_print_parse", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs each query's parse tree."),
NULL
},
@@ -1445,7 +1445,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_print_rewritten", PGC_USERSET, LOGGING_WHAT,
+ {"debug_print_rewritten", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs each query's rewritten parse tree."),
NULL
},
@@ -1454,7 +1454,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_print_plan", PGC_USERSET, LOGGING_WHAT,
+ {"debug_print_plan", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs each query's execution plan."),
NULL
},
@@ -1463,7 +1463,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_pretty_print", PGC_USERSET, LOGGING_WHAT,
+ {"debug_pretty_print", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Indents parse and plan tree displays."),
NULL
},
@@ -1584,7 +1584,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"trace_notify", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"trace_notify", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Generates debugging output for LISTEN and NOTIFY."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -1668,7 +1668,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"transform_null_equals", PGC_USERSET, COMPAT_OPTIONS_CLIENT,
+ {"transform_null_equals", PGC_SUSET, COMPAT_OPTIONS_CLIENT,
gettext_noop("Treats \"expr=NULL\" as \"expr IS NULL\"."),
gettext_noop("When turned on, expressions of the form expr = NULL "
"(or NULL = expr) are treated as expr IS NULL, that is, they "
@@ -1690,7 +1690,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"default_transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_transaction_read_only", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default read-only status of new transactions."),
NULL,
GUC_REPORT
@@ -1700,7 +1700,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"transaction_read_only", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the current transaction's read-only status."),
NULL,
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -1710,7 +1710,7 @@ static struct config_bool ConfigureNamesBool[] =
check_transaction_read_only, NULL, NULL
},
{
- {"default_transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_transaction_deferrable", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default deferrable status of new transactions."),
NULL
},
@@ -1719,7 +1719,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"transaction_deferrable", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Whether to defer a read-only serializable transaction until it can be executed with no possible serialization failures."),
NULL,
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -1729,7 +1729,7 @@ static struct config_bool ConfigureNamesBool[] =
check_transaction_deferrable, NULL, NULL
},
{
- {"row_security", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"row_security", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Enable row security."),
gettext_noop("When enabled, row security will be applied to all users.")
},
@@ -1738,7 +1738,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"check_function_bodies", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"check_function_bodies", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Check routine bodies during CREATE FUNCTION and CREATE PROCEDURE."),
NULL
},
@@ -1747,7 +1747,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"array_nulls", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"array_nulls", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Enable input of NULL elements in arrays."),
gettext_noop("When turned on, unquoted NULL in an array input "
"value means a null value; "
@@ -1764,7 +1764,7 @@ static struct config_bool ConfigureNamesBool[] =
* avoid unnecessarily breaking older dump files.
*/
{
- {"default_with_oids", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"default_with_oids", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("WITH OIDS is no longer supported; this can only be false."),
NULL,
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE
@@ -1794,7 +1794,7 @@ static struct config_bool ConfigureNamesBool[] =
#ifdef TRACE_SORT
{
- {"trace_sort", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"trace_sort", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Emit information about resource usage in sorting."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -1808,7 +1808,7 @@ static struct config_bool ConfigureNamesBool[] =
#ifdef TRACE_SYNCSCAN
/* this is undocumented because not exposed in a standard build */
{
- {"trace_syncscan", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"trace_syncscan", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Generate debugging output for synchronized scanning."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -1823,7 +1823,7 @@ static struct config_bool ConfigureNamesBool[] =
/* this is undocumented because not exposed in a standard build */
{
{
- "optimize_bounded_sort", PGC_USERSET, QUERY_TUNING_METHOD,
+ "optimize_bounded_sort", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enable bounded sorting using heap sort."),
NULL,
GUC_NOT_IN_SAMPLE | GUC_EXPLAIN
@@ -1869,7 +1869,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"escape_string_warning", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"escape_string_warning", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Warn about backslash escapes in ordinary string literals."),
NULL
},
@@ -1879,7 +1879,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"standard_conforming_strings", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"standard_conforming_strings", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Causes '...' strings to treat backslashes literally."),
NULL,
GUC_REPORT
@@ -1890,7 +1890,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"synchronize_seqscans", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"synchronize_seqscans", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Enable synchronized sequential scans."),
NULL
},
@@ -1975,7 +1975,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"quote_all_identifiers", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"quote_all_identifiers", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("When generating SQL fragments, quote all identifiers."),
NULL,
},
@@ -2016,7 +2016,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"parallel_leader_participation", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"parallel_leader_participation", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Controls whether Gather and Gather Merge also run subplans."),
gettext_noop("Should gather nodes also run subplans or just gather tuples?"),
GUC_EXPLAIN
@@ -2027,7 +2027,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"jit", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"jit", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Allow JIT compilation."),
NULL,
GUC_EXPLAIN
@@ -2066,7 +2066,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"jit_expressions", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"jit_expressions", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Allow JIT compilation of expressions."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -2094,7 +2094,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"jit_tuple_deforming", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"jit_tuple_deforming", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Allow JIT compilation of tuple deforming."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -2154,7 +2154,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"default_statistics_target", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"default_statistics_target", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the default statistics target."),
gettext_noop("This applies to table columns that have not had a "
"column-specific target set via ALTER TABLE SET STATISTICS.")
@@ -2164,7 +2164,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"from_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"from_collapse_limit", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the FROM-list size beyond which subqueries "
"are not collapsed."),
gettext_noop("The planner will merge subqueries into upper "
@@ -2177,7 +2177,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"join_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"join_collapse_limit", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the FROM-list size beyond which JOIN "
"constructs are not flattened."),
gettext_noop("The planner will flatten explicit JOIN "
@@ -2190,7 +2190,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_threshold", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_threshold", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("Sets the threshold of FROM items beyond which GEQO is used."),
NULL,
GUC_EXPLAIN
@@ -2200,7 +2200,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_effort", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_effort", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: effort is used to set the default for other GEQO parameters."),
NULL,
GUC_EXPLAIN
@@ -2210,7 +2210,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_pool_size", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_pool_size", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: number of individuals in the population."),
gettext_noop("Zero selects a suitable default value."),
GUC_EXPLAIN
@@ -2220,7 +2220,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_generations", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_generations", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: number of iterations of the algorithm."),
gettext_noop("Zero selects a suitable default value."),
GUC_EXPLAIN
@@ -2367,7 +2367,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"temp_buffers", PGC_USERSET, RESOURCES_MEM,
+ {"temp_buffers", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum number of temporary buffers used by each session."),
NULL,
GUC_UNIT_BLOCKS | GUC_EXPLAIN
@@ -2432,7 +2432,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"work_mem", PGC_USERSET, RESOURCES_MEM,
+ {"work_mem", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum memory to be used for query workspaces."),
gettext_noop("This much memory can be used by each internal "
"sort operation and hash table before switching to "
@@ -2445,7 +2445,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"maintenance_work_mem", PGC_USERSET, RESOURCES_MEM,
+ {"maintenance_work_mem", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum memory to be used for maintenance operations."),
gettext_noop("This includes operations such as VACUUM and CREATE INDEX."),
GUC_UNIT_KB
@@ -2456,7 +2456,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"logical_decoding_work_mem", PGC_USERSET, RESOURCES_MEM,
+ {"logical_decoding_work_mem", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum memory to be used for logical decoding."),
gettext_noop("This much memory can be used by each internal "
"reorder buffer before spilling to disk."),
@@ -2495,7 +2495,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_page_hit", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_page_hit", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost for a page found in the buffer cache."),
NULL
},
@@ -2505,7 +2505,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_page_miss", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_page_miss", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost for a page not found in the buffer cache."),
NULL
},
@@ -2515,7 +2515,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_page_dirty", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_page_dirty", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost for a page dirtied by vacuum."),
NULL
},
@@ -2525,7 +2525,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_limit", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_limit", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost amount available before napping."),
NULL
},
@@ -2591,7 +2591,7 @@ static struct config_int ConfigureNamesInt[] =
#endif
{
- {"statement_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"statement_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed duration of any statement."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2602,7 +2602,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"lock_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"lock_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed duration of any wait for a lock."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2613,7 +2613,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"idle_in_transaction_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"idle_in_transaction_session_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed idle time between queries, when in a transaction."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2624,7 +2624,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"idle_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"idle_session_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed idle time between queries, when not in a transaction."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2635,7 +2635,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_freeze_min_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Minimum age at which VACUUM should freeze a table row."),
NULL
},
@@ -2645,7 +2645,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_freeze_table_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Age at which VACUUM should scan whole table to freeze tuples."),
NULL
},
@@ -2655,7 +2655,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_multixact_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_multixact_freeze_min_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Minimum age at which VACUUM should freeze a MultiXactId in a table row."),
NULL
},
@@ -2665,7 +2665,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_multixact_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_multixact_freeze_table_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Multixact age at which VACUUM should scan whole table to freeze tuples."),
NULL
},
@@ -2684,7 +2684,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"vacuum_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_failsafe_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
NULL
},
@@ -2693,7 +2693,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"vacuum_multixact_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_multixact_failsafe_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Multixact age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
NULL
},
@@ -2881,7 +2881,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"wal_skip_threshold", PGC_USERSET, WAL_SETTINGS,
+ {"wal_skip_threshold", PGC_SUSET, WAL_SETTINGS,
gettext_noop("Minimum size of new file to fsync instead of writing WAL."),
NULL,
GUC_UNIT_KB
@@ -2926,7 +2926,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"wal_sender_timeout", PGC_USERSET, REPLICATION_SENDING,
+ {"wal_sender_timeout", PGC_SUSET, REPLICATION_SENDING,
gettext_noop("Sets the maximum time to wait for WAL replication."),
NULL,
GUC_UNIT_MS
@@ -2949,7 +2949,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"commit_siblings", PGC_USERSET, WAL_SETTINGS,
+ {"commit_siblings", PGC_SUSET, WAL_SETTINGS,
gettext_noop("Sets the minimum number of concurrent open transactions "
"required before performing commit_delay."),
NULL
@@ -2960,7 +2960,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"extra_float_digits", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"extra_float_digits", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the number of digits displayed for floating-point values."),
gettext_noop("This affects real, double precision, and geometric data types. "
"A zero or negative parameter value is added to the standard "
@@ -3022,7 +3022,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"log_parameter_max_length_on_error", PGC_USERSET, LOGGING_WHAT,
+ {"log_parameter_max_length_on_error", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Sets the maximum length in bytes of data logged for bind "
"parameter values when logging statements, on error."),
gettext_noop("-1 to print values in full."),
@@ -3067,7 +3067,7 @@ static struct config_int ConfigureNamesInt[] =
{
{"effective_io_concurrency",
- PGC_USERSET,
+ PGC_SUSET,
RESOURCES_ASYNCHRONOUS,
gettext_noop("Number of simultaneous requests that can be handled efficiently by the disk subsystem."),
NULL,
@@ -3085,7 +3085,7 @@ static struct config_int ConfigureNamesInt[] =
{
{"maintenance_io_concurrency",
- PGC_USERSET,
+ PGC_SUSET,
RESOURCES_ASYNCHRONOUS,
gettext_noop("A variant of effective_io_concurrency that is used for maintenance work."),
NULL,
@@ -3102,7 +3102,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"backend_flush_after", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"backend_flush_after", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Number of pages after which previously performed writes are flushed to disk."),
NULL,
GUC_UNIT_BLOCKS
@@ -3334,7 +3334,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"max_parallel_maintenance_workers", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"max_parallel_maintenance_workers", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Sets the maximum number of parallel processes per maintenance operation."),
NULL
},
@@ -3344,7 +3344,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"max_parallel_workers_per_gather", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"max_parallel_workers_per_gather", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Sets the maximum number of parallel processes per executor node."),
NULL,
GUC_EXPLAIN
@@ -3355,7 +3355,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"max_parallel_workers", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"max_parallel_workers", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Sets the maximum number of parallel workers that can be active at one time."),
NULL,
GUC_EXPLAIN
@@ -3388,7 +3388,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_keepalives_idle", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_keepalives_idle", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Time between issuing TCP keepalives."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_S
@@ -3399,7 +3399,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_keepalives_interval", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_keepalives_interval", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Time between TCP keepalive retransmits."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_S
@@ -3410,7 +3410,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"ssl_renegotiation_limit", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"ssl_renegotiation_limit", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("SSL renegotiation is no longer supported; this can only be 0."),
NULL,
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE,
@@ -3421,7 +3421,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_keepalives_count", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_keepalives_count", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Maximum number of TCP keepalive retransmits."),
gettext_noop("This controls the number of consecutive keepalive retransmits that can be "
"lost before a connection is considered dead. A value of 0 uses the "
@@ -3433,7 +3433,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"gin_fuzzy_search_limit", PGC_USERSET, CLIENT_CONN_OTHER,
+ {"gin_fuzzy_search_limit", PGC_SUSET, CLIENT_CONN_OTHER,
gettext_noop("Sets the maximum allowed result for exact search by GIN."),
NULL,
0
@@ -3444,7 +3444,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"effective_cache_size", PGC_USERSET, QUERY_TUNING_COST,
+ {"effective_cache_size", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's assumption about the total size of the data caches."),
gettext_noop("That is, the total size of the caches (kernel cache and shared buffers) used for PostgreSQL data files. "
"This is measured in disk pages, which are normally 8 kB each."),
@@ -3456,7 +3456,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"min_parallel_table_scan_size", PGC_USERSET, QUERY_TUNING_COST,
+ {"min_parallel_table_scan_size", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the minimum amount of table data for a parallel scan."),
gettext_noop("If the planner estimates that it will read a number of table pages too small to reach this limit, a parallel scan will not be considered."),
GUC_UNIT_BLOCKS | GUC_EXPLAIN,
@@ -3467,7 +3467,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"min_parallel_index_scan_size", PGC_USERSET, QUERY_TUNING_COST,
+ {"min_parallel_index_scan_size", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the minimum amount of index data for a parallel scan."),
gettext_noop("If the planner estimates that it will read a number of index pages too small to reach this limit, a parallel scan will not be considered."),
GUC_UNIT_BLOCKS | GUC_EXPLAIN,
@@ -3512,7 +3512,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"gin_pending_list_limit", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"gin_pending_list_limit", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum size of the pending list for GIN index."),
NULL,
GUC_UNIT_KB
@@ -3523,7 +3523,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_user_timeout", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_user_timeout", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("TCP user timeout."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_MS
@@ -3568,7 +3568,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"client_connection_check_interval", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"client_connection_check_interval", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Sets the time interval between checks for disconnection while running queries."),
NULL,
GUC_UNIT_MS
@@ -3600,7 +3600,7 @@ static struct config_int ConfigureNamesInt[] =
static struct config_real ConfigureNamesReal[] =
{
{
- {"seq_page_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"seq_page_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of a "
"sequentially fetched disk page."),
NULL,
@@ -3611,7 +3611,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"random_page_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"random_page_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of a "
"nonsequentially fetched disk page."),
NULL,
@@ -3622,7 +3622,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"cpu_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"cpu_tuple_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"processing each tuple (row)."),
NULL,
@@ -3633,7 +3633,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"cpu_index_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"cpu_index_tuple_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"processing each index entry during an index scan."),
NULL,
@@ -3644,7 +3644,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"cpu_operator_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"cpu_operator_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"processing each operator or function call."),
NULL,
@@ -3655,7 +3655,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"parallel_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"parallel_tuple_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"passing each tuple (row) from worker to leader backend."),
NULL,
@@ -3666,7 +3666,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"parallel_setup_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"parallel_setup_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"starting up worker processes for parallel query."),
NULL,
@@ -3678,7 +3678,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"jit_above_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"jit_above_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Perform JIT compilation if query is more expensive."),
gettext_noop("-1 disables JIT compilation."),
GUC_EXPLAIN
@@ -3689,7 +3689,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"jit_optimize_above_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"jit_optimize_above_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Optimize JIT-compiled functions if query is more expensive."),
gettext_noop("-1 disables optimization."),
GUC_EXPLAIN
@@ -3700,7 +3700,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"jit_inline_above_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"jit_inline_above_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Perform JIT inlining if query is more expensive."),
gettext_noop("-1 disables inlining."),
GUC_EXPLAIN
@@ -3711,7 +3711,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"cursor_tuple_fraction", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"cursor_tuple_fraction", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the planner's estimate of the fraction of "
"a cursor's rows that will be retrieved."),
NULL,
@@ -3723,7 +3723,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"geqo_selection_bias", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_selection_bias", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: selective pressure within the population."),
NULL,
GUC_EXPLAIN
@@ -3734,7 +3734,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"geqo_seed", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_seed", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: seed for random path selection."),
NULL,
GUC_EXPLAIN
@@ -3745,7 +3745,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"hash_mem_multiplier", PGC_USERSET, RESOURCES_MEM,
+ {"hash_mem_multiplier", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Multiple of work_mem to use for hash tables."),
NULL,
GUC_EXPLAIN
@@ -3766,7 +3766,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"seed", PGC_USERSET, UNGROUPED,
+ {"seed", PGC_SUSET, UNGROUPED,
gettext_noop("Sets the seed for random-number generation."),
NULL,
GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -3777,7 +3777,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"vacuum_cost_delay", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_delay", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost delay in milliseconds."),
NULL,
GUC_UNIT_MS
@@ -4027,7 +4027,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"DateStyle", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"DateStyle", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the display format for date and time values."),
gettext_noop("Also controls interpretation of ambiguous "
"date inputs."),
@@ -4039,7 +4039,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"default_table_access_method", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_table_access_method", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default table access method for new tables."),
NULL,
GUC_IS_NAME
@@ -4050,7 +4050,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"default_tablespace", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_tablespace", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default tablespace to create tables and indexes in."),
gettext_noop("An empty string selects the database's default tablespace."),
GUC_IS_NAME
@@ -4061,7 +4061,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"temp_tablespaces", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE
@@ -4141,7 +4141,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"lc_monetary", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"lc_monetary", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the locale for formatting monetary amounts."),
NULL
},
@@ -4151,7 +4151,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"lc_numeric", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"lc_numeric", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the locale for formatting numbers."),
NULL
},
@@ -4161,7 +4161,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"lc_time", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"lc_time", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the locale for formatting date and time values."),
NULL
},
@@ -4193,7 +4193,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"local_preload_libraries", PGC_USERSET, CLIENT_CONN_PRELOAD,
+ {"local_preload_libraries", PGC_SUSET, CLIENT_CONN_PRELOAD,
gettext_noop("Lists unprivileged shared libraries to preload into each backend."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE
@@ -4204,7 +4204,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"search_path", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"search_path", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the schema search order for names that are not schema-qualified."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE | GUC_EXPLAIN
@@ -4240,7 +4240,7 @@ static struct config_string ConfigureNamesString[] =
{
/* Not for general use --- used by SET ROLE */
- {"role", PGC_USERSET, UNGROUPED,
+ {"role", PGC_SUSET, UNGROUPED,
gettext_noop("Sets the current role."),
NULL,
GUC_IS_NAME | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_NOT_WHILE_SEC_REST
@@ -4319,7 +4319,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"TimeZone", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"TimeZone", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the time zone for displaying and interpreting time stamps."),
NULL,
GUC_REPORT
@@ -4329,7 +4329,7 @@ static struct config_string ConfigureNamesString[] =
check_timezone, assign_timezone, show_timezone
},
{
- {"timezone_abbreviations", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"timezone_abbreviations", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Selects a file of time zone abbreviations."),
NULL
},
@@ -4522,7 +4522,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"default_text_search_config", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"default_text_search_config", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets default text search configuration."),
NULL
},
@@ -4584,7 +4584,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"application_name", PGC_USERSET, LOGGING_WHAT,
+ {"application_name", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Sets the application name to be reported in statistics and logs."),
NULL,
GUC_IS_NAME | GUC_REPORT | GUC_NOT_IN_SAMPLE
@@ -4648,7 +4648,7 @@ static struct config_string ConfigureNamesString[] =
static struct config_enum ConfigureNamesEnum[] =
{
{
- {"backslash_quote", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"backslash_quote", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Sets whether \"\\'\" is allowed in string literals."),
NULL
},
@@ -4658,7 +4658,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"bytea_output", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"bytea_output", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the output format for bytea."),
NULL
},
@@ -4668,7 +4668,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"client_min_messages", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"client_min_messages", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the message levels that are sent to the client."),
gettext_noop("Each level includes all the levels that follow it. The later"
" the level, the fewer messages are sent.")
@@ -4689,7 +4689,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"constraint_exclusion", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"constraint_exclusion", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Enables the planner to use constraints to optimize queries."),
gettext_noop("Table scans will be skipped if their constraints"
" guarantee that no rows match the query."),
@@ -4701,7 +4701,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"default_toast_compression", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_toast_compression", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default compression method for compressible values."),
NULL
},
@@ -4712,7 +4712,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"default_transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_transaction_isolation", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the transaction isolation level of each new transaction."),
NULL
},
@@ -4722,7 +4722,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"transaction_isolation", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the current transaction's isolation level."),
NULL,
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -4733,7 +4733,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"IntervalStyle", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"IntervalStyle", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the display format for interval values."),
NULL,
GUC_REPORT
@@ -4811,7 +4811,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"synchronous_commit", PGC_USERSET, WAL_SETTINGS,
+ {"synchronous_commit", PGC_SUSET, WAL_SETTINGS,
gettext_noop("Sets the current transaction's synchronization level."),
NULL
},
@@ -4918,7 +4918,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"xmlbinary", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"xmlbinary", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets how binary values are to be encoded in XML."),
NULL
},
@@ -4928,7 +4928,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"xmloption", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"xmloption", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets whether XML data in implicit parsing and serialization "
"operations is to be considered as documents or content fragments."),
NULL
@@ -4949,7 +4949,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"force_parallel_mode", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"force_parallel_mode", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Forces use of parallel query facilities."),
gettext_noop("If possible, run query using a parallel worker and with parallel restrictions."),
GUC_NOT_IN_SAMPLE | GUC_EXPLAIN
@@ -4960,7 +4960,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"password_encryption", PGC_USERSET, CONN_AUTH_AUTH,
+ {"password_encryption", PGC_SUSET, CONN_AUTH_AUTH,
gettext_noop("Chooses the algorithm for encrypting passwords."),
NULL
},
@@ -4970,7 +4970,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"plan_cache_mode", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"plan_cache_mode", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Controls the planner's selection of custom or generic plan."),
gettext_noop("Prepared statements can have custom and generic plans, and the planner "
"will attempt to choose which is better. This can be set to override "
@@ -5029,6 +5029,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_setting_acl
+ * entries.
*/
static const char *const map_old_guc_names[] = {
"sort_mem", "work_mem",
@@ -5428,25 +5432,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().)
+ *
+ * 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"
@@ -5463,8 +5471,24 @@ 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;
+}
+
+/*
+ * 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().)
+ */
+static bool
+valid_custom_variable_name(const char *name)
+{
+ int partcnt;
+
+ return (valid_variable_name(name, &partcnt) && partcnt > 1);
}
/*
@@ -7492,6 +7516,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 settingid = get_setting_oid(name, true);
+
+ if (OidIsValid(settingid))
+ {
+ AclResult aclresult;
+
+ aclresult = pg_setting_acl_aclcheck(settingid, GetUserId(),
+ ACL_SET_VALUE);
+
+ if (aclresult == ACLCHECK_OK)
+ break; /* okay */
+ }
+
+ /* No granted privilege */
ereport(elevel,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to set parameter \"%s\"",
@@ -8541,16 +8583,32 @@ 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;
+ Oid settingId;
+
+ settingId = get_setting_oid(name, true);
+ if (!OidIsValid(settingId))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+
+ aclresult = pg_setting_acl_aclcheck(settingId, GetUserId(),
+ ACL_ALTER_SYSTEM);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_SETTING, name);
+ }
+
switch (altersysstmt->setstmt->kind)
{
case VAR_SET_VALUE:
@@ -8743,6 +8801,9 @@ void
ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
{
GucAction action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET;
+ GucContext context;
+ AclResult aclresult;
+ Oid settingId;
/*
* Workers synchronize these parameters at the start of the parallel
@@ -8753,6 +8814,21 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
errmsg("cannot set parameters during a parallel operation")));
+ /*
+ * 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(settingId = get_setting_oid(stmt->name, true)))
+ {
+ aclresult = pg_setting_acl_aclcheck(settingId, GetUserId(),
+ ACL_SET_VALUE);
+ if (aclresult == ACLCHECK_OK)
+ context = PGC_SUSET;
+ }
+
switch (stmt->kind)
{
case VAR_SET_VALUE:
@@ -8761,7 +8837,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);
check_reserved_prefixes(stmt->name);
@@ -8847,7 +8923,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);
@@ -9631,6 +9707,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, false, 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,
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 03b80f9575..fc421a894c 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -163,6 +163,7 @@ static char *features_file;
static char *system_constraints_file;
static char *system_functions_file;
static char *system_views_file;
+static char *setting_privileges_file;
static bool success = false;
static bool made_new_pgdata = false;
static bool found_existing_pgdata = false;
@@ -2441,6 +2442,7 @@ setup_data_file_paths(void)
set_input(&system_constraints_file, "system_constraints.sql");
set_input(&system_functions_file, "system_functions.sql");
set_input(&system_views_file, "system_views.sql");
+ set_input(&setting_privileges_file, "setting_privileges.sql");
if (show_setting || debug)
{
@@ -2469,6 +2471,7 @@ setup_data_file_paths(void)
check_input(system_constraints_file);
check_input(system_functions_file);
check_input(system_views_file);
+ check_input(setting_privileges_file);
}
@@ -2816,6 +2819,8 @@ initialize_data_directory(void)
setup_run_file(cmdfd, system_views_file);
+ setup_run_file(cmdfd, setting_privileges_file);
+
setup_description(cmdfd);
setup_collation(cmdfd);
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index 6e216313c6..6552ef345c 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, SETTING or LARGE OBJECT)
* acls: the ACL string fetched from the database
* baseacls: the initial ACL string for this object; can be
* NULL or empty string to indicate "not available from server"
@@ -561,6 +561,11 @@ do { \
CONVERT_PRIV('U', "USAGE");
else if (strcmp(type, "FOREIGN TABLE") == 0)
CONVERT_PRIV('r', "SELECT");
+ else if (strcmp(type, "SETTING") == 0)
+ {
+ CONVERT_PRIV('s', "SET VALUE");
+ 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 59f4fbb2cc..0950d159c2 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -3413,6 +3413,7 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te)
strcmp(type, "SCHEMA") == 0 ||
strcmp(type, "EVENT TRIGGER") == 0 ||
strcmp(type, "FOREIGN DATA WRAPPER") == 0 ||
+ strcmp(type, "SETTING") == 0 ||
strcmp(type, "SERVER") == 0 ||
strcmp(type, "PUBLICATION") == 0 ||
strcmp(type, "SUBSCRIPTION") == 0 ||
@@ -3596,6 +3597,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, "SETTING") == 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 10a86f9810..5fd8923ce8 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -15236,6 +15236,9 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
case DEFACLOBJ_NAMESPACE:
type = "SCHEMAS";
break;
+ case DEFACLOBJ_SETTING:
+ type = "SETTINGS";
+ break;
default:
/* shouldn't get here */
fatal("unrecognized object type in default privileges: %d",
@@ -15279,7 +15282,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, SETTING 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 27b95732c8..62a5f612b0 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 dumpGroups(PGconn *conn);
static void dropTablespaces(PGconn *conn);
static void dumpTablespaces(PGconn *conn);
@@ -586,6 +587,10 @@ main(int argc, char *argv[])
dumpRoleMembership(conn);
else
dumpGroups(conn);
+
+ /* Dump role guc privileges */
+ if (server_version >= 150000)
+ dumpRoleGUCPrivs(conn);
}
/* Dump tablespaces */
@@ -1049,6 +1054,72 @@ dumpRoleMembership(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)
+{
+ PQExpBuffer buf = createPQExpBuffer();
+ PGresult *res;
+ int i;
+
+ printfPQExpBuffer(buf, "SELECT string_agg(acl.privilege_type, ', ' ORDER BY acl.privilege_type), "
+ "set_acl.setting, "
+ "grantee.rolname AS grantee, "
+ "acl.is_grantable, "
+ "grantor.rolname AS grantor "
+ "FROM pg_catalog.pg_setting_acl set_acl, "
+ "LATERAL (SELECT * FROM aclexplode(set_acl.setacl)) acl "
+ "JOIN pg_catalog.pg_authid grantee "
+ "ON acl.grantee = grantee.oid "
+ "LEFT JOIN pg_catalog.pg_authid grantor ON "
+ "acl.grantor = grantor.oid "
+ "WHERE acl.grantee > 0 "
+ "GROUP BY setting, grantee.rolname, is_grantable, grantor.rolname"
+ );
+
+ res = executeQuery(conn, buf->data);
+
+ if (PQntuples(res) > 0)
+ fprintf(OPF, "--\n-- Role privileges on configuration parameters\n--\n\n");
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ char *privilege = PQgetvalue(res, i, 0);
+ char *setting = PQgetvalue(res, i, 1);
+ char *grantee = PQgetvalue(res, i, 2);
+ char *grantable = PQgetvalue(res, i, 3);
+
+ fprintf(OPF, "GRANT %s", privilege);
+ fprintf(OPF, " ON %s", setting);
+ fprintf(OPF, " TO %s", fmtId(grantee));
+ if (*grantable == 't')
+ fprintf(OPF, " WITH GRANT OPTION");
+
+ /*
+ * We don't track the grantor very carefully in the backend, so cope
+ * with the possibility that it has been dropped.
+ */
+ if (!PQgetisnull(res, i, 4))
+ {
+ char *grantor = PQgetvalue(res, i, 4);
+
+ fprintf(OPF, " GRANTED BY %s", fmtId(grantor));
+ }
+ fprintf(OPF, ";\n");
+ }
+
+ PQclear(res);
+ destroyPQExpBuffer(buf);
+
+ fprintf(OPF, "\n\n");
+}
+
/*
* Dump group memberships from a pre-8.1 server. It's annoying that we
* can't share any useful amount of code with the post-8.1 case, but
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 3eca295ff4..a800f0b544 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -92,6 +92,7 @@ typedef enum ObjectClass
OCLASS_TYPE, /* pg_type */
OCLASS_CAST, /* pg_cast */
OCLASS_COLLATION, /* pg_collation */
+ OCLASS_SETTING, /* pg_setting_acl */
OCLASS_CONSTRAINT, /* pg_constraint */
OCLASS_CONVERSION, /* pg_conversion */
OCLASS_DEFAULT, /* pg_attrdef */
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index eb72dd6293..08a2dcee41 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_SETTING 'c' /* configuration parameter */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 79d787cd26..b6f1159931 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7214,6 +7214,25 @@
proname => 'has_type_privilege', provolatile => 's', prorettype => 'bool',
proargtypes => 'oid text', prosrc => 'has_type_privilege_id' },
+{ oid => '8050', descr => 'user privilege on setting by username, setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'name text text', prosrc => 'has_setting_privilege_name_name' },
+{ oid => '8051', descr => 'user privilege on setting by username, setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'name oid text', prosrc => 'has_setting_privilege_name_id' },
+{ oid => '8052', descr => 'user privilege on setting by user oid, setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid text text', prosrc => 'has_setting_privilege_id_name' },
+{ oid => '8053', descr => 'user privilege on setting by user oid, setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid oid text', prosrc => 'has_setting_privilege_id_id' },
+{ oid => '8054', descr => 'current user privilege on setting by setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'text text', prosrc => 'has_setting_privilege_name' },
+{ oid => '8055', descr => 'current user privilege on setting by setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid text', prosrc => 'has_setting_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/catalog/pg_setting_acl.h b/src/include/catalog/pg_setting_acl.h
new file mode 100644
index 0000000000..b7dee55e5c
--- /dev/null
+++ b/src/include/catalog/pg_setting_acl.h
@@ -0,0 +1,63 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_setting_acl.h
+ * definition of the "configuration parameter" system catalog
+ * (pg_setting_acl).
+ *
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_setting_acl.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SETTING_ACL_H
+#define PG_SETTING_ACL_H
+
+#include "catalog/genbki.h"
+#include "catalog/pg_setting_acl_d.h"
+
+/* ----------------
+ * pg_setting_acl definition. cpp turns this into
+ * typedef struct FormData_pg_setting_acl
+ * ----------------
+ */
+CATALOG(pg_setting_acl,8924,SettingAclRelationId) BKI_SHARED_RELATION
+{
+ Oid oid; /* oid */
+ /*
+
+ * Variable-length fields start here, but we allow direct access to
+ * setting.
+ */
+ text setting BKI_FORCE_NOT_NULL;
+
+#ifdef CATALOG_VARLEN
+ /* Access privileges */
+ aclitem setacl[1] BKI_DEFAULT(_null_);
+#endif
+} FormData_pg_setting_acl;
+
+
+/* ----------------
+ * Form_pg_setting_acl corresponds to a pointer to a tuple with
+ * the format of pg_setting_acl relation.
+ * ----------------
+ */
+typedef FormData_pg_setting_acl *Form_pg_setting_acl;
+
+DECLARE_TOAST(pg_setting_acl, 8925, 8926);
+#define PgSettingAclToastTable 8925
+#define PgSettingAclToastIndex 8926
+
+DECLARE_UNIQUE_INDEX(pg_setting_acl_setting_index, 8927, SettingAclSettingIndexId, on pg_setting_acl using btree(setting text_ops));
+DECLARE_UNIQUE_INDEX_PKEY(pg_setting_acl_oid_index, 8928, SettingAclOidIndexId, on pg_setting_acl using btree(oid oid_ops));
+
+extern Oid SettingAclCreate(const char *setting, bool if_not_exists);
+
+#endif /* PG_SETTING_ACL_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 4c5a8a39bf..1cd57d5552 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -92,7 +92,9 @@ 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 N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */
+#define ACL_SET_VALUE (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
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
@@ -1794,6 +1796,7 @@ typedef enum ObjectType
OBJECT_CAST,
OBJECT_COLUMN,
OBJECT_COLLATION,
+ OBJECT_SETTING,
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DEFAULT,
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f836acf876..527e723b39 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -307,6 +307,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 af771c901d..70a6d83336 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -146,9 +146,11 @@ 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_ALTER_SYSTEM_CHR 'A'
/* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTc"
+#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcsA"
/*
* Bitmasks defining "all rights" for each supported object type
@@ -165,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_SETTING (ACL_SET_VALUE|ACL_ALTER_SYSTEM)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -223,6 +226,8 @@ extern void select_best_grantor(Oid roleId, AclMode privileges,
extern void initialize_acl(void);
+extern Oid get_setting_oid(const char *setting, bool missing_ok);
+
/*
* prototypes for functions in aclchk.c
*/
@@ -243,6 +248,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_setting_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,
@@ -271,6 +278,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_setting_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 aa18d304ac..fb2fe193c5 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -379,6 +379,8 @@ extern int set_config_option(const char *name, const char *value,
GucAction action, bool changeVal, int elevel,
bool is_reload);
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 77871aaefc..a620eb6dca 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_setting_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 c8cfbc30f6..27cf1c6088 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -89,6 +89,8 @@ enum SysCacheIdentifier
REPLORIGNAME,
RULERELNAME,
SEQRELID,
+ SETTINGNAME,
+ SETTINGOID,
STATEXTDATASTXOID,
STATEXTNAMENSP,
STATEXTOID,
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 16f7610883..f8071eca00 100644
--- a/src/test/modules/test_pg_dump/t/001_base.pl
+++ b/src/test/modules/test_pg_dump/t/001_base.pl
@@ -317,6 +317,53 @@ my %tests = (
like => { pg_dumpall_globals => 1, },
},
+ 'GRANT ALTER SYSTEM ON ignore_checksum_failure' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT ALTER SYSTEM ON ignore_checksum_failure TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM ON ignore_checksum_failure TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT SET VALUE ON my.missing.guc' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET VALUE ON my.missing.guc TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT SET VALUE ON my\.missing\.guc TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT SET VALUE, ALTER SYSTEM ON something WITH GRANT OPTION' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET VALUE, ALTER SYSTEM ON something TO regress_dump_test_role WITH GRANT OPTION;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM, SET VALUE ON something TO regress_dump_test_role WITH GRANT OPTION GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALTER SYSTEM ON MyReallyLong.ButValidCustom.GucNameThatCannotFit.InNamedata64Byte.Format' => {
+ create_order => 2,
+ create_sql =>
+ # configuration parameters get cased folded
+ 'GRANT ALTER SYSTEM ON MyReallyLong.ButValidCustom.GucNameThatCannotFit.InNamedata64Byte.Format TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM ON myreallylong\.butvalidcustom\.gucnamethatcannotfit\.innamedata64byte\.format TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALTER SYSTEM, SET VALUE ON my.guc TO regress_dump_test_role GRANTED BY CURRENT_ROLE' => {
+ create_order => 2,
+ create_sql =>
+ # GRANTED BY CURRENT_ROLE is allowed for SQL compatibility, but is ignored
+ 'GRANT ALTER SYSTEM, SET VALUE ON my.guc TO regress_dump_test_role GRANTED BY CURRENT_ROLE;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM, SET VALUE ON my\.guc TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
'CREATE SCHEMA public' => {
regexp => qr/^CREATE SCHEMA public;/m,
like => {
diff --git a/src/test/regress/expected/guc_privs.out b/src/test/regress/expected/guc_privs.out
new file mode 100644
index 0000000000..f8bc8e7276
--- /dev/null
+++ b/src/test/regress/expected/guc_privs.out
@@ -0,0 +1,780 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform all 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 = 'en_US.UTF-8'; -- ok
+RESET lc_messages; -- ok
+ALTER SYSTEM SET lc_messages = 'en_US.UTF-8'; -- 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;
+-- Check setting privileges prior to creating any
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+ grantee | setting | privilege_type | is_grantable
+---------+-------------------------------------+----------------+--------------
+ | DateStyle | SET VALUE | f
+ | IntervalStyle | SET VALUE | f
+ | TimeZone | SET VALUE | f
+ | application_name | SET VALUE | f
+ | array_nulls | SET VALUE | f
+ | backend_flush_after | SET VALUE | f
+ | backslash_quote | SET VALUE | f
+ | bytea_output | SET VALUE | f
+ | check_function_bodies | SET VALUE | f
+ | client_connection_check_interval | SET VALUE | f
+ | client_min_messages | SET VALUE | f
+ | commit_siblings | SET VALUE | f
+ | constraint_exclusion | SET VALUE | f
+ | cpu_index_tuple_cost | SET VALUE | f
+ | cpu_operator_cost | SET VALUE | f
+ | cpu_tuple_cost | SET VALUE | f
+ | cursor_tuple_fraction | SET VALUE | f
+ | debug_pretty_print | SET VALUE | f
+ | debug_print_parse | SET VALUE | f
+ | debug_print_plan | SET VALUE | f
+ | debug_print_rewritten | SET VALUE | f
+ | default_statistics_target | SET VALUE | f
+ | default_table_access_method | SET VALUE | f
+ | default_tablespace | SET VALUE | f
+ | default_text_search_config | SET VALUE | f
+ | default_toast_compression | SET VALUE | f
+ | default_transaction_deferrable | SET VALUE | f
+ | default_transaction_isolation | SET VALUE | f
+ | default_transaction_read_only | SET VALUE | f
+ | default_with_oids | SET VALUE | f
+ | effective_cache_size | SET VALUE | f
+ | effective_io_concurrency | SET VALUE | f
+ | enable_async_append | SET VALUE | f
+ | enable_bitmapscan | SET VALUE | f
+ | enable_gathermerge | SET VALUE | f
+ | enable_hashagg | SET VALUE | f
+ | enable_hashjoin | SET VALUE | f
+ | enable_incremental_sort | SET VALUE | f
+ | enable_indexonlyscan | SET VALUE | f
+ | enable_indexscan | SET VALUE | f
+ | enable_material | SET VALUE | f
+ | enable_memoize | SET VALUE | f
+ | enable_mergejoin | SET VALUE | f
+ | enable_nestloop | SET VALUE | f
+ | enable_parallel_append | SET VALUE | f
+ | enable_parallel_hash | SET VALUE | f
+ | enable_partition_pruning | SET VALUE | f
+ | enable_partitionwise_aggregate | SET VALUE | f
+ | enable_partitionwise_join | SET VALUE | f
+ | enable_seqscan | SET VALUE | f
+ | enable_sort | SET VALUE | f
+ | enable_tidscan | SET VALUE | f
+ | escape_string_warning | SET VALUE | f
+ | exit_on_error | SET VALUE | f
+ | extra_float_digits | SET VALUE | f
+ | force_parallel_mode | SET VALUE | f
+ | from_collapse_limit | SET VALUE | f
+ | geqo | SET VALUE | f
+ | geqo_effort | SET VALUE | f
+ | geqo_generations | SET VALUE | f
+ | geqo_pool_size | SET VALUE | f
+ | geqo_seed | SET VALUE | f
+ | geqo_selection_bias | SET VALUE | f
+ | geqo_threshold | SET VALUE | f
+ | gin_fuzzy_search_limit | SET VALUE | f
+ | gin_pending_list_limit | SET VALUE | f
+ | hash_mem_multiplier | SET VALUE | f
+ | idle_in_transaction_session_timeout | SET VALUE | f
+ | idle_session_timeout | SET VALUE | f
+ | jit | SET VALUE | f
+ | jit_above_cost | SET VALUE | f
+ | jit_expressions | SET VALUE | f
+ | jit_inline_above_cost | SET VALUE | f
+ | jit_optimize_above_cost | SET VALUE | f
+ | jit_tuple_deforming | SET VALUE | f
+ | join_collapse_limit | SET VALUE | f
+ | lc_monetary | SET VALUE | f
+ | lc_numeric | SET VALUE | f
+ | lc_time | SET VALUE | f
+ | local_preload_libraries | SET VALUE | f
+ | lock_timeout | SET VALUE | f
+ | log_parameter_max_length_on_error | SET VALUE | f
+ | logical_decoding_work_mem | SET VALUE | f
+ | maintenance_io_concurrency | SET VALUE | f
+ | maintenance_work_mem | SET VALUE | f
+ | max_parallel_maintenance_workers | SET VALUE | f
+ | max_parallel_workers | SET VALUE | f
+ | max_parallel_workers_per_gather | SET VALUE | f
+ | min_parallel_index_scan_size | SET VALUE | f
+ | min_parallel_table_scan_size | SET VALUE | f
+ | optimize_bounded_sort | SET VALUE | f
+ | parallel_leader_participation | SET VALUE | f
+ | parallel_setup_cost | SET VALUE | f
+ | parallel_tuple_cost | SET VALUE | f
+ | password_encryption | SET VALUE | f
+ | plan_cache_mode | SET VALUE | f
+ | quote_all_identifiers | SET VALUE | f
+ | random_page_cost | SET VALUE | f
+ | role | SET VALUE | f
+ | row_security | SET VALUE | f
+ | search_path | SET VALUE | f
+ | seed | SET VALUE | f
+ | seq_page_cost | SET VALUE | f
+ | ssl_renegotiation_limit | SET VALUE | f
+ | standard_conforming_strings | SET VALUE | f
+ | statement_timeout | SET VALUE | f
+ | synchronize_seqscans | SET VALUE | f
+ | synchronous_commit | SET VALUE | f
+ | tcp_keepalives_count | SET VALUE | f
+ | tcp_keepalives_idle | SET VALUE | f
+ | tcp_keepalives_interval | SET VALUE | f
+ | tcp_user_timeout | SET VALUE | f
+ | temp_buffers | SET VALUE | f
+ | temp_tablespaces | SET VALUE | f
+ | timezone_abbreviations | SET VALUE | f
+ | trace_notify | SET VALUE | f
+ | trace_sort | SET VALUE | f
+ | trace_syncscan | SET VALUE | f
+ | transaction_deferrable | SET VALUE | f
+ | transaction_isolation | SET VALUE | f
+ | transaction_read_only | SET VALUE | f
+ | transform_null_equals | SET VALUE | f
+ | vacuum_cost_delay | SET VALUE | f
+ | vacuum_cost_limit | SET VALUE | f
+ | vacuum_cost_page_dirty | SET VALUE | f
+ | vacuum_cost_page_hit | SET VALUE | f
+ | vacuum_cost_page_miss | SET VALUE | f
+ | vacuum_failsafe_age | SET VALUE | f
+ | vacuum_freeze_min_age | SET VALUE | f
+ | vacuum_freeze_table_age | SET VALUE | f
+ | vacuum_multixact_failsafe_age | SET VALUE | f
+ | vacuum_multixact_freeze_min_age | SET VALUE | f
+ | vacuum_multixact_freeze_table_age | SET VALUE | f
+ | wal_sender_timeout | SET VALUE | f
+ | wal_skip_threshold | SET VALUE | f
+ | work_mem | SET VALUE | f
+ | xmlbinary | SET VALUE | f
+ | xmloption | SET VALUE | f
+(138 rows)
+
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+-- Check the new role does not yet have privileges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ f
+(1 row)
+
+-- Check inappropriate and nonsense privilege types
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+-- Grant privileges on settings to the new non-superuser role
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Check setting privileges after creating some
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+ grantee | setting | privilege_type | is_grantable
+-----------------------------+-------------------------------------+----------------+--------------
+ regress_host_resource_admin | autovacuum_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | autovacuum_work_mem | SET VALUE | f
+ regress_host_resource_admin | hash_mem_multiplier | ALTER SYSTEM | f
+ regress_host_resource_admin | hash_mem_multiplier | SET VALUE | f
+ regress_host_resource_admin | logical_decoding_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | logical_decoding_work_mem | SET VALUE | f
+ regress_host_resource_admin | maintenance_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | maintenance_work_mem | SET VALUE | f
+ regress_host_resource_admin | max_stack_depth | ALTER SYSTEM | f
+ regress_host_resource_admin | max_stack_depth | SET VALUE | f
+ regress_host_resource_admin | min_dynamic_shared_memory | ALTER SYSTEM | f
+ regress_host_resource_admin | min_dynamic_shared_memory | SET VALUE | f
+ regress_host_resource_admin | shared_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | shared_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_file_limit | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_file_limit | SET VALUE | f
+ regress_host_resource_admin | work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | work_mem | SET VALUE | f
+ | DateStyle | SET VALUE | f
+ | IntervalStyle | SET VALUE | f
+ | TimeZone | SET VALUE | f
+ | application_name | SET VALUE | f
+ | array_nulls | SET VALUE | f
+ | backend_flush_after | SET VALUE | f
+ | backslash_quote | SET VALUE | f
+ | bytea_output | SET VALUE | f
+ | check_function_bodies | SET VALUE | f
+ | client_connection_check_interval | SET VALUE | f
+ | client_min_messages | SET VALUE | f
+ | commit_siblings | SET VALUE | f
+ | constraint_exclusion | SET VALUE | f
+ | cpu_index_tuple_cost | SET VALUE | f
+ | cpu_operator_cost | SET VALUE | f
+ | cpu_tuple_cost | SET VALUE | f
+ | cursor_tuple_fraction | SET VALUE | f
+ | debug_pretty_print | SET VALUE | f
+ | debug_print_parse | SET VALUE | f
+ | debug_print_plan | SET VALUE | f
+ | debug_print_rewritten | SET VALUE | f
+ | default_statistics_target | SET VALUE | f
+ | default_table_access_method | SET VALUE | f
+ | default_tablespace | SET VALUE | f
+ | default_text_search_config | SET VALUE | f
+ | default_toast_compression | SET VALUE | f
+ | default_transaction_deferrable | SET VALUE | f
+ | default_transaction_isolation | SET VALUE | f
+ | default_transaction_read_only | SET VALUE | f
+ | default_with_oids | SET VALUE | f
+ | effective_cache_size | SET VALUE | f
+ | effective_io_concurrency | SET VALUE | f
+ | enable_async_append | SET VALUE | f
+ | enable_bitmapscan | SET VALUE | f
+ | enable_gathermerge | SET VALUE | f
+ | enable_hashagg | SET VALUE | f
+ | enable_hashjoin | SET VALUE | f
+ | enable_incremental_sort | SET VALUE | f
+ | enable_indexonlyscan | SET VALUE | f
+ | enable_indexscan | SET VALUE | f
+ | enable_material | SET VALUE | f
+ | enable_memoize | SET VALUE | f
+ | enable_mergejoin | SET VALUE | f
+ | enable_nestloop | SET VALUE | f
+ | enable_parallel_append | SET VALUE | f
+ | enable_parallel_hash | SET VALUE | f
+ | enable_partition_pruning | SET VALUE | f
+ | enable_partitionwise_aggregate | SET VALUE | f
+ | enable_partitionwise_join | SET VALUE | f
+ | enable_seqscan | SET VALUE | f
+ | enable_sort | SET VALUE | f
+ | enable_tidscan | SET VALUE | f
+ | escape_string_warning | SET VALUE | f
+ | exit_on_error | SET VALUE | f
+ | extra_float_digits | SET VALUE | f
+ | force_parallel_mode | SET VALUE | f
+ | from_collapse_limit | SET VALUE | f
+ | geqo | SET VALUE | f
+ | geqo_effort | SET VALUE | f
+ | geqo_generations | SET VALUE | f
+ | geqo_pool_size | SET VALUE | f
+ | geqo_seed | SET VALUE | f
+ | geqo_selection_bias | SET VALUE | f
+ | geqo_threshold | SET VALUE | f
+ | gin_fuzzy_search_limit | SET VALUE | f
+ | gin_pending_list_limit | SET VALUE | f
+ | hash_mem_multiplier | SET VALUE | f
+ | idle_in_transaction_session_timeout | SET VALUE | f
+ | idle_session_timeout | SET VALUE | f
+ | jit | SET VALUE | f
+ | jit_above_cost | SET VALUE | f
+ | jit_expressions | SET VALUE | f
+ | jit_inline_above_cost | SET VALUE | f
+ | jit_optimize_above_cost | SET VALUE | f
+ | jit_tuple_deforming | SET VALUE | f
+ | join_collapse_limit | SET VALUE | f
+ | lc_monetary | SET VALUE | f
+ | lc_numeric | SET VALUE | f
+ | lc_time | SET VALUE | f
+ | local_preload_libraries | SET VALUE | f
+ | lock_timeout | SET VALUE | f
+ | log_parameter_max_length_on_error | SET VALUE | f
+ | logical_decoding_work_mem | SET VALUE | f
+ | maintenance_io_concurrency | SET VALUE | f
+ | maintenance_work_mem | SET VALUE | f
+ | max_parallel_maintenance_workers | SET VALUE | f
+ | max_parallel_workers | SET VALUE | f
+ | max_parallel_workers_per_gather | SET VALUE | f
+ | min_parallel_index_scan_size | SET VALUE | f
+ | min_parallel_table_scan_size | SET VALUE | f
+ | optimize_bounded_sort | SET VALUE | f
+ | parallel_leader_participation | SET VALUE | f
+ | parallel_setup_cost | SET VALUE | f
+ | parallel_tuple_cost | SET VALUE | f
+ | password_encryption | SET VALUE | f
+ | plan_cache_mode | SET VALUE | f
+ | quote_all_identifiers | SET VALUE | f
+ | random_page_cost | SET VALUE | f
+ | role | SET VALUE | f
+ | row_security | SET VALUE | f
+ | search_path | SET VALUE | f
+ | seed | SET VALUE | f
+ | seq_page_cost | SET VALUE | f
+ | ssl_renegotiation_limit | SET VALUE | f
+ | standard_conforming_strings | SET VALUE | f
+ | statement_timeout | SET VALUE | f
+ | synchronize_seqscans | SET VALUE | f
+ | synchronous_commit | SET VALUE | f
+ | tcp_keepalives_count | SET VALUE | f
+ | tcp_keepalives_idle | SET VALUE | f
+ | tcp_keepalives_interval | SET VALUE | f
+ | tcp_user_timeout | SET VALUE | f
+ | temp_buffers | SET VALUE | f
+ | temp_tablespaces | SET VALUE | f
+ | timezone_abbreviations | SET VALUE | f
+ | trace_notify | SET VALUE | f
+ | trace_sort | SET VALUE | f
+ | trace_syncscan | SET VALUE | f
+ | transaction_deferrable | SET VALUE | f
+ | transaction_isolation | SET VALUE | f
+ | transaction_read_only | SET VALUE | f
+ | transform_null_equals | SET VALUE | f
+ | vacuum_cost_delay | SET VALUE | f
+ | vacuum_cost_limit | SET VALUE | f
+ | vacuum_cost_page_dirty | SET VALUE | f
+ | vacuum_cost_page_hit | SET VALUE | f
+ | vacuum_cost_page_miss | SET VALUE | f
+ | vacuum_failsafe_age | SET VALUE | f
+ | vacuum_freeze_min_age | SET VALUE | f
+ | vacuum_freeze_table_age | SET VALUE | f
+ | vacuum_multixact_failsafe_age | SET VALUE | f
+ | vacuum_multixact_freeze_min_age | SET VALUE | f
+ | vacuum_multixact_freeze_table_age | SET VALUE | f
+ | wal_sender_timeout | SET VALUE | f
+ | wal_skip_threshold | SET VALUE | f
+ | work_mem | SET VALUE | f
+ | xmlbinary | SET VALUE | f
+ | xmloption | SET VALUE | f
+(158 rows)
+
+-- Check the new role now has privilges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE WITH GRANT OPTION, ALTER SYSTEM WITH GRANT OPTION');
+ has_setting_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_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+SELECT has_setting_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_setting_privilege('regress_host_resource_admin',
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('hash_mem_multiplier', 'set value');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'work_mem'),
+ 'alter system with grant option');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+-- Check setting privileges show up in view
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+ grantee | setting | privilege_type | is_grantable
+-----------------------------+-------------------------------------+----------------+--------------
+ regress_host_resource_admin | autovacuum_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | autovacuum_work_mem | SET VALUE | f
+ regress_host_resource_admin | hash_mem_multiplier | ALTER SYSTEM | f
+ regress_host_resource_admin | hash_mem_multiplier | SET VALUE | f
+ regress_host_resource_admin | logical_decoding_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | logical_decoding_work_mem | SET VALUE | f
+ regress_host_resource_admin | maintenance_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | maintenance_work_mem | SET VALUE | f
+ regress_host_resource_admin | max_stack_depth | ALTER SYSTEM | f
+ regress_host_resource_admin | max_stack_depth | SET VALUE | f
+ regress_host_resource_admin | min_dynamic_shared_memory | ALTER SYSTEM | f
+ regress_host_resource_admin | min_dynamic_shared_memory | SET VALUE | f
+ regress_host_resource_admin | shared_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | shared_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_file_limit | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_file_limit | SET VALUE | f
+ regress_host_resource_admin | work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | work_mem | SET VALUE | f
+ | DateStyle | SET VALUE | f
+ | IntervalStyle | SET VALUE | f
+ | TimeZone | SET VALUE | f
+ | application_name | SET VALUE | f
+ | array_nulls | SET VALUE | f
+ | backend_flush_after | SET VALUE | f
+ | backslash_quote | SET VALUE | f
+ | bytea_output | SET VALUE | f
+ | check_function_bodies | SET VALUE | f
+ | client_connection_check_interval | SET VALUE | f
+ | client_min_messages | SET VALUE | f
+ | commit_siblings | SET VALUE | f
+ | constraint_exclusion | SET VALUE | f
+ | cpu_index_tuple_cost | SET VALUE | f
+ | cpu_operator_cost | SET VALUE | f
+ | cpu_tuple_cost | SET VALUE | f
+ | cursor_tuple_fraction | SET VALUE | f
+ | debug_pretty_print | SET VALUE | f
+ | debug_print_parse | SET VALUE | f
+ | debug_print_plan | SET VALUE | f
+ | debug_print_rewritten | SET VALUE | f
+ | default_statistics_target | SET VALUE | f
+ | default_table_access_method | SET VALUE | f
+ | default_tablespace | SET VALUE | f
+ | default_text_search_config | SET VALUE | f
+ | default_toast_compression | SET VALUE | f
+ | default_transaction_deferrable | SET VALUE | f
+ | default_transaction_isolation | SET VALUE | f
+ | default_transaction_read_only | SET VALUE | f
+ | default_with_oids | SET VALUE | f
+ | effective_cache_size | SET VALUE | f
+ | effective_io_concurrency | SET VALUE | f
+ | enable_async_append | SET VALUE | f
+ | enable_bitmapscan | SET VALUE | f
+ | enable_gathermerge | SET VALUE | f
+ | enable_hashagg | SET VALUE | f
+ | enable_hashjoin | SET VALUE | f
+ | enable_incremental_sort | SET VALUE | f
+ | enable_indexonlyscan | SET VALUE | f
+ | enable_indexscan | SET VALUE | f
+ | enable_material | SET VALUE | f
+ | enable_memoize | SET VALUE | f
+ | enable_mergejoin | SET VALUE | f
+ | enable_nestloop | SET VALUE | f
+ | enable_parallel_append | SET VALUE | f
+ | enable_parallel_hash | SET VALUE | f
+ | enable_partition_pruning | SET VALUE | f
+ | enable_partitionwise_aggregate | SET VALUE | f
+ | enable_partitionwise_join | SET VALUE | f
+ | enable_seqscan | SET VALUE | f
+ | enable_sort | SET VALUE | f
+ | enable_tidscan | SET VALUE | f
+ | escape_string_warning | SET VALUE | f
+ | exit_on_error | SET VALUE | f
+ | extra_float_digits | SET VALUE | f
+ | force_parallel_mode | SET VALUE | f
+ | from_collapse_limit | SET VALUE | f
+ | geqo | SET VALUE | f
+ | geqo_effort | SET VALUE | f
+ | geqo_generations | SET VALUE | f
+ | geqo_pool_size | SET VALUE | f
+ | geqo_seed | SET VALUE | f
+ | geqo_selection_bias | SET VALUE | f
+ | geqo_threshold | SET VALUE | f
+ | gin_fuzzy_search_limit | SET VALUE | f
+ | gin_pending_list_limit | SET VALUE | f
+ | hash_mem_multiplier | SET VALUE | f
+ | idle_in_transaction_session_timeout | SET VALUE | f
+ | idle_session_timeout | SET VALUE | f
+ | jit | SET VALUE | f
+ | jit_above_cost | SET VALUE | f
+ | jit_expressions | SET VALUE | f
+ | jit_inline_above_cost | SET VALUE | f
+ | jit_optimize_above_cost | SET VALUE | f
+ | jit_tuple_deforming | SET VALUE | f
+ | join_collapse_limit | SET VALUE | f
+ | lc_monetary | SET VALUE | f
+ | lc_numeric | SET VALUE | f
+ | lc_time | SET VALUE | f
+ | local_preload_libraries | SET VALUE | f
+ | lock_timeout | SET VALUE | f
+ | log_parameter_max_length_on_error | SET VALUE | f
+ | logical_decoding_work_mem | SET VALUE | f
+ | maintenance_io_concurrency | SET VALUE | f
+ | maintenance_work_mem | SET VALUE | f
+ | max_parallel_maintenance_workers | SET VALUE | f
+ | max_parallel_workers | SET VALUE | f
+ | max_parallel_workers_per_gather | SET VALUE | f
+ | min_parallel_index_scan_size | SET VALUE | f
+ | min_parallel_table_scan_size | SET VALUE | f
+ | optimize_bounded_sort | SET VALUE | f
+ | parallel_leader_participation | SET VALUE | f
+ | parallel_setup_cost | SET VALUE | f
+ | parallel_tuple_cost | SET VALUE | f
+ | password_encryption | SET VALUE | f
+ | plan_cache_mode | SET VALUE | f
+ | quote_all_identifiers | SET VALUE | f
+ | random_page_cost | SET VALUE | f
+ | role | SET VALUE | f
+ | row_security | SET VALUE | f
+ | search_path | SET VALUE | f
+ | seed | SET VALUE | f
+ | seq_page_cost | SET VALUE | f
+ | ssl_renegotiation_limit | SET VALUE | f
+ | standard_conforming_strings | SET VALUE | f
+ | statement_timeout | SET VALUE | f
+ | synchronize_seqscans | SET VALUE | f
+ | synchronous_commit | SET VALUE | f
+ | tcp_keepalives_count | SET VALUE | f
+ | tcp_keepalives_idle | SET VALUE | f
+ | tcp_keepalives_interval | SET VALUE | f
+ | tcp_user_timeout | SET VALUE | f
+ | temp_buffers | SET VALUE | f
+ | temp_tablespaces | SET VALUE | f
+ | timezone_abbreviations | SET VALUE | f
+ | trace_notify | SET VALUE | f
+ | trace_sort | SET VALUE | f
+ | trace_syncscan | SET VALUE | f
+ | transaction_deferrable | SET VALUE | f
+ | transaction_isolation | SET VALUE | f
+ | transaction_read_only | SET VALUE | f
+ | transform_null_equals | SET VALUE | f
+ | vacuum_cost_delay | SET VALUE | f
+ | vacuum_cost_limit | SET VALUE | f
+ | vacuum_cost_page_dirty | SET VALUE | f
+ | vacuum_cost_page_hit | SET VALUE | f
+ | vacuum_cost_page_miss | SET VALUE | f
+ | vacuum_failsafe_age | SET VALUE | f
+ | vacuum_freeze_min_age | SET VALUE | f
+ | vacuum_freeze_table_age | SET VALUE | f
+ | vacuum_multixact_failsafe_age | SET VALUE | f
+ | vacuum_multixact_freeze_min_age | SET VALUE | f
+ | vacuum_multixact_freeze_table_age | SET VALUE | f
+ | wal_sender_timeout | SET VALUE | f
+ | wal_skip_threshold | SET VALUE | f
+ | work_mem | SET VALUE | f
+ | xmlbinary | SET VALUE | f
+ | xmloption | SET VALUE | f
+(158 rows)
+
+-- 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
+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
+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 setting temp_buffers
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting logical_decoding_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting autovacuum_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting temp_file_limit
+-- Use "revoke" to remove the privileges and allow the role to be dropped
+REVOKE SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 setting temp_buffers
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting logical_decoding_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting autovacuum_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting 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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 setting temp_buffers
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting logical_decoding_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting autovacuum_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting 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
+-- Create non-superuser with privileges to configure plgsql custom variables
+CREATE ROLE regress_plpgsql_admin NOSUPERUSER;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin;
+SET plpgsql.extra_errors TO 'all';
+SET plpgsql.extra_warnings TO 'all';
+RESET plpgsql.extra_errors;
+RESET plpgsql.extra_warnings;
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all';
+ERROR: permission denied to set parameter "plpgsql.extra_errors"
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all';
+ERROR: permission denied to set parameter "plpgsql.extra_warnings"
+ALTER SYSTEM RESET plpgsql.extra_errors;
+ERROR: permission denied to set parameter "plpgsql.extra_errors"
+ALTER SYSTEM RESET plpgsql.extra_warnings;
+ERROR: permission denied to set parameter "plpgsql.extra_warnings"
+SET SESSION AUTHORIZATION regress_admin;
+GRANT SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+TO regress_plpgsql_admin;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin; -- ok
+SET plpgsql.extra_errors TO 'all'; -- ok
+SET plpgsql.extra_warnings TO 'all'; -- ok
+RESET plpgsql.extra_errors; -- ok
+RESET plpgsql.extra_warnings; -- ok
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all'; -- ok
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all'; -- ok
+ALTER SYSTEM RESET plpgsql.extra_errors; -- ok
+ALTER SYSTEM RESET plpgsql.extra_warnings; -- ok
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_plpgsql_admin; -- fail, privileges remain
+ERROR: role "regress_plpgsql_admin" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting plpgsql.extra_warnings
+privileges for setting plpgsql.extra_errors
+REVOKE SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+FROM regress_plpgsql_admin;
+DROP ROLE regress_plpgsql_admin; -- ok
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 0bc79be03d..6c8afdba57 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -54,6 +54,33 @@ REVOKE pg_read_all_settings FROM regress_priv_user8;
DROP USER regress_priv_user10;
DROP USER regress_priv_user9;
DROP USER regress_priv_user8;
+GRANT SET VALUE ON enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON enable_nestloop TO regress_priv_user6;
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+GRANT ALTER SYSTEM ON enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON "" TO regress_priv_user7; -- bad name
+ERROR: zero-length delimited identifier at or near """"
+LINE 1: GRANT ALTER SYSTEM ON "" TO regress_priv_user7;
+ ^
+GRANT ALTER SYSTEM ON " " TO regress_priv_user7; -- bad name
+ERROR: invalid setting name " "
+GRANT ALTER SYSTEM ON " foo " TO regress_priv_user7; -- bad name
+ERROR: invalid setting name " foo "
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
ALTER GROUP regress_priv_group1 ADD USER regress_priv_user4;
@@ -2357,10 +2384,32 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+ERROR: role "regress_priv_user6" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting enable_memoize
+privileges for setting enable_nestloop
+DROP USER regress_priv_user7; -- privileges remain
+ERROR: role "regress_priv_user7" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting enable_seqscan
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for table persons2
+privileges for setting no_such_param
+privileges for setting no_such_extension.no_such_param
+privileges for setting no_such_extension.no_such_param.longer.than.maximum.namedata.length
DROP USER regress_priv_user8; -- does not exist
ERROR: role "regress_priv_user8" does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+DROP USER regress_priv_user7; -- ok
+REVOKE SET VALUE ON enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON enable_nestloop FROM regress_priv_user6;
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
CREATE TABLE lock_table (a int);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index b58b062b10..e262035269 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1708,6 +1708,19 @@ pg_sequences| SELECT n.nspname AS schemaname,
JOIN pg_class c ON ((c.oid = s.seqrelid)))
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char"));
+pg_setting_privileges| SELECT grantor.rolname AS grantor,
+ grantee.rolname AS grantee,
+ set_acl.setting,
+ acl.privilege_type,
+ acl.is_grantable
+ FROM pg_setting_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_settings| SELECT a.name,
a.setting,
a.unit,
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 63706a28cc..4a8208c789 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -147,6 +147,7 @@ pg_replication_origin|t
pg_rewrite|t
pg_seclabel|t
pg_sequence|t
+pg_setting_acl|t
pg_shdepend|t
pg_shdescription|t
pg_shseclabel|t
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 017e962fed..b45a203eb6 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -86,7 +86,7 @@ test: brin_bloom brin_multi
# ----------
# Another group of parallel tests
# ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort guc_privs
# rules cannot run concurrently with any test that creates
# a view or rule in the public schema
diff --git a/src/test/regress/sql/guc_privs.sql b/src/test/regress/sql/guc_privs.sql
new file mode 100644
index 0000000000..31728c5be8
--- /dev/null
+++ b/src/test/regress/sql/guc_privs.sql
@@ -0,0 +1,185 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform all 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 = 'en_US.UTF-8'; -- ok
+RESET lc_messages; -- ok
+ALTER SYSTEM SET lc_messages = 'en_US.UTF-8'; -- 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;
+-- Check setting privileges prior to creating any
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+-- Check the new role does not yet have privileges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+-- Check inappropriate and nonsense privilege types
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+-- Grant privileges on settings to the new non-superuser role
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Check setting privileges after creating some
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+-- Check the new role now has privilges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE 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_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER WITH GRANT OPTION');
+-- Check other function signatures
+SELECT has_setting_privilege('regress_host_resource_admin',
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET VALUE');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+SELECT has_setting_privilege('hash_mem_multiplier', 'set value');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'work_mem'),
+ 'alter system with grant option');
+-- Check setting privileges show up in view
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+-- 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
+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
+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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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
+-- Create non-superuser with privileges to configure plgsql custom variables
+CREATE ROLE regress_plpgsql_admin NOSUPERUSER;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin;
+SET plpgsql.extra_errors TO 'all';
+SET plpgsql.extra_warnings TO 'all';
+RESET plpgsql.extra_errors;
+RESET plpgsql.extra_warnings;
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all';
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all';
+ALTER SYSTEM RESET plpgsql.extra_errors;
+ALTER SYSTEM RESET plpgsql.extra_warnings;
+SET SESSION AUTHORIZATION regress_admin;
+GRANT SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+TO regress_plpgsql_admin;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin; -- ok
+SET plpgsql.extra_errors TO 'all'; -- ok
+SET plpgsql.extra_warnings TO 'all'; -- ok
+RESET plpgsql.extra_errors; -- ok
+RESET plpgsql.extra_warnings; -- ok
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all'; -- ok
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all'; -- ok
+ALTER SYSTEM RESET plpgsql.extra_errors; -- ok
+ALTER SYSTEM RESET plpgsql.extra_warnings; -- ok
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_plpgsql_admin; -- fail, privileges remain
+REVOKE SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+FROM regress_plpgsql_admin;
+DROP ROLE regress_plpgsql_admin; -- ok
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index c8c545b64c..ddbf6afa44 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -66,6 +66,33 @@ DROP USER regress_priv_user10;
DROP USER regress_priv_user9;
DROP USER regress_priv_user8;
+GRANT SET VALUE ON enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON enable_nestloop TO regress_priv_user6;
+
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+
+GRANT ALTER SYSTEM ON enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON "" TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON " " TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON " foo " TO regress_priv_user7; -- bad name
+
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
+
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
@@ -1426,10 +1453,23 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+DROP USER regress_priv_user7; -- privileges remain
DROP USER regress_priv_user8; -- does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+DROP USER regress_priv_user7; -- ok
+
+REVOKE SET VALUE ON enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON enable_nestloop FROM regress_priv_user6;
+
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
--
2.21.1 (Apple Git-122.3)
On Mon, Dec 13, 2021 at 5:34 PM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:
On Dec 13, 2021, at 1:33 PM, Mark Dilger <mark.dilger@enterprisedb.com> wrote:
but will repost in a few hours
... and here it is:
currently there is a failure in check-world (not sure if it's known):
diff -U3 /src/postgres/src/test/regress/expected/guc_privs.out
/src/postgres/src/test/regress/results/guc_privs.out
--- /src/postgres/src/test/regress/expected/guc_privs.out 2021-12-14
14:11:45.000000000 +0000
+++ /src/postgres/src/test/regress/results/guc_privs.out 2021-12-14
15:50:52.219583421 +0000
@@ -39,8 +39,10 @@
ALTER SYSTEM RESET autovacuum; -- ok
-- PGC_SUSET
SET lc_messages = 'en_US.UTF-8'; -- ok
+ERROR: invalid value for parameter "lc_messages": "en_US.UTF-8"
RESET lc_messages; -- ok
ALTER SYSTEM SET lc_messages = 'en_US.UTF-8'; -- ok
+ERROR: invalid value for parameter "lc_messages": "en_US.UTF-8"
ALTER SYSTEM RESET lc_messages; -- ok
-- PGC_SU_BACKEND
SET jit_debugging_support = OFF; -- fail, cannot be set after connection start
Aside from that I've tested this and it seems to function as
advertised and in my view is worth adding to PG.
One thing that seems like an omission to me is the absence of a
InvokeObjectPostAlterHook in pg_setting_acl_aclcheck or
pg_setting_acl_aclmask so that MAC extensions can also block this,
InvokeObjectPostCreateHook is already in the create path so a
PostAlter hook seems appropriate.
Thank you.
On Dec 14, 2021, at 2:26 PM, Joshua Brindle <joshua.brindle@crunchydata.com> wrote:
currently there is a failure in check-world (not sure if it's known):
That one is definitely my fault. 'en_US.UTF-8' exists on my platform, so I hadn't noticed. I've changed it to use 'C', which should be portable.
One thing that seems like an omission to me is the absence of a
InvokeObjectPostAlterHook in pg_setting_acl_aclcheck or
pg_setting_acl_aclmask so that MAC extensions can also block this,
InvokeObjectPostCreateHook is already in the create path so a
PostAlter hook seems appropriate.
Good catch, but that seems like a strange place to put a PostAlterHook, so I added it to ExecGrant_Setting for v6, instead. This seems more consistent with the hook in SetDefaultACL.
(If you are really trying to do Managed Access Control (MAC), wouldn't that be a separate patch which adds security hooks into all *_aclcheck functions?)
Attachments:
v6-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-SET-for-gucs.patchapplication/octet-stream; name=v6-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-SET-for-gucs.patch; x-unix-mode=0644Download
From 5f2b574f4b0a36042e6393e6765a8429120b1918 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Wed, 15 Dec 2021 07:35:00 -0800
Subject: [PATCH v6] Allow GRANT of SET and ALTER SYSTEM SET for gucs
Allow granting of privilege to set or alter system set variables
which otherwise can only be managed by superusers. Each
(role,variable,privilege) triple is independently grantable, so a
user may be granted privilege to SET but not to ALTER SYSTEM SET on
a variable, or vice versa. The privilege to SET a userset variable
may be granted, though doing so has no practical effect, since any
role can set userset variables anyway. Worse, there is no way to
revoke the privilege to SET a userset variable. To remedy that,
most core userset variables have been changed to suset, with
explicit grants to set the variable to public.
---
contrib/pg_trgm/Makefile | 6 +-
contrib/pg_trgm/pg_trgm--1.6--1.7.sql | 8 +
contrib/pg_trgm/pg_trgm.control | 2 +-
contrib/postgres_fdw/Makefile | 3 +-
contrib/postgres_fdw/option.c | 2 +-
.../postgres_fdw/postgres_fdw--1.1--1.2.sql | 6 +
contrib/postgres_fdw/postgres_fdw.control | 2 +-
contrib/sepgsql/hooks.c | 2 +-
contrib/sepgsql/sepgsql.sql.in | 1 +
doc/src/sgml/catalogs.sgml | 75 +-
doc/src/sgml/ddl.sgml | 41 +-
doc/src/sgml/func.sgml | 20 +-
doc/src/sgml/ref/grant.sgml | 7 +
doc/src/sgml/ref/set.sgml | 5 +-
src/backend/catalog/Makefile | 6 +-
src/backend/catalog/aclchk.c | 254 ++++++
src/backend/catalog/catalog.c | 6 +
src/backend/catalog/dependency.c | 6 +
src/backend/catalog/objectaddress.c | 46 ++
src/backend/catalog/pg_setting_acl.c | 122 +++
src/backend/catalog/setting_privileges.sql | 62 ++
src/backend/catalog/system_views.sql | 13 +
src/backend/commands/alter.c | 1 +
src/backend/commands/dropcmds.c | 7 +
src/backend/commands/event_trigger.c | 6 +
src/backend/commands/seclabel.c | 1 +
src/backend/commands/tablecmds.c | 1 +
src/backend/parser/gram.y | 99 ++-
src/backend/utils/adt/acl.c | 258 ++++++
src/backend/utils/cache/lsyscache.c | 20 +
src/backend/utils/cache/syscache.c | 23 +
src/backend/utils/misc/guc.c | 402 +++++----
src/bin/initdb/initdb.c | 5 +
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 | 71 ++
src/include/catalog/dependency.h | 1 +
src/include/catalog/pg_default_acl.h | 1 +
src/include/catalog/pg_proc.dat | 19 +
src/include/catalog/pg_setting_acl.h | 63 ++
src/include/nodes/parsenodes.h | 5 +-
src/include/parser/kwlist.h | 1 +
src/include/utils/acl.h | 10 +-
src/include/utils/guc.h | 2 +
src/include/utils/lsyscache.h | 1 +
src/include/utils/syscache.h | 2 +
src/test/modules/test_pg_dump/t/001_base.pl | 47 ++
src/test/regress/expected/guc_privs.out | 780 ++++++++++++++++++
src/test/regress/expected/privileges.out | 53 +-
src/test/regress/expected/rules.out | 13 +
src/test/regress/expected/sanity_check.out | 1 +
src/test/regress/parallel_schedule | 2 +-
src/test/regress/sql/guc_privs.sql | 185 +++++
src/test/regress/sql/privileges.sql | 44 +-
55 files changed, 2644 insertions(+), 189 deletions(-)
create mode 100644 contrib/pg_trgm/pg_trgm--1.6--1.7.sql
create mode 100644 contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql
create mode 100644 src/backend/catalog/pg_setting_acl.c
create mode 100644 src/backend/catalog/setting_privileges.sql
create mode 100644 src/include/catalog/pg_setting_acl.h
create mode 100644 src/test/regress/expected/guc_privs.out
create mode 100644 src/test/regress/sql/guc_privs.sql
diff --git a/contrib/pg_trgm/Makefile b/contrib/pg_trgm/Makefile
index 1fbdc9ec1e..8fac4f6289 100644
--- a/contrib/pg_trgm/Makefile
+++ b/contrib/pg_trgm/Makefile
@@ -9,9 +9,9 @@ OBJS = \
trgm_regexp.o
EXTENSION = pg_trgm
-DATA = pg_trgm--1.5--1.6.sql pg_trgm--1.4--1.5.sql pg_trgm--1.3--1.4.sql \
- pg_trgm--1.3.sql pg_trgm--1.2--1.3.sql pg_trgm--1.1--1.2.sql \
- pg_trgm--1.0--1.1.sql
+DATA = pg_trgm--1.6--1.7.sql pg_trgm--1.5--1.6.sql pg_trgm--1.4--1.5.sql \
+ pg_trgm--1.3--1.4.sql pg_trgm--1.3.sql pg_trgm--1.2--1.3.sql \
+ pg_trgm--1.1--1.2.sql pg_trgm--1.0--1.1.sql
PGFILEDESC = "pg_trgm - trigram matching"
REGRESS = pg_trgm pg_word_trgm pg_strict_word_trgm
diff --git a/contrib/pg_trgm/pg_trgm--1.6--1.7.sql b/contrib/pg_trgm/pg_trgm--1.6--1.7.sql
new file mode 100644
index 0000000000..06b783acbd
--- /dev/null
+++ b/contrib/pg_trgm/pg_trgm--1.6--1.7.sql
@@ -0,0 +1,8 @@
+/* contrib/pg_trgm/pg_trgm--1.6--1.7.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_trgm UPDATE TO '1.7'" to load this file. \quit
+
+GRANT SET VALUE ON "pg_trgm.similarity_threshold" TO public;
+GRANT SET VALUE ON "pg_trgm.word_similarity_threshold" TO public;
+GRANT SET VALUE ON "pg_trgm.strict_word_similarity_threshold" TO public;
diff --git a/contrib/pg_trgm/pg_trgm.control b/contrib/pg_trgm/pg_trgm.control
index 1d6a9ddf25..6e3ee43c51 100644
--- a/contrib/pg_trgm/pg_trgm.control
+++ b/contrib/pg_trgm/pg_trgm.control
@@ -1,6 +1,6 @@
# pg_trgm extension
comment = 'text similarity measurement and index searching based on trigrams'
-default_version = '1.6'
+default_version = '1.7'
module_pathname = '$libdir/pg_trgm'
relocatable = true
trusted = true
diff --git a/contrib/postgres_fdw/Makefile b/contrib/postgres_fdw/Makefile
index c1b0cad453..3564015ee8 100644
--- a/contrib/postgres_fdw/Makefile
+++ b/contrib/postgres_fdw/Makefile
@@ -14,7 +14,8 @@ PG_CPPFLAGS = -I$(libpq_srcdir)
SHLIB_LINK_INTERNAL = $(libpq)
EXTENSION = postgres_fdw
-DATA = postgres_fdw--1.0.sql postgres_fdw--1.0--1.1.sql
+DATA = postgres_fdw--1.0.sql postgres_fdw--1.0--1.1.sql \
+ postgres_fdw--1.1--1.2.sql
REGRESS = postgres_fdw
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 48c7417e6e..5d30345fcf 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -464,7 +464,7 @@ _PG_init(void)
NULL,
&pgfdw_application_name,
NULL,
- PGC_USERSET,
+ PGC_SUSET,
0,
NULL,
NULL,
diff --git a/contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql b/contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql
new file mode 100644
index 0000000000..b174e04283
--- /dev/null
+++ b/contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql
@@ -0,0 +1,6 @@
+/* contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION postgres_fdw UPDATE TO '1.2'" to load this file. \quit
+
+GRANT SET VALUE ON "postgres_fdw.application_name" TO public;
diff --git a/contrib/postgres_fdw/postgres_fdw.control b/contrib/postgres_fdw/postgres_fdw.control
index d489382064..a4b800be4f 100644
--- a/contrib/postgres_fdw/postgres_fdw.control
+++ b/contrib/postgres_fdw/postgres_fdw.control
@@ -1,5 +1,5 @@
# postgres_fdw extension
comment = 'foreign-data wrapper for remote PostgreSQL servers'
-default_version = '1.1'
+default_version = '1.2'
module_pathname = '$libdir/postgres_fdw'
relocatable = true
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 19a3ffb7ff..f7ffd3d486 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -449,7 +449,7 @@ _PG_init(void)
NULL,
&sepgsql_debug_audit,
false,
- PGC_USERSET,
+ PGC_SUSET,
GUC_NOT_IN_SAMPLE,
NULL,
NULL,
diff --git a/contrib/sepgsql/sepgsql.sql.in b/contrib/sepgsql/sepgsql.sql.in
index 917d12dbbe..4dce769306 100644
--- a/contrib/sepgsql/sepgsql.sql.in
+++ b/contrib/sepgsql/sepgsql.sql.in
@@ -34,4 +34,5 @@ CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_setcon(text) RETURNS bool AS 'MODU
CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_mcstrans_in(text) RETURNS text AS 'MODULE_PATHNAME', 'sepgsql_mcstrans_in' LANGUAGE C STRICT;
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;
+GRANT SET VALUE ON "sepgsql.debug_audit" TO public;
SELECT sepgsql_restorecon(NULL);
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 03e2537b07..3f7e55c3b9 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -105,6 +105,11 @@
<entry>collations (locale information)</entry>
</row>
+ <row>
+ <entry><link linkend="catalog-pg-setting-acl"><structname>pg_setting_acl</structname></link></entry>
+ <entry>configuration parameters which have privileges granted to roles</entry>
+ </row>
+
<row>
<entry><link linkend="catalog-pg-constraint"><structname>pg_constraint</structname></link></entry>
<entry>check constraints, unique constraints, primary key constraints, foreign key constraints</entry>
@@ -2423,6 +2428,64 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</para>
</sect1>
+ <sect1 id="catalog-pg-setting-acl">
+ <title><structname>pg_setting_acl</structname></title>
+
+ <indexterm zone="catalog-pg-setting-acl">
+ <primary>pg_setting_acl</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_setting_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_setting_acl</structname>
+ is shared across all databases of a cluster: there is only one
+ copy of <structname>pg_setting_acl</structname> per cluster, not
+ one per database.
+ </para>
+
+ <table>
+ <title><structname>pg_setting_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>setting</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>
@@ -12494,11 +12557,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 settings 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 VALUE</literal> privilege granted
+ on the setting 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 64d9030652..3b48e8006e 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -1672,7 +1672,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 VALUE</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.
@@ -1940,6 +1941,28 @@ REVOKE ALL ON accounts FROM PUBLIC;
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>SET VALUE</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>SET</command> command to
+ change <literal>superuser</literal> run-time configuration parameters.
+ (Any user can set <literal>user</literal> run-time configuration
+ parameters.)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ALTER SYSTEM</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>ALTER SYSTEM
+ SET</command> command to change server configuration parameters.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
The privileges required by other commands are listed on the
@@ -2078,6 +2101,16 @@ REVOKE ALL ON accounts FROM PUBLIC;
<literal>TYPE</literal>
</entry>
</row>
+ <row>
+ <entry><literal>SET VALUE</literal></entry>
+ <entry><literal>s</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
+ <row>
+ <entry><literal>ALTER SYSTEM</literal></entry>
+ <entry><literal>A</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -2184,6 +2217,12 @@ REVOKE ALL ON accounts FROM PUBLIC;
<entry><literal>U</literal></entry>
<entry><literal>\dT+</literal></entry>
</row>
+ <row>
+ <entry><literal>Configuration parameter</literal></entry>
+ <entry><literal>sA</literal></entry>
+ <entry>none</entry>
+ <entry><literal></literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 5801299b27..7a66eaef08 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -22624,8 +22624,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');
@@ -22890,6 +22889,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_setting_privilege</primary>
+ </indexterm>
+ <function>has_setting_privilege</function> (
+ <optional> <parameter>user</parameter> <type>name</type> or <type>oid</type>, </optional>
+ <parameter>setting</parameter> <type>text</type> or <type>oid</type>,
+ <parameter>privilege</parameter> <type>text</type> )
+ <returnvalue>boolean</returnvalue>
+ </para>
+ <para>
+ Does user have privilege for setting?
+ Allowable privilege types are <literal>SET VALUE</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 a897712de2..21ec320a8d 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 VALUE | ALTER SYSTEM }
+ ON <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 VALUE</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/set.sgml b/doc/src/sgml/ref/set.sgml
index 339ee9eec9..a08057d1d1 100644
--- a/doc/src/sgml/ref/set.sgml
+++ b/doc/src/sgml/ref/set.sgml
@@ -34,8 +34,9 @@ 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.)
+ (But some require either superuser privileges or granted <literal>SET
+ VALUE</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>
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 4e6efda97f..45e5330c12 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -28,6 +28,7 @@ OBJS = \
pg_cast.o \
pg_class.o \
pg_collation.o \
+ pg_setting_acl.o \
pg_constraint.o \
pg_conversion.o \
pg_db_role_setting.o \
@@ -54,7 +55,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_setting_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 \
@@ -126,6 +127,7 @@ install-data: bki-stamp installdirs
$(INSTALL_DATA) $(srcdir)/system_functions.sql '$(DESTDIR)$(datadir)/system_functions.sql'
$(INSTALL_DATA) $(srcdir)/system_views.sql '$(DESTDIR)$(datadir)/system_views.sql'
$(INSTALL_DATA) $(srcdir)/information_schema.sql '$(DESTDIR)$(datadir)/information_schema.sql'
+ $(INSTALL_DATA) $(srcdir)/setting_privileges.sql '$(DESTDIR)$(datadir)/setting_privileges.sql'
$(INSTALL_DATA) $(srcdir)/sql_features.txt '$(DESTDIR)$(datadir)/sql_features.txt'
installdirs:
@@ -133,7 +135,7 @@ installdirs:
.PHONY: uninstall-data
uninstall-data:
- rm -f $(addprefix '$(DESTDIR)$(datadir)'/, postgres.bki system_constraints.sql system_functions.sql system_views.sql information_schema.sql sql_features.txt)
+ rm -f $(addprefix '$(DESTDIR)$(datadir)'/, postgres.bki system_constraints.sql system_functions.sql system_views.sql information_schema.sql setting_privileges.sql sql_features.txt)
# postgres.bki, system_constraints.sql, and the generated headers are
# in the distribution tarball, so they are not cleaned here.
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index ce0a4ff14e..c40dc94bc1 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_setting_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_Setting(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_SETTING:
+ whole_mask = ACL_ALL_RIGHTS_SETTING;
+ 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_SETTING:
+ all_privileges = ACL_ALL_RIGHTS_SETTING;
+ errormsg = gettext_noop("invalid privilege type %s for setting");
+ 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_SETTING:
+ ExecGrant_Setting(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -759,6 +771,38 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_SETTING:
+ foreach(cell, objnames)
+ {
+ char *setting = strVal(lfirst(cell));
+ Oid settingid = get_setting_oid(setting, true);
+
+ if (!OidIsValid(settingid))
+ {
+ /*
+ * Lookup the existing entry, or if necessary, add a new
+ * entry for this parameter. Entries only exist for
+ * parameters which currently have, or previously have had,
+ * privileges assigned.
+ *
+ * It is tempting to sanity-check the given configuration
+ * parameter name against known guc names in the guc
+ * tables, but for handling upgrades we need to accept
+ * setting names that do not yet exist.
+ */
+ settingid = SettingAclCreate(setting, 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, settingid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -1494,6 +1538,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case ForeignDataWrapperRelationId:
istmt.objtype = OBJECT_FDW;
break;
+ case SettingAclRelationId:
+ istmt.objtype = OBJECT_SETTING;
+ break;
default:
elog(ERROR, "unexpected object class %u", classid);
break;
@@ -3225,6 +3272,131 @@ ExecGrant_Type(InternalGrant *istmt)
table_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Setting(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_SETTING;
+
+ relation = table_open(SettingAclRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid settingId = lfirst_oid(cell);
+ Form_pg_setting_acl pg_setting_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_setting_acl];
+ bool nulls[Natts_pg_setting_acl];
+ bool replaces[Natts_pg_setting_acl];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(settingId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for setting %u", settingId);
+
+ pg_setting_acl_tuple = (Form_pg_setting_acl) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ aclDatum = SysCacheGetAttr(SETTINGNAME, tuple, Anum_pg_setting_acl_setacl,
+ &isNull);
+ if (isNull)
+ {
+ old_acl = acldefault(OBJECT_SETTING, InvalidOid);
+ /* 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, GetUserId(),
+ &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,
+ settingId, grantorId, OBJECT_SETTING,
+ text_to_cstring(&pg_setting_acl_tuple->setting),
+ 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_setting_acl_setacl - 1] = true;
+ values[Anum_pg_setting_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(settingId, SettingAclRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(SettingAclRelationId,
+ pg_setting_acl_tuple->oid, 0,
+ InvalidOid,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* Post alter hook called for grant and revoke */
+ InvokeObjectPostAlterHook(SettingAclRelationId, settingId, 0);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ table_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3255,6 +3427,10 @@ string_to_privilege(const char *privname)
return ACL_CREATE_TEMP;
if (strcmp(privname, "connect") == 0)
return ACL_CONNECT;
+ if (strcmp(privname, "set value") == 0)
+ return ACL_SET_VALUE;
+ 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 +3468,10 @@ privilege_to_string(AclMode privilege)
return "TEMP";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET_VALUE:
+ return "SET VALUE";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3328,6 +3508,13 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_COLUMN:
msg = gettext_noop("permission denied for column %s");
break;
+ case OBJECT_SETTING:
+ /*
+ * 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 +3751,7 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_AMPROC:
case OBJECT_ATTRIBUTE:
case OBJECT_CAST:
+ case OBJECT_SETTING:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
case OBJECT_DOMCONSTRAINT:
@@ -4000,6 +4188,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_setting_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 setting's ACL from pg_setting_acl
+ */
+ tuple = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(config_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("setting with OID %u does not exist",
+ config_oid)));
+
+ aclDatum = SysCacheGetAttr(SETTINGOID, tuple, Anum_pg_setting_acl_setacl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_SETTING, 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 +4954,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_setting_acl_aclcheck(Oid config_oid, Oid roleid, AclMode mode)
+{
+ if (pg_setting_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 aa7d4d5456..a069c2fb39 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_setting_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 == SettingAclRelationId ||
relationId == DatabaseRelationId ||
relationId == SharedDescriptionRelationId ||
relationId == SharedDependRelationId ||
@@ -260,6 +262,8 @@ IsSharedRelation(Oid relationId)
relationId == AuthIdOidIndexId ||
relationId == AuthMemRoleMemIndexId ||
relationId == AuthMemMemRoleIndexId ||
+ relationId == SettingAclSettingIndexId ||
+ relationId == SettingAclOidIndexId ||
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 == PgSettingAclToastTable ||
+ relationId == PgSettingAclToastIndex ||
relationId == PgDatabaseToastTable ||
relationId == PgDatabaseToastIndex ||
relationId == PgDbRoleSettingToastTable ||
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index fe9c714257..57174b7c19 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_setting_acl.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
@@ -150,6 +151,7 @@ static const Oid object_classes[] = {
TypeRelationId, /* OCLASS_TYPE */
CastRelationId, /* OCLASS_CAST */
CollationRelationId, /* OCLASS_COLLATION */
+ SettingAclRelationId, /* OCLASS_SETTING */
ConstraintRelationId, /* OCLASS_CONSTRAINT */
ConversionRelationId, /* OCLASS_CONVERSION */
AttrDefaultRelationId, /* OCLASS_DEFAULT */
@@ -1504,6 +1506,7 @@ doDeletion(const ObjectAddress *object, int flags)
/*
* These global object types are not supported here.
*/
+ case OCLASS_SETTING:
case OCLASS_ROLE:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
@@ -2778,6 +2781,9 @@ getObjectClass(const ObjectAddress *object)
case CollationRelationId:
return OCLASS_COLLATION;
+ case SettingAclRelationId:
+ return OCLASS_SETTING;
+
case ConstraintRelationId:
return OCLASS_CONSTRAINT;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 2bae3fbb17..682ae5e390 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_setting_acl.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
@@ -2309,6 +2310,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_COLUMN:
case OBJECT_ATTRIBUTE:
case OBJECT_COLLATION:
+ case OBJECT_SETTING:
case OBJECT_CONVERSION:
case OBJECT_STATISTIC_EXT:
case OBJECT_TSPARSER:
@@ -3510,6 +3512,22 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
break;
}
+ case OCLASS_SETTING:
+ {
+ char *setting;
+
+ setting = get_setting_name(object->objectId);
+ if (!setting)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for setting %u",
+ object->objectId);
+ break;
+ }
+ appendStringInfo(&buffer, _("setting %s"), setting);
+ break;
+ }
+
case OCLASS_SCHEMA:
{
char *nspname;
@@ -4473,6 +4491,10 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "collation");
break;
+ case OCLASS_SETTING:
+ appendStringInfoString(&buffer, "setting");
+ break;
+
case OCLASS_CONSTRAINT:
getConstraintTypeDescription(&buffer, object->objectId,
missing_ok);
@@ -4977,6 +4999,30 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_SETTING:
+ {
+ HeapTuple configTup;
+ Form_pg_setting_acl configForm;
+ char *namestr;
+
+ configTup = SearchSysCache1(SETTINGOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(configTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for setting %u",
+ object->objectId);
+ break;
+ }
+ configForm = (Form_pg_setting_acl) GETSTRUCT(configTup);
+ namestr = text_to_cstring(&configForm->setting);
+ appendStringInfoString(&buffer, namestr);
+ if (objname)
+ *objname = list_make1(namestr);
+ ReleaseSysCache(configTup);
+ break;
+ }
+
case OCLASS_CONVERSION:
{
HeapTuple conTup;
diff --git a/src/backend/catalog/pg_setting_acl.c b/src/backend/catalog/pg_setting_acl.c
new file mode 100644
index 0000000000..3c6594da03
--- /dev/null
+++ b/src/backend/catalog/pg_setting_acl.c
@@ -0,0 +1,122 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_setting_acl.c
+ * routines to support manipulation of the pg_setting_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_setting_acl.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/sysattr.h"
+#include "access/table.h"
+#include "access/tableam.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_setting_acl.h"
+#include "mb/pg_wchar.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/guc.h"
+#include "utils/pg_locale.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * SettingAclCreate
+ *
+ * Add a new tuple to pg_setting_acl.
+ *
+ * setting: the setting name to create.
+ * if_not_exists: if true, don't fail on duplicate name, but rather return
+ * the existing entry's Oid.
+ */
+Oid
+SettingAclCreate(const char *setting, bool if_not_exists)
+{
+ Relation rel;
+ TupleDesc tupDesc;
+ HeapTuple tuple;
+ Datum values[Natts_pg_setting_acl];
+ bool nulls[Natts_pg_setting_acl];
+ Oid settingId;
+ const char *canonical;
+
+ /*
+ * Check whether the setting (by the given name or alias)
+ * already exists.
+ */
+ settingId = get_setting_oid(setting, true);
+ if (OidIsValid(settingId))
+ {
+ if (if_not_exists)
+ return settingId;
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("setting \"%s\" already exists",
+ setting)));
+ }
+
+ /*
+ * We must not require the setting to be in the list of existent GUCs,
+ * as we may be called at different points during upgrades or the
+ * installation or removal of extensions. Instead, perform a basic sanity
+ * check of the setting, 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_setting_acl entries.
+ */
+ if (!valid_variable_name(setting, NULL))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid setting name \"%s\"",
+ setting)));
+ canonical = GetConfigOptionCanonicalName(setting);
+ if (!canonical)
+ canonical = setting;
+
+ /*
+ * 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(SettingAclRelationId, RowExclusiveLock);
+ tupDesc = RelationGetDescr(rel);
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ values[Anum_pg_setting_acl_setting - 1] =
+ DirectFunctionCall1(textin, CStringGetDatum(canonical));
+ settingId = GetNewOidWithIndex(rel,
+ SettingAclOidIndexId,
+ Anum_pg_setting_acl_oid);
+ values[Anum_pg_setting_acl_oid - 1] = ObjectIdGetDatum(settingId);
+ nulls[Anum_pg_setting_acl_setacl - 1] = true;
+ tuple = heap_form_tuple(tupDesc, values, nulls);
+ CatalogTupleInsert(rel, tuple);
+
+ /* Post creation hook for new setting */
+ InvokeObjectPostCreateHook(SettingAclRelationId, settingId, 0);
+
+ /*
+ * Close pg_setting_acl, but keep lock till commit.
+ */
+ heap_freetuple(tuple);
+ table_close(rel, NoLock);
+
+ return settingId;
+}
diff --git a/src/backend/catalog/setting_privileges.sql b/src/backend/catalog/setting_privileges.sql
new file mode 100644
index 0000000000..b788ebee7f
--- /dev/null
+++ b/src/backend/catalog/setting_privileges.sql
@@ -0,0 +1,62 @@
+/*
+ * PostgreSQL User SET variables
+ *
+ * Copyright (c) 2021, PostgreSQL Global Development Group
+ *
+ * src/backend/catalog/setting_privileges.sql
+ *
+ * Note: this file is read in single-user -j mode, which means that the
+ * command terminator is semicolon-newline-newline; whenever the backend
+ * sees that, it stops and executes what it's got. If you write a lot of
+ * statements without empty lines between, they'll all get quoted to you
+ * in any error message about one of them, so don't do that. Also, you
+ * cannot write a semicolon immediately followed by an empty line in a
+ * string literal (including a function body!) or a multiline comment.
+ */
+
+GRANT SET VALUE ON
+ enable_seqscan, enable_indexscan, enable_indexonlyscan, enable_bitmapscan,
+ enable_tidscan, enable_sort, enable_incremental_sort, enable_hashagg,
+ enable_material, enable_memoize, enable_nestloop, enable_mergejoin,
+ enable_hashjoin, enable_gathermerge, enable_partitionwise_join,
+ enable_partitionwise_aggregate, enable_parallel_append,
+ enable_parallel_hash, enable_partition_pruning, enable_async_append, geqo,
+ exit_on_error, debug_print_parse, debug_print_rewritten, debug_print_plan,
+ debug_pretty_print, trace_notify, transform_null_equals,
+ default_transaction_read_only, transaction_read_only,
+ default_transaction_deferrable, transaction_deferrable, row_security,
+ check_function_bodies, array_nulls, default_with_oids, trace_sort,
+ trace_syncscan, optimize_bounded_sort, escape_string_warning,
+ standard_conforming_strings, synchronize_seqscans, quote_all_identifiers,
+ parallel_leader_participation, jit, jit_expressions, jit_tuple_deforming,
+ default_statistics_target, from_collapse_limit, join_collapse_limit,
+ geqo_threshold, geqo_effort, geqo_pool_size, geqo_generations,
+ temp_buffers, work_mem, maintenance_work_mem, logical_decoding_work_mem,
+ vacuum_cost_page_hit, vacuum_cost_page_miss, vacuum_cost_page_dirty,
+ vacuum_cost_limit, statement_timeout, lock_timeout,
+ idle_in_transaction_session_timeout, idle_session_timeout,
+ vacuum_freeze_min_age, vacuum_freeze_table_age,
+ vacuum_multixact_freeze_min_age, vacuum_multixact_freeze_table_age,
+ vacuum_failsafe_age, vacuum_multixact_failsafe_age, wal_skip_threshold,
+ wal_sender_timeout, commit_siblings, extra_float_digits,
+ log_parameter_max_length_on_error, effective_io_concurrency,
+ maintenance_io_concurrency, backend_flush_after,
+ max_parallel_maintenance_workers, max_parallel_workers_per_gather,
+ max_parallel_workers, tcp_keepalives_idle, tcp_keepalives_interval,
+ ssl_renegotiation_limit, tcp_keepalives_count, gin_fuzzy_search_limit,
+ effective_cache_size, min_parallel_table_scan_size,
+ min_parallel_index_scan_size, gin_pending_list_limit, tcp_user_timeout,
+ client_connection_check_interval, seq_page_cost, random_page_cost,
+ cpu_tuple_cost, cpu_index_tuple_cost, cpu_operator_cost,
+ parallel_tuple_cost, parallel_setup_cost, jit_above_cost,
+ jit_optimize_above_cost, jit_inline_above_cost, cursor_tuple_fraction,
+ geqo_selection_bias, geqo_seed, hash_mem_multiplier, seed,
+ vacuum_cost_delay, DateStyle, default_table_access_method,
+ default_tablespace, temp_tablespaces, lc_monetary, lc_numeric, lc_time,
+ local_preload_libraries, search_path, role, TimeZone,
+ timezone_abbreviations, default_text_search_config, application_name,
+ backslash_quote, bytea_output, client_min_messages, constraint_exclusion,
+ default_toast_compression, default_transaction_isolation,
+ transaction_isolation, IntervalStyle, synchronous_commit, xmlbinary,
+ xmloption, force_parallel_mode, password_encryption, plan_cache_mode
+ TO public;
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 61b515cdb8..bd772dc9c5 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -593,6 +593,19 @@ CREATE RULE pg_settings_n AS
GRANT SELECT, UPDATE ON pg_settings TO PUBLIC;
+CREATE VIEW pg_setting_privileges AS
+ SELECT grantor.rolname AS grantor,
+ grantee.rolname AS grantee,
+ set_acl.setting AS setting,
+ acl.privilege_type AS privilege_type,
+ acl.is_grantable
+ FROM pg_catalog.pg_setting_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_setting_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 40044070cf..7da2e62683 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -639,6 +639,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
break;
case OCLASS_CAST:
+ case OCLASS_SETTING:
case OCLASS_CONSTRAINT:
case OCLASS_DEFAULT:
case OCLASS_LANGUAGE:
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 4e545adf95..45bc1c1948 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -276,6 +276,13 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
name = NameListToString(castNode(List, object));
}
break;
+ case OBJECT_SETTING:
+ if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
+ {
+ msg = gettext_noop("setting \"%s\" does not exist, skipping");
+ name = NameListToString(castNode(List, object));
+ }
+ break;
case OBJECT_CONVERSION:
if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
{
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index df264329d8..d6df5b3ad9 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_SETTING:
case OBJECT_DATABASE:
case OBJECT_TABLESPACE:
case OBJECT_ROLE:
@@ -1012,6 +1013,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
{
switch (objclass)
{
+ case OCLASS_SETTING:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
@@ -2078,6 +2080,8 @@ stringify_grant_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMN";
+ case OBJECT_SETTING:
+ return "SETTING";
case OBJECT_TABLE:
return "TABLE";
case OBJECT_SEQUENCE:
@@ -2161,6 +2165,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMNS";
+ case OBJECT_SETTING:
+ return "SETTINGS";
case OBJECT_TABLE:
return "TABLES";
case OBJECT_SEQUENCE:
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 53c18628a7..8f6f9bd7a0 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_SETTING:
case OBJECT_CONVERSION:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 47b29001d5..5672e9d1e4 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -12359,6 +12359,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_TYPE:
case OCLASS_CAST:
case OCLASS_COLLATION:
+ case OCLASS_SETTING:
case OCLASS_CONVERSION:
case OCLASS_LANGUAGE:
case OCLASS_LARGEOBJECT:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 3d4dd43e47..132b6907c5 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -363,8 +363,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 setting_name
+%type <list> OptSchemaEltList setting_target
%type <chr> am_type
@@ -402,8 +402,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> iso_level opt_encoding
%type <rolespec> grantee
%type <list> grantee_list
-%type <accesspriv> privilege
-%type <list> privileges privilege_list
+%type <accesspriv> privilege setting_priv
+%type <list> privileges privilege_list setting_priv_list
%type <privtarget> privilege_target
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
@@ -715,7 +715,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
@@ -6972,6 +6972,20 @@ GrantStmt: GRANT privileges ON privilege_target TO grantee_list
n->grantor = $8;
$$ = (Node*)n;
}
+ | GRANT setting_priv_list ON setting_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_SETTING;
+ n->objects = $4;
+ n->grantees = $6;
+ n->grant_option = $7;
+ n->grantor = $8;
+ $$ = (Node*)n;
+ }
;
RevokeStmt:
@@ -7005,6 +7019,36 @@ RevokeStmt:
n->behavior = $11;
$$ = (Node *)n;
}
+ | REVOKE setting_priv_list ON setting_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_SETTING;
+ n->objects = $4;
+ n->grantees = $6;
+ n->grantor = $7;
+ n->behavior = $8;
+ $$ = (Node *)n;
+ }
+ | REVOKE GRANT OPTION FOR setting_priv_list ON setting_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_SETTING;
+ n->objects = $7;
+ n->grantees = $9;
+ n->grantor = $10;
+ n->behavior = $11;
+ $$ = (Node *)n;
+ }
;
@@ -7073,6 +7117,49 @@ privilege: SELECT opt_column_list
}
;
+setting_priv_list: setting_priv { $$ = list_make1($1); }
+ | setting_priv_list ',' setting_priv { $$ = lappend($1, $3); }
+ ;
+
+setting_priv:
+ ALTER SYSTEM_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("alter system");
+ n->cols = NIL;
+ $$ = n;
+ }
+ | SET VALUE_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("set value");
+ n->cols = NIL;
+ $$ = n;
+ }
+ ;
+
+
+setting_target:
+ setting_name
+ {
+ $$ = list_make1(makeString($1));
+ }
+ | setting_target ',' setting_name
+ {
+ $$ = lappend($1, makeString($3));
+ }
+ ;
+
+setting_name:
+ ColId
+ {
+ $$ = $1;
+ }
+ | setting_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.
@@ -15818,6 +15905,7 @@ unreserved_keyword:
| OWNED
| OWNER
| PARALLEL
+ | PARAMETER
| PARSER
| PARTIAL
| PARTITION
@@ -16396,6 +16484,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 67f8b29434..f0dd3e52ed 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_setting_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,8 @@ 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 Oid convert_setting_name(text *setting);
+static AclMode convert_setting_priv_string(text *priv_setting_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 +310,12 @@ aclparse(const char *s, AclItem *aip)
case ACL_CONNECT_CHR:
read = ACL_CONNECT;
break;
+ case ACL_SET_VALUE_CHR:
+ read = ACL_SET_VALUE;
+ break;
+ case ACL_ALTER_SYSTEM_CHR:
+ read = ACL_ALTER_SYSTEM;
+ break;
case 'R': /* ignore old RULE privileges */
read = 0;
break;
@@ -794,6 +804,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_SETTING:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_NO_RIGHTS;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -1602,6 +1616,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 VALUE") == 0)
+ return ACL_SET_VALUE;
+ 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 +1716,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET_VALUE:
+ return "SET VALUE";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
@@ -4429,6 +4451,205 @@ convert_type_priv_string(text *priv_type_text)
return convert_any_priv_string(priv_type_text, type_priv_map);
}
+/*
+ * has_setting_privilege variants
+ * These are all named "has_setting_privilege" at the SQL level.
+ * They take various combinations of setting name, setting 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_setting_privilege_name_name
+ * Check user privileges on a setting given
+ * name username, text setting, and text priv name.
+ */
+Datum
+has_setting_privilege_name_name(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ text *setting = PG_GETARG_TEXT_PP(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid roleid;
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_role_oid_or_public(NameStr(*username));
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_name
+ * Check user privileges on a setting given
+ * text setting and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_setting_privilege_name(PG_FUNCTION_ARGS)
+{
+ text *setting = PG_GETARG_TEXT_PP(0);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(1);
+ Oid roleid;
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_name_id
+ * Check user privileges on a setting given
+ * name usename, setting oid, and text priv name.
+ */
+Datum
+has_setting_privilege_name_id(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ Oid settingoid = PG_GETARG_OID(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_role_oid_or_public(NameStr(*username));
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id
+ * Check user privileges on a setting given
+ * setting oid, and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_setting_privilege_id(PG_FUNCTION_ARGS)
+{
+ Oid settingoid = PG_GETARG_OID(0);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(1);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id_name
+ * Check user privileges on a setting given
+ * roleid, text setting, and text priv name.
+ */
+Datum
+has_setting_privilege_id_name(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ text *setting = PG_GETARG_TEXT_PP(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id_id
+ * Check user privileges on a setting given
+ * roleid, setting oid, and text priv name.
+ */
+Datum
+has_setting_privilege_id_id(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ Oid settingoid = PG_GETARG_OID(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ AclMode mode;
+ AclResult aclresult;
+
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * Support routines for has_setting_privilege family.
+ */
+
+/*
+ * Given a setting name expressed as a string, look it up and return Oid
+ */
+static Oid
+convert_setting_name(text *settingname)
+{
+ Oid oid;
+ char *setting = text_to_cstring(settingname);
+
+ oid = get_setting_oid(setting, true);
+
+ if (!OidIsValid(oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("setting \"%s\" does not exist", setting)));
+
+ return oid;
+}
+
+/*
+ * convert_setting_priv_string
+ * Convert text string to AclMode value.
+ */
+static AclMode
+convert_setting_priv_string(text *priv_setting_text)
+{
+ static const priv_map setting_priv_map[] = {
+ {"SET VALUE", ACL_SET_VALUE},
+ {"SET VALUE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SET_VALUE)},
+ {"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_setting_text, setting_priv_map);
+}
/*
* pg_has_role variants
@@ -4670,6 +4891,43 @@ initialize_acl(void)
}
}
+/*
+ * get_setting_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_setting_oid(const char *setting, bool missing_ok)
+{
+ Oid oid;
+
+ /* Check for the variable by the name we were given */
+ oid = GetSysCacheOid1(SETTINGNAME, Anum_pg_setting_acl_oid,
+ PointerGetDatum(cstring_to_text(setting)));
+ if (!OidIsValid(oid))
+ {
+ const char *canonical;
+
+ /* Check if the variable has a different canonical spelling */
+ canonical = GetConfigOptionCanonicalName(setting);
+ if (canonical != NULL)
+ oid = GetSysCacheOid1(SETTINGNAME, Anum_pg_setting_acl_oid,
+ PointerGetDatum(cstring_to_text(canonical)));
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("setting \"%s\" does not exist", setting)));
+ }
+
+ return oid;
+}
+
/*
* RoleMembershipCacheCallback
* Syscache inval callback function
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 9176514a96..0a0cbfd9f0 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_setting_acl.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_transform.h"
#include "catalog/pg_type.h"
@@ -3304,6 +3305,25 @@ free_attstatsslot(AttStatsSlot *sslot)
pfree(sslot->numbers_arr);
}
+char *
+get_setting_name(Oid configid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(configid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_setting_acl configtup = (Form_pg_setting_acl) GETSTRUCT(tp);
+ char *result;
+
+ result = pstrdup(text_to_cstring(&configtup->setting));
+ 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 56870b46e4..00fbe5218d 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_setting_acl.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
#include "catalog/pg_shseclabel.h"
@@ -762,6 +763,28 @@ static const struct cachedesc cacheinfo[] = {
},
32
},
+ {SettingAclRelationId, /* SETTINGNAME */
+ SettingAclSettingIndexId,
+ 1,
+ {
+ Anum_pg_setting_acl_setting,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+ {SettingAclRelationId, /* SETTINGOID */
+ SettingAclOidIndexId,
+ 1,
+ {
+ Anum_pg_setting_acl_oid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{StatisticExtDataRelationId, /* STATEXTDATASTXOID */
StatisticExtDataStxoidIndexId,
1,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index f736e8d872..e2615138b6 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -974,7 +974,7 @@ static const unit_conversion time_unit_conversion_table[] =
static struct config_bool ConfigureNamesBool[] =
{
{
- {"enable_seqscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_seqscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of sequential-scan plans."),
NULL,
GUC_EXPLAIN
@@ -984,7 +984,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_indexscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_indexscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of index-scan plans."),
NULL,
GUC_EXPLAIN
@@ -994,7 +994,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_indexonlyscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_indexonlyscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of index-only-scan plans."),
NULL,
GUC_EXPLAIN
@@ -1004,7 +1004,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_bitmapscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_bitmapscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of bitmap-scan plans."),
NULL,
GUC_EXPLAIN
@@ -1014,7 +1014,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_tidscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_tidscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of TID scan plans."),
NULL,
GUC_EXPLAIN
@@ -1024,7 +1024,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_sort", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_sort", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of explicit sort steps."),
NULL,
GUC_EXPLAIN
@@ -1034,7 +1034,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_incremental_sort", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_incremental_sort", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of incremental sort steps."),
NULL,
GUC_EXPLAIN
@@ -1044,7 +1044,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_hashagg", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_hashagg", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of hashed aggregation plans."),
NULL,
GUC_EXPLAIN
@@ -1054,7 +1054,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_material", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_material", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of materialization."),
NULL,
GUC_EXPLAIN
@@ -1064,7 +1064,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_memoize", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_memoize", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of memoization."),
NULL,
GUC_EXPLAIN
@@ -1074,7 +1074,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_nestloop", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_nestloop", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of nested-loop join plans."),
NULL,
GUC_EXPLAIN
@@ -1084,7 +1084,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_mergejoin", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_mergejoin", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of merge join plans."),
NULL,
GUC_EXPLAIN
@@ -1094,7 +1094,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_hashjoin", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_hashjoin", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of hash join plans."),
NULL,
GUC_EXPLAIN
@@ -1104,7 +1104,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_gathermerge", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_gathermerge", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of gather merge plans."),
NULL,
GUC_EXPLAIN
@@ -1114,7 +1114,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_partitionwise_join", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_partitionwise_join", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables partitionwise join."),
NULL,
GUC_EXPLAIN
@@ -1124,7 +1124,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_partitionwise_aggregate", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_partitionwise_aggregate", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables partitionwise aggregation and grouping."),
NULL,
GUC_EXPLAIN
@@ -1134,7 +1134,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_parallel_append", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_parallel_append", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of parallel append plans."),
NULL,
GUC_EXPLAIN
@@ -1144,7 +1144,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_parallel_hash", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_parallel_hash", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of parallel hash plans."),
NULL,
GUC_EXPLAIN
@@ -1154,7 +1154,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_partition_pruning", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_partition_pruning", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables plan-time and execution-time partition pruning."),
gettext_noop("Allows the query planner and executor to compare partition "
"bounds to conditions in the query to determine which "
@@ -1166,7 +1166,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_async_append", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_async_append", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of async append plans."),
NULL,
GUC_EXPLAIN
@@ -1176,7 +1176,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("Enables genetic query optimization."),
gettext_noop("This algorithm attempts to do planning without "
"exhaustive searching."),
@@ -1398,7 +1398,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"exit_on_error", PGC_USERSET, ERROR_HANDLING_OPTIONS,
+ {"exit_on_error", PGC_SUSET, ERROR_HANDLING_OPTIONS,
gettext_noop("Terminate session on any error."),
NULL
},
@@ -1436,7 +1436,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_print_parse", PGC_USERSET, LOGGING_WHAT,
+ {"debug_print_parse", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs each query's parse tree."),
NULL
},
@@ -1445,7 +1445,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_print_rewritten", PGC_USERSET, LOGGING_WHAT,
+ {"debug_print_rewritten", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs each query's rewritten parse tree."),
NULL
},
@@ -1454,7 +1454,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_print_plan", PGC_USERSET, LOGGING_WHAT,
+ {"debug_print_plan", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs each query's execution plan."),
NULL
},
@@ -1463,7 +1463,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_pretty_print", PGC_USERSET, LOGGING_WHAT,
+ {"debug_pretty_print", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Indents parse and plan tree displays."),
NULL
},
@@ -1584,7 +1584,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"trace_notify", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"trace_notify", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Generates debugging output for LISTEN and NOTIFY."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -1668,7 +1668,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"transform_null_equals", PGC_USERSET, COMPAT_OPTIONS_CLIENT,
+ {"transform_null_equals", PGC_SUSET, COMPAT_OPTIONS_CLIENT,
gettext_noop("Treats \"expr=NULL\" as \"expr IS NULL\"."),
gettext_noop("When turned on, expressions of the form expr = NULL "
"(or NULL = expr) are treated as expr IS NULL, that is, they "
@@ -1690,7 +1690,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"default_transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_transaction_read_only", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default read-only status of new transactions."),
NULL,
GUC_REPORT
@@ -1700,7 +1700,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"transaction_read_only", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the current transaction's read-only status."),
NULL,
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -1710,7 +1710,7 @@ static struct config_bool ConfigureNamesBool[] =
check_transaction_read_only, NULL, NULL
},
{
- {"default_transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_transaction_deferrable", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default deferrable status of new transactions."),
NULL
},
@@ -1719,7 +1719,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"transaction_deferrable", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Whether to defer a read-only serializable transaction until it can be executed with no possible serialization failures."),
NULL,
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -1729,7 +1729,7 @@ static struct config_bool ConfigureNamesBool[] =
check_transaction_deferrable, NULL, NULL
},
{
- {"row_security", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"row_security", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Enable row security."),
gettext_noop("When enabled, row security will be applied to all users.")
},
@@ -1738,7 +1738,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"check_function_bodies", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"check_function_bodies", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Check routine bodies during CREATE FUNCTION and CREATE PROCEDURE."),
NULL
},
@@ -1747,7 +1747,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"array_nulls", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"array_nulls", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Enable input of NULL elements in arrays."),
gettext_noop("When turned on, unquoted NULL in an array input "
"value means a null value; "
@@ -1764,7 +1764,7 @@ static struct config_bool ConfigureNamesBool[] =
* avoid unnecessarily breaking older dump files.
*/
{
- {"default_with_oids", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"default_with_oids", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("WITH OIDS is no longer supported; this can only be false."),
NULL,
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE
@@ -1794,7 +1794,7 @@ static struct config_bool ConfigureNamesBool[] =
#ifdef TRACE_SORT
{
- {"trace_sort", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"trace_sort", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Emit information about resource usage in sorting."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -1808,7 +1808,7 @@ static struct config_bool ConfigureNamesBool[] =
#ifdef TRACE_SYNCSCAN
/* this is undocumented because not exposed in a standard build */
{
- {"trace_syncscan", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"trace_syncscan", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Generate debugging output for synchronized scanning."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -1823,7 +1823,7 @@ static struct config_bool ConfigureNamesBool[] =
/* this is undocumented because not exposed in a standard build */
{
{
- "optimize_bounded_sort", PGC_USERSET, QUERY_TUNING_METHOD,
+ "optimize_bounded_sort", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enable bounded sorting using heap sort."),
NULL,
GUC_NOT_IN_SAMPLE | GUC_EXPLAIN
@@ -1869,7 +1869,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"escape_string_warning", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"escape_string_warning", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Warn about backslash escapes in ordinary string literals."),
NULL
},
@@ -1879,7 +1879,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"standard_conforming_strings", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"standard_conforming_strings", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Causes '...' strings to treat backslashes literally."),
NULL,
GUC_REPORT
@@ -1890,7 +1890,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"synchronize_seqscans", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"synchronize_seqscans", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Enable synchronized sequential scans."),
NULL
},
@@ -1975,7 +1975,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"quote_all_identifiers", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"quote_all_identifiers", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("When generating SQL fragments, quote all identifiers."),
NULL,
},
@@ -2016,7 +2016,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"parallel_leader_participation", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"parallel_leader_participation", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Controls whether Gather and Gather Merge also run subplans."),
gettext_noop("Should gather nodes also run subplans or just gather tuples?"),
GUC_EXPLAIN
@@ -2027,7 +2027,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"jit", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"jit", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Allow JIT compilation."),
NULL,
GUC_EXPLAIN
@@ -2066,7 +2066,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"jit_expressions", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"jit_expressions", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Allow JIT compilation of expressions."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -2094,7 +2094,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"jit_tuple_deforming", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"jit_tuple_deforming", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Allow JIT compilation of tuple deforming."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -2154,7 +2154,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"default_statistics_target", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"default_statistics_target", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the default statistics target."),
gettext_noop("This applies to table columns that have not had a "
"column-specific target set via ALTER TABLE SET STATISTICS.")
@@ -2164,7 +2164,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"from_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"from_collapse_limit", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the FROM-list size beyond which subqueries "
"are not collapsed."),
gettext_noop("The planner will merge subqueries into upper "
@@ -2177,7 +2177,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"join_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"join_collapse_limit", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the FROM-list size beyond which JOIN "
"constructs are not flattened."),
gettext_noop("The planner will flatten explicit JOIN "
@@ -2190,7 +2190,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_threshold", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_threshold", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("Sets the threshold of FROM items beyond which GEQO is used."),
NULL,
GUC_EXPLAIN
@@ -2200,7 +2200,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_effort", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_effort", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: effort is used to set the default for other GEQO parameters."),
NULL,
GUC_EXPLAIN
@@ -2210,7 +2210,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_pool_size", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_pool_size", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: number of individuals in the population."),
gettext_noop("Zero selects a suitable default value."),
GUC_EXPLAIN
@@ -2220,7 +2220,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_generations", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_generations", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: number of iterations of the algorithm."),
gettext_noop("Zero selects a suitable default value."),
GUC_EXPLAIN
@@ -2367,7 +2367,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"temp_buffers", PGC_USERSET, RESOURCES_MEM,
+ {"temp_buffers", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum number of temporary buffers used by each session."),
NULL,
GUC_UNIT_BLOCKS | GUC_EXPLAIN
@@ -2432,7 +2432,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"work_mem", PGC_USERSET, RESOURCES_MEM,
+ {"work_mem", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum memory to be used for query workspaces."),
gettext_noop("This much memory can be used by each internal "
"sort operation and hash table before switching to "
@@ -2445,7 +2445,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"maintenance_work_mem", PGC_USERSET, RESOURCES_MEM,
+ {"maintenance_work_mem", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum memory to be used for maintenance operations."),
gettext_noop("This includes operations such as VACUUM and CREATE INDEX."),
GUC_UNIT_KB
@@ -2456,7 +2456,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"logical_decoding_work_mem", PGC_USERSET, RESOURCES_MEM,
+ {"logical_decoding_work_mem", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum memory to be used for logical decoding."),
gettext_noop("This much memory can be used by each internal "
"reorder buffer before spilling to disk."),
@@ -2495,7 +2495,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_page_hit", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_page_hit", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost for a page found in the buffer cache."),
NULL
},
@@ -2505,7 +2505,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_page_miss", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_page_miss", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost for a page not found in the buffer cache."),
NULL
},
@@ -2515,7 +2515,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_page_dirty", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_page_dirty", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost for a page dirtied by vacuum."),
NULL
},
@@ -2525,7 +2525,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_limit", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_limit", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost amount available before napping."),
NULL
},
@@ -2591,7 +2591,7 @@ static struct config_int ConfigureNamesInt[] =
#endif
{
- {"statement_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"statement_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed duration of any statement."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2602,7 +2602,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"lock_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"lock_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed duration of any wait for a lock."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2613,7 +2613,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"idle_in_transaction_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"idle_in_transaction_session_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed idle time between queries, when in a transaction."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2624,7 +2624,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"idle_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"idle_session_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed idle time between queries, when not in a transaction."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2635,7 +2635,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_freeze_min_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Minimum age at which VACUUM should freeze a table row."),
NULL
},
@@ -2645,7 +2645,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_freeze_table_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Age at which VACUUM should scan whole table to freeze tuples."),
NULL
},
@@ -2655,7 +2655,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_multixact_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_multixact_freeze_min_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Minimum age at which VACUUM should freeze a MultiXactId in a table row."),
NULL
},
@@ -2665,7 +2665,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_multixact_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_multixact_freeze_table_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Multixact age at which VACUUM should scan whole table to freeze tuples."),
NULL
},
@@ -2684,7 +2684,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"vacuum_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_failsafe_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
NULL
},
@@ -2693,7 +2693,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"vacuum_multixact_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_multixact_failsafe_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Multixact age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
NULL
},
@@ -2881,7 +2881,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"wal_skip_threshold", PGC_USERSET, WAL_SETTINGS,
+ {"wal_skip_threshold", PGC_SUSET, WAL_SETTINGS,
gettext_noop("Minimum size of new file to fsync instead of writing WAL."),
NULL,
GUC_UNIT_KB
@@ -2926,7 +2926,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"wal_sender_timeout", PGC_USERSET, REPLICATION_SENDING,
+ {"wal_sender_timeout", PGC_SUSET, REPLICATION_SENDING,
gettext_noop("Sets the maximum time to wait for WAL replication."),
NULL,
GUC_UNIT_MS
@@ -2949,7 +2949,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"commit_siblings", PGC_USERSET, WAL_SETTINGS,
+ {"commit_siblings", PGC_SUSET, WAL_SETTINGS,
gettext_noop("Sets the minimum number of concurrent open transactions "
"required before performing commit_delay."),
NULL
@@ -2960,7 +2960,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"extra_float_digits", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"extra_float_digits", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the number of digits displayed for floating-point values."),
gettext_noop("This affects real, double precision, and geometric data types. "
"A zero or negative parameter value is added to the standard "
@@ -3022,7 +3022,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"log_parameter_max_length_on_error", PGC_USERSET, LOGGING_WHAT,
+ {"log_parameter_max_length_on_error", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Sets the maximum length in bytes of data logged for bind "
"parameter values when logging statements, on error."),
gettext_noop("-1 to print values in full."),
@@ -3067,7 +3067,7 @@ static struct config_int ConfigureNamesInt[] =
{
{"effective_io_concurrency",
- PGC_USERSET,
+ PGC_SUSET,
RESOURCES_ASYNCHRONOUS,
gettext_noop("Number of simultaneous requests that can be handled efficiently by the disk subsystem."),
NULL,
@@ -3085,7 +3085,7 @@ static struct config_int ConfigureNamesInt[] =
{
{"maintenance_io_concurrency",
- PGC_USERSET,
+ PGC_SUSET,
RESOURCES_ASYNCHRONOUS,
gettext_noop("A variant of effective_io_concurrency that is used for maintenance work."),
NULL,
@@ -3102,7 +3102,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"backend_flush_after", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"backend_flush_after", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Number of pages after which previously performed writes are flushed to disk."),
NULL,
GUC_UNIT_BLOCKS
@@ -3334,7 +3334,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"max_parallel_maintenance_workers", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"max_parallel_maintenance_workers", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Sets the maximum number of parallel processes per maintenance operation."),
NULL
},
@@ -3344,7 +3344,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"max_parallel_workers_per_gather", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"max_parallel_workers_per_gather", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Sets the maximum number of parallel processes per executor node."),
NULL,
GUC_EXPLAIN
@@ -3355,7 +3355,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"max_parallel_workers", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"max_parallel_workers", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Sets the maximum number of parallel workers that can be active at one time."),
NULL,
GUC_EXPLAIN
@@ -3388,7 +3388,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_keepalives_idle", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_keepalives_idle", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Time between issuing TCP keepalives."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_S
@@ -3399,7 +3399,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_keepalives_interval", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_keepalives_interval", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Time between TCP keepalive retransmits."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_S
@@ -3410,7 +3410,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"ssl_renegotiation_limit", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"ssl_renegotiation_limit", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("SSL renegotiation is no longer supported; this can only be 0."),
NULL,
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE,
@@ -3421,7 +3421,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_keepalives_count", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_keepalives_count", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Maximum number of TCP keepalive retransmits."),
gettext_noop("This controls the number of consecutive keepalive retransmits that can be "
"lost before a connection is considered dead. A value of 0 uses the "
@@ -3433,7 +3433,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"gin_fuzzy_search_limit", PGC_USERSET, CLIENT_CONN_OTHER,
+ {"gin_fuzzy_search_limit", PGC_SUSET, CLIENT_CONN_OTHER,
gettext_noop("Sets the maximum allowed result for exact search by GIN."),
NULL,
0
@@ -3444,7 +3444,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"effective_cache_size", PGC_USERSET, QUERY_TUNING_COST,
+ {"effective_cache_size", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's assumption about the total size of the data caches."),
gettext_noop("That is, the total size of the caches (kernel cache and shared buffers) used for PostgreSQL data files. "
"This is measured in disk pages, which are normally 8 kB each."),
@@ -3456,7 +3456,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"min_parallel_table_scan_size", PGC_USERSET, QUERY_TUNING_COST,
+ {"min_parallel_table_scan_size", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the minimum amount of table data for a parallel scan."),
gettext_noop("If the planner estimates that it will read a number of table pages too small to reach this limit, a parallel scan will not be considered."),
GUC_UNIT_BLOCKS | GUC_EXPLAIN,
@@ -3467,7 +3467,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"min_parallel_index_scan_size", PGC_USERSET, QUERY_TUNING_COST,
+ {"min_parallel_index_scan_size", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the minimum amount of index data for a parallel scan."),
gettext_noop("If the planner estimates that it will read a number of index pages too small to reach this limit, a parallel scan will not be considered."),
GUC_UNIT_BLOCKS | GUC_EXPLAIN,
@@ -3512,7 +3512,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"gin_pending_list_limit", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"gin_pending_list_limit", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum size of the pending list for GIN index."),
NULL,
GUC_UNIT_KB
@@ -3523,7 +3523,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_user_timeout", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_user_timeout", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("TCP user timeout."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_MS
@@ -3568,7 +3568,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"client_connection_check_interval", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"client_connection_check_interval", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Sets the time interval between checks for disconnection while running queries."),
NULL,
GUC_UNIT_MS
@@ -3600,7 +3600,7 @@ static struct config_int ConfigureNamesInt[] =
static struct config_real ConfigureNamesReal[] =
{
{
- {"seq_page_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"seq_page_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of a "
"sequentially fetched disk page."),
NULL,
@@ -3611,7 +3611,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"random_page_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"random_page_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of a "
"nonsequentially fetched disk page."),
NULL,
@@ -3622,7 +3622,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"cpu_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"cpu_tuple_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"processing each tuple (row)."),
NULL,
@@ -3633,7 +3633,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"cpu_index_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"cpu_index_tuple_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"processing each index entry during an index scan."),
NULL,
@@ -3644,7 +3644,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"cpu_operator_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"cpu_operator_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"processing each operator or function call."),
NULL,
@@ -3655,7 +3655,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"parallel_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"parallel_tuple_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"passing each tuple (row) from worker to leader backend."),
NULL,
@@ -3666,7 +3666,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"parallel_setup_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"parallel_setup_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"starting up worker processes for parallel query."),
NULL,
@@ -3678,7 +3678,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"jit_above_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"jit_above_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Perform JIT compilation if query is more expensive."),
gettext_noop("-1 disables JIT compilation."),
GUC_EXPLAIN
@@ -3689,7 +3689,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"jit_optimize_above_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"jit_optimize_above_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Optimize JIT-compiled functions if query is more expensive."),
gettext_noop("-1 disables optimization."),
GUC_EXPLAIN
@@ -3700,7 +3700,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"jit_inline_above_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"jit_inline_above_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Perform JIT inlining if query is more expensive."),
gettext_noop("-1 disables inlining."),
GUC_EXPLAIN
@@ -3711,7 +3711,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"cursor_tuple_fraction", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"cursor_tuple_fraction", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the planner's estimate of the fraction of "
"a cursor's rows that will be retrieved."),
NULL,
@@ -3723,7 +3723,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"geqo_selection_bias", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_selection_bias", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: selective pressure within the population."),
NULL,
GUC_EXPLAIN
@@ -3734,7 +3734,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"geqo_seed", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_seed", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: seed for random path selection."),
NULL,
GUC_EXPLAIN
@@ -3745,7 +3745,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"hash_mem_multiplier", PGC_USERSET, RESOURCES_MEM,
+ {"hash_mem_multiplier", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Multiple of work_mem to use for hash tables."),
NULL,
GUC_EXPLAIN
@@ -3766,7 +3766,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"seed", PGC_USERSET, UNGROUPED,
+ {"seed", PGC_SUSET, UNGROUPED,
gettext_noop("Sets the seed for random-number generation."),
NULL,
GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -3777,7 +3777,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"vacuum_cost_delay", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_delay", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost delay in milliseconds."),
NULL,
GUC_UNIT_MS
@@ -4027,7 +4027,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"DateStyle", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"DateStyle", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the display format for date and time values."),
gettext_noop("Also controls interpretation of ambiguous "
"date inputs."),
@@ -4039,7 +4039,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"default_table_access_method", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_table_access_method", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default table access method for new tables."),
NULL,
GUC_IS_NAME
@@ -4050,7 +4050,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"default_tablespace", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_tablespace", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default tablespace to create tables and indexes in."),
gettext_noop("An empty string selects the database's default tablespace."),
GUC_IS_NAME
@@ -4061,7 +4061,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"temp_tablespaces", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE
@@ -4141,7 +4141,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"lc_monetary", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"lc_monetary", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the locale for formatting monetary amounts."),
NULL
},
@@ -4151,7 +4151,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"lc_numeric", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"lc_numeric", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the locale for formatting numbers."),
NULL
},
@@ -4161,7 +4161,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"lc_time", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"lc_time", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the locale for formatting date and time values."),
NULL
},
@@ -4193,7 +4193,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"local_preload_libraries", PGC_USERSET, CLIENT_CONN_PRELOAD,
+ {"local_preload_libraries", PGC_SUSET, CLIENT_CONN_PRELOAD,
gettext_noop("Lists unprivileged shared libraries to preload into each backend."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE
@@ -4204,7 +4204,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"search_path", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"search_path", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the schema search order for names that are not schema-qualified."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE | GUC_EXPLAIN
@@ -4240,7 +4240,7 @@ static struct config_string ConfigureNamesString[] =
{
/* Not for general use --- used by SET ROLE */
- {"role", PGC_USERSET, UNGROUPED,
+ {"role", PGC_SUSET, UNGROUPED,
gettext_noop("Sets the current role."),
NULL,
GUC_IS_NAME | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_NOT_WHILE_SEC_REST
@@ -4319,7 +4319,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"TimeZone", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"TimeZone", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the time zone for displaying and interpreting time stamps."),
NULL,
GUC_REPORT
@@ -4329,7 +4329,7 @@ static struct config_string ConfigureNamesString[] =
check_timezone, assign_timezone, show_timezone
},
{
- {"timezone_abbreviations", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"timezone_abbreviations", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Selects a file of time zone abbreviations."),
NULL
},
@@ -4522,7 +4522,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"default_text_search_config", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"default_text_search_config", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets default text search configuration."),
NULL
},
@@ -4584,7 +4584,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"application_name", PGC_USERSET, LOGGING_WHAT,
+ {"application_name", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Sets the application name to be reported in statistics and logs."),
NULL,
GUC_IS_NAME | GUC_REPORT | GUC_NOT_IN_SAMPLE
@@ -4648,7 +4648,7 @@ static struct config_string ConfigureNamesString[] =
static struct config_enum ConfigureNamesEnum[] =
{
{
- {"backslash_quote", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"backslash_quote", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Sets whether \"\\'\" is allowed in string literals."),
NULL
},
@@ -4658,7 +4658,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"bytea_output", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"bytea_output", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the output format for bytea."),
NULL
},
@@ -4668,7 +4668,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"client_min_messages", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"client_min_messages", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the message levels that are sent to the client."),
gettext_noop("Each level includes all the levels that follow it. The later"
" the level, the fewer messages are sent.")
@@ -4689,7 +4689,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"constraint_exclusion", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"constraint_exclusion", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Enables the planner to use constraints to optimize queries."),
gettext_noop("Table scans will be skipped if their constraints"
" guarantee that no rows match the query."),
@@ -4701,7 +4701,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"default_toast_compression", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_toast_compression", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default compression method for compressible values."),
NULL
},
@@ -4712,7 +4712,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"default_transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_transaction_isolation", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the transaction isolation level of each new transaction."),
NULL
},
@@ -4722,7 +4722,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"transaction_isolation", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the current transaction's isolation level."),
NULL,
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -4733,7 +4733,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"IntervalStyle", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"IntervalStyle", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the display format for interval values."),
NULL,
GUC_REPORT
@@ -4811,7 +4811,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"synchronous_commit", PGC_USERSET, WAL_SETTINGS,
+ {"synchronous_commit", PGC_SUSET, WAL_SETTINGS,
gettext_noop("Sets the current transaction's synchronization level."),
NULL
},
@@ -4918,7 +4918,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"xmlbinary", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"xmlbinary", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets how binary values are to be encoded in XML."),
NULL
},
@@ -4928,7 +4928,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"xmloption", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"xmloption", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets whether XML data in implicit parsing and serialization "
"operations is to be considered as documents or content fragments."),
NULL
@@ -4949,7 +4949,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"force_parallel_mode", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"force_parallel_mode", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Forces use of parallel query facilities."),
gettext_noop("If possible, run query using a parallel worker and with parallel restrictions."),
GUC_NOT_IN_SAMPLE | GUC_EXPLAIN
@@ -4960,7 +4960,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"password_encryption", PGC_USERSET, CONN_AUTH_AUTH,
+ {"password_encryption", PGC_SUSET, CONN_AUTH_AUTH,
gettext_noop("Chooses the algorithm for encrypting passwords."),
NULL
},
@@ -4970,7 +4970,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"plan_cache_mode", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"plan_cache_mode", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Controls the planner's selection of custom or generic plan."),
gettext_noop("Prepared statements can have custom and generic plans, and the planner "
"will attempt to choose which is better. This can be set to override "
@@ -5029,6 +5029,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_setting_acl
+ * entries.
*/
static const char *const map_old_guc_names[] = {
"sort_mem", "work_mem",
@@ -5428,25 +5432,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().)
+ *
+ * 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"
@@ -5463,8 +5471,24 @@ 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;
+}
+
+/*
+ * 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().)
+ */
+static bool
+valid_custom_variable_name(const char *name)
+{
+ int partcnt;
+
+ return (valid_variable_name(name, &partcnt) && partcnt > 1);
}
/*
@@ -7492,6 +7516,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 settingid = get_setting_oid(name, true);
+
+ if (OidIsValid(settingid))
+ {
+ AclResult aclresult;
+
+ aclresult = pg_setting_acl_aclcheck(settingid, GetUserId(),
+ ACL_SET_VALUE);
+
+ if (aclresult == ACLCHECK_OK)
+ break; /* okay */
+ }
+
+ /* No granted privilege */
ereport(elevel,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to set parameter \"%s\"",
@@ -8541,16 +8583,32 @@ 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;
+ Oid settingId;
+
+ settingId = get_setting_oid(name, true);
+ if (!OidIsValid(settingId))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+
+ aclresult = pg_setting_acl_aclcheck(settingId, GetUserId(),
+ ACL_ALTER_SYSTEM);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_SETTING, name);
+ }
+
switch (altersysstmt->setstmt->kind)
{
case VAR_SET_VALUE:
@@ -8743,6 +8801,9 @@ void
ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
{
GucAction action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET;
+ GucContext context;
+ AclResult aclresult;
+ Oid settingId;
/*
* Workers synchronize these parameters at the start of the parallel
@@ -8753,6 +8814,21 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
errmsg("cannot set parameters during a parallel operation")));
+ /*
+ * 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(settingId = get_setting_oid(stmt->name, true)))
+ {
+ aclresult = pg_setting_acl_aclcheck(settingId, GetUserId(),
+ ACL_SET_VALUE);
+ if (aclresult == ACLCHECK_OK)
+ context = PGC_SUSET;
+ }
+
switch (stmt->kind)
{
case VAR_SET_VALUE:
@@ -8761,7 +8837,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);
check_reserved_prefixes(stmt->name);
@@ -8847,7 +8923,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);
@@ -9631,6 +9707,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, false, 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,
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 03b80f9575..fc421a894c 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -163,6 +163,7 @@ static char *features_file;
static char *system_constraints_file;
static char *system_functions_file;
static char *system_views_file;
+static char *setting_privileges_file;
static bool success = false;
static bool made_new_pgdata = false;
static bool found_existing_pgdata = false;
@@ -2441,6 +2442,7 @@ setup_data_file_paths(void)
set_input(&system_constraints_file, "system_constraints.sql");
set_input(&system_functions_file, "system_functions.sql");
set_input(&system_views_file, "system_views.sql");
+ set_input(&setting_privileges_file, "setting_privileges.sql");
if (show_setting || debug)
{
@@ -2469,6 +2471,7 @@ setup_data_file_paths(void)
check_input(system_constraints_file);
check_input(system_functions_file);
check_input(system_views_file);
+ check_input(setting_privileges_file);
}
@@ -2816,6 +2819,8 @@ initialize_data_directory(void)
setup_run_file(cmdfd, system_views_file);
+ setup_run_file(cmdfd, setting_privileges_file);
+
setup_description(cmdfd);
setup_collation(cmdfd);
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index dfc96bc98b..2a44e6ffec 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, SETTING or LARGE OBJECT)
* acls: the ACL string fetched from the database
* baseacls: the initial ACL string for this object; can be
* NULL or empty string to indicate "not available from server"
@@ -539,6 +539,11 @@ do { \
CONVERT_PRIV('U', "USAGE");
else if (strcmp(type, "FOREIGN TABLE") == 0)
CONVERT_PRIV('r', "SELECT");
+ else if (strcmp(type, "SETTING") == 0)
+ {
+ CONVERT_PRIV('s', "SET VALUE");
+ 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 8903a694ae..2b8e70ff43 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -3410,6 +3410,7 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te)
strcmp(type, "SCHEMA") == 0 ||
strcmp(type, "EVENT TRIGGER") == 0 ||
strcmp(type, "FOREIGN DATA WRAPPER") == 0 ||
+ strcmp(type, "SETTING") == 0 ||
strcmp(type, "SERVER") == 0 ||
strcmp(type, "PUBLICATION") == 0 ||
strcmp(type, "SUBSCRIPTION") == 0 ||
@@ -3593,6 +3594,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, "SETTING") == 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 326441a76a..e075f03dbd 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -14147,6 +14147,9 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
case DEFACLOBJ_NAMESPACE:
type = "SCHEMAS";
break;
+ case DEFACLOBJ_SETTING:
+ type = "SETTINGS";
+ break;
default:
/* shouldn't get here */
fatal("unrecognized object type in default privileges: %d",
@@ -14190,7 +14193,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, SETTING 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 9ff0c091a9..2e198ba94d 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);
@@ -581,6 +582,10 @@ main(int argc, char *argv[])
/* Dump role memberships */
dumpRoleMembership(conn);
+
+ /* Dump role guc privileges */
+ if (server_version >= 150000)
+ dumpRoleGUCPrivs(conn);
}
/* Dump tablespaces */
@@ -1019,6 +1024,72 @@ 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)
+{
+ PQExpBuffer buf = createPQExpBuffer();
+ PGresult *res;
+ int i;
+
+ printfPQExpBuffer(buf, "SELECT string_agg(acl.privilege_type, ', ' ORDER BY acl.privilege_type), "
+ "set_acl.setting, "
+ "grantee.rolname AS grantee, "
+ "acl.is_grantable, "
+ "grantor.rolname AS grantor "
+ "FROM pg_catalog.pg_setting_acl set_acl, "
+ "LATERAL (SELECT * FROM aclexplode(set_acl.setacl)) acl "
+ "JOIN pg_catalog.pg_authid grantee "
+ "ON acl.grantee = grantee.oid "
+ "LEFT JOIN pg_catalog.pg_authid grantor ON "
+ "acl.grantor = grantor.oid "
+ "WHERE acl.grantee > 0 "
+ "GROUP BY setting, grantee.rolname, is_grantable, grantor.rolname"
+ );
+
+ res = executeQuery(conn, buf->data);
+
+ if (PQntuples(res) > 0)
+ fprintf(OPF, "--\n-- Role privileges on configuration parameters\n--\n\n");
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ char *privilege = PQgetvalue(res, i, 0);
+ char *setting = PQgetvalue(res, i, 1);
+ char *grantee = PQgetvalue(res, i, 2);
+ char *grantable = PQgetvalue(res, i, 3);
+
+ fprintf(OPF, "GRANT %s", privilege);
+ fprintf(OPF, " ON %s", setting);
+ fprintf(OPF, " TO %s", fmtId(grantee));
+ if (*grantable == 't')
+ fprintf(OPF, " WITH GRANT OPTION");
+
+ /*
+ * We don't track the grantor very carefully in the backend, so cope
+ * with the possibility that it has been dropped.
+ */
+ if (!PQgetisnull(res, i, 4))
+ {
+ char *grantor = PQgetvalue(res, i, 4);
+
+ fprintf(OPF, " GRANTED BY %s", fmtId(grantor));
+ }
+ fprintf(OPF, ";\n");
+ }
+
+ PQclear(res);
+ destroyPQExpBuffer(buf);
+
+ fprintf(OPF, "\n\n");
+}
+
/*
* Dump tablespaces.
*/
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 3eca295ff4..a800f0b544 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -92,6 +92,7 @@ typedef enum ObjectClass
OCLASS_TYPE, /* pg_type */
OCLASS_CAST, /* pg_cast */
OCLASS_COLLATION, /* pg_collation */
+ OCLASS_SETTING, /* pg_setting_acl */
OCLASS_CONSTRAINT, /* pg_constraint */
OCLASS_CONVERSION, /* pg_conversion */
OCLASS_DEFAULT, /* pg_attrdef */
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index eb72dd6293..08a2dcee41 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_SETTING 'c' /* configuration parameter */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 4d992dc224..5443aad4bd 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7196,6 +7196,25 @@
proname => 'has_type_privilege', provolatile => 's', prorettype => 'bool',
proargtypes => 'oid text', prosrc => 'has_type_privilege_id' },
+{ oid => '8050', descr => 'user privilege on setting by username, setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'name text text', prosrc => 'has_setting_privilege_name_name' },
+{ oid => '8051', descr => 'user privilege on setting by username, setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'name oid text', prosrc => 'has_setting_privilege_name_id' },
+{ oid => '8052', descr => 'user privilege on setting by user oid, setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid text text', prosrc => 'has_setting_privilege_id_name' },
+{ oid => '8053', descr => 'user privilege on setting by user oid, setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid oid text', prosrc => 'has_setting_privilege_id_id' },
+{ oid => '8054', descr => 'current user privilege on setting by setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'text text', prosrc => 'has_setting_privilege_name' },
+{ oid => '8055', descr => 'current user privilege on setting by setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid text', prosrc => 'has_setting_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/catalog/pg_setting_acl.h b/src/include/catalog/pg_setting_acl.h
new file mode 100644
index 0000000000..b7dee55e5c
--- /dev/null
+++ b/src/include/catalog/pg_setting_acl.h
@@ -0,0 +1,63 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_setting_acl.h
+ * definition of the "configuration parameter" system catalog
+ * (pg_setting_acl).
+ *
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_setting_acl.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SETTING_ACL_H
+#define PG_SETTING_ACL_H
+
+#include "catalog/genbki.h"
+#include "catalog/pg_setting_acl_d.h"
+
+/* ----------------
+ * pg_setting_acl definition. cpp turns this into
+ * typedef struct FormData_pg_setting_acl
+ * ----------------
+ */
+CATALOG(pg_setting_acl,8924,SettingAclRelationId) BKI_SHARED_RELATION
+{
+ Oid oid; /* oid */
+ /*
+
+ * Variable-length fields start here, but we allow direct access to
+ * setting.
+ */
+ text setting BKI_FORCE_NOT_NULL;
+
+#ifdef CATALOG_VARLEN
+ /* Access privileges */
+ aclitem setacl[1] BKI_DEFAULT(_null_);
+#endif
+} FormData_pg_setting_acl;
+
+
+/* ----------------
+ * Form_pg_setting_acl corresponds to a pointer to a tuple with
+ * the format of pg_setting_acl relation.
+ * ----------------
+ */
+typedef FormData_pg_setting_acl *Form_pg_setting_acl;
+
+DECLARE_TOAST(pg_setting_acl, 8925, 8926);
+#define PgSettingAclToastTable 8925
+#define PgSettingAclToastIndex 8926
+
+DECLARE_UNIQUE_INDEX(pg_setting_acl_setting_index, 8927, SettingAclSettingIndexId, on pg_setting_acl using btree(setting text_ops));
+DECLARE_UNIQUE_INDEX_PKEY(pg_setting_acl_oid_index, 8928, SettingAclOidIndexId, on pg_setting_acl using btree(oid oid_ops));
+
+extern Oid SettingAclCreate(const char *setting, bool if_not_exists);
+
+#endif /* PG_SETTING_ACL_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 4c5a8a39bf..1cd57d5552 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -92,7 +92,9 @@ 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 N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */
+#define ACL_SET_VALUE (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
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
@@ -1794,6 +1796,7 @@ typedef enum ObjectType
OBJECT_CAST,
OBJECT_COLUMN,
OBJECT_COLLATION,
+ OBJECT_SETTING,
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DEFAULT,
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f836acf876..527e723b39 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -307,6 +307,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 af771c901d..70a6d83336 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -146,9 +146,11 @@ 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_ALTER_SYSTEM_CHR 'A'
/* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTc"
+#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcsA"
/*
* Bitmasks defining "all rights" for each supported object type
@@ -165,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_SETTING (ACL_SET_VALUE|ACL_ALTER_SYSTEM)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -223,6 +226,8 @@ extern void select_best_grantor(Oid roleId, AclMode privileges,
extern void initialize_acl(void);
+extern Oid get_setting_oid(const char *setting, bool missing_ok);
+
/*
* prototypes for functions in aclchk.c
*/
@@ -243,6 +248,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_setting_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,
@@ -271,6 +278,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_setting_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 aa18d304ac..fb2fe193c5 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -379,6 +379,8 @@ extern int set_config_option(const char *name, const char *value,
GucAction action, bool changeVal, int elevel,
bool is_reload);
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 77871aaefc..a620eb6dca 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_setting_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 c8cfbc30f6..27cf1c6088 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -89,6 +89,8 @@ enum SysCacheIdentifier
REPLORIGNAME,
RULERELNAME,
SEQRELID,
+ SETTINGNAME,
+ SETTINGOID,
STATEXTDATASTXOID,
STATEXTNAMENSP,
STATEXTOID,
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 16f7610883..f8071eca00 100644
--- a/src/test/modules/test_pg_dump/t/001_base.pl
+++ b/src/test/modules/test_pg_dump/t/001_base.pl
@@ -317,6 +317,53 @@ my %tests = (
like => { pg_dumpall_globals => 1, },
},
+ 'GRANT ALTER SYSTEM ON ignore_checksum_failure' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT ALTER SYSTEM ON ignore_checksum_failure TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM ON ignore_checksum_failure TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT SET VALUE ON my.missing.guc' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET VALUE ON my.missing.guc TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT SET VALUE ON my\.missing\.guc TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT SET VALUE, ALTER SYSTEM ON something WITH GRANT OPTION' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET VALUE, ALTER SYSTEM ON something TO regress_dump_test_role WITH GRANT OPTION;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM, SET VALUE ON something TO regress_dump_test_role WITH GRANT OPTION GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALTER SYSTEM ON MyReallyLong.ButValidCustom.GucNameThatCannotFit.InNamedata64Byte.Format' => {
+ create_order => 2,
+ create_sql =>
+ # configuration parameters get cased folded
+ 'GRANT ALTER SYSTEM ON MyReallyLong.ButValidCustom.GucNameThatCannotFit.InNamedata64Byte.Format TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM ON myreallylong\.butvalidcustom\.gucnamethatcannotfit\.innamedata64byte\.format TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALTER SYSTEM, SET VALUE ON my.guc TO regress_dump_test_role GRANTED BY CURRENT_ROLE' => {
+ create_order => 2,
+ create_sql =>
+ # GRANTED BY CURRENT_ROLE is allowed for SQL compatibility, but is ignored
+ 'GRANT ALTER SYSTEM, SET VALUE ON my.guc TO regress_dump_test_role GRANTED BY CURRENT_ROLE;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM, SET VALUE ON my\.guc TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
'CREATE SCHEMA public' => {
regexp => qr/^CREATE SCHEMA public;/m,
like => {
diff --git a/src/test/regress/expected/guc_privs.out b/src/test/regress/expected/guc_privs.out
new file mode 100644
index 0000000000..c2033e77d1
--- /dev/null
+++ b/src/test/regress/expected/guc_privs.out
@@ -0,0 +1,780 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform all 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;
+-- Check setting privileges prior to creating any
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+ grantee | setting | privilege_type | is_grantable
+---------+-------------------------------------+----------------+--------------
+ | DateStyle | SET VALUE | f
+ | IntervalStyle | SET VALUE | f
+ | TimeZone | SET VALUE | f
+ | application_name | SET VALUE | f
+ | array_nulls | SET VALUE | f
+ | backend_flush_after | SET VALUE | f
+ | backslash_quote | SET VALUE | f
+ | bytea_output | SET VALUE | f
+ | check_function_bodies | SET VALUE | f
+ | client_connection_check_interval | SET VALUE | f
+ | client_min_messages | SET VALUE | f
+ | commit_siblings | SET VALUE | f
+ | constraint_exclusion | SET VALUE | f
+ | cpu_index_tuple_cost | SET VALUE | f
+ | cpu_operator_cost | SET VALUE | f
+ | cpu_tuple_cost | SET VALUE | f
+ | cursor_tuple_fraction | SET VALUE | f
+ | debug_pretty_print | SET VALUE | f
+ | debug_print_parse | SET VALUE | f
+ | debug_print_plan | SET VALUE | f
+ | debug_print_rewritten | SET VALUE | f
+ | default_statistics_target | SET VALUE | f
+ | default_table_access_method | SET VALUE | f
+ | default_tablespace | SET VALUE | f
+ | default_text_search_config | SET VALUE | f
+ | default_toast_compression | SET VALUE | f
+ | default_transaction_deferrable | SET VALUE | f
+ | default_transaction_isolation | SET VALUE | f
+ | default_transaction_read_only | SET VALUE | f
+ | default_with_oids | SET VALUE | f
+ | effective_cache_size | SET VALUE | f
+ | effective_io_concurrency | SET VALUE | f
+ | enable_async_append | SET VALUE | f
+ | enable_bitmapscan | SET VALUE | f
+ | enable_gathermerge | SET VALUE | f
+ | enable_hashagg | SET VALUE | f
+ | enable_hashjoin | SET VALUE | f
+ | enable_incremental_sort | SET VALUE | f
+ | enable_indexonlyscan | SET VALUE | f
+ | enable_indexscan | SET VALUE | f
+ | enable_material | SET VALUE | f
+ | enable_memoize | SET VALUE | f
+ | enable_mergejoin | SET VALUE | f
+ | enable_nestloop | SET VALUE | f
+ | enable_parallel_append | SET VALUE | f
+ | enable_parallel_hash | SET VALUE | f
+ | enable_partition_pruning | SET VALUE | f
+ | enable_partitionwise_aggregate | SET VALUE | f
+ | enable_partitionwise_join | SET VALUE | f
+ | enable_seqscan | SET VALUE | f
+ | enable_sort | SET VALUE | f
+ | enable_tidscan | SET VALUE | f
+ | escape_string_warning | SET VALUE | f
+ | exit_on_error | SET VALUE | f
+ | extra_float_digits | SET VALUE | f
+ | force_parallel_mode | SET VALUE | f
+ | from_collapse_limit | SET VALUE | f
+ | geqo | SET VALUE | f
+ | geqo_effort | SET VALUE | f
+ | geqo_generations | SET VALUE | f
+ | geqo_pool_size | SET VALUE | f
+ | geqo_seed | SET VALUE | f
+ | geqo_selection_bias | SET VALUE | f
+ | geqo_threshold | SET VALUE | f
+ | gin_fuzzy_search_limit | SET VALUE | f
+ | gin_pending_list_limit | SET VALUE | f
+ | hash_mem_multiplier | SET VALUE | f
+ | idle_in_transaction_session_timeout | SET VALUE | f
+ | idle_session_timeout | SET VALUE | f
+ | jit | SET VALUE | f
+ | jit_above_cost | SET VALUE | f
+ | jit_expressions | SET VALUE | f
+ | jit_inline_above_cost | SET VALUE | f
+ | jit_optimize_above_cost | SET VALUE | f
+ | jit_tuple_deforming | SET VALUE | f
+ | join_collapse_limit | SET VALUE | f
+ | lc_monetary | SET VALUE | f
+ | lc_numeric | SET VALUE | f
+ | lc_time | SET VALUE | f
+ | local_preload_libraries | SET VALUE | f
+ | lock_timeout | SET VALUE | f
+ | log_parameter_max_length_on_error | SET VALUE | f
+ | logical_decoding_work_mem | SET VALUE | f
+ | maintenance_io_concurrency | SET VALUE | f
+ | maintenance_work_mem | SET VALUE | f
+ | max_parallel_maintenance_workers | SET VALUE | f
+ | max_parallel_workers | SET VALUE | f
+ | max_parallel_workers_per_gather | SET VALUE | f
+ | min_parallel_index_scan_size | SET VALUE | f
+ | min_parallel_table_scan_size | SET VALUE | f
+ | optimize_bounded_sort | SET VALUE | f
+ | parallel_leader_participation | SET VALUE | f
+ | parallel_setup_cost | SET VALUE | f
+ | parallel_tuple_cost | SET VALUE | f
+ | password_encryption | SET VALUE | f
+ | plan_cache_mode | SET VALUE | f
+ | quote_all_identifiers | SET VALUE | f
+ | random_page_cost | SET VALUE | f
+ | role | SET VALUE | f
+ | row_security | SET VALUE | f
+ | search_path | SET VALUE | f
+ | seed | SET VALUE | f
+ | seq_page_cost | SET VALUE | f
+ | ssl_renegotiation_limit | SET VALUE | f
+ | standard_conforming_strings | SET VALUE | f
+ | statement_timeout | SET VALUE | f
+ | synchronize_seqscans | SET VALUE | f
+ | synchronous_commit | SET VALUE | f
+ | tcp_keepalives_count | SET VALUE | f
+ | tcp_keepalives_idle | SET VALUE | f
+ | tcp_keepalives_interval | SET VALUE | f
+ | tcp_user_timeout | SET VALUE | f
+ | temp_buffers | SET VALUE | f
+ | temp_tablespaces | SET VALUE | f
+ | timezone_abbreviations | SET VALUE | f
+ | trace_notify | SET VALUE | f
+ | trace_sort | SET VALUE | f
+ | trace_syncscan | SET VALUE | f
+ | transaction_deferrable | SET VALUE | f
+ | transaction_isolation | SET VALUE | f
+ | transaction_read_only | SET VALUE | f
+ | transform_null_equals | SET VALUE | f
+ | vacuum_cost_delay | SET VALUE | f
+ | vacuum_cost_limit | SET VALUE | f
+ | vacuum_cost_page_dirty | SET VALUE | f
+ | vacuum_cost_page_hit | SET VALUE | f
+ | vacuum_cost_page_miss | SET VALUE | f
+ | vacuum_failsafe_age | SET VALUE | f
+ | vacuum_freeze_min_age | SET VALUE | f
+ | vacuum_freeze_table_age | SET VALUE | f
+ | vacuum_multixact_failsafe_age | SET VALUE | f
+ | vacuum_multixact_freeze_min_age | SET VALUE | f
+ | vacuum_multixact_freeze_table_age | SET VALUE | f
+ | wal_sender_timeout | SET VALUE | f
+ | wal_skip_threshold | SET VALUE | f
+ | work_mem | SET VALUE | f
+ | xmlbinary | SET VALUE | f
+ | xmloption | SET VALUE | f
+(138 rows)
+
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+-- Check the new role does not yet have privileges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ f
+(1 row)
+
+-- Check inappropriate and nonsense privilege types
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+-- Grant privileges on settings to the new non-superuser role
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Check setting privileges after creating some
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+ grantee | setting | privilege_type | is_grantable
+-----------------------------+-------------------------------------+----------------+--------------
+ regress_host_resource_admin | autovacuum_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | autovacuum_work_mem | SET VALUE | f
+ regress_host_resource_admin | hash_mem_multiplier | ALTER SYSTEM | f
+ regress_host_resource_admin | hash_mem_multiplier | SET VALUE | f
+ regress_host_resource_admin | logical_decoding_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | logical_decoding_work_mem | SET VALUE | f
+ regress_host_resource_admin | maintenance_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | maintenance_work_mem | SET VALUE | f
+ regress_host_resource_admin | max_stack_depth | ALTER SYSTEM | f
+ regress_host_resource_admin | max_stack_depth | SET VALUE | f
+ regress_host_resource_admin | min_dynamic_shared_memory | ALTER SYSTEM | f
+ regress_host_resource_admin | min_dynamic_shared_memory | SET VALUE | f
+ regress_host_resource_admin | shared_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | shared_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_file_limit | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_file_limit | SET VALUE | f
+ regress_host_resource_admin | work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | work_mem | SET VALUE | f
+ | DateStyle | SET VALUE | f
+ | IntervalStyle | SET VALUE | f
+ | TimeZone | SET VALUE | f
+ | application_name | SET VALUE | f
+ | array_nulls | SET VALUE | f
+ | backend_flush_after | SET VALUE | f
+ | backslash_quote | SET VALUE | f
+ | bytea_output | SET VALUE | f
+ | check_function_bodies | SET VALUE | f
+ | client_connection_check_interval | SET VALUE | f
+ | client_min_messages | SET VALUE | f
+ | commit_siblings | SET VALUE | f
+ | constraint_exclusion | SET VALUE | f
+ | cpu_index_tuple_cost | SET VALUE | f
+ | cpu_operator_cost | SET VALUE | f
+ | cpu_tuple_cost | SET VALUE | f
+ | cursor_tuple_fraction | SET VALUE | f
+ | debug_pretty_print | SET VALUE | f
+ | debug_print_parse | SET VALUE | f
+ | debug_print_plan | SET VALUE | f
+ | debug_print_rewritten | SET VALUE | f
+ | default_statistics_target | SET VALUE | f
+ | default_table_access_method | SET VALUE | f
+ | default_tablespace | SET VALUE | f
+ | default_text_search_config | SET VALUE | f
+ | default_toast_compression | SET VALUE | f
+ | default_transaction_deferrable | SET VALUE | f
+ | default_transaction_isolation | SET VALUE | f
+ | default_transaction_read_only | SET VALUE | f
+ | default_with_oids | SET VALUE | f
+ | effective_cache_size | SET VALUE | f
+ | effective_io_concurrency | SET VALUE | f
+ | enable_async_append | SET VALUE | f
+ | enable_bitmapscan | SET VALUE | f
+ | enable_gathermerge | SET VALUE | f
+ | enable_hashagg | SET VALUE | f
+ | enable_hashjoin | SET VALUE | f
+ | enable_incremental_sort | SET VALUE | f
+ | enable_indexonlyscan | SET VALUE | f
+ | enable_indexscan | SET VALUE | f
+ | enable_material | SET VALUE | f
+ | enable_memoize | SET VALUE | f
+ | enable_mergejoin | SET VALUE | f
+ | enable_nestloop | SET VALUE | f
+ | enable_parallel_append | SET VALUE | f
+ | enable_parallel_hash | SET VALUE | f
+ | enable_partition_pruning | SET VALUE | f
+ | enable_partitionwise_aggregate | SET VALUE | f
+ | enable_partitionwise_join | SET VALUE | f
+ | enable_seqscan | SET VALUE | f
+ | enable_sort | SET VALUE | f
+ | enable_tidscan | SET VALUE | f
+ | escape_string_warning | SET VALUE | f
+ | exit_on_error | SET VALUE | f
+ | extra_float_digits | SET VALUE | f
+ | force_parallel_mode | SET VALUE | f
+ | from_collapse_limit | SET VALUE | f
+ | geqo | SET VALUE | f
+ | geqo_effort | SET VALUE | f
+ | geqo_generations | SET VALUE | f
+ | geqo_pool_size | SET VALUE | f
+ | geqo_seed | SET VALUE | f
+ | geqo_selection_bias | SET VALUE | f
+ | geqo_threshold | SET VALUE | f
+ | gin_fuzzy_search_limit | SET VALUE | f
+ | gin_pending_list_limit | SET VALUE | f
+ | hash_mem_multiplier | SET VALUE | f
+ | idle_in_transaction_session_timeout | SET VALUE | f
+ | idle_session_timeout | SET VALUE | f
+ | jit | SET VALUE | f
+ | jit_above_cost | SET VALUE | f
+ | jit_expressions | SET VALUE | f
+ | jit_inline_above_cost | SET VALUE | f
+ | jit_optimize_above_cost | SET VALUE | f
+ | jit_tuple_deforming | SET VALUE | f
+ | join_collapse_limit | SET VALUE | f
+ | lc_monetary | SET VALUE | f
+ | lc_numeric | SET VALUE | f
+ | lc_time | SET VALUE | f
+ | local_preload_libraries | SET VALUE | f
+ | lock_timeout | SET VALUE | f
+ | log_parameter_max_length_on_error | SET VALUE | f
+ | logical_decoding_work_mem | SET VALUE | f
+ | maintenance_io_concurrency | SET VALUE | f
+ | maintenance_work_mem | SET VALUE | f
+ | max_parallel_maintenance_workers | SET VALUE | f
+ | max_parallel_workers | SET VALUE | f
+ | max_parallel_workers_per_gather | SET VALUE | f
+ | min_parallel_index_scan_size | SET VALUE | f
+ | min_parallel_table_scan_size | SET VALUE | f
+ | optimize_bounded_sort | SET VALUE | f
+ | parallel_leader_participation | SET VALUE | f
+ | parallel_setup_cost | SET VALUE | f
+ | parallel_tuple_cost | SET VALUE | f
+ | password_encryption | SET VALUE | f
+ | plan_cache_mode | SET VALUE | f
+ | quote_all_identifiers | SET VALUE | f
+ | random_page_cost | SET VALUE | f
+ | role | SET VALUE | f
+ | row_security | SET VALUE | f
+ | search_path | SET VALUE | f
+ | seed | SET VALUE | f
+ | seq_page_cost | SET VALUE | f
+ | ssl_renegotiation_limit | SET VALUE | f
+ | standard_conforming_strings | SET VALUE | f
+ | statement_timeout | SET VALUE | f
+ | synchronize_seqscans | SET VALUE | f
+ | synchronous_commit | SET VALUE | f
+ | tcp_keepalives_count | SET VALUE | f
+ | tcp_keepalives_idle | SET VALUE | f
+ | tcp_keepalives_interval | SET VALUE | f
+ | tcp_user_timeout | SET VALUE | f
+ | temp_buffers | SET VALUE | f
+ | temp_tablespaces | SET VALUE | f
+ | timezone_abbreviations | SET VALUE | f
+ | trace_notify | SET VALUE | f
+ | trace_sort | SET VALUE | f
+ | trace_syncscan | SET VALUE | f
+ | transaction_deferrable | SET VALUE | f
+ | transaction_isolation | SET VALUE | f
+ | transaction_read_only | SET VALUE | f
+ | transform_null_equals | SET VALUE | f
+ | vacuum_cost_delay | SET VALUE | f
+ | vacuum_cost_limit | SET VALUE | f
+ | vacuum_cost_page_dirty | SET VALUE | f
+ | vacuum_cost_page_hit | SET VALUE | f
+ | vacuum_cost_page_miss | SET VALUE | f
+ | vacuum_failsafe_age | SET VALUE | f
+ | vacuum_freeze_min_age | SET VALUE | f
+ | vacuum_freeze_table_age | SET VALUE | f
+ | vacuum_multixact_failsafe_age | SET VALUE | f
+ | vacuum_multixact_freeze_min_age | SET VALUE | f
+ | vacuum_multixact_freeze_table_age | SET VALUE | f
+ | wal_sender_timeout | SET VALUE | f
+ | wal_skip_threshold | SET VALUE | f
+ | work_mem | SET VALUE | f
+ | xmlbinary | SET VALUE | f
+ | xmloption | SET VALUE | f
+(158 rows)
+
+-- Check the new role now has privilges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE WITH GRANT OPTION, ALTER SYSTEM WITH GRANT OPTION');
+ has_setting_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_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+SELECT has_setting_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_setting_privilege('regress_host_resource_admin',
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('hash_mem_multiplier', 'set value');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'work_mem'),
+ 'alter system with grant option');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+-- Check setting privileges show up in view
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+ grantee | setting | privilege_type | is_grantable
+-----------------------------+-------------------------------------+----------------+--------------
+ regress_host_resource_admin | autovacuum_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | autovacuum_work_mem | SET VALUE | f
+ regress_host_resource_admin | hash_mem_multiplier | ALTER SYSTEM | f
+ regress_host_resource_admin | hash_mem_multiplier | SET VALUE | f
+ regress_host_resource_admin | logical_decoding_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | logical_decoding_work_mem | SET VALUE | f
+ regress_host_resource_admin | maintenance_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | maintenance_work_mem | SET VALUE | f
+ regress_host_resource_admin | max_stack_depth | ALTER SYSTEM | f
+ regress_host_resource_admin | max_stack_depth | SET VALUE | f
+ regress_host_resource_admin | min_dynamic_shared_memory | ALTER SYSTEM | f
+ regress_host_resource_admin | min_dynamic_shared_memory | SET VALUE | f
+ regress_host_resource_admin | shared_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | shared_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_file_limit | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_file_limit | SET VALUE | f
+ regress_host_resource_admin | work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | work_mem | SET VALUE | f
+ | DateStyle | SET VALUE | f
+ | IntervalStyle | SET VALUE | f
+ | TimeZone | SET VALUE | f
+ | application_name | SET VALUE | f
+ | array_nulls | SET VALUE | f
+ | backend_flush_after | SET VALUE | f
+ | backslash_quote | SET VALUE | f
+ | bytea_output | SET VALUE | f
+ | check_function_bodies | SET VALUE | f
+ | client_connection_check_interval | SET VALUE | f
+ | client_min_messages | SET VALUE | f
+ | commit_siblings | SET VALUE | f
+ | constraint_exclusion | SET VALUE | f
+ | cpu_index_tuple_cost | SET VALUE | f
+ | cpu_operator_cost | SET VALUE | f
+ | cpu_tuple_cost | SET VALUE | f
+ | cursor_tuple_fraction | SET VALUE | f
+ | debug_pretty_print | SET VALUE | f
+ | debug_print_parse | SET VALUE | f
+ | debug_print_plan | SET VALUE | f
+ | debug_print_rewritten | SET VALUE | f
+ | default_statistics_target | SET VALUE | f
+ | default_table_access_method | SET VALUE | f
+ | default_tablespace | SET VALUE | f
+ | default_text_search_config | SET VALUE | f
+ | default_toast_compression | SET VALUE | f
+ | default_transaction_deferrable | SET VALUE | f
+ | default_transaction_isolation | SET VALUE | f
+ | default_transaction_read_only | SET VALUE | f
+ | default_with_oids | SET VALUE | f
+ | effective_cache_size | SET VALUE | f
+ | effective_io_concurrency | SET VALUE | f
+ | enable_async_append | SET VALUE | f
+ | enable_bitmapscan | SET VALUE | f
+ | enable_gathermerge | SET VALUE | f
+ | enable_hashagg | SET VALUE | f
+ | enable_hashjoin | SET VALUE | f
+ | enable_incremental_sort | SET VALUE | f
+ | enable_indexonlyscan | SET VALUE | f
+ | enable_indexscan | SET VALUE | f
+ | enable_material | SET VALUE | f
+ | enable_memoize | SET VALUE | f
+ | enable_mergejoin | SET VALUE | f
+ | enable_nestloop | SET VALUE | f
+ | enable_parallel_append | SET VALUE | f
+ | enable_parallel_hash | SET VALUE | f
+ | enable_partition_pruning | SET VALUE | f
+ | enable_partitionwise_aggregate | SET VALUE | f
+ | enable_partitionwise_join | SET VALUE | f
+ | enable_seqscan | SET VALUE | f
+ | enable_sort | SET VALUE | f
+ | enable_tidscan | SET VALUE | f
+ | escape_string_warning | SET VALUE | f
+ | exit_on_error | SET VALUE | f
+ | extra_float_digits | SET VALUE | f
+ | force_parallel_mode | SET VALUE | f
+ | from_collapse_limit | SET VALUE | f
+ | geqo | SET VALUE | f
+ | geqo_effort | SET VALUE | f
+ | geqo_generations | SET VALUE | f
+ | geqo_pool_size | SET VALUE | f
+ | geqo_seed | SET VALUE | f
+ | geqo_selection_bias | SET VALUE | f
+ | geqo_threshold | SET VALUE | f
+ | gin_fuzzy_search_limit | SET VALUE | f
+ | gin_pending_list_limit | SET VALUE | f
+ | hash_mem_multiplier | SET VALUE | f
+ | idle_in_transaction_session_timeout | SET VALUE | f
+ | idle_session_timeout | SET VALUE | f
+ | jit | SET VALUE | f
+ | jit_above_cost | SET VALUE | f
+ | jit_expressions | SET VALUE | f
+ | jit_inline_above_cost | SET VALUE | f
+ | jit_optimize_above_cost | SET VALUE | f
+ | jit_tuple_deforming | SET VALUE | f
+ | join_collapse_limit | SET VALUE | f
+ | lc_monetary | SET VALUE | f
+ | lc_numeric | SET VALUE | f
+ | lc_time | SET VALUE | f
+ | local_preload_libraries | SET VALUE | f
+ | lock_timeout | SET VALUE | f
+ | log_parameter_max_length_on_error | SET VALUE | f
+ | logical_decoding_work_mem | SET VALUE | f
+ | maintenance_io_concurrency | SET VALUE | f
+ | maintenance_work_mem | SET VALUE | f
+ | max_parallel_maintenance_workers | SET VALUE | f
+ | max_parallel_workers | SET VALUE | f
+ | max_parallel_workers_per_gather | SET VALUE | f
+ | min_parallel_index_scan_size | SET VALUE | f
+ | min_parallel_table_scan_size | SET VALUE | f
+ | optimize_bounded_sort | SET VALUE | f
+ | parallel_leader_participation | SET VALUE | f
+ | parallel_setup_cost | SET VALUE | f
+ | parallel_tuple_cost | SET VALUE | f
+ | password_encryption | SET VALUE | f
+ | plan_cache_mode | SET VALUE | f
+ | quote_all_identifiers | SET VALUE | f
+ | random_page_cost | SET VALUE | f
+ | role | SET VALUE | f
+ | row_security | SET VALUE | f
+ | search_path | SET VALUE | f
+ | seed | SET VALUE | f
+ | seq_page_cost | SET VALUE | f
+ | ssl_renegotiation_limit | SET VALUE | f
+ | standard_conforming_strings | SET VALUE | f
+ | statement_timeout | SET VALUE | f
+ | synchronize_seqscans | SET VALUE | f
+ | synchronous_commit | SET VALUE | f
+ | tcp_keepalives_count | SET VALUE | f
+ | tcp_keepalives_idle | SET VALUE | f
+ | tcp_keepalives_interval | SET VALUE | f
+ | tcp_user_timeout | SET VALUE | f
+ | temp_buffers | SET VALUE | f
+ | temp_tablespaces | SET VALUE | f
+ | timezone_abbreviations | SET VALUE | f
+ | trace_notify | SET VALUE | f
+ | trace_sort | SET VALUE | f
+ | trace_syncscan | SET VALUE | f
+ | transaction_deferrable | SET VALUE | f
+ | transaction_isolation | SET VALUE | f
+ | transaction_read_only | SET VALUE | f
+ | transform_null_equals | SET VALUE | f
+ | vacuum_cost_delay | SET VALUE | f
+ | vacuum_cost_limit | SET VALUE | f
+ | vacuum_cost_page_dirty | SET VALUE | f
+ | vacuum_cost_page_hit | SET VALUE | f
+ | vacuum_cost_page_miss | SET VALUE | f
+ | vacuum_failsafe_age | SET VALUE | f
+ | vacuum_freeze_min_age | SET VALUE | f
+ | vacuum_freeze_table_age | SET VALUE | f
+ | vacuum_multixact_failsafe_age | SET VALUE | f
+ | vacuum_multixact_freeze_min_age | SET VALUE | f
+ | vacuum_multixact_freeze_table_age | SET VALUE | f
+ | wal_sender_timeout | SET VALUE | f
+ | wal_skip_threshold | SET VALUE | f
+ | work_mem | SET VALUE | f
+ | xmlbinary | SET VALUE | f
+ | xmloption | SET VALUE | f
+(158 rows)
+
+-- 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
+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
+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 setting temp_buffers
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting logical_decoding_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting autovacuum_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting temp_file_limit
+-- Use "revoke" to remove the privileges and allow the role to be dropped
+REVOKE SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 setting temp_buffers
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting logical_decoding_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting autovacuum_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting 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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 setting temp_buffers
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting logical_decoding_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting autovacuum_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting 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
+-- Create non-superuser with privileges to configure plgsql custom variables
+CREATE ROLE regress_plpgsql_admin NOSUPERUSER;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin;
+SET plpgsql.extra_errors TO 'all';
+SET plpgsql.extra_warnings TO 'all';
+RESET plpgsql.extra_errors;
+RESET plpgsql.extra_warnings;
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all';
+ERROR: permission denied to set parameter "plpgsql.extra_errors"
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all';
+ERROR: permission denied to set parameter "plpgsql.extra_warnings"
+ALTER SYSTEM RESET plpgsql.extra_errors;
+ERROR: permission denied to set parameter "plpgsql.extra_errors"
+ALTER SYSTEM RESET plpgsql.extra_warnings;
+ERROR: permission denied to set parameter "plpgsql.extra_warnings"
+SET SESSION AUTHORIZATION regress_admin;
+GRANT SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+TO regress_plpgsql_admin;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin; -- ok
+SET plpgsql.extra_errors TO 'all'; -- ok
+SET plpgsql.extra_warnings TO 'all'; -- ok
+RESET plpgsql.extra_errors; -- ok
+RESET plpgsql.extra_warnings; -- ok
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all'; -- ok
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all'; -- ok
+ALTER SYSTEM RESET plpgsql.extra_errors; -- ok
+ALTER SYSTEM RESET plpgsql.extra_warnings; -- ok
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_plpgsql_admin; -- fail, privileges remain
+ERROR: role "regress_plpgsql_admin" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting plpgsql.extra_warnings
+privileges for setting plpgsql.extra_errors
+REVOKE SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+FROM regress_plpgsql_admin;
+DROP ROLE regress_plpgsql_admin; -- ok
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 0bc79be03d..6c8afdba57 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -54,6 +54,33 @@ REVOKE pg_read_all_settings FROM regress_priv_user8;
DROP USER regress_priv_user10;
DROP USER regress_priv_user9;
DROP USER regress_priv_user8;
+GRANT SET VALUE ON enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON enable_nestloop TO regress_priv_user6;
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+GRANT ALTER SYSTEM ON enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON "" TO regress_priv_user7; -- bad name
+ERROR: zero-length delimited identifier at or near """"
+LINE 1: GRANT ALTER SYSTEM ON "" TO regress_priv_user7;
+ ^
+GRANT ALTER SYSTEM ON " " TO regress_priv_user7; -- bad name
+ERROR: invalid setting name " "
+GRANT ALTER SYSTEM ON " foo " TO regress_priv_user7; -- bad name
+ERROR: invalid setting name " foo "
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
ALTER GROUP regress_priv_group1 ADD USER regress_priv_user4;
@@ -2357,10 +2384,32 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+ERROR: role "regress_priv_user6" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting enable_memoize
+privileges for setting enable_nestloop
+DROP USER regress_priv_user7; -- privileges remain
+ERROR: role "regress_priv_user7" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting enable_seqscan
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for table persons2
+privileges for setting no_such_param
+privileges for setting no_such_extension.no_such_param
+privileges for setting no_such_extension.no_such_param.longer.than.maximum.namedata.length
DROP USER regress_priv_user8; -- does not exist
ERROR: role "regress_priv_user8" does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+DROP USER regress_priv_user7; -- ok
+REVOKE SET VALUE ON enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON enable_nestloop FROM regress_priv_user6;
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
CREATE TABLE lock_table (a int);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index b58b062b10..e262035269 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1708,6 +1708,19 @@ pg_sequences| SELECT n.nspname AS schemaname,
JOIN pg_class c ON ((c.oid = s.seqrelid)))
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char"));
+pg_setting_privileges| SELECT grantor.rolname AS grantor,
+ grantee.rolname AS grantee,
+ set_acl.setting,
+ acl.privilege_type,
+ acl.is_grantable
+ FROM pg_setting_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_settings| SELECT a.name,
a.setting,
a.unit,
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 63706a28cc..4a8208c789 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -147,6 +147,7 @@ pg_replication_origin|t
pg_rewrite|t
pg_seclabel|t
pg_sequence|t
+pg_setting_acl|t
pg_shdepend|t
pg_shdescription|t
pg_shseclabel|t
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 017e962fed..b45a203eb6 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -86,7 +86,7 @@ test: brin_bloom brin_multi
# ----------
# Another group of parallel tests
# ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort guc_privs
# rules cannot run concurrently with any test that creates
# a view or rule in the public schema
diff --git a/src/test/regress/sql/guc_privs.sql b/src/test/regress/sql/guc_privs.sql
new file mode 100644
index 0000000000..076c136563
--- /dev/null
+++ b/src/test/regress/sql/guc_privs.sql
@@ -0,0 +1,185 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform all 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;
+-- Check setting privileges prior to creating any
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+-- Check the new role does not yet have privileges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+-- Check inappropriate and nonsense privilege types
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+-- Grant privileges on settings to the new non-superuser role
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Check setting privileges after creating some
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+-- Check the new role now has privilges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE 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_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER WITH GRANT OPTION');
+-- Check other function signatures
+SELECT has_setting_privilege('regress_host_resource_admin',
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET VALUE');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+SELECT has_setting_privilege('hash_mem_multiplier', 'set value');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'work_mem'),
+ 'alter system with grant option');
+-- Check setting privileges show up in view
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+-- 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
+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
+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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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
+-- Create non-superuser with privileges to configure plgsql custom variables
+CREATE ROLE regress_plpgsql_admin NOSUPERUSER;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin;
+SET plpgsql.extra_errors TO 'all';
+SET plpgsql.extra_warnings TO 'all';
+RESET plpgsql.extra_errors;
+RESET plpgsql.extra_warnings;
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all';
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all';
+ALTER SYSTEM RESET plpgsql.extra_errors;
+ALTER SYSTEM RESET plpgsql.extra_warnings;
+SET SESSION AUTHORIZATION regress_admin;
+GRANT SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+TO regress_plpgsql_admin;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin; -- ok
+SET plpgsql.extra_errors TO 'all'; -- ok
+SET plpgsql.extra_warnings TO 'all'; -- ok
+RESET plpgsql.extra_errors; -- ok
+RESET plpgsql.extra_warnings; -- ok
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all'; -- ok
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all'; -- ok
+ALTER SYSTEM RESET plpgsql.extra_errors; -- ok
+ALTER SYSTEM RESET plpgsql.extra_warnings; -- ok
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_plpgsql_admin; -- fail, privileges remain
+REVOKE SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+FROM regress_plpgsql_admin;
+DROP ROLE regress_plpgsql_admin; -- ok
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index c8c545b64c..ddbf6afa44 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -66,6 +66,33 @@ DROP USER regress_priv_user10;
DROP USER regress_priv_user9;
DROP USER regress_priv_user8;
+GRANT SET VALUE ON enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON enable_nestloop TO regress_priv_user6;
+
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+
+GRANT ALTER SYSTEM ON enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON "" TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON " " TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON " foo " TO regress_priv_user7; -- bad name
+
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
+
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
@@ -1426,10 +1453,23 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+DROP USER regress_priv_user7; -- privileges remain
DROP USER regress_priv_user8; -- does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+DROP USER regress_priv_user7; -- ok
+
+REVOKE SET VALUE ON enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON enable_nestloop FROM regress_priv_user6;
+
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
--
2.21.1 (Apple Git-122.3)
On Wed, Dec 15, 2021 at 10:56 AM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:
On Dec 14, 2021, at 2:26 PM, Joshua Brindle <joshua.brindle@crunchydata.com> wrote:
currently there is a failure in check-world (not sure if it's known):
That one is definitely my fault. 'en_US.UTF-8' exists on my platform, so I hadn't noticed. I've changed it to use 'C', which should be portable.
One thing that seems like an omission to me is the absence of a
InvokeObjectPostAlterHook in pg_setting_acl_aclcheck or
pg_setting_acl_aclmask so that MAC extensions can also block this,
InvokeObjectPostCreateHook is already in the create path so a
PostAlter hook seems appropriate.Good catch, but that seems like a strange place to put a PostAlterHook, so I added it to ExecGrant_Setting for v6, instead. This seems more consistent with the hook in SetDefaultACL.
Ah, I was actually requesting a hook where the acl check was done for
setting a GUC, such that we could deny setting them in a hook,
something that would be useful for the set_user extension
(github.com/pgaudit/set_user) but having a hook for grant/revoke is
also helpful.
(If you are really trying to do Managed Access Control (MAC), wouldn't that be a separate patch which adds security hooks into all *_aclcheck functions?)
MAC is mandatory access controls, so something that can be layered on
top of DAC and can only be enforced even on object owners and
superuser. sepgsql is a MAC extension, for example. It uses the object
access hooks to enforce SELinux policy on PG objects.
On Dec 15, 2021, at 10:02 AM, Joshua Brindle <joshua.brindle@crunchydata.com> wrote:
Ah, I was actually requesting a hook where the acl check was done for
setting a GUC, such that we could deny setting them in a hook,
something that would be useful for the set_user extension
(github.com/pgaudit/set_user)
Hmm, this seems orthogonal to the patch under discussion. This patch only adds a pg_setting_acl_aclcheck in ExecSetVariableStmt() for settings which have been explicitly granted, otherwise it works the traditional way (checking whether the setting is suset/userset). I don't think you'd get MAC support without finding a way to fire the hook for all settings, regardless of their presence in the new pg_setting_acl table. That is hard, because InvokeObjectPostAlterHook expects the classId (SettingAclRelationId) and the objectId (pg_setting_acl.oid), but you don't have those for many (most?) settings. As discussed upthread, we *do not* want to force an entry into the table for all settings, only for ones that have been explicitly granted.
Do you agree? I'm happy to support MAC in this patch if can explain a simple way of doing so.
but having a hook for grant/revoke is
also helpful.
Yes, I see no reason to rip this out.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Wed, Dec 15, 2021 at 1:18 PM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:
On Dec 15, 2021, at 10:02 AM, Joshua Brindle <joshua.brindle@crunchydata.com> wrote:
Ah, I was actually requesting a hook where the acl check was done for
setting a GUC, such that we could deny setting them in a hook,
something that would be useful for the set_user extension
(github.com/pgaudit/set_user)Hmm, this seems orthogonal to the patch under discussion. This patch only adds a pg_setting_acl_aclcheck in ExecSetVariableStmt() for settings which have been explicitly granted, otherwise it works the traditional way (checking whether the setting is suset/userset). I don't think you'd get MAC support without finding a way to fire the hook for all settings, regardless of their presence in the new pg_setting_acl table. That is hard, because InvokeObjectPostAlterHook expects the classId (SettingAclRelationId) and the objectId (pg_setting_acl.oid), but you don't have those for many (most?) settings. As discussed upthread, we *do not* want to force an entry into the table for all settings, only for ones that have been explicitly granted.
Do you agree? I'm happy to support MAC in this patch if can explain a simple way of doing so.
Ah, I understand now. Would it be possible to pass the
SettingAclRelationId if it exists or InvalidOid if not? That way if a
MAC implementation cares about a particular GUC it'll ensure it's in
pg_setting_acl.
I don't know if others will object to that but it seems like an okay
compromise.
Show quoted text
but having a hook for grant/revoke is
also helpful.Yes, I see no reason to rip this out.
On Dec 16, 2021, at 7:43 AM, Joshua Brindle <joshua.brindle@crunchydata.com> wrote:
Ah, I understand now. Would it be possible to pass the
SettingAclRelationId if it exists or InvalidOid if not?
SettingAclRelationId is always defined, so we can always pass that value. But the settingId itself may sometimes be InvalidOid.
That way if a
MAC implementation cares about a particular GUC it'll ensure it's in
pg_setting_acl.
A much cleaner solution would be to create new ObjectAccessTypes with a corresponding new Invoke macro and Run function. Those could take setting names, not Oids, and include additional information about whether the operation is SET, RESET or ALTER SYSTEM, what the new value is (if any), what kind of setting it is (bool, int, ...), etc. I don't think such a patch would even be all that hard to write.
What do you think?
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Thu, Dec 16, 2021 at 12:53 PM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:
On Dec 16, 2021, at 7:43 AM, Joshua Brindle <joshua.brindle@crunchydata.com> wrote:
Ah, I understand now. Would it be possible to pass the
SettingAclRelationId if it exists or InvalidOid if not?SettingAclRelationId is always defined, so we can always pass that value. But the settingId itself may sometimes be InvalidOid.
Yes, that is what I meant.
That way if a
MAC implementation cares about a particular GUC it'll ensure it's in
pg_setting_acl.A much cleaner solution would be to create new ObjectAccessTypes with a corresponding new Invoke macro and Run function. Those could take setting names, not Oids, and include additional information about whether the operation is SET, RESET or ALTER SYSTEM, what the new value is (if any), what kind of setting it is (bool, int, ...), etc. I don't think such a patch would even be all that hard to write.
What do you think?
Personally, I would be happy with that, but since it's a whole new
hooking method I suspect it'll be an uphill battle. That definitely
seems like another patchset though, if you do submit this I will test
and review.
Thank you.
On Dec 15, 2021, at 7:56 AM, Mark Dilger <mark.dilger@enterprisedb.com> wrote:
<v6-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-SET-for-gucs.patch>
Rebased:
Attachments:
v7-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-SET-for-gucs.patchapplication/octet-stream; name=v7-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-SET-for-gucs.patch; x-unix-mode=0644Download
From 0b0b39fed53febfcdd86f006f3b3b8dccad1c50a Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Wed, 26 Jan 2022 07:19:37 -0800
Subject: [PATCH v7] Allow GRANT of SET and ALTER SYSTEM SET for gucs
Allow granting of privilege to set or alter system set variables
which otherwise can only be managed by superusers. Each
(role,variable,privilege) triple is independently grantable, so a
user may be granted privilege to SET but not to ALTER SYSTEM SET on
a variable, or vice versa. The privilege to SET a userset variable
may be granted, though doing so has no practical effect, since any
role can set userset variables anyway. Worse, there is no way to
revoke the privilege to SET a userset variable. To remedy that,
most core userset variables have been changed to suset, with
explicit grants to set the variable to public.
---
contrib/pg_trgm/Makefile | 6 +-
contrib/pg_trgm/pg_trgm--1.6--1.7.sql | 8 +
contrib/pg_trgm/pg_trgm.control | 2 +-
contrib/postgres_fdw/Makefile | 3 +-
contrib/postgres_fdw/option.c | 2 +-
.../postgres_fdw/postgres_fdw--1.1--1.2.sql | 6 +
contrib/postgres_fdw/postgres_fdw.control | 2 +-
contrib/sepgsql/hooks.c | 2 +-
contrib/sepgsql/sepgsql.sql.in | 1 +
doc/src/sgml/catalogs.sgml | 75 +-
doc/src/sgml/ddl.sgml | 41 +-
doc/src/sgml/func.sgml | 20 +-
doc/src/sgml/ref/grant.sgml | 7 +
doc/src/sgml/ref/set.sgml | 5 +-
src/backend/catalog/Makefile | 6 +-
src/backend/catalog/aclchk.c | 254 ++++++
src/backend/catalog/catalog.c | 6 +
src/backend/catalog/dependency.c | 6 +
src/backend/catalog/objectaddress.c | 46 ++
src/backend/catalog/pg_setting_acl.c | 122 +++
src/backend/catalog/setting_privileges.sql | 62 ++
src/backend/catalog/system_views.sql | 13 +
src/backend/commands/alter.c | 1 +
src/backend/commands/dropcmds.c | 7 +
src/backend/commands/event_trigger.c | 6 +
src/backend/commands/seclabel.c | 1 +
src/backend/commands/tablecmds.c | 1 +
src/backend/parser/gram.y | 99 ++-
src/backend/utils/adt/acl.c | 258 ++++++
src/backend/utils/cache/lsyscache.c | 20 +
src/backend/utils/cache/syscache.c | 23 +
src/backend/utils/misc/guc.c | 402 +++++----
src/bin/initdb/initdb.c | 5 +
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 | 71 ++
src/include/catalog/dependency.h | 1 +
src/include/catalog/pg_default_acl.h | 1 +
src/include/catalog/pg_proc.dat | 19 +
src/include/catalog/pg_setting_acl.h | 63 ++
src/include/nodes/parsenodes.h | 5 +-
src/include/parser/kwlist.h | 1 +
src/include/utils/acl.h | 10 +-
src/include/utils/guc.h | 2 +
src/include/utils/lsyscache.h | 1 +
src/include/utils/syscache.h | 2 +
src/test/modules/test_pg_dump/t/001_base.pl | 47 ++
src/test/regress/expected/guc_privs.out | 780 ++++++++++++++++++
src/test/regress/expected/privileges.out | 53 +-
src/test/regress/expected/rules.out | 13 +
src/test/regress/expected/sanity_check.out | 1 +
src/test/regress/parallel_schedule | 2 +-
src/test/regress/sql/guc_privs.sql | 185 +++++
src/test/regress/sql/privileges.sql | 44 +-
55 files changed, 2644 insertions(+), 189 deletions(-)
create mode 100644 contrib/pg_trgm/pg_trgm--1.6--1.7.sql
create mode 100644 contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql
create mode 100644 src/backend/catalog/pg_setting_acl.c
create mode 100644 src/backend/catalog/setting_privileges.sql
create mode 100644 src/include/catalog/pg_setting_acl.h
create mode 100644 src/test/regress/expected/guc_privs.out
create mode 100644 src/test/regress/sql/guc_privs.sql
diff --git a/contrib/pg_trgm/Makefile b/contrib/pg_trgm/Makefile
index 1fbdc9ec1e..8fac4f6289 100644
--- a/contrib/pg_trgm/Makefile
+++ b/contrib/pg_trgm/Makefile
@@ -9,9 +9,9 @@ OBJS = \
trgm_regexp.o
EXTENSION = pg_trgm
-DATA = pg_trgm--1.5--1.6.sql pg_trgm--1.4--1.5.sql pg_trgm--1.3--1.4.sql \
- pg_trgm--1.3.sql pg_trgm--1.2--1.3.sql pg_trgm--1.1--1.2.sql \
- pg_trgm--1.0--1.1.sql
+DATA = pg_trgm--1.6--1.7.sql pg_trgm--1.5--1.6.sql pg_trgm--1.4--1.5.sql \
+ pg_trgm--1.3--1.4.sql pg_trgm--1.3.sql pg_trgm--1.2--1.3.sql \
+ pg_trgm--1.1--1.2.sql pg_trgm--1.0--1.1.sql
PGFILEDESC = "pg_trgm - trigram matching"
REGRESS = pg_trgm pg_word_trgm pg_strict_word_trgm
diff --git a/contrib/pg_trgm/pg_trgm--1.6--1.7.sql b/contrib/pg_trgm/pg_trgm--1.6--1.7.sql
new file mode 100644
index 0000000000..06b783acbd
--- /dev/null
+++ b/contrib/pg_trgm/pg_trgm--1.6--1.7.sql
@@ -0,0 +1,8 @@
+/* contrib/pg_trgm/pg_trgm--1.6--1.7.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_trgm UPDATE TO '1.7'" to load this file. \quit
+
+GRANT SET VALUE ON "pg_trgm.similarity_threshold" TO public;
+GRANT SET VALUE ON "pg_trgm.word_similarity_threshold" TO public;
+GRANT SET VALUE ON "pg_trgm.strict_word_similarity_threshold" TO public;
diff --git a/contrib/pg_trgm/pg_trgm.control b/contrib/pg_trgm/pg_trgm.control
index 1d6a9ddf25..6e3ee43c51 100644
--- a/contrib/pg_trgm/pg_trgm.control
+++ b/contrib/pg_trgm/pg_trgm.control
@@ -1,6 +1,6 @@
# pg_trgm extension
comment = 'text similarity measurement and index searching based on trigrams'
-default_version = '1.6'
+default_version = '1.7'
module_pathname = '$libdir/pg_trgm'
relocatable = true
trusted = true
diff --git a/contrib/postgres_fdw/Makefile b/contrib/postgres_fdw/Makefile
index c1b0cad453..3564015ee8 100644
--- a/contrib/postgres_fdw/Makefile
+++ b/contrib/postgres_fdw/Makefile
@@ -14,7 +14,8 @@ PG_CPPFLAGS = -I$(libpq_srcdir)
SHLIB_LINK_INTERNAL = $(libpq)
EXTENSION = postgres_fdw
-DATA = postgres_fdw--1.0.sql postgres_fdw--1.0--1.1.sql
+DATA = postgres_fdw--1.0.sql postgres_fdw--1.0--1.1.sql \
+ postgres_fdw--1.1--1.2.sql
REGRESS = postgres_fdw
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index fc3ce6a53a..04250cc401 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -526,7 +526,7 @@ _PG_init(void)
NULL,
&pgfdw_application_name,
NULL,
- PGC_USERSET,
+ PGC_SUSET,
0,
NULL,
NULL,
diff --git a/contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql b/contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql
new file mode 100644
index 0000000000..b174e04283
--- /dev/null
+++ b/contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql
@@ -0,0 +1,6 @@
+/* contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION postgres_fdw UPDATE TO '1.2'" to load this file. \quit
+
+GRANT SET VALUE ON "postgres_fdw.application_name" TO public;
diff --git a/contrib/postgres_fdw/postgres_fdw.control b/contrib/postgres_fdw/postgres_fdw.control
index d489382064..a4b800be4f 100644
--- a/contrib/postgres_fdw/postgres_fdw.control
+++ b/contrib/postgres_fdw/postgres_fdw.control
@@ -1,5 +1,5 @@
# postgres_fdw extension
comment = 'foreign-data wrapper for remote PostgreSQL servers'
-default_version = '1.1'
+default_version = '1.2'
module_pathname = '$libdir/postgres_fdw'
relocatable = true
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index d71c802106..8fbddd58ec 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -449,7 +449,7 @@ _PG_init(void)
NULL,
&sepgsql_debug_audit,
false,
- PGC_USERSET,
+ PGC_SUSET,
GUC_NOT_IN_SAMPLE,
NULL,
NULL,
diff --git a/contrib/sepgsql/sepgsql.sql.in b/contrib/sepgsql/sepgsql.sql.in
index 917d12dbbe..4dce769306 100644
--- a/contrib/sepgsql/sepgsql.sql.in
+++ b/contrib/sepgsql/sepgsql.sql.in
@@ -34,4 +34,5 @@ CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_setcon(text) RETURNS bool AS 'MODU
CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_mcstrans_in(text) RETURNS text AS 'MODULE_PATHNAME', 'sepgsql_mcstrans_in' LANGUAGE C STRICT;
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;
+GRANT SET VALUE ON "sepgsql.debug_audit" TO public;
SELECT sepgsql_restorecon(NULL);
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 1e65c426b2..7e43b58895 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -105,6 +105,11 @@
<entry>collations (locale information)</entry>
</row>
+ <row>
+ <entry><link linkend="catalog-pg-setting-acl"><structname>pg_setting_acl</structname></link></entry>
+ <entry>configuration parameters which have privileges granted to roles</entry>
+ </row>
+
<row>
<entry><link linkend="catalog-pg-constraint"><structname>pg_constraint</structname></link></entry>
<entry>check constraints, unique constraints, primary key constraints, foreign key constraints</entry>
@@ -2423,6 +2428,64 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</para>
</sect1>
+ <sect1 id="catalog-pg-setting-acl">
+ <title><structname>pg_setting_acl</structname></title>
+
+ <indexterm zone="catalog-pg-setting-acl">
+ <primary>pg_setting_acl</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_setting_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_setting_acl</structname>
+ is shared across all databases of a cluster: there is only one
+ copy of <structname>pg_setting_acl</structname> per cluster, not
+ one per database.
+ </para>
+
+ <table>
+ <title><structname>pg_setting_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>setting</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>
@@ -12507,11 +12570,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 settings 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 VALUE</literal> privilege granted
+ on the setting 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 7cf0f0da3b..88de571382 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -1672,7 +1672,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 VALUE</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.
@@ -1940,6 +1941,28 @@ REVOKE ALL ON accounts FROM PUBLIC;
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>SET VALUE</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>SET</command> command to
+ change <literal>superuser</literal> run-time configuration parameters.
+ (Any user can set <literal>user</literal> run-time configuration
+ parameters.)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ALTER SYSTEM</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>ALTER SYSTEM
+ SET</command> command to change server configuration parameters.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
The privileges required by other commands are listed on the
@@ -2078,6 +2101,16 @@ REVOKE ALL ON accounts FROM PUBLIC;
<literal>TYPE</literal>
</entry>
</row>
+ <row>
+ <entry><literal>SET VALUE</literal></entry>
+ <entry><literal>s</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
+ <row>
+ <entry><literal>ALTER SYSTEM</literal></entry>
+ <entry><literal>A</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -2184,6 +2217,12 @@ REVOKE ALL ON accounts FROM PUBLIC;
<entry><literal>U</literal></entry>
<entry><literal>\dT+</literal></entry>
</row>
+ <row>
+ <entry><literal>Configuration parameter</literal></entry>
+ <entry><literal>sA</literal></entry>
+ <entry>none</entry>
+ <entry><literal></literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 0ee6974f1c..6029dcec5d 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');
@@ -22957,6 +22956,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_setting_privilege</primary>
+ </indexterm>
+ <function>has_setting_privilege</function> (
+ <optional> <parameter>user</parameter> <type>name</type> or <type>oid</type>, </optional>
+ <parameter>setting</parameter> <type>text</type> or <type>oid</type>,
+ <parameter>privilege</parameter> <type>text</type> )
+ <returnvalue>boolean</returnvalue>
+ </para>
+ <para>
+ Does user have privilege for setting?
+ Allowable privilege types are <literal>SET VALUE</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 a897712de2..21ec320a8d 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 VALUE | ALTER SYSTEM }
+ ON <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 VALUE</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/set.sgml b/doc/src/sgml/ref/set.sgml
index 339ee9eec9..a08057d1d1 100644
--- a/doc/src/sgml/ref/set.sgml
+++ b/doc/src/sgml/ref/set.sgml
@@ -34,8 +34,9 @@ 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.)
+ (But some require either superuser privileges or granted <literal>SET
+ VALUE</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>
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index df5268fbc3..e9efab5d30 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -28,6 +28,7 @@ OBJS = \
pg_cast.o \
pg_class.o \
pg_collation.o \
+ pg_setting_acl.o \
pg_constraint.o \
pg_conversion.o \
pg_db_role_setting.o \
@@ -54,7 +55,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_setting_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 \
@@ -126,6 +127,7 @@ install-data: bki-stamp installdirs
$(INSTALL_DATA) $(srcdir)/system_functions.sql '$(DESTDIR)$(datadir)/system_functions.sql'
$(INSTALL_DATA) $(srcdir)/system_views.sql '$(DESTDIR)$(datadir)/system_views.sql'
$(INSTALL_DATA) $(srcdir)/information_schema.sql '$(DESTDIR)$(datadir)/information_schema.sql'
+ $(INSTALL_DATA) $(srcdir)/setting_privileges.sql '$(DESTDIR)$(datadir)/setting_privileges.sql'
$(INSTALL_DATA) $(srcdir)/sql_features.txt '$(DESTDIR)$(datadir)/sql_features.txt'
installdirs:
@@ -133,7 +135,7 @@ installdirs:
.PHONY: uninstall-data
uninstall-data:
- rm -f $(addprefix '$(DESTDIR)$(datadir)'/, postgres.bki system_constraints.sql system_functions.sql system_views.sql information_schema.sql sql_features.txt)
+ rm -f $(addprefix '$(DESTDIR)$(datadir)'/, postgres.bki system_constraints.sql system_functions.sql system_views.sql information_schema.sql setting_privileges.sql sql_features.txt)
# postgres.bki, system_constraints.sql, and the generated headers are
# in the distribution tarball, so they are not cleaned here.
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 1dd03a8e51..3f8699b867 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_setting_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_Setting(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_SETTING:
+ whole_mask = ACL_ALL_RIGHTS_SETTING;
+ 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_SETTING:
+ all_privileges = ACL_ALL_RIGHTS_SETTING;
+ errormsg = gettext_noop("invalid privilege type %s for setting");
+ 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_SETTING:
+ ExecGrant_Setting(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -759,6 +771,38 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_SETTING:
+ foreach(cell, objnames)
+ {
+ char *setting = strVal(lfirst(cell));
+ Oid settingid = get_setting_oid(setting, true);
+
+ if (!OidIsValid(settingid))
+ {
+ /*
+ * Lookup the existing entry, or if necessary, add a new
+ * entry for this parameter. Entries only exist for
+ * parameters which currently have, or previously have had,
+ * privileges assigned.
+ *
+ * It is tempting to sanity-check the given configuration
+ * parameter name against known guc names in the guc
+ * tables, but for handling upgrades we need to accept
+ * setting names that do not yet exist.
+ */
+ settingid = SettingAclCreate(setting, 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, settingid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -1494,6 +1538,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case ForeignDataWrapperRelationId:
istmt.objtype = OBJECT_FDW;
break;
+ case SettingAclRelationId:
+ istmt.objtype = OBJECT_SETTING;
+ break;
default:
elog(ERROR, "unexpected object class %u", classid);
break;
@@ -3225,6 +3272,131 @@ ExecGrant_Type(InternalGrant *istmt)
table_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Setting(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_SETTING;
+
+ relation = table_open(SettingAclRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid settingId = lfirst_oid(cell);
+ Form_pg_setting_acl pg_setting_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_setting_acl];
+ bool nulls[Natts_pg_setting_acl];
+ bool replaces[Natts_pg_setting_acl];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(settingId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for setting %u", settingId);
+
+ pg_setting_acl_tuple = (Form_pg_setting_acl) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ aclDatum = SysCacheGetAttr(SETTINGNAME, tuple, Anum_pg_setting_acl_setacl,
+ &isNull);
+ if (isNull)
+ {
+ old_acl = acldefault(OBJECT_SETTING, InvalidOid);
+ /* 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, GetUserId(),
+ &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,
+ settingId, grantorId, OBJECT_SETTING,
+ text_to_cstring(&pg_setting_acl_tuple->setting),
+ 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_setting_acl_setacl - 1] = true;
+ values[Anum_pg_setting_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(settingId, SettingAclRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(SettingAclRelationId,
+ pg_setting_acl_tuple->oid, 0,
+ InvalidOid,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* Post alter hook called for grant and revoke */
+ InvokeObjectPostAlterHook(SettingAclRelationId, settingId, 0);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ table_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3255,6 +3427,10 @@ string_to_privilege(const char *privname)
return ACL_CREATE_TEMP;
if (strcmp(privname, "connect") == 0)
return ACL_CONNECT;
+ if (strcmp(privname, "set value") == 0)
+ return ACL_SET_VALUE;
+ 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 +3468,10 @@ privilege_to_string(AclMode privilege)
return "TEMP";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET_VALUE:
+ return "SET VALUE";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3328,6 +3508,13 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_COLUMN:
msg = gettext_noop("permission denied for column %s");
break;
+ case OBJECT_SETTING:
+ /*
+ * 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 +3751,7 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_AMPROC:
case OBJECT_ATTRIBUTE:
case OBJECT_CAST:
+ case OBJECT_SETTING:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
case OBJECT_DOMCONSTRAINT:
@@ -4000,6 +4188,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_setting_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 setting's ACL from pg_setting_acl
+ */
+ tuple = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(config_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("setting with OID %u does not exist",
+ config_oid)));
+
+ aclDatum = SysCacheGetAttr(SETTINGOID, tuple, Anum_pg_setting_acl_setacl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_SETTING, 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 +4954,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_setting_acl_aclcheck(Oid config_oid, Oid roleid, AclMode mode)
+{
+ if (pg_setting_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..78868e7052 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_setting_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 == SettingAclRelationId ||
relationId == DatabaseRelationId ||
relationId == SharedDescriptionRelationId ||
relationId == SharedDependRelationId ||
@@ -260,6 +262,8 @@ IsSharedRelation(Oid relationId)
relationId == AuthIdOidIndexId ||
relationId == AuthMemRoleMemIndexId ||
relationId == AuthMemMemRoleIndexId ||
+ relationId == SettingAclSettingIndexId ||
+ relationId == SettingAclOidIndexId ||
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 == PgSettingAclToastTable ||
+ relationId == PgSettingAclToastIndex ||
relationId == PgDatabaseToastTable ||
relationId == PgDatabaseToastIndex ||
relationId == PgDbRoleSettingToastTable ||
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index ab9e42d7d1..5d2504463d 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_setting_acl.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
@@ -150,6 +151,7 @@ static const Oid object_classes[] = {
TypeRelationId, /* OCLASS_TYPE */
CastRelationId, /* OCLASS_CAST */
CollationRelationId, /* OCLASS_COLLATION */
+ SettingAclRelationId, /* OCLASS_SETTING */
ConstraintRelationId, /* OCLASS_CONSTRAINT */
ConversionRelationId, /* OCLASS_CONVERSION */
AttrDefaultRelationId, /* OCLASS_DEFAULT */
@@ -1504,6 +1506,7 @@ doDeletion(const ObjectAddress *object, int flags)
/*
* These global object types are not supported here.
*/
+ case OCLASS_SETTING:
case OCLASS_ROLE:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
@@ -2778,6 +2781,9 @@ getObjectClass(const ObjectAddress *object)
case CollationRelationId:
return OCLASS_COLLATION;
+ case SettingAclRelationId:
+ return OCLASS_SETTING;
+
case ConstraintRelationId:
return OCLASS_CONSTRAINT;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index f30c742d48..1ef833a0e3 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_setting_acl.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
@@ -2309,6 +2310,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_COLUMN:
case OBJECT_ATTRIBUTE:
case OBJECT_COLLATION:
+ case OBJECT_SETTING:
case OBJECT_CONVERSION:
case OBJECT_STATISTIC_EXT:
case OBJECT_TSPARSER:
@@ -3510,6 +3512,22 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
break;
}
+ case OCLASS_SETTING:
+ {
+ char *setting;
+
+ setting = get_setting_name(object->objectId);
+ if (!setting)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for setting %u",
+ object->objectId);
+ break;
+ }
+ appendStringInfo(&buffer, _("setting %s"), setting);
+ break;
+ }
+
case OCLASS_SCHEMA:
{
char *nspname;
@@ -4473,6 +4491,10 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "collation");
break;
+ case OCLASS_SETTING:
+ appendStringInfoString(&buffer, "setting");
+ break;
+
case OCLASS_CONSTRAINT:
getConstraintTypeDescription(&buffer, object->objectId,
missing_ok);
@@ -4977,6 +4999,30 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_SETTING:
+ {
+ HeapTuple configTup;
+ Form_pg_setting_acl configForm;
+ char *namestr;
+
+ configTup = SearchSysCache1(SETTINGOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(configTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for setting %u",
+ object->objectId);
+ break;
+ }
+ configForm = (Form_pg_setting_acl) GETSTRUCT(configTup);
+ namestr = text_to_cstring(&configForm->setting);
+ appendStringInfoString(&buffer, namestr);
+ if (objname)
+ *objname = list_make1(namestr);
+ ReleaseSysCache(configTup);
+ break;
+ }
+
case OCLASS_CONVERSION:
{
HeapTuple conTup;
diff --git a/src/backend/catalog/pg_setting_acl.c b/src/backend/catalog/pg_setting_acl.c
new file mode 100644
index 0000000000..3c6594da03
--- /dev/null
+++ b/src/backend/catalog/pg_setting_acl.c
@@ -0,0 +1,122 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_setting_acl.c
+ * routines to support manipulation of the pg_setting_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_setting_acl.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/sysattr.h"
+#include "access/table.h"
+#include "access/tableam.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_setting_acl.h"
+#include "mb/pg_wchar.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/guc.h"
+#include "utils/pg_locale.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * SettingAclCreate
+ *
+ * Add a new tuple to pg_setting_acl.
+ *
+ * setting: the setting name to create.
+ * if_not_exists: if true, don't fail on duplicate name, but rather return
+ * the existing entry's Oid.
+ */
+Oid
+SettingAclCreate(const char *setting, bool if_not_exists)
+{
+ Relation rel;
+ TupleDesc tupDesc;
+ HeapTuple tuple;
+ Datum values[Natts_pg_setting_acl];
+ bool nulls[Natts_pg_setting_acl];
+ Oid settingId;
+ const char *canonical;
+
+ /*
+ * Check whether the setting (by the given name or alias)
+ * already exists.
+ */
+ settingId = get_setting_oid(setting, true);
+ if (OidIsValid(settingId))
+ {
+ if (if_not_exists)
+ return settingId;
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("setting \"%s\" already exists",
+ setting)));
+ }
+
+ /*
+ * We must not require the setting to be in the list of existent GUCs,
+ * as we may be called at different points during upgrades or the
+ * installation or removal of extensions. Instead, perform a basic sanity
+ * check of the setting, 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_setting_acl entries.
+ */
+ if (!valid_variable_name(setting, NULL))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid setting name \"%s\"",
+ setting)));
+ canonical = GetConfigOptionCanonicalName(setting);
+ if (!canonical)
+ canonical = setting;
+
+ /*
+ * 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(SettingAclRelationId, RowExclusiveLock);
+ tupDesc = RelationGetDescr(rel);
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ values[Anum_pg_setting_acl_setting - 1] =
+ DirectFunctionCall1(textin, CStringGetDatum(canonical));
+ settingId = GetNewOidWithIndex(rel,
+ SettingAclOidIndexId,
+ Anum_pg_setting_acl_oid);
+ values[Anum_pg_setting_acl_oid - 1] = ObjectIdGetDatum(settingId);
+ nulls[Anum_pg_setting_acl_setacl - 1] = true;
+ tuple = heap_form_tuple(tupDesc, values, nulls);
+ CatalogTupleInsert(rel, tuple);
+
+ /* Post creation hook for new setting */
+ InvokeObjectPostCreateHook(SettingAclRelationId, settingId, 0);
+
+ /*
+ * Close pg_setting_acl, but keep lock till commit.
+ */
+ heap_freetuple(tuple);
+ table_close(rel, NoLock);
+
+ return settingId;
+}
diff --git a/src/backend/catalog/setting_privileges.sql b/src/backend/catalog/setting_privileges.sql
new file mode 100644
index 0000000000..b788ebee7f
--- /dev/null
+++ b/src/backend/catalog/setting_privileges.sql
@@ -0,0 +1,62 @@
+/*
+ * PostgreSQL User SET variables
+ *
+ * Copyright (c) 2021, PostgreSQL Global Development Group
+ *
+ * src/backend/catalog/setting_privileges.sql
+ *
+ * Note: this file is read in single-user -j mode, which means that the
+ * command terminator is semicolon-newline-newline; whenever the backend
+ * sees that, it stops and executes what it's got. If you write a lot of
+ * statements without empty lines between, they'll all get quoted to you
+ * in any error message about one of them, so don't do that. Also, you
+ * cannot write a semicolon immediately followed by an empty line in a
+ * string literal (including a function body!) or a multiline comment.
+ */
+
+GRANT SET VALUE ON
+ enable_seqscan, enable_indexscan, enable_indexonlyscan, enable_bitmapscan,
+ enable_tidscan, enable_sort, enable_incremental_sort, enable_hashagg,
+ enable_material, enable_memoize, enable_nestloop, enable_mergejoin,
+ enable_hashjoin, enable_gathermerge, enable_partitionwise_join,
+ enable_partitionwise_aggregate, enable_parallel_append,
+ enable_parallel_hash, enable_partition_pruning, enable_async_append, geqo,
+ exit_on_error, debug_print_parse, debug_print_rewritten, debug_print_plan,
+ debug_pretty_print, trace_notify, transform_null_equals,
+ default_transaction_read_only, transaction_read_only,
+ default_transaction_deferrable, transaction_deferrable, row_security,
+ check_function_bodies, array_nulls, default_with_oids, trace_sort,
+ trace_syncscan, optimize_bounded_sort, escape_string_warning,
+ standard_conforming_strings, synchronize_seqscans, quote_all_identifiers,
+ parallel_leader_participation, jit, jit_expressions, jit_tuple_deforming,
+ default_statistics_target, from_collapse_limit, join_collapse_limit,
+ geqo_threshold, geqo_effort, geqo_pool_size, geqo_generations,
+ temp_buffers, work_mem, maintenance_work_mem, logical_decoding_work_mem,
+ vacuum_cost_page_hit, vacuum_cost_page_miss, vacuum_cost_page_dirty,
+ vacuum_cost_limit, statement_timeout, lock_timeout,
+ idle_in_transaction_session_timeout, idle_session_timeout,
+ vacuum_freeze_min_age, vacuum_freeze_table_age,
+ vacuum_multixact_freeze_min_age, vacuum_multixact_freeze_table_age,
+ vacuum_failsafe_age, vacuum_multixact_failsafe_age, wal_skip_threshold,
+ wal_sender_timeout, commit_siblings, extra_float_digits,
+ log_parameter_max_length_on_error, effective_io_concurrency,
+ maintenance_io_concurrency, backend_flush_after,
+ max_parallel_maintenance_workers, max_parallel_workers_per_gather,
+ max_parallel_workers, tcp_keepalives_idle, tcp_keepalives_interval,
+ ssl_renegotiation_limit, tcp_keepalives_count, gin_fuzzy_search_limit,
+ effective_cache_size, min_parallel_table_scan_size,
+ min_parallel_index_scan_size, gin_pending_list_limit, tcp_user_timeout,
+ client_connection_check_interval, seq_page_cost, random_page_cost,
+ cpu_tuple_cost, cpu_index_tuple_cost, cpu_operator_cost,
+ parallel_tuple_cost, parallel_setup_cost, jit_above_cost,
+ jit_optimize_above_cost, jit_inline_above_cost, cursor_tuple_fraction,
+ geqo_selection_bias, geqo_seed, hash_mem_multiplier, seed,
+ vacuum_cost_delay, DateStyle, default_table_access_method,
+ default_tablespace, temp_tablespaces, lc_monetary, lc_numeric, lc_time,
+ local_preload_libraries, search_path, role, TimeZone,
+ timezone_abbreviations, default_text_search_config, application_name,
+ backslash_quote, bytea_output, client_min_messages, constraint_exclusion,
+ default_toast_compression, default_transaction_isolation,
+ transaction_isolation, IntervalStyle, synchronous_commit, xmlbinary,
+ xmloption, force_parallel_mode, password_encryption, plan_cache_mode
+ TO public;
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 3cb69b1f87..8fd2d33dc0 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -595,6 +595,19 @@ CREATE RULE pg_settings_n AS
GRANT SELECT, UPDATE ON pg_settings TO PUBLIC;
+CREATE VIEW pg_setting_privileges AS
+ SELECT grantor.rolname AS grantor,
+ grantee.rolname AS grantee,
+ set_acl.setting AS setting,
+ acl.privilege_type AS privilege_type,
+ acl.is_grantable
+ FROM pg_catalog.pg_setting_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_setting_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..3dd0c5cadb 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -639,6 +639,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
break;
case OCLASS_CAST:
+ case OCLASS_SETTING:
case OCLASS_CONSTRAINT:
case OCLASS_DEFAULT:
case OCLASS_LANGUAGE:
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index c9b5732448..00398727ed 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -276,6 +276,13 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
name = NameListToString(castNode(List, object));
}
break;
+ case OBJECT_SETTING:
+ if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
+ {
+ msg = gettext_noop("setting \"%s\" does not exist, skipping");
+ name = NameListToString(castNode(List, object));
+ }
+ break;
case OBJECT_CONVERSION:
if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
{
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 93c2099735..4de3b85db1 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_SETTING:
case OBJECT_DATABASE:
case OBJECT_TABLESPACE:
case OBJECT_ROLE:
@@ -1012,6 +1013,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
{
switch (objclass)
{
+ case OCLASS_SETTING:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
@@ -2078,6 +2080,8 @@ stringify_grant_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMN";
+ case OBJECT_SETTING:
+ return "SETTING";
case OBJECT_TABLE:
return "TABLE";
case OBJECT_SEQUENCE:
@@ -2161,6 +2165,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMNS";
+ case OBJECT_SETTING:
+ return "SETTINGS";
case OBJECT_TABLE:
return "TABLES";
case OBJECT_SEQUENCE:
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 7a62d547e2..7b05eb0110 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_SETTING:
case OBJECT_CONVERSION:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1f0654c2f5..4b6de1d893 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -12619,6 +12619,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_TYPE:
case OCLASS_CAST:
case OCLASS_COLLATION:
+ case OCLASS_SETTING:
case OCLASS_CONVERSION:
case OCLASS_LANGUAGE:
case OCLASS_LARGEOBJECT:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b5966712ce..533b996145 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -363,8 +363,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 setting_name
+%type <list> OptSchemaEltList setting_target
%type <chr> am_type
@@ -402,8 +402,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> iso_level opt_encoding
%type <rolespec> grantee
%type <list> grantee_list
-%type <accesspriv> privilege
-%type <list> privileges privilege_list
+%type <accesspriv> privilege setting_priv
+%type <list> privileges privilege_list setting_priv_list
%type <privtarget> privilege_target
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
@@ -715,7 +715,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
@@ -6972,6 +6972,20 @@ GrantStmt: GRANT privileges ON privilege_target TO grantee_list
n->grantor = $8;
$$ = (Node*)n;
}
+ | GRANT setting_priv_list ON setting_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_SETTING;
+ n->objects = $4;
+ n->grantees = $6;
+ n->grant_option = $7;
+ n->grantor = $8;
+ $$ = (Node*)n;
+ }
;
RevokeStmt:
@@ -7005,6 +7019,36 @@ RevokeStmt:
n->behavior = $11;
$$ = (Node *)n;
}
+ | REVOKE setting_priv_list ON setting_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_SETTING;
+ n->objects = $4;
+ n->grantees = $6;
+ n->grantor = $7;
+ n->behavior = $8;
+ $$ = (Node *)n;
+ }
+ | REVOKE GRANT OPTION FOR setting_priv_list ON setting_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_SETTING;
+ n->objects = $7;
+ n->grantees = $9;
+ n->grantor = $10;
+ n->behavior = $11;
+ $$ = (Node *)n;
+ }
;
@@ -7073,6 +7117,49 @@ privilege: SELECT opt_column_list
}
;
+setting_priv_list: setting_priv { $$ = list_make1($1); }
+ | setting_priv_list ',' setting_priv { $$ = lappend($1, $3); }
+ ;
+
+setting_priv:
+ ALTER SYSTEM_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("alter system");
+ n->cols = NIL;
+ $$ = n;
+ }
+ | SET VALUE_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("set value");
+ n->cols = NIL;
+ $$ = n;
+ }
+ ;
+
+
+setting_target:
+ setting_name
+ {
+ $$ = list_make1(makeString($1));
+ }
+ | setting_target ',' setting_name
+ {
+ $$ = lappend($1, makeString($3));
+ }
+ ;
+
+setting_name:
+ ColId
+ {
+ $$ = $1;
+ }
+ | setting_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.
@@ -15816,6 +15903,7 @@ unreserved_keyword:
| OWNED
| OWNER
| PARALLEL
+ | PARAMETER
| PARSER
| PARTIAL
| PARTITION
@@ -16394,6 +16482,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 0a16f8156c..b0e43118bf 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_setting_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,8 @@ 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 Oid convert_setting_name(text *setting);
+static AclMode convert_setting_priv_string(text *priv_setting_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 +310,12 @@ aclparse(const char *s, AclItem *aip)
case ACL_CONNECT_CHR:
read = ACL_CONNECT;
break;
+ case ACL_SET_VALUE_CHR:
+ read = ACL_SET_VALUE;
+ break;
+ case ACL_ALTER_SYSTEM_CHR:
+ read = ACL_ALTER_SYSTEM;
+ break;
case 'R': /* ignore old RULE privileges */
read = 0;
break;
@@ -794,6 +804,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_SETTING:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_NO_RIGHTS;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -1602,6 +1616,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 VALUE") == 0)
+ return ACL_SET_VALUE;
+ 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 +1716,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET_VALUE:
+ return "SET VALUE";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
@@ -4429,6 +4451,205 @@ convert_type_priv_string(text *priv_type_text)
return convert_any_priv_string(priv_type_text, type_priv_map);
}
+/*
+ * has_setting_privilege variants
+ * These are all named "has_setting_privilege" at the SQL level.
+ * They take various combinations of setting name, setting 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_setting_privilege_name_name
+ * Check user privileges on a setting given
+ * name username, text setting, and text priv name.
+ */
+Datum
+has_setting_privilege_name_name(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ text *setting = PG_GETARG_TEXT_PP(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid roleid;
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_role_oid_or_public(NameStr(*username));
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_name
+ * Check user privileges on a setting given
+ * text setting and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_setting_privilege_name(PG_FUNCTION_ARGS)
+{
+ text *setting = PG_GETARG_TEXT_PP(0);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(1);
+ Oid roleid;
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_name_id
+ * Check user privileges on a setting given
+ * name usename, setting oid, and text priv name.
+ */
+Datum
+has_setting_privilege_name_id(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ Oid settingoid = PG_GETARG_OID(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_role_oid_or_public(NameStr(*username));
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id
+ * Check user privileges on a setting given
+ * setting oid, and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_setting_privilege_id(PG_FUNCTION_ARGS)
+{
+ Oid settingoid = PG_GETARG_OID(0);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(1);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id_name
+ * Check user privileges on a setting given
+ * roleid, text setting, and text priv name.
+ */
+Datum
+has_setting_privilege_id_name(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ text *setting = PG_GETARG_TEXT_PP(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id_id
+ * Check user privileges on a setting given
+ * roleid, setting oid, and text priv name.
+ */
+Datum
+has_setting_privilege_id_id(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ Oid settingoid = PG_GETARG_OID(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ AclMode mode;
+ AclResult aclresult;
+
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * Support routines for has_setting_privilege family.
+ */
+
+/*
+ * Given a setting name expressed as a string, look it up and return Oid
+ */
+static Oid
+convert_setting_name(text *settingname)
+{
+ Oid oid;
+ char *setting = text_to_cstring(settingname);
+
+ oid = get_setting_oid(setting, true);
+
+ if (!OidIsValid(oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("setting \"%s\" does not exist", setting)));
+
+ return oid;
+}
+
+/*
+ * convert_setting_priv_string
+ * Convert text string to AclMode value.
+ */
+static AclMode
+convert_setting_priv_string(text *priv_setting_text)
+{
+ static const priv_map setting_priv_map[] = {
+ {"SET VALUE", ACL_SET_VALUE},
+ {"SET VALUE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SET_VALUE)},
+ {"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_setting_text, setting_priv_map);
+}
/*
* pg_has_role variants
@@ -4670,6 +4891,43 @@ initialize_acl(void)
}
}
+/*
+ * get_setting_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_setting_oid(const char *setting, bool missing_ok)
+{
+ Oid oid;
+
+ /* Check for the variable by the name we were given */
+ oid = GetSysCacheOid1(SETTINGNAME, Anum_pg_setting_acl_oid,
+ PointerGetDatum(cstring_to_text(setting)));
+ if (!OidIsValid(oid))
+ {
+ const char *canonical;
+
+ /* Check if the variable has a different canonical spelling */
+ canonical = GetConfigOptionCanonicalName(setting);
+ if (canonical != NULL)
+ oid = GetSysCacheOid1(SETTINGNAME, Anum_pg_setting_acl_oid,
+ PointerGetDatum(cstring_to_text(canonical)));
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("setting \"%s\" does not exist", setting)));
+ }
+
+ return oid;
+}
+
/*
* RoleMembershipCacheCallback
* Syscache inval callback function
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index feef999863..f4e806fe8e 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_setting_acl.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_transform.h"
#include "catalog/pg_type.h"
@@ -3304,6 +3305,25 @@ free_attstatsslot(AttStatsSlot *sslot)
pfree(sslot->numbers_arr);
}
+char *
+get_setting_name(Oid configid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(configid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_setting_acl configtup = (Form_pg_setting_acl) GETSTRUCT(tp);
+ char *result;
+
+ result = pstrdup(text_to_cstring(&configtup->setting));
+ 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 f4e7819f1e..cbcbf02839 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_setting_acl.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
#include "catalog/pg_shseclabel.h"
@@ -762,6 +763,28 @@ static const struct cachedesc cacheinfo[] = {
},
32
},
+ {SettingAclRelationId, /* SETTINGNAME */
+ SettingAclSettingIndexId,
+ 1,
+ {
+ Anum_pg_setting_acl_setting,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+ {SettingAclRelationId, /* SETTINGOID */
+ SettingAclOidIndexId,
+ 1,
+ {
+ Anum_pg_setting_acl_oid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{StatisticExtDataRelationId, /* STATEXTDATASTXOID */
StatisticExtDataStxoidInhIndexId,
2,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 4c94f09c64..00f5021fb0 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -973,7 +973,7 @@ static const unit_conversion time_unit_conversion_table[] =
static struct config_bool ConfigureNamesBool[] =
{
{
- {"enable_seqscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_seqscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of sequential-scan plans."),
NULL,
GUC_EXPLAIN
@@ -983,7 +983,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_indexscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_indexscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of index-scan plans."),
NULL,
GUC_EXPLAIN
@@ -993,7 +993,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_indexonlyscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_indexonlyscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of index-only-scan plans."),
NULL,
GUC_EXPLAIN
@@ -1003,7 +1003,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_bitmapscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_bitmapscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of bitmap-scan plans."),
NULL,
GUC_EXPLAIN
@@ -1013,7 +1013,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_tidscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_tidscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of TID scan plans."),
NULL,
GUC_EXPLAIN
@@ -1023,7 +1023,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_sort", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_sort", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of explicit sort steps."),
NULL,
GUC_EXPLAIN
@@ -1033,7 +1033,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_incremental_sort", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_incremental_sort", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of incremental sort steps."),
NULL,
GUC_EXPLAIN
@@ -1043,7 +1043,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_hashagg", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_hashagg", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of hashed aggregation plans."),
NULL,
GUC_EXPLAIN
@@ -1053,7 +1053,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_material", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_material", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of materialization."),
NULL,
GUC_EXPLAIN
@@ -1063,7 +1063,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_memoize", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_memoize", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of memoization."),
NULL,
GUC_EXPLAIN
@@ -1073,7 +1073,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_nestloop", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_nestloop", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of nested-loop join plans."),
NULL,
GUC_EXPLAIN
@@ -1083,7 +1083,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_mergejoin", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_mergejoin", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of merge join plans."),
NULL,
GUC_EXPLAIN
@@ -1093,7 +1093,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_hashjoin", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_hashjoin", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of hash join plans."),
NULL,
GUC_EXPLAIN
@@ -1103,7 +1103,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_gathermerge", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_gathermerge", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of gather merge plans."),
NULL,
GUC_EXPLAIN
@@ -1113,7 +1113,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_partitionwise_join", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_partitionwise_join", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables partitionwise join."),
NULL,
GUC_EXPLAIN
@@ -1123,7 +1123,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_partitionwise_aggregate", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_partitionwise_aggregate", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables partitionwise aggregation and grouping."),
NULL,
GUC_EXPLAIN
@@ -1133,7 +1133,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_parallel_append", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_parallel_append", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of parallel append plans."),
NULL,
GUC_EXPLAIN
@@ -1143,7 +1143,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_parallel_hash", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_parallel_hash", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of parallel hash plans."),
NULL,
GUC_EXPLAIN
@@ -1153,7 +1153,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_partition_pruning", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_partition_pruning", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables plan-time and execution-time partition pruning."),
gettext_noop("Allows the query planner and executor to compare partition "
"bounds to conditions in the query to determine which "
@@ -1165,7 +1165,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_async_append", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_async_append", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of async append plans."),
NULL,
GUC_EXPLAIN
@@ -1175,7 +1175,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("Enables genetic query optimization."),
gettext_noop("This algorithm attempts to do planning without "
"exhaustive searching."),
@@ -1397,7 +1397,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"exit_on_error", PGC_USERSET, ERROR_HANDLING_OPTIONS,
+ {"exit_on_error", PGC_SUSET, ERROR_HANDLING_OPTIONS,
gettext_noop("Terminate session on any error."),
NULL
},
@@ -1435,7 +1435,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_print_parse", PGC_USERSET, LOGGING_WHAT,
+ {"debug_print_parse", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs each query's parse tree."),
NULL
},
@@ -1444,7 +1444,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_print_rewritten", PGC_USERSET, LOGGING_WHAT,
+ {"debug_print_rewritten", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs each query's rewritten parse tree."),
NULL
},
@@ -1453,7 +1453,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_print_plan", PGC_USERSET, LOGGING_WHAT,
+ {"debug_print_plan", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs each query's execution plan."),
NULL
},
@@ -1462,7 +1462,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_pretty_print", PGC_USERSET, LOGGING_WHAT,
+ {"debug_pretty_print", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Indents parse and plan tree displays."),
NULL
},
@@ -1583,7 +1583,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"trace_notify", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"trace_notify", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Generates debugging output for LISTEN and NOTIFY."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -1667,7 +1667,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"transform_null_equals", PGC_USERSET, COMPAT_OPTIONS_CLIENT,
+ {"transform_null_equals", PGC_SUSET, COMPAT_OPTIONS_CLIENT,
gettext_noop("Treats \"expr=NULL\" as \"expr IS NULL\"."),
gettext_noop("When turned on, expressions of the form expr = NULL "
"(or NULL = expr) are treated as expr IS NULL, that is, they "
@@ -1689,7 +1689,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"default_transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_transaction_read_only", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default read-only status of new transactions."),
NULL,
GUC_REPORT
@@ -1699,7 +1699,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"transaction_read_only", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the current transaction's read-only status."),
NULL,
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -1709,7 +1709,7 @@ static struct config_bool ConfigureNamesBool[] =
check_transaction_read_only, NULL, NULL
},
{
- {"default_transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_transaction_deferrable", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default deferrable status of new transactions."),
NULL
},
@@ -1718,7 +1718,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"transaction_deferrable", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Whether to defer a read-only serializable transaction until it can be executed with no possible serialization failures."),
NULL,
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -1728,7 +1728,7 @@ static struct config_bool ConfigureNamesBool[] =
check_transaction_deferrable, NULL, NULL
},
{
- {"row_security", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"row_security", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Enable row security."),
gettext_noop("When enabled, row security will be applied to all users.")
},
@@ -1737,7 +1737,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"check_function_bodies", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"check_function_bodies", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Check routine bodies during CREATE FUNCTION and CREATE PROCEDURE."),
NULL
},
@@ -1746,7 +1746,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"array_nulls", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"array_nulls", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Enable input of NULL elements in arrays."),
gettext_noop("When turned on, unquoted NULL in an array input "
"value means a null value; "
@@ -1763,7 +1763,7 @@ static struct config_bool ConfigureNamesBool[] =
* avoid unnecessarily breaking older dump files.
*/
{
- {"default_with_oids", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"default_with_oids", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("WITH OIDS is no longer supported; this can only be false."),
NULL,
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE
@@ -1793,7 +1793,7 @@ static struct config_bool ConfigureNamesBool[] =
#ifdef TRACE_SORT
{
- {"trace_sort", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"trace_sort", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Emit information about resource usage in sorting."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -1807,7 +1807,7 @@ static struct config_bool ConfigureNamesBool[] =
#ifdef TRACE_SYNCSCAN
/* this is undocumented because not exposed in a standard build */
{
- {"trace_syncscan", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"trace_syncscan", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Generate debugging output for synchronized scanning."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -1822,7 +1822,7 @@ static struct config_bool ConfigureNamesBool[] =
/* this is undocumented because not exposed in a standard build */
{
{
- "optimize_bounded_sort", PGC_USERSET, QUERY_TUNING_METHOD,
+ "optimize_bounded_sort", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enable bounded sorting using heap sort."),
NULL,
GUC_NOT_IN_SAMPLE | GUC_EXPLAIN
@@ -1868,7 +1868,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"escape_string_warning", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"escape_string_warning", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Warn about backslash escapes in ordinary string literals."),
NULL
},
@@ -1878,7 +1878,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"standard_conforming_strings", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"standard_conforming_strings", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Causes '...' strings to treat backslashes literally."),
NULL,
GUC_REPORT
@@ -1889,7 +1889,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"synchronize_seqscans", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"synchronize_seqscans", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Enable synchronized sequential scans."),
NULL
},
@@ -1985,7 +1985,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"quote_all_identifiers", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"quote_all_identifiers", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("When generating SQL fragments, quote all identifiers."),
NULL,
},
@@ -2026,7 +2026,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"parallel_leader_participation", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"parallel_leader_participation", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Controls whether Gather and Gather Merge also run subplans."),
gettext_noop("Should gather nodes also run subplans or just gather tuples?"),
GUC_EXPLAIN
@@ -2037,7 +2037,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"jit", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"jit", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Allow JIT compilation."),
NULL,
GUC_EXPLAIN
@@ -2076,7 +2076,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"jit_expressions", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"jit_expressions", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Allow JIT compilation of expressions."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -2104,7 +2104,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"jit_tuple_deforming", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"jit_tuple_deforming", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Allow JIT compilation of tuple deforming."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -2164,7 +2164,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"default_statistics_target", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"default_statistics_target", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the default statistics target."),
gettext_noop("This applies to table columns that have not had a "
"column-specific target set via ALTER TABLE SET STATISTICS.")
@@ -2174,7 +2174,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"from_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"from_collapse_limit", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the FROM-list size beyond which subqueries "
"are not collapsed."),
gettext_noop("The planner will merge subqueries into upper "
@@ -2187,7 +2187,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"join_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"join_collapse_limit", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the FROM-list size beyond which JOIN "
"constructs are not flattened."),
gettext_noop("The planner will flatten explicit JOIN "
@@ -2200,7 +2200,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_threshold", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_threshold", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("Sets the threshold of FROM items beyond which GEQO is used."),
NULL,
GUC_EXPLAIN
@@ -2210,7 +2210,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_effort", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_effort", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: effort is used to set the default for other GEQO parameters."),
NULL,
GUC_EXPLAIN
@@ -2220,7 +2220,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_pool_size", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_pool_size", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: number of individuals in the population."),
gettext_noop("Zero selects a suitable default value."),
GUC_EXPLAIN
@@ -2230,7 +2230,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_generations", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_generations", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: number of iterations of the algorithm."),
gettext_noop("Zero selects a suitable default value."),
GUC_EXPLAIN
@@ -2377,7 +2377,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"temp_buffers", PGC_USERSET, RESOURCES_MEM,
+ {"temp_buffers", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum number of temporary buffers used by each session."),
NULL,
GUC_UNIT_BLOCKS | GUC_EXPLAIN
@@ -2442,7 +2442,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"work_mem", PGC_USERSET, RESOURCES_MEM,
+ {"work_mem", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum memory to be used for query workspaces."),
gettext_noop("This much memory can be used by each internal "
"sort operation and hash table before switching to "
@@ -2455,7 +2455,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"maintenance_work_mem", PGC_USERSET, RESOURCES_MEM,
+ {"maintenance_work_mem", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum memory to be used for maintenance operations."),
gettext_noop("This includes operations such as VACUUM and CREATE INDEX."),
GUC_UNIT_KB
@@ -2466,7 +2466,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"logical_decoding_work_mem", PGC_USERSET, RESOURCES_MEM,
+ {"logical_decoding_work_mem", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum memory to be used for logical decoding."),
gettext_noop("This much memory can be used by each internal "
"reorder buffer before spilling to disk."),
@@ -2505,7 +2505,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_page_hit", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_page_hit", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost for a page found in the buffer cache."),
NULL
},
@@ -2515,7 +2515,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_page_miss", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_page_miss", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost for a page not found in the buffer cache."),
NULL
},
@@ -2525,7 +2525,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_page_dirty", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_page_dirty", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost for a page dirtied by vacuum."),
NULL
},
@@ -2535,7 +2535,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_limit", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_limit", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost amount available before napping."),
NULL
},
@@ -2601,7 +2601,7 @@ static struct config_int ConfigureNamesInt[] =
#endif
{
- {"statement_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"statement_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed duration of any statement."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2612,7 +2612,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"lock_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"lock_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed duration of any wait for a lock."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2623,7 +2623,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"idle_in_transaction_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"idle_in_transaction_session_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed idle time between queries, when in a transaction."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2634,7 +2634,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"idle_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"idle_session_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed idle time between queries, when not in a transaction."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2645,7 +2645,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_freeze_min_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Minimum age at which VACUUM should freeze a table row."),
NULL
},
@@ -2655,7 +2655,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_freeze_table_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Age at which VACUUM should scan whole table to freeze tuples."),
NULL
},
@@ -2665,7 +2665,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_multixact_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_multixact_freeze_min_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Minimum age at which VACUUM should freeze a MultiXactId in a table row."),
NULL
},
@@ -2675,7 +2675,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_multixact_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_multixact_freeze_table_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Multixact age at which VACUUM should scan whole table to freeze tuples."),
NULL
},
@@ -2694,7 +2694,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"vacuum_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_failsafe_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
NULL
},
@@ -2703,7 +2703,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"vacuum_multixact_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_multixact_failsafe_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Multixact age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
NULL
},
@@ -2891,7 +2891,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"wal_skip_threshold", PGC_USERSET, WAL_SETTINGS,
+ {"wal_skip_threshold", PGC_SUSET, WAL_SETTINGS,
gettext_noop("Minimum size of new file to fsync instead of writing WAL."),
NULL,
GUC_UNIT_KB
@@ -2936,7 +2936,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"wal_sender_timeout", PGC_USERSET, REPLICATION_SENDING,
+ {"wal_sender_timeout", PGC_SUSET, REPLICATION_SENDING,
gettext_noop("Sets the maximum time to wait for WAL replication."),
NULL,
GUC_UNIT_MS
@@ -2959,7 +2959,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"commit_siblings", PGC_USERSET, WAL_SETTINGS,
+ {"commit_siblings", PGC_SUSET, WAL_SETTINGS,
gettext_noop("Sets the minimum number of concurrent open transactions "
"required before performing commit_delay."),
NULL
@@ -2970,7 +2970,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"extra_float_digits", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"extra_float_digits", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the number of digits displayed for floating-point values."),
gettext_noop("This affects real, double precision, and geometric data types. "
"A zero or negative parameter value is added to the standard "
@@ -3032,7 +3032,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"log_parameter_max_length_on_error", PGC_USERSET, LOGGING_WHAT,
+ {"log_parameter_max_length_on_error", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Sets the maximum length in bytes of data logged for bind "
"parameter values when logging statements, on error."),
gettext_noop("-1 to print values in full."),
@@ -3077,7 +3077,7 @@ static struct config_int ConfigureNamesInt[] =
{
{"effective_io_concurrency",
- PGC_USERSET,
+ PGC_SUSET,
RESOURCES_ASYNCHRONOUS,
gettext_noop("Number of simultaneous requests that can be handled efficiently by the disk subsystem."),
NULL,
@@ -3095,7 +3095,7 @@ static struct config_int ConfigureNamesInt[] =
{
{"maintenance_io_concurrency",
- PGC_USERSET,
+ PGC_SUSET,
RESOURCES_ASYNCHRONOUS,
gettext_noop("A variant of effective_io_concurrency that is used for maintenance work."),
NULL,
@@ -3112,7 +3112,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"backend_flush_after", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"backend_flush_after", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Number of pages after which previously performed writes are flushed to disk."),
NULL,
GUC_UNIT_BLOCKS
@@ -3344,7 +3344,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"max_parallel_maintenance_workers", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"max_parallel_maintenance_workers", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Sets the maximum number of parallel processes per maintenance operation."),
NULL
},
@@ -3354,7 +3354,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"max_parallel_workers_per_gather", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"max_parallel_workers_per_gather", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Sets the maximum number of parallel processes per executor node."),
NULL,
GUC_EXPLAIN
@@ -3365,7 +3365,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"max_parallel_workers", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"max_parallel_workers", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Sets the maximum number of parallel workers that can be active at one time."),
NULL,
GUC_EXPLAIN
@@ -3398,7 +3398,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_keepalives_idle", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_keepalives_idle", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Time between issuing TCP keepalives."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_S
@@ -3409,7 +3409,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_keepalives_interval", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_keepalives_interval", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Time between TCP keepalive retransmits."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_S
@@ -3420,7 +3420,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"ssl_renegotiation_limit", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"ssl_renegotiation_limit", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("SSL renegotiation is no longer supported; this can only be 0."),
NULL,
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE,
@@ -3431,7 +3431,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_keepalives_count", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_keepalives_count", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Maximum number of TCP keepalive retransmits."),
gettext_noop("This controls the number of consecutive keepalive retransmits that can be "
"lost before a connection is considered dead. A value of 0 uses the "
@@ -3443,7 +3443,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"gin_fuzzy_search_limit", PGC_USERSET, CLIENT_CONN_OTHER,
+ {"gin_fuzzy_search_limit", PGC_SUSET, CLIENT_CONN_OTHER,
gettext_noop("Sets the maximum allowed result for exact search by GIN."),
NULL,
0
@@ -3454,7 +3454,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"effective_cache_size", PGC_USERSET, QUERY_TUNING_COST,
+ {"effective_cache_size", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's assumption about the total size of the data caches."),
gettext_noop("That is, the total size of the caches (kernel cache and shared buffers) used for PostgreSQL data files. "
"This is measured in disk pages, which are normally 8 kB each."),
@@ -3466,7 +3466,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"min_parallel_table_scan_size", PGC_USERSET, QUERY_TUNING_COST,
+ {"min_parallel_table_scan_size", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the minimum amount of table data for a parallel scan."),
gettext_noop("If the planner estimates that it will read a number of table pages too small to reach this limit, a parallel scan will not be considered."),
GUC_UNIT_BLOCKS | GUC_EXPLAIN,
@@ -3477,7 +3477,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"min_parallel_index_scan_size", PGC_USERSET, QUERY_TUNING_COST,
+ {"min_parallel_index_scan_size", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the minimum amount of index data for a parallel scan."),
gettext_noop("If the planner estimates that it will read a number of index pages too small to reach this limit, a parallel scan will not be considered."),
GUC_UNIT_BLOCKS | GUC_EXPLAIN,
@@ -3522,7 +3522,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"gin_pending_list_limit", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"gin_pending_list_limit", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum size of the pending list for GIN index."),
NULL,
GUC_UNIT_KB
@@ -3533,7 +3533,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_user_timeout", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_user_timeout", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("TCP user timeout."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_MS
@@ -3578,7 +3578,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"client_connection_check_interval", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"client_connection_check_interval", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Sets the time interval between checks for disconnection while running queries."),
NULL,
GUC_UNIT_MS
@@ -3610,7 +3610,7 @@ static struct config_int ConfigureNamesInt[] =
static struct config_real ConfigureNamesReal[] =
{
{
- {"seq_page_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"seq_page_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of a "
"sequentially fetched disk page."),
NULL,
@@ -3621,7 +3621,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"random_page_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"random_page_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of a "
"nonsequentially fetched disk page."),
NULL,
@@ -3632,7 +3632,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"cpu_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"cpu_tuple_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"processing each tuple (row)."),
NULL,
@@ -3643,7 +3643,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"cpu_index_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"cpu_index_tuple_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"processing each index entry during an index scan."),
NULL,
@@ -3654,7 +3654,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"cpu_operator_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"cpu_operator_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"processing each operator or function call."),
NULL,
@@ -3665,7 +3665,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"parallel_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"parallel_tuple_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"passing each tuple (row) from worker to leader backend."),
NULL,
@@ -3676,7 +3676,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"parallel_setup_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"parallel_setup_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"starting up worker processes for parallel query."),
NULL,
@@ -3688,7 +3688,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"jit_above_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"jit_above_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Perform JIT compilation if query is more expensive."),
gettext_noop("-1 disables JIT compilation."),
GUC_EXPLAIN
@@ -3699,7 +3699,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"jit_optimize_above_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"jit_optimize_above_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Optimize JIT-compiled functions if query is more expensive."),
gettext_noop("-1 disables optimization."),
GUC_EXPLAIN
@@ -3710,7 +3710,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"jit_inline_above_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"jit_inline_above_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Perform JIT inlining if query is more expensive."),
gettext_noop("-1 disables inlining."),
GUC_EXPLAIN
@@ -3721,7 +3721,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"cursor_tuple_fraction", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"cursor_tuple_fraction", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the planner's estimate of the fraction of "
"a cursor's rows that will be retrieved."),
NULL,
@@ -3733,7 +3733,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"geqo_selection_bias", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_selection_bias", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: selective pressure within the population."),
NULL,
GUC_EXPLAIN
@@ -3744,7 +3744,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"geqo_seed", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_seed", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: seed for random path selection."),
NULL,
GUC_EXPLAIN
@@ -3755,7 +3755,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"hash_mem_multiplier", PGC_USERSET, RESOURCES_MEM,
+ {"hash_mem_multiplier", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Multiple of work_mem to use for hash tables."),
NULL,
GUC_EXPLAIN
@@ -3776,7 +3776,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"seed", PGC_USERSET, UNGROUPED,
+ {"seed", PGC_SUSET, UNGROUPED,
gettext_noop("Sets the seed for random-number generation."),
NULL,
GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -3787,7 +3787,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"vacuum_cost_delay", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_delay", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost delay in milliseconds."),
NULL,
GUC_UNIT_MS
@@ -4037,7 +4037,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"DateStyle", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"DateStyle", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the display format for date and time values."),
gettext_noop("Also controls interpretation of ambiguous "
"date inputs."),
@@ -4049,7 +4049,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"default_table_access_method", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_table_access_method", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default table access method for new tables."),
NULL,
GUC_IS_NAME
@@ -4060,7 +4060,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"default_tablespace", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_tablespace", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default tablespace to create tables and indexes in."),
gettext_noop("An empty string selects the database's default tablespace."),
GUC_IS_NAME
@@ -4071,7 +4071,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"temp_tablespaces", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE
@@ -4151,7 +4151,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"lc_monetary", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"lc_monetary", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the locale for formatting monetary amounts."),
NULL
},
@@ -4161,7 +4161,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"lc_numeric", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"lc_numeric", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the locale for formatting numbers."),
NULL
},
@@ -4171,7 +4171,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"lc_time", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"lc_time", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the locale for formatting date and time values."),
NULL
},
@@ -4203,7 +4203,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"local_preload_libraries", PGC_USERSET, CLIENT_CONN_PRELOAD,
+ {"local_preload_libraries", PGC_SUSET, CLIENT_CONN_PRELOAD,
gettext_noop("Lists unprivileged shared libraries to preload into each backend."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE
@@ -4214,7 +4214,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"search_path", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"search_path", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the schema search order for names that are not schema-qualified."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE | GUC_EXPLAIN
@@ -4250,7 +4250,7 @@ static struct config_string ConfigureNamesString[] =
{
/* Not for general use --- used by SET ROLE */
- {"role", PGC_USERSET, UNGROUPED,
+ {"role", PGC_SUSET, UNGROUPED,
gettext_noop("Sets the current role."),
NULL,
GUC_IS_NAME | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_NOT_WHILE_SEC_REST
@@ -4329,7 +4329,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"TimeZone", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"TimeZone", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the time zone for displaying and interpreting time stamps."),
NULL,
GUC_REPORT
@@ -4339,7 +4339,7 @@ static struct config_string ConfigureNamesString[] =
check_timezone, assign_timezone, show_timezone
},
{
- {"timezone_abbreviations", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"timezone_abbreviations", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Selects a file of time zone abbreviations."),
NULL
},
@@ -4532,7 +4532,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"default_text_search_config", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"default_text_search_config", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets default text search configuration."),
NULL
},
@@ -4594,7 +4594,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"application_name", PGC_USERSET, LOGGING_WHAT,
+ {"application_name", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Sets the application name to be reported in statistics and logs."),
NULL,
GUC_IS_NAME | GUC_REPORT | GUC_NOT_IN_SAMPLE
@@ -4658,7 +4658,7 @@ static struct config_string ConfigureNamesString[] =
static struct config_enum ConfigureNamesEnum[] =
{
{
- {"backslash_quote", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"backslash_quote", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Sets whether \"\\'\" is allowed in string literals."),
NULL
},
@@ -4668,7 +4668,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"bytea_output", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"bytea_output", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the output format for bytea."),
NULL
},
@@ -4678,7 +4678,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"client_min_messages", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"client_min_messages", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the message levels that are sent to the client."),
gettext_noop("Each level includes all the levels that follow it. The later"
" the level, the fewer messages are sent.")
@@ -4699,7 +4699,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"constraint_exclusion", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"constraint_exclusion", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Enables the planner to use constraints to optimize queries."),
gettext_noop("Table scans will be skipped if their constraints"
" guarantee that no rows match the query."),
@@ -4711,7 +4711,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"default_toast_compression", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_toast_compression", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default compression method for compressible values."),
NULL
},
@@ -4722,7 +4722,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"default_transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_transaction_isolation", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the transaction isolation level of each new transaction."),
NULL
},
@@ -4732,7 +4732,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"transaction_isolation", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the current transaction's isolation level."),
NULL,
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -4743,7 +4743,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"IntervalStyle", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"IntervalStyle", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the display format for interval values."),
NULL,
GUC_REPORT
@@ -4821,7 +4821,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"synchronous_commit", PGC_USERSET, WAL_SETTINGS,
+ {"synchronous_commit", PGC_SUSET, WAL_SETTINGS,
gettext_noop("Sets the current transaction's synchronization level."),
NULL
},
@@ -4928,7 +4928,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"xmlbinary", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"xmlbinary", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets how binary values are to be encoded in XML."),
NULL
},
@@ -4938,7 +4938,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"xmloption", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"xmloption", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets whether XML data in implicit parsing and serialization "
"operations is to be considered as documents or content fragments."),
NULL
@@ -4959,7 +4959,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"force_parallel_mode", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"force_parallel_mode", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Forces use of parallel query facilities."),
gettext_noop("If possible, run query using a parallel worker and with parallel restrictions."),
GUC_NOT_IN_SAMPLE | GUC_EXPLAIN
@@ -4970,7 +4970,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"password_encryption", PGC_USERSET, CONN_AUTH_AUTH,
+ {"password_encryption", PGC_SUSET, CONN_AUTH_AUTH,
gettext_noop("Chooses the algorithm for encrypting passwords."),
NULL
},
@@ -4980,7 +4980,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"plan_cache_mode", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"plan_cache_mode", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Controls the planner's selection of custom or generic plan."),
gettext_noop("Prepared statements can have custom and generic plans, and the planner "
"will attempt to choose which is better. This can be set to override "
@@ -5039,6 +5039,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_setting_acl
+ * entries.
*/
static const char *const map_old_guc_names[] = {
"sort_mem", "work_mem",
@@ -5438,25 +5442,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().)
+ *
+ * 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"
@@ -5473,8 +5481,24 @@ 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;
+}
+
+/*
+ * 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().)
+ */
+static bool
+valid_custom_variable_name(const char *name)
+{
+ int partcnt;
+
+ return (valid_variable_name(name, &partcnt) && partcnt > 1);
}
/*
@@ -7502,6 +7526,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 settingid = get_setting_oid(name, true);
+
+ if (OidIsValid(settingid))
+ {
+ AclResult aclresult;
+
+ aclresult = pg_setting_acl_aclcheck(settingid, GetUserId(),
+ ACL_SET_VALUE);
+
+ if (aclresult == ACLCHECK_OK)
+ break; /* okay */
+ }
+
+ /* No granted privilege */
ereport(elevel,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to set parameter \"%s\"",
@@ -8551,16 +8593,32 @@ 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;
+ Oid settingId;
+
+ settingId = get_setting_oid(name, true);
+ if (!OidIsValid(settingId))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+
+ aclresult = pg_setting_acl_aclcheck(settingId, GetUserId(),
+ ACL_ALTER_SYSTEM);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_SETTING, name);
+ }
+
switch (altersysstmt->setstmt->kind)
{
case VAR_SET_VALUE:
@@ -8753,6 +8811,9 @@ void
ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
{
GucAction action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET;
+ GucContext context;
+ AclResult aclresult;
+ Oid settingId;
/*
* Workers synchronize these parameters at the start of the parallel
@@ -8763,6 +8824,21 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
errmsg("cannot set parameters during a parallel operation")));
+ /*
+ * 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(settingId = get_setting_oid(stmt->name, true)))
+ {
+ aclresult = pg_setting_acl_aclcheck(settingId, GetUserId(),
+ ACL_SET_VALUE);
+ if (aclresult == ACLCHECK_OK)
+ context = PGC_SUSET;
+ }
+
switch (stmt->kind)
{
case VAR_SET_VALUE:
@@ -8771,7 +8847,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;
@@ -8856,7 +8932,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;
@@ -9603,6 +9679,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, false, 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,
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d78e8e67b8..872dd2ddfd 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -164,6 +164,7 @@ static char *features_file;
static char *system_constraints_file;
static char *system_functions_file;
static char *system_views_file;
+static char *setting_privileges_file;
static bool success = false;
static bool made_new_pgdata = false;
static bool found_existing_pgdata = false;
@@ -2452,6 +2453,7 @@ setup_data_file_paths(void)
set_input(&system_constraints_file, "system_constraints.sql");
set_input(&system_functions_file, "system_functions.sql");
set_input(&system_views_file, "system_views.sql");
+ set_input(&setting_privileges_file, "setting_privileges.sql");
if (show_setting || debug)
{
@@ -2480,6 +2482,7 @@ setup_data_file_paths(void)
check_input(system_constraints_file);
check_input(system_functions_file);
check_input(system_views_file);
+ check_input(setting_privileges_file);
}
@@ -2827,6 +2830,8 @@ initialize_data_directory(void)
setup_run_file(cmdfd, system_views_file);
+ setup_run_file(cmdfd, setting_privileges_file);
+
setup_description(cmdfd);
setup_collation(cmdfd);
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index 6086d57cf3..a84efd79e5 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, SETTING 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, "SETTING") == 0)
+ {
+ CONVERT_PRIV('s', "SET VALUE");
+ 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 49bf0907cd..b000d3012a 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -3421,6 +3421,7 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te)
strcmp(type, "SCHEMA") == 0 ||
strcmp(type, "EVENT TRIGGER") == 0 ||
strcmp(type, "FOREIGN DATA WRAPPER") == 0 ||
+ strcmp(type, "SETTING") == 0 ||
strcmp(type, "SERVER") == 0 ||
strcmp(type, "PUBLICATION") == 0 ||
strcmp(type, "SUBSCRIPTION") == 0 ||
@@ -3604,6 +3605,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, "SETTING") == 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 e3ddf19959..2d94935cf1 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -14115,6 +14115,9 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
case DEFACLOBJ_NAMESPACE:
type = "SCHEMAS";
break;
+ case DEFACLOBJ_SETTING:
+ type = "SETTINGS";
+ break;
default:
/* shouldn't get here */
fatal("unrecognized object type in default privileges: %d",
@@ -14158,7 +14161,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, SETTING 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 10383c713f..a20a1062fd 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,72 @@ 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)
+{
+ PQExpBuffer buf = createPQExpBuffer();
+ PGresult *res;
+ int i;
+
+ printfPQExpBuffer(buf, "SELECT string_agg(acl.privilege_type, ', ' ORDER BY acl.privilege_type), "
+ "set_acl.setting, "
+ "grantee.rolname AS grantee, "
+ "acl.is_grantable, "
+ "grantor.rolname AS grantor "
+ "FROM pg_catalog.pg_setting_acl set_acl, "
+ "LATERAL (SELECT * FROM aclexplode(set_acl.setacl)) acl "
+ "JOIN pg_catalog.pg_authid grantee "
+ "ON acl.grantee = grantee.oid "
+ "LEFT JOIN pg_catalog.pg_authid grantor ON "
+ "acl.grantor = grantor.oid "
+ "WHERE acl.grantee > 0 "
+ "GROUP BY setting, grantee.rolname, is_grantable, grantor.rolname"
+ );
+
+ res = executeQuery(conn, buf->data);
+
+ if (PQntuples(res) > 0)
+ fprintf(OPF, "--\n-- Role privileges on configuration parameters\n--\n\n");
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ char *privilege = PQgetvalue(res, i, 0);
+ char *setting = PQgetvalue(res, i, 1);
+ char *grantee = PQgetvalue(res, i, 2);
+ char *grantable = PQgetvalue(res, i, 3);
+
+ fprintf(OPF, "GRANT %s", privilege);
+ fprintf(OPF, " ON %s", setting);
+ fprintf(OPF, " TO %s", fmtId(grantee));
+ if (*grantable == 't')
+ fprintf(OPF, " WITH GRANT OPTION");
+
+ /*
+ * We don't track the grantor very carefully in the backend, so cope
+ * with the possibility that it has been dropped.
+ */
+ if (!PQgetisnull(res, i, 4))
+ {
+ char *grantor = PQgetvalue(res, i, 4);
+
+ fprintf(OPF, " GRANTED BY %s", fmtId(grantor));
+ }
+ fprintf(OPF, ";\n");
+ }
+
+ PQclear(res);
+ destroyPQExpBuffer(buf);
+
+ fprintf(OPF, "\n\n");
+}
+
/*
* Dump tablespaces.
*/
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 344482ec87..a0c3966da9 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -92,6 +92,7 @@ typedef enum ObjectClass
OCLASS_TYPE, /* pg_type */
OCLASS_CAST, /* pg_cast */
OCLASS_COLLATION, /* pg_collation */
+ OCLASS_SETTING, /* pg_setting_acl */
OCLASS_CONSTRAINT, /* pg_constraint */
OCLASS_CONVERSION, /* pg_conversion */
OCLASS_DEFAULT, /* pg_attrdef */
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index 2a79155636..12ec9b0a31 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_SETTING 'c' /* configuration parameter */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 0859dc81ca..368e19d6d3 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7196,6 +7196,25 @@
proname => 'has_type_privilege', provolatile => 's', prorettype => 'bool',
proargtypes => 'oid text', prosrc => 'has_type_privilege_id' },
+{ oid => '8050', descr => 'user privilege on setting by username, setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'name text text', prosrc => 'has_setting_privilege_name_name' },
+{ oid => '8051', descr => 'user privilege on setting by username, setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'name oid text', prosrc => 'has_setting_privilege_name_id' },
+{ oid => '8052', descr => 'user privilege on setting by user oid, setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid text text', prosrc => 'has_setting_privilege_id_name' },
+{ oid => '8053', descr => 'user privilege on setting by user oid, setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid oid text', prosrc => 'has_setting_privilege_id_id' },
+{ oid => '8054', descr => 'current user privilege on setting by setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'text text', prosrc => 'has_setting_privilege_name' },
+{ oid => '8055', descr => 'current user privilege on setting by setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid text', prosrc => 'has_setting_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/catalog/pg_setting_acl.h b/src/include/catalog/pg_setting_acl.h
new file mode 100644
index 0000000000..b7dee55e5c
--- /dev/null
+++ b/src/include/catalog/pg_setting_acl.h
@@ -0,0 +1,63 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_setting_acl.h
+ * definition of the "configuration parameter" system catalog
+ * (pg_setting_acl).
+ *
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_setting_acl.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SETTING_ACL_H
+#define PG_SETTING_ACL_H
+
+#include "catalog/genbki.h"
+#include "catalog/pg_setting_acl_d.h"
+
+/* ----------------
+ * pg_setting_acl definition. cpp turns this into
+ * typedef struct FormData_pg_setting_acl
+ * ----------------
+ */
+CATALOG(pg_setting_acl,8924,SettingAclRelationId) BKI_SHARED_RELATION
+{
+ Oid oid; /* oid */
+ /*
+
+ * Variable-length fields start here, but we allow direct access to
+ * setting.
+ */
+ text setting BKI_FORCE_NOT_NULL;
+
+#ifdef CATALOG_VARLEN
+ /* Access privileges */
+ aclitem setacl[1] BKI_DEFAULT(_null_);
+#endif
+} FormData_pg_setting_acl;
+
+
+/* ----------------
+ * Form_pg_setting_acl corresponds to a pointer to a tuple with
+ * the format of pg_setting_acl relation.
+ * ----------------
+ */
+typedef FormData_pg_setting_acl *Form_pg_setting_acl;
+
+DECLARE_TOAST(pg_setting_acl, 8925, 8926);
+#define PgSettingAclToastTable 8925
+#define PgSettingAclToastIndex 8926
+
+DECLARE_UNIQUE_INDEX(pg_setting_acl_setting_index, 8927, SettingAclSettingIndexId, on pg_setting_acl using btree(setting text_ops));
+DECLARE_UNIQUE_INDEX_PKEY(pg_setting_acl_oid_index, 8928, SettingAclOidIndexId, on pg_setting_acl using btree(oid oid_ops));
+
+extern Oid SettingAclCreate(const char *setting, bool if_not_exists);
+
+#endif /* PG_SETTING_ACL_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 3e9bdc781f..c76f59c6b2 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -92,7 +92,9 @@ 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 N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */
+#define ACL_SET_VALUE (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
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
@@ -1795,6 +1797,7 @@ typedef enum ObjectType
OBJECT_CAST,
OBJECT_COLUMN,
OBJECT_COLLATION,
+ OBJECT_SETTING,
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DEFAULT,
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index bcef7eed2f..f5ed8c2082 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -307,6 +307,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 1ce4c5556e..e6d7761643 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -146,9 +146,11 @@ 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_ALTER_SYSTEM_CHR 'A'
/* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTc"
+#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcsA"
/*
* Bitmasks defining "all rights" for each supported object type
@@ -165,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_SETTING (ACL_SET_VALUE|ACL_ALTER_SYSTEM)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -223,6 +226,8 @@ extern void select_best_grantor(Oid roleId, AclMode privileges,
extern void initialize_acl(void);
+extern Oid get_setting_oid(const char *setting, bool missing_ok);
+
/*
* prototypes for functions in aclchk.c
*/
@@ -243,6 +248,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_setting_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,
@@ -271,6 +278,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_setting_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 6bb81707b0..3bc4dd7861 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -379,6 +379,8 @@ extern int set_config_option(const char *name, const char *value,
GucAction action, bool changeVal, int elevel,
bool is_reload);
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..7a8ba82770 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_setting_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..5961921462 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -89,6 +89,8 @@ enum SysCacheIdentifier
REPLORIGNAME,
RULERELNAME,
SEQRELID,
+ SETTINGNAME,
+ SETTINGOID,
STATEXTDATASTXOID,
STATEXTNAMENSP,
STATEXTOID,
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 a906f8f03d..04fc9c2e25 100644
--- a/src/test/modules/test_pg_dump/t/001_base.pl
+++ b/src/test/modules/test_pg_dump/t/001_base.pl
@@ -317,6 +317,53 @@ my %tests = (
like => { pg_dumpall_globals => 1, },
},
+ 'GRANT ALTER SYSTEM ON ignore_checksum_failure' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT ALTER SYSTEM ON ignore_checksum_failure TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM ON ignore_checksum_failure TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT SET VALUE ON my.missing.guc' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET VALUE ON my.missing.guc TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT SET VALUE ON my\.missing\.guc TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT SET VALUE, ALTER SYSTEM ON something WITH GRANT OPTION' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET VALUE, ALTER SYSTEM ON something TO regress_dump_test_role WITH GRANT OPTION;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM, SET VALUE ON something TO regress_dump_test_role WITH GRANT OPTION GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALTER SYSTEM ON MyReallyLong.ButValidCustom.GucNameThatCannotFit.InNamedata64Byte.Format' => {
+ create_order => 2,
+ create_sql =>
+ # configuration parameters get cased folded
+ 'GRANT ALTER SYSTEM ON MyReallyLong.ButValidCustom.GucNameThatCannotFit.InNamedata64Byte.Format TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM ON myreallylong\.butvalidcustom\.gucnamethatcannotfit\.innamedata64byte\.format TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALTER SYSTEM, SET VALUE ON my.guc TO regress_dump_test_role GRANTED BY CURRENT_ROLE' => {
+ create_order => 2,
+ create_sql =>
+ # GRANTED BY CURRENT_ROLE is allowed for SQL compatibility, but is ignored
+ 'GRANT ALTER SYSTEM, SET VALUE ON my.guc TO regress_dump_test_role GRANTED BY CURRENT_ROLE;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM, SET VALUE ON my\.guc TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
'CREATE SCHEMA public' => {
regexp => qr/^CREATE SCHEMA public;/m,
like => {
diff --git a/src/test/regress/expected/guc_privs.out b/src/test/regress/expected/guc_privs.out
new file mode 100644
index 0000000000..c2033e77d1
--- /dev/null
+++ b/src/test/regress/expected/guc_privs.out
@@ -0,0 +1,780 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform all 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;
+-- Check setting privileges prior to creating any
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+ grantee | setting | privilege_type | is_grantable
+---------+-------------------------------------+----------------+--------------
+ | DateStyle | SET VALUE | f
+ | IntervalStyle | SET VALUE | f
+ | TimeZone | SET VALUE | f
+ | application_name | SET VALUE | f
+ | array_nulls | SET VALUE | f
+ | backend_flush_after | SET VALUE | f
+ | backslash_quote | SET VALUE | f
+ | bytea_output | SET VALUE | f
+ | check_function_bodies | SET VALUE | f
+ | client_connection_check_interval | SET VALUE | f
+ | client_min_messages | SET VALUE | f
+ | commit_siblings | SET VALUE | f
+ | constraint_exclusion | SET VALUE | f
+ | cpu_index_tuple_cost | SET VALUE | f
+ | cpu_operator_cost | SET VALUE | f
+ | cpu_tuple_cost | SET VALUE | f
+ | cursor_tuple_fraction | SET VALUE | f
+ | debug_pretty_print | SET VALUE | f
+ | debug_print_parse | SET VALUE | f
+ | debug_print_plan | SET VALUE | f
+ | debug_print_rewritten | SET VALUE | f
+ | default_statistics_target | SET VALUE | f
+ | default_table_access_method | SET VALUE | f
+ | default_tablespace | SET VALUE | f
+ | default_text_search_config | SET VALUE | f
+ | default_toast_compression | SET VALUE | f
+ | default_transaction_deferrable | SET VALUE | f
+ | default_transaction_isolation | SET VALUE | f
+ | default_transaction_read_only | SET VALUE | f
+ | default_with_oids | SET VALUE | f
+ | effective_cache_size | SET VALUE | f
+ | effective_io_concurrency | SET VALUE | f
+ | enable_async_append | SET VALUE | f
+ | enable_bitmapscan | SET VALUE | f
+ | enable_gathermerge | SET VALUE | f
+ | enable_hashagg | SET VALUE | f
+ | enable_hashjoin | SET VALUE | f
+ | enable_incremental_sort | SET VALUE | f
+ | enable_indexonlyscan | SET VALUE | f
+ | enable_indexscan | SET VALUE | f
+ | enable_material | SET VALUE | f
+ | enable_memoize | SET VALUE | f
+ | enable_mergejoin | SET VALUE | f
+ | enable_nestloop | SET VALUE | f
+ | enable_parallel_append | SET VALUE | f
+ | enable_parallel_hash | SET VALUE | f
+ | enable_partition_pruning | SET VALUE | f
+ | enable_partitionwise_aggregate | SET VALUE | f
+ | enable_partitionwise_join | SET VALUE | f
+ | enable_seqscan | SET VALUE | f
+ | enable_sort | SET VALUE | f
+ | enable_tidscan | SET VALUE | f
+ | escape_string_warning | SET VALUE | f
+ | exit_on_error | SET VALUE | f
+ | extra_float_digits | SET VALUE | f
+ | force_parallel_mode | SET VALUE | f
+ | from_collapse_limit | SET VALUE | f
+ | geqo | SET VALUE | f
+ | geqo_effort | SET VALUE | f
+ | geqo_generations | SET VALUE | f
+ | geqo_pool_size | SET VALUE | f
+ | geqo_seed | SET VALUE | f
+ | geqo_selection_bias | SET VALUE | f
+ | geqo_threshold | SET VALUE | f
+ | gin_fuzzy_search_limit | SET VALUE | f
+ | gin_pending_list_limit | SET VALUE | f
+ | hash_mem_multiplier | SET VALUE | f
+ | idle_in_transaction_session_timeout | SET VALUE | f
+ | idle_session_timeout | SET VALUE | f
+ | jit | SET VALUE | f
+ | jit_above_cost | SET VALUE | f
+ | jit_expressions | SET VALUE | f
+ | jit_inline_above_cost | SET VALUE | f
+ | jit_optimize_above_cost | SET VALUE | f
+ | jit_tuple_deforming | SET VALUE | f
+ | join_collapse_limit | SET VALUE | f
+ | lc_monetary | SET VALUE | f
+ | lc_numeric | SET VALUE | f
+ | lc_time | SET VALUE | f
+ | local_preload_libraries | SET VALUE | f
+ | lock_timeout | SET VALUE | f
+ | log_parameter_max_length_on_error | SET VALUE | f
+ | logical_decoding_work_mem | SET VALUE | f
+ | maintenance_io_concurrency | SET VALUE | f
+ | maintenance_work_mem | SET VALUE | f
+ | max_parallel_maintenance_workers | SET VALUE | f
+ | max_parallel_workers | SET VALUE | f
+ | max_parallel_workers_per_gather | SET VALUE | f
+ | min_parallel_index_scan_size | SET VALUE | f
+ | min_parallel_table_scan_size | SET VALUE | f
+ | optimize_bounded_sort | SET VALUE | f
+ | parallel_leader_participation | SET VALUE | f
+ | parallel_setup_cost | SET VALUE | f
+ | parallel_tuple_cost | SET VALUE | f
+ | password_encryption | SET VALUE | f
+ | plan_cache_mode | SET VALUE | f
+ | quote_all_identifiers | SET VALUE | f
+ | random_page_cost | SET VALUE | f
+ | role | SET VALUE | f
+ | row_security | SET VALUE | f
+ | search_path | SET VALUE | f
+ | seed | SET VALUE | f
+ | seq_page_cost | SET VALUE | f
+ | ssl_renegotiation_limit | SET VALUE | f
+ | standard_conforming_strings | SET VALUE | f
+ | statement_timeout | SET VALUE | f
+ | synchronize_seqscans | SET VALUE | f
+ | synchronous_commit | SET VALUE | f
+ | tcp_keepalives_count | SET VALUE | f
+ | tcp_keepalives_idle | SET VALUE | f
+ | tcp_keepalives_interval | SET VALUE | f
+ | tcp_user_timeout | SET VALUE | f
+ | temp_buffers | SET VALUE | f
+ | temp_tablespaces | SET VALUE | f
+ | timezone_abbreviations | SET VALUE | f
+ | trace_notify | SET VALUE | f
+ | trace_sort | SET VALUE | f
+ | trace_syncscan | SET VALUE | f
+ | transaction_deferrable | SET VALUE | f
+ | transaction_isolation | SET VALUE | f
+ | transaction_read_only | SET VALUE | f
+ | transform_null_equals | SET VALUE | f
+ | vacuum_cost_delay | SET VALUE | f
+ | vacuum_cost_limit | SET VALUE | f
+ | vacuum_cost_page_dirty | SET VALUE | f
+ | vacuum_cost_page_hit | SET VALUE | f
+ | vacuum_cost_page_miss | SET VALUE | f
+ | vacuum_failsafe_age | SET VALUE | f
+ | vacuum_freeze_min_age | SET VALUE | f
+ | vacuum_freeze_table_age | SET VALUE | f
+ | vacuum_multixact_failsafe_age | SET VALUE | f
+ | vacuum_multixact_freeze_min_age | SET VALUE | f
+ | vacuum_multixact_freeze_table_age | SET VALUE | f
+ | wal_sender_timeout | SET VALUE | f
+ | wal_skip_threshold | SET VALUE | f
+ | work_mem | SET VALUE | f
+ | xmlbinary | SET VALUE | f
+ | xmloption | SET VALUE | f
+(138 rows)
+
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+-- Check the new role does not yet have privileges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ f
+(1 row)
+
+-- Check inappropriate and nonsense privilege types
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+-- Grant privileges on settings to the new non-superuser role
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Check setting privileges after creating some
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+ grantee | setting | privilege_type | is_grantable
+-----------------------------+-------------------------------------+----------------+--------------
+ regress_host_resource_admin | autovacuum_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | autovacuum_work_mem | SET VALUE | f
+ regress_host_resource_admin | hash_mem_multiplier | ALTER SYSTEM | f
+ regress_host_resource_admin | hash_mem_multiplier | SET VALUE | f
+ regress_host_resource_admin | logical_decoding_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | logical_decoding_work_mem | SET VALUE | f
+ regress_host_resource_admin | maintenance_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | maintenance_work_mem | SET VALUE | f
+ regress_host_resource_admin | max_stack_depth | ALTER SYSTEM | f
+ regress_host_resource_admin | max_stack_depth | SET VALUE | f
+ regress_host_resource_admin | min_dynamic_shared_memory | ALTER SYSTEM | f
+ regress_host_resource_admin | min_dynamic_shared_memory | SET VALUE | f
+ regress_host_resource_admin | shared_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | shared_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_file_limit | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_file_limit | SET VALUE | f
+ regress_host_resource_admin | work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | work_mem | SET VALUE | f
+ | DateStyle | SET VALUE | f
+ | IntervalStyle | SET VALUE | f
+ | TimeZone | SET VALUE | f
+ | application_name | SET VALUE | f
+ | array_nulls | SET VALUE | f
+ | backend_flush_after | SET VALUE | f
+ | backslash_quote | SET VALUE | f
+ | bytea_output | SET VALUE | f
+ | check_function_bodies | SET VALUE | f
+ | client_connection_check_interval | SET VALUE | f
+ | client_min_messages | SET VALUE | f
+ | commit_siblings | SET VALUE | f
+ | constraint_exclusion | SET VALUE | f
+ | cpu_index_tuple_cost | SET VALUE | f
+ | cpu_operator_cost | SET VALUE | f
+ | cpu_tuple_cost | SET VALUE | f
+ | cursor_tuple_fraction | SET VALUE | f
+ | debug_pretty_print | SET VALUE | f
+ | debug_print_parse | SET VALUE | f
+ | debug_print_plan | SET VALUE | f
+ | debug_print_rewritten | SET VALUE | f
+ | default_statistics_target | SET VALUE | f
+ | default_table_access_method | SET VALUE | f
+ | default_tablespace | SET VALUE | f
+ | default_text_search_config | SET VALUE | f
+ | default_toast_compression | SET VALUE | f
+ | default_transaction_deferrable | SET VALUE | f
+ | default_transaction_isolation | SET VALUE | f
+ | default_transaction_read_only | SET VALUE | f
+ | default_with_oids | SET VALUE | f
+ | effective_cache_size | SET VALUE | f
+ | effective_io_concurrency | SET VALUE | f
+ | enable_async_append | SET VALUE | f
+ | enable_bitmapscan | SET VALUE | f
+ | enable_gathermerge | SET VALUE | f
+ | enable_hashagg | SET VALUE | f
+ | enable_hashjoin | SET VALUE | f
+ | enable_incremental_sort | SET VALUE | f
+ | enable_indexonlyscan | SET VALUE | f
+ | enable_indexscan | SET VALUE | f
+ | enable_material | SET VALUE | f
+ | enable_memoize | SET VALUE | f
+ | enable_mergejoin | SET VALUE | f
+ | enable_nestloop | SET VALUE | f
+ | enable_parallel_append | SET VALUE | f
+ | enable_parallel_hash | SET VALUE | f
+ | enable_partition_pruning | SET VALUE | f
+ | enable_partitionwise_aggregate | SET VALUE | f
+ | enable_partitionwise_join | SET VALUE | f
+ | enable_seqscan | SET VALUE | f
+ | enable_sort | SET VALUE | f
+ | enable_tidscan | SET VALUE | f
+ | escape_string_warning | SET VALUE | f
+ | exit_on_error | SET VALUE | f
+ | extra_float_digits | SET VALUE | f
+ | force_parallel_mode | SET VALUE | f
+ | from_collapse_limit | SET VALUE | f
+ | geqo | SET VALUE | f
+ | geqo_effort | SET VALUE | f
+ | geqo_generations | SET VALUE | f
+ | geqo_pool_size | SET VALUE | f
+ | geqo_seed | SET VALUE | f
+ | geqo_selection_bias | SET VALUE | f
+ | geqo_threshold | SET VALUE | f
+ | gin_fuzzy_search_limit | SET VALUE | f
+ | gin_pending_list_limit | SET VALUE | f
+ | hash_mem_multiplier | SET VALUE | f
+ | idle_in_transaction_session_timeout | SET VALUE | f
+ | idle_session_timeout | SET VALUE | f
+ | jit | SET VALUE | f
+ | jit_above_cost | SET VALUE | f
+ | jit_expressions | SET VALUE | f
+ | jit_inline_above_cost | SET VALUE | f
+ | jit_optimize_above_cost | SET VALUE | f
+ | jit_tuple_deforming | SET VALUE | f
+ | join_collapse_limit | SET VALUE | f
+ | lc_monetary | SET VALUE | f
+ | lc_numeric | SET VALUE | f
+ | lc_time | SET VALUE | f
+ | local_preload_libraries | SET VALUE | f
+ | lock_timeout | SET VALUE | f
+ | log_parameter_max_length_on_error | SET VALUE | f
+ | logical_decoding_work_mem | SET VALUE | f
+ | maintenance_io_concurrency | SET VALUE | f
+ | maintenance_work_mem | SET VALUE | f
+ | max_parallel_maintenance_workers | SET VALUE | f
+ | max_parallel_workers | SET VALUE | f
+ | max_parallel_workers_per_gather | SET VALUE | f
+ | min_parallel_index_scan_size | SET VALUE | f
+ | min_parallel_table_scan_size | SET VALUE | f
+ | optimize_bounded_sort | SET VALUE | f
+ | parallel_leader_participation | SET VALUE | f
+ | parallel_setup_cost | SET VALUE | f
+ | parallel_tuple_cost | SET VALUE | f
+ | password_encryption | SET VALUE | f
+ | plan_cache_mode | SET VALUE | f
+ | quote_all_identifiers | SET VALUE | f
+ | random_page_cost | SET VALUE | f
+ | role | SET VALUE | f
+ | row_security | SET VALUE | f
+ | search_path | SET VALUE | f
+ | seed | SET VALUE | f
+ | seq_page_cost | SET VALUE | f
+ | ssl_renegotiation_limit | SET VALUE | f
+ | standard_conforming_strings | SET VALUE | f
+ | statement_timeout | SET VALUE | f
+ | synchronize_seqscans | SET VALUE | f
+ | synchronous_commit | SET VALUE | f
+ | tcp_keepalives_count | SET VALUE | f
+ | tcp_keepalives_idle | SET VALUE | f
+ | tcp_keepalives_interval | SET VALUE | f
+ | tcp_user_timeout | SET VALUE | f
+ | temp_buffers | SET VALUE | f
+ | temp_tablespaces | SET VALUE | f
+ | timezone_abbreviations | SET VALUE | f
+ | trace_notify | SET VALUE | f
+ | trace_sort | SET VALUE | f
+ | trace_syncscan | SET VALUE | f
+ | transaction_deferrable | SET VALUE | f
+ | transaction_isolation | SET VALUE | f
+ | transaction_read_only | SET VALUE | f
+ | transform_null_equals | SET VALUE | f
+ | vacuum_cost_delay | SET VALUE | f
+ | vacuum_cost_limit | SET VALUE | f
+ | vacuum_cost_page_dirty | SET VALUE | f
+ | vacuum_cost_page_hit | SET VALUE | f
+ | vacuum_cost_page_miss | SET VALUE | f
+ | vacuum_failsafe_age | SET VALUE | f
+ | vacuum_freeze_min_age | SET VALUE | f
+ | vacuum_freeze_table_age | SET VALUE | f
+ | vacuum_multixact_failsafe_age | SET VALUE | f
+ | vacuum_multixact_freeze_min_age | SET VALUE | f
+ | vacuum_multixact_freeze_table_age | SET VALUE | f
+ | wal_sender_timeout | SET VALUE | f
+ | wal_skip_threshold | SET VALUE | f
+ | work_mem | SET VALUE | f
+ | xmlbinary | SET VALUE | f
+ | xmloption | SET VALUE | f
+(158 rows)
+
+-- Check the new role now has privilges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE WITH GRANT OPTION, ALTER SYSTEM WITH GRANT OPTION');
+ has_setting_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_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+SELECT has_setting_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_setting_privilege('regress_host_resource_admin',
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('hash_mem_multiplier', 'set value');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'work_mem'),
+ 'alter system with grant option');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+-- Check setting privileges show up in view
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+ grantee | setting | privilege_type | is_grantable
+-----------------------------+-------------------------------------+----------------+--------------
+ regress_host_resource_admin | autovacuum_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | autovacuum_work_mem | SET VALUE | f
+ regress_host_resource_admin | hash_mem_multiplier | ALTER SYSTEM | f
+ regress_host_resource_admin | hash_mem_multiplier | SET VALUE | f
+ regress_host_resource_admin | logical_decoding_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | logical_decoding_work_mem | SET VALUE | f
+ regress_host_resource_admin | maintenance_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | maintenance_work_mem | SET VALUE | f
+ regress_host_resource_admin | max_stack_depth | ALTER SYSTEM | f
+ regress_host_resource_admin | max_stack_depth | SET VALUE | f
+ regress_host_resource_admin | min_dynamic_shared_memory | ALTER SYSTEM | f
+ regress_host_resource_admin | min_dynamic_shared_memory | SET VALUE | f
+ regress_host_resource_admin | shared_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | shared_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_file_limit | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_file_limit | SET VALUE | f
+ regress_host_resource_admin | work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | work_mem | SET VALUE | f
+ | DateStyle | SET VALUE | f
+ | IntervalStyle | SET VALUE | f
+ | TimeZone | SET VALUE | f
+ | application_name | SET VALUE | f
+ | array_nulls | SET VALUE | f
+ | backend_flush_after | SET VALUE | f
+ | backslash_quote | SET VALUE | f
+ | bytea_output | SET VALUE | f
+ | check_function_bodies | SET VALUE | f
+ | client_connection_check_interval | SET VALUE | f
+ | client_min_messages | SET VALUE | f
+ | commit_siblings | SET VALUE | f
+ | constraint_exclusion | SET VALUE | f
+ | cpu_index_tuple_cost | SET VALUE | f
+ | cpu_operator_cost | SET VALUE | f
+ | cpu_tuple_cost | SET VALUE | f
+ | cursor_tuple_fraction | SET VALUE | f
+ | debug_pretty_print | SET VALUE | f
+ | debug_print_parse | SET VALUE | f
+ | debug_print_plan | SET VALUE | f
+ | debug_print_rewritten | SET VALUE | f
+ | default_statistics_target | SET VALUE | f
+ | default_table_access_method | SET VALUE | f
+ | default_tablespace | SET VALUE | f
+ | default_text_search_config | SET VALUE | f
+ | default_toast_compression | SET VALUE | f
+ | default_transaction_deferrable | SET VALUE | f
+ | default_transaction_isolation | SET VALUE | f
+ | default_transaction_read_only | SET VALUE | f
+ | default_with_oids | SET VALUE | f
+ | effective_cache_size | SET VALUE | f
+ | effective_io_concurrency | SET VALUE | f
+ | enable_async_append | SET VALUE | f
+ | enable_bitmapscan | SET VALUE | f
+ | enable_gathermerge | SET VALUE | f
+ | enable_hashagg | SET VALUE | f
+ | enable_hashjoin | SET VALUE | f
+ | enable_incremental_sort | SET VALUE | f
+ | enable_indexonlyscan | SET VALUE | f
+ | enable_indexscan | SET VALUE | f
+ | enable_material | SET VALUE | f
+ | enable_memoize | SET VALUE | f
+ | enable_mergejoin | SET VALUE | f
+ | enable_nestloop | SET VALUE | f
+ | enable_parallel_append | SET VALUE | f
+ | enable_parallel_hash | SET VALUE | f
+ | enable_partition_pruning | SET VALUE | f
+ | enable_partitionwise_aggregate | SET VALUE | f
+ | enable_partitionwise_join | SET VALUE | f
+ | enable_seqscan | SET VALUE | f
+ | enable_sort | SET VALUE | f
+ | enable_tidscan | SET VALUE | f
+ | escape_string_warning | SET VALUE | f
+ | exit_on_error | SET VALUE | f
+ | extra_float_digits | SET VALUE | f
+ | force_parallel_mode | SET VALUE | f
+ | from_collapse_limit | SET VALUE | f
+ | geqo | SET VALUE | f
+ | geqo_effort | SET VALUE | f
+ | geqo_generations | SET VALUE | f
+ | geqo_pool_size | SET VALUE | f
+ | geqo_seed | SET VALUE | f
+ | geqo_selection_bias | SET VALUE | f
+ | geqo_threshold | SET VALUE | f
+ | gin_fuzzy_search_limit | SET VALUE | f
+ | gin_pending_list_limit | SET VALUE | f
+ | hash_mem_multiplier | SET VALUE | f
+ | idle_in_transaction_session_timeout | SET VALUE | f
+ | idle_session_timeout | SET VALUE | f
+ | jit | SET VALUE | f
+ | jit_above_cost | SET VALUE | f
+ | jit_expressions | SET VALUE | f
+ | jit_inline_above_cost | SET VALUE | f
+ | jit_optimize_above_cost | SET VALUE | f
+ | jit_tuple_deforming | SET VALUE | f
+ | join_collapse_limit | SET VALUE | f
+ | lc_monetary | SET VALUE | f
+ | lc_numeric | SET VALUE | f
+ | lc_time | SET VALUE | f
+ | local_preload_libraries | SET VALUE | f
+ | lock_timeout | SET VALUE | f
+ | log_parameter_max_length_on_error | SET VALUE | f
+ | logical_decoding_work_mem | SET VALUE | f
+ | maintenance_io_concurrency | SET VALUE | f
+ | maintenance_work_mem | SET VALUE | f
+ | max_parallel_maintenance_workers | SET VALUE | f
+ | max_parallel_workers | SET VALUE | f
+ | max_parallel_workers_per_gather | SET VALUE | f
+ | min_parallel_index_scan_size | SET VALUE | f
+ | min_parallel_table_scan_size | SET VALUE | f
+ | optimize_bounded_sort | SET VALUE | f
+ | parallel_leader_participation | SET VALUE | f
+ | parallel_setup_cost | SET VALUE | f
+ | parallel_tuple_cost | SET VALUE | f
+ | password_encryption | SET VALUE | f
+ | plan_cache_mode | SET VALUE | f
+ | quote_all_identifiers | SET VALUE | f
+ | random_page_cost | SET VALUE | f
+ | role | SET VALUE | f
+ | row_security | SET VALUE | f
+ | search_path | SET VALUE | f
+ | seed | SET VALUE | f
+ | seq_page_cost | SET VALUE | f
+ | ssl_renegotiation_limit | SET VALUE | f
+ | standard_conforming_strings | SET VALUE | f
+ | statement_timeout | SET VALUE | f
+ | synchronize_seqscans | SET VALUE | f
+ | synchronous_commit | SET VALUE | f
+ | tcp_keepalives_count | SET VALUE | f
+ | tcp_keepalives_idle | SET VALUE | f
+ | tcp_keepalives_interval | SET VALUE | f
+ | tcp_user_timeout | SET VALUE | f
+ | temp_buffers | SET VALUE | f
+ | temp_tablespaces | SET VALUE | f
+ | timezone_abbreviations | SET VALUE | f
+ | trace_notify | SET VALUE | f
+ | trace_sort | SET VALUE | f
+ | trace_syncscan | SET VALUE | f
+ | transaction_deferrable | SET VALUE | f
+ | transaction_isolation | SET VALUE | f
+ | transaction_read_only | SET VALUE | f
+ | transform_null_equals | SET VALUE | f
+ | vacuum_cost_delay | SET VALUE | f
+ | vacuum_cost_limit | SET VALUE | f
+ | vacuum_cost_page_dirty | SET VALUE | f
+ | vacuum_cost_page_hit | SET VALUE | f
+ | vacuum_cost_page_miss | SET VALUE | f
+ | vacuum_failsafe_age | SET VALUE | f
+ | vacuum_freeze_min_age | SET VALUE | f
+ | vacuum_freeze_table_age | SET VALUE | f
+ | vacuum_multixact_failsafe_age | SET VALUE | f
+ | vacuum_multixact_freeze_min_age | SET VALUE | f
+ | vacuum_multixact_freeze_table_age | SET VALUE | f
+ | wal_sender_timeout | SET VALUE | f
+ | wal_skip_threshold | SET VALUE | f
+ | work_mem | SET VALUE | f
+ | xmlbinary | SET VALUE | f
+ | xmloption | SET VALUE | f
+(158 rows)
+
+-- 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
+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
+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 setting temp_buffers
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting logical_decoding_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting autovacuum_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting temp_file_limit
+-- Use "revoke" to remove the privileges and allow the role to be dropped
+REVOKE SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 setting temp_buffers
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting logical_decoding_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting autovacuum_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting 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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 setting temp_buffers
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting logical_decoding_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting autovacuum_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting 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
+-- Create non-superuser with privileges to configure plgsql custom variables
+CREATE ROLE regress_plpgsql_admin NOSUPERUSER;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin;
+SET plpgsql.extra_errors TO 'all';
+SET plpgsql.extra_warnings TO 'all';
+RESET plpgsql.extra_errors;
+RESET plpgsql.extra_warnings;
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all';
+ERROR: permission denied to set parameter "plpgsql.extra_errors"
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all';
+ERROR: permission denied to set parameter "plpgsql.extra_warnings"
+ALTER SYSTEM RESET plpgsql.extra_errors;
+ERROR: permission denied to set parameter "plpgsql.extra_errors"
+ALTER SYSTEM RESET plpgsql.extra_warnings;
+ERROR: permission denied to set parameter "plpgsql.extra_warnings"
+SET SESSION AUTHORIZATION regress_admin;
+GRANT SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+TO regress_plpgsql_admin;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin; -- ok
+SET plpgsql.extra_errors TO 'all'; -- ok
+SET plpgsql.extra_warnings TO 'all'; -- ok
+RESET plpgsql.extra_errors; -- ok
+RESET plpgsql.extra_warnings; -- ok
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all'; -- ok
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all'; -- ok
+ALTER SYSTEM RESET plpgsql.extra_errors; -- ok
+ALTER SYSTEM RESET plpgsql.extra_warnings; -- ok
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_plpgsql_admin; -- fail, privileges remain
+ERROR: role "regress_plpgsql_admin" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting plpgsql.extra_warnings
+privileges for setting plpgsql.extra_errors
+REVOKE SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+FROM regress_plpgsql_admin;
+DROP ROLE regress_plpgsql_admin; -- ok
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 291e21d7a6..1c762cea54 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -54,6 +54,33 @@ REVOKE pg_read_all_settings FROM regress_priv_user8;
DROP USER regress_priv_user10;
DROP USER regress_priv_user9;
DROP USER regress_priv_user8;
+GRANT SET VALUE ON enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON enable_nestloop TO regress_priv_user6;
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+GRANT ALTER SYSTEM ON enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON "" TO regress_priv_user7; -- bad name
+ERROR: zero-length delimited identifier at or near """"
+LINE 1: GRANT ALTER SYSTEM ON "" TO regress_priv_user7;
+ ^
+GRANT ALTER SYSTEM ON " " TO regress_priv_user7; -- bad name
+ERROR: invalid setting name " "
+GRANT ALTER SYSTEM ON " foo " TO regress_priv_user7; -- bad name
+ERROR: invalid setting name " foo "
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
ALTER GROUP regress_priv_group1 ADD USER regress_priv_user4;
@@ -2355,10 +2382,32 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+ERROR: role "regress_priv_user6" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting enable_memoize
+privileges for setting enable_nestloop
+DROP USER regress_priv_user7; -- privileges remain
+ERROR: role "regress_priv_user7" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting enable_seqscan
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for table persons2
+privileges for setting no_such_param
+privileges for setting no_such_extension.no_such_param
+privileges for setting no_such_extension.no_such_param.longer.than.maximum.namedata.length
DROP USER regress_priv_user8; -- does not exist
ERROR: role "regress_priv_user8" does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+DROP USER regress_priv_user7; -- ok
+REVOKE SET VALUE ON enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON enable_nestloop FROM regress_priv_user6;
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
CREATE TABLE lock_table (a int);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index d652f7b5fb..14a6f4c0b4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1708,6 +1708,19 @@ pg_sequences| SELECT n.nspname AS schemaname,
JOIN pg_class c ON ((c.oid = s.seqrelid)))
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char"));
+pg_setting_privileges| SELECT grantor.rolname AS grantor,
+ grantee.rolname AS grantee,
+ set_acl.setting,
+ acl.privilege_type,
+ acl.is_grantable
+ FROM pg_setting_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_settings| SELECT a.name,
a.setting,
a.unit,
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 63706a28cc..4a8208c789 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -147,6 +147,7 @@ pg_replication_origin|t
pg_rewrite|t
pg_seclabel|t
pg_sequence|t
+pg_setting_acl|t
pg_shdepend|t
pg_shdescription|t
pg_shseclabel|t
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 861c30a73a..9b85253532 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -89,7 +89,7 @@ test: brin_bloom brin_multi
# ----------
# Another group of parallel tests
# ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role guc_privs
# rules cannot run concurrently with any test that creates
# a view or rule in the public schema
diff --git a/src/test/regress/sql/guc_privs.sql b/src/test/regress/sql/guc_privs.sql
new file mode 100644
index 0000000000..076c136563
--- /dev/null
+++ b/src/test/regress/sql/guc_privs.sql
@@ -0,0 +1,185 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform all 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;
+-- Check setting privileges prior to creating any
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+-- Check the new role does not yet have privileges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+-- Check inappropriate and nonsense privilege types
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+-- Grant privileges on settings to the new non-superuser role
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Check setting privileges after creating some
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+-- Check the new role now has privilges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE 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_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER WITH GRANT OPTION');
+-- Check other function signatures
+SELECT has_setting_privilege('regress_host_resource_admin',
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET VALUE');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+SELECT has_setting_privilege('hash_mem_multiplier', 'set value');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'work_mem'),
+ 'alter system with grant option');
+-- Check setting privileges show up in view
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+-- 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
+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
+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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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
+-- Create non-superuser with privileges to configure plgsql custom variables
+CREATE ROLE regress_plpgsql_admin NOSUPERUSER;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin;
+SET plpgsql.extra_errors TO 'all';
+SET plpgsql.extra_warnings TO 'all';
+RESET plpgsql.extra_errors;
+RESET plpgsql.extra_warnings;
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all';
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all';
+ALTER SYSTEM RESET plpgsql.extra_errors;
+ALTER SYSTEM RESET plpgsql.extra_warnings;
+SET SESSION AUTHORIZATION regress_admin;
+GRANT SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+TO regress_plpgsql_admin;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin; -- ok
+SET plpgsql.extra_errors TO 'all'; -- ok
+SET plpgsql.extra_warnings TO 'all'; -- ok
+RESET plpgsql.extra_errors; -- ok
+RESET plpgsql.extra_warnings; -- ok
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all'; -- ok
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all'; -- ok
+ALTER SYSTEM RESET plpgsql.extra_errors; -- ok
+ALTER SYSTEM RESET plpgsql.extra_warnings; -- ok
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_plpgsql_admin; -- fail, privileges remain
+REVOKE SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+FROM regress_plpgsql_admin;
+DROP ROLE regress_plpgsql_admin; -- ok
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index c8c545b64c..ddbf6afa44 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -66,6 +66,33 @@ DROP USER regress_priv_user10;
DROP USER regress_priv_user9;
DROP USER regress_priv_user8;
+GRANT SET VALUE ON enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON enable_nestloop TO regress_priv_user6;
+
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+
+GRANT ALTER SYSTEM ON enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON "" TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON " " TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON " foo " TO regress_priv_user7; -- bad name
+
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
+
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
@@ -1426,10 +1453,23 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+DROP USER regress_priv_user7; -- privileges remain
DROP USER regress_priv_user8; -- does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+DROP USER regress_priv_user7; -- ok
+
+REVOKE SET VALUE ON enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON enable_nestloop FROM regress_priv_user6;
+
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
--
2.21.1 (Apple Git-122.3)
On Jan 26, 2022, at 7:24 AM, Mark Dilger <mark.dilger@enterprisedb.com> wrote:
Rebased:
<v7-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-SET-for-gucs.patch>
Hackers,
Joshua and I had some off-list back-and-forth on this patch and I think the attached v8 patch meets his needs. Specifically, v8 adds support for object access hooks in the case of SET and ALTER SYSTEM SET of a guc variable.
The way InvokeObjectPostAlterHookArg() is written, it expects some things like a Oid that we don't always have. I abused the parameters of that function to pass as much information as possible. Anyone wanting to review this might consider how that is coded.
Joshua, this is byte-for-byte the same patch that you already reviewed off-list (or I attached the wrong patch!) If you are satisfied, could you set the status to ready-for-committer?
Attachments:
v8-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-SET-for-gucs.patchapplication/octet-stream; name=v8-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-SET-for-gucs.patch; x-unix-mode=0644Download
From 5bf69568fa712bd0c4ac9c79ee28a9ddcb73f232 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Wed, 26 Jan 2022 07:19:37 -0800
Subject: [PATCH v8] Allow GRANT of SET and ALTER SYSTEM SET for gucs
Allow granting of privilege to set or alter system set variables
which otherwise can only be managed by superusers. Each
(role,variable,privilege) triple is independently grantable, so a
user may be granted privilege to SET but not to ALTER SYSTEM SET on
a variable, or vice versa. The privilege to SET a userset variable
may be granted, though doing so has no practical effect, since any
role can set userset variables anyway. Worse, there is no way to
revoke the privilege to SET a userset variable. To remedy that,
most core userset variables have been changed to suset, with
explicit grants to set the variable to public.
---
contrib/pg_trgm/Makefile | 6 +-
contrib/pg_trgm/pg_trgm--1.6--1.7.sql | 8 +
contrib/pg_trgm/pg_trgm.control | 2 +-
contrib/postgres_fdw/Makefile | 3 +-
contrib/postgres_fdw/option.c | 2 +-
.../postgres_fdw/postgres_fdw--1.1--1.2.sql | 6 +
contrib/postgres_fdw/postgres_fdw.control | 2 +-
contrib/sepgsql/hooks.c | 2 +-
contrib/sepgsql/sepgsql.sql.in | 1 +
doc/src/sgml/catalogs.sgml | 75 +-
doc/src/sgml/ddl.sgml | 41 +-
doc/src/sgml/func.sgml | 20 +-
doc/src/sgml/ref/grant.sgml | 7 +
doc/src/sgml/ref/set.sgml | 5 +-
src/backend/catalog/Makefile | 6 +-
src/backend/catalog/aclchk.c | 254 ++++++
src/backend/catalog/catalog.c | 6 +
src/backend/catalog/dependency.c | 6 +
src/backend/catalog/objectaddress.c | 46 ++
src/backend/catalog/pg_setting_acl.c | 122 +++
src/backend/catalog/setting_privileges.sql | 62 ++
src/backend/catalog/system_views.sql | 13 +
src/backend/commands/alter.c | 1 +
src/backend/commands/dropcmds.c | 7 +
src/backend/commands/event_trigger.c | 6 +
src/backend/commands/seclabel.c | 1 +
src/backend/commands/tablecmds.c | 1 +
src/backend/parser/gram.y | 99 ++-
src/backend/utils/adt/acl.c | 258 ++++++
src/backend/utils/cache/lsyscache.c | 20 +
src/backend/utils/cache/syscache.c | 23 +
src/backend/utils/misc/guc.c | 432 ++++++----
src/bin/initdb/initdb.c | 5 +
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 | 71 ++
src/include/catalog/dependency.h | 1 +
src/include/catalog/pg_default_acl.h | 1 +
src/include/catalog/pg_proc.dat | 19 +
src/include/catalog/pg_setting_acl.h | 63 ++
src/include/nodes/parsenodes.h | 5 +-
src/include/parser/kwlist.h | 1 +
src/include/utils/acl.h | 10 +-
src/include/utils/guc.h | 2 +
src/include/utils/lsyscache.h | 1 +
src/include/utils/syscache.h | 2 +
src/test/modules/test_pg_dump/t/001_base.pl | 47 ++
src/test/regress/expected/guc_privs.out | 780 ++++++++++++++++++
src/test/regress/expected/privileges.out | 53 +-
src/test/regress/expected/rules.out | 13 +
src/test/regress/expected/sanity_check.out | 1 +
src/test/regress/parallel_schedule | 2 +-
src/test/regress/sql/guc_privs.sql | 185 +++++
src/test/regress/sql/privileges.sql | 44 +-
55 files changed, 2674 insertions(+), 189 deletions(-)
create mode 100644 contrib/pg_trgm/pg_trgm--1.6--1.7.sql
create mode 100644 contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql
create mode 100644 src/backend/catalog/pg_setting_acl.c
create mode 100644 src/backend/catalog/setting_privileges.sql
create mode 100644 src/include/catalog/pg_setting_acl.h
create mode 100644 src/test/regress/expected/guc_privs.out
create mode 100644 src/test/regress/sql/guc_privs.sql
diff --git a/contrib/pg_trgm/Makefile b/contrib/pg_trgm/Makefile
index 1fbdc9ec1e..8fac4f6289 100644
--- a/contrib/pg_trgm/Makefile
+++ b/contrib/pg_trgm/Makefile
@@ -9,9 +9,9 @@ OBJS = \
trgm_regexp.o
EXTENSION = pg_trgm
-DATA = pg_trgm--1.5--1.6.sql pg_trgm--1.4--1.5.sql pg_trgm--1.3--1.4.sql \
- pg_trgm--1.3.sql pg_trgm--1.2--1.3.sql pg_trgm--1.1--1.2.sql \
- pg_trgm--1.0--1.1.sql
+DATA = pg_trgm--1.6--1.7.sql pg_trgm--1.5--1.6.sql pg_trgm--1.4--1.5.sql \
+ pg_trgm--1.3--1.4.sql pg_trgm--1.3.sql pg_trgm--1.2--1.3.sql \
+ pg_trgm--1.1--1.2.sql pg_trgm--1.0--1.1.sql
PGFILEDESC = "pg_trgm - trigram matching"
REGRESS = pg_trgm pg_word_trgm pg_strict_word_trgm
diff --git a/contrib/pg_trgm/pg_trgm--1.6--1.7.sql b/contrib/pg_trgm/pg_trgm--1.6--1.7.sql
new file mode 100644
index 0000000000..06b783acbd
--- /dev/null
+++ b/contrib/pg_trgm/pg_trgm--1.6--1.7.sql
@@ -0,0 +1,8 @@
+/* contrib/pg_trgm/pg_trgm--1.6--1.7.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_trgm UPDATE TO '1.7'" to load this file. \quit
+
+GRANT SET VALUE ON "pg_trgm.similarity_threshold" TO public;
+GRANT SET VALUE ON "pg_trgm.word_similarity_threshold" TO public;
+GRANT SET VALUE ON "pg_trgm.strict_word_similarity_threshold" TO public;
diff --git a/contrib/pg_trgm/pg_trgm.control b/contrib/pg_trgm/pg_trgm.control
index 1d6a9ddf25..6e3ee43c51 100644
--- a/contrib/pg_trgm/pg_trgm.control
+++ b/contrib/pg_trgm/pg_trgm.control
@@ -1,6 +1,6 @@
# pg_trgm extension
comment = 'text similarity measurement and index searching based on trigrams'
-default_version = '1.6'
+default_version = '1.7'
module_pathname = '$libdir/pg_trgm'
relocatable = true
trusted = true
diff --git a/contrib/postgres_fdw/Makefile b/contrib/postgres_fdw/Makefile
index c1b0cad453..3564015ee8 100644
--- a/contrib/postgres_fdw/Makefile
+++ b/contrib/postgres_fdw/Makefile
@@ -14,7 +14,8 @@ PG_CPPFLAGS = -I$(libpq_srcdir)
SHLIB_LINK_INTERNAL = $(libpq)
EXTENSION = postgres_fdw
-DATA = postgres_fdw--1.0.sql postgres_fdw--1.0--1.1.sql
+DATA = postgres_fdw--1.0.sql postgres_fdw--1.0--1.1.sql \
+ postgres_fdw--1.1--1.2.sql
REGRESS = postgres_fdw
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index fc3ce6a53a..04250cc401 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -526,7 +526,7 @@ _PG_init(void)
NULL,
&pgfdw_application_name,
NULL,
- PGC_USERSET,
+ PGC_SUSET,
0,
NULL,
NULL,
diff --git a/contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql b/contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql
new file mode 100644
index 0000000000..b174e04283
--- /dev/null
+++ b/contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql
@@ -0,0 +1,6 @@
+/* contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION postgres_fdw UPDATE TO '1.2'" to load this file. \quit
+
+GRANT SET VALUE ON "postgres_fdw.application_name" TO public;
diff --git a/contrib/postgres_fdw/postgres_fdw.control b/contrib/postgres_fdw/postgres_fdw.control
index d489382064..a4b800be4f 100644
--- a/contrib/postgres_fdw/postgres_fdw.control
+++ b/contrib/postgres_fdw/postgres_fdw.control
@@ -1,5 +1,5 @@
# postgres_fdw extension
comment = 'foreign-data wrapper for remote PostgreSQL servers'
-default_version = '1.1'
+default_version = '1.2'
module_pathname = '$libdir/postgres_fdw'
relocatable = true
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index d71c802106..8fbddd58ec 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -449,7 +449,7 @@ _PG_init(void)
NULL,
&sepgsql_debug_audit,
false,
- PGC_USERSET,
+ PGC_SUSET,
GUC_NOT_IN_SAMPLE,
NULL,
NULL,
diff --git a/contrib/sepgsql/sepgsql.sql.in b/contrib/sepgsql/sepgsql.sql.in
index 917d12dbbe..4dce769306 100644
--- a/contrib/sepgsql/sepgsql.sql.in
+++ b/contrib/sepgsql/sepgsql.sql.in
@@ -34,4 +34,5 @@ CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_setcon(text) RETURNS bool AS 'MODU
CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_mcstrans_in(text) RETURNS text AS 'MODULE_PATHNAME', 'sepgsql_mcstrans_in' LANGUAGE C STRICT;
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;
+GRANT SET VALUE ON "sepgsql.debug_audit" TO public;
SELECT sepgsql_restorecon(NULL);
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 7d5b0b1656..8b43c1788c 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -105,6 +105,11 @@
<entry>collations (locale information)</entry>
</row>
+ <row>
+ <entry><link linkend="catalog-pg-setting-acl"><structname>pg_setting_acl</structname></link></entry>
+ <entry>configuration parameters which have privileges granted to roles</entry>
+ </row>
+
<row>
<entry><link linkend="catalog-pg-constraint"><structname>pg_constraint</structname></link></entry>
<entry>check constraints, unique constraints, primary key constraints, foreign key constraints</entry>
@@ -2423,6 +2428,64 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</para>
</sect1>
+ <sect1 id="catalog-pg-setting-acl">
+ <title><structname>pg_setting_acl</structname></title>
+
+ <indexterm zone="catalog-pg-setting-acl">
+ <primary>pg_setting_acl</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_setting_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_setting_acl</structname>
+ is shared across all databases of a cluster: there is only one
+ copy of <structname>pg_setting_acl</structname> per cluster, not
+ one per database.
+ </para>
+
+ <table>
+ <title><structname>pg_setting_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>setting</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>
@@ -12507,11 +12570,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 settings 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 VALUE</literal> privilege granted
+ on the setting 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 7cf0f0da3b..88de571382 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -1672,7 +1672,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 VALUE</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.
@@ -1940,6 +1941,28 @@ REVOKE ALL ON accounts FROM PUBLIC;
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>SET VALUE</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>SET</command> command to
+ change <literal>superuser</literal> run-time configuration parameters.
+ (Any user can set <literal>user</literal> run-time configuration
+ parameters.)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ALTER SYSTEM</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>ALTER SYSTEM
+ SET</command> command to change server configuration parameters.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
The privileges required by other commands are listed on the
@@ -2078,6 +2101,16 @@ REVOKE ALL ON accounts FROM PUBLIC;
<literal>TYPE</literal>
</entry>
</row>
+ <row>
+ <entry><literal>SET VALUE</literal></entry>
+ <entry><literal>s</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
+ <row>
+ <entry><literal>ALTER SYSTEM</literal></entry>
+ <entry><literal>A</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -2184,6 +2217,12 @@ REVOKE ALL ON accounts FROM PUBLIC;
<entry><literal>U</literal></entry>
<entry><literal>\dT+</literal></entry>
</row>
+ <row>
+ <entry><literal>Configuration parameter</literal></entry>
+ <entry><literal>sA</literal></entry>
+ <entry>none</entry>
+ <entry><literal></literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 0ee6974f1c..6029dcec5d 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');
@@ -22957,6 +22956,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_setting_privilege</primary>
+ </indexterm>
+ <function>has_setting_privilege</function> (
+ <optional> <parameter>user</parameter> <type>name</type> or <type>oid</type>, </optional>
+ <parameter>setting</parameter> <type>text</type> or <type>oid</type>,
+ <parameter>privilege</parameter> <type>text</type> )
+ <returnvalue>boolean</returnvalue>
+ </para>
+ <para>
+ Does user have privilege for setting?
+ Allowable privilege types are <literal>SET VALUE</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 a897712de2..21ec320a8d 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 VALUE | ALTER SYSTEM }
+ ON <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 VALUE</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/set.sgml b/doc/src/sgml/ref/set.sgml
index 339ee9eec9..a08057d1d1 100644
--- a/doc/src/sgml/ref/set.sgml
+++ b/doc/src/sgml/ref/set.sgml
@@ -34,8 +34,9 @@ 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.)
+ (But some require either superuser privileges or granted <literal>SET
+ VALUE</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>
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index df5268fbc3..e9efab5d30 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -28,6 +28,7 @@ OBJS = \
pg_cast.o \
pg_class.o \
pg_collation.o \
+ pg_setting_acl.o \
pg_constraint.o \
pg_conversion.o \
pg_db_role_setting.o \
@@ -54,7 +55,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_setting_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 \
@@ -126,6 +127,7 @@ install-data: bki-stamp installdirs
$(INSTALL_DATA) $(srcdir)/system_functions.sql '$(DESTDIR)$(datadir)/system_functions.sql'
$(INSTALL_DATA) $(srcdir)/system_views.sql '$(DESTDIR)$(datadir)/system_views.sql'
$(INSTALL_DATA) $(srcdir)/information_schema.sql '$(DESTDIR)$(datadir)/information_schema.sql'
+ $(INSTALL_DATA) $(srcdir)/setting_privileges.sql '$(DESTDIR)$(datadir)/setting_privileges.sql'
$(INSTALL_DATA) $(srcdir)/sql_features.txt '$(DESTDIR)$(datadir)/sql_features.txt'
installdirs:
@@ -133,7 +135,7 @@ installdirs:
.PHONY: uninstall-data
uninstall-data:
- rm -f $(addprefix '$(DESTDIR)$(datadir)'/, postgres.bki system_constraints.sql system_functions.sql system_views.sql information_schema.sql sql_features.txt)
+ rm -f $(addprefix '$(DESTDIR)$(datadir)'/, postgres.bki system_constraints.sql system_functions.sql system_views.sql information_schema.sql setting_privileges.sql sql_features.txt)
# postgres.bki, system_constraints.sql, and the generated headers are
# in the distribution tarball, so they are not cleaned here.
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 1dd03a8e51..3f8699b867 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_setting_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_Setting(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_SETTING:
+ whole_mask = ACL_ALL_RIGHTS_SETTING;
+ 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_SETTING:
+ all_privileges = ACL_ALL_RIGHTS_SETTING;
+ errormsg = gettext_noop("invalid privilege type %s for setting");
+ 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_SETTING:
+ ExecGrant_Setting(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -759,6 +771,38 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_SETTING:
+ foreach(cell, objnames)
+ {
+ char *setting = strVal(lfirst(cell));
+ Oid settingid = get_setting_oid(setting, true);
+
+ if (!OidIsValid(settingid))
+ {
+ /*
+ * Lookup the existing entry, or if necessary, add a new
+ * entry for this parameter. Entries only exist for
+ * parameters which currently have, or previously have had,
+ * privileges assigned.
+ *
+ * It is tempting to sanity-check the given configuration
+ * parameter name against known guc names in the guc
+ * tables, but for handling upgrades we need to accept
+ * setting names that do not yet exist.
+ */
+ settingid = SettingAclCreate(setting, 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, settingid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -1494,6 +1538,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case ForeignDataWrapperRelationId:
istmt.objtype = OBJECT_FDW;
break;
+ case SettingAclRelationId:
+ istmt.objtype = OBJECT_SETTING;
+ break;
default:
elog(ERROR, "unexpected object class %u", classid);
break;
@@ -3225,6 +3272,131 @@ ExecGrant_Type(InternalGrant *istmt)
table_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Setting(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_SETTING;
+
+ relation = table_open(SettingAclRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid settingId = lfirst_oid(cell);
+ Form_pg_setting_acl pg_setting_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_setting_acl];
+ bool nulls[Natts_pg_setting_acl];
+ bool replaces[Natts_pg_setting_acl];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(settingId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for setting %u", settingId);
+
+ pg_setting_acl_tuple = (Form_pg_setting_acl) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ aclDatum = SysCacheGetAttr(SETTINGNAME, tuple, Anum_pg_setting_acl_setacl,
+ &isNull);
+ if (isNull)
+ {
+ old_acl = acldefault(OBJECT_SETTING, InvalidOid);
+ /* 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, GetUserId(),
+ &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,
+ settingId, grantorId, OBJECT_SETTING,
+ text_to_cstring(&pg_setting_acl_tuple->setting),
+ 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_setting_acl_setacl - 1] = true;
+ values[Anum_pg_setting_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(settingId, SettingAclRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(SettingAclRelationId,
+ pg_setting_acl_tuple->oid, 0,
+ InvalidOid,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* Post alter hook called for grant and revoke */
+ InvokeObjectPostAlterHook(SettingAclRelationId, settingId, 0);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ table_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3255,6 +3427,10 @@ string_to_privilege(const char *privname)
return ACL_CREATE_TEMP;
if (strcmp(privname, "connect") == 0)
return ACL_CONNECT;
+ if (strcmp(privname, "set value") == 0)
+ return ACL_SET_VALUE;
+ 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 +3468,10 @@ privilege_to_string(AclMode privilege)
return "TEMP";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET_VALUE:
+ return "SET VALUE";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3328,6 +3508,13 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_COLUMN:
msg = gettext_noop("permission denied for column %s");
break;
+ case OBJECT_SETTING:
+ /*
+ * 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 +3751,7 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_AMPROC:
case OBJECT_ATTRIBUTE:
case OBJECT_CAST:
+ case OBJECT_SETTING:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
case OBJECT_DOMCONSTRAINT:
@@ -4000,6 +4188,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_setting_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 setting's ACL from pg_setting_acl
+ */
+ tuple = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(config_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("setting with OID %u does not exist",
+ config_oid)));
+
+ aclDatum = SysCacheGetAttr(SETTINGOID, tuple, Anum_pg_setting_acl_setacl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_SETTING, 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 +4954,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_setting_acl_aclcheck(Oid config_oid, Oid roleid, AclMode mode)
+{
+ if (pg_setting_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..78868e7052 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_setting_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 == SettingAclRelationId ||
relationId == DatabaseRelationId ||
relationId == SharedDescriptionRelationId ||
relationId == SharedDependRelationId ||
@@ -260,6 +262,8 @@ IsSharedRelation(Oid relationId)
relationId == AuthIdOidIndexId ||
relationId == AuthMemRoleMemIndexId ||
relationId == AuthMemMemRoleIndexId ||
+ relationId == SettingAclSettingIndexId ||
+ relationId == SettingAclOidIndexId ||
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 == PgSettingAclToastTable ||
+ relationId == PgSettingAclToastIndex ||
relationId == PgDatabaseToastTable ||
relationId == PgDatabaseToastIndex ||
relationId == PgDbRoleSettingToastTable ||
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index ab9e42d7d1..5d2504463d 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_setting_acl.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
@@ -150,6 +151,7 @@ static const Oid object_classes[] = {
TypeRelationId, /* OCLASS_TYPE */
CastRelationId, /* OCLASS_CAST */
CollationRelationId, /* OCLASS_COLLATION */
+ SettingAclRelationId, /* OCLASS_SETTING */
ConstraintRelationId, /* OCLASS_CONSTRAINT */
ConversionRelationId, /* OCLASS_CONVERSION */
AttrDefaultRelationId, /* OCLASS_DEFAULT */
@@ -1504,6 +1506,7 @@ doDeletion(const ObjectAddress *object, int flags)
/*
* These global object types are not supported here.
*/
+ case OCLASS_SETTING:
case OCLASS_ROLE:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
@@ -2778,6 +2781,9 @@ getObjectClass(const ObjectAddress *object)
case CollationRelationId:
return OCLASS_COLLATION;
+ case SettingAclRelationId:
+ return OCLASS_SETTING;
+
case ConstraintRelationId:
return OCLASS_CONSTRAINT;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index f30c742d48..1ef833a0e3 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_setting_acl.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
@@ -2309,6 +2310,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_COLUMN:
case OBJECT_ATTRIBUTE:
case OBJECT_COLLATION:
+ case OBJECT_SETTING:
case OBJECT_CONVERSION:
case OBJECT_STATISTIC_EXT:
case OBJECT_TSPARSER:
@@ -3510,6 +3512,22 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
break;
}
+ case OCLASS_SETTING:
+ {
+ char *setting;
+
+ setting = get_setting_name(object->objectId);
+ if (!setting)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for setting %u",
+ object->objectId);
+ break;
+ }
+ appendStringInfo(&buffer, _("setting %s"), setting);
+ break;
+ }
+
case OCLASS_SCHEMA:
{
char *nspname;
@@ -4473,6 +4491,10 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "collation");
break;
+ case OCLASS_SETTING:
+ appendStringInfoString(&buffer, "setting");
+ break;
+
case OCLASS_CONSTRAINT:
getConstraintTypeDescription(&buffer, object->objectId,
missing_ok);
@@ -4977,6 +4999,30 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_SETTING:
+ {
+ HeapTuple configTup;
+ Form_pg_setting_acl configForm;
+ char *namestr;
+
+ configTup = SearchSysCache1(SETTINGOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(configTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for setting %u",
+ object->objectId);
+ break;
+ }
+ configForm = (Form_pg_setting_acl) GETSTRUCT(configTup);
+ namestr = text_to_cstring(&configForm->setting);
+ appendStringInfoString(&buffer, namestr);
+ if (objname)
+ *objname = list_make1(namestr);
+ ReleaseSysCache(configTup);
+ break;
+ }
+
case OCLASS_CONVERSION:
{
HeapTuple conTup;
diff --git a/src/backend/catalog/pg_setting_acl.c b/src/backend/catalog/pg_setting_acl.c
new file mode 100644
index 0000000000..3c6594da03
--- /dev/null
+++ b/src/backend/catalog/pg_setting_acl.c
@@ -0,0 +1,122 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_setting_acl.c
+ * routines to support manipulation of the pg_setting_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_setting_acl.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/sysattr.h"
+#include "access/table.h"
+#include "access/tableam.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_setting_acl.h"
+#include "mb/pg_wchar.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/guc.h"
+#include "utils/pg_locale.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * SettingAclCreate
+ *
+ * Add a new tuple to pg_setting_acl.
+ *
+ * setting: the setting name to create.
+ * if_not_exists: if true, don't fail on duplicate name, but rather return
+ * the existing entry's Oid.
+ */
+Oid
+SettingAclCreate(const char *setting, bool if_not_exists)
+{
+ Relation rel;
+ TupleDesc tupDesc;
+ HeapTuple tuple;
+ Datum values[Natts_pg_setting_acl];
+ bool nulls[Natts_pg_setting_acl];
+ Oid settingId;
+ const char *canonical;
+
+ /*
+ * Check whether the setting (by the given name or alias)
+ * already exists.
+ */
+ settingId = get_setting_oid(setting, true);
+ if (OidIsValid(settingId))
+ {
+ if (if_not_exists)
+ return settingId;
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("setting \"%s\" already exists",
+ setting)));
+ }
+
+ /*
+ * We must not require the setting to be in the list of existent GUCs,
+ * as we may be called at different points during upgrades or the
+ * installation or removal of extensions. Instead, perform a basic sanity
+ * check of the setting, 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_setting_acl entries.
+ */
+ if (!valid_variable_name(setting, NULL))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid setting name \"%s\"",
+ setting)));
+ canonical = GetConfigOptionCanonicalName(setting);
+ if (!canonical)
+ canonical = setting;
+
+ /*
+ * 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(SettingAclRelationId, RowExclusiveLock);
+ tupDesc = RelationGetDescr(rel);
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ values[Anum_pg_setting_acl_setting - 1] =
+ DirectFunctionCall1(textin, CStringGetDatum(canonical));
+ settingId = GetNewOidWithIndex(rel,
+ SettingAclOidIndexId,
+ Anum_pg_setting_acl_oid);
+ values[Anum_pg_setting_acl_oid - 1] = ObjectIdGetDatum(settingId);
+ nulls[Anum_pg_setting_acl_setacl - 1] = true;
+ tuple = heap_form_tuple(tupDesc, values, nulls);
+ CatalogTupleInsert(rel, tuple);
+
+ /* Post creation hook for new setting */
+ InvokeObjectPostCreateHook(SettingAclRelationId, settingId, 0);
+
+ /*
+ * Close pg_setting_acl, but keep lock till commit.
+ */
+ heap_freetuple(tuple);
+ table_close(rel, NoLock);
+
+ return settingId;
+}
diff --git a/src/backend/catalog/setting_privileges.sql b/src/backend/catalog/setting_privileges.sql
new file mode 100644
index 0000000000..b788ebee7f
--- /dev/null
+++ b/src/backend/catalog/setting_privileges.sql
@@ -0,0 +1,62 @@
+/*
+ * PostgreSQL User SET variables
+ *
+ * Copyright (c) 2021, PostgreSQL Global Development Group
+ *
+ * src/backend/catalog/setting_privileges.sql
+ *
+ * Note: this file is read in single-user -j mode, which means that the
+ * command terminator is semicolon-newline-newline; whenever the backend
+ * sees that, it stops and executes what it's got. If you write a lot of
+ * statements without empty lines between, they'll all get quoted to you
+ * in any error message about one of them, so don't do that. Also, you
+ * cannot write a semicolon immediately followed by an empty line in a
+ * string literal (including a function body!) or a multiline comment.
+ */
+
+GRANT SET VALUE ON
+ enable_seqscan, enable_indexscan, enable_indexonlyscan, enable_bitmapscan,
+ enable_tidscan, enable_sort, enable_incremental_sort, enable_hashagg,
+ enable_material, enable_memoize, enable_nestloop, enable_mergejoin,
+ enable_hashjoin, enable_gathermerge, enable_partitionwise_join,
+ enable_partitionwise_aggregate, enable_parallel_append,
+ enable_parallel_hash, enable_partition_pruning, enable_async_append, geqo,
+ exit_on_error, debug_print_parse, debug_print_rewritten, debug_print_plan,
+ debug_pretty_print, trace_notify, transform_null_equals,
+ default_transaction_read_only, transaction_read_only,
+ default_transaction_deferrable, transaction_deferrable, row_security,
+ check_function_bodies, array_nulls, default_with_oids, trace_sort,
+ trace_syncscan, optimize_bounded_sort, escape_string_warning,
+ standard_conforming_strings, synchronize_seqscans, quote_all_identifiers,
+ parallel_leader_participation, jit, jit_expressions, jit_tuple_deforming,
+ default_statistics_target, from_collapse_limit, join_collapse_limit,
+ geqo_threshold, geqo_effort, geqo_pool_size, geqo_generations,
+ temp_buffers, work_mem, maintenance_work_mem, logical_decoding_work_mem,
+ vacuum_cost_page_hit, vacuum_cost_page_miss, vacuum_cost_page_dirty,
+ vacuum_cost_limit, statement_timeout, lock_timeout,
+ idle_in_transaction_session_timeout, idle_session_timeout,
+ vacuum_freeze_min_age, vacuum_freeze_table_age,
+ vacuum_multixact_freeze_min_age, vacuum_multixact_freeze_table_age,
+ vacuum_failsafe_age, vacuum_multixact_failsafe_age, wal_skip_threshold,
+ wal_sender_timeout, commit_siblings, extra_float_digits,
+ log_parameter_max_length_on_error, effective_io_concurrency,
+ maintenance_io_concurrency, backend_flush_after,
+ max_parallel_maintenance_workers, max_parallel_workers_per_gather,
+ max_parallel_workers, tcp_keepalives_idle, tcp_keepalives_interval,
+ ssl_renegotiation_limit, tcp_keepalives_count, gin_fuzzy_search_limit,
+ effective_cache_size, min_parallel_table_scan_size,
+ min_parallel_index_scan_size, gin_pending_list_limit, tcp_user_timeout,
+ client_connection_check_interval, seq_page_cost, random_page_cost,
+ cpu_tuple_cost, cpu_index_tuple_cost, cpu_operator_cost,
+ parallel_tuple_cost, parallel_setup_cost, jit_above_cost,
+ jit_optimize_above_cost, jit_inline_above_cost, cursor_tuple_fraction,
+ geqo_selection_bias, geqo_seed, hash_mem_multiplier, seed,
+ vacuum_cost_delay, DateStyle, default_table_access_method,
+ default_tablespace, temp_tablespaces, lc_monetary, lc_numeric, lc_time,
+ local_preload_libraries, search_path, role, TimeZone,
+ timezone_abbreviations, default_text_search_config, application_name,
+ backslash_quote, bytea_output, client_min_messages, constraint_exclusion,
+ default_toast_compression, default_transaction_isolation,
+ transaction_isolation, IntervalStyle, synchronous_commit, xmlbinary,
+ xmloption, force_parallel_mode, password_encryption, plan_cache_mode
+ TO public;
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 3cb69b1f87..8fd2d33dc0 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -595,6 +595,19 @@ CREATE RULE pg_settings_n AS
GRANT SELECT, UPDATE ON pg_settings TO PUBLIC;
+CREATE VIEW pg_setting_privileges AS
+ SELECT grantor.rolname AS grantor,
+ grantee.rolname AS grantee,
+ set_acl.setting AS setting,
+ acl.privilege_type AS privilege_type,
+ acl.is_grantable
+ FROM pg_catalog.pg_setting_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_setting_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..3dd0c5cadb 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -639,6 +639,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
break;
case OCLASS_CAST:
+ case OCLASS_SETTING:
case OCLASS_CONSTRAINT:
case OCLASS_DEFAULT:
case OCLASS_LANGUAGE:
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index c9b5732448..00398727ed 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -276,6 +276,13 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
name = NameListToString(castNode(List, object));
}
break;
+ case OBJECT_SETTING:
+ if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
+ {
+ msg = gettext_noop("setting \"%s\" does not exist, skipping");
+ name = NameListToString(castNode(List, object));
+ }
+ break;
case OBJECT_CONVERSION:
if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
{
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 93c2099735..4de3b85db1 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_SETTING:
case OBJECT_DATABASE:
case OBJECT_TABLESPACE:
case OBJECT_ROLE:
@@ -1012,6 +1013,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
{
switch (objclass)
{
+ case OCLASS_SETTING:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
@@ -2078,6 +2080,8 @@ stringify_grant_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMN";
+ case OBJECT_SETTING:
+ return "SETTING";
case OBJECT_TABLE:
return "TABLE";
case OBJECT_SEQUENCE:
@@ -2161,6 +2165,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMNS";
+ case OBJECT_SETTING:
+ return "SETTINGS";
case OBJECT_TABLE:
return "TABLES";
case OBJECT_SEQUENCE:
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 7a62d547e2..7b05eb0110 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_SETTING:
case OBJECT_CONVERSION:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1f0654c2f5..4b6de1d893 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -12619,6 +12619,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_TYPE:
case OCLASS_CAST:
case OCLASS_COLLATION:
+ case OCLASS_SETTING:
case OCLASS_CONVERSION:
case OCLASS_LANGUAGE:
case OCLASS_LARGEOBJECT:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b5966712ce..533b996145 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -363,8 +363,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 setting_name
+%type <list> OptSchemaEltList setting_target
%type <chr> am_type
@@ -402,8 +402,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> iso_level opt_encoding
%type <rolespec> grantee
%type <list> grantee_list
-%type <accesspriv> privilege
-%type <list> privileges privilege_list
+%type <accesspriv> privilege setting_priv
+%type <list> privileges privilege_list setting_priv_list
%type <privtarget> privilege_target
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
@@ -715,7 +715,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
@@ -6972,6 +6972,20 @@ GrantStmt: GRANT privileges ON privilege_target TO grantee_list
n->grantor = $8;
$$ = (Node*)n;
}
+ | GRANT setting_priv_list ON setting_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_SETTING;
+ n->objects = $4;
+ n->grantees = $6;
+ n->grant_option = $7;
+ n->grantor = $8;
+ $$ = (Node*)n;
+ }
;
RevokeStmt:
@@ -7005,6 +7019,36 @@ RevokeStmt:
n->behavior = $11;
$$ = (Node *)n;
}
+ | REVOKE setting_priv_list ON setting_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_SETTING;
+ n->objects = $4;
+ n->grantees = $6;
+ n->grantor = $7;
+ n->behavior = $8;
+ $$ = (Node *)n;
+ }
+ | REVOKE GRANT OPTION FOR setting_priv_list ON setting_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_SETTING;
+ n->objects = $7;
+ n->grantees = $9;
+ n->grantor = $10;
+ n->behavior = $11;
+ $$ = (Node *)n;
+ }
;
@@ -7073,6 +7117,49 @@ privilege: SELECT opt_column_list
}
;
+setting_priv_list: setting_priv { $$ = list_make1($1); }
+ | setting_priv_list ',' setting_priv { $$ = lappend($1, $3); }
+ ;
+
+setting_priv:
+ ALTER SYSTEM_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("alter system");
+ n->cols = NIL;
+ $$ = n;
+ }
+ | SET VALUE_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("set value");
+ n->cols = NIL;
+ $$ = n;
+ }
+ ;
+
+
+setting_target:
+ setting_name
+ {
+ $$ = list_make1(makeString($1));
+ }
+ | setting_target ',' setting_name
+ {
+ $$ = lappend($1, makeString($3));
+ }
+ ;
+
+setting_name:
+ ColId
+ {
+ $$ = $1;
+ }
+ | setting_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.
@@ -15816,6 +15903,7 @@ unreserved_keyword:
| OWNED
| OWNER
| PARALLEL
+ | PARAMETER
| PARSER
| PARTIAL
| PARTITION
@@ -16394,6 +16482,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 0a16f8156c..b0e43118bf 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_setting_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,8 @@ 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 Oid convert_setting_name(text *setting);
+static AclMode convert_setting_priv_string(text *priv_setting_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 +310,12 @@ aclparse(const char *s, AclItem *aip)
case ACL_CONNECT_CHR:
read = ACL_CONNECT;
break;
+ case ACL_SET_VALUE_CHR:
+ read = ACL_SET_VALUE;
+ break;
+ case ACL_ALTER_SYSTEM_CHR:
+ read = ACL_ALTER_SYSTEM;
+ break;
case 'R': /* ignore old RULE privileges */
read = 0;
break;
@@ -794,6 +804,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_SETTING:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_NO_RIGHTS;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -1602,6 +1616,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 VALUE") == 0)
+ return ACL_SET_VALUE;
+ 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 +1716,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET_VALUE:
+ return "SET VALUE";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
@@ -4429,6 +4451,205 @@ convert_type_priv_string(text *priv_type_text)
return convert_any_priv_string(priv_type_text, type_priv_map);
}
+/*
+ * has_setting_privilege variants
+ * These are all named "has_setting_privilege" at the SQL level.
+ * They take various combinations of setting name, setting 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_setting_privilege_name_name
+ * Check user privileges on a setting given
+ * name username, text setting, and text priv name.
+ */
+Datum
+has_setting_privilege_name_name(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ text *setting = PG_GETARG_TEXT_PP(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid roleid;
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_role_oid_or_public(NameStr(*username));
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_name
+ * Check user privileges on a setting given
+ * text setting and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_setting_privilege_name(PG_FUNCTION_ARGS)
+{
+ text *setting = PG_GETARG_TEXT_PP(0);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(1);
+ Oid roleid;
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_name_id
+ * Check user privileges on a setting given
+ * name usename, setting oid, and text priv name.
+ */
+Datum
+has_setting_privilege_name_id(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ Oid settingoid = PG_GETARG_OID(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_role_oid_or_public(NameStr(*username));
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id
+ * Check user privileges on a setting given
+ * setting oid, and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_setting_privilege_id(PG_FUNCTION_ARGS)
+{
+ Oid settingoid = PG_GETARG_OID(0);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(1);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id_name
+ * Check user privileges on a setting given
+ * roleid, text setting, and text priv name.
+ */
+Datum
+has_setting_privilege_id_name(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ text *setting = PG_GETARG_TEXT_PP(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id_id
+ * Check user privileges on a setting given
+ * roleid, setting oid, and text priv name.
+ */
+Datum
+has_setting_privilege_id_id(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ Oid settingoid = PG_GETARG_OID(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ AclMode mode;
+ AclResult aclresult;
+
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * Support routines for has_setting_privilege family.
+ */
+
+/*
+ * Given a setting name expressed as a string, look it up and return Oid
+ */
+static Oid
+convert_setting_name(text *settingname)
+{
+ Oid oid;
+ char *setting = text_to_cstring(settingname);
+
+ oid = get_setting_oid(setting, true);
+
+ if (!OidIsValid(oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("setting \"%s\" does not exist", setting)));
+
+ return oid;
+}
+
+/*
+ * convert_setting_priv_string
+ * Convert text string to AclMode value.
+ */
+static AclMode
+convert_setting_priv_string(text *priv_setting_text)
+{
+ static const priv_map setting_priv_map[] = {
+ {"SET VALUE", ACL_SET_VALUE},
+ {"SET VALUE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SET_VALUE)},
+ {"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_setting_text, setting_priv_map);
+}
/*
* pg_has_role variants
@@ -4670,6 +4891,43 @@ initialize_acl(void)
}
}
+/*
+ * get_setting_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_setting_oid(const char *setting, bool missing_ok)
+{
+ Oid oid;
+
+ /* Check for the variable by the name we were given */
+ oid = GetSysCacheOid1(SETTINGNAME, Anum_pg_setting_acl_oid,
+ PointerGetDatum(cstring_to_text(setting)));
+ if (!OidIsValid(oid))
+ {
+ const char *canonical;
+
+ /* Check if the variable has a different canonical spelling */
+ canonical = GetConfigOptionCanonicalName(setting);
+ if (canonical != NULL)
+ oid = GetSysCacheOid1(SETTINGNAME, Anum_pg_setting_acl_oid,
+ PointerGetDatum(cstring_to_text(canonical)));
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("setting \"%s\" does not exist", setting)));
+ }
+
+ return oid;
+}
+
/*
* RoleMembershipCacheCallback
* Syscache inval callback function
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index feef999863..f4e806fe8e 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_setting_acl.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_transform.h"
#include "catalog/pg_type.h"
@@ -3304,6 +3305,25 @@ free_attstatsslot(AttStatsSlot *sslot)
pfree(sslot->numbers_arr);
}
+char *
+get_setting_name(Oid configid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(configid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_setting_acl configtup = (Form_pg_setting_acl) GETSTRUCT(tp);
+ char *result;
+
+ result = pstrdup(text_to_cstring(&configtup->setting));
+ 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 f4e7819f1e..cbcbf02839 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_setting_acl.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
#include "catalog/pg_shseclabel.h"
@@ -762,6 +763,28 @@ static const struct cachedesc cacheinfo[] = {
},
32
},
+ {SettingAclRelationId, /* SETTINGNAME */
+ SettingAclSettingIndexId,
+ 1,
+ {
+ Anum_pg_setting_acl_setting,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+ {SettingAclRelationId, /* SETTINGOID */
+ SettingAclOidIndexId,
+ 1,
+ {
+ Anum_pg_setting_acl_oid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{StatisticExtDataRelationId, /* STATEXTDATASTXOID */
StatisticExtDataStxoidInhIndexId,
2,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 4c94f09c64..cca3b4bd70 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -42,7 +42,9 @@
#include "access/xact.h"
#include "access/xlog_internal.h"
#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_authid.h"
+#include "catalog/pg_setting_acl.h"
#include "catalog/storage.h"
#include "commands/async.h"
#include "commands/prepare.h"
@@ -973,7 +975,7 @@ static const unit_conversion time_unit_conversion_table[] =
static struct config_bool ConfigureNamesBool[] =
{
{
- {"enable_seqscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_seqscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of sequential-scan plans."),
NULL,
GUC_EXPLAIN
@@ -983,7 +985,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_indexscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_indexscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of index-scan plans."),
NULL,
GUC_EXPLAIN
@@ -993,7 +995,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_indexonlyscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_indexonlyscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of index-only-scan plans."),
NULL,
GUC_EXPLAIN
@@ -1003,7 +1005,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_bitmapscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_bitmapscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of bitmap-scan plans."),
NULL,
GUC_EXPLAIN
@@ -1013,7 +1015,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_tidscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_tidscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of TID scan plans."),
NULL,
GUC_EXPLAIN
@@ -1023,7 +1025,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_sort", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_sort", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of explicit sort steps."),
NULL,
GUC_EXPLAIN
@@ -1033,7 +1035,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_incremental_sort", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_incremental_sort", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of incremental sort steps."),
NULL,
GUC_EXPLAIN
@@ -1043,7 +1045,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_hashagg", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_hashagg", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of hashed aggregation plans."),
NULL,
GUC_EXPLAIN
@@ -1053,7 +1055,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_material", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_material", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of materialization."),
NULL,
GUC_EXPLAIN
@@ -1063,7 +1065,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_memoize", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_memoize", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of memoization."),
NULL,
GUC_EXPLAIN
@@ -1073,7 +1075,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_nestloop", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_nestloop", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of nested-loop join plans."),
NULL,
GUC_EXPLAIN
@@ -1083,7 +1085,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_mergejoin", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_mergejoin", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of merge join plans."),
NULL,
GUC_EXPLAIN
@@ -1093,7 +1095,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_hashjoin", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_hashjoin", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of hash join plans."),
NULL,
GUC_EXPLAIN
@@ -1103,7 +1105,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_gathermerge", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_gathermerge", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of gather merge plans."),
NULL,
GUC_EXPLAIN
@@ -1113,7 +1115,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_partitionwise_join", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_partitionwise_join", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables partitionwise join."),
NULL,
GUC_EXPLAIN
@@ -1123,7 +1125,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_partitionwise_aggregate", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_partitionwise_aggregate", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables partitionwise aggregation and grouping."),
NULL,
GUC_EXPLAIN
@@ -1133,7 +1135,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_parallel_append", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_parallel_append", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of parallel append plans."),
NULL,
GUC_EXPLAIN
@@ -1143,7 +1145,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_parallel_hash", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_parallel_hash", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of parallel hash plans."),
NULL,
GUC_EXPLAIN
@@ -1153,7 +1155,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_partition_pruning", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_partition_pruning", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables plan-time and execution-time partition pruning."),
gettext_noop("Allows the query planner and executor to compare partition "
"bounds to conditions in the query to determine which "
@@ -1165,7 +1167,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_async_append", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_async_append", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of async append plans."),
NULL,
GUC_EXPLAIN
@@ -1175,7 +1177,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("Enables genetic query optimization."),
gettext_noop("This algorithm attempts to do planning without "
"exhaustive searching."),
@@ -1397,7 +1399,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"exit_on_error", PGC_USERSET, ERROR_HANDLING_OPTIONS,
+ {"exit_on_error", PGC_SUSET, ERROR_HANDLING_OPTIONS,
gettext_noop("Terminate session on any error."),
NULL
},
@@ -1435,7 +1437,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_print_parse", PGC_USERSET, LOGGING_WHAT,
+ {"debug_print_parse", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs each query's parse tree."),
NULL
},
@@ -1444,7 +1446,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_print_rewritten", PGC_USERSET, LOGGING_WHAT,
+ {"debug_print_rewritten", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs each query's rewritten parse tree."),
NULL
},
@@ -1453,7 +1455,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_print_plan", PGC_USERSET, LOGGING_WHAT,
+ {"debug_print_plan", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs each query's execution plan."),
NULL
},
@@ -1462,7 +1464,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_pretty_print", PGC_USERSET, LOGGING_WHAT,
+ {"debug_pretty_print", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Indents parse and plan tree displays."),
NULL
},
@@ -1583,7 +1585,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"trace_notify", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"trace_notify", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Generates debugging output for LISTEN and NOTIFY."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -1667,7 +1669,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"transform_null_equals", PGC_USERSET, COMPAT_OPTIONS_CLIENT,
+ {"transform_null_equals", PGC_SUSET, COMPAT_OPTIONS_CLIENT,
gettext_noop("Treats \"expr=NULL\" as \"expr IS NULL\"."),
gettext_noop("When turned on, expressions of the form expr = NULL "
"(or NULL = expr) are treated as expr IS NULL, that is, they "
@@ -1689,7 +1691,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"default_transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_transaction_read_only", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default read-only status of new transactions."),
NULL,
GUC_REPORT
@@ -1699,7 +1701,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"transaction_read_only", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the current transaction's read-only status."),
NULL,
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -1709,7 +1711,7 @@ static struct config_bool ConfigureNamesBool[] =
check_transaction_read_only, NULL, NULL
},
{
- {"default_transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_transaction_deferrable", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default deferrable status of new transactions."),
NULL
},
@@ -1718,7 +1720,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"transaction_deferrable", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Whether to defer a read-only serializable transaction until it can be executed with no possible serialization failures."),
NULL,
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -1728,7 +1730,7 @@ static struct config_bool ConfigureNamesBool[] =
check_transaction_deferrable, NULL, NULL
},
{
- {"row_security", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"row_security", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Enable row security."),
gettext_noop("When enabled, row security will be applied to all users.")
},
@@ -1737,7 +1739,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"check_function_bodies", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"check_function_bodies", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Check routine bodies during CREATE FUNCTION and CREATE PROCEDURE."),
NULL
},
@@ -1746,7 +1748,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"array_nulls", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"array_nulls", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Enable input of NULL elements in arrays."),
gettext_noop("When turned on, unquoted NULL in an array input "
"value means a null value; "
@@ -1763,7 +1765,7 @@ static struct config_bool ConfigureNamesBool[] =
* avoid unnecessarily breaking older dump files.
*/
{
- {"default_with_oids", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"default_with_oids", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("WITH OIDS is no longer supported; this can only be false."),
NULL,
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE
@@ -1793,7 +1795,7 @@ static struct config_bool ConfigureNamesBool[] =
#ifdef TRACE_SORT
{
- {"trace_sort", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"trace_sort", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Emit information about resource usage in sorting."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -1807,7 +1809,7 @@ static struct config_bool ConfigureNamesBool[] =
#ifdef TRACE_SYNCSCAN
/* this is undocumented because not exposed in a standard build */
{
- {"trace_syncscan", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"trace_syncscan", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Generate debugging output for synchronized scanning."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -1822,7 +1824,7 @@ static struct config_bool ConfigureNamesBool[] =
/* this is undocumented because not exposed in a standard build */
{
{
- "optimize_bounded_sort", PGC_USERSET, QUERY_TUNING_METHOD,
+ "optimize_bounded_sort", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enable bounded sorting using heap sort."),
NULL,
GUC_NOT_IN_SAMPLE | GUC_EXPLAIN
@@ -1868,7 +1870,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"escape_string_warning", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"escape_string_warning", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Warn about backslash escapes in ordinary string literals."),
NULL
},
@@ -1878,7 +1880,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"standard_conforming_strings", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"standard_conforming_strings", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Causes '...' strings to treat backslashes literally."),
NULL,
GUC_REPORT
@@ -1889,7 +1891,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"synchronize_seqscans", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"synchronize_seqscans", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Enable synchronized sequential scans."),
NULL
},
@@ -1985,7 +1987,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"quote_all_identifiers", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"quote_all_identifiers", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("When generating SQL fragments, quote all identifiers."),
NULL,
},
@@ -2026,7 +2028,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"parallel_leader_participation", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"parallel_leader_participation", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Controls whether Gather and Gather Merge also run subplans."),
gettext_noop("Should gather nodes also run subplans or just gather tuples?"),
GUC_EXPLAIN
@@ -2037,7 +2039,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"jit", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"jit", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Allow JIT compilation."),
NULL,
GUC_EXPLAIN
@@ -2076,7 +2078,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"jit_expressions", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"jit_expressions", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Allow JIT compilation of expressions."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -2104,7 +2106,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"jit_tuple_deforming", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"jit_tuple_deforming", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Allow JIT compilation of tuple deforming."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -2164,7 +2166,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"default_statistics_target", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"default_statistics_target", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the default statistics target."),
gettext_noop("This applies to table columns that have not had a "
"column-specific target set via ALTER TABLE SET STATISTICS.")
@@ -2174,7 +2176,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"from_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"from_collapse_limit", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the FROM-list size beyond which subqueries "
"are not collapsed."),
gettext_noop("The planner will merge subqueries into upper "
@@ -2187,7 +2189,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"join_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"join_collapse_limit", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the FROM-list size beyond which JOIN "
"constructs are not flattened."),
gettext_noop("The planner will flatten explicit JOIN "
@@ -2200,7 +2202,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_threshold", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_threshold", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("Sets the threshold of FROM items beyond which GEQO is used."),
NULL,
GUC_EXPLAIN
@@ -2210,7 +2212,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_effort", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_effort", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: effort is used to set the default for other GEQO parameters."),
NULL,
GUC_EXPLAIN
@@ -2220,7 +2222,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_pool_size", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_pool_size", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: number of individuals in the population."),
gettext_noop("Zero selects a suitable default value."),
GUC_EXPLAIN
@@ -2230,7 +2232,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_generations", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_generations", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: number of iterations of the algorithm."),
gettext_noop("Zero selects a suitable default value."),
GUC_EXPLAIN
@@ -2377,7 +2379,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"temp_buffers", PGC_USERSET, RESOURCES_MEM,
+ {"temp_buffers", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum number of temporary buffers used by each session."),
NULL,
GUC_UNIT_BLOCKS | GUC_EXPLAIN
@@ -2442,7 +2444,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"work_mem", PGC_USERSET, RESOURCES_MEM,
+ {"work_mem", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum memory to be used for query workspaces."),
gettext_noop("This much memory can be used by each internal "
"sort operation and hash table before switching to "
@@ -2455,7 +2457,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"maintenance_work_mem", PGC_USERSET, RESOURCES_MEM,
+ {"maintenance_work_mem", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum memory to be used for maintenance operations."),
gettext_noop("This includes operations such as VACUUM and CREATE INDEX."),
GUC_UNIT_KB
@@ -2466,7 +2468,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"logical_decoding_work_mem", PGC_USERSET, RESOURCES_MEM,
+ {"logical_decoding_work_mem", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum memory to be used for logical decoding."),
gettext_noop("This much memory can be used by each internal "
"reorder buffer before spilling to disk."),
@@ -2505,7 +2507,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_page_hit", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_page_hit", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost for a page found in the buffer cache."),
NULL
},
@@ -2515,7 +2517,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_page_miss", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_page_miss", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost for a page not found in the buffer cache."),
NULL
},
@@ -2525,7 +2527,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_page_dirty", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_page_dirty", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost for a page dirtied by vacuum."),
NULL
},
@@ -2535,7 +2537,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_limit", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_limit", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost amount available before napping."),
NULL
},
@@ -2601,7 +2603,7 @@ static struct config_int ConfigureNamesInt[] =
#endif
{
- {"statement_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"statement_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed duration of any statement."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2612,7 +2614,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"lock_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"lock_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed duration of any wait for a lock."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2623,7 +2625,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"idle_in_transaction_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"idle_in_transaction_session_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed idle time between queries, when in a transaction."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2634,7 +2636,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"idle_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"idle_session_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed idle time between queries, when not in a transaction."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2645,7 +2647,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_freeze_min_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Minimum age at which VACUUM should freeze a table row."),
NULL
},
@@ -2655,7 +2657,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_freeze_table_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Age at which VACUUM should scan whole table to freeze tuples."),
NULL
},
@@ -2665,7 +2667,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_multixact_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_multixact_freeze_min_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Minimum age at which VACUUM should freeze a MultiXactId in a table row."),
NULL
},
@@ -2675,7 +2677,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_multixact_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_multixact_freeze_table_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Multixact age at which VACUUM should scan whole table to freeze tuples."),
NULL
},
@@ -2694,7 +2696,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"vacuum_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_failsafe_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
NULL
},
@@ -2703,7 +2705,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"vacuum_multixact_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_multixact_failsafe_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Multixact age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
NULL
},
@@ -2891,7 +2893,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"wal_skip_threshold", PGC_USERSET, WAL_SETTINGS,
+ {"wal_skip_threshold", PGC_SUSET, WAL_SETTINGS,
gettext_noop("Minimum size of new file to fsync instead of writing WAL."),
NULL,
GUC_UNIT_KB
@@ -2936,7 +2938,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"wal_sender_timeout", PGC_USERSET, REPLICATION_SENDING,
+ {"wal_sender_timeout", PGC_SUSET, REPLICATION_SENDING,
gettext_noop("Sets the maximum time to wait for WAL replication."),
NULL,
GUC_UNIT_MS
@@ -2959,7 +2961,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"commit_siblings", PGC_USERSET, WAL_SETTINGS,
+ {"commit_siblings", PGC_SUSET, WAL_SETTINGS,
gettext_noop("Sets the minimum number of concurrent open transactions "
"required before performing commit_delay."),
NULL
@@ -2970,7 +2972,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"extra_float_digits", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"extra_float_digits", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the number of digits displayed for floating-point values."),
gettext_noop("This affects real, double precision, and geometric data types. "
"A zero or negative parameter value is added to the standard "
@@ -3032,7 +3034,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"log_parameter_max_length_on_error", PGC_USERSET, LOGGING_WHAT,
+ {"log_parameter_max_length_on_error", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Sets the maximum length in bytes of data logged for bind "
"parameter values when logging statements, on error."),
gettext_noop("-1 to print values in full."),
@@ -3077,7 +3079,7 @@ static struct config_int ConfigureNamesInt[] =
{
{"effective_io_concurrency",
- PGC_USERSET,
+ PGC_SUSET,
RESOURCES_ASYNCHRONOUS,
gettext_noop("Number of simultaneous requests that can be handled efficiently by the disk subsystem."),
NULL,
@@ -3095,7 +3097,7 @@ static struct config_int ConfigureNamesInt[] =
{
{"maintenance_io_concurrency",
- PGC_USERSET,
+ PGC_SUSET,
RESOURCES_ASYNCHRONOUS,
gettext_noop("A variant of effective_io_concurrency that is used for maintenance work."),
NULL,
@@ -3112,7 +3114,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"backend_flush_after", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"backend_flush_after", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Number of pages after which previously performed writes are flushed to disk."),
NULL,
GUC_UNIT_BLOCKS
@@ -3344,7 +3346,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"max_parallel_maintenance_workers", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"max_parallel_maintenance_workers", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Sets the maximum number of parallel processes per maintenance operation."),
NULL
},
@@ -3354,7 +3356,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"max_parallel_workers_per_gather", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"max_parallel_workers_per_gather", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Sets the maximum number of parallel processes per executor node."),
NULL,
GUC_EXPLAIN
@@ -3365,7 +3367,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"max_parallel_workers", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"max_parallel_workers", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Sets the maximum number of parallel workers that can be active at one time."),
NULL,
GUC_EXPLAIN
@@ -3398,7 +3400,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_keepalives_idle", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_keepalives_idle", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Time between issuing TCP keepalives."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_S
@@ -3409,7 +3411,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_keepalives_interval", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_keepalives_interval", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Time between TCP keepalive retransmits."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_S
@@ -3420,7 +3422,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"ssl_renegotiation_limit", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"ssl_renegotiation_limit", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("SSL renegotiation is no longer supported; this can only be 0."),
NULL,
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE,
@@ -3431,7 +3433,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_keepalives_count", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_keepalives_count", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Maximum number of TCP keepalive retransmits."),
gettext_noop("This controls the number of consecutive keepalive retransmits that can be "
"lost before a connection is considered dead. A value of 0 uses the "
@@ -3443,7 +3445,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"gin_fuzzy_search_limit", PGC_USERSET, CLIENT_CONN_OTHER,
+ {"gin_fuzzy_search_limit", PGC_SUSET, CLIENT_CONN_OTHER,
gettext_noop("Sets the maximum allowed result for exact search by GIN."),
NULL,
0
@@ -3454,7 +3456,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"effective_cache_size", PGC_USERSET, QUERY_TUNING_COST,
+ {"effective_cache_size", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's assumption about the total size of the data caches."),
gettext_noop("That is, the total size of the caches (kernel cache and shared buffers) used for PostgreSQL data files. "
"This is measured in disk pages, which are normally 8 kB each."),
@@ -3466,7 +3468,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"min_parallel_table_scan_size", PGC_USERSET, QUERY_TUNING_COST,
+ {"min_parallel_table_scan_size", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the minimum amount of table data for a parallel scan."),
gettext_noop("If the planner estimates that it will read a number of table pages too small to reach this limit, a parallel scan will not be considered."),
GUC_UNIT_BLOCKS | GUC_EXPLAIN,
@@ -3477,7 +3479,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"min_parallel_index_scan_size", PGC_USERSET, QUERY_TUNING_COST,
+ {"min_parallel_index_scan_size", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the minimum amount of index data for a parallel scan."),
gettext_noop("If the planner estimates that it will read a number of index pages too small to reach this limit, a parallel scan will not be considered."),
GUC_UNIT_BLOCKS | GUC_EXPLAIN,
@@ -3522,7 +3524,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"gin_pending_list_limit", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"gin_pending_list_limit", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum size of the pending list for GIN index."),
NULL,
GUC_UNIT_KB
@@ -3533,7 +3535,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_user_timeout", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_user_timeout", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("TCP user timeout."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_MS
@@ -3578,7 +3580,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"client_connection_check_interval", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"client_connection_check_interval", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Sets the time interval between checks for disconnection while running queries."),
NULL,
GUC_UNIT_MS
@@ -3610,7 +3612,7 @@ static struct config_int ConfigureNamesInt[] =
static struct config_real ConfigureNamesReal[] =
{
{
- {"seq_page_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"seq_page_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of a "
"sequentially fetched disk page."),
NULL,
@@ -3621,7 +3623,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"random_page_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"random_page_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of a "
"nonsequentially fetched disk page."),
NULL,
@@ -3632,7 +3634,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"cpu_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"cpu_tuple_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"processing each tuple (row)."),
NULL,
@@ -3643,7 +3645,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"cpu_index_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"cpu_index_tuple_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"processing each index entry during an index scan."),
NULL,
@@ -3654,7 +3656,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"cpu_operator_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"cpu_operator_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"processing each operator or function call."),
NULL,
@@ -3665,7 +3667,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"parallel_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"parallel_tuple_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"passing each tuple (row) from worker to leader backend."),
NULL,
@@ -3676,7 +3678,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"parallel_setup_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"parallel_setup_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"starting up worker processes for parallel query."),
NULL,
@@ -3688,7 +3690,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"jit_above_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"jit_above_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Perform JIT compilation if query is more expensive."),
gettext_noop("-1 disables JIT compilation."),
GUC_EXPLAIN
@@ -3699,7 +3701,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"jit_optimize_above_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"jit_optimize_above_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Optimize JIT-compiled functions if query is more expensive."),
gettext_noop("-1 disables optimization."),
GUC_EXPLAIN
@@ -3710,7 +3712,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"jit_inline_above_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"jit_inline_above_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Perform JIT inlining if query is more expensive."),
gettext_noop("-1 disables inlining."),
GUC_EXPLAIN
@@ -3721,7 +3723,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"cursor_tuple_fraction", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"cursor_tuple_fraction", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the planner's estimate of the fraction of "
"a cursor's rows that will be retrieved."),
NULL,
@@ -3733,7 +3735,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"geqo_selection_bias", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_selection_bias", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: selective pressure within the population."),
NULL,
GUC_EXPLAIN
@@ -3744,7 +3746,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"geqo_seed", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_seed", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: seed for random path selection."),
NULL,
GUC_EXPLAIN
@@ -3755,7 +3757,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"hash_mem_multiplier", PGC_USERSET, RESOURCES_MEM,
+ {"hash_mem_multiplier", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Multiple of work_mem to use for hash tables."),
NULL,
GUC_EXPLAIN
@@ -3776,7 +3778,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"seed", PGC_USERSET, UNGROUPED,
+ {"seed", PGC_SUSET, UNGROUPED,
gettext_noop("Sets the seed for random-number generation."),
NULL,
GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -3787,7 +3789,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"vacuum_cost_delay", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_delay", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost delay in milliseconds."),
NULL,
GUC_UNIT_MS
@@ -4037,7 +4039,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"DateStyle", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"DateStyle", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the display format for date and time values."),
gettext_noop("Also controls interpretation of ambiguous "
"date inputs."),
@@ -4049,7 +4051,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"default_table_access_method", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_table_access_method", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default table access method for new tables."),
NULL,
GUC_IS_NAME
@@ -4060,7 +4062,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"default_tablespace", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_tablespace", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default tablespace to create tables and indexes in."),
gettext_noop("An empty string selects the database's default tablespace."),
GUC_IS_NAME
@@ -4071,7 +4073,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"temp_tablespaces", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE
@@ -4151,7 +4153,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"lc_monetary", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"lc_monetary", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the locale for formatting monetary amounts."),
NULL
},
@@ -4161,7 +4163,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"lc_numeric", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"lc_numeric", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the locale for formatting numbers."),
NULL
},
@@ -4171,7 +4173,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"lc_time", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"lc_time", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the locale for formatting date and time values."),
NULL
},
@@ -4203,7 +4205,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"local_preload_libraries", PGC_USERSET, CLIENT_CONN_PRELOAD,
+ {"local_preload_libraries", PGC_SUSET, CLIENT_CONN_PRELOAD,
gettext_noop("Lists unprivileged shared libraries to preload into each backend."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE
@@ -4214,7 +4216,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"search_path", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"search_path", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the schema search order for names that are not schema-qualified."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE | GUC_EXPLAIN
@@ -4250,7 +4252,7 @@ static struct config_string ConfigureNamesString[] =
{
/* Not for general use --- used by SET ROLE */
- {"role", PGC_USERSET, UNGROUPED,
+ {"role", PGC_SUSET, UNGROUPED,
gettext_noop("Sets the current role."),
NULL,
GUC_IS_NAME | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_NOT_WHILE_SEC_REST
@@ -4329,7 +4331,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"TimeZone", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"TimeZone", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the time zone for displaying and interpreting time stamps."),
NULL,
GUC_REPORT
@@ -4339,7 +4341,7 @@ static struct config_string ConfigureNamesString[] =
check_timezone, assign_timezone, show_timezone
},
{
- {"timezone_abbreviations", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"timezone_abbreviations", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Selects a file of time zone abbreviations."),
NULL
},
@@ -4532,7 +4534,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"default_text_search_config", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"default_text_search_config", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets default text search configuration."),
NULL
},
@@ -4594,7 +4596,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"application_name", PGC_USERSET, LOGGING_WHAT,
+ {"application_name", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Sets the application name to be reported in statistics and logs."),
NULL,
GUC_IS_NAME | GUC_REPORT | GUC_NOT_IN_SAMPLE
@@ -4658,7 +4660,7 @@ static struct config_string ConfigureNamesString[] =
static struct config_enum ConfigureNamesEnum[] =
{
{
- {"backslash_quote", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"backslash_quote", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Sets whether \"\\'\" is allowed in string literals."),
NULL
},
@@ -4668,7 +4670,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"bytea_output", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"bytea_output", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the output format for bytea."),
NULL
},
@@ -4678,7 +4680,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"client_min_messages", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"client_min_messages", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the message levels that are sent to the client."),
gettext_noop("Each level includes all the levels that follow it. The later"
" the level, the fewer messages are sent.")
@@ -4699,7 +4701,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"constraint_exclusion", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"constraint_exclusion", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Enables the planner to use constraints to optimize queries."),
gettext_noop("Table scans will be skipped if their constraints"
" guarantee that no rows match the query."),
@@ -4711,7 +4713,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"default_toast_compression", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_toast_compression", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default compression method for compressible values."),
NULL
},
@@ -4722,7 +4724,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"default_transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_transaction_isolation", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the transaction isolation level of each new transaction."),
NULL
},
@@ -4732,7 +4734,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"transaction_isolation", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the current transaction's isolation level."),
NULL,
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -4743,7 +4745,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"IntervalStyle", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"IntervalStyle", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the display format for interval values."),
NULL,
GUC_REPORT
@@ -4821,7 +4823,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"synchronous_commit", PGC_USERSET, WAL_SETTINGS,
+ {"synchronous_commit", PGC_SUSET, WAL_SETTINGS,
gettext_noop("Sets the current transaction's synchronization level."),
NULL
},
@@ -4928,7 +4930,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"xmlbinary", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"xmlbinary", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets how binary values are to be encoded in XML."),
NULL
},
@@ -4938,7 +4940,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"xmloption", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"xmloption", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets whether XML data in implicit parsing and serialization "
"operations is to be considered as documents or content fragments."),
NULL
@@ -4959,7 +4961,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"force_parallel_mode", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"force_parallel_mode", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Forces use of parallel query facilities."),
gettext_noop("If possible, run query using a parallel worker and with parallel restrictions."),
GUC_NOT_IN_SAMPLE | GUC_EXPLAIN
@@ -4970,7 +4972,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"password_encryption", PGC_USERSET, CONN_AUTH_AUTH,
+ {"password_encryption", PGC_SUSET, CONN_AUTH_AUTH,
gettext_noop("Chooses the algorithm for encrypting passwords."),
NULL
},
@@ -4980,7 +4982,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"plan_cache_mode", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"plan_cache_mode", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Controls the planner's selection of custom or generic plan."),
gettext_noop("Prepared statements can have custom and generic plans, and the planner "
"will attempt to choose which is better. This can be set to override "
@@ -5039,6 +5041,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_setting_acl
+ * entries.
*/
static const char *const map_old_guc_names[] = {
"sort_mem", "work_mem",
@@ -5438,25 +5444,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().)
+ *
+ * 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"
@@ -5473,8 +5483,24 @@ 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;
+}
+
+/*
+ * 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().)
+ */
+static bool
+valid_custom_variable_name(const char *name)
+{
+ int partcnt;
+
+ return (valid_variable_name(name, &partcnt) && partcnt > 1);
}
/*
@@ -7502,6 +7528,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 settingid = get_setting_oid(name, true);
+
+ if (OidIsValid(settingid))
+ {
+ AclResult aclresult;
+
+ aclresult = pg_setting_acl_aclcheck(settingid, GetUserId(),
+ ACL_SET_VALUE);
+
+ if (aclresult == ACLCHECK_OK)
+ break; /* okay */
+ }
+
+ /* No granted privilege */
ereport(elevel,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to set parameter \"%s\"",
@@ -8544,6 +8588,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
{
char *name;
char *value;
+ Oid settingId = InvalidOid;
bool resetall = false;
ConfigVariable *head = NULL;
ConfigVariable *tail = NULL;
@@ -8551,16 +8596,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;
+
+ settingId = get_setting_oid(name, true);
+ if (!OidIsValid(settingId))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+
+ aclresult = pg_setting_acl_aclcheck(settingId, GetUserId(),
+ ACL_ALTER_SYSTEM);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_SETTING, name);
+ }
+
switch (altersysstmt->setstmt->kind)
{
case VAR_SET_VALUE:
@@ -8693,6 +8753,21 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
replace_auto_config_value(&head, &tail, name, value);
}
+ /*
+ * Invoke the post-alter hook for setting this GUC variable. Guc variables
+ * do not always have corresponding entries in pg_setting_acl; the hooks
+ * must therefore be prepared to receive settingId = InvalidOid. We also
+ * abuse the notion of subId to pass the kind of alteration (set vs. alter
+ * system), and auxiliaryId to pass the VariableSetKind.
+ *
+ * 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.
+ */
+ InvokeObjectPostAlterHookArg(SettingAclRelationId, settingId,
+ ACL_ALTER_SYSTEM, altersysstmt->setstmt->kind,
+ false);
+
/*
* To ensure crash safety, first write the new file data to a temp file,
* then atomically rename it into place.
@@ -8753,6 +8828,9 @@ void
ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
{
GucAction action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET;
+ GucContext context;
+ AclResult aclresult;
+ Oid settingId;
/*
* Workers synchronize these parameters at the start of the parallel
@@ -8763,6 +8841,24 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
errmsg("cannot set parameters during a parallel operation")));
+ /* Get the Oid of this setting, or InvalidOid if none. */
+ settingId = get_setting_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(settingId))
+ {
+ aclresult = pg_setting_acl_aclcheck(settingId, GetUserId(),
+ ACL_SET_VALUE);
+ if (aclresult == ACLCHECK_OK)
+ context = PGC_SUSET;
+ }
+
switch (stmt->kind)
{
case VAR_SET_VALUE:
@@ -8771,7 +8867,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;
@@ -8856,7 +8952,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;
@@ -8864,6 +8960,16 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
ResetAllOptions();
break;
}
+
+ /*
+ * Invoke the post-alter hook for setting this GUC variable. Guc variables
+ * do not always have corresponding entries in pg_setting_acl; the hooks
+ * must therefore be prepared to receive settingId = InvalidOid. We also
+ * abuse the notion of subId to pass the kind of alteration (set vs. alter
+ * system), and auxiliaryId to pass the VariableSetKind.
+ */
+ InvokeObjectPostAlterHookArg(SettingAclRelationId, settingId,
+ ACL_SET_VALUE, stmt->kind, false);
}
/*
@@ -9603,6 +9709,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, false, 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,
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d78e8e67b8..872dd2ddfd 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -164,6 +164,7 @@ static char *features_file;
static char *system_constraints_file;
static char *system_functions_file;
static char *system_views_file;
+static char *setting_privileges_file;
static bool success = false;
static bool made_new_pgdata = false;
static bool found_existing_pgdata = false;
@@ -2452,6 +2453,7 @@ setup_data_file_paths(void)
set_input(&system_constraints_file, "system_constraints.sql");
set_input(&system_functions_file, "system_functions.sql");
set_input(&system_views_file, "system_views.sql");
+ set_input(&setting_privileges_file, "setting_privileges.sql");
if (show_setting || debug)
{
@@ -2480,6 +2482,7 @@ setup_data_file_paths(void)
check_input(system_constraints_file);
check_input(system_functions_file);
check_input(system_views_file);
+ check_input(setting_privileges_file);
}
@@ -2827,6 +2830,8 @@ initialize_data_directory(void)
setup_run_file(cmdfd, system_views_file);
+ setup_run_file(cmdfd, setting_privileges_file);
+
setup_description(cmdfd);
setup_collation(cmdfd);
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index 6086d57cf3..a84efd79e5 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, SETTING 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, "SETTING") == 0)
+ {
+ CONVERT_PRIV('s', "SET VALUE");
+ 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 49bf0907cd..b000d3012a 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -3421,6 +3421,7 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te)
strcmp(type, "SCHEMA") == 0 ||
strcmp(type, "EVENT TRIGGER") == 0 ||
strcmp(type, "FOREIGN DATA WRAPPER") == 0 ||
+ strcmp(type, "SETTING") == 0 ||
strcmp(type, "SERVER") == 0 ||
strcmp(type, "PUBLICATION") == 0 ||
strcmp(type, "SUBSCRIPTION") == 0 ||
@@ -3604,6 +3605,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, "SETTING") == 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 e3ddf19959..2d94935cf1 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -14115,6 +14115,9 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
case DEFACLOBJ_NAMESPACE:
type = "SCHEMAS";
break;
+ case DEFACLOBJ_SETTING:
+ type = "SETTINGS";
+ break;
default:
/* shouldn't get here */
fatal("unrecognized object type in default privileges: %d",
@@ -14158,7 +14161,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, SETTING 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 10383c713f..a20a1062fd 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,72 @@ 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)
+{
+ PQExpBuffer buf = createPQExpBuffer();
+ PGresult *res;
+ int i;
+
+ printfPQExpBuffer(buf, "SELECT string_agg(acl.privilege_type, ', ' ORDER BY acl.privilege_type), "
+ "set_acl.setting, "
+ "grantee.rolname AS grantee, "
+ "acl.is_grantable, "
+ "grantor.rolname AS grantor "
+ "FROM pg_catalog.pg_setting_acl set_acl, "
+ "LATERAL (SELECT * FROM aclexplode(set_acl.setacl)) acl "
+ "JOIN pg_catalog.pg_authid grantee "
+ "ON acl.grantee = grantee.oid "
+ "LEFT JOIN pg_catalog.pg_authid grantor ON "
+ "acl.grantor = grantor.oid "
+ "WHERE acl.grantee > 0 "
+ "GROUP BY setting, grantee.rolname, is_grantable, grantor.rolname"
+ );
+
+ res = executeQuery(conn, buf->data);
+
+ if (PQntuples(res) > 0)
+ fprintf(OPF, "--\n-- Role privileges on configuration parameters\n--\n\n");
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ char *privilege = PQgetvalue(res, i, 0);
+ char *setting = PQgetvalue(res, i, 1);
+ char *grantee = PQgetvalue(res, i, 2);
+ char *grantable = PQgetvalue(res, i, 3);
+
+ fprintf(OPF, "GRANT %s", privilege);
+ fprintf(OPF, " ON %s", setting);
+ fprintf(OPF, " TO %s", fmtId(grantee));
+ if (*grantable == 't')
+ fprintf(OPF, " WITH GRANT OPTION");
+
+ /*
+ * We don't track the grantor very carefully in the backend, so cope
+ * with the possibility that it has been dropped.
+ */
+ if (!PQgetisnull(res, i, 4))
+ {
+ char *grantor = PQgetvalue(res, i, 4);
+
+ fprintf(OPF, " GRANTED BY %s", fmtId(grantor));
+ }
+ fprintf(OPF, ";\n");
+ }
+
+ PQclear(res);
+ destroyPQExpBuffer(buf);
+
+ fprintf(OPF, "\n\n");
+}
+
/*
* Dump tablespaces.
*/
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 344482ec87..a0c3966da9 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -92,6 +92,7 @@ typedef enum ObjectClass
OCLASS_TYPE, /* pg_type */
OCLASS_CAST, /* pg_cast */
OCLASS_COLLATION, /* pg_collation */
+ OCLASS_SETTING, /* pg_setting_acl */
OCLASS_CONSTRAINT, /* pg_constraint */
OCLASS_CONVERSION, /* pg_conversion */
OCLASS_DEFAULT, /* pg_attrdef */
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index 2a79155636..12ec9b0a31 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_SETTING 'c' /* configuration parameter */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 0859dc81ca..368e19d6d3 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7196,6 +7196,25 @@
proname => 'has_type_privilege', provolatile => 's', prorettype => 'bool',
proargtypes => 'oid text', prosrc => 'has_type_privilege_id' },
+{ oid => '8050', descr => 'user privilege on setting by username, setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'name text text', prosrc => 'has_setting_privilege_name_name' },
+{ oid => '8051', descr => 'user privilege on setting by username, setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'name oid text', prosrc => 'has_setting_privilege_name_id' },
+{ oid => '8052', descr => 'user privilege on setting by user oid, setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid text text', prosrc => 'has_setting_privilege_id_name' },
+{ oid => '8053', descr => 'user privilege on setting by user oid, setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid oid text', prosrc => 'has_setting_privilege_id_id' },
+{ oid => '8054', descr => 'current user privilege on setting by setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'text text', prosrc => 'has_setting_privilege_name' },
+{ oid => '8055', descr => 'current user privilege on setting by setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid text', prosrc => 'has_setting_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/catalog/pg_setting_acl.h b/src/include/catalog/pg_setting_acl.h
new file mode 100644
index 0000000000..b7dee55e5c
--- /dev/null
+++ b/src/include/catalog/pg_setting_acl.h
@@ -0,0 +1,63 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_setting_acl.h
+ * definition of the "configuration parameter" system catalog
+ * (pg_setting_acl).
+ *
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_setting_acl.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SETTING_ACL_H
+#define PG_SETTING_ACL_H
+
+#include "catalog/genbki.h"
+#include "catalog/pg_setting_acl_d.h"
+
+/* ----------------
+ * pg_setting_acl definition. cpp turns this into
+ * typedef struct FormData_pg_setting_acl
+ * ----------------
+ */
+CATALOG(pg_setting_acl,8924,SettingAclRelationId) BKI_SHARED_RELATION
+{
+ Oid oid; /* oid */
+ /*
+
+ * Variable-length fields start here, but we allow direct access to
+ * setting.
+ */
+ text setting BKI_FORCE_NOT_NULL;
+
+#ifdef CATALOG_VARLEN
+ /* Access privileges */
+ aclitem setacl[1] BKI_DEFAULT(_null_);
+#endif
+} FormData_pg_setting_acl;
+
+
+/* ----------------
+ * Form_pg_setting_acl corresponds to a pointer to a tuple with
+ * the format of pg_setting_acl relation.
+ * ----------------
+ */
+typedef FormData_pg_setting_acl *Form_pg_setting_acl;
+
+DECLARE_TOAST(pg_setting_acl, 8925, 8926);
+#define PgSettingAclToastTable 8925
+#define PgSettingAclToastIndex 8926
+
+DECLARE_UNIQUE_INDEX(pg_setting_acl_setting_index, 8927, SettingAclSettingIndexId, on pg_setting_acl using btree(setting text_ops));
+DECLARE_UNIQUE_INDEX_PKEY(pg_setting_acl_oid_index, 8928, SettingAclOidIndexId, on pg_setting_acl using btree(oid oid_ops));
+
+extern Oid SettingAclCreate(const char *setting, bool if_not_exists);
+
+#endif /* PG_SETTING_ACL_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 3e9bdc781f..c76f59c6b2 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -92,7 +92,9 @@ 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 N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */
+#define ACL_SET_VALUE (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
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
@@ -1795,6 +1797,7 @@ typedef enum ObjectType
OBJECT_CAST,
OBJECT_COLUMN,
OBJECT_COLLATION,
+ OBJECT_SETTING,
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DEFAULT,
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index bcef7eed2f..f5ed8c2082 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -307,6 +307,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 1ce4c5556e..e6d7761643 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -146,9 +146,11 @@ 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_ALTER_SYSTEM_CHR 'A'
/* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTc"
+#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcsA"
/*
* Bitmasks defining "all rights" for each supported object type
@@ -165,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_SETTING (ACL_SET_VALUE|ACL_ALTER_SYSTEM)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -223,6 +226,8 @@ extern void select_best_grantor(Oid roleId, AclMode privileges,
extern void initialize_acl(void);
+extern Oid get_setting_oid(const char *setting, bool missing_ok);
+
/*
* prototypes for functions in aclchk.c
*/
@@ -243,6 +248,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_setting_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,
@@ -271,6 +278,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_setting_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 6bb81707b0..3bc4dd7861 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -379,6 +379,8 @@ extern int set_config_option(const char *name, const char *value,
GucAction action, bool changeVal, int elevel,
bool is_reload);
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..7a8ba82770 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_setting_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..5961921462 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -89,6 +89,8 @@ enum SysCacheIdentifier
REPLORIGNAME,
RULERELNAME,
SEQRELID,
+ SETTINGNAME,
+ SETTINGOID,
STATEXTDATASTXOID,
STATEXTNAMENSP,
STATEXTOID,
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 a906f8f03d..04fc9c2e25 100644
--- a/src/test/modules/test_pg_dump/t/001_base.pl
+++ b/src/test/modules/test_pg_dump/t/001_base.pl
@@ -317,6 +317,53 @@ my %tests = (
like => { pg_dumpall_globals => 1, },
},
+ 'GRANT ALTER SYSTEM ON ignore_checksum_failure' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT ALTER SYSTEM ON ignore_checksum_failure TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM ON ignore_checksum_failure TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT SET VALUE ON my.missing.guc' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET VALUE ON my.missing.guc TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT SET VALUE ON my\.missing\.guc TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT SET VALUE, ALTER SYSTEM ON something WITH GRANT OPTION' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET VALUE, ALTER SYSTEM ON something TO regress_dump_test_role WITH GRANT OPTION;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM, SET VALUE ON something TO regress_dump_test_role WITH GRANT OPTION GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALTER SYSTEM ON MyReallyLong.ButValidCustom.GucNameThatCannotFit.InNamedata64Byte.Format' => {
+ create_order => 2,
+ create_sql =>
+ # configuration parameters get cased folded
+ 'GRANT ALTER SYSTEM ON MyReallyLong.ButValidCustom.GucNameThatCannotFit.InNamedata64Byte.Format TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM ON myreallylong\.butvalidcustom\.gucnamethatcannotfit\.innamedata64byte\.format TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALTER SYSTEM, SET VALUE ON my.guc TO regress_dump_test_role GRANTED BY CURRENT_ROLE' => {
+ create_order => 2,
+ create_sql =>
+ # GRANTED BY CURRENT_ROLE is allowed for SQL compatibility, but is ignored
+ 'GRANT ALTER SYSTEM, SET VALUE ON my.guc TO regress_dump_test_role GRANTED BY CURRENT_ROLE;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM, SET VALUE ON my\.guc TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
'CREATE SCHEMA public' => {
regexp => qr/^CREATE SCHEMA public;/m,
like => {
diff --git a/src/test/regress/expected/guc_privs.out b/src/test/regress/expected/guc_privs.out
new file mode 100644
index 0000000000..c2033e77d1
--- /dev/null
+++ b/src/test/regress/expected/guc_privs.out
@@ -0,0 +1,780 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform all 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;
+-- Check setting privileges prior to creating any
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+ grantee | setting | privilege_type | is_grantable
+---------+-------------------------------------+----------------+--------------
+ | DateStyle | SET VALUE | f
+ | IntervalStyle | SET VALUE | f
+ | TimeZone | SET VALUE | f
+ | application_name | SET VALUE | f
+ | array_nulls | SET VALUE | f
+ | backend_flush_after | SET VALUE | f
+ | backslash_quote | SET VALUE | f
+ | bytea_output | SET VALUE | f
+ | check_function_bodies | SET VALUE | f
+ | client_connection_check_interval | SET VALUE | f
+ | client_min_messages | SET VALUE | f
+ | commit_siblings | SET VALUE | f
+ | constraint_exclusion | SET VALUE | f
+ | cpu_index_tuple_cost | SET VALUE | f
+ | cpu_operator_cost | SET VALUE | f
+ | cpu_tuple_cost | SET VALUE | f
+ | cursor_tuple_fraction | SET VALUE | f
+ | debug_pretty_print | SET VALUE | f
+ | debug_print_parse | SET VALUE | f
+ | debug_print_plan | SET VALUE | f
+ | debug_print_rewritten | SET VALUE | f
+ | default_statistics_target | SET VALUE | f
+ | default_table_access_method | SET VALUE | f
+ | default_tablespace | SET VALUE | f
+ | default_text_search_config | SET VALUE | f
+ | default_toast_compression | SET VALUE | f
+ | default_transaction_deferrable | SET VALUE | f
+ | default_transaction_isolation | SET VALUE | f
+ | default_transaction_read_only | SET VALUE | f
+ | default_with_oids | SET VALUE | f
+ | effective_cache_size | SET VALUE | f
+ | effective_io_concurrency | SET VALUE | f
+ | enable_async_append | SET VALUE | f
+ | enable_bitmapscan | SET VALUE | f
+ | enable_gathermerge | SET VALUE | f
+ | enable_hashagg | SET VALUE | f
+ | enable_hashjoin | SET VALUE | f
+ | enable_incremental_sort | SET VALUE | f
+ | enable_indexonlyscan | SET VALUE | f
+ | enable_indexscan | SET VALUE | f
+ | enable_material | SET VALUE | f
+ | enable_memoize | SET VALUE | f
+ | enable_mergejoin | SET VALUE | f
+ | enable_nestloop | SET VALUE | f
+ | enable_parallel_append | SET VALUE | f
+ | enable_parallel_hash | SET VALUE | f
+ | enable_partition_pruning | SET VALUE | f
+ | enable_partitionwise_aggregate | SET VALUE | f
+ | enable_partitionwise_join | SET VALUE | f
+ | enable_seqscan | SET VALUE | f
+ | enable_sort | SET VALUE | f
+ | enable_tidscan | SET VALUE | f
+ | escape_string_warning | SET VALUE | f
+ | exit_on_error | SET VALUE | f
+ | extra_float_digits | SET VALUE | f
+ | force_parallel_mode | SET VALUE | f
+ | from_collapse_limit | SET VALUE | f
+ | geqo | SET VALUE | f
+ | geqo_effort | SET VALUE | f
+ | geqo_generations | SET VALUE | f
+ | geqo_pool_size | SET VALUE | f
+ | geqo_seed | SET VALUE | f
+ | geqo_selection_bias | SET VALUE | f
+ | geqo_threshold | SET VALUE | f
+ | gin_fuzzy_search_limit | SET VALUE | f
+ | gin_pending_list_limit | SET VALUE | f
+ | hash_mem_multiplier | SET VALUE | f
+ | idle_in_transaction_session_timeout | SET VALUE | f
+ | idle_session_timeout | SET VALUE | f
+ | jit | SET VALUE | f
+ | jit_above_cost | SET VALUE | f
+ | jit_expressions | SET VALUE | f
+ | jit_inline_above_cost | SET VALUE | f
+ | jit_optimize_above_cost | SET VALUE | f
+ | jit_tuple_deforming | SET VALUE | f
+ | join_collapse_limit | SET VALUE | f
+ | lc_monetary | SET VALUE | f
+ | lc_numeric | SET VALUE | f
+ | lc_time | SET VALUE | f
+ | local_preload_libraries | SET VALUE | f
+ | lock_timeout | SET VALUE | f
+ | log_parameter_max_length_on_error | SET VALUE | f
+ | logical_decoding_work_mem | SET VALUE | f
+ | maintenance_io_concurrency | SET VALUE | f
+ | maintenance_work_mem | SET VALUE | f
+ | max_parallel_maintenance_workers | SET VALUE | f
+ | max_parallel_workers | SET VALUE | f
+ | max_parallel_workers_per_gather | SET VALUE | f
+ | min_parallel_index_scan_size | SET VALUE | f
+ | min_parallel_table_scan_size | SET VALUE | f
+ | optimize_bounded_sort | SET VALUE | f
+ | parallel_leader_participation | SET VALUE | f
+ | parallel_setup_cost | SET VALUE | f
+ | parallel_tuple_cost | SET VALUE | f
+ | password_encryption | SET VALUE | f
+ | plan_cache_mode | SET VALUE | f
+ | quote_all_identifiers | SET VALUE | f
+ | random_page_cost | SET VALUE | f
+ | role | SET VALUE | f
+ | row_security | SET VALUE | f
+ | search_path | SET VALUE | f
+ | seed | SET VALUE | f
+ | seq_page_cost | SET VALUE | f
+ | ssl_renegotiation_limit | SET VALUE | f
+ | standard_conforming_strings | SET VALUE | f
+ | statement_timeout | SET VALUE | f
+ | synchronize_seqscans | SET VALUE | f
+ | synchronous_commit | SET VALUE | f
+ | tcp_keepalives_count | SET VALUE | f
+ | tcp_keepalives_idle | SET VALUE | f
+ | tcp_keepalives_interval | SET VALUE | f
+ | tcp_user_timeout | SET VALUE | f
+ | temp_buffers | SET VALUE | f
+ | temp_tablespaces | SET VALUE | f
+ | timezone_abbreviations | SET VALUE | f
+ | trace_notify | SET VALUE | f
+ | trace_sort | SET VALUE | f
+ | trace_syncscan | SET VALUE | f
+ | transaction_deferrable | SET VALUE | f
+ | transaction_isolation | SET VALUE | f
+ | transaction_read_only | SET VALUE | f
+ | transform_null_equals | SET VALUE | f
+ | vacuum_cost_delay | SET VALUE | f
+ | vacuum_cost_limit | SET VALUE | f
+ | vacuum_cost_page_dirty | SET VALUE | f
+ | vacuum_cost_page_hit | SET VALUE | f
+ | vacuum_cost_page_miss | SET VALUE | f
+ | vacuum_failsafe_age | SET VALUE | f
+ | vacuum_freeze_min_age | SET VALUE | f
+ | vacuum_freeze_table_age | SET VALUE | f
+ | vacuum_multixact_failsafe_age | SET VALUE | f
+ | vacuum_multixact_freeze_min_age | SET VALUE | f
+ | vacuum_multixact_freeze_table_age | SET VALUE | f
+ | wal_sender_timeout | SET VALUE | f
+ | wal_skip_threshold | SET VALUE | f
+ | work_mem | SET VALUE | f
+ | xmlbinary | SET VALUE | f
+ | xmloption | SET VALUE | f
+(138 rows)
+
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+-- Check the new role does not yet have privileges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ f
+(1 row)
+
+-- Check inappropriate and nonsense privilege types
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+-- Grant privileges on settings to the new non-superuser role
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Check setting privileges after creating some
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+ grantee | setting | privilege_type | is_grantable
+-----------------------------+-------------------------------------+----------------+--------------
+ regress_host_resource_admin | autovacuum_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | autovacuum_work_mem | SET VALUE | f
+ regress_host_resource_admin | hash_mem_multiplier | ALTER SYSTEM | f
+ regress_host_resource_admin | hash_mem_multiplier | SET VALUE | f
+ regress_host_resource_admin | logical_decoding_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | logical_decoding_work_mem | SET VALUE | f
+ regress_host_resource_admin | maintenance_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | maintenance_work_mem | SET VALUE | f
+ regress_host_resource_admin | max_stack_depth | ALTER SYSTEM | f
+ regress_host_resource_admin | max_stack_depth | SET VALUE | f
+ regress_host_resource_admin | min_dynamic_shared_memory | ALTER SYSTEM | f
+ regress_host_resource_admin | min_dynamic_shared_memory | SET VALUE | f
+ regress_host_resource_admin | shared_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | shared_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_file_limit | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_file_limit | SET VALUE | f
+ regress_host_resource_admin | work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | work_mem | SET VALUE | f
+ | DateStyle | SET VALUE | f
+ | IntervalStyle | SET VALUE | f
+ | TimeZone | SET VALUE | f
+ | application_name | SET VALUE | f
+ | array_nulls | SET VALUE | f
+ | backend_flush_after | SET VALUE | f
+ | backslash_quote | SET VALUE | f
+ | bytea_output | SET VALUE | f
+ | check_function_bodies | SET VALUE | f
+ | client_connection_check_interval | SET VALUE | f
+ | client_min_messages | SET VALUE | f
+ | commit_siblings | SET VALUE | f
+ | constraint_exclusion | SET VALUE | f
+ | cpu_index_tuple_cost | SET VALUE | f
+ | cpu_operator_cost | SET VALUE | f
+ | cpu_tuple_cost | SET VALUE | f
+ | cursor_tuple_fraction | SET VALUE | f
+ | debug_pretty_print | SET VALUE | f
+ | debug_print_parse | SET VALUE | f
+ | debug_print_plan | SET VALUE | f
+ | debug_print_rewritten | SET VALUE | f
+ | default_statistics_target | SET VALUE | f
+ | default_table_access_method | SET VALUE | f
+ | default_tablespace | SET VALUE | f
+ | default_text_search_config | SET VALUE | f
+ | default_toast_compression | SET VALUE | f
+ | default_transaction_deferrable | SET VALUE | f
+ | default_transaction_isolation | SET VALUE | f
+ | default_transaction_read_only | SET VALUE | f
+ | default_with_oids | SET VALUE | f
+ | effective_cache_size | SET VALUE | f
+ | effective_io_concurrency | SET VALUE | f
+ | enable_async_append | SET VALUE | f
+ | enable_bitmapscan | SET VALUE | f
+ | enable_gathermerge | SET VALUE | f
+ | enable_hashagg | SET VALUE | f
+ | enable_hashjoin | SET VALUE | f
+ | enable_incremental_sort | SET VALUE | f
+ | enable_indexonlyscan | SET VALUE | f
+ | enable_indexscan | SET VALUE | f
+ | enable_material | SET VALUE | f
+ | enable_memoize | SET VALUE | f
+ | enable_mergejoin | SET VALUE | f
+ | enable_nestloop | SET VALUE | f
+ | enable_parallel_append | SET VALUE | f
+ | enable_parallel_hash | SET VALUE | f
+ | enable_partition_pruning | SET VALUE | f
+ | enable_partitionwise_aggregate | SET VALUE | f
+ | enable_partitionwise_join | SET VALUE | f
+ | enable_seqscan | SET VALUE | f
+ | enable_sort | SET VALUE | f
+ | enable_tidscan | SET VALUE | f
+ | escape_string_warning | SET VALUE | f
+ | exit_on_error | SET VALUE | f
+ | extra_float_digits | SET VALUE | f
+ | force_parallel_mode | SET VALUE | f
+ | from_collapse_limit | SET VALUE | f
+ | geqo | SET VALUE | f
+ | geqo_effort | SET VALUE | f
+ | geqo_generations | SET VALUE | f
+ | geqo_pool_size | SET VALUE | f
+ | geqo_seed | SET VALUE | f
+ | geqo_selection_bias | SET VALUE | f
+ | geqo_threshold | SET VALUE | f
+ | gin_fuzzy_search_limit | SET VALUE | f
+ | gin_pending_list_limit | SET VALUE | f
+ | hash_mem_multiplier | SET VALUE | f
+ | idle_in_transaction_session_timeout | SET VALUE | f
+ | idle_session_timeout | SET VALUE | f
+ | jit | SET VALUE | f
+ | jit_above_cost | SET VALUE | f
+ | jit_expressions | SET VALUE | f
+ | jit_inline_above_cost | SET VALUE | f
+ | jit_optimize_above_cost | SET VALUE | f
+ | jit_tuple_deforming | SET VALUE | f
+ | join_collapse_limit | SET VALUE | f
+ | lc_monetary | SET VALUE | f
+ | lc_numeric | SET VALUE | f
+ | lc_time | SET VALUE | f
+ | local_preload_libraries | SET VALUE | f
+ | lock_timeout | SET VALUE | f
+ | log_parameter_max_length_on_error | SET VALUE | f
+ | logical_decoding_work_mem | SET VALUE | f
+ | maintenance_io_concurrency | SET VALUE | f
+ | maintenance_work_mem | SET VALUE | f
+ | max_parallel_maintenance_workers | SET VALUE | f
+ | max_parallel_workers | SET VALUE | f
+ | max_parallel_workers_per_gather | SET VALUE | f
+ | min_parallel_index_scan_size | SET VALUE | f
+ | min_parallel_table_scan_size | SET VALUE | f
+ | optimize_bounded_sort | SET VALUE | f
+ | parallel_leader_participation | SET VALUE | f
+ | parallel_setup_cost | SET VALUE | f
+ | parallel_tuple_cost | SET VALUE | f
+ | password_encryption | SET VALUE | f
+ | plan_cache_mode | SET VALUE | f
+ | quote_all_identifiers | SET VALUE | f
+ | random_page_cost | SET VALUE | f
+ | role | SET VALUE | f
+ | row_security | SET VALUE | f
+ | search_path | SET VALUE | f
+ | seed | SET VALUE | f
+ | seq_page_cost | SET VALUE | f
+ | ssl_renegotiation_limit | SET VALUE | f
+ | standard_conforming_strings | SET VALUE | f
+ | statement_timeout | SET VALUE | f
+ | synchronize_seqscans | SET VALUE | f
+ | synchronous_commit | SET VALUE | f
+ | tcp_keepalives_count | SET VALUE | f
+ | tcp_keepalives_idle | SET VALUE | f
+ | tcp_keepalives_interval | SET VALUE | f
+ | tcp_user_timeout | SET VALUE | f
+ | temp_buffers | SET VALUE | f
+ | temp_tablespaces | SET VALUE | f
+ | timezone_abbreviations | SET VALUE | f
+ | trace_notify | SET VALUE | f
+ | trace_sort | SET VALUE | f
+ | trace_syncscan | SET VALUE | f
+ | transaction_deferrable | SET VALUE | f
+ | transaction_isolation | SET VALUE | f
+ | transaction_read_only | SET VALUE | f
+ | transform_null_equals | SET VALUE | f
+ | vacuum_cost_delay | SET VALUE | f
+ | vacuum_cost_limit | SET VALUE | f
+ | vacuum_cost_page_dirty | SET VALUE | f
+ | vacuum_cost_page_hit | SET VALUE | f
+ | vacuum_cost_page_miss | SET VALUE | f
+ | vacuum_failsafe_age | SET VALUE | f
+ | vacuum_freeze_min_age | SET VALUE | f
+ | vacuum_freeze_table_age | SET VALUE | f
+ | vacuum_multixact_failsafe_age | SET VALUE | f
+ | vacuum_multixact_freeze_min_age | SET VALUE | f
+ | vacuum_multixact_freeze_table_age | SET VALUE | f
+ | wal_sender_timeout | SET VALUE | f
+ | wal_skip_threshold | SET VALUE | f
+ | work_mem | SET VALUE | f
+ | xmlbinary | SET VALUE | f
+ | xmloption | SET VALUE | f
+(158 rows)
+
+-- Check the new role now has privilges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE WITH GRANT OPTION, ALTER SYSTEM WITH GRANT OPTION');
+ has_setting_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_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+SELECT has_setting_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_setting_privilege('regress_host_resource_admin',
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('hash_mem_multiplier', 'set value');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'work_mem'),
+ 'alter system with grant option');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+-- Check setting privileges show up in view
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+ grantee | setting | privilege_type | is_grantable
+-----------------------------+-------------------------------------+----------------+--------------
+ regress_host_resource_admin | autovacuum_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | autovacuum_work_mem | SET VALUE | f
+ regress_host_resource_admin | hash_mem_multiplier | ALTER SYSTEM | f
+ regress_host_resource_admin | hash_mem_multiplier | SET VALUE | f
+ regress_host_resource_admin | logical_decoding_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | logical_decoding_work_mem | SET VALUE | f
+ regress_host_resource_admin | maintenance_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | maintenance_work_mem | SET VALUE | f
+ regress_host_resource_admin | max_stack_depth | ALTER SYSTEM | f
+ regress_host_resource_admin | max_stack_depth | SET VALUE | f
+ regress_host_resource_admin | min_dynamic_shared_memory | ALTER SYSTEM | f
+ regress_host_resource_admin | min_dynamic_shared_memory | SET VALUE | f
+ regress_host_resource_admin | shared_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | shared_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_file_limit | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_file_limit | SET VALUE | f
+ regress_host_resource_admin | work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | work_mem | SET VALUE | f
+ | DateStyle | SET VALUE | f
+ | IntervalStyle | SET VALUE | f
+ | TimeZone | SET VALUE | f
+ | application_name | SET VALUE | f
+ | array_nulls | SET VALUE | f
+ | backend_flush_after | SET VALUE | f
+ | backslash_quote | SET VALUE | f
+ | bytea_output | SET VALUE | f
+ | check_function_bodies | SET VALUE | f
+ | client_connection_check_interval | SET VALUE | f
+ | client_min_messages | SET VALUE | f
+ | commit_siblings | SET VALUE | f
+ | constraint_exclusion | SET VALUE | f
+ | cpu_index_tuple_cost | SET VALUE | f
+ | cpu_operator_cost | SET VALUE | f
+ | cpu_tuple_cost | SET VALUE | f
+ | cursor_tuple_fraction | SET VALUE | f
+ | debug_pretty_print | SET VALUE | f
+ | debug_print_parse | SET VALUE | f
+ | debug_print_plan | SET VALUE | f
+ | debug_print_rewritten | SET VALUE | f
+ | default_statistics_target | SET VALUE | f
+ | default_table_access_method | SET VALUE | f
+ | default_tablespace | SET VALUE | f
+ | default_text_search_config | SET VALUE | f
+ | default_toast_compression | SET VALUE | f
+ | default_transaction_deferrable | SET VALUE | f
+ | default_transaction_isolation | SET VALUE | f
+ | default_transaction_read_only | SET VALUE | f
+ | default_with_oids | SET VALUE | f
+ | effective_cache_size | SET VALUE | f
+ | effective_io_concurrency | SET VALUE | f
+ | enable_async_append | SET VALUE | f
+ | enable_bitmapscan | SET VALUE | f
+ | enable_gathermerge | SET VALUE | f
+ | enable_hashagg | SET VALUE | f
+ | enable_hashjoin | SET VALUE | f
+ | enable_incremental_sort | SET VALUE | f
+ | enable_indexonlyscan | SET VALUE | f
+ | enable_indexscan | SET VALUE | f
+ | enable_material | SET VALUE | f
+ | enable_memoize | SET VALUE | f
+ | enable_mergejoin | SET VALUE | f
+ | enable_nestloop | SET VALUE | f
+ | enable_parallel_append | SET VALUE | f
+ | enable_parallel_hash | SET VALUE | f
+ | enable_partition_pruning | SET VALUE | f
+ | enable_partitionwise_aggregate | SET VALUE | f
+ | enable_partitionwise_join | SET VALUE | f
+ | enable_seqscan | SET VALUE | f
+ | enable_sort | SET VALUE | f
+ | enable_tidscan | SET VALUE | f
+ | escape_string_warning | SET VALUE | f
+ | exit_on_error | SET VALUE | f
+ | extra_float_digits | SET VALUE | f
+ | force_parallel_mode | SET VALUE | f
+ | from_collapse_limit | SET VALUE | f
+ | geqo | SET VALUE | f
+ | geqo_effort | SET VALUE | f
+ | geqo_generations | SET VALUE | f
+ | geqo_pool_size | SET VALUE | f
+ | geqo_seed | SET VALUE | f
+ | geqo_selection_bias | SET VALUE | f
+ | geqo_threshold | SET VALUE | f
+ | gin_fuzzy_search_limit | SET VALUE | f
+ | gin_pending_list_limit | SET VALUE | f
+ | hash_mem_multiplier | SET VALUE | f
+ | idle_in_transaction_session_timeout | SET VALUE | f
+ | idle_session_timeout | SET VALUE | f
+ | jit | SET VALUE | f
+ | jit_above_cost | SET VALUE | f
+ | jit_expressions | SET VALUE | f
+ | jit_inline_above_cost | SET VALUE | f
+ | jit_optimize_above_cost | SET VALUE | f
+ | jit_tuple_deforming | SET VALUE | f
+ | join_collapse_limit | SET VALUE | f
+ | lc_monetary | SET VALUE | f
+ | lc_numeric | SET VALUE | f
+ | lc_time | SET VALUE | f
+ | local_preload_libraries | SET VALUE | f
+ | lock_timeout | SET VALUE | f
+ | log_parameter_max_length_on_error | SET VALUE | f
+ | logical_decoding_work_mem | SET VALUE | f
+ | maintenance_io_concurrency | SET VALUE | f
+ | maintenance_work_mem | SET VALUE | f
+ | max_parallel_maintenance_workers | SET VALUE | f
+ | max_parallel_workers | SET VALUE | f
+ | max_parallel_workers_per_gather | SET VALUE | f
+ | min_parallel_index_scan_size | SET VALUE | f
+ | min_parallel_table_scan_size | SET VALUE | f
+ | optimize_bounded_sort | SET VALUE | f
+ | parallel_leader_participation | SET VALUE | f
+ | parallel_setup_cost | SET VALUE | f
+ | parallel_tuple_cost | SET VALUE | f
+ | password_encryption | SET VALUE | f
+ | plan_cache_mode | SET VALUE | f
+ | quote_all_identifiers | SET VALUE | f
+ | random_page_cost | SET VALUE | f
+ | role | SET VALUE | f
+ | row_security | SET VALUE | f
+ | search_path | SET VALUE | f
+ | seed | SET VALUE | f
+ | seq_page_cost | SET VALUE | f
+ | ssl_renegotiation_limit | SET VALUE | f
+ | standard_conforming_strings | SET VALUE | f
+ | statement_timeout | SET VALUE | f
+ | synchronize_seqscans | SET VALUE | f
+ | synchronous_commit | SET VALUE | f
+ | tcp_keepalives_count | SET VALUE | f
+ | tcp_keepalives_idle | SET VALUE | f
+ | tcp_keepalives_interval | SET VALUE | f
+ | tcp_user_timeout | SET VALUE | f
+ | temp_buffers | SET VALUE | f
+ | temp_tablespaces | SET VALUE | f
+ | timezone_abbreviations | SET VALUE | f
+ | trace_notify | SET VALUE | f
+ | trace_sort | SET VALUE | f
+ | trace_syncscan | SET VALUE | f
+ | transaction_deferrable | SET VALUE | f
+ | transaction_isolation | SET VALUE | f
+ | transaction_read_only | SET VALUE | f
+ | transform_null_equals | SET VALUE | f
+ | vacuum_cost_delay | SET VALUE | f
+ | vacuum_cost_limit | SET VALUE | f
+ | vacuum_cost_page_dirty | SET VALUE | f
+ | vacuum_cost_page_hit | SET VALUE | f
+ | vacuum_cost_page_miss | SET VALUE | f
+ | vacuum_failsafe_age | SET VALUE | f
+ | vacuum_freeze_min_age | SET VALUE | f
+ | vacuum_freeze_table_age | SET VALUE | f
+ | vacuum_multixact_failsafe_age | SET VALUE | f
+ | vacuum_multixact_freeze_min_age | SET VALUE | f
+ | vacuum_multixact_freeze_table_age | SET VALUE | f
+ | wal_sender_timeout | SET VALUE | f
+ | wal_skip_threshold | SET VALUE | f
+ | work_mem | SET VALUE | f
+ | xmlbinary | SET VALUE | f
+ | xmloption | SET VALUE | f
+(158 rows)
+
+-- 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
+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
+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 setting temp_buffers
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting logical_decoding_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting autovacuum_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting temp_file_limit
+-- Use "revoke" to remove the privileges and allow the role to be dropped
+REVOKE SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 setting temp_buffers
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting logical_decoding_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting autovacuum_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting 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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 setting temp_buffers
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting logical_decoding_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting autovacuum_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting 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
+-- Create non-superuser with privileges to configure plgsql custom variables
+CREATE ROLE regress_plpgsql_admin NOSUPERUSER;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin;
+SET plpgsql.extra_errors TO 'all';
+SET plpgsql.extra_warnings TO 'all';
+RESET plpgsql.extra_errors;
+RESET plpgsql.extra_warnings;
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all';
+ERROR: permission denied to set parameter "plpgsql.extra_errors"
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all';
+ERROR: permission denied to set parameter "plpgsql.extra_warnings"
+ALTER SYSTEM RESET plpgsql.extra_errors;
+ERROR: permission denied to set parameter "plpgsql.extra_errors"
+ALTER SYSTEM RESET plpgsql.extra_warnings;
+ERROR: permission denied to set parameter "plpgsql.extra_warnings"
+SET SESSION AUTHORIZATION regress_admin;
+GRANT SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+TO regress_plpgsql_admin;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin; -- ok
+SET plpgsql.extra_errors TO 'all'; -- ok
+SET plpgsql.extra_warnings TO 'all'; -- ok
+RESET plpgsql.extra_errors; -- ok
+RESET plpgsql.extra_warnings; -- ok
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all'; -- ok
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all'; -- ok
+ALTER SYSTEM RESET plpgsql.extra_errors; -- ok
+ALTER SYSTEM RESET plpgsql.extra_warnings; -- ok
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_plpgsql_admin; -- fail, privileges remain
+ERROR: role "regress_plpgsql_admin" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting plpgsql.extra_warnings
+privileges for setting plpgsql.extra_errors
+REVOKE SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+FROM regress_plpgsql_admin;
+DROP ROLE regress_plpgsql_admin; -- ok
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 291e21d7a6..1c762cea54 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -54,6 +54,33 @@ REVOKE pg_read_all_settings FROM regress_priv_user8;
DROP USER regress_priv_user10;
DROP USER regress_priv_user9;
DROP USER regress_priv_user8;
+GRANT SET VALUE ON enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON enable_nestloop TO regress_priv_user6;
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+GRANT ALTER SYSTEM ON enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON "" TO regress_priv_user7; -- bad name
+ERROR: zero-length delimited identifier at or near """"
+LINE 1: GRANT ALTER SYSTEM ON "" TO regress_priv_user7;
+ ^
+GRANT ALTER SYSTEM ON " " TO regress_priv_user7; -- bad name
+ERROR: invalid setting name " "
+GRANT ALTER SYSTEM ON " foo " TO regress_priv_user7; -- bad name
+ERROR: invalid setting name " foo "
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
ALTER GROUP regress_priv_group1 ADD USER regress_priv_user4;
@@ -2355,10 +2382,32 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+ERROR: role "regress_priv_user6" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting enable_memoize
+privileges for setting enable_nestloop
+DROP USER regress_priv_user7; -- privileges remain
+ERROR: role "regress_priv_user7" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting enable_seqscan
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for table persons2
+privileges for setting no_such_param
+privileges for setting no_such_extension.no_such_param
+privileges for setting no_such_extension.no_such_param.longer.than.maximum.namedata.length
DROP USER regress_priv_user8; -- does not exist
ERROR: role "regress_priv_user8" does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+DROP USER regress_priv_user7; -- ok
+REVOKE SET VALUE ON enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON enable_nestloop FROM regress_priv_user6;
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
CREATE TABLE lock_table (a int);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index d652f7b5fb..14a6f4c0b4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1708,6 +1708,19 @@ pg_sequences| SELECT n.nspname AS schemaname,
JOIN pg_class c ON ((c.oid = s.seqrelid)))
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char"));
+pg_setting_privileges| SELECT grantor.rolname AS grantor,
+ grantee.rolname AS grantee,
+ set_acl.setting,
+ acl.privilege_type,
+ acl.is_grantable
+ FROM pg_setting_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_settings| SELECT a.name,
a.setting,
a.unit,
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 63706a28cc..4a8208c789 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -147,6 +147,7 @@ pg_replication_origin|t
pg_rewrite|t
pg_seclabel|t
pg_sequence|t
+pg_setting_acl|t
pg_shdepend|t
pg_shdescription|t
pg_shseclabel|t
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 861c30a73a..9b85253532 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -89,7 +89,7 @@ test: brin_bloom brin_multi
# ----------
# Another group of parallel tests
# ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role guc_privs
# rules cannot run concurrently with any test that creates
# a view or rule in the public schema
diff --git a/src/test/regress/sql/guc_privs.sql b/src/test/regress/sql/guc_privs.sql
new file mode 100644
index 0000000000..076c136563
--- /dev/null
+++ b/src/test/regress/sql/guc_privs.sql
@@ -0,0 +1,185 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform all 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;
+-- Check setting privileges prior to creating any
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+-- Check the new role does not yet have privileges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+-- Check inappropriate and nonsense privilege types
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+-- Grant privileges on settings to the new non-superuser role
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Check setting privileges after creating some
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+-- Check the new role now has privilges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE 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_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER WITH GRANT OPTION');
+-- Check other function signatures
+SELECT has_setting_privilege('regress_host_resource_admin',
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET VALUE');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+SELECT has_setting_privilege('hash_mem_multiplier', 'set value');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'work_mem'),
+ 'alter system with grant option');
+-- Check setting privileges show up in view
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+-- 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
+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
+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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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
+-- Create non-superuser with privileges to configure plgsql custom variables
+CREATE ROLE regress_plpgsql_admin NOSUPERUSER;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin;
+SET plpgsql.extra_errors TO 'all';
+SET plpgsql.extra_warnings TO 'all';
+RESET plpgsql.extra_errors;
+RESET plpgsql.extra_warnings;
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all';
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all';
+ALTER SYSTEM RESET plpgsql.extra_errors;
+ALTER SYSTEM RESET plpgsql.extra_warnings;
+SET SESSION AUTHORIZATION regress_admin;
+GRANT SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+TO regress_plpgsql_admin;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin; -- ok
+SET plpgsql.extra_errors TO 'all'; -- ok
+SET plpgsql.extra_warnings TO 'all'; -- ok
+RESET plpgsql.extra_errors; -- ok
+RESET plpgsql.extra_warnings; -- ok
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all'; -- ok
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all'; -- ok
+ALTER SYSTEM RESET plpgsql.extra_errors; -- ok
+ALTER SYSTEM RESET plpgsql.extra_warnings; -- ok
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_plpgsql_admin; -- fail, privileges remain
+REVOKE SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+FROM regress_plpgsql_admin;
+DROP ROLE regress_plpgsql_admin; -- ok
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index c8c545b64c..ddbf6afa44 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -66,6 +66,33 @@ DROP USER regress_priv_user10;
DROP USER regress_priv_user9;
DROP USER regress_priv_user8;
+GRANT SET VALUE ON enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON enable_nestloop TO regress_priv_user6;
+
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+
+GRANT ALTER SYSTEM ON enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON "" TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON " " TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON " foo " TO regress_priv_user7; -- bad name
+
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
+
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
@@ -1426,10 +1453,23 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+DROP USER regress_priv_user7; -- privileges remain
DROP USER regress_priv_user8; -- does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+DROP USER regress_priv_user7; -- ok
+
+REVOKE SET VALUE ON enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON enable_nestloop FROM regress_priv_user6;
+
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
--
2.21.1 (Apple Git-122.3)
Mark Dilger <mark.dilger@enterprisedb.com> writes:
[ v8-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-SET-for-gucs.patch ]
I noticed that this is failing in the cfbot as a side-effect of my
cc50080a8, so here's a rebase (basically it just removes the no-
longer-necessary sanity_check.out hunk).
I haven't reviewed this particularly, or read the thread, but I'm
pretty disturbed by the contrib/ changes that it proposes.
1. If we need to change these two contrib modules, doesn't that imply
a lot of changes forced on external modules as well? What are the
security implications if somebody doesn't make such a change?
2. It looks to me like if someone installs the updated postgres_fdw.so,
but doesn't run ALTER EXTENSION UPDATE, they are going to be left with a
rather broken extension. This seems not good either, especially if it's
multiplied by a boatload of third-party extensions requiring updates.
So I think some more thought is needed to see if we can't avoid
the need to touch extension modules in this patch. Or at least
avoid the need for synchronized C-level and SQL-level updates,
because that is going to create a lot of pain for end users.
I'm also fairly distressed by the number of changes in guc.c,
mainly because I fear that that means that pending patches that
add GUC variables will be subtly incorrect/insecure if they're not
updated to account for this. Frankly, I also reject the apparent
position that we need to support forbidding users from touching
many of these GUCs. Or, if that's our position, why are there
per-GUC changes at all, rather than just redefining what the
context values mean? (That is, why not redefine USERSET and
SUSET as simply indicating the default ACL to be applied if there's
no entry in the catalog.)
regards, tom lane
Attachments:
v9-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-SET-for-gucs.patchtext/x-diff; charset=us-ascii; name*0=v9-0001-Allow-GRANT-of-SET-and-ALTER-SYSTEM-SET-for-gucs.pa; name*1=tchDownload
diff --git a/contrib/pg_trgm/Makefile b/contrib/pg_trgm/Makefile
index 1fbdc9ec1e..8fac4f6289 100644
--- a/contrib/pg_trgm/Makefile
+++ b/contrib/pg_trgm/Makefile
@@ -9,9 +9,9 @@ OBJS = \
trgm_regexp.o
EXTENSION = pg_trgm
-DATA = pg_trgm--1.5--1.6.sql pg_trgm--1.4--1.5.sql pg_trgm--1.3--1.4.sql \
- pg_trgm--1.3.sql pg_trgm--1.2--1.3.sql pg_trgm--1.1--1.2.sql \
- pg_trgm--1.0--1.1.sql
+DATA = pg_trgm--1.6--1.7.sql pg_trgm--1.5--1.6.sql pg_trgm--1.4--1.5.sql \
+ pg_trgm--1.3--1.4.sql pg_trgm--1.3.sql pg_trgm--1.2--1.3.sql \
+ pg_trgm--1.1--1.2.sql pg_trgm--1.0--1.1.sql
PGFILEDESC = "pg_trgm - trigram matching"
REGRESS = pg_trgm pg_word_trgm pg_strict_word_trgm
diff --git a/contrib/pg_trgm/pg_trgm--1.6--1.7.sql b/contrib/pg_trgm/pg_trgm--1.6--1.7.sql
new file mode 100644
index 0000000000..06b783acbd
--- /dev/null
+++ b/contrib/pg_trgm/pg_trgm--1.6--1.7.sql
@@ -0,0 +1,8 @@
+/* contrib/pg_trgm/pg_trgm--1.6--1.7.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_trgm UPDATE TO '1.7'" to load this file. \quit
+
+GRANT SET VALUE ON "pg_trgm.similarity_threshold" TO public;
+GRANT SET VALUE ON "pg_trgm.word_similarity_threshold" TO public;
+GRANT SET VALUE ON "pg_trgm.strict_word_similarity_threshold" TO public;
diff --git a/contrib/pg_trgm/pg_trgm.control b/contrib/pg_trgm/pg_trgm.control
index 1d6a9ddf25..6e3ee43c51 100644
--- a/contrib/pg_trgm/pg_trgm.control
+++ b/contrib/pg_trgm/pg_trgm.control
@@ -1,6 +1,6 @@
# pg_trgm extension
comment = 'text similarity measurement and index searching based on trigrams'
-default_version = '1.6'
+default_version = '1.7'
module_pathname = '$libdir/pg_trgm'
relocatable = true
trusted = true
diff --git a/contrib/postgres_fdw/Makefile b/contrib/postgres_fdw/Makefile
index c1b0cad453..3564015ee8 100644
--- a/contrib/postgres_fdw/Makefile
+++ b/contrib/postgres_fdw/Makefile
@@ -14,7 +14,8 @@ PG_CPPFLAGS = -I$(libpq_srcdir)
SHLIB_LINK_INTERNAL = $(libpq)
EXTENSION = postgres_fdw
-DATA = postgres_fdw--1.0.sql postgres_fdw--1.0--1.1.sql
+DATA = postgres_fdw--1.0.sql postgres_fdw--1.0--1.1.sql \
+ postgres_fdw--1.1--1.2.sql
REGRESS = postgres_fdw
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 572591a558..df58b5f339 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -534,7 +534,7 @@ _PG_init(void)
NULL,
&pgfdw_application_name,
NULL,
- PGC_USERSET,
+ PGC_SUSET,
0,
NULL,
NULL,
diff --git a/contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql b/contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql
new file mode 100644
index 0000000000..b174e04283
--- /dev/null
+++ b/contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql
@@ -0,0 +1,6 @@
+/* contrib/postgres_fdw/postgres_fdw--1.1--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION postgres_fdw UPDATE TO '1.2'" to load this file. \quit
+
+GRANT SET VALUE ON "postgres_fdw.application_name" TO public;
diff --git a/contrib/postgres_fdw/postgres_fdw.control b/contrib/postgres_fdw/postgres_fdw.control
index d489382064..a4b800be4f 100644
--- a/contrib/postgres_fdw/postgres_fdw.control
+++ b/contrib/postgres_fdw/postgres_fdw.control
@@ -1,5 +1,5 @@
# postgres_fdw extension
comment = 'foreign-data wrapper for remote PostgreSQL servers'
-default_version = '1.1'
+default_version = '1.2'
module_pathname = '$libdir/postgres_fdw'
relocatable = true
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 97e61b8043..7f899fa8bf 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -449,7 +449,7 @@ _PG_init(void)
NULL,
&sepgsql_debug_audit,
false,
- PGC_USERSET,
+ PGC_SUSET,
GUC_NOT_IN_SAMPLE,
NULL,
NULL,
diff --git a/contrib/sepgsql/sepgsql.sql.in b/contrib/sepgsql/sepgsql.sql.in
index 917d12dbbe..4dce769306 100644
--- a/contrib/sepgsql/sepgsql.sql.in
+++ b/contrib/sepgsql/sepgsql.sql.in
@@ -34,4 +34,5 @@ CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_setcon(text) RETURNS bool AS 'MODU
CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_mcstrans_in(text) RETURNS text AS 'MODULE_PATHNAME', 'sepgsql_mcstrans_in' LANGUAGE C STRICT;
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;
+GRANT SET VALUE ON "sepgsql.debug_audit" TO public;
SELECT sepgsql_restorecon(NULL);
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 83987a9904..7b288cce30 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -105,6 +105,11 @@
<entry>collations (locale information)</entry>
</row>
+ <row>
+ <entry><link linkend="catalog-pg-setting-acl"><structname>pg_setting_acl</structname></link></entry>
+ <entry>configuration parameters which have privileges granted to roles</entry>
+ </row>
+
<row>
<entry><link linkend="catalog-pg-constraint"><structname>pg_constraint</structname></link></entry>
<entry>check constraints, unique constraints, primary key constraints, foreign key constraints</entry>
@@ -2423,6 +2428,64 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</para>
</sect1>
+ <sect1 id="catalog-pg-setting-acl">
+ <title><structname>pg_setting_acl</structname></title>
+
+ <indexterm zone="catalog-pg-setting-acl">
+ <primary>pg_setting_acl</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_setting_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_setting_acl</structname>
+ is shared across all databases of a cluster: there is only one
+ copy of <structname>pg_setting_acl</structname> per cluster, not
+ one per database.
+ </para>
+
+ <table>
+ <title><structname>pg_setting_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>setting</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>
@@ -12540,11 +12603,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 settings 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 VALUE</literal> privilege granted
+ on the setting 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..78d97a9689 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 VALUE</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,28 @@ REVOKE ALL ON accounts FROM PUBLIC;
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>SET VALUE</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>SET</command> command to
+ change <literal>superuser</literal> run-time configuration parameters.
+ (Any user can set <literal>user</literal> run-time configuration
+ parameters.)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ALTER SYSTEM</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>ALTER SYSTEM
+ SET</command> command to change server configuration parameters.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
The privileges required by other commands are listed on the
@@ -2097,6 +2120,16 @@ REVOKE ALL ON accounts FROM PUBLIC;
<literal>TYPE</literal>
</entry>
</row>
+ <row>
+ <entry><literal>SET VALUE</literal></entry>
+ <entry><literal>s</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
+ <row>
+ <entry><literal>ALTER SYSTEM</literal></entry>
+ <entry><literal>A</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -2203,6 +2236,12 @@ REVOKE ALL ON accounts FROM PUBLIC;
<entry><literal>U</literal></entry>
<entry><literal>\dT+</literal></entry>
</row>
+ <row>
+ <entry><literal>Configuration parameter</literal></entry>
+ <entry><literal>sA</literal></entry>
+ <entry>none</entry>
+ <entry><literal></literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 8a802fb225..36b33d8639 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');
@@ -22957,6 +22956,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_setting_privilege</primary>
+ </indexterm>
+ <function>has_setting_privilege</function> (
+ <optional> <parameter>user</parameter> <type>name</type> or <type>oid</type>, </optional>
+ <parameter>setting</parameter> <type>text</type> or <type>oid</type>,
+ <parameter>privilege</parameter> <type>text</type> )
+ <returnvalue>boolean</returnvalue>
+ </para>
+ <para>
+ Does user have privilege for setting?
+ Allowable privilege types are <literal>SET VALUE</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 a897712de2..21ec320a8d 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 VALUE | ALTER SYSTEM }
+ ON <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 VALUE</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/set.sgml b/doc/src/sgml/ref/set.sgml
index 339ee9eec9..a08057d1d1 100644
--- a/doc/src/sgml/ref/set.sgml
+++ b/doc/src/sgml/ref/set.sgml
@@ -34,8 +34,9 @@ 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.)
+ (But some require either superuser privileges or granted <literal>SET
+ VALUE</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>
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index eefebb7bb8..fefbfc343d 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -28,6 +28,7 @@ OBJS = \
pg_cast.o \
pg_class.o \
pg_collation.o \
+ pg_setting_acl.o \
pg_constraint.o \
pg_conversion.o \
pg_db_role_setting.o \
@@ -54,7 +55,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_setting_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 \
@@ -126,6 +127,7 @@ install-data: bki-stamp installdirs
$(INSTALL_DATA) $(srcdir)/system_functions.sql '$(DESTDIR)$(datadir)/system_functions.sql'
$(INSTALL_DATA) $(srcdir)/system_views.sql '$(DESTDIR)$(datadir)/system_views.sql'
$(INSTALL_DATA) $(srcdir)/information_schema.sql '$(DESTDIR)$(datadir)/information_schema.sql'
+ $(INSTALL_DATA) $(srcdir)/setting_privileges.sql '$(DESTDIR)$(datadir)/setting_privileges.sql'
$(INSTALL_DATA) $(srcdir)/sql_features.txt '$(DESTDIR)$(datadir)/sql_features.txt'
installdirs:
@@ -133,7 +135,7 @@ installdirs:
.PHONY: uninstall-data
uninstall-data:
- rm -f $(addprefix '$(DESTDIR)$(datadir)'/, postgres.bki system_constraints.sql system_functions.sql system_views.sql information_schema.sql sql_features.txt)
+ rm -f $(addprefix '$(DESTDIR)$(datadir)'/, postgres.bki system_constraints.sql system_functions.sql system_views.sql information_schema.sql setting_privileges.sql sql_features.txt)
# postgres.bki, system_constraints.sql, and the generated headers are
# in the distribution tarball, so they are not cleaned here.
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 1dd03a8e51..3f8699b867 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_setting_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_Setting(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_SETTING:
+ whole_mask = ACL_ALL_RIGHTS_SETTING;
+ 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_SETTING:
+ all_privileges = ACL_ALL_RIGHTS_SETTING;
+ errormsg = gettext_noop("invalid privilege type %s for setting");
+ 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_SETTING:
+ ExecGrant_Setting(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -759,6 +771,38 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_SETTING:
+ foreach(cell, objnames)
+ {
+ char *setting = strVal(lfirst(cell));
+ Oid settingid = get_setting_oid(setting, true);
+
+ if (!OidIsValid(settingid))
+ {
+ /*
+ * Lookup the existing entry, or if necessary, add a new
+ * entry for this parameter. Entries only exist for
+ * parameters which currently have, or previously have had,
+ * privileges assigned.
+ *
+ * It is tempting to sanity-check the given configuration
+ * parameter name against known guc names in the guc
+ * tables, but for handling upgrades we need to accept
+ * setting names that do not yet exist.
+ */
+ settingid = SettingAclCreate(setting, 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, settingid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -1494,6 +1538,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case ForeignDataWrapperRelationId:
istmt.objtype = OBJECT_FDW;
break;
+ case SettingAclRelationId:
+ istmt.objtype = OBJECT_SETTING;
+ break;
default:
elog(ERROR, "unexpected object class %u", classid);
break;
@@ -3225,6 +3272,131 @@ ExecGrant_Type(InternalGrant *istmt)
table_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Setting(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_SETTING;
+
+ relation = table_open(SettingAclRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid settingId = lfirst_oid(cell);
+ Form_pg_setting_acl pg_setting_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_setting_acl];
+ bool nulls[Natts_pg_setting_acl];
+ bool replaces[Natts_pg_setting_acl];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(settingId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for setting %u", settingId);
+
+ pg_setting_acl_tuple = (Form_pg_setting_acl) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ aclDatum = SysCacheGetAttr(SETTINGNAME, tuple, Anum_pg_setting_acl_setacl,
+ &isNull);
+ if (isNull)
+ {
+ old_acl = acldefault(OBJECT_SETTING, InvalidOid);
+ /* 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, GetUserId(),
+ &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,
+ settingId, grantorId, OBJECT_SETTING,
+ text_to_cstring(&pg_setting_acl_tuple->setting),
+ 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_setting_acl_setacl - 1] = true;
+ values[Anum_pg_setting_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(settingId, SettingAclRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(SettingAclRelationId,
+ pg_setting_acl_tuple->oid, 0,
+ InvalidOid,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* Post alter hook called for grant and revoke */
+ InvokeObjectPostAlterHook(SettingAclRelationId, settingId, 0);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ table_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3255,6 +3427,10 @@ string_to_privilege(const char *privname)
return ACL_CREATE_TEMP;
if (strcmp(privname, "connect") == 0)
return ACL_CONNECT;
+ if (strcmp(privname, "set value") == 0)
+ return ACL_SET_VALUE;
+ 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 +3468,10 @@ privilege_to_string(AclMode privilege)
return "TEMP";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET_VALUE:
+ return "SET VALUE";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3328,6 +3508,13 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_COLUMN:
msg = gettext_noop("permission denied for column %s");
break;
+ case OBJECT_SETTING:
+ /*
+ * 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 +3751,7 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_AMPROC:
case OBJECT_ATTRIBUTE:
case OBJECT_CAST:
+ case OBJECT_SETTING:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
case OBJECT_DOMCONSTRAINT:
@@ -4000,6 +4188,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_setting_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 setting's ACL from pg_setting_acl
+ */
+ tuple = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(config_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("setting with OID %u does not exist",
+ config_oid)));
+
+ aclDatum = SysCacheGetAttr(SETTINGOID, tuple, Anum_pg_setting_acl_setacl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_SETTING, 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 +4954,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_setting_acl_aclcheck(Oid config_oid, Oid roleid, AclMode mode)
+{
+ if (pg_setting_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..78868e7052 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_setting_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 == SettingAclRelationId ||
relationId == DatabaseRelationId ||
relationId == SharedDescriptionRelationId ||
relationId == SharedDependRelationId ||
@@ -260,6 +262,8 @@ IsSharedRelation(Oid relationId)
relationId == AuthIdOidIndexId ||
relationId == AuthMemRoleMemIndexId ||
relationId == AuthMemMemRoleIndexId ||
+ relationId == SettingAclSettingIndexId ||
+ relationId == SettingAclOidIndexId ||
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 == PgSettingAclToastTable ||
+ relationId == PgSettingAclToastIndex ||
relationId == PgDatabaseToastTable ||
relationId == PgDatabaseToastIndex ||
relationId == PgDbRoleSettingToastTable ||
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index ab9e42d7d1..5d2504463d 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_setting_acl.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
@@ -150,6 +151,7 @@ static const Oid object_classes[] = {
TypeRelationId, /* OCLASS_TYPE */
CastRelationId, /* OCLASS_CAST */
CollationRelationId, /* OCLASS_COLLATION */
+ SettingAclRelationId, /* OCLASS_SETTING */
ConstraintRelationId, /* OCLASS_CONSTRAINT */
ConversionRelationId, /* OCLASS_CONVERSION */
AttrDefaultRelationId, /* OCLASS_DEFAULT */
@@ -1504,6 +1506,7 @@ doDeletion(const ObjectAddress *object, int flags)
/*
* These global object types are not supported here.
*/
+ case OCLASS_SETTING:
case OCLASS_ROLE:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
@@ -2778,6 +2781,9 @@ getObjectClass(const ObjectAddress *object)
case CollationRelationId:
return OCLASS_COLLATION;
+ case SettingAclRelationId:
+ return OCLASS_SETTING;
+
case ConstraintRelationId:
return OCLASS_CONSTRAINT;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index f30c742d48..1ef833a0e3 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_setting_acl.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
@@ -2309,6 +2310,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_COLUMN:
case OBJECT_ATTRIBUTE:
case OBJECT_COLLATION:
+ case OBJECT_SETTING:
case OBJECT_CONVERSION:
case OBJECT_STATISTIC_EXT:
case OBJECT_TSPARSER:
@@ -3510,6 +3512,22 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
break;
}
+ case OCLASS_SETTING:
+ {
+ char *setting;
+
+ setting = get_setting_name(object->objectId);
+ if (!setting)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for setting %u",
+ object->objectId);
+ break;
+ }
+ appendStringInfo(&buffer, _("setting %s"), setting);
+ break;
+ }
+
case OCLASS_SCHEMA:
{
char *nspname;
@@ -4473,6 +4491,10 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "collation");
break;
+ case OCLASS_SETTING:
+ appendStringInfoString(&buffer, "setting");
+ break;
+
case OCLASS_CONSTRAINT:
getConstraintTypeDescription(&buffer, object->objectId,
missing_ok);
@@ -4977,6 +4999,30 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_SETTING:
+ {
+ HeapTuple configTup;
+ Form_pg_setting_acl configForm;
+ char *namestr;
+
+ configTup = SearchSysCache1(SETTINGOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(configTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for setting %u",
+ object->objectId);
+ break;
+ }
+ configForm = (Form_pg_setting_acl) GETSTRUCT(configTup);
+ namestr = text_to_cstring(&configForm->setting);
+ appendStringInfoString(&buffer, namestr);
+ if (objname)
+ *objname = list_make1(namestr);
+ ReleaseSysCache(configTup);
+ break;
+ }
+
case OCLASS_CONVERSION:
{
HeapTuple conTup;
diff --git a/src/backend/catalog/pg_setting_acl.c b/src/backend/catalog/pg_setting_acl.c
new file mode 100644
index 0000000000..3c6594da03
--- /dev/null
+++ b/src/backend/catalog/pg_setting_acl.c
@@ -0,0 +1,122 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_setting_acl.c
+ * routines to support manipulation of the pg_setting_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_setting_acl.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/sysattr.h"
+#include "access/table.h"
+#include "access/tableam.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_setting_acl.h"
+#include "mb/pg_wchar.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/guc.h"
+#include "utils/pg_locale.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * SettingAclCreate
+ *
+ * Add a new tuple to pg_setting_acl.
+ *
+ * setting: the setting name to create.
+ * if_not_exists: if true, don't fail on duplicate name, but rather return
+ * the existing entry's Oid.
+ */
+Oid
+SettingAclCreate(const char *setting, bool if_not_exists)
+{
+ Relation rel;
+ TupleDesc tupDesc;
+ HeapTuple tuple;
+ Datum values[Natts_pg_setting_acl];
+ bool nulls[Natts_pg_setting_acl];
+ Oid settingId;
+ const char *canonical;
+
+ /*
+ * Check whether the setting (by the given name or alias)
+ * already exists.
+ */
+ settingId = get_setting_oid(setting, true);
+ if (OidIsValid(settingId))
+ {
+ if (if_not_exists)
+ return settingId;
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("setting \"%s\" already exists",
+ setting)));
+ }
+
+ /*
+ * We must not require the setting to be in the list of existent GUCs,
+ * as we may be called at different points during upgrades or the
+ * installation or removal of extensions. Instead, perform a basic sanity
+ * check of the setting, 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_setting_acl entries.
+ */
+ if (!valid_variable_name(setting, NULL))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid setting name \"%s\"",
+ setting)));
+ canonical = GetConfigOptionCanonicalName(setting);
+ if (!canonical)
+ canonical = setting;
+
+ /*
+ * 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(SettingAclRelationId, RowExclusiveLock);
+ tupDesc = RelationGetDescr(rel);
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ values[Anum_pg_setting_acl_setting - 1] =
+ DirectFunctionCall1(textin, CStringGetDatum(canonical));
+ settingId = GetNewOidWithIndex(rel,
+ SettingAclOidIndexId,
+ Anum_pg_setting_acl_oid);
+ values[Anum_pg_setting_acl_oid - 1] = ObjectIdGetDatum(settingId);
+ nulls[Anum_pg_setting_acl_setacl - 1] = true;
+ tuple = heap_form_tuple(tupDesc, values, nulls);
+ CatalogTupleInsert(rel, tuple);
+
+ /* Post creation hook for new setting */
+ InvokeObjectPostCreateHook(SettingAclRelationId, settingId, 0);
+
+ /*
+ * Close pg_setting_acl, but keep lock till commit.
+ */
+ heap_freetuple(tuple);
+ table_close(rel, NoLock);
+
+ return settingId;
+}
diff --git a/src/backend/catalog/setting_privileges.sql b/src/backend/catalog/setting_privileges.sql
new file mode 100644
index 0000000000..b788ebee7f
--- /dev/null
+++ b/src/backend/catalog/setting_privileges.sql
@@ -0,0 +1,62 @@
+/*
+ * PostgreSQL User SET variables
+ *
+ * Copyright (c) 2021, PostgreSQL Global Development Group
+ *
+ * src/backend/catalog/setting_privileges.sql
+ *
+ * Note: this file is read in single-user -j mode, which means that the
+ * command terminator is semicolon-newline-newline; whenever the backend
+ * sees that, it stops and executes what it's got. If you write a lot of
+ * statements without empty lines between, they'll all get quoted to you
+ * in any error message about one of them, so don't do that. Also, you
+ * cannot write a semicolon immediately followed by an empty line in a
+ * string literal (including a function body!) or a multiline comment.
+ */
+
+GRANT SET VALUE ON
+ enable_seqscan, enable_indexscan, enable_indexonlyscan, enable_bitmapscan,
+ enable_tidscan, enable_sort, enable_incremental_sort, enable_hashagg,
+ enable_material, enable_memoize, enable_nestloop, enable_mergejoin,
+ enable_hashjoin, enable_gathermerge, enable_partitionwise_join,
+ enable_partitionwise_aggregate, enable_parallel_append,
+ enable_parallel_hash, enable_partition_pruning, enable_async_append, geqo,
+ exit_on_error, debug_print_parse, debug_print_rewritten, debug_print_plan,
+ debug_pretty_print, trace_notify, transform_null_equals,
+ default_transaction_read_only, transaction_read_only,
+ default_transaction_deferrable, transaction_deferrable, row_security,
+ check_function_bodies, array_nulls, default_with_oids, trace_sort,
+ trace_syncscan, optimize_bounded_sort, escape_string_warning,
+ standard_conforming_strings, synchronize_seqscans, quote_all_identifiers,
+ parallel_leader_participation, jit, jit_expressions, jit_tuple_deforming,
+ default_statistics_target, from_collapse_limit, join_collapse_limit,
+ geqo_threshold, geqo_effort, geqo_pool_size, geqo_generations,
+ temp_buffers, work_mem, maintenance_work_mem, logical_decoding_work_mem,
+ vacuum_cost_page_hit, vacuum_cost_page_miss, vacuum_cost_page_dirty,
+ vacuum_cost_limit, statement_timeout, lock_timeout,
+ idle_in_transaction_session_timeout, idle_session_timeout,
+ vacuum_freeze_min_age, vacuum_freeze_table_age,
+ vacuum_multixact_freeze_min_age, vacuum_multixact_freeze_table_age,
+ vacuum_failsafe_age, vacuum_multixact_failsafe_age, wal_skip_threshold,
+ wal_sender_timeout, commit_siblings, extra_float_digits,
+ log_parameter_max_length_on_error, effective_io_concurrency,
+ maintenance_io_concurrency, backend_flush_after,
+ max_parallel_maintenance_workers, max_parallel_workers_per_gather,
+ max_parallel_workers, tcp_keepalives_idle, tcp_keepalives_interval,
+ ssl_renegotiation_limit, tcp_keepalives_count, gin_fuzzy_search_limit,
+ effective_cache_size, min_parallel_table_scan_size,
+ min_parallel_index_scan_size, gin_pending_list_limit, tcp_user_timeout,
+ client_connection_check_interval, seq_page_cost, random_page_cost,
+ cpu_tuple_cost, cpu_index_tuple_cost, cpu_operator_cost,
+ parallel_tuple_cost, parallel_setup_cost, jit_above_cost,
+ jit_optimize_above_cost, jit_inline_above_cost, cursor_tuple_fraction,
+ geqo_selection_bias, geqo_seed, hash_mem_multiplier, seed,
+ vacuum_cost_delay, DateStyle, default_table_access_method,
+ default_tablespace, temp_tablespaces, lc_monetary, lc_numeric, lc_time,
+ local_preload_libraries, search_path, role, TimeZone,
+ timezone_abbreviations, default_text_search_config, application_name,
+ backslash_quote, bytea_output, client_min_messages, constraint_exclusion,
+ default_toast_compression, default_transaction_isolation,
+ transaction_isolation, IntervalStyle, synchronous_commit, xmlbinary,
+ xmloption, force_parallel_mode, password_encryption, plan_cache_mode
+ TO public;
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 40b7bca5a9..92b226b8d2 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -595,6 +595,19 @@ CREATE RULE pg_settings_n AS
GRANT SELECT, UPDATE ON pg_settings TO PUBLIC;
+CREATE VIEW pg_setting_privileges AS
+ SELECT grantor.rolname AS grantor,
+ grantee.rolname AS grantee,
+ set_acl.setting AS setting,
+ acl.privilege_type AS privilege_type,
+ acl.is_grantable
+ FROM pg_catalog.pg_setting_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_setting_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..3dd0c5cadb 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -639,6 +639,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
break;
case OCLASS_CAST:
+ case OCLASS_SETTING:
case OCLASS_CONSTRAINT:
case OCLASS_DEFAULT:
case OCLASS_LANGUAGE:
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index c9b5732448..00398727ed 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -276,6 +276,13 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
name = NameListToString(castNode(List, object));
}
break;
+ case OBJECT_SETTING:
+ if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
+ {
+ msg = gettext_noop("setting \"%s\" does not exist, skipping");
+ name = NameListToString(castNode(List, object));
+ }
+ break;
case OBJECT_CONVERSION:
if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
{
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 1e8587502e..038486a622 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_SETTING:
case OBJECT_DATABASE:
case OBJECT_TABLESPACE:
case OBJECT_ROLE:
@@ -1012,6 +1013,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
{
switch (objclass)
{
+ case OCLASS_SETTING:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
@@ -2072,6 +2074,8 @@ stringify_grant_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMN";
+ case OBJECT_SETTING:
+ return "SETTING";
case OBJECT_TABLE:
return "TABLE";
case OBJECT_SEQUENCE:
@@ -2155,6 +2159,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMNS";
+ case OBJECT_SETTING:
+ return "SETTINGS";
case OBJECT_TABLE:
return "TABLES";
case OBJECT_SEQUENCE:
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 7a62d547e2..7b05eb0110 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_SETTING:
case OBJECT_CONVERSION:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index dc5872f988..11671390a5 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -12620,6 +12620,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_TYPE:
case OCLASS_CAST:
case OCLASS_COLLATION:
+ case OCLASS_SETTING:
case OCLASS_CONVERSION:
case OCLASS_LANGUAGE:
case OCLASS_LARGEOBJECT:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a03b33b53b..45a9f078f4 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -363,8 +363,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 setting_name
+%type <list> OptSchemaEltList setting_target
%type <chr> am_type
@@ -402,8 +402,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <str> iso_level opt_encoding
%type <rolespec> grantee
%type <list> grantee_list
-%type <accesspriv> privilege
-%type <list> privileges privilege_list
+%type <accesspriv> privilege setting_priv
+%type <list> privileges privilege_list setting_priv_list
%type <privtarget> privilege_target
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
@@ -716,7 +716,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
@@ -6981,6 +6981,20 @@ GrantStmt: GRANT privileges ON privilege_target TO grantee_list
n->grantor = $8;
$$ = (Node*)n;
}
+ | GRANT setting_priv_list ON setting_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_SETTING;
+ n->objects = $4;
+ n->grantees = $6;
+ n->grant_option = $7;
+ n->grantor = $8;
+ $$ = (Node*)n;
+ }
;
RevokeStmt:
@@ -7014,6 +7028,36 @@ RevokeStmt:
n->behavior = $11;
$$ = (Node *)n;
}
+ | REVOKE setting_priv_list ON setting_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_SETTING;
+ n->objects = $4;
+ n->grantees = $6;
+ n->grantor = $7;
+ n->behavior = $8;
+ $$ = (Node *)n;
+ }
+ | REVOKE GRANT OPTION FOR setting_priv_list ON setting_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_SETTING;
+ n->objects = $7;
+ n->grantees = $9;
+ n->grantor = $10;
+ n->behavior = $11;
+ $$ = (Node *)n;
+ }
;
@@ -7082,6 +7126,49 @@ privilege: SELECT opt_column_list
}
;
+setting_priv_list: setting_priv { $$ = list_make1($1); }
+ | setting_priv_list ',' setting_priv { $$ = lappend($1, $3); }
+ ;
+
+setting_priv:
+ ALTER SYSTEM_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("alter system");
+ n->cols = NIL;
+ $$ = n;
+ }
+ | SET VALUE_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("set value");
+ n->cols = NIL;
+ $$ = n;
+ }
+ ;
+
+
+setting_target:
+ setting_name
+ {
+ $$ = list_make1(makeString($1));
+ }
+ | setting_target ',' setting_name
+ {
+ $$ = lappend($1, makeString($3));
+ }
+ ;
+
+setting_name:
+ ColId
+ {
+ $$ = $1;
+ }
+ | setting_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.
@@ -15851,6 +15938,7 @@ unreserved_keyword:
| OWNED
| OWNER
| PARALLEL
+ | PARAMETER
| PARSER
| PARTIAL
| PARTITION
@@ -16429,6 +16517,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 0a16f8156c..b0e43118bf 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_setting_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,8 @@ 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 Oid convert_setting_name(text *setting);
+static AclMode convert_setting_priv_string(text *priv_setting_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 +310,12 @@ aclparse(const char *s, AclItem *aip)
case ACL_CONNECT_CHR:
read = ACL_CONNECT;
break;
+ case ACL_SET_VALUE_CHR:
+ read = ACL_SET_VALUE;
+ break;
+ case ACL_ALTER_SYSTEM_CHR:
+ read = ACL_ALTER_SYSTEM;
+ break;
case 'R': /* ignore old RULE privileges */
read = 0;
break;
@@ -794,6 +804,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case OBJECT_SETTING:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_NO_RIGHTS;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -1602,6 +1616,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 VALUE") == 0)
+ return ACL_SET_VALUE;
+ 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 +1716,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET_VALUE:
+ return "SET VALUE";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
@@ -4429,6 +4451,205 @@ convert_type_priv_string(text *priv_type_text)
return convert_any_priv_string(priv_type_text, type_priv_map);
}
+/*
+ * has_setting_privilege variants
+ * These are all named "has_setting_privilege" at the SQL level.
+ * They take various combinations of setting name, setting 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_setting_privilege_name_name
+ * Check user privileges on a setting given
+ * name username, text setting, and text priv name.
+ */
+Datum
+has_setting_privilege_name_name(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ text *setting = PG_GETARG_TEXT_PP(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid roleid;
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_role_oid_or_public(NameStr(*username));
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_name
+ * Check user privileges on a setting given
+ * text setting and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_setting_privilege_name(PG_FUNCTION_ARGS)
+{
+ text *setting = PG_GETARG_TEXT_PP(0);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(1);
+ Oid roleid;
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_name_id
+ * Check user privileges on a setting given
+ * name usename, setting oid, and text priv name.
+ */
+Datum
+has_setting_privilege_name_id(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ Oid settingoid = PG_GETARG_OID(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_role_oid_or_public(NameStr(*username));
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id
+ * Check user privileges on a setting given
+ * setting oid, and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_setting_privilege_id(PG_FUNCTION_ARGS)
+{
+ Oid settingoid = PG_GETARG_OID(0);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(1);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id_name
+ * Check user privileges on a setting given
+ * roleid, text setting, and text priv name.
+ */
+Datum
+has_setting_privilege_id_name(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ text *setting = PG_GETARG_TEXT_PP(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ settingoid = convert_setting_name(setting);
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id_id
+ * Check user privileges on a setting given
+ * roleid, setting oid, and text priv name.
+ */
+Datum
+has_setting_privilege_id_id(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ Oid settingoid = PG_GETARG_OID(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ AclMode mode;
+ AclResult aclresult;
+
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * Support routines for has_setting_privilege family.
+ */
+
+/*
+ * Given a setting name expressed as a string, look it up and return Oid
+ */
+static Oid
+convert_setting_name(text *settingname)
+{
+ Oid oid;
+ char *setting = text_to_cstring(settingname);
+
+ oid = get_setting_oid(setting, true);
+
+ if (!OidIsValid(oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("setting \"%s\" does not exist", setting)));
+
+ return oid;
+}
+
+/*
+ * convert_setting_priv_string
+ * Convert text string to AclMode value.
+ */
+static AclMode
+convert_setting_priv_string(text *priv_setting_text)
+{
+ static const priv_map setting_priv_map[] = {
+ {"SET VALUE", ACL_SET_VALUE},
+ {"SET VALUE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SET_VALUE)},
+ {"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_setting_text, setting_priv_map);
+}
/*
* pg_has_role variants
@@ -4670,6 +4891,43 @@ initialize_acl(void)
}
}
+/*
+ * get_setting_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_setting_oid(const char *setting, bool missing_ok)
+{
+ Oid oid;
+
+ /* Check for the variable by the name we were given */
+ oid = GetSysCacheOid1(SETTINGNAME, Anum_pg_setting_acl_oid,
+ PointerGetDatum(cstring_to_text(setting)));
+ if (!OidIsValid(oid))
+ {
+ const char *canonical;
+
+ /* Check if the variable has a different canonical spelling */
+ canonical = GetConfigOptionCanonicalName(setting);
+ if (canonical != NULL)
+ oid = GetSysCacheOid1(SETTINGNAME, Anum_pg_setting_acl_oid,
+ PointerGetDatum(cstring_to_text(canonical)));
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("setting \"%s\" does not exist", setting)));
+ }
+
+ return oid;
+}
+
/*
* RoleMembershipCacheCallback
* Syscache inval callback function
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index feef999863..f4e806fe8e 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_setting_acl.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_transform.h"
#include "catalog/pg_type.h"
@@ -3304,6 +3305,25 @@ free_attstatsslot(AttStatsSlot *sslot)
pfree(sslot->numbers_arr);
}
+char *
+get_setting_name(Oid configid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(configid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_setting_acl configtup = (Form_pg_setting_acl) GETSTRUCT(tp);
+ char *result;
+
+ result = pstrdup(text_to_cstring(&configtup->setting));
+ 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 f4e7819f1e..cbcbf02839 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_setting_acl.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
#include "catalog/pg_shseclabel.h"
@@ -762,6 +763,28 @@ static const struct cachedesc cacheinfo[] = {
},
32
},
+ {SettingAclRelationId, /* SETTINGNAME */
+ SettingAclSettingIndexId,
+ 1,
+ {
+ Anum_pg_setting_acl_setting,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+ {SettingAclRelationId, /* SETTINGOID */
+ SettingAclOidIndexId,
+ 1,
+ {
+ Anum_pg_setting_acl_oid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{StatisticExtDataRelationId, /* STATEXTDATASTXOID */
StatisticExtDataStxoidInhIndexId,
2,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 1e3650184b..2fa38b14af 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -43,7 +43,9 @@
#include "access/xlog_internal.h"
#include "access/xlogrecovery.h"
#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_authid.h"
+#include "catalog/pg_setting_acl.h"
#include "catalog/storage.h"
#include "commands/async.h"
#include "commands/prepare.h"
@@ -977,7 +979,7 @@ static const unit_conversion time_unit_conversion_table[] =
static struct config_bool ConfigureNamesBool[] =
{
{
- {"enable_seqscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_seqscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of sequential-scan plans."),
NULL,
GUC_EXPLAIN
@@ -987,7 +989,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_indexscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_indexscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of index-scan plans."),
NULL,
GUC_EXPLAIN
@@ -997,7 +999,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_indexonlyscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_indexonlyscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of index-only-scan plans."),
NULL,
GUC_EXPLAIN
@@ -1007,7 +1009,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_bitmapscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_bitmapscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of bitmap-scan plans."),
NULL,
GUC_EXPLAIN
@@ -1017,7 +1019,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_tidscan", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_tidscan", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of TID scan plans."),
NULL,
GUC_EXPLAIN
@@ -1027,7 +1029,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_sort", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_sort", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of explicit sort steps."),
NULL,
GUC_EXPLAIN
@@ -1037,7 +1039,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_incremental_sort", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_incremental_sort", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of incremental sort steps."),
NULL,
GUC_EXPLAIN
@@ -1047,7 +1049,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_hashagg", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_hashagg", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of hashed aggregation plans."),
NULL,
GUC_EXPLAIN
@@ -1057,7 +1059,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_material", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_material", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of materialization."),
NULL,
GUC_EXPLAIN
@@ -1067,7 +1069,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_memoize", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_memoize", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of memoization."),
NULL,
GUC_EXPLAIN
@@ -1077,7 +1079,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_nestloop", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_nestloop", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of nested-loop join plans."),
NULL,
GUC_EXPLAIN
@@ -1087,7 +1089,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_mergejoin", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_mergejoin", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of merge join plans."),
NULL,
GUC_EXPLAIN
@@ -1097,7 +1099,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_hashjoin", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_hashjoin", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of hash join plans."),
NULL,
GUC_EXPLAIN
@@ -1107,7 +1109,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_gathermerge", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_gathermerge", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of gather merge plans."),
NULL,
GUC_EXPLAIN
@@ -1117,7 +1119,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_partitionwise_join", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_partitionwise_join", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables partitionwise join."),
NULL,
GUC_EXPLAIN
@@ -1127,7 +1129,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_partitionwise_aggregate", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_partitionwise_aggregate", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables partitionwise aggregation and grouping."),
NULL,
GUC_EXPLAIN
@@ -1137,7 +1139,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_parallel_append", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_parallel_append", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of parallel append plans."),
NULL,
GUC_EXPLAIN
@@ -1147,7 +1149,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_parallel_hash", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_parallel_hash", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of parallel hash plans."),
NULL,
GUC_EXPLAIN
@@ -1157,7 +1159,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_partition_pruning", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_partition_pruning", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables plan-time and execution-time partition pruning."),
gettext_noop("Allows the query planner and executor to compare partition "
"bounds to conditions in the query to determine which "
@@ -1169,7 +1171,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"enable_async_append", PGC_USERSET, QUERY_TUNING_METHOD,
+ {"enable_async_append", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enables the planner's use of async append plans."),
NULL,
GUC_EXPLAIN
@@ -1179,7 +1181,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("Enables genetic query optimization."),
gettext_noop("This algorithm attempts to do planning without "
"exhaustive searching."),
@@ -1401,7 +1403,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"exit_on_error", PGC_USERSET, ERROR_HANDLING_OPTIONS,
+ {"exit_on_error", PGC_SUSET, ERROR_HANDLING_OPTIONS,
gettext_noop("Terminate session on any error."),
NULL
},
@@ -1439,7 +1441,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_print_parse", PGC_USERSET, LOGGING_WHAT,
+ {"debug_print_parse", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs each query's parse tree."),
NULL
},
@@ -1448,7 +1450,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_print_rewritten", PGC_USERSET, LOGGING_WHAT,
+ {"debug_print_rewritten", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs each query's rewritten parse tree."),
NULL
},
@@ -1457,7 +1459,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_print_plan", PGC_USERSET, LOGGING_WHAT,
+ {"debug_print_plan", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Logs each query's execution plan."),
NULL
},
@@ -1466,7 +1468,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"debug_pretty_print", PGC_USERSET, LOGGING_WHAT,
+ {"debug_pretty_print", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Indents parse and plan tree displays."),
NULL
},
@@ -1587,7 +1589,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"trace_notify", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"trace_notify", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Generates debugging output for LISTEN and NOTIFY."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -1671,7 +1673,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"transform_null_equals", PGC_USERSET, COMPAT_OPTIONS_CLIENT,
+ {"transform_null_equals", PGC_SUSET, COMPAT_OPTIONS_CLIENT,
gettext_noop("Treats \"expr=NULL\" as \"expr IS NULL\"."),
gettext_noop("When turned on, expressions of the form expr = NULL "
"(or NULL = expr) are treated as expr IS NULL, that is, they "
@@ -1693,7 +1695,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"default_transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_transaction_read_only", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default read-only status of new transactions."),
NULL,
GUC_REPORT
@@ -1703,7 +1705,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"transaction_read_only", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the current transaction's read-only status."),
NULL,
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -1713,7 +1715,7 @@ static struct config_bool ConfigureNamesBool[] =
check_transaction_read_only, NULL, NULL
},
{
- {"default_transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_transaction_deferrable", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default deferrable status of new transactions."),
NULL
},
@@ -1722,7 +1724,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"transaction_deferrable", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Whether to defer a read-only serializable transaction until it can be executed with no possible serialization failures."),
NULL,
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -1732,7 +1734,7 @@ static struct config_bool ConfigureNamesBool[] =
check_transaction_deferrable, NULL, NULL
},
{
- {"row_security", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"row_security", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Enable row security."),
gettext_noop("When enabled, row security will be applied to all users.")
},
@@ -1741,7 +1743,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"check_function_bodies", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"check_function_bodies", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Check routine bodies during CREATE FUNCTION and CREATE PROCEDURE."),
NULL
},
@@ -1750,7 +1752,7 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
- {"array_nulls", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"array_nulls", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Enable input of NULL elements in arrays."),
gettext_noop("When turned on, unquoted NULL in an array input "
"value means a null value; "
@@ -1767,7 +1769,7 @@ static struct config_bool ConfigureNamesBool[] =
* avoid unnecessarily breaking older dump files.
*/
{
- {"default_with_oids", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"default_with_oids", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("WITH OIDS is no longer supported; this can only be false."),
NULL,
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE
@@ -1797,7 +1799,7 @@ static struct config_bool ConfigureNamesBool[] =
#ifdef TRACE_SORT
{
- {"trace_sort", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"trace_sort", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Emit information about resource usage in sorting."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -1811,7 +1813,7 @@ static struct config_bool ConfigureNamesBool[] =
#ifdef TRACE_SYNCSCAN
/* this is undocumented because not exposed in a standard build */
{
- {"trace_syncscan", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"trace_syncscan", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Generate debugging output for synchronized scanning."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -1826,7 +1828,7 @@ static struct config_bool ConfigureNamesBool[] =
/* this is undocumented because not exposed in a standard build */
{
{
- "optimize_bounded_sort", PGC_USERSET, QUERY_TUNING_METHOD,
+ "optimize_bounded_sort", PGC_SUSET, QUERY_TUNING_METHOD,
gettext_noop("Enable bounded sorting using heap sort."),
NULL,
GUC_NOT_IN_SAMPLE | GUC_EXPLAIN
@@ -1872,7 +1874,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"escape_string_warning", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"escape_string_warning", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Warn about backslash escapes in ordinary string literals."),
NULL
},
@@ -1882,7 +1884,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"standard_conforming_strings", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"standard_conforming_strings", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Causes '...' strings to treat backslashes literally."),
NULL,
GUC_REPORT
@@ -1893,7 +1895,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"synchronize_seqscans", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"synchronize_seqscans", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Enable synchronized sequential scans."),
NULL
},
@@ -1989,7 +1991,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"quote_all_identifiers", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"quote_all_identifiers", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("When generating SQL fragments, quote all identifiers."),
NULL,
},
@@ -2030,7 +2032,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"parallel_leader_participation", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"parallel_leader_participation", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Controls whether Gather and Gather Merge also run subplans."),
gettext_noop("Should gather nodes also run subplans or just gather tuples?"),
GUC_EXPLAIN
@@ -2041,7 +2043,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"jit", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"jit", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Allow JIT compilation."),
NULL,
GUC_EXPLAIN
@@ -2080,7 +2082,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"jit_expressions", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"jit_expressions", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Allow JIT compilation of expressions."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -2108,7 +2110,7 @@ static struct config_bool ConfigureNamesBool[] =
},
{
- {"jit_tuple_deforming", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"jit_tuple_deforming", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Allow JIT compilation of tuple deforming."),
NULL,
GUC_NOT_IN_SAMPLE
@@ -2168,7 +2170,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"default_statistics_target", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"default_statistics_target", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the default statistics target."),
gettext_noop("This applies to table columns that have not had a "
"column-specific target set via ALTER TABLE SET STATISTICS.")
@@ -2178,7 +2180,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"from_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"from_collapse_limit", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the FROM-list size beyond which subqueries "
"are not collapsed."),
gettext_noop("The planner will merge subqueries into upper "
@@ -2191,7 +2193,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"join_collapse_limit", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"join_collapse_limit", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the FROM-list size beyond which JOIN "
"constructs are not flattened."),
gettext_noop("The planner will flatten explicit JOIN "
@@ -2204,7 +2206,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_threshold", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_threshold", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("Sets the threshold of FROM items beyond which GEQO is used."),
NULL,
GUC_EXPLAIN
@@ -2214,7 +2216,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_effort", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_effort", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: effort is used to set the default for other GEQO parameters."),
NULL,
GUC_EXPLAIN
@@ -2224,7 +2226,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_pool_size", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_pool_size", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: number of individuals in the population."),
gettext_noop("Zero selects a suitable default value."),
GUC_EXPLAIN
@@ -2234,7 +2236,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"geqo_generations", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_generations", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: number of iterations of the algorithm."),
gettext_noop("Zero selects a suitable default value."),
GUC_EXPLAIN
@@ -2381,7 +2383,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"temp_buffers", PGC_USERSET, RESOURCES_MEM,
+ {"temp_buffers", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum number of temporary buffers used by each session."),
NULL,
GUC_UNIT_BLOCKS | GUC_EXPLAIN
@@ -2446,7 +2448,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"work_mem", PGC_USERSET, RESOURCES_MEM,
+ {"work_mem", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum memory to be used for query workspaces."),
gettext_noop("This much memory can be used by each internal "
"sort operation and hash table before switching to "
@@ -2459,7 +2461,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"maintenance_work_mem", PGC_USERSET, RESOURCES_MEM,
+ {"maintenance_work_mem", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum memory to be used for maintenance operations."),
gettext_noop("This includes operations such as VACUUM and CREATE INDEX."),
GUC_UNIT_KB
@@ -2470,7 +2472,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"logical_decoding_work_mem", PGC_USERSET, RESOURCES_MEM,
+ {"logical_decoding_work_mem", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Sets the maximum memory to be used for logical decoding."),
gettext_noop("This much memory can be used by each internal "
"reorder buffer before spilling to disk."),
@@ -2509,7 +2511,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_page_hit", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_page_hit", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost for a page found in the buffer cache."),
NULL
},
@@ -2519,7 +2521,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_page_miss", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_page_miss", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost for a page not found in the buffer cache."),
NULL
},
@@ -2529,7 +2531,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_page_dirty", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_page_dirty", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost for a page dirtied by vacuum."),
NULL
},
@@ -2539,7 +2541,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_cost_limit", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_limit", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost amount available before napping."),
NULL
},
@@ -2605,7 +2607,7 @@ static struct config_int ConfigureNamesInt[] =
#endif
{
- {"statement_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"statement_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed duration of any statement."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2616,7 +2618,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"lock_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"lock_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed duration of any wait for a lock."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2627,7 +2629,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"idle_in_transaction_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"idle_in_transaction_session_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed idle time between queries, when in a transaction."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2638,7 +2640,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"idle_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"idle_session_timeout", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum allowed idle time between queries, when not in a transaction."),
gettext_noop("A value of 0 turns off the timeout."),
GUC_UNIT_MS
@@ -2649,7 +2651,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_freeze_min_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Minimum age at which VACUUM should freeze a table row."),
NULL
},
@@ -2659,7 +2661,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_freeze_table_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Age at which VACUUM should scan whole table to freeze tuples."),
NULL
},
@@ -2669,7 +2671,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_multixact_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_multixact_freeze_min_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Minimum age at which VACUUM should freeze a MultiXactId in a table row."),
NULL
},
@@ -2679,7 +2681,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"vacuum_multixact_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_multixact_freeze_table_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Multixact age at which VACUUM should scan whole table to freeze tuples."),
NULL
},
@@ -2698,7 +2700,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"vacuum_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_failsafe_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
NULL
},
@@ -2707,7 +2709,7 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
{
- {"vacuum_multixact_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"vacuum_multixact_failsafe_age", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Multixact age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
NULL
},
@@ -2895,7 +2897,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"wal_skip_threshold", PGC_USERSET, WAL_SETTINGS,
+ {"wal_skip_threshold", PGC_SUSET, WAL_SETTINGS,
gettext_noop("Minimum size of new file to fsync instead of writing WAL."),
NULL,
GUC_UNIT_KB
@@ -2940,7 +2942,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"wal_sender_timeout", PGC_USERSET, REPLICATION_SENDING,
+ {"wal_sender_timeout", PGC_SUSET, REPLICATION_SENDING,
gettext_noop("Sets the maximum time to wait for WAL replication."),
NULL,
GUC_UNIT_MS
@@ -2963,7 +2965,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"commit_siblings", PGC_USERSET, WAL_SETTINGS,
+ {"commit_siblings", PGC_SUSET, WAL_SETTINGS,
gettext_noop("Sets the minimum number of concurrent open transactions "
"required before performing commit_delay."),
NULL
@@ -2974,7 +2976,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"extra_float_digits", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"extra_float_digits", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the number of digits displayed for floating-point values."),
gettext_noop("This affects real, double precision, and geometric data types. "
"A zero or negative parameter value is added to the standard "
@@ -3036,7 +3038,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"log_parameter_max_length_on_error", PGC_USERSET, LOGGING_WHAT,
+ {"log_parameter_max_length_on_error", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Sets the maximum length in bytes of data logged for bind "
"parameter values when logging statements, on error."),
gettext_noop("-1 to print values in full."),
@@ -3081,7 +3083,7 @@ static struct config_int ConfigureNamesInt[] =
{
{"effective_io_concurrency",
- PGC_USERSET,
+ PGC_SUSET,
RESOURCES_ASYNCHRONOUS,
gettext_noop("Number of simultaneous requests that can be handled efficiently by the disk subsystem."),
NULL,
@@ -3099,7 +3101,7 @@ static struct config_int ConfigureNamesInt[] =
{
{"maintenance_io_concurrency",
- PGC_USERSET,
+ PGC_SUSET,
RESOURCES_ASYNCHRONOUS,
gettext_noop("A variant of effective_io_concurrency that is used for maintenance work."),
NULL,
@@ -3116,7 +3118,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"backend_flush_after", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"backend_flush_after", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Number of pages after which previously performed writes are flushed to disk."),
NULL,
GUC_UNIT_BLOCKS
@@ -3348,7 +3350,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"max_parallel_maintenance_workers", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"max_parallel_maintenance_workers", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Sets the maximum number of parallel processes per maintenance operation."),
NULL
},
@@ -3358,7 +3360,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"max_parallel_workers_per_gather", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"max_parallel_workers_per_gather", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Sets the maximum number of parallel processes per executor node."),
NULL,
GUC_EXPLAIN
@@ -3369,7 +3371,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"max_parallel_workers", PGC_USERSET, RESOURCES_ASYNCHRONOUS,
+ {"max_parallel_workers", PGC_SUSET, RESOURCES_ASYNCHRONOUS,
gettext_noop("Sets the maximum number of parallel workers that can be active at one time."),
NULL,
GUC_EXPLAIN
@@ -3402,7 +3404,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_keepalives_idle", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_keepalives_idle", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Time between issuing TCP keepalives."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_S
@@ -3413,7 +3415,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_keepalives_interval", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_keepalives_interval", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Time between TCP keepalive retransmits."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_S
@@ -3424,7 +3426,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"ssl_renegotiation_limit", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"ssl_renegotiation_limit", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("SSL renegotiation is no longer supported; this can only be 0."),
NULL,
GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE,
@@ -3435,7 +3437,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_keepalives_count", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_keepalives_count", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Maximum number of TCP keepalive retransmits."),
gettext_noop("This controls the number of consecutive keepalive retransmits that can be "
"lost before a connection is considered dead. A value of 0 uses the "
@@ -3447,7 +3449,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"gin_fuzzy_search_limit", PGC_USERSET, CLIENT_CONN_OTHER,
+ {"gin_fuzzy_search_limit", PGC_SUSET, CLIENT_CONN_OTHER,
gettext_noop("Sets the maximum allowed result for exact search by GIN."),
NULL,
0
@@ -3458,7 +3460,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"effective_cache_size", PGC_USERSET, QUERY_TUNING_COST,
+ {"effective_cache_size", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's assumption about the total size of the data caches."),
gettext_noop("That is, the total size of the caches (kernel cache and shared buffers) used for PostgreSQL data files. "
"This is measured in disk pages, which are normally 8 kB each."),
@@ -3470,7 +3472,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"min_parallel_table_scan_size", PGC_USERSET, QUERY_TUNING_COST,
+ {"min_parallel_table_scan_size", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the minimum amount of table data for a parallel scan."),
gettext_noop("If the planner estimates that it will read a number of table pages too small to reach this limit, a parallel scan will not be considered."),
GUC_UNIT_BLOCKS | GUC_EXPLAIN,
@@ -3481,7 +3483,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"min_parallel_index_scan_size", PGC_USERSET, QUERY_TUNING_COST,
+ {"min_parallel_index_scan_size", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the minimum amount of index data for a parallel scan."),
gettext_noop("If the planner estimates that it will read a number of index pages too small to reach this limit, a parallel scan will not be considered."),
GUC_UNIT_BLOCKS | GUC_EXPLAIN,
@@ -3526,7 +3528,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"gin_pending_list_limit", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"gin_pending_list_limit", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the maximum size of the pending list for GIN index."),
NULL,
GUC_UNIT_KB
@@ -3537,7 +3539,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"tcp_user_timeout", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"tcp_user_timeout", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("TCP user timeout."),
gettext_noop("A value of 0 uses the system default."),
GUC_UNIT_MS
@@ -3582,7 +3584,7 @@ static struct config_int ConfigureNamesInt[] =
},
{
- {"client_connection_check_interval", PGC_USERSET, CONN_AUTH_SETTINGS,
+ {"client_connection_check_interval", PGC_SUSET, CONN_AUTH_SETTINGS,
gettext_noop("Sets the time interval between checks for disconnection while running queries."),
NULL,
GUC_UNIT_MS
@@ -3614,7 +3616,7 @@ static struct config_int ConfigureNamesInt[] =
static struct config_real ConfigureNamesReal[] =
{
{
- {"seq_page_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"seq_page_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of a "
"sequentially fetched disk page."),
NULL,
@@ -3625,7 +3627,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"random_page_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"random_page_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of a "
"nonsequentially fetched disk page."),
NULL,
@@ -3636,7 +3638,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"cpu_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"cpu_tuple_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"processing each tuple (row)."),
NULL,
@@ -3647,7 +3649,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"cpu_index_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"cpu_index_tuple_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"processing each index entry during an index scan."),
NULL,
@@ -3658,7 +3660,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"cpu_operator_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"cpu_operator_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"processing each operator or function call."),
NULL,
@@ -3669,7 +3671,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"parallel_tuple_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"parallel_tuple_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"passing each tuple (row) from worker to leader backend."),
NULL,
@@ -3680,7 +3682,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"parallel_setup_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"parallel_setup_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Sets the planner's estimate of the cost of "
"starting up worker processes for parallel query."),
NULL,
@@ -3692,7 +3694,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"jit_above_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"jit_above_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Perform JIT compilation if query is more expensive."),
gettext_noop("-1 disables JIT compilation."),
GUC_EXPLAIN
@@ -3703,7 +3705,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"jit_optimize_above_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"jit_optimize_above_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Optimize JIT-compiled functions if query is more expensive."),
gettext_noop("-1 disables optimization."),
GUC_EXPLAIN
@@ -3714,7 +3716,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"jit_inline_above_cost", PGC_USERSET, QUERY_TUNING_COST,
+ {"jit_inline_above_cost", PGC_SUSET, QUERY_TUNING_COST,
gettext_noop("Perform JIT inlining if query is more expensive."),
gettext_noop("-1 disables inlining."),
GUC_EXPLAIN
@@ -3725,7 +3727,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"cursor_tuple_fraction", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"cursor_tuple_fraction", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Sets the planner's estimate of the fraction of "
"a cursor's rows that will be retrieved."),
NULL,
@@ -3737,7 +3739,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"geqo_selection_bias", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_selection_bias", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: selective pressure within the population."),
NULL,
GUC_EXPLAIN
@@ -3748,7 +3750,7 @@ static struct config_real ConfigureNamesReal[] =
NULL, NULL, NULL
},
{
- {"geqo_seed", PGC_USERSET, QUERY_TUNING_GEQO,
+ {"geqo_seed", PGC_SUSET, QUERY_TUNING_GEQO,
gettext_noop("GEQO: seed for random path selection."),
NULL,
GUC_EXPLAIN
@@ -3759,7 +3761,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"hash_mem_multiplier", PGC_USERSET, RESOURCES_MEM,
+ {"hash_mem_multiplier", PGC_SUSET, RESOURCES_MEM,
gettext_noop("Multiple of work_mem to use for hash tables."),
NULL,
GUC_EXPLAIN
@@ -3780,7 +3782,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"seed", PGC_USERSET, UNGROUPED,
+ {"seed", PGC_SUSET, UNGROUPED,
gettext_noop("Sets the seed for random-number generation."),
NULL,
GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -3791,7 +3793,7 @@ static struct config_real ConfigureNamesReal[] =
},
{
- {"vacuum_cost_delay", PGC_USERSET, RESOURCES_VACUUM_DELAY,
+ {"vacuum_cost_delay", PGC_SUSET, RESOURCES_VACUUM_DELAY,
gettext_noop("Vacuum cost delay in milliseconds."),
NULL,
GUC_UNIT_MS
@@ -4051,7 +4053,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"DateStyle", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"DateStyle", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the display format for date and time values."),
gettext_noop("Also controls interpretation of ambiguous "
"date inputs."),
@@ -4063,7 +4065,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"default_table_access_method", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_table_access_method", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default table access method for new tables."),
NULL,
GUC_IS_NAME
@@ -4074,7 +4076,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"default_tablespace", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_tablespace", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default tablespace to create tables and indexes in."),
gettext_noop("An empty string selects the database's default tablespace."),
GUC_IS_NAME
@@ -4085,7 +4087,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"temp_tablespaces", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE
@@ -4165,7 +4167,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"lc_monetary", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"lc_monetary", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the locale for formatting monetary amounts."),
NULL
},
@@ -4175,7 +4177,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"lc_numeric", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"lc_numeric", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the locale for formatting numbers."),
NULL
},
@@ -4185,7 +4187,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"lc_time", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"lc_time", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the locale for formatting date and time values."),
NULL
},
@@ -4217,7 +4219,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"local_preload_libraries", PGC_USERSET, CLIENT_CONN_PRELOAD,
+ {"local_preload_libraries", PGC_SUSET, CLIENT_CONN_PRELOAD,
gettext_noop("Lists unprivileged shared libraries to preload into each backend."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE
@@ -4228,7 +4230,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"search_path", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"search_path", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the schema search order for names that are not schema-qualified."),
NULL,
GUC_LIST_INPUT | GUC_LIST_QUOTE | GUC_EXPLAIN
@@ -4264,7 +4266,7 @@ static struct config_string ConfigureNamesString[] =
{
/* Not for general use --- used by SET ROLE */
- {"role", PGC_USERSET, UNGROUPED,
+ {"role", PGC_SUSET, UNGROUPED,
gettext_noop("Sets the current role."),
NULL,
GUC_IS_NAME | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_NOT_WHILE_SEC_REST
@@ -4343,7 +4345,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"TimeZone", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"TimeZone", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the time zone for displaying and interpreting time stamps."),
NULL,
GUC_REPORT
@@ -4353,7 +4355,7 @@ static struct config_string ConfigureNamesString[] =
check_timezone, assign_timezone, show_timezone
},
{
- {"timezone_abbreviations", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"timezone_abbreviations", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Selects a file of time zone abbreviations."),
NULL
},
@@ -4546,7 +4548,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"default_text_search_config", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"default_text_search_config", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets default text search configuration."),
NULL
},
@@ -4608,7 +4610,7 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"application_name", PGC_USERSET, LOGGING_WHAT,
+ {"application_name", PGC_SUSET, LOGGING_WHAT,
gettext_noop("Sets the application name to be reported in statistics and logs."),
NULL,
GUC_IS_NAME | GUC_REPORT | GUC_NOT_IN_SAMPLE
@@ -4672,7 +4674,7 @@ static struct config_string ConfigureNamesString[] =
static struct config_enum ConfigureNamesEnum[] =
{
{
- {"backslash_quote", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
+ {"backslash_quote", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Sets whether \"\\'\" is allowed in string literals."),
NULL
},
@@ -4682,7 +4684,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"bytea_output", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"bytea_output", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the output format for bytea."),
NULL
},
@@ -4692,7 +4694,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"client_min_messages", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"client_min_messages", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the message levels that are sent to the client."),
gettext_noop("Each level includes all the levels that follow it. The later"
" the level, the fewer messages are sent.")
@@ -4713,7 +4715,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"constraint_exclusion", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"constraint_exclusion", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Enables the planner to use constraints to optimize queries."),
gettext_noop("Table scans will be skipped if their constraints"
" guarantee that no rows match the query."),
@@ -4725,7 +4727,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"default_toast_compression", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_toast_compression", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default compression method for compressible values."),
NULL
},
@@ -4736,7 +4738,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"default_transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"default_transaction_isolation", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the transaction isolation level of each new transaction."),
NULL
},
@@ -4746,7 +4748,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"transaction_isolation", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the current transaction's isolation level."),
NULL,
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
@@ -4757,7 +4759,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"IntervalStyle", PGC_USERSET, CLIENT_CONN_LOCALE,
+ {"IntervalStyle", PGC_SUSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the display format for interval values."),
NULL,
GUC_REPORT
@@ -4835,7 +4837,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"synchronous_commit", PGC_USERSET, WAL_SETTINGS,
+ {"synchronous_commit", PGC_SUSET, WAL_SETTINGS,
gettext_noop("Sets the current transaction's synchronization level."),
NULL
},
@@ -4942,7 +4944,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"xmlbinary", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"xmlbinary", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets how binary values are to be encoded in XML."),
NULL
},
@@ -4952,7 +4954,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"xmloption", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ {"xmloption", PGC_SUSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets whether XML data in implicit parsing and serialization "
"operations is to be considered as documents or content fragments."),
NULL
@@ -4973,7 +4975,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"force_parallel_mode", PGC_USERSET, DEVELOPER_OPTIONS,
+ {"force_parallel_mode", PGC_SUSET, DEVELOPER_OPTIONS,
gettext_noop("Forces use of parallel query facilities."),
gettext_noop("If possible, run query using a parallel worker and with parallel restrictions."),
GUC_NOT_IN_SAMPLE | GUC_EXPLAIN
@@ -4984,7 +4986,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"password_encryption", PGC_USERSET, CONN_AUTH_AUTH,
+ {"password_encryption", PGC_SUSET, CONN_AUTH_AUTH,
gettext_noop("Chooses the algorithm for encrypting passwords."),
NULL
},
@@ -4994,7 +4996,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
{
- {"plan_cache_mode", PGC_USERSET, QUERY_TUNING_OTHER,
+ {"plan_cache_mode", PGC_SUSET, QUERY_TUNING_OTHER,
gettext_noop("Controls the planner's selection of custom or generic plan."),
gettext_noop("Prepared statements can have custom and generic plans, and the planner "
"will attempt to choose which is better. This can be set to override "
@@ -5053,6 +5055,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_setting_acl
+ * entries.
*/
static const char *const map_old_guc_names[] = {
"sort_mem", "work_mem",
@@ -5452,25 +5458,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().)
+ *
+ * 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"
@@ -5487,8 +5497,24 @@ 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;
+}
+
+/*
+ * 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().)
+ */
+static bool
+valid_custom_variable_name(const char *name)
+{
+ int partcnt;
+
+ return (valid_variable_name(name, &partcnt) && partcnt > 1);
}
/*
@@ -7542,6 +7568,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 settingid = get_setting_oid(name, true);
+
+ if (OidIsValid(settingid))
+ {
+ AclResult aclresult;
+
+ aclresult = pg_setting_acl_aclcheck(settingid, GetUserId(),
+ ACL_SET_VALUE);
+
+ if (aclresult == ACLCHECK_OK)
+ break; /* okay */
+ }
+
+ /* No granted privilege */
ereport(elevel,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to set parameter \"%s\"",
@@ -8584,6 +8628,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
{
char *name;
char *value;
+ Oid settingId = InvalidOid;
bool resetall = false;
ConfigVariable *head = NULL;
ConfigVariable *tail = NULL;
@@ -8591,16 +8636,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;
+
+ settingId = get_setting_oid(name, true);
+ if (!OidIsValid(settingId))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+
+ aclresult = pg_setting_acl_aclcheck(settingId, GetUserId(),
+ ACL_ALTER_SYSTEM);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_SETTING, name);
+ }
+
switch (altersysstmt->setstmt->kind)
{
case VAR_SET_VALUE:
@@ -8733,6 +8793,21 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
replace_auto_config_value(&head, &tail, name, value);
}
+ /*
+ * Invoke the post-alter hook for setting this GUC variable. Guc variables
+ * do not always have corresponding entries in pg_setting_acl; the hooks
+ * must therefore be prepared to receive settingId = InvalidOid. We also
+ * abuse the notion of subId to pass the kind of alteration (set vs. alter
+ * system), and auxiliaryId to pass the VariableSetKind.
+ *
+ * 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.
+ */
+ InvokeObjectPostAlterHookArg(SettingAclRelationId, settingId,
+ ACL_ALTER_SYSTEM, altersysstmt->setstmt->kind,
+ false);
+
/*
* To ensure crash safety, first write the new file data to a temp file,
* then atomically rename it into place.
@@ -8793,6 +8868,9 @@ void
ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
{
GucAction action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET;
+ GucContext context;
+ AclResult aclresult;
+ Oid settingId;
/*
* Workers synchronize these parameters at the start of the parallel
@@ -8803,6 +8881,24 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
errmsg("cannot set parameters during a parallel operation")));
+ /* Get the Oid of this setting, or InvalidOid if none. */
+ settingId = get_setting_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(settingId))
+ {
+ aclresult = pg_setting_acl_aclcheck(settingId, GetUserId(),
+ ACL_SET_VALUE);
+ if (aclresult == ACLCHECK_OK)
+ context = PGC_SUSET;
+ }
+
switch (stmt->kind)
{
case VAR_SET_VALUE:
@@ -8811,7 +8907,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;
@@ -8896,7 +8992,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;
@@ -8904,6 +9000,16 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
ResetAllOptions();
break;
}
+
+ /*
+ * Invoke the post-alter hook for setting this GUC variable. Guc variables
+ * do not always have corresponding entries in pg_setting_acl; the hooks
+ * must therefore be prepared to receive settingId = InvalidOid. We also
+ * abuse the notion of subId to pass the kind of alteration (set vs. alter
+ * system), and auxiliaryId to pass the VariableSetKind.
+ */
+ InvokeObjectPostAlterHookArg(SettingAclRelationId, settingId,
+ ACL_SET_VALUE, stmt->kind, false);
}
/*
@@ -9664,6 +9770,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, false, 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,
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 97f15971e2..3138722c19 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -164,6 +164,7 @@ static char *features_file;
static char *system_constraints_file;
static char *system_functions_file;
static char *system_views_file;
+static char *setting_privileges_file;
static bool success = false;
static bool made_new_pgdata = false;
static bool found_existing_pgdata = false;
@@ -2464,6 +2465,7 @@ setup_data_file_paths(void)
set_input(&system_constraints_file, "system_constraints.sql");
set_input(&system_functions_file, "system_functions.sql");
set_input(&system_views_file, "system_views.sql");
+ set_input(&setting_privileges_file, "setting_privileges.sql");
if (show_setting || debug)
{
@@ -2492,6 +2494,7 @@ setup_data_file_paths(void)
check_input(system_constraints_file);
check_input(system_functions_file);
check_input(system_views_file);
+ check_input(setting_privileges_file);
}
@@ -2839,6 +2842,8 @@ initialize_data_directory(void)
setup_run_file(cmdfd, system_views_file);
+ setup_run_file(cmdfd, setting_privileges_file);
+
setup_description(cmdfd);
setup_collation(cmdfd);
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index 6086d57cf3..a84efd79e5 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, SETTING 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, "SETTING") == 0)
+ {
+ CONVERT_PRIV('s', "SET VALUE");
+ 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..bb0badca65 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, "SETTING") == 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, "SETTING") == 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 e69dcf8a48..dea1b89ff8 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -14166,6 +14166,9 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
case DEFACLOBJ_NAMESPACE:
type = "SCHEMAS";
break;
+ case DEFACLOBJ_SETTING:
+ type = "SETTINGS";
+ break;
default:
/* shouldn't get here */
fatal("unrecognized object type in default privileges: %d",
@@ -14209,7 +14212,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, SETTING 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..b2cf8cbe76 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,72 @@ 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)
+{
+ PQExpBuffer buf = createPQExpBuffer();
+ PGresult *res;
+ int i;
+
+ printfPQExpBuffer(buf, "SELECT string_agg(acl.privilege_type, ', ' ORDER BY acl.privilege_type), "
+ "set_acl.setting, "
+ "grantee.rolname AS grantee, "
+ "acl.is_grantable, "
+ "grantor.rolname AS grantor "
+ "FROM pg_catalog.pg_setting_acl set_acl, "
+ "LATERAL (SELECT * FROM aclexplode(set_acl.setacl)) acl "
+ "JOIN pg_catalog.pg_authid grantee "
+ "ON acl.grantee = grantee.oid "
+ "LEFT JOIN pg_catalog.pg_authid grantor ON "
+ "acl.grantor = grantor.oid "
+ "WHERE acl.grantee > 0 "
+ "GROUP BY setting, grantee.rolname, is_grantable, grantor.rolname"
+ );
+
+ res = executeQuery(conn, buf->data);
+
+ if (PQntuples(res) > 0)
+ fprintf(OPF, "--\n-- Role privileges on configuration parameters\n--\n\n");
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ char *privilege = PQgetvalue(res, i, 0);
+ char *setting = PQgetvalue(res, i, 1);
+ char *grantee = PQgetvalue(res, i, 2);
+ char *grantable = PQgetvalue(res, i, 3);
+
+ fprintf(OPF, "GRANT %s", privilege);
+ fprintf(OPF, " ON %s", setting);
+ fprintf(OPF, " TO %s", fmtId(grantee));
+ if (*grantable == 't')
+ fprintf(OPF, " WITH GRANT OPTION");
+
+ /*
+ * We don't track the grantor very carefully in the backend, so cope
+ * with the possibility that it has been dropped.
+ */
+ if (!PQgetisnull(res, i, 4))
+ {
+ char *grantor = PQgetvalue(res, i, 4);
+
+ fprintf(OPF, " GRANTED BY %s", fmtId(grantor));
+ }
+ fprintf(OPF, ";\n");
+ }
+
+ PQclear(res);
+ destroyPQExpBuffer(buf);
+
+ fprintf(OPF, "\n\n");
+}
+
/*
* Dump tablespaces.
*/
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 344482ec87..a0c3966da9 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -92,6 +92,7 @@ typedef enum ObjectClass
OCLASS_TYPE, /* pg_type */
OCLASS_CAST, /* pg_cast */
OCLASS_COLLATION, /* pg_collation */
+ OCLASS_SETTING, /* pg_setting_acl */
OCLASS_CONSTRAINT, /* pg_constraint */
OCLASS_CONVERSION, /* pg_conversion */
OCLASS_DEFAULT, /* pg_attrdef */
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index 2a79155636..12ec9b0a31 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_SETTING 'c' /* configuration parameter */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index d8e8715ed1..ebae1f4f65 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7207,6 +7207,25 @@
proname => 'has_type_privilege', provolatile => 's', prorettype => 'bool',
proargtypes => 'oid text', prosrc => 'has_type_privilege_id' },
+{ oid => '8050', descr => 'user privilege on setting by username, setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'name text text', prosrc => 'has_setting_privilege_name_name' },
+{ oid => '8051', descr => 'user privilege on setting by username, setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'name oid text', prosrc => 'has_setting_privilege_name_id' },
+{ oid => '8052', descr => 'user privilege on setting by user oid, setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid text text', prosrc => 'has_setting_privilege_id_name' },
+{ oid => '8053', descr => 'user privilege on setting by user oid, setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid oid text', prosrc => 'has_setting_privilege_id_id' },
+{ oid => '8054', descr => 'current user privilege on setting by setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'text text', prosrc => 'has_setting_privilege_name' },
+{ oid => '8055', descr => 'current user privilege on setting by setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid text', prosrc => 'has_setting_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/catalog/pg_setting_acl.h b/src/include/catalog/pg_setting_acl.h
new file mode 100644
index 0000000000..b7dee55e5c
--- /dev/null
+++ b/src/include/catalog/pg_setting_acl.h
@@ -0,0 +1,63 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_setting_acl.h
+ * definition of the "configuration parameter" system catalog
+ * (pg_setting_acl).
+ *
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_setting_acl.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SETTING_ACL_H
+#define PG_SETTING_ACL_H
+
+#include "catalog/genbki.h"
+#include "catalog/pg_setting_acl_d.h"
+
+/* ----------------
+ * pg_setting_acl definition. cpp turns this into
+ * typedef struct FormData_pg_setting_acl
+ * ----------------
+ */
+CATALOG(pg_setting_acl,8924,SettingAclRelationId) BKI_SHARED_RELATION
+{
+ Oid oid; /* oid */
+ /*
+
+ * Variable-length fields start here, but we allow direct access to
+ * setting.
+ */
+ text setting BKI_FORCE_NOT_NULL;
+
+#ifdef CATALOG_VARLEN
+ /* Access privileges */
+ aclitem setacl[1] BKI_DEFAULT(_null_);
+#endif
+} FormData_pg_setting_acl;
+
+
+/* ----------------
+ * Form_pg_setting_acl corresponds to a pointer to a tuple with
+ * the format of pg_setting_acl relation.
+ * ----------------
+ */
+typedef FormData_pg_setting_acl *Form_pg_setting_acl;
+
+DECLARE_TOAST(pg_setting_acl, 8925, 8926);
+#define PgSettingAclToastTable 8925
+#define PgSettingAclToastIndex 8926
+
+DECLARE_UNIQUE_INDEX(pg_setting_acl_setting_index, 8927, SettingAclSettingIndexId, on pg_setting_acl using btree(setting text_ops));
+DECLARE_UNIQUE_INDEX_PKEY(pg_setting_acl_oid_index, 8928, SettingAclOidIndexId, on pg_setting_acl using btree(oid oid_ops));
+
+extern Oid SettingAclCreate(const char *setting, bool if_not_exists);
+
+#endif /* PG_SETTING_ACL_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 1617702d9d..5f5e4ae8e1 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -92,7 +92,9 @@ 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 N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */
+#define ACL_SET_VALUE (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
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
@@ -1795,6 +1797,7 @@ typedef enum ObjectType
OBJECT_CAST,
OBJECT_COLUMN,
OBJECT_COLLATION,
+ OBJECT_SETTING,
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DEFAULT,
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index bcef7eed2f..f5ed8c2082 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -307,6 +307,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 1ce4c5556e..e6d7761643 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -146,9 +146,11 @@ 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_ALTER_SYSTEM_CHR 'A'
/* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTc"
+#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcsA"
/*
* Bitmasks defining "all rights" for each supported object type
@@ -165,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_SETTING (ACL_SET_VALUE|ACL_ALTER_SYSTEM)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -223,6 +226,8 @@ extern void select_best_grantor(Oid roleId, AclMode privileges,
extern void initialize_acl(void);
+extern Oid get_setting_oid(const char *setting, bool missing_ok);
+
/*
* prototypes for functions in aclchk.c
*/
@@ -243,6 +248,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_setting_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,
@@ -271,6 +278,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_setting_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..2d0a1a53f7 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -382,6 +382,8 @@ extern int set_config_option(const char *name, const char *value,
GucAction action, bool changeVal, int elevel,
bool is_reload);
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..7a8ba82770 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_setting_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..5961921462 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -89,6 +89,8 @@ enum SysCacheIdentifier
REPLORIGNAME,
RULERELNAME,
SEQRELID,
+ SETTINGNAME,
+ SETTINGOID,
STATEXTDATASTXOID,
STATEXTNAMENSP,
STATEXTOID,
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 c73bd37835..7f8984bc45 100644
--- a/src/test/modules/test_pg_dump/t/001_base.pl
+++ b/src/test/modules/test_pg_dump/t/001_base.pl
@@ -317,6 +317,53 @@ my %tests = (
like => { pg_dumpall_globals => 1, },
},
+ 'GRANT ALTER SYSTEM ON ignore_checksum_failure' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT ALTER SYSTEM ON ignore_checksum_failure TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM ON ignore_checksum_failure TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT SET VALUE ON my.missing.guc' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET VALUE ON my.missing.guc TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT SET VALUE ON my\.missing\.guc TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT SET VALUE, ALTER SYSTEM ON something WITH GRANT OPTION' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET VALUE, ALTER SYSTEM ON something TO regress_dump_test_role WITH GRANT OPTION;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM, SET VALUE ON something TO regress_dump_test_role WITH GRANT OPTION GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALTER SYSTEM ON MyReallyLong.ButValidCustom.GucNameThatCannotFit.InNamedata64Byte.Format' => {
+ create_order => 2,
+ create_sql =>
+ # configuration parameters get cased folded
+ 'GRANT ALTER SYSTEM ON MyReallyLong.ButValidCustom.GucNameThatCannotFit.InNamedata64Byte.Format TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM ON myreallylong\.butvalidcustom\.gucnamethatcannotfit\.innamedata64byte\.format TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALTER SYSTEM, SET VALUE ON my.guc TO regress_dump_test_role GRANTED BY CURRENT_ROLE' => {
+ create_order => 2,
+ create_sql =>
+ # GRANTED BY CURRENT_ROLE is allowed for SQL compatibility, but is ignored
+ 'GRANT ALTER SYSTEM, SET VALUE ON my.guc TO regress_dump_test_role GRANTED BY CURRENT_ROLE;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM, SET VALUE ON my\.guc TO regress_dump_test_role GRANTED BY /m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
'CREATE SCHEMA public' => {
regexp => qr/^CREATE SCHEMA public;/m,
like => {
diff --git a/src/test/regress/expected/guc_privs.out b/src/test/regress/expected/guc_privs.out
new file mode 100644
index 0000000000..c2033e77d1
--- /dev/null
+++ b/src/test/regress/expected/guc_privs.out
@@ -0,0 +1,780 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform all 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;
+-- Check setting privileges prior to creating any
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+ grantee | setting | privilege_type | is_grantable
+---------+-------------------------------------+----------------+--------------
+ | DateStyle | SET VALUE | f
+ | IntervalStyle | SET VALUE | f
+ | TimeZone | SET VALUE | f
+ | application_name | SET VALUE | f
+ | array_nulls | SET VALUE | f
+ | backend_flush_after | SET VALUE | f
+ | backslash_quote | SET VALUE | f
+ | bytea_output | SET VALUE | f
+ | check_function_bodies | SET VALUE | f
+ | client_connection_check_interval | SET VALUE | f
+ | client_min_messages | SET VALUE | f
+ | commit_siblings | SET VALUE | f
+ | constraint_exclusion | SET VALUE | f
+ | cpu_index_tuple_cost | SET VALUE | f
+ | cpu_operator_cost | SET VALUE | f
+ | cpu_tuple_cost | SET VALUE | f
+ | cursor_tuple_fraction | SET VALUE | f
+ | debug_pretty_print | SET VALUE | f
+ | debug_print_parse | SET VALUE | f
+ | debug_print_plan | SET VALUE | f
+ | debug_print_rewritten | SET VALUE | f
+ | default_statistics_target | SET VALUE | f
+ | default_table_access_method | SET VALUE | f
+ | default_tablespace | SET VALUE | f
+ | default_text_search_config | SET VALUE | f
+ | default_toast_compression | SET VALUE | f
+ | default_transaction_deferrable | SET VALUE | f
+ | default_transaction_isolation | SET VALUE | f
+ | default_transaction_read_only | SET VALUE | f
+ | default_with_oids | SET VALUE | f
+ | effective_cache_size | SET VALUE | f
+ | effective_io_concurrency | SET VALUE | f
+ | enable_async_append | SET VALUE | f
+ | enable_bitmapscan | SET VALUE | f
+ | enable_gathermerge | SET VALUE | f
+ | enable_hashagg | SET VALUE | f
+ | enable_hashjoin | SET VALUE | f
+ | enable_incremental_sort | SET VALUE | f
+ | enable_indexonlyscan | SET VALUE | f
+ | enable_indexscan | SET VALUE | f
+ | enable_material | SET VALUE | f
+ | enable_memoize | SET VALUE | f
+ | enable_mergejoin | SET VALUE | f
+ | enable_nestloop | SET VALUE | f
+ | enable_parallel_append | SET VALUE | f
+ | enable_parallel_hash | SET VALUE | f
+ | enable_partition_pruning | SET VALUE | f
+ | enable_partitionwise_aggregate | SET VALUE | f
+ | enable_partitionwise_join | SET VALUE | f
+ | enable_seqscan | SET VALUE | f
+ | enable_sort | SET VALUE | f
+ | enable_tidscan | SET VALUE | f
+ | escape_string_warning | SET VALUE | f
+ | exit_on_error | SET VALUE | f
+ | extra_float_digits | SET VALUE | f
+ | force_parallel_mode | SET VALUE | f
+ | from_collapse_limit | SET VALUE | f
+ | geqo | SET VALUE | f
+ | geqo_effort | SET VALUE | f
+ | geqo_generations | SET VALUE | f
+ | geqo_pool_size | SET VALUE | f
+ | geqo_seed | SET VALUE | f
+ | geqo_selection_bias | SET VALUE | f
+ | geqo_threshold | SET VALUE | f
+ | gin_fuzzy_search_limit | SET VALUE | f
+ | gin_pending_list_limit | SET VALUE | f
+ | hash_mem_multiplier | SET VALUE | f
+ | idle_in_transaction_session_timeout | SET VALUE | f
+ | idle_session_timeout | SET VALUE | f
+ | jit | SET VALUE | f
+ | jit_above_cost | SET VALUE | f
+ | jit_expressions | SET VALUE | f
+ | jit_inline_above_cost | SET VALUE | f
+ | jit_optimize_above_cost | SET VALUE | f
+ | jit_tuple_deforming | SET VALUE | f
+ | join_collapse_limit | SET VALUE | f
+ | lc_monetary | SET VALUE | f
+ | lc_numeric | SET VALUE | f
+ | lc_time | SET VALUE | f
+ | local_preload_libraries | SET VALUE | f
+ | lock_timeout | SET VALUE | f
+ | log_parameter_max_length_on_error | SET VALUE | f
+ | logical_decoding_work_mem | SET VALUE | f
+ | maintenance_io_concurrency | SET VALUE | f
+ | maintenance_work_mem | SET VALUE | f
+ | max_parallel_maintenance_workers | SET VALUE | f
+ | max_parallel_workers | SET VALUE | f
+ | max_parallel_workers_per_gather | SET VALUE | f
+ | min_parallel_index_scan_size | SET VALUE | f
+ | min_parallel_table_scan_size | SET VALUE | f
+ | optimize_bounded_sort | SET VALUE | f
+ | parallel_leader_participation | SET VALUE | f
+ | parallel_setup_cost | SET VALUE | f
+ | parallel_tuple_cost | SET VALUE | f
+ | password_encryption | SET VALUE | f
+ | plan_cache_mode | SET VALUE | f
+ | quote_all_identifiers | SET VALUE | f
+ | random_page_cost | SET VALUE | f
+ | role | SET VALUE | f
+ | row_security | SET VALUE | f
+ | search_path | SET VALUE | f
+ | seed | SET VALUE | f
+ | seq_page_cost | SET VALUE | f
+ | ssl_renegotiation_limit | SET VALUE | f
+ | standard_conforming_strings | SET VALUE | f
+ | statement_timeout | SET VALUE | f
+ | synchronize_seqscans | SET VALUE | f
+ | synchronous_commit | SET VALUE | f
+ | tcp_keepalives_count | SET VALUE | f
+ | tcp_keepalives_idle | SET VALUE | f
+ | tcp_keepalives_interval | SET VALUE | f
+ | tcp_user_timeout | SET VALUE | f
+ | temp_buffers | SET VALUE | f
+ | temp_tablespaces | SET VALUE | f
+ | timezone_abbreviations | SET VALUE | f
+ | trace_notify | SET VALUE | f
+ | trace_sort | SET VALUE | f
+ | trace_syncscan | SET VALUE | f
+ | transaction_deferrable | SET VALUE | f
+ | transaction_isolation | SET VALUE | f
+ | transaction_read_only | SET VALUE | f
+ | transform_null_equals | SET VALUE | f
+ | vacuum_cost_delay | SET VALUE | f
+ | vacuum_cost_limit | SET VALUE | f
+ | vacuum_cost_page_dirty | SET VALUE | f
+ | vacuum_cost_page_hit | SET VALUE | f
+ | vacuum_cost_page_miss | SET VALUE | f
+ | vacuum_failsafe_age | SET VALUE | f
+ | vacuum_freeze_min_age | SET VALUE | f
+ | vacuum_freeze_table_age | SET VALUE | f
+ | vacuum_multixact_failsafe_age | SET VALUE | f
+ | vacuum_multixact_freeze_min_age | SET VALUE | f
+ | vacuum_multixact_freeze_table_age | SET VALUE | f
+ | wal_sender_timeout | SET VALUE | f
+ | wal_skip_threshold | SET VALUE | f
+ | work_mem | SET VALUE | f
+ | xmlbinary | SET VALUE | f
+ | xmloption | SET VALUE | f
+(138 rows)
+
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+-- Check the new role does not yet have privileges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ f
+(1 row)
+
+-- Check inappropriate and nonsense privilege types
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+-- Grant privileges on settings to the new non-superuser role
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Check setting privileges after creating some
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+ grantee | setting | privilege_type | is_grantable
+-----------------------------+-------------------------------------+----------------+--------------
+ regress_host_resource_admin | autovacuum_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | autovacuum_work_mem | SET VALUE | f
+ regress_host_resource_admin | hash_mem_multiplier | ALTER SYSTEM | f
+ regress_host_resource_admin | hash_mem_multiplier | SET VALUE | f
+ regress_host_resource_admin | logical_decoding_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | logical_decoding_work_mem | SET VALUE | f
+ regress_host_resource_admin | maintenance_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | maintenance_work_mem | SET VALUE | f
+ regress_host_resource_admin | max_stack_depth | ALTER SYSTEM | f
+ regress_host_resource_admin | max_stack_depth | SET VALUE | f
+ regress_host_resource_admin | min_dynamic_shared_memory | ALTER SYSTEM | f
+ regress_host_resource_admin | min_dynamic_shared_memory | SET VALUE | f
+ regress_host_resource_admin | shared_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | shared_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_file_limit | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_file_limit | SET VALUE | f
+ regress_host_resource_admin | work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | work_mem | SET VALUE | f
+ | DateStyle | SET VALUE | f
+ | IntervalStyle | SET VALUE | f
+ | TimeZone | SET VALUE | f
+ | application_name | SET VALUE | f
+ | array_nulls | SET VALUE | f
+ | backend_flush_after | SET VALUE | f
+ | backslash_quote | SET VALUE | f
+ | bytea_output | SET VALUE | f
+ | check_function_bodies | SET VALUE | f
+ | client_connection_check_interval | SET VALUE | f
+ | client_min_messages | SET VALUE | f
+ | commit_siblings | SET VALUE | f
+ | constraint_exclusion | SET VALUE | f
+ | cpu_index_tuple_cost | SET VALUE | f
+ | cpu_operator_cost | SET VALUE | f
+ | cpu_tuple_cost | SET VALUE | f
+ | cursor_tuple_fraction | SET VALUE | f
+ | debug_pretty_print | SET VALUE | f
+ | debug_print_parse | SET VALUE | f
+ | debug_print_plan | SET VALUE | f
+ | debug_print_rewritten | SET VALUE | f
+ | default_statistics_target | SET VALUE | f
+ | default_table_access_method | SET VALUE | f
+ | default_tablespace | SET VALUE | f
+ | default_text_search_config | SET VALUE | f
+ | default_toast_compression | SET VALUE | f
+ | default_transaction_deferrable | SET VALUE | f
+ | default_transaction_isolation | SET VALUE | f
+ | default_transaction_read_only | SET VALUE | f
+ | default_with_oids | SET VALUE | f
+ | effective_cache_size | SET VALUE | f
+ | effective_io_concurrency | SET VALUE | f
+ | enable_async_append | SET VALUE | f
+ | enable_bitmapscan | SET VALUE | f
+ | enable_gathermerge | SET VALUE | f
+ | enable_hashagg | SET VALUE | f
+ | enable_hashjoin | SET VALUE | f
+ | enable_incremental_sort | SET VALUE | f
+ | enable_indexonlyscan | SET VALUE | f
+ | enable_indexscan | SET VALUE | f
+ | enable_material | SET VALUE | f
+ | enable_memoize | SET VALUE | f
+ | enable_mergejoin | SET VALUE | f
+ | enable_nestloop | SET VALUE | f
+ | enable_parallel_append | SET VALUE | f
+ | enable_parallel_hash | SET VALUE | f
+ | enable_partition_pruning | SET VALUE | f
+ | enable_partitionwise_aggregate | SET VALUE | f
+ | enable_partitionwise_join | SET VALUE | f
+ | enable_seqscan | SET VALUE | f
+ | enable_sort | SET VALUE | f
+ | enable_tidscan | SET VALUE | f
+ | escape_string_warning | SET VALUE | f
+ | exit_on_error | SET VALUE | f
+ | extra_float_digits | SET VALUE | f
+ | force_parallel_mode | SET VALUE | f
+ | from_collapse_limit | SET VALUE | f
+ | geqo | SET VALUE | f
+ | geqo_effort | SET VALUE | f
+ | geqo_generations | SET VALUE | f
+ | geqo_pool_size | SET VALUE | f
+ | geqo_seed | SET VALUE | f
+ | geqo_selection_bias | SET VALUE | f
+ | geqo_threshold | SET VALUE | f
+ | gin_fuzzy_search_limit | SET VALUE | f
+ | gin_pending_list_limit | SET VALUE | f
+ | hash_mem_multiplier | SET VALUE | f
+ | idle_in_transaction_session_timeout | SET VALUE | f
+ | idle_session_timeout | SET VALUE | f
+ | jit | SET VALUE | f
+ | jit_above_cost | SET VALUE | f
+ | jit_expressions | SET VALUE | f
+ | jit_inline_above_cost | SET VALUE | f
+ | jit_optimize_above_cost | SET VALUE | f
+ | jit_tuple_deforming | SET VALUE | f
+ | join_collapse_limit | SET VALUE | f
+ | lc_monetary | SET VALUE | f
+ | lc_numeric | SET VALUE | f
+ | lc_time | SET VALUE | f
+ | local_preload_libraries | SET VALUE | f
+ | lock_timeout | SET VALUE | f
+ | log_parameter_max_length_on_error | SET VALUE | f
+ | logical_decoding_work_mem | SET VALUE | f
+ | maintenance_io_concurrency | SET VALUE | f
+ | maintenance_work_mem | SET VALUE | f
+ | max_parallel_maintenance_workers | SET VALUE | f
+ | max_parallel_workers | SET VALUE | f
+ | max_parallel_workers_per_gather | SET VALUE | f
+ | min_parallel_index_scan_size | SET VALUE | f
+ | min_parallel_table_scan_size | SET VALUE | f
+ | optimize_bounded_sort | SET VALUE | f
+ | parallel_leader_participation | SET VALUE | f
+ | parallel_setup_cost | SET VALUE | f
+ | parallel_tuple_cost | SET VALUE | f
+ | password_encryption | SET VALUE | f
+ | plan_cache_mode | SET VALUE | f
+ | quote_all_identifiers | SET VALUE | f
+ | random_page_cost | SET VALUE | f
+ | role | SET VALUE | f
+ | row_security | SET VALUE | f
+ | search_path | SET VALUE | f
+ | seed | SET VALUE | f
+ | seq_page_cost | SET VALUE | f
+ | ssl_renegotiation_limit | SET VALUE | f
+ | standard_conforming_strings | SET VALUE | f
+ | statement_timeout | SET VALUE | f
+ | synchronize_seqscans | SET VALUE | f
+ | synchronous_commit | SET VALUE | f
+ | tcp_keepalives_count | SET VALUE | f
+ | tcp_keepalives_idle | SET VALUE | f
+ | tcp_keepalives_interval | SET VALUE | f
+ | tcp_user_timeout | SET VALUE | f
+ | temp_buffers | SET VALUE | f
+ | temp_tablespaces | SET VALUE | f
+ | timezone_abbreviations | SET VALUE | f
+ | trace_notify | SET VALUE | f
+ | trace_sort | SET VALUE | f
+ | trace_syncscan | SET VALUE | f
+ | transaction_deferrable | SET VALUE | f
+ | transaction_isolation | SET VALUE | f
+ | transaction_read_only | SET VALUE | f
+ | transform_null_equals | SET VALUE | f
+ | vacuum_cost_delay | SET VALUE | f
+ | vacuum_cost_limit | SET VALUE | f
+ | vacuum_cost_page_dirty | SET VALUE | f
+ | vacuum_cost_page_hit | SET VALUE | f
+ | vacuum_cost_page_miss | SET VALUE | f
+ | vacuum_failsafe_age | SET VALUE | f
+ | vacuum_freeze_min_age | SET VALUE | f
+ | vacuum_freeze_table_age | SET VALUE | f
+ | vacuum_multixact_failsafe_age | SET VALUE | f
+ | vacuum_multixact_freeze_min_age | SET VALUE | f
+ | vacuum_multixact_freeze_table_age | SET VALUE | f
+ | wal_sender_timeout | SET VALUE | f
+ | wal_skip_threshold | SET VALUE | f
+ | work_mem | SET VALUE | f
+ | xmlbinary | SET VALUE | f
+ | xmloption | SET VALUE | f
+(158 rows)
+
+-- Check the new role now has privilges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE WITH GRANT OPTION, ALTER SYSTEM WITH GRANT OPTION');
+ has_setting_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_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+SELECT has_setting_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_setting_privilege('regress_host_resource_admin',
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('hash_mem_multiplier', 'set value');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'work_mem'),
+ 'alter system with grant option');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+-- Check setting privileges show up in view
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+ grantee | setting | privilege_type | is_grantable
+-----------------------------+-------------------------------------+----------------+--------------
+ regress_host_resource_admin | autovacuum_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | autovacuum_work_mem | SET VALUE | f
+ regress_host_resource_admin | hash_mem_multiplier | ALTER SYSTEM | f
+ regress_host_resource_admin | hash_mem_multiplier | SET VALUE | f
+ regress_host_resource_admin | logical_decoding_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | logical_decoding_work_mem | SET VALUE | f
+ regress_host_resource_admin | maintenance_work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | maintenance_work_mem | SET VALUE | f
+ regress_host_resource_admin | max_stack_depth | ALTER SYSTEM | f
+ regress_host_resource_admin | max_stack_depth | SET VALUE | f
+ regress_host_resource_admin | min_dynamic_shared_memory | ALTER SYSTEM | f
+ regress_host_resource_admin | min_dynamic_shared_memory | SET VALUE | f
+ regress_host_resource_admin | shared_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | shared_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_buffers | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_buffers | SET VALUE | f
+ regress_host_resource_admin | temp_file_limit | ALTER SYSTEM | f
+ regress_host_resource_admin | temp_file_limit | SET VALUE | f
+ regress_host_resource_admin | work_mem | ALTER SYSTEM | f
+ regress_host_resource_admin | work_mem | SET VALUE | f
+ | DateStyle | SET VALUE | f
+ | IntervalStyle | SET VALUE | f
+ | TimeZone | SET VALUE | f
+ | application_name | SET VALUE | f
+ | array_nulls | SET VALUE | f
+ | backend_flush_after | SET VALUE | f
+ | backslash_quote | SET VALUE | f
+ | bytea_output | SET VALUE | f
+ | check_function_bodies | SET VALUE | f
+ | client_connection_check_interval | SET VALUE | f
+ | client_min_messages | SET VALUE | f
+ | commit_siblings | SET VALUE | f
+ | constraint_exclusion | SET VALUE | f
+ | cpu_index_tuple_cost | SET VALUE | f
+ | cpu_operator_cost | SET VALUE | f
+ | cpu_tuple_cost | SET VALUE | f
+ | cursor_tuple_fraction | SET VALUE | f
+ | debug_pretty_print | SET VALUE | f
+ | debug_print_parse | SET VALUE | f
+ | debug_print_plan | SET VALUE | f
+ | debug_print_rewritten | SET VALUE | f
+ | default_statistics_target | SET VALUE | f
+ | default_table_access_method | SET VALUE | f
+ | default_tablespace | SET VALUE | f
+ | default_text_search_config | SET VALUE | f
+ | default_toast_compression | SET VALUE | f
+ | default_transaction_deferrable | SET VALUE | f
+ | default_transaction_isolation | SET VALUE | f
+ | default_transaction_read_only | SET VALUE | f
+ | default_with_oids | SET VALUE | f
+ | effective_cache_size | SET VALUE | f
+ | effective_io_concurrency | SET VALUE | f
+ | enable_async_append | SET VALUE | f
+ | enable_bitmapscan | SET VALUE | f
+ | enable_gathermerge | SET VALUE | f
+ | enable_hashagg | SET VALUE | f
+ | enable_hashjoin | SET VALUE | f
+ | enable_incremental_sort | SET VALUE | f
+ | enable_indexonlyscan | SET VALUE | f
+ | enable_indexscan | SET VALUE | f
+ | enable_material | SET VALUE | f
+ | enable_memoize | SET VALUE | f
+ | enable_mergejoin | SET VALUE | f
+ | enable_nestloop | SET VALUE | f
+ | enable_parallel_append | SET VALUE | f
+ | enable_parallel_hash | SET VALUE | f
+ | enable_partition_pruning | SET VALUE | f
+ | enable_partitionwise_aggregate | SET VALUE | f
+ | enable_partitionwise_join | SET VALUE | f
+ | enable_seqscan | SET VALUE | f
+ | enable_sort | SET VALUE | f
+ | enable_tidscan | SET VALUE | f
+ | escape_string_warning | SET VALUE | f
+ | exit_on_error | SET VALUE | f
+ | extra_float_digits | SET VALUE | f
+ | force_parallel_mode | SET VALUE | f
+ | from_collapse_limit | SET VALUE | f
+ | geqo | SET VALUE | f
+ | geqo_effort | SET VALUE | f
+ | geqo_generations | SET VALUE | f
+ | geqo_pool_size | SET VALUE | f
+ | geqo_seed | SET VALUE | f
+ | geqo_selection_bias | SET VALUE | f
+ | geqo_threshold | SET VALUE | f
+ | gin_fuzzy_search_limit | SET VALUE | f
+ | gin_pending_list_limit | SET VALUE | f
+ | hash_mem_multiplier | SET VALUE | f
+ | idle_in_transaction_session_timeout | SET VALUE | f
+ | idle_session_timeout | SET VALUE | f
+ | jit | SET VALUE | f
+ | jit_above_cost | SET VALUE | f
+ | jit_expressions | SET VALUE | f
+ | jit_inline_above_cost | SET VALUE | f
+ | jit_optimize_above_cost | SET VALUE | f
+ | jit_tuple_deforming | SET VALUE | f
+ | join_collapse_limit | SET VALUE | f
+ | lc_monetary | SET VALUE | f
+ | lc_numeric | SET VALUE | f
+ | lc_time | SET VALUE | f
+ | local_preload_libraries | SET VALUE | f
+ | lock_timeout | SET VALUE | f
+ | log_parameter_max_length_on_error | SET VALUE | f
+ | logical_decoding_work_mem | SET VALUE | f
+ | maintenance_io_concurrency | SET VALUE | f
+ | maintenance_work_mem | SET VALUE | f
+ | max_parallel_maintenance_workers | SET VALUE | f
+ | max_parallel_workers | SET VALUE | f
+ | max_parallel_workers_per_gather | SET VALUE | f
+ | min_parallel_index_scan_size | SET VALUE | f
+ | min_parallel_table_scan_size | SET VALUE | f
+ | optimize_bounded_sort | SET VALUE | f
+ | parallel_leader_participation | SET VALUE | f
+ | parallel_setup_cost | SET VALUE | f
+ | parallel_tuple_cost | SET VALUE | f
+ | password_encryption | SET VALUE | f
+ | plan_cache_mode | SET VALUE | f
+ | quote_all_identifiers | SET VALUE | f
+ | random_page_cost | SET VALUE | f
+ | role | SET VALUE | f
+ | row_security | SET VALUE | f
+ | search_path | SET VALUE | f
+ | seed | SET VALUE | f
+ | seq_page_cost | SET VALUE | f
+ | ssl_renegotiation_limit | SET VALUE | f
+ | standard_conforming_strings | SET VALUE | f
+ | statement_timeout | SET VALUE | f
+ | synchronize_seqscans | SET VALUE | f
+ | synchronous_commit | SET VALUE | f
+ | tcp_keepalives_count | SET VALUE | f
+ | tcp_keepalives_idle | SET VALUE | f
+ | tcp_keepalives_interval | SET VALUE | f
+ | tcp_user_timeout | SET VALUE | f
+ | temp_buffers | SET VALUE | f
+ | temp_tablespaces | SET VALUE | f
+ | timezone_abbreviations | SET VALUE | f
+ | trace_notify | SET VALUE | f
+ | trace_sort | SET VALUE | f
+ | trace_syncscan | SET VALUE | f
+ | transaction_deferrable | SET VALUE | f
+ | transaction_isolation | SET VALUE | f
+ | transaction_read_only | SET VALUE | f
+ | transform_null_equals | SET VALUE | f
+ | vacuum_cost_delay | SET VALUE | f
+ | vacuum_cost_limit | SET VALUE | f
+ | vacuum_cost_page_dirty | SET VALUE | f
+ | vacuum_cost_page_hit | SET VALUE | f
+ | vacuum_cost_page_miss | SET VALUE | f
+ | vacuum_failsafe_age | SET VALUE | f
+ | vacuum_freeze_min_age | SET VALUE | f
+ | vacuum_freeze_table_age | SET VALUE | f
+ | vacuum_multixact_failsafe_age | SET VALUE | f
+ | vacuum_multixact_freeze_min_age | SET VALUE | f
+ | vacuum_multixact_freeze_table_age | SET VALUE | f
+ | wal_sender_timeout | SET VALUE | f
+ | wal_skip_threshold | SET VALUE | f
+ | work_mem | SET VALUE | f
+ | xmlbinary | SET VALUE | f
+ | xmloption | SET VALUE | f
+(158 rows)
+
+-- 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
+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
+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 setting temp_buffers
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting logical_decoding_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting autovacuum_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting temp_file_limit
+-- Use "revoke" to remove the privileges and allow the role to be dropped
+REVOKE SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 setting temp_buffers
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting logical_decoding_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting autovacuum_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting 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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 setting temp_buffers
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting logical_decoding_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting autovacuum_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting 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
+-- Create non-superuser with privileges to configure plgsql custom variables
+CREATE ROLE regress_plpgsql_admin NOSUPERUSER;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin;
+SET plpgsql.extra_errors TO 'all';
+SET plpgsql.extra_warnings TO 'all';
+RESET plpgsql.extra_errors;
+RESET plpgsql.extra_warnings;
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all';
+ERROR: permission denied to set parameter "plpgsql.extra_errors"
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all';
+ERROR: permission denied to set parameter "plpgsql.extra_warnings"
+ALTER SYSTEM RESET plpgsql.extra_errors;
+ERROR: permission denied to set parameter "plpgsql.extra_errors"
+ALTER SYSTEM RESET plpgsql.extra_warnings;
+ERROR: permission denied to set parameter "plpgsql.extra_warnings"
+SET SESSION AUTHORIZATION regress_admin;
+GRANT SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+TO regress_plpgsql_admin;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin; -- ok
+SET plpgsql.extra_errors TO 'all'; -- ok
+SET plpgsql.extra_warnings TO 'all'; -- ok
+RESET plpgsql.extra_errors; -- ok
+RESET plpgsql.extra_warnings; -- ok
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all'; -- ok
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all'; -- ok
+ALTER SYSTEM RESET plpgsql.extra_errors; -- ok
+ALTER SYSTEM RESET plpgsql.extra_warnings; -- ok
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_plpgsql_admin; -- fail, privileges remain
+ERROR: role "regress_plpgsql_admin" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting plpgsql.extra_warnings
+privileges for setting plpgsql.extra_errors
+REVOKE SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+FROM regress_plpgsql_admin;
+DROP ROLE regress_plpgsql_admin; -- ok
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 291e21d7a6..1c762cea54 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -54,6 +54,33 @@ REVOKE pg_read_all_settings FROM regress_priv_user8;
DROP USER regress_priv_user10;
DROP USER regress_priv_user9;
DROP USER regress_priv_user8;
+GRANT SET VALUE ON enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON enable_nestloop TO regress_priv_user6;
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+GRANT ALTER SYSTEM ON enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON "" TO regress_priv_user7; -- bad name
+ERROR: zero-length delimited identifier at or near """"
+LINE 1: GRANT ALTER SYSTEM ON "" TO regress_priv_user7;
+ ^
+GRANT ALTER SYSTEM ON " " TO regress_priv_user7; -- bad name
+ERROR: invalid setting name " "
+GRANT ALTER SYSTEM ON " foo " TO regress_priv_user7; -- bad name
+ERROR: invalid setting name " foo "
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
ALTER GROUP regress_priv_group1 ADD USER regress_priv_user4;
@@ -2355,10 +2382,32 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+ERROR: role "regress_priv_user6" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting enable_memoize
+privileges for setting enable_nestloop
+DROP USER regress_priv_user7; -- privileges remain
+ERROR: role "regress_priv_user7" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting enable_seqscan
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
+privileges for table persons2
+privileges for setting no_such_param
+privileges for setting no_such_extension.no_such_param
+privileges for setting no_such_extension.no_such_param.longer.than.maximum.namedata.length
DROP USER regress_priv_user8; -- does not exist
ERROR: role "regress_priv_user8" does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+DROP USER regress_priv_user7; -- ok
+REVOKE SET VALUE ON enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON enable_nestloop FROM regress_priv_user6;
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
CREATE TABLE lock_table (a int);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index ac468568a1..0d0e3ff410 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1686,6 +1686,19 @@ pg_sequences| SELECT n.nspname AS schemaname,
JOIN pg_class c ON ((c.oid = s.seqrelid)))
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char"));
+pg_setting_privileges| SELECT grantor.rolname AS grantor,
+ grantee.rolname AS grantee,
+ set_acl.setting,
+ acl.privilege_type,
+ acl.is_grantable
+ FROM pg_setting_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_settings| SELECT a.name,
a.setting,
a.unit,
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 6d8f524ae9..4dfa0642c0 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -86,7 +86,7 @@ test: brin_bloom brin_multi
# psql depends on create_am
# amutils depends on geometry, create_index_spgist, hash_index, brin
# ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role guc_privs
# collate.*.utf8 tests cannot be run in parallel with each other
test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8
diff --git a/src/test/regress/sql/guc_privs.sql b/src/test/regress/sql/guc_privs.sql
new file mode 100644
index 0000000000..076c136563
--- /dev/null
+++ b/src/test/regress/sql/guc_privs.sql
@@ -0,0 +1,185 @@
+-- Test superuser
+-- Superuser DBA
+CREATE ROLE regress_admin SUPERUSER;
+-- Perform all 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;
+-- Check setting privileges prior to creating any
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+-- Create non-superuser with privileges to configure host resource usage
+CREATE ROLE regress_host_resource_admin NOSUPERUSER;
+-- Check the new role does not yet have privileges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+-- Check inappropriate and nonsense privilege types
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+-- Grant privileges on settings to the new non-superuser role
+GRANT SET VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Check setting privileges after creating some
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+-- Check the new role now has privilges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE 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_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER WITH GRANT OPTION');
+-- Check other function signatures
+SELECT has_setting_privilege('regress_host_resource_admin',
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET VALUE');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+SELECT has_setting_privilege('hash_mem_multiplier', 'set value');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'work_mem'),
+ 'alter system with grant option');
+-- Check setting privileges show up in view
+SELECT grantee, setting, privilege_type, is_grantable
+ FROM pg_catalog.pg_setting_privileges
+ ORDER BY grantee, setting, privilege_type;
+-- 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
+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
+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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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
+-- Create non-superuser with privileges to configure plgsql custom variables
+CREATE ROLE regress_plpgsql_admin NOSUPERUSER;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin;
+SET plpgsql.extra_errors TO 'all';
+SET plpgsql.extra_warnings TO 'all';
+RESET plpgsql.extra_errors;
+RESET plpgsql.extra_warnings;
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all';
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all';
+ALTER SYSTEM RESET plpgsql.extra_errors;
+ALTER SYSTEM RESET plpgsql.extra_warnings;
+SET SESSION AUTHORIZATION regress_admin;
+GRANT SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+TO regress_plpgsql_admin;
+-- Perform all operations as user 'regress_plpgsql_admin' --
+SET SESSION AUTHORIZATION regress_plpgsql_admin; -- ok
+SET plpgsql.extra_errors TO 'all'; -- ok
+SET plpgsql.extra_warnings TO 'all'; -- ok
+RESET plpgsql.extra_errors; -- ok
+RESET plpgsql.extra_warnings; -- ok
+ALTER SYSTEM SET plpgsql.extra_errors TO 'all'; -- ok
+ALTER SYSTEM SET plpgsql.extra_warnings TO 'all'; -- ok
+ALTER SYSTEM RESET plpgsql.extra_errors; -- ok
+ALTER SYSTEM RESET plpgsql.extra_warnings; -- ok
+SET SESSION AUTHORIZATION regress_admin;
+DROP ROLE regress_plpgsql_admin; -- fail, privileges remain
+REVOKE SET VALUE, ALTER SYSTEM ON
+ plpgsql.extra_warnings, plpgsql.extra_errors
+FROM regress_plpgsql_admin;
+DROP ROLE regress_plpgsql_admin; -- ok
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index c8c545b64c..ddbf6afa44 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -66,6 +66,33 @@ DROP USER regress_priv_user10;
DROP USER regress_priv_user9;
DROP USER regress_priv_user8;
+GRANT SET VALUE ON enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON enable_nestloop TO regress_priv_user6;
+
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+
+GRANT ALTER SYSTEM ON enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON "" TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON " " TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON " foo " TO regress_priv_user7; -- bad name
+
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
+
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
@@ -1426,10 +1453,23 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+DROP USER regress_priv_user7; -- privileges remain
DROP USER regress_priv_user8; -- does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+DROP USER regress_priv_user7; -- ok
+
+REVOKE SET VALUE ON enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON enable_nestloop FROM regress_priv_user6;
+
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
On Mar 6, 2022, at 2:13 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
1. If we need to change these two contrib modules, doesn't that imply
a lot of changes forced on external modules as well? What are the
security implications if somebody doesn't make such a change?2. It looks to me like if someone installs the updated postgres_fdw.so,
but doesn't run ALTER EXTENSION UPDATE, they are going to be left with a
rather broken extension. This seems not good either, especially if it's
multiplied by a boatload of third-party extensions requiring updates.So I think some more thought is needed to see if we can't avoid
the need to touch extension modules in this patch. Or at least
avoid the need for synchronized C-level and SQL-level updates,
because that is going to create a lot of pain for end users.I'm also fairly distressed by the number of changes in guc.c,
mainly because I fear that that means that pending patches that
add GUC variables will be subtly incorrect/insecure if they're not
updated to account for this. Frankly, I also reject the apparent
position that we need to support forbidding users from touching
many of these GUCs. Or, if that's our position, why are there
per-GUC changes at all, rather than just redefining what the
context values mean? (That is, why not redefine USERSET and
SUSET as simply indicating the default ACL to be applied if there's
no entry in the catalog.)
To my knowledge, there is no mechanism to revoke an implicit privilege. You can revoke a privilege explicitly listed in an aclitem[], but only if the privilege is being tracked that way.
Userset variables are implicitly settable by any user. There was a request, off-list as I recall, to make it possible to revoke the privilege to set variables such as "work_mem". To make that possible, but not change the default behavior vis-a-vis prior releases, I upgraded most userset variables to suset with a corresponding grant to public on the variable. Sites which wish to have a more restrictive policy on such variables can revoke that privilege from public and instead issue more restrictive grants. There were a few variables where such treatment didn't seem sensible, such as ones to do with client connections, and I left them alone. I didn't insist on a defense for why any particular setting needed to be revocable in order to apply this treatment. My assumption was that sites should be allowed to determine their own security policies per setting unless there is a technical difficulty for a given setting that would make it overly burdensome to implement.
It seemed more complete to do the same thing for contrib modules, so I did.
If a third-party module doesn't do the equivalent operation, they would simply continue with their userset variables working as before. I don't see a specific security concern with that, though I'd be happy to consider arguments to the contrary.
I believe there are also some issues with this patch relating to pg_dump/pg_restore and to pg_upgrade, which EDB is working to address. We intend to repost something soon.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Mark Dilger <mark.dilger@enterprisedb.com> writes:
On Mar 6, 2022, at 2:13 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
... Or, if that's our position, why are there
per-GUC changes at all, rather than just redefining what the
context values mean? (That is, why not redefine USERSET and
SUSET as simply indicating the default ACL to be applied if there's
no entry in the catalog.)
To my knowledge, there is no mechanism to revoke an implicit privilege. You can revoke a privilege explicitly listed in an aclitem[], but only if the privilege is being tracked that way.
So? What I'm suggesting is along the lines of
(1) pg_setting_acl starts out empty, or at least mostly empty (maybe
there are a few GUCs that need custom values).
(2) If there's a pg_setting_acl entry for a GUC that's to be set,
we apply it: either it grants the desired permission or it doesn't.
(3) If there's no entry, then for a USERSET GUC we assume that the
entry would be like "=s/postgres", while for any other context value
we assume the ACL grants nothing.
I don't think this is materially different from what we do with
permissions on (say) functions. If you want to revoke the public
SET privilege on some USERSET variable, you instantiate the default
and then revoke. You end up with an empty ACL stored in pg_setting_acl,
and voila.
It'd likely be necessary to refuse to record a grant/revoke on
an unknown GUC, since if we don't know the GUC then we can't know
what the relevant default ACL ought to be. But I bet your existing
patch has some dubious behavior in that case too.
regards, tom lane
On Mar 6, 2022, at 2:57 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I don't think this is materially different from what we do with
permissions on (say) functions. If you want to revoke the public
SET privilege on some USERSET variable, you instantiate the default
and then revoke. You end up with an empty ACL stored in pg_setting_acl,
and voila.
I assume you mean the implementation of REVOKE does this, not that the user needs to do both a grant and a revoke.
It'd likely be necessary to refuse to record a grant/revoke on
an unknown GUC, since if we don't know the GUC then we can't know
what the relevant default ACL ought to be. But I bet your existing
patch has some dubious behavior in that case too.
The existing patch allows grants on unknown gucs, because it can't know what guc an upgrade script will introduce, and the grant statement may need to execute before the guc exists. That opens a window for granting privileges on non-existent gucs. That sounds bad, but I don't know of any actual harm that it does, beyond just being ugly.
With your proposal, it sounds like we could avoid that ugliness, so I'm inclined to at least try doing it as you propose.
Thanks for the suggestion!
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Mark Dilger <mark.dilger@enterprisedb.com> writes:
On Mar 6, 2022, at 2:57 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I don't think this is materially different from what we do with
permissions on (say) functions. If you want to revoke the public
SET privilege on some USERSET variable, you instantiate the default
and then revoke. You end up with an empty ACL stored in pg_setting_acl,
and voila.
I assume you mean the implementation of REVOKE does this, not that the user needs to do both a grant and a revoke.
Right. Again, look at what happens when you create a function and
then revoke its default PUBLIC EXECUTE permission.
It'd likely be necessary to refuse to record a grant/revoke on
an unknown GUC, since if we don't know the GUC then we can't know
what the relevant default ACL ought to be. But I bet your existing
patch has some dubious behavior in that case too.
The existing patch allows grants on unknown gucs, because it can't know what guc an upgrade script will introduce, and the grant statement may need to execute before the guc exists.
Yeah, that's the problematic case. It might mostly work to assume that
an unknown GUC has an empty default ACL. This could fail to retain the
default PUBLIC SET permission if it later turns out the GUC is USERSET
... but I suspect in most cases anybody who's messing with the permissions
would've started out by revoking that anyway. We could make this
definitely work in pg_dump if we teach pg_dump to explicitly grant or
revoke the PUBLIC SET permission anytime it's emitting anything for
a GUC, even if it thinks that would be the default state anyway.
Extension scripts that want to modify permissions for their GUCs
could follow that same principle to be sure.
regards, tom lane
On Mar 6, 2022, at 3:27 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Mark Dilger <mark.dilger@enterprisedb.com> writes:
The existing patch allows grants on unknown gucs, because it can't know what guc an upgrade script will introduce, and the grant statement may need to execute before the guc exists.
Yeah, that's the problematic case. It might mostly work to assume that
an unknown GUC has an empty default ACL. This could fail to retain the
default PUBLIC SET permission if it later turns out the GUC is USERSET
On further reflection, I concluded this isn't needed. No current extension, whether in-core or third party, expects to be able to create a new GUC and then grant or revoke permissions on it. They can already specify the guc context (PGC_USERS, etc). Introducing a feature that depends on the dubious assumption that unrecognized GUCs will turn out to be USERSET doesn't seem warranted.
The patch attributes all grants of setting privileges to the bootstrap superuser. Only superusers can grant or revoke privileges on settings, and all settings are implicitly owned by the bootstrap superuser because there is no explicit owner associated with settings. Consequently, select_best_grantor(some_superuser, ..., BOOTSTRAP_SUPERUSERID, ...) always chooses the bootstrap superuser. I don't see a problem with this, but wouldn't mind a second opinion. Some people might find it surprising when viewing the pg_setting_acl.setacl field.
Attachments:
v10-0001-Allow-grant-and-revoke-of-privileges-on-settings.patchapplication/octet-stream; name=v10-0001-Allow-grant-and-revoke-of-privileges-on-settings.patch; x-unix-mode=0644Download
From e5d01d17ca0a38d8f06b3989323fb5105b64230d Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Tue, 8 Mar 2022 14:12:29 -0800
Subject: [PATCH v10] Allow grant and revoke of privileges on settings
Allow grant and revoke of privileges to set or alter system set
configuration variables. Each (role,variable,privilege) triple can
be independently granted or revoked, so a user may be granted
privilege to SET but not to ALTER SYSTEM SET on a variable, or vice
versa. Privilege to SET a userset variable is implicitly granted to
public, but may be revoked.
---
doc/src/sgml/catalogs.sgml | 75 +++-
doc/src/sgml/ddl.sgml | 42 ++-
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 | 5 +-
src/backend/catalog/Makefile | 3 +-
src/backend/catalog/aclchk.c | 262 ++++++++++++++
src/backend/catalog/catalog.c | 6 +
src/backend/catalog/dependency.c | 6 +
src/backend/catalog/objectaddress.c | 46 +++
src/backend/catalog/pg_setting_acl.c | 122 +++++++
src/backend/catalog/system_views.sql | 13 +
src/backend/commands/alter.c | 1 +
src/backend/commands/dropcmds.c | 7 +
src/backend/commands/event_trigger.c | 6 +
src/backend/commands/seclabel.c | 1 +
src/backend/commands/tablecmds.c | 1 +
src/backend/parser/gram.y | 87 ++++-
src/backend/utils/adt/acl.c | 291 ++++++++++++++++
src/backend/utils/cache/lsyscache.c | 20 ++
src/backend/utils/cache/syscache.c | 23 ++
src/backend/utils/misc/guc.c | 204 ++++++++++-
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 | 185 +++++++++-
src/include/catalog/dependency.h | 1 +
src/include/catalog/pg_default_acl.h | 1 +
src/include/catalog/pg_proc.dat | 23 ++
src/include/catalog/pg_setting_acl.h | 63 ++++
src/include/nodes/parsenodes.h | 5 +-
src/include/parser/kwlist.h | 1 +
src/include/utils/acl.h | 11 +-
src/include/utils/guc.h | 3 +
src/include/utils/lsyscache.h | 1 +
src/include/utils/syscache.h | 2 +
src/test/modules/test_pg_dump/t/001_base.pl | 61 ++++
src/test/regress/expected/guc_privs.out | 367 ++++++++++++++++++++
src/test/regress/expected/privileges.out | 56 ++-
src/test/regress/expected/rules.out | 13 +
src/test/regress/parallel_schedule | 2 +-
src/test/regress/sql/guc_privs.sql | 165 +++++++++
src/test/regress/sql/privileges.sql | 44 ++-
45 files changed, 2284 insertions(+), 50 deletions(-)
create mode 100644 src/backend/catalog/pg_setting_acl.c
create mode 100644 src/include/catalog/pg_setting_acl.h
create mode 100644 src/test/regress/expected/guc_privs.out
create mode 100644 src/test/regress/sql/guc_privs.sql
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 83987a9904..7b288cce30 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -105,6 +105,11 @@
<entry>collations (locale information)</entry>
</row>
+ <row>
+ <entry><link linkend="catalog-pg-setting-acl"><structname>pg_setting_acl</structname></link></entry>
+ <entry>configuration parameters which have privileges granted to roles</entry>
+ </row>
+
<row>
<entry><link linkend="catalog-pg-constraint"><structname>pg_constraint</structname></link></entry>
<entry>check constraints, unique constraints, primary key constraints, foreign key constraints</entry>
@@ -2423,6 +2428,64 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</para>
</sect1>
+ <sect1 id="catalog-pg-setting-acl">
+ <title><structname>pg_setting_acl</structname></title>
+
+ <indexterm zone="catalog-pg-setting-acl">
+ <primary>pg_setting_acl</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_setting_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_setting_acl</structname>
+ is shared across all databases of a cluster: there is only one
+ copy of <structname>pg_setting_acl</structname> per cluster, not
+ one per database.
+ </para>
+
+ <table>
+ <title><structname>pg_setting_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>setting</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>
@@ -12540,11 +12603,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 settings 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 VALUE</literal> privilege granted
+ on the setting 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..49a1f3796e 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 VALUE</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,29 @@ REVOKE ALL ON accounts FROM PUBLIC;
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>SET VALUE</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>SET</command> command to
+ change run-time configuration parameters. By default,
+ <literal>superuser</literal> run-time configuration parameters may only
+ be set by superusers, and <literal>user</literal> run-time configuration
+ parameters can be set by any user.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ALTER SYSTEM</literal></term>
+ <listitem>
+ <para>
+ Allows non-superuser roles to use the <command>ALTER SYSTEM
+ SET</command> command to change server configuration parameters.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
The privileges required by other commands are listed on the
@@ -2097,6 +2121,16 @@ REVOKE ALL ON accounts FROM PUBLIC;
<literal>TYPE</literal>
</entry>
</row>
+ <row>
+ <entry><literal>SET VALUE</literal></entry>
+ <entry><literal>s</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
+ <row>
+ <entry><literal>ALTER SYSTEM</literal></entry>
+ <entry><literal>A</literal></entry>
+ <entry>configuration parameter</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -2203,6 +2237,12 @@ REVOKE ALL ON accounts FROM PUBLIC;
<entry><literal>U</literal></entry>
<entry><literal>\dT+</literal></entry>
</row>
+ <row>
+ <entry><literal>Configuration parameter</literal></entry>
+ <entry><literal>sA</literal></entry>
+ <entry>none</entry>
+ <entry><literal></literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 8a802fb225..36b33d8639 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');
@@ -22957,6 +22956,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_setting_privilege</primary>
+ </indexterm>
+ <function>has_setting_privilege</function> (
+ <optional> <parameter>user</parameter> <type>name</type> or <type>oid</type>, </optional>
+ <parameter>setting</parameter> <type>text</type> or <type>oid</type>,
+ <parameter>privilege</parameter> <type>text</type> )
+ <returnvalue>boolean</returnvalue>
+ </para>
+ <para>
+ Does user have privilege for setting?
+ Allowable privilege types are <literal>SET VALUE</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 a897712de2..39c2ff1c81 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 VALUE | ALTER SYSTEM } [, ... ] | ALL [ PRIVILEGES ] }
+ ON SETTING <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 VALUE</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..741ded8d1a 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 VALUE | ALTER SYSTEM } [, ...] | ALL [ PRIVILEGES ] }
+ ON SETTING <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..a08057d1d1 100644
--- a/doc/src/sgml/ref/set.sgml
+++ b/doc/src/sgml/ref/set.sgml
@@ -34,8 +34,9 @@ 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.)
+ (But some require either superuser privileges or granted <literal>SET
+ VALUE</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>
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index eefebb7bb8..4b28216f89 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -28,6 +28,7 @@ OBJS = \
pg_cast.o \
pg_class.o \
pg_collation.o \
+ pg_setting_acl.o \
pg_constraint.o \
pg_conversion.o \
pg_db_role_setting.o \
@@ -54,7 +55,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_setting_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..83f066118a 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_setting_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_Setting(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_SETTING:
+ whole_mask = ACL_ALL_RIGHTS_SETTING;
+ 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_SETTING:
+ all_privileges = ACL_ALL_RIGHTS_SETTING;
+ errormsg = gettext_noop("invalid privilege type %s for setting");
+ 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_SETTING:
+ ExecGrant_Setting(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -759,6 +771,38 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_SETTING:
+ foreach(cell, objnames)
+ {
+ char *setting = strVal(lfirst(cell));
+ Oid settingid = get_setting_oid(setting, true);
+
+ if (!OidIsValid(settingid))
+ {
+ /*
+ * Lookup the existing entry, or if necessary, add a new
+ * entry for this parameter. Entries only exist for
+ * parameters which currently have, or previously have had,
+ * privileges assigned.
+ *
+ * It is tempting to sanity-check the given configuration
+ * parameter name against known guc names in the guc
+ * tables, but for handling upgrades we need to accept
+ * setting names that do not yet exist.
+ */
+ settingid = SettingAclCreate(setting, 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, settingid);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -1494,6 +1538,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case ForeignDataWrapperRelationId:
istmt.objtype = OBJECT_FDW;
break;
+ case SettingAclRelationId:
+ istmt.objtype = OBJECT_SETTING;
+ break;
default:
elog(ERROR, "unexpected object class %u", classid);
break;
@@ -3225,6 +3272,139 @@ ExecGrant_Type(InternalGrant *istmt)
table_close(relation, RowExclusiveLock);
}
+static void
+ExecGrant_Setting(InternalGrant *istmt)
+{
+ Relation relation;
+ ListCell *cell;
+
+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
+ istmt->privileges = ACL_ALL_RIGHTS_SETTING;
+
+ relation = table_open(SettingAclRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid settingId = lfirst_oid(cell);
+ Form_pg_setting_acl pg_setting_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_setting_acl];
+ bool nulls[Natts_pg_setting_acl];
+ bool replaces[Natts_pg_setting_acl];
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(settingId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for setting %u", settingId);
+
+ pg_setting_acl_tuple = (Form_pg_setting_acl) GETSTRUCT(tuple);
+
+ /*
+ * Get owner ID and working copy of existing ACL. If there's no ACL,
+ * substitute the proper default.
+ */
+ aclDatum = SysCacheGetAttr(SETTINGNAME, tuple, Anum_pg_setting_acl_setacl,
+ &isNull);
+
+ /*
+ * If the acl is null, we need to create a more permissive default acl
+ * for userset variables than for any others.
+ */
+ if (isNull)
+ {
+ const char *setting;
+
+ setting = text_to_cstring(&pg_setting_acl_tuple->setting);
+ old_acl = aclsettingdefault(find_option_context(setting) == 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,
+ settingId, grantorId, OBJECT_SETTING,
+ text_to_cstring(&pg_setting_acl_tuple->setting),
+ 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_setting_acl_setacl - 1] = true;
+ values[Anum_pg_setting_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(settingId, SettingAclRelationId, 0, new_acl);
+
+ /* Update the shared dependency ACL info */
+ updateAclDependencies(SettingAclRelationId,
+ pg_setting_acl_tuple->oid, 0,
+ InvalidOid,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+
+ pfree(new_acl);
+
+ /* Post alter hook called for grant and revoke */
+ InvokeObjectPostAlterHook(SettingAclRelationId, settingId, 0);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ table_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3255,6 +3435,10 @@ string_to_privilege(const char *privname)
return ACL_CREATE_TEMP;
if (strcmp(privname, "connect") == 0)
return ACL_CONNECT;
+ if (strcmp(privname, "set value") == 0)
+ return ACL_SET_VALUE;
+ 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 +3476,10 @@ privilege_to_string(AclMode privilege)
return "TEMP";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET_VALUE:
+ return "SET VALUE";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized privilege: %d", (int) privilege);
}
@@ -3328,6 +3516,13 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_COLUMN:
msg = gettext_noop("permission denied for column %s");
break;
+ case OBJECT_SETTING:
+ /*
+ * 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 +3759,7 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_AMPROC:
case OBJECT_ATTRIBUTE:
case OBJECT_CAST:
+ case OBJECT_SETTING:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
case OBJECT_DOMCONSTRAINT:
@@ -4000,6 +4196,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_setting_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 setting's ACL from pg_setting_acl
+ */
+ tuple = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(config_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("setting with OID %u does not exist",
+ config_oid)));
+
+ aclDatum = SysCacheGetAttr(SETTINGOID, tuple, Anum_pg_setting_acl_setacl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_SETTING, 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 +4962,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_setting_acl_aclcheck(Oid config_oid, Oid roleid, AclMode mode)
+{
+ if (pg_setting_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..78868e7052 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_setting_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 == SettingAclRelationId ||
relationId == DatabaseRelationId ||
relationId == SharedDescriptionRelationId ||
relationId == SharedDependRelationId ||
@@ -260,6 +262,8 @@ IsSharedRelation(Oid relationId)
relationId == AuthIdOidIndexId ||
relationId == AuthMemRoleMemIndexId ||
relationId == AuthMemMemRoleIndexId ||
+ relationId == SettingAclSettingIndexId ||
+ relationId == SettingAclOidIndexId ||
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 == PgSettingAclToastTable ||
+ relationId == PgSettingAclToastIndex ||
relationId == PgDatabaseToastTable ||
relationId == PgDatabaseToastIndex ||
relationId == PgDbRoleSettingToastTable ||
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index ab9e42d7d1..5d2504463d 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_setting_acl.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
@@ -150,6 +151,7 @@ static const Oid object_classes[] = {
TypeRelationId, /* OCLASS_TYPE */
CastRelationId, /* OCLASS_CAST */
CollationRelationId, /* OCLASS_COLLATION */
+ SettingAclRelationId, /* OCLASS_SETTING */
ConstraintRelationId, /* OCLASS_CONSTRAINT */
ConversionRelationId, /* OCLASS_CONVERSION */
AttrDefaultRelationId, /* OCLASS_DEFAULT */
@@ -1504,6 +1506,7 @@ doDeletion(const ObjectAddress *object, int flags)
/*
* These global object types are not supported here.
*/
+ case OCLASS_SETTING:
case OCLASS_ROLE:
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
@@ -2778,6 +2781,9 @@ getObjectClass(const ObjectAddress *object)
case CollationRelationId:
return OCLASS_COLLATION;
+ case SettingAclRelationId:
+ return OCLASS_SETTING;
+
case ConstraintRelationId:
return OCLASS_CONSTRAINT;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index f30c742d48..1ef833a0e3 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_setting_acl.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
@@ -2309,6 +2310,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_COLUMN:
case OBJECT_ATTRIBUTE:
case OBJECT_COLLATION:
+ case OBJECT_SETTING:
case OBJECT_CONVERSION:
case OBJECT_STATISTIC_EXT:
case OBJECT_TSPARSER:
@@ -3510,6 +3512,22 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
break;
}
+ case OCLASS_SETTING:
+ {
+ char *setting;
+
+ setting = get_setting_name(object->objectId);
+ if (!setting)
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for setting %u",
+ object->objectId);
+ break;
+ }
+ appendStringInfo(&buffer, _("setting %s"), setting);
+ break;
+ }
+
case OCLASS_SCHEMA:
{
char *nspname;
@@ -4473,6 +4491,10 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "collation");
break;
+ case OCLASS_SETTING:
+ appendStringInfoString(&buffer, "setting");
+ break;
+
case OCLASS_CONSTRAINT:
getConstraintTypeDescription(&buffer, object->objectId,
missing_ok);
@@ -4977,6 +4999,30 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_SETTING:
+ {
+ HeapTuple configTup;
+ Form_pg_setting_acl configForm;
+ char *namestr;
+
+ configTup = SearchSysCache1(SETTINGOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(configTup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for setting %u",
+ object->objectId);
+ break;
+ }
+ configForm = (Form_pg_setting_acl) GETSTRUCT(configTup);
+ namestr = text_to_cstring(&configForm->setting);
+ appendStringInfoString(&buffer, namestr);
+ if (objname)
+ *objname = list_make1(namestr);
+ ReleaseSysCache(configTup);
+ break;
+ }
+
case OCLASS_CONVERSION:
{
HeapTuple conTup;
diff --git a/src/backend/catalog/pg_setting_acl.c b/src/backend/catalog/pg_setting_acl.c
new file mode 100644
index 0000000000..3c6594da03
--- /dev/null
+++ b/src/backend/catalog/pg_setting_acl.c
@@ -0,0 +1,122 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_setting_acl.c
+ * routines to support manipulation of the pg_setting_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_setting_acl.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/sysattr.h"
+#include "access/table.h"
+#include "access/tableam.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_setting_acl.h"
+#include "mb/pg_wchar.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/guc.h"
+#include "utils/pg_locale.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * SettingAclCreate
+ *
+ * Add a new tuple to pg_setting_acl.
+ *
+ * setting: the setting name to create.
+ * if_not_exists: if true, don't fail on duplicate name, but rather return
+ * the existing entry's Oid.
+ */
+Oid
+SettingAclCreate(const char *setting, bool if_not_exists)
+{
+ Relation rel;
+ TupleDesc tupDesc;
+ HeapTuple tuple;
+ Datum values[Natts_pg_setting_acl];
+ bool nulls[Natts_pg_setting_acl];
+ Oid settingId;
+ const char *canonical;
+
+ /*
+ * Check whether the setting (by the given name or alias)
+ * already exists.
+ */
+ settingId = get_setting_oid(setting, true);
+ if (OidIsValid(settingId))
+ {
+ if (if_not_exists)
+ return settingId;
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("setting \"%s\" already exists",
+ setting)));
+ }
+
+ /*
+ * We must not require the setting to be in the list of existent GUCs,
+ * as we may be called at different points during upgrades or the
+ * installation or removal of extensions. Instead, perform a basic sanity
+ * check of the setting, 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_setting_acl entries.
+ */
+ if (!valid_variable_name(setting, NULL))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid setting name \"%s\"",
+ setting)));
+ canonical = GetConfigOptionCanonicalName(setting);
+ if (!canonical)
+ canonical = setting;
+
+ /*
+ * 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(SettingAclRelationId, RowExclusiveLock);
+ tupDesc = RelationGetDescr(rel);
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ values[Anum_pg_setting_acl_setting - 1] =
+ DirectFunctionCall1(textin, CStringGetDatum(canonical));
+ settingId = GetNewOidWithIndex(rel,
+ SettingAclOidIndexId,
+ Anum_pg_setting_acl_oid);
+ values[Anum_pg_setting_acl_oid - 1] = ObjectIdGetDatum(settingId);
+ nulls[Anum_pg_setting_acl_setacl - 1] = true;
+ tuple = heap_form_tuple(tupDesc, values, nulls);
+ CatalogTupleInsert(rel, tuple);
+
+ /* Post creation hook for new setting */
+ InvokeObjectPostCreateHook(SettingAclRelationId, settingId, 0);
+
+ /*
+ * Close pg_setting_acl, but keep lock till commit.
+ */
+ heap_freetuple(tuple);
+ table_close(rel, NoLock);
+
+ return settingId;
+}
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 40b7bca5a9..92b226b8d2 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -595,6 +595,19 @@ CREATE RULE pg_settings_n AS
GRANT SELECT, UPDATE ON pg_settings TO PUBLIC;
+CREATE VIEW pg_setting_privileges AS
+ SELECT grantor.rolname AS grantor,
+ grantee.rolname AS grantee,
+ set_acl.setting AS setting,
+ acl.privilege_type AS privilege_type,
+ acl.is_grantable
+ FROM pg_catalog.pg_setting_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_setting_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..3dd0c5cadb 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -639,6 +639,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
break;
case OCLASS_CAST:
+ case OCLASS_SETTING:
case OCLASS_CONSTRAINT:
case OCLASS_DEFAULT:
case OCLASS_LANGUAGE:
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index c9b5732448..00398727ed 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -276,6 +276,13 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
name = NameListToString(castNode(List, object));
}
break;
+ case OBJECT_SETTING:
+ if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
+ {
+ msg = gettext_noop("setting \"%s\" does not exist, skipping");
+ name = NameListToString(castNode(List, object));
+ }
+ break;
case OBJECT_CONVERSION:
if (!schema_does_not_exist_skipping(castNode(List, object), &msg, &name))
{
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 3c3fc2515b..f500e8c40d 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_SETTING:
case OBJECT_DATABASE:
case OBJECT_TABLESPACE:
case OBJECT_ROLE:
@@ -1012,6 +1013,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
{
switch (objclass)
{
+ case OCLASS_SETTING:
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_SETTING:
+ return "SETTING";
case OBJECT_TABLE:
return "TABLE";
case OBJECT_SEQUENCE:
@@ -2105,6 +2109,8 @@ stringify_adefprivs_objtype(ObjectType objtype)
{
case OBJECT_COLUMN:
return "COLUMNS";
+ case OBJECT_SETTING:
+ return "SETTINGS";
case OBJECT_TABLE:
return "TABLES";
case OBJECT_SEQUENCE:
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 7a62d547e2..7b05eb0110 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_SETTING:
case OBJECT_CONVERSION:
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index dc5872f988..11671390a5 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -12620,6 +12620,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
case OCLASS_TYPE:
case OCLASS_CAST:
case OCLASS_COLLATION:
+ case OCLASS_SETTING:
case OCLASS_CONVERSION:
case OCLASS_LANGUAGE:
case OCLASS_LARGEOBJECT:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a03b33b53b..172d9e31e3 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -363,8 +363,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 setting_name
+%type <list> OptSchemaEltList setting_target
%type <chr> am_type
@@ -728,7 +728,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
ROUTINE ROUTINES ROW ROWS RULE
SAVEPOINT SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
- SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW
+ SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SETTING SHARE SHOW
SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P
START STATEMENT STATISTICS STDIN STDOUT STORAGE STORED STRICT_P STRIP_P
SUBSCRIPTION SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P
@@ -6981,6 +6981,20 @@ GrantStmt: GRANT privileges ON privilege_target TO grantee_list
n->grantor = $8;
$$ = (Node*)n;
}
+ | GRANT privileges ON SETTING setting_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_SETTING;
+ n->objects = $5;
+ n->grantees = $7;
+ n->grant_option = $8;
+ n->grantor = $9;
+ $$ = (Node*)n;
+ }
;
RevokeStmt:
@@ -7014,6 +7028,36 @@ RevokeStmt:
n->behavior = $11;
$$ = (Node *)n;
}
+ | REVOKE privileges ON SETTING setting_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_SETTING;
+ n->objects = $5;
+ n->grantees = $7;
+ n->grantor = $8;
+ n->behavior = $9;
+ $$ = (Node *)n;
+ }
+ | REVOKE GRANT OPTION FOR privileges ON SETTING setting_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_SETTING;
+ n->objects = $8;
+ n->grantees = $10;
+ n->grantor = $11;
+ n->behavior = $12;
+ $$ = (Node *)n;
+ }
;
@@ -7073,6 +7117,20 @@ privilege: SELECT opt_column_list
n->cols = $2;
$$ = n;
}
+ | SET VALUE_P
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("set value");
+ n->cols = NULL;
+ $$ = 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);
@@ -7082,6 +7140,27 @@ privilege: SELECT opt_column_list
}
;
+setting_target:
+ setting_name
+ {
+ $$ = list_make1(makeString($1));
+ }
+ | setting_target ',' setting_name
+ {
+ $$ = lappend($1, makeString($3));
+ }
+ ;
+
+setting_name:
+ ColId
+ {
+ $$ = $1;
+ }
+ | setting_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.
@@ -15912,6 +15991,7 @@ unreserved_keyword:
| SESSION
| SET
| SETS
+ | SETTING
| SHARE
| SHOW
| SIMPLE
@@ -16499,6 +16579,7 @@ bare_label_keyword:
| SET
| SETOF
| SETS
+ | SETTING
| SHARE
| SHOW
| SIMILAR
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 0a16f8156c..fa15f3155c 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_setting_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_setting_priv_string(text *priv_setting_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_VALUE_CHR:
+ read = ACL_SET_VALUE;
+ 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_SETTING:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_SETTING;
+ 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;
}
+/*
+ * aclsettingdefault()
+ *
+ * Creates and returns an ACL describing the default access permissions for a
+ * setting, taking into account whether it is a PGC_USERSET GUC setting.
+ */
+Acl *
+aclsettingdefault(bool is_userset)
+{
+ Acl *acl;
+
+ /*
+ * Treat all settings 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_SETTING, 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_VALUE;
+
+ 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,17 @@ acldefault_sql(PG_FUNCTION_ARGS)
PG_RETURN_ACL_P(acldefault(objtype, owner));
}
+/*
+ * SQL-accessible version of aclsettingdefault().
+ */
+Datum
+aclsettingdefault_sql(PG_FUNCTION_ARGS)
+{
+ const char *setting = text_to_cstring(PG_GETARG_TEXT_P(0));
+
+ PG_RETURN_ACL_P(aclsettingdefault(find_option_context(setting) ==
+ PGC_USERSET));
+}
/*
* Update an ACL array to add or remove specified privileges.
@@ -1602,6 +1661,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 VALUE") == 0)
+ return ACL_SET_VALUE;
+ 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 +1761,10 @@ convert_aclright_to_string(int aclright)
return "TEMPORARY";
case ACL_CONNECT:
return "CONNECT";
+ case ACL_SET_VALUE:
+ return "SET VALUE";
+ case ACL_ALTER_SYSTEM:
+ return "ALTER SYSTEM";
default:
elog(ERROR, "unrecognized aclright: %d", aclright);
return NULL;
@@ -4429,6 +4496,193 @@ convert_type_priv_string(text *priv_type_text)
return convert_any_priv_string(priv_type_text, type_priv_map);
}
+/*
+ * has_setting_privilege variants
+ * These are all named "has_setting_privilege" at the SQL level.
+ * They take various combinations of setting name, setting 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_setting_privilege_name_name
+ * Check user privileges on a setting given
+ * name username, text setting, and text priv name.
+ */
+Datum
+has_setting_privilege_name_name(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ text *setting = PG_GETARG_TEXT_PP(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid roleid;
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_role_oid_or_public(NameStr(*username));
+
+ settingoid = get_setting_oid(text_to_cstring(setting), true);
+ if (!OidIsValid(settingoid))
+ PG_RETURN_NULL();
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_name
+ * Check user privileges on a setting given
+ * text setting and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_setting_privilege_name(PG_FUNCTION_ARGS)
+{
+ text *setting = PG_GETARG_TEXT_PP(0);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(1);
+ Oid roleid;
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ settingoid = get_setting_oid(text_to_cstring(setting), true);
+ if (!OidIsValid(settingoid))
+ PG_RETURN_NULL();
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_name_id
+ * Check user privileges on a setting given
+ * name usename, setting oid, and text priv name.
+ */
+Datum
+has_setting_privilege_name_id(PG_FUNCTION_ARGS)
+{
+ Name username = PG_GETARG_NAME(0);
+ Oid settingoid = PG_GETARG_OID(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = get_role_oid_or_public(NameStr(*username));
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id
+ * Check user privileges on a setting given
+ * setting oid, and text priv name.
+ * current_user is assumed
+ */
+Datum
+has_setting_privilege_id(PG_FUNCTION_ARGS)
+{
+ Oid settingoid = PG_GETARG_OID(0);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(1);
+ Oid roleid;
+ AclMode mode;
+ AclResult aclresult;
+
+ roleid = GetUserId();
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id_name
+ * Check user privileges on a setting given
+ * roleid, text setting, and text priv name.
+ */
+Datum
+has_setting_privilege_id_name(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ text *setting = PG_GETARG_TEXT_PP(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ Oid settingoid;
+ AclMode mode;
+ AclResult aclresult;
+
+ settingoid = get_setting_oid(text_to_cstring(setting), true);
+ if (!OidIsValid(settingoid))
+ PG_RETURN_NULL();
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * has_setting_privilege_id_id
+ * Check user privileges on a setting given
+ * roleid, setting oid, and text priv name.
+ */
+Datum
+has_setting_privilege_id_id(PG_FUNCTION_ARGS)
+{
+ Oid roleid = PG_GETARG_OID(0);
+ Oid settingoid = PG_GETARG_OID(1);
+ text *priv_setting_text = PG_GETARG_TEXT_PP(2);
+ AclMode mode;
+ AclResult aclresult;
+
+ mode = convert_setting_priv_string(priv_setting_text);
+
+ if (!SearchSysCacheExists1(SETTINGOID, ObjectIdGetDatum(settingoid)))
+ PG_RETURN_NULL();
+
+ aclresult = pg_setting_acl_aclcheck(settingoid, roleid, mode);
+
+ PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
+}
+
+/*
+ * Support routines for has_setting_privilege family.
+ */
+
+/*
+ * convert_setting_priv_string
+ * Convert text string to AclMode value.
+ */
+static AclMode
+convert_setting_priv_string(text *priv_setting_text)
+{
+ static const priv_map setting_priv_map[] = {
+ {"SET VALUE", ACL_SET_VALUE},
+ {"SET VALUE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SET_VALUE)},
+ {"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_setting_text, setting_priv_map);
+}
/*
* pg_has_role variants
@@ -4670,6 +4924,43 @@ initialize_acl(void)
}
}
+/*
+ * get_setting_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_setting_oid(const char *setting, bool missing_ok)
+{
+ Oid oid;
+
+ /* Check for the variable by the name we were given */
+ oid = GetSysCacheOid1(SETTINGNAME, Anum_pg_setting_acl_oid,
+ PointerGetDatum(cstring_to_text(setting)));
+ if (!OidIsValid(oid))
+ {
+ const char *canonical;
+
+ /* Check if the variable has a different canonical spelling */
+ canonical = GetConfigOptionCanonicalName(setting);
+ if (canonical != NULL)
+ oid = GetSysCacheOid1(SETTINGNAME, Anum_pg_setting_acl_oid,
+ PointerGetDatum(cstring_to_text(canonical)));
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("setting \"%s\" does not exist", setting)));
+ }
+
+ return oid;
+}
+
/*
* RoleMembershipCacheCallback
* Syscache inval callback function
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index feef999863..f4e806fe8e 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_setting_acl.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_transform.h"
#include "catalog/pg_type.h"
@@ -3304,6 +3305,25 @@ free_attstatsslot(AttStatsSlot *sslot)
pfree(sslot->numbers_arr);
}
+char *
+get_setting_name(Oid configid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(SETTINGOID, ObjectIdGetDatum(configid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_setting_acl configtup = (Form_pg_setting_acl) GETSTRUCT(tp);
+ char *result;
+
+ result = pstrdup(text_to_cstring(&configtup->setting));
+ 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 f4e7819f1e..cbcbf02839 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_setting_acl.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
#include "catalog/pg_shseclabel.h"
@@ -762,6 +763,28 @@ static const struct cachedesc cacheinfo[] = {
},
32
},
+ {SettingAclRelationId, /* SETTINGNAME */
+ SettingAclSettingIndexId,
+ 1,
+ {
+ Anum_pg_setting_acl_setting,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+ {SettingAclRelationId, /* SETTINGOID */
+ SettingAclOidIndexId,
+ 1,
+ {
+ Anum_pg_setting_acl_oid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{StatisticExtDataRelationId, /* STATEXTDATASTXOID */
StatisticExtDataStxoidInhIndexId,
2,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 6d11f9c71b..e64d0b2564 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -43,7 +43,9 @@
#include "access/xlog_internal.h"
#include "access/xlogrecovery.h"
#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_authid.h"
+#include "catalog/pg_setting_acl.h"
#include "catalog/storage.h"
#include "commands/async.h"
#include "commands/prepare.h"
@@ -5053,6 +5055,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_setting_acl
+ * entries.
*/
static const char *const map_old_guc_names[] = {
"sort_mem", "work_mem",
@@ -5452,25 +5458,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().)
+ *
+ * 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"
@@ -5487,8 +5497,25 @@ 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;
+}
+
+
+/*
+ * 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().)
+ */
+static bool
+valid_custom_variable_name(const char *name)
+{
+ int partcnt;
+
+ return (valid_variable_name(name, &partcnt) && partcnt > 1);
}
/*
@@ -7542,6 +7569,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 settingid = get_setting_oid(name, true);
+
+ if (OidIsValid(settingid))
+ {
+ AclResult aclresult;
+
+ aclresult = pg_setting_acl_aclcheck(settingid, GetUserId(),
+ ACL_SET_VALUE);
+
+ if (aclresult == ACLCHECK_OK)
+ break; /* okay */
+ }
+
+ /* No granted privilege */
ereport(elevel,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to set parameter \"%s\"",
@@ -7550,7 +7595,35 @@ set_config_option(const char *name, const char *value,
}
break;
case PGC_USERSET:
- /* always okay */
+ if (context == PGC_USERSET)
+ {
+ /*
+ * If this GUC setting has an entry in pg_setting_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 settingid = get_setting_oid(name, true);
+
+ if (OidIsValid(settingid))
+ {
+ AclResult aclresult;
+
+ aclresult = pg_setting_acl_aclcheck(settingid, GetUserId(),
+ ACL_SET_VALUE);
+
+ 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_setting_acl entry, okay */
+ }
break;
}
@@ -8140,6 +8213,23 @@ set_config_option(const char *name, const char *value,
}
+/*
+ * Get the context required to set the variable, or -1 if the given name cannot
+ * be found.
+ */
+GucContext
+find_option_context(const char *name)
+{
+ struct config_generic *conf;
+
+ conf = find_option(name, false, false, ERROR);
+ if (conf)
+ return conf->context;
+
+ return -1; /* Not reached */
+}
+
+
/*
* Set the fields for source file and line number the setting came from.
*/
@@ -8584,6 +8674,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
{
char *name;
char *value;
+ Oid settingId = InvalidOid;
bool resetall = false;
ConfigVariable *head = NULL;
ConfigVariable *tail = NULL;
@@ -8591,16 +8682,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;
+
+ settingId = get_setting_oid(name, true);
+ if (!OidIsValid(settingId))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+
+ aclresult = pg_setting_acl_aclcheck(settingId, GetUserId(),
+ ACL_ALTER_SYSTEM);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, OBJECT_SETTING, name);
+ }
+
switch (altersysstmt->setstmt->kind)
{
case VAR_SET_VALUE:
@@ -8733,6 +8839,21 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
replace_auto_config_value(&head, &tail, name, value);
}
+ /*
+ * Invoke the post-alter hook for setting this GUC variable. Guc variables
+ * do not always have corresponding entries in pg_setting_acl; the hooks
+ * must therefore be prepared to receive settingId = InvalidOid. We also
+ * abuse the notion of subId to pass the kind of alteration (set vs. alter
+ * system), and auxiliaryId to pass the VariableSetKind.
+ *
+ * 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.
+ */
+ InvokeObjectPostAlterHookArg(SettingAclRelationId, settingId,
+ ACL_ALTER_SYSTEM, altersysstmt->setstmt->kind,
+ false);
+
/*
* To ensure crash safety, first write the new file data to a temp file,
* then atomically rename it into place.
@@ -8793,6 +8914,9 @@ void
ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
{
GucAction action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET;
+ GucContext context;
+ AclResult aclresult;
+ Oid settingId;
/*
* Workers synchronize these parameters at the start of the parallel
@@ -8803,6 +8927,24 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
errmsg("cannot set parameters during a parallel operation")));
+ /* Get the Oid of this setting, or InvalidOid if none. */
+ settingId = get_setting_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(settingId))
+ {
+ aclresult = pg_setting_acl_aclcheck(settingId, GetUserId(),
+ ACL_SET_VALUE);
+ if (aclresult == ACLCHECK_OK)
+ context = PGC_SUSET;
+ }
+
switch (stmt->kind)
{
case VAR_SET_VALUE:
@@ -8811,7 +8953,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;
@@ -8896,7 +9038,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;
@@ -8904,6 +9046,16 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
ResetAllOptions();
break;
}
+
+ /*
+ * Invoke the post-alter hook for setting this GUC variable. Guc variables
+ * do not always have corresponding entries in pg_setting_acl; the hooks
+ * must therefore be prepared to receive settingId = InvalidOid. We also
+ * abuse the notion of subId to pass the kind of alteration (set vs. alter
+ * system), and auxiliaryId to pass the VariableSetKind.
+ */
+ InvokeObjectPostAlterHookArg(SettingAclRelationId, settingId,
+ ACL_SET_VALUE, stmt->kind, false);
}
/*
@@ -9664,6 +9816,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, false, 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,
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index 6086d57cf3..a84efd79e5 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, SETTING 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, "SETTING") == 0)
+ {
+ CONVERT_PRIV('s', "SET VALUE");
+ 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..bb0badca65 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, "SETTING") == 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, "SETTING") == 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 e69dcf8a48..dea1b89ff8 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -14166,6 +14166,9 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
case DEFACLOBJ_NAMESPACE:
type = "SCHEMAS";
break;
+ case DEFACLOBJ_SETTING:
+ type = "SETTINGS";
+ break;
default:
/* shouldn't get here */
fatal("unrecognized object type in default privileges: %d",
@@ -14209,7 +14212,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, SETTING 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..ecb6a45a5f 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 settings which have non-default acls defined.
+ */
+ res = executeQuery(conn, "SELECT setting, "
+ "pg_catalog.pg_get_userbyid(10) AS setowner, "
+ "setacl, aclsettingdefault(setting) AS acldefault "
+ "FROM pg_catalog.pg_setting_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 *setting = PQgetvalue(res, i, 0);
+ char *setowner = PQgetvalue(res, i, 1);
+ char *setacl = PQgetvalue(res, i, 2);
+ char *acldefault = PQgetvalue(res, i, 3);
+ char *fsetting;
+
+ /* needed for buildACLCommands() */
+ fsetting = pg_strdup(fmtId(setting));
+
+ if (!buildACLCommands(fsetting, NULL, NULL, "SETTING",
+ setacl, acldefault,
+ setowner, "", server_version, buf))
+ {
+ pg_log_error("could not parse ACL list (%s) for tablespace \"%s\"",
+ setacl, setting);
+ PQfinish(conn);
+ exit_nicely(1);
+ }
+
+ fprintf(OPF, "%s", buf->data);
+
+ free(fsetting);
+ 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 6957567264..c259dcf79f 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3672,7 +3672,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
@@ -3684,8 +3688,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",
@@ -3698,12 +3705,146 @@ 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", "SET") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET"))
+ COMPLETE_WITH("VALUE");
+ else if (TailMatches("GRANT|REVOKE", "ALTER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER"))
+ COMPLETE_WITH("SYSTEM");
+
+ else if (TailMatches("GRANT|REVOKE", "SET", "VALUE") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", "VALUE"))
+ COMPLETE_WITH(",",
+ "ON SETTING");
+ else if (TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM"))
+ COMPLETE_WITH(",",
+ "ON SETTING");
+
+ else if (TailMatches("GRANT|REVOKE", "SET", "VALUE,") ||
+ TailMatches("GRANT|REVOKE", "SET", "VALUE", ",") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", "VALUE,") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", "VALUE", ","))
+ COMPLETE_WITH("ALTER SYSTEM ON SETTING");
+ 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 VALUE ON SETTING");
+
+ else if (TailMatches("GRANT|REVOKE", "SET", "VALUE,", "ALTER") ||
+ TailMatches("GRANT|REVOKE", "SET", "VALUE", ",", "ALTER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", "VALUE,", "ALTER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", "VALUE", ",", "ALTER"))
+ COMPLETE_WITH("SYSTEM ON SETTING");
+ 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"))
+ COMPLETE_WITH("VALUE ON SETTING");
+
+ else if (TailMatches("GRANT|REVOKE", "SET", "VALUE,", "ALTER", "SYSTEM") ||
+ TailMatches("GRANT|REVOKE", "SET", "VALUE", ",", "ALTER", "SYSTEM") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM,", "SET", "VALUE") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM", ",", "SET", "VALUE"))
+ COMPLETE_WITH("ON SETTING");
+
+ else if (TailMatches("GRANT|REVOKE", "SET", "VALUE", "ON") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM", "ON") ||
+ TailMatches("GRANT|REVOKE", "SET", "VALUE,", "ALTER", "SYSTEM", "ON") ||
+ TailMatches("GRANT|REVOKE", "SET", "VALUE", ",", "ALTER", "SYSTEM", "ON") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM,", "SET", "VALUE", "ON") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM", ",", "SET", "VALUE", "ON") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", "VALUE", "ON") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM", "ON") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", "VALUE,", "ALTER", "SYSTEM", "ON") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", "VALUE", ",", "ALTER", "SYSTEM", "ON") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM,", "SET", "VALUE", "ON") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM", ",", "SET", "VALUE", "ON"))
+ COMPLETE_WITH("SETTING");
+
+ else if (TailMatches("GRANT|REVOKE", "SET", "VALUE", "ON", "SETTING") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM", "ON", "SETTING") ||
+ TailMatches("GRANT|REVOKE", "SET", "VALUE,", "ALTER", "SYSTEM", "ON", "SETTING") ||
+ TailMatches("GRANT|REVOKE", "SET", "VALUE", ",", "ALTER", "SYSTEM", "ON", "SETTING") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM,", "SET", "VALUE", "ON", "SETTING") ||
+ TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM", ",", "SET", "VALUE", "ON", "SETTING") ||
+ TailMatches("GRANT|REVOKE", "ALL", "ON", "SETTING") ||
+ TailMatches("GRANT|REVOKE", "ALL", "PRIVILEGES", "ON", "SETTING") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", "VALUE", "ON", "SETTING") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM", "ON", "SETTING") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", "VALUE,", "ALTER", "SYSTEM", "ON", "SETTING") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", "VALUE", ",", "ALTER", "SYSTEM", "ON", "SETTING") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM,", "SET", "VALUE", "ON", "SETTING") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM", ",", "SET", "VALUE", "ON", "SETTING") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALL", "ON", "SETTING") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALL", "PRIVILEGES", "ON", "SETTING"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_set_vars);
+
+ else if (TailMatches("GRANT", "SET", "VALUE", "ON", "SETTING", MatchAny) ||
+ TailMatches("GRANT", "ALTER", "SYSTEM", "ON", "SETTING", MatchAny) ||
+ TailMatches("GRANT", "SET", "VALUE,", "ALTER", "SYSTEM", "ON", "SETTING", MatchAny) ||
+ TailMatches("GRANT", "SET", "VALUE", ",", "ALTER", "SYSTEM", "ON", "SETTING", MatchAny) ||
+ TailMatches("GRANT", "ALTER", "SYSTEM,", "SET", "VALUE", "ON", "SETTING", MatchAny) ||
+ TailMatches("GRANT", "ALTER", "SYSTEM", ",", "SET", "VALUE", "ON", "SETTING", MatchAny) ||
+ TailMatches("GRANT", "ALL", "ON", "SETTING", MatchAny) ||
+ TailMatches("GRANT", "ALL", "PRIVILEGES", "ON", "SETTING", MatchAny))
+ COMPLETE_WITH("TO");
+
+ else if (TailMatches("REVOKE", "SET", "VALUE", "ON", "SETTING", MatchAny) ||
+ TailMatches("REVOKE", "ALTER", "SYSTEM", "ON", "SETTING", MatchAny) ||
+ TailMatches("REVOKE", "SET", "VALUE,", "ALTER", "SYSTEM", "ON", "SETTING", MatchAny) ||
+ TailMatches("REVOKE", "SET", "VALUE", ",", "ALTER", "SYSTEM", "ON", "SETTING", MatchAny) ||
+ TailMatches("REVOKE", "ALL", "ON", "SETTING", MatchAny) ||
+ TailMatches("REVOKE", "ALL", "PRIVILEGES", "ON", "SETTING", MatchAny) ||
+ TailMatches("REVOKE", "ALTER", "SYSTEM,", "SET", "VALUE", "ON", "SETTING", MatchAny) ||
+ TailMatches("REVOKE", "ALTER", "SYSTEM", ",", "SET", "VALUE", "ON", "SETTING", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", "VALUE", "ON", "SETTING", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM", "ON", "SETTING", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", "VALUE,", "ALTER", "SYSTEM", "ON", "SETTING", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET", "VALUE", ",", "ALTER", "SYSTEM", "ON", "SETTING", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM,", "SET", "VALUE", "ON", "SETTING", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM", ",", "SET", "VALUE", "ON", "SETTING", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALL", "ON", "SETTING", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALL", "PRIVILEGES", "ON", "SETTING", 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");
@@ -3720,7 +3861,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
@@ -3750,13 +3892,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");
/*
@@ -3765,7 +3909,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);
@@ -3803,6 +3948,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,
@@ -3814,7 +3978,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");
@@ -3823,7 +3988,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");
@@ -3832,7 +3998,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..a0c3966da9 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -92,6 +92,7 @@ typedef enum ObjectClass
OCLASS_TYPE, /* pg_type */
OCLASS_CAST, /* pg_cast */
OCLASS_COLLATION, /* pg_collation */
+ OCLASS_SETTING, /* pg_setting_acl */
OCLASS_CONSTRAINT, /* pg_constraint */
OCLASS_CONVERSION, /* pg_conversion */
OCLASS_DEFAULT, /* pg_attrdef */
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
index 2a79155636..12ec9b0a31 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_SETTING 'c' /* configuration parameter */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index d8e8715ed1..9c6cf7c558 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 settings',
+ proname => 'aclsettingdefault', prorettype => '_aclitem', proargtypes => 'text',
+ prosrc => 'aclsettingdefault_sql' },
{ oid => '1689',
descr => 'convert ACL item array to table, primarily for use by information schema',
proname => 'aclexplode', prorows => '10', proretset => 't',
@@ -7207,6 +7211,25 @@
proname => 'has_type_privilege', provolatile => 's', prorettype => 'bool',
proargtypes => 'oid text', prosrc => 'has_type_privilege_id' },
+{ oid => '8050', descr => 'user privilege on setting by username, setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'name text text', prosrc => 'has_setting_privilege_name_name' },
+{ oid => '8051', descr => 'user privilege on setting by username, setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'name oid text', prosrc => 'has_setting_privilege_name_id' },
+{ oid => '8052', descr => 'user privilege on setting by user oid, setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid text text', prosrc => 'has_setting_privilege_id_name' },
+{ oid => '8053', descr => 'user privilege on setting by user oid, setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid oid text', prosrc => 'has_setting_privilege_id_id' },
+{ oid => '8054', descr => 'current user privilege on setting by setting name',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'text text', prosrc => 'has_setting_privilege_name' },
+{ oid => '8055', descr => 'current user privilege on setting by setting oid',
+ proname => 'has_setting_privilege', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'oid text', prosrc => 'has_setting_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/catalog/pg_setting_acl.h b/src/include/catalog/pg_setting_acl.h
new file mode 100644
index 0000000000..b7dee55e5c
--- /dev/null
+++ b/src/include/catalog/pg_setting_acl.h
@@ -0,0 +1,63 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_setting_acl.h
+ * definition of the "configuration parameter" system catalog
+ * (pg_setting_acl).
+ *
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_setting_acl.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SETTING_ACL_H
+#define PG_SETTING_ACL_H
+
+#include "catalog/genbki.h"
+#include "catalog/pg_setting_acl_d.h"
+
+/* ----------------
+ * pg_setting_acl definition. cpp turns this into
+ * typedef struct FormData_pg_setting_acl
+ * ----------------
+ */
+CATALOG(pg_setting_acl,8924,SettingAclRelationId) BKI_SHARED_RELATION
+{
+ Oid oid; /* oid */
+ /*
+
+ * Variable-length fields start here, but we allow direct access to
+ * setting.
+ */
+ text setting BKI_FORCE_NOT_NULL;
+
+#ifdef CATALOG_VARLEN
+ /* Access privileges */
+ aclitem setacl[1] BKI_DEFAULT(_null_);
+#endif
+} FormData_pg_setting_acl;
+
+
+/* ----------------
+ * Form_pg_setting_acl corresponds to a pointer to a tuple with
+ * the format of pg_setting_acl relation.
+ * ----------------
+ */
+typedef FormData_pg_setting_acl *Form_pg_setting_acl;
+
+DECLARE_TOAST(pg_setting_acl, 8925, 8926);
+#define PgSettingAclToastTable 8925
+#define PgSettingAclToastIndex 8926
+
+DECLARE_UNIQUE_INDEX(pg_setting_acl_setting_index, 8927, SettingAclSettingIndexId, on pg_setting_acl using btree(setting text_ops));
+DECLARE_UNIQUE_INDEX_PKEY(pg_setting_acl_oid_index, 8928, SettingAclOidIndexId, on pg_setting_acl using btree(oid oid_ops));
+
+extern Oid SettingAclCreate(const char *setting, bool if_not_exists);
+
+#endif /* PG_SETTING_ACL_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 1617702d9d..5f5e4ae8e1 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -92,7 +92,9 @@ 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 N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */
+#define ACL_SET_VALUE (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
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
#define ACL_SELECT_FOR_UPDATE ACL_UPDATE
@@ -1795,6 +1797,7 @@ typedef enum ObjectType
OBJECT_CAST,
OBJECT_COLUMN,
OBJECT_COLLATION,
+ OBJECT_SETTING,
OBJECT_CONVERSION,
OBJECT_DATABASE,
OBJECT_DEFAULT,
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index bcef7eed2f..168aaafb93 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -380,6 +380,7 @@ PG_KEYWORD("session_user", SESSION_USER, RESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("set", SET, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("setof", SETOF, COL_NAME_KEYWORD, BARE_LABEL)
PG_KEYWORD("sets", SETS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("setting", SETTING, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("share", SHARE, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("show", SHOW, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("similar", SIMILAR, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 1ce4c5556e..b50ef98c69 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -146,9 +146,11 @@ 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_ALTER_SYSTEM_CHR 'A'
/* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTc"
+#define ACL_ALL_RIGHTS_STR "arwdDxtXUCTcsA"
/*
* Bitmasks defining "all rights" for each supported object type
@@ -165,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_SETTING (ACL_SET_VALUE|ACL_ALTER_SYSTEM)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -186,6 +189,7 @@ typedef enum
* routines used internally
*/
extern Acl *acldefault(ObjectType objtype, Oid ownerId);
+extern Acl *aclsettingdefault(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,
@@ -223,6 +227,8 @@ extern void select_best_grantor(Oid roleId, AclMode privileges,
extern void initialize_acl(void);
+extern Oid get_setting_oid(const char *setting, bool missing_ok);
+
/*
* prototypes for functions in aclchk.c
*/
@@ -243,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_setting_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,
@@ -271,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_setting_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..e3baa87535 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -381,7 +381,10 @@ 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);
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..7a8ba82770 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_setting_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..5961921462 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -89,6 +89,8 @@ enum SysCacheIdentifier
REPLORIGNAME,
RULERELNAME,
SEQRELID,
+ SETTINGNAME,
+ SETTINGOID,
STATEXTDATASTXOID,
STATEXTNAMENSP,
STATEXTOID,
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 c73bd37835..832804b394 100644
--- a/src/test/modules/test_pg_dump/t/001_base.pl
+++ b/src/test/modules/test_pg_dump/t/001_base.pl
@@ -317,6 +317,66 @@ my %tests = (
like => { pg_dumpall_globals => 1, },
},
+ 'GRANT ALTER SYSTEM ON SETTING full_page_writes TO regress_dump_test_role' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT ALTER SYSTEM ON SETTING full_page_writes TO regress_dump_test_role;',
+ regexp =>
+
+ qr/^GRANT ALTER SYSTEM ON SETTING full_page_writes TO regress_dump_test_role;/m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALL ON SETTING fsync TO regress_dump_test_role WITH GRANT OPTION' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET VALUE, ALTER SYSTEM ON SETTING fsync TO regress_dump_test_role WITH GRANT OPTION;',
+ regexp =>
+ # "set value" plus "alter system" is "all" privileges on settings
+ qr/^GRANT ALL ON SETTING fsync TO regress_dump_test_role WITH GRANT OPTION;/m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALTER SYSTEM ON SETTING zero_damaged_pages" TO regress_dump_test_role' => {
+ create_order => 2,
+ create_sql =>
+ # configuration parameters get cased folded
+ 'GRANT ALTER SYSTEM ON SETTING ZERO_DAMAGED_PAGES TO regress_dump_test_role;',
+ regexp =>
+ qr/^GRANT ALTER SYSTEM ON SETTING zero_damaged_pages TO regress_dump_test_role;/m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALL ON SETTING 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 VALUE ON SETTING ignore_checksum_failure TO regress_dump_test_role GRANTED BY CURRENT_ROLE;',
+ regexp =>
+ qr/^GRANT ALL ON SETTING ignore_checksum_failure TO regress_dump_test_role/m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'REVOKE SET VALUE ON SETTING work_mem FROM PUBLIC' => {
+ create_order => 2,
+ create_sql =>
+ 'REVOKE ALL PRIVILEGES ON SETTING work_mem FROM PUBLIC;',
+ regexp =>
+ # Pubilc only has "set value" by default, so revoking "all privileges" is simplified to revoking "set value"
+ qr/^REVOKE SET VALUE ON SETTING work_mem FROM PUBLIC;/m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
+ 'GRANT ALL ON SETTING "DateStyle" TO regress_dump_test_role' => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT ALL ON SETTING DateStyle TO regress_dump_test_role WITH GRANT OPTION; REVOKE GRANT OPTION FOR ALL ON SETTING 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 SETTING "DateStyle" TO regress_dump_test_role;/m,
+ like => { pg_dumpall_globals => 1, },
+ },
+
'CREATE SCHEMA public' => {
regexp => qr/^CREATE SCHEMA public;/m,
like => {
@@ -791,6 +851,7 @@ foreach my $run (sort keys %pgdump_runs)
if (!ok($output_file =~ $tests{$test}->{regexp},
"$run: should dump $test"))
{
+ die "\n\n--\n\n$output_file\n\n--\n\n";
diag("Review $run results in $tempdir");
}
}
diff --git a/src/test/regress/expected/guc_privs.out b/src/test/regress/expected/guc_privs.out
new file mode 100644
index 0000000000..3d99cc028c
--- /dev/null
+++ b/src/test/regress/expected/guc_privs.out
@@ -0,0 +1,367 @@
+-- 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;
+-- Check the new role does not yet have privileges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ f
+(1 row)
+
+-- Check inappropriate and nonsense privilege types
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+-- Revoke, grant, and revoke again a SUSET setting not yet granted
+SELECT has_setting_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+
+(1 row)
+
+REVOKE SET VALUE ON SETTING zero_damaged_pages FROM regress_host_resource_admin;
+SELECT has_setting_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ f
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ f
+(1 row)
+
+GRANT SET VALUE ON SETTING zero_damaged_pages TO regress_host_resource_admin;
+SELECT has_setting_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ f
+(1 row)
+
+REVOKE SET VALUE ON SETTING zero_damaged_pages FROM regress_host_resource_admin;
+SELECT has_setting_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ f
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ f
+(1 row)
+
+-- Revoke, grant, and revoke again a USERSET setting not yet granted
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ f
+(1 row)
+
+REVOKE SET VALUE ON SETTING work_mem FROM regress_host_resource_admin;
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ f
+(1 row)
+
+GRANT SET VALUE ON SETTING work_mem TO regress_host_resource_admin;
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ f
+(1 row)
+
+REVOKE SET VALUE ON SETTING work_mem FROM regress_host_resource_admin;
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ f
+(1 row)
+
+-- Grant privileges on settings to the new non-superuser role
+GRANT SET VALUE, ALTER SYSTEM ON SETTING
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Check the new role now has privilges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE WITH GRANT OPTION, ALTER SYSTEM WITH GRANT OPTION');
+ has_setting_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_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+ERROR: unrecognized privilege type: "SELECT"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+ERROR: unrecognized privilege type: "USAGE"
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+ERROR: unrecognized privilege type: "WHATEVER"
+SELECT has_setting_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_setting_privilege('regress_host_resource_admin',
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege('hash_mem_multiplier', 'set value');
+ has_setting_privilege
+-----------------------
+ t
+(1 row)
+
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'work_mem'),
+ 'alter system with grant option');
+ has_setting_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
+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
+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 setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting autovacuum_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting logical_decoding_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting temp_buffers
+privileges for setting temp_file_limit
+-- Use "revoke" to remove the privileges and allow the role to be dropped
+REVOKE SET VALUE, ALTER SYSTEM ON SETTING
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON SETTING
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting autovacuum_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting logical_decoding_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting temp_buffers
+privileges for setting 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 VALUE, ALTER SYSTEM ON SETTING
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 setting work_mem
+privileges for setting maintenance_work_mem
+privileges for setting autovacuum_work_mem
+privileges for setting hash_mem_multiplier
+privileges for setting logical_decoding_work_mem
+privileges for setting max_stack_depth
+privileges for setting min_dynamic_shared_memory
+privileges for setting shared_buffers
+privileges for setting temp_buffers
+privileges for setting 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
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 291e21d7a6..3158903bda 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -54,6 +54,36 @@ REVOKE pg_read_all_settings FROM regress_priv_user8;
DROP USER regress_priv_user10;
DROP USER regress_priv_user9;
DROP USER regress_priv_user8;
+GRANT SET VALUE ON SETTING enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON SETTING enable_nestloop TO regress_priv_user6;
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+GRANT ALTER SYSTEM ON SETTING enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON SETTING sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON SETTING maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON SETTING no_such_param TO regress_priv_user7; -- ok
+ERROR: unrecognized configuration parameter "no_such_param"
+GRANT ALTER SYSTEM ON SETTING no_such_extension.no_such_param TO regress_priv_user7; -- ok
+ERROR: unrecognized configuration parameter "no_such_extension.no_such_param"
+GRANT ALTER SYSTEM ON SETTING no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+ERROR: unrecognized configuration parameter "no_such_extension.no_such_param.longer.than.maximum.namedata.length"
+GRANT ALTER SYSTEM ON SETTING "" TO regress_priv_user7; -- bad name
+ERROR: zero-length delimited identifier at or near """"
+LINE 1: GRANT ALTER SYSTEM ON SETTING "" TO regress_priv_user7;
+ ^
+GRANT ALTER SYSTEM ON SETTING " " TO regress_priv_user7; -- bad name
+ERROR: invalid setting name " "
+GRANT ALTER SYSTEM ON SETTING " foo " TO regress_priv_user7; -- bad name
+ERROR: invalid setting name " foo "
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
ALTER GROUP regress_priv_group1 ADD USER regress_priv_user4;
@@ -2355,10 +2385,32 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+ERROR: role "regress_priv_user6" cannot be dropped because some objects depend on it
+DETAIL: privileges for setting enable_memoize
+privileges for setting enable_nestloop
+DROP USER regress_priv_user7; -- privileges remain
+ERROR: role "regress_priv_user7" cannot be dropped because some objects depend on it
+DETAIL: privileges for table persons2
+privileges for setting enable_seqscan
+privileges for setting work_mem
+privileges for setting maintenance_work_mem
DROP USER regress_priv_user8; -- does not exist
ERROR: role "regress_priv_user8" does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON SETTING enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON SETTING work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON SETTING vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON SETTING no_such_param FROM regress_priv_user7; -- ok
+ERROR: unrecognized configuration parameter "no_such_param"
+REVOKE ALTER SYSTEM ON SETTING no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+ERROR: unrecognized configuration parameter "no_such_extension.no_such_param"
+REVOKE ALTER SYSTEM ON SETTING no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+ERROR: unrecognized configuration parameter "no_such_extension.no_such_param.longer.than.maximum.namedata.length"
+DROP USER regress_priv_user7; -- ok
+REVOKE SET VALUE ON SETTING enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON SETTING enable_nestloop FROM regress_priv_user6;
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
CREATE TABLE lock_table (a int);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index ac468568a1..0d0e3ff410 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1686,6 +1686,19 @@ pg_sequences| SELECT n.nspname AS schemaname,
JOIN pg_class c ON ((c.oid = s.seqrelid)))
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char"));
+pg_setting_privileges| SELECT grantor.rolname AS grantor,
+ grantee.rolname AS grantee,
+ set_acl.setting,
+ acl.privilege_type,
+ acl.is_grantable
+ FROM pg_setting_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_settings| SELECT a.name,
a.setting,
a.unit,
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 6d8f524ae9..4dfa0642c0 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -86,7 +86,7 @@ test: brin_bloom brin_multi
# psql depends on create_am
# amutils depends on geometry, create_index_spgist, hash_index, brin
# ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role guc_privs
# collate.*.utf8 tests cannot be run in parallel with each other
test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8
diff --git a/src/test/regress/sql/guc_privs.sql b/src/test/regress/sql/guc_privs.sql
new file mode 100644
index 0000000000..5914ae69de
--- /dev/null
+++ b/src/test/regress/sql/guc_privs.sql
@@ -0,0 +1,165 @@
+-- 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;
+-- Check the new role does not yet have privileges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+-- Check inappropriate and nonsense privilege types
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+-- Revoke, grant, and revoke again a SUSET setting not yet granted
+SELECT has_setting_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+REVOKE SET VALUE ON SETTING zero_damaged_pages FROM regress_host_resource_admin;
+SELECT has_setting_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+GRANT SET VALUE ON SETTING zero_damaged_pages TO regress_host_resource_admin;
+SELECT has_setting_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+REVOKE SET VALUE ON SETTING zero_damaged_pages FROM regress_host_resource_admin;
+SELECT has_setting_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+-- Revoke, grant, and revoke again a USERSET setting not yet granted
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+REVOKE SET VALUE ON SETTING work_mem FROM regress_host_resource_admin;
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+GRANT SET VALUE ON SETTING work_mem TO regress_host_resource_admin;
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+REVOKE SET VALUE ON SETTING work_mem FROM regress_host_resource_admin;
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+-- Grant privileges on settings to the new non-superuser role
+GRANT SET VALUE, ALTER SYSTEM ON SETTING
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- Check the new role now has privilges on settings
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE, ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'SET VALUE 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_setting_privilege('regress_host_resource_admin', 'work_mem', 'SELECT, UPDATE, CREATE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'USAGE');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER');
+SELECT has_setting_privilege('regress_host_resource_admin', 'work_mem', 'WHATEVER WITH GRANT OPTION');
+-- Check other function signatures
+SELECT has_setting_privilege('regress_host_resource_admin',
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET VALUE');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ (SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = 'max_stack_depth'),
+ 'SET VALUE');
+SELECT has_setting_privilege('hash_mem_multiplier', 'set value');
+SELECT has_setting_privilege((SELECT oid FROM pg_catalog.pg_setting_acl WHERE setting = '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
+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
+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 VALUE, ALTER SYSTEM ON SETTING
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON SETTING
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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 VALUE, ALTER SYSTEM ON SETTING
+ autovacuum_work_mem, hash_mem_multiplier, logical_decoding_work_mem,
+ maintenance_work_mem, max_stack_depth, min_dynamic_shared_memory,
+ shared_buffers, temp_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
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index c8c545b64c..9f1f436b33 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -66,6 +66,33 @@ DROP USER regress_priv_user10;
DROP USER regress_priv_user9;
DROP USER regress_priv_user8;
+GRANT SET VALUE ON SETTING enable_memoize TO regress_priv_user6;
+GRANT SET VALUE ON SETTING enable_nestloop TO regress_priv_user6;
+
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+
+GRANT ALTER SYSTEM ON SETTING enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON SETTING sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON SETTING maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON SETTING no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON SETTING no_such_extension.no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON SETTING no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON SETTING "" TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON SETTING " " TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON SETTING " foo " TO regress_priv_user7; -- bad name
+
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
+
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
@@ -1426,10 +1453,23 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+DROP USER regress_priv_user7; -- privileges remain
DROP USER regress_priv_user8; -- does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON SETTING enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON SETTING work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON SETTING vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON SETTING no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON SETTING no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON SETTING no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+DROP USER regress_priv_user7; -- ok
+
+REVOKE SET VALUE ON SETTING enable_memoize FROM regress_priv_user6;
+REVOKE SET VALUE ON SETTING enable_nestloop FROM regress_priv_user6;
+
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
--
2.35.1
On 3/15/22 16:59, Mark Dilger wrote:
On Mar 6, 2022, at 3:27 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Mark Dilger <mark.dilger@enterprisedb.com> writes:
The existing patch allows grants on unknown gucs, because it can't know what guc an upgrade script will introduce, and the grant statement may need to execute before the guc exists.
Yeah, that's the problematic case. It might mostly work to assume that
an unknown GUC has an empty default ACL. This could fail to retain the
default PUBLIC SET permission if it later turns out the GUC is USERSETOn further reflection, I concluded this isn't needed. No current extension, whether in-core or third party, expects to be able to create a new GUC and then grant or revoke permissions on it. They can already specify the guc context (PGC_USERS, etc). Introducing a feature that depends on the dubious assumption that unrecognized GUCs will turn out to be USERSET doesn't seem warranted.
Agreed.
The patch attributes all grants of setting privileges to the bootstrap superuser. Only superusers can grant or revoke privileges on settings, and all settings are implicitly owned by the bootstrap superuser because there is no explicit owner associated with settings. Consequently, select_best_grantor(some_superuser, ..., BOOTSTRAP_SUPERUSERID, ...) always chooses the bootstrap superuser. I don't see a problem with this, but wouldn't mind a second opinion. Some people might find it surprising when viewing the pg_setting_acl.setacl field.
I think it's OK as long as we document it. An alternative might be to
invent a pseudo-superuser called, say, 'postgres_system', but that seems
like overkill to solve what is in effect a cosmetic problem.
Generally I think this is now in fairly good shape, I've played with it
and it seems to do what I expect in every case, and the things I found
surprising are gone.
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
Andrew Dunstan <andrew@dunslane.net> writes:
Generally I think this is now in fairly good shape, I've played with it
and it seems to do what I expect in every case, and the things I found
surprising are gone.
Stepping back a bit ... do we really want to institutionalize the
term "setting" for GUC variables? I realize that the view pg_settings
exists, but the documentation generally prefers the term "configuration
parameters". Where config.sgml uses "setting" as a noun, it's usually
talking about a specific concrete value for a parameter, and you can
argue that the view's name comports with that meaning. But you can't
GRANT a parameter's current value.
I don't have a better name to offer offhand --- I surely am not proposing
that we change the syntax to be "GRANT ... ON CONFIGURATION PARAMETER x",
because that's way too wordy. But now is the time to bikeshed if we're
gonna bikeshed, or else admit that we're not interested in precise
vocabulary.
I'm also fairly allergic to the way that this patch has decided to assign
multi-word names to privilege types (ie SET VALUE, ALTER SYSTEM). There
is no existing precedent for that, and I think it's going to break
client-side code that we don't need to break. It's not coincidental that
this forces weird changes in rules about whitespace in the has_privilege
functions, for example; and if you think that isn't going to cause
problems I think you are wrong. Perhaps we could just use "SET" and
"ALTER", or "SET" and "SYSTEM"?
I also agree with the upthread criticism that this is abusing the
ObjectPostAlterHook API unreasonably much. In particular, it's
trying to use pg_setting_acl OIDs as identities for GUCs, which
they are not, first because they're unstable and second because
most GUCs won't even have an entry there. I therefore judge the
hook calls added to ExecSetVariableStmt and AlterSystemSetConfigFile
to be 100% useless, in fact probably counterproductive because they
introduce a boatload of worries about whether the right things happen
if the hook errors out or does something guc.c isn't expecting.
I suggest that what might be saner is to consider that the "objects"
that the hook calls are concerned with are the pg_setting_acl entries,
not the underlying GUCs, and thus that the hooks need be invoked only
when creating, destroying or altering those entries. If we do have
a need for a hook editorializing on GUC value settings, that would
have to be an independent API --- but I have heard no calls for
the ability to have such a hook, and I don't think that this patch
is the place to introduce one.
regards, tom lane
On Mar 16, 2022, at 11:47 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Stepping back a bit ... do we really want to institutionalize the
term "setting" for GUC variables? I realize that the view pg_settings
exists, but the documentation generally prefers the term "configuration
parameters". Where config.sgml uses "setting" as a noun, it's usually
talking about a specific concrete value for a parameter, and you can
argue that the view's name comports with that meaning. But you can't
GRANT a parameter's current value.I don't have a better name to offer offhand --- I surely am not proposing
that we change the syntax to be "GRANT ... ON CONFIGURATION PARAMETER x",
because that's way too wordy. But now is the time to bikeshed if we're
gonna bikeshed, or else admit that we're not interested in precise
vocabulary.
Informally, we often use "GUC" on this list, but that isn't used formally, leaving "configuration parameter" and "setting" as the two obvious choices. I preferred "configuration parameter" originally and was argued out of it. My take on "setting" was also that it more naturally refers to the choice of setting, not the thing being set, such that "work_mem = 8192" means the configuration parameter "work_mem" has the setting "8192". I'm happy to change the patch along these lines, but I vaguely recall it being Robert who liked the "setting" language. Robert? (Sorry if I misremember that...)
I'm also fairly allergic to the way that this patch has decided to assign
multi-word names to privilege types (ie SET VALUE, ALTER SYSTEM). There
is no existing precedent for that, and I think it's going to break
client-side code that we don't need to break. It's not coincidental that
this forces weird changes in rules about whitespace in the has_privilege
functions, for example; and if you think that isn't going to cause
problems I think you are wrong. Perhaps we could just use "SET" and
"ALTER", or "SET" and "SYSTEM"?
I chose those particular multi-word names to avoid needing to promote any keywords. That's been a while ago, and I don't recall exactly where the shift-reduce or reduce-reduce errors were coming from. I'll play with it to see what I can find.
I also agree with the upthread criticism that this is abusing the
ObjectPostAlterHook API unreasonably much. In particular, it's
trying to use pg_setting_acl OIDs as identities for GUCs, which
they are not, first because they're unstable and second because
most GUCs won't even have an entry there. I therefore judge the
hook calls added to ExecSetVariableStmt and AlterSystemSetConfigFile
to be 100% useless, in fact probably counterproductive because they
introduce a boatload of worries about whether the right things happen
if the hook errors out or does something guc.c isn't expecting.
I think Joshua was planning to use these hooks for security purposes. The hooks are supposed to check whether the Oid is valid, and if not, still be able to make choices based on the other information. Joshua, any comment on this?
I suggest that what might be saner is to consider that the "objects"
that the hook calls are concerned with are the pg_setting_acl entries,
not the underlying GUCs, and thus that the hooks need be invoked only
when creating, destroying or altering those entries.
That's certainly a thing we could do, but I got the impression that Joshua wanted to hook into SET, RESET, and ALTER SYSTEM SET, not merely into GRANT and REVOKE.
If we do have
a need for a hook editorializing on GUC value settings, that would
have to be an independent API --- but I have heard no calls for
the ability to have such a hook, and I don't think that this patch
is the place to introduce one.
Well, the call for it was in this thread, but I'm ok with yanking out that part of the patch if it seems half-baked.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
<snip>
I suggest that what might be saner is to consider that the "objects"
that the hook calls are concerned with are the pg_setting_acl entries,
not the underlying GUCs, and thus that the hooks need be invoked only
when creating, destroying or altering those entries. If we do have
a need for a hook editorializing on GUC value settings, that would
have to be an independent API --- but I have heard no calls for
the ability to have such a hook, and I don't think that this patch
is the place to introduce one.
I requested it here:
/messages/by-id/CAGB+Vh5pVFAqw8YzeXy4xxmEt_4Hq_8pEUHdCQvv3mCjvC-S-w@mail.gmail.com
with compromises here:
/messages/by-id/CAGB+Vh6wLJ3FKsno62fi54pfg0FDrZRWcpuuCJBkHcCj-G1ndw@mail.gmail.com
/messages/by-id/0A3D3CBA-6548-4C9E-9F46-59D5C51A1F31@enterprisedb.com
/messages/by-id/CAGB+Vh65R5vKC4rEt7r2_pK3kMZd-VY0n99RJwcP8Bic7xvOxQ@mail.gmail.com
and the new version here:
/messages/by-id/C8DF19D8-C15D-4C2D-91CA-391390F1E421@enterprisedb.com
which I wrote. a toy module to test and was satisfied with it, despite
the limitations.
If we are adding a DAC Grant for a type of object it seems untenable
that it would not come with analogous MAC capable hooks.
Thank you.
Mark Dilger <mark.dilger@enterprisedb.com> writes:
On Mar 16, 2022, at 11:47 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
... I therefore judge the
hook calls added to ExecSetVariableStmt and AlterSystemSetConfigFile
to be 100% useless, in fact probably counterproductive because they
introduce a boatload of worries about whether the right things happen
if the hook errors out or does something guc.c isn't expecting.
I think Joshua was planning to use these hooks for security purposes. The hooks are supposed to check whether the Oid is valid, and if not, still be able to make choices based on the other information. Joshua, any comment on this?
It's going to be hard to do anything useful in a hook that (a) does
not know which GUC is being assigned to and (b) cannot do catalog
accesses for fear that we're not inside a transaction. (b), in
particular, seems like a rather thorough API break; up to now
ObjectPostAlter hooks could assume that catalog accesses are OK.
regards, tom lane
On Wed, Mar 16, 2022 at 3:06 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Mark Dilger <mark.dilger@enterprisedb.com> writes:
On Mar 16, 2022, at 11:47 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
... I therefore judge the
hook calls added to ExecSetVariableStmt and AlterSystemSetConfigFile
to be 100% useless, in fact probably counterproductive because they
introduce a boatload of worries about whether the right things happen
if the hook errors out or does something guc.c isn't expecting.I think Joshua was planning to use these hooks for security purposes. The hooks are supposed to check whether the Oid is valid, and if not, still be able to make choices based on the other information. Joshua, any comment on this?
It's going to be hard to do anything useful in a hook that (a) does
not know which GUC is being assigned to and (b) cannot do catalog
accesses for fear that we're not inside a transaction. (b), in
particular, seems like a rather thorough API break; up to now
ObjectPostAlter hooks could assume that catalog accesses are OK.
Can you elaborate on this point? This is perhaps an area where I don't
know the rules of the road, to test this hook I modified the set_user
extension, which normally uses the parse tree to figure out if someone
is trying to SET log_statement or ALTER SYSTEM log_statement and
replaced it with:
case OAT_POST_ALTER:
if (classId == SettingAclRelationId)
{
ObjectAddress object;
object.classId = SettingAclRelationId;
object.objectId = objectId;
object.objectSubId = 0;
if (strcmp(getObjectIdentity(&object),
"log_statement") == 0)
{
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("Setting %s blocked",
getObjectIdentity(&object))));
}
}
Is that inherently unsafe?
Joshua Brindle <joshua.brindle@crunchydata.com> writes:
On Wed, Mar 16, 2022 at 3:06 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
It's going to be hard to do anything useful in a hook that (a) does
not know which GUC is being assigned to and (b) cannot do catalog
accesses for fear that we're not inside a transaction. (b), in
particular, seems like a rather thorough API break; up to now
ObjectPostAlter hooks could assume that catalog accesses are OK.
Can you elaborate on this point?
Hmm, I glossed over too many details there perhaps. I was thinking
about the restrictions on GUC check_hooks, which can be run outside
a transaction altogether. But that's not quite relevant here.
ExecSetVariableStmt can assume it's inside a transaction, but what
it *can't* assume is that we've set a transaction snapshot as yet
(cf. PlannedStmtRequiresSnapshot). If we call an ObjectPostAlter hook
there, and it does a catalog access, that's going to break things for
modifications of GUCs that are supposed to be modifiable without
freezing the transaction snapshot. So the net result is the same:
catalog access not okay, at least not in general.
Between that and the fact that an OID-based API is largely useless here,
I don't think it's sane to try to use the existing ObjectPostAlter API.
Maybe there is a case for inventing a separate hook API that could pass
the GUC name as a string, instead. I remain of the opinion that this
patch should not concern itself with that, though.
regards, tom lane
On 3/16/22 14:47, Tom Lane wrote:
Andrew Dunstan <andrew@dunslane.net> writes:
Generally I think this is now in fairly good shape, I've played with it
and it seems to do what I expect in every case, and the things I found
surprising are gone.Stepping back a bit ... do we really want to institutionalize the
term "setting" for GUC variables? I realize that the view pg_settings
exists, but the documentation generally prefers the term "configuration
parameters". Where config.sgml uses "setting" as a noun, it's usually
talking about a specific concrete value for a parameter, and you can
argue that the view's name comports with that meaning. But you can't
GRANT a parameter's current value.I don't have a better name to offer offhand --- I surely am not proposing
that we change the syntax to be "GRANT ... ON CONFIGURATION PARAMETER x",
because that's way too wordy. But now is the time to bikeshed if we're
gonna bikeshed, or else admit that we're not interested in precise
vocabulary.I'm also fairly allergic to the way that this patch has decided to assign
multi-word names to privilege types (ie SET VALUE, ALTER SYSTEM). There
is no existing precedent for that, and I think it's going to break
client-side code that we don't need to break. It's not coincidental that
this forces weird changes in rules about whitespace in the has_privilege
functions, for example; and if you think that isn't going to cause
problems I think you are wrong. Perhaps we could just use "SET" and
"ALTER", or "SET" and "SYSTEM"?
That's going to look weird, ISTM. This is less clear about what it's
granting.
GRANT ALTER ON SOMETHING shared_buffers TO myuser;
If you don't like that maybe ALTER_SYSTEM and SET_VALUE would work,
although mostly we have avoided things like that.
How about MODIFY instead of SET VALUE and CONFIGURE instead of ALTER SYSTEM?
Personally I don't have problem with the use of SETTING. I think the
meaning is pretty plain in context and unlikely to produce any confusion.
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
Andrew Dunstan <andrew@dunslane.net> writes:
On 3/16/22 14:47, Tom Lane wrote:
I'm also fairly allergic to the way that this patch has decided to assign
multi-word names to privilege types (ie SET VALUE, ALTER SYSTEM). There
is no existing precedent for that, and I think it's going to break
client-side code that we don't need to break. It's not coincidental that
this forces weird changes in rules about whitespace in the has_privilege
functions, for example; and if you think that isn't going to cause
problems I think you are wrong. Perhaps we could just use "SET" and
"ALTER", or "SET" and "SYSTEM"?
That's going to look weird, ISTM. This is less clear about what it's
granting.
GRANT ALTER ON SOMETHING shared_buffers TO myuser;
True. I think "GRANT SET" is clear enough, and it fits with the custom of
using the name of the SQL statement that the privilege allows you to
invoke. (I gather from Mark's comments that Bison gave him problems with
that, but maybe that can be dealt with.) But I concede that "ALTER" by
itself is pretty vague.
If you don't like that maybe ALTER_SYSTEM and SET_VALUE would work,
although mostly we have avoided things like that.
How about MODIFY instead of SET VALUE and CONFIGURE instead of ALTER SYSTEM?
I thought about ALTER_SYSTEM too. It's not great but maybe the best we
can do. Not sure that CONFIGURE is better.
Personally I don't have problem with the use of SETTING. I think the
meaning is pretty plain in context and unlikely to produce any confusion.
I'm just unhappy about the disconnect with the documentation. I wonder
if we could get away with s/configuration parameter/setting/g in the docs.
regards, tom lane
On 3/16/22 16:53, Tom Lane wrote:
Personally I don't have problem with the use of SETTING. I think the
meaning is pretty plain in context and unlikely to produce any confusion.I'm just unhappy about the disconnect with the documentation. I wonder
if we could get away with s/configuration parameter/setting/g in the docs.
I don't think we need to fix that here though. If you can live with
SETTING for now I will undertake to fix the docs post-feature-freeze if
necessary.
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
<snip>
I remain of the opinion that this
patch should not concern itself with that, though.
So you are saying that people can add new object types to PG with DAC
permissions and not concern themselves with MAC capable hooks? Is that
an official PG community stance?
On 16.03.22 19:59, Mark Dilger wrote:
Informally, we often use "GUC" on this list, but that isn't used formally, leaving "configuration parameter" and "setting" as the two obvious choices. I preferred "configuration parameter" originally and was argued out of it. My take on "setting" was also that it more naturally refers to the choice of setting, not the thing being set, such that "work_mem = 8192" means the configuration parameter "work_mem" has the setting "8192".
"The current setting of the work_mem parameter is 8192."
I think something based on "parameter" is good. We also use that
language in the protocol (e.g., ParameterStatus).
On 16.03.22 19:47, Tom Lane wrote:
I'm also fairly allergic to the way that this patch has decided to assign
multi-word names to privilege types (ie SET VALUE, ALTER SYSTEM). There
is no existing precedent for that, and I think it's going to break
client-side code that we don't need to break. It's not coincidental that
this forces weird changes in rules about whitespace in the has_privilege
functions, for example; and if you think that isn't going to cause
problems I think you are wrong. Perhaps we could just use "SET" and
"ALTER", or "SET" and "SYSTEM"?
I think Oracle and MS SQL Server have many multi-word privilege names.
So users are quite used to that. And if we want to add more complex
privileges, we might run out of sensible single words eventually. So I
would not exclude this approach.
Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:
On 16.03.22 19:47, Tom Lane wrote:
... Perhaps we could just use "SET" and
"ALTER", or "SET" and "SYSTEM"?
I think Oracle and MS SQL Server have many multi-word privilege names.
So users are quite used to that. And if we want to add more complex
privileges, we might run out of sensible single words eventually. So I
would not exclude this approach.
Well, I still say that "SET" is sufficient for the one privilege name
(unless we really can't make Bison handle that, which I doubt). But
I'm willing to yield on using "ALTER SYSTEM" for the other.
If we go with s/SETTING/PARAMETER/ as per your other message, then
that would be adequately consistent with the docs I think. So it'd
be
GRANT { SET | ALTER SYSTEM } ON PARAMETER foo TO ...
and the new catalog would be pg_parameter_acl, and so on.
regards, tom lane
On 3/17/22 10:47, Tom Lane wrote:
Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:
On 16.03.22 19:47, Tom Lane wrote:
... Perhaps we could just use "SET" and
"ALTER", or "SET" and "SYSTEM"?I think Oracle and MS SQL Server have many multi-word privilege names.
So users are quite used to that. And if we want to add more complex
privileges, we might run out of sensible single words eventually. So I
would not exclude this approach.Well, I still say that "SET" is sufficient for the one privilege name
(unless we really can't make Bison handle that, which I doubt). But
I'm willing to yield on using "ALTER SYSTEM" for the other.If we go with s/SETTING/PARAMETER/ as per your other message, then
that would be adequately consistent with the docs I think. So it'd
beGRANT { SET | ALTER SYSTEM } ON PARAMETER foo TO ...
and the new catalog would be pg_parameter_acl, and so on.
The upside of this is that it avoids the inelegant
GRANT SET ON SETTING ...
But I was just looking again at the grammar, and the only reason we need
this keyword at all AFAICS is to disambiguate ALL [PRIVILEGES] cases.
If we abandoned that for this form of GRANT/REVOKE I think we could
probably get away with
GRANT { SET | ALTER SYSTEM } ON setting_name ...
I haven't tried it, so I could be all wrong.
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
On Thu, Mar 17, 2022 at 9:25 AM Joshua Brindle
<joshua.brindle@crunchydata.com> wrote:
<snip>
I remain of the opinion that this
patch should not concern itself with that, though.So you are saying that people can add new object types to PG with DAC
permissions and not concern themselves with MAC capable hooks? Is that
an official PG community stance?
I don't know that the community has an official position on that
topic, but I do not think it's reasonable to expect everyone who
tinkers with MAC permissions to try to make a corresponding equivalent
for DAC. The number of people using PostgreSQL with DAC is relatively
small, and the topic is extremely complicated, and a lot of hackers
don't really understand it well enough to be sure that whatever they
might do is right. I think it's reasonable to expect people who
understand DAC and care about it to put some energy into the topic,
and not just in terms of telling other people how they have to write
their patches.
I *don't* think it's appropriate for a patch that touches MAC to
deliberately sabotage the existing support we have for DAC or to just
ignore it where the right thing to do is obvious. But maintaining a
million lines of code is a lot of work, and I can't think of any
reason why the burden of maintaining relatively little-used features
should fall entirely on people who don't care about them.
--
Robert Haas
EDB: http://www.enterprisedb.com
On Wed, Mar 16, 2022 at 2:47 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Stepping back a bit ... do we really want to institutionalize the
term "setting" for GUC variables? I realize that the view pg_settings
exists, but the documentation generally prefers the term "configuration
parameters". Where config.sgml uses "setting" as a noun, it's usually
talking about a specific concrete value for a parameter, and you can
argue that the view's name comports with that meaning. But you can't
GRANT a parameter's current value.
I agree that the lack of a good user-friendly term for GUCs is a real
problem. Here at EDB I've observed even relatively non-technical
people using that term, which appears nowhere in the documentation and
is utterly unintelligible to a typical end-user. Somebody gets on the
phone and tells the customer that they need to set a GUC and the
customer is like "what's a guck?" except that they probably don't
actually ask that question but are just confused and fail to
understand that a postgresql.conf change is being proposed. I hate it.
It sucks.
I have sort of been trying to promote the use of the word "setting"
and use it in my own writing, especially to end-users. That is
definitely more intelligible to random users, but it's admittedly also
awkward. "Set a setting" just sounds redundant. But "set a
configuration variable" sounds wordy, so I don't know.
--
Robert Haas
EDB: http://www.enterprisedb.com
On Mar 17, 2022, at 9:04 AM, Robert Haas <robertmhaas@gmail.com> wrote:
not just in terms of telling other people how they have to write
their patches.
...
the burden of maintaining relatively little-used features
should fall entirely on people who don't care about them.
Joshua helped test the DAC portion of this patch, and answered a number of my questions on the topic, including in-person back in December. I take your point, Robert, on the general principle, but the archives should reflect that Joshua did contribute to this specific patch.
Joshua, should we drop the DAC portion for postgres 15 and revisit the issue for postgres 16? I think it's getting late in the development cycle to attempt what Tom described upthread, and I'd hate to see the rest of this patch punted.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Thu, Mar 17, 2022 at 12:19 PM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:
Joshua helped test the DAC portion of this patch, and answered a number of my questions on the topic, including in-person back in December. I take your point, Robert, on the general principle, but the archives should reflect that Joshua did contribute to this specific patch.
I wasn't intending to say otherwise, and if the changes needed for DAC
here are straightforward and don't make the patch significantly harder
to finish, then I would say Tom is wrong and we should just go ahead
and make them. But if they do, then I think it's perfectly fine to say
that we're going to leave that alone and let someone with
subject-matter expertise sort it out when they have time. We do that
all the time with other things, most notably MSVC builds, where we
just can't expect every hacker or even every committer to understand
exactly what's required to make it all work. I try my best to commit
things that don't break it, but sometimes I do, and then Andrew helps
sort it out, because he understands it and I don't. I think DAC should
fall into that kind of category as well.
--
Robert Haas
EDB: http://www.enterprisedb.com
On Thu, Mar 17, 2022 at 12:04 PM Robert Haas <robertmhaas@gmail.com> wrote:
On Thu, Mar 17, 2022 at 9:25 AM Joshua Brindle
<joshua.brindle@crunchydata.com> wrote:<snip>
I remain of the opinion that this
patch should not concern itself with that, though.So you are saying that people can add new object types to PG with DAC
permissions and not concern themselves with MAC capable hooks? Is that
an official PG community stance?I don't know that the community has an official position on that
topic, but I do not think it's reasonable to expect everyone who
tinkers with MAC permissions to try to make a corresponding equivalent
for DAC. The number of people using PostgreSQL with DAC is relatively
small, and the topic is extremely complicated, and a lot of hackers
don't really understand it well enough to be sure that whatever they
might do is right. I think it's reasonable to expect people who
understand DAC and care about it to put some energy into the topic,
and not just in terms of telling other people how they have to write
their patches.I *don't* think it's appropriate for a patch that touches MAC to
deliberately sabotage the existing support we have for DAC or to just
ignore it where the right thing to do is obvious. But maintaining a
million lines of code is a lot of work, and I can't think of any
reason why the burden of maintaining relatively little-used features
should fall entirely on people who don't care about them.
I agree with this view, outside of the mixup between MAC and DAC (DAC
is in-core, MAC is via hooks)
So there should be some committers or contributors that keep an eye
out and make sure new objects have appropriate hooks, and since an Oid
based hook for GUCs is inappropriate, I hope this patch can be rolled
into the contribution from Mark.
The attached patch adds a set of hooks for strings, and changes the
GUC patch from Mark to use it, I tested with this code:
void set_user_object_access_str(ObjectAccessType access, Oid classId,
const char *objName, int subId, void *arg)
{
if (next_object_access_hook_str)
{
(*next_object_access_hook_str)(access, classId, objName, subId, arg);
}
switch (access)
{
case OAT_POST_ALTER:
if (classId == SettingAclRelationId)
{
Oid userid = GetUserId();
bool is_superuser = superuser_arg(userid);
char *username = GETUSERNAMEFROMID(GetUserId());
const char *setting = "setting value";
const char *altering = "altering system";
const char *unknown = "unknown";
const char *action;
if (subId && ACL_SET_VALUE)
action = setting;
else if (subId && ACL_ALTER_SYSTEM)
action = altering;
else
action = unknown;
elog(WARNING, "%s is %s %s (%d)", username, action, objName, is_superuser);
if (strcmp(objName, "log_statement") == 0)
{
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("Setting %s blocked", objName)));
}
}
default:
break;
}
}
static void
set_user_object_access (ObjectAccessType access, Oid classId, Oid
objectId, int subId, void *arg)
{
if (next_object_access_hook)
{
(*next_object_access_hook)(access, classId, objectId, subId, arg);
}
switch (access)
{
case OAT_FUNCTION_EXECUTE:
{
/* Update the `set_config` Oid cache if necessary. */
set_user_cache_proc(InvalidOid);
/* Now see if this function is blocked */
set_user_block_set_config(objectId);
break;
}
/* fallthrough */
case OAT_POST_CREATE:
{
if (classId == ProcedureRelationId)
{
set_user_cache_proc(objectId);
}
break;
}
default:
break;
}
}
Attachments:
0001-Add-String-object-access-hooks.patchapplication/octet-stream; name=0001-Add-String-object-access-hooks.patchDownload
From 737d105298bbf0632700869b8b30601516ffd163 Mon Sep 17 00:00:00 2001
From: Joshua Brindle <joshua.brindle@crunchydata.com>
Date: Thu, 17 Mar 2022 09:24:57 -0700
Subject: [PATCH] Add String object access hooks
The first user of these will be the GUC access controls
Signed-off-by: Joshua Brindle <joshua.brindle@crunchydata.com>
---
src/backend/catalog/objectaccess.c | 128 +++++++++++++++++++++++++++++
src/backend/utils/misc/guc.c | 4 +-
src/include/catalog/objectaccess.h | 70 +++++++++++++++-
3 files changed, 199 insertions(+), 3 deletions(-)
diff --git a/src/backend/catalog/objectaccess.c b/src/backend/catalog/objectaccess.c
index 549fac4539..72ad6e9a90 100644
--- a/src/backend/catalog/objectaccess.c
+++ b/src/backend/catalog/objectaccess.c
@@ -20,6 +20,8 @@
* and logging plugins.
*/
object_access_hook_type object_access_hook = NULL;
+object_access_hook_type_str object_access_hook_str = NULL;
+
/*
* RunObjectPostCreateHook
@@ -143,3 +145,129 @@ RunFunctionExecuteHook(Oid objectId)
ProcedureRelationId, objectId, 0,
NULL);
}
+
+/* String versions */
+
+
+/*
+ * RunObjectPostCreateHook
+ *
+ * It is entrypoint of OAT_POST_CREATE event
+ */
+void
+RunObjectPostCreateHookStr(Oid classId, const char *objectName, int subId,
+ bool is_internal)
+{
+ ObjectAccessPostCreate pc_arg;
+
+ /* caller should check, but just in case... */
+ Assert(object_access_hook_str != NULL);
+
+ memset(&pc_arg, 0, sizeof(ObjectAccessPostCreate));
+ pc_arg.is_internal = is_internal;
+
+ (*object_access_hook_str) (OAT_POST_CREATE,
+ classId, objectName, subId,
+ (void *) &pc_arg);
+}
+
+/*
+ * RunObjectDropHook
+ *
+ * It is entrypoint of OAT_DROP event
+ */
+void
+RunObjectDropHookStr(Oid classId, const char *objectName, int subId,
+ int dropflags)
+{
+ ObjectAccessDrop drop_arg;
+
+ /* caller should check, but just in case... */
+ Assert(object_access_hook_str != NULL);
+
+ memset(&drop_arg, 0, sizeof(ObjectAccessDrop));
+ drop_arg.dropflags = dropflags;
+
+ (*object_access_hook_str) (OAT_DROP,
+ classId, objectName, subId,
+ (void *) &drop_arg);
+}
+
+/*
+ * RunObjectTruncateHook
+ *
+ * It is the entrypoint of OAT_TRUNCATE event
+ */
+void
+RunObjectTruncateHookStr(const char *objectName)
+{
+ /* caller should check, but just in case... */
+ Assert(object_access_hook_str != NULL);
+
+ (*object_access_hook_str) (OAT_TRUNCATE,
+ RelationRelationId, objectName, 0,
+ NULL);
+}
+
+/*
+ * RunObjectPostAlterHook
+ *
+ * It is entrypoint of OAT_POST_ALTER event
+ */
+void
+RunObjectPostAlterHookStr(Oid classId, const char *objectName, int subId,
+ Oid auxiliaryId, bool is_internal)
+{
+ ObjectAccessPostAlter pa_arg;
+
+ /* caller should check, but just in case... */
+ Assert(object_access_hook_str != NULL);
+
+ memset(&pa_arg, 0, sizeof(ObjectAccessPostAlter));
+ pa_arg.auxiliary_id = auxiliaryId;
+ pa_arg.is_internal = is_internal;
+
+ (*object_access_hook_str) (OAT_POST_ALTER,
+ classId, objectName, subId,
+ (void *) &pa_arg);
+}
+
+/*
+ * RunNamespaceSearchHook
+ *
+ * It is entrypoint of OAT_NAMESPACE_SEARCH event
+ */
+bool
+RunNamespaceSearchHookStr(const char *objectName, bool ereport_on_violation)
+{
+ ObjectAccessNamespaceSearch ns_arg;
+
+ /* caller should check, but just in case... */
+ Assert(object_access_hook_str != NULL);
+
+ memset(&ns_arg, 0, sizeof(ObjectAccessNamespaceSearch));
+ ns_arg.ereport_on_violation = ereport_on_violation;
+ ns_arg.result = true;
+
+ (*object_access_hook_str) (OAT_NAMESPACE_SEARCH,
+ NamespaceRelationId, objectName, 0,
+ (void *) &ns_arg);
+
+ return ns_arg.result;
+}
+
+/*
+ * RunFunctionExecuteHook
+ *
+ * It is entrypoint of OAT_FUNCTION_EXECUTE event
+ */
+void
+RunFunctionExecuteHookStr(const char *objectName)
+{
+ /* caller should check, but just in case... */
+ Assert(object_access_hook_str != NULL);
+
+ (*object_access_hook_str) (OAT_FUNCTION_EXECUTE,
+ ProcedureRelationId, objectName, 0,
+ NULL);
+}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 683031db9a..8edee70394 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -8853,7 +8853,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
* transactional. If the hook aborts our transaction, it will be cleaner
* to do so before we touch any files.
*/
- InvokeObjectPostAlterHookArg(SettingAclRelationId, settingId,
+ InvokeObjectPostAlterHookArgStr(SettingAclRelationId, name,
ACL_ALTER_SYSTEM, altersysstmt->setstmt->kind,
false);
@@ -9057,7 +9057,7 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
* abuse the notion of subId to pass the kind of alteration (set vs. alter
* system), and auxiliaryId to pass the VariableSetKind.
*/
- InvokeObjectPostAlterHookArg(SettingAclRelationId, settingId,
+ InvokeObjectPostAlterHookArgStr(SettingAclRelationId, stmt->name,
ACL_SET_VALUE, stmt->kind, false);
}
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index 508dfd0a6b..4c1211600e 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -121,15 +121,23 @@ typedef struct
bool result;
} ObjectAccessNamespaceSearch;
-/* Plugin provides a hook function matching this signature. */
+/* Plugin provides a hook function matching one or both of these signatures. */
typedef void (*object_access_hook_type) (ObjectAccessType access,
Oid classId,
Oid objectId,
int subId,
void *arg);
+typedef void (*object_access_hook_type_str) (ObjectAccessType access,
+ Oid classId,
+ const char *objectStr,
+ int subId,
+ void *arg);
+
/* Plugin sets this variable to a suitable hook function. */
extern PGDLLIMPORT object_access_hook_type object_access_hook;
+extern PGDLLIMPORT object_access_hook_type_str object_access_hook_str;
+
/* Core code uses these functions to call the hook (see macros below). */
extern void RunObjectPostCreateHook(Oid classId, Oid objectId, int subId,
@@ -142,6 +150,18 @@ extern void RunObjectPostAlterHook(Oid classId, Oid objectId, int subId,
extern bool RunNamespaceSearchHook(Oid objectId, bool ereport_on_violation);
extern void RunFunctionExecuteHook(Oid objectId);
+/* String versions */
+extern void RunObjectPostCreateHookStr(Oid classId, const char *objectStr, int subId,
+ bool is_internal);
+extern void RunObjectDropHookStr(Oid classId, const char *objectStr, int subId,
+ int dropflags);
+extern void RunObjectTruncateHookStr(const char *objectStr);
+extern void RunObjectPostAlterHookStr(Oid classId, const char *objectStr, int subId,
+ Oid auxiliaryId, bool is_internal);
+extern bool RunNamespaceSearchHookStr(const char *objectStr, bool ereport_on_violation);
+extern void RunFunctionExecuteHookStr(const char *objectStr);
+
+
/*
* The following macros are wrappers around the functions above; these should
* normally be used to invoke the hook in lieu of calling the above functions
@@ -194,4 +214,52 @@ extern void RunFunctionExecuteHook(Oid objectId);
RunFunctionExecuteHook(objectId); \
} while(0)
+
+#define InvokeObjectPostCreateHookStr(classId,objectName,subId) \
+ InvokeObjectPostCreateHookArgStr((classId),(objectName),(subId),false)
+#define InvokeObjectPostCreateHookArgStr(classId,objectName,subId,is_internal) \
+ do { \
+ if (object_access_hook_str) \
+ RunObjectPostCreateHookStr((classId),(objectName),(subId), \
+ (is_internal)); \
+ } while(0)
+
+#define InvokeObjectDropHookStr(classId,objectId,subId) \
+ InvokeObjectDropHookArgStr((classId),(objectName),(subId),0)
+#define InvokeObjectDropHookArgStr(classId,objectName,subId,dropflags) \
+ do { \
+ if (object_access_hook_str) \
+ RunObjectDropHookStr((classId),(objectName),(subId), \
+ (dropflags)); \
+ } while(0)
+
+#define InvokeObjectTruncateHookStr(objectName) \
+ do { \
+ if (object_access_hook_str) \
+ RunObjectTruncateHookStr(objectName); \
+ } while(0)
+
+#define InvokeObjectPostAlterHookStr(className,objectName,subId) \
+ InvokeObjectPostAlterHookArgStr((classId),(objectName),(subId), \
+ InvalidOid,false)
+#define InvokeObjectPostAlterHookArgStr(classId,objectName,subId, \
+ auxiliaryId,is_internal) \
+ do { \
+ if (object_access_hook_str) \
+ RunObjectPostAlterHookStr((classId),(objectName),(subId), \
+ (auxiliaryId),(is_internal)); \
+ } while(0)
+
+#define InvokeNamespaceSearchHookStr(objectName, ereport_on_violation) \
+ (!object_access_hook_str \
+ ? true \
+ : RunNamespaceSearchHookStr((objectName), (ereport_on_violation)))
+
+#define InvokeFunctionExecuteHookStr(objectName) \
+ do { \
+ if (object_access_hook_str) \
+ RunFunctionExecuteHookStr(objectName); \
+ } while(0)
+
+
#endif /* OBJECTACCESS_H */
--
2.35.1
On Thu, Mar 17, 2022 at 12:30 PM Joshua Brindle
<joshua.brindle@crunchydata.com> wrote:
I agree with this view, outside of the mixup between MAC and DAC (DAC
is in-core, MAC is via hooks)
An excellent point! Exactly why we need expert-level help with this stuff! :-)
So there should be some committers or contributors that keep an eye
out and make sure new objects have appropriate hooks, and since an Oid
based hook for GUCs is inappropriate, I hope this patch can be rolled
into the contribution from Mark.
I'm not going to take a position on this patch right this minute, but
I appreciate you providing it.
--
Robert Haas
EDB: http://www.enterprisedb.com
On Mar 17, 2022, at 9:29 AM, Joshua Brindle <joshua.brindle@crunchydata.com> wrote:
I hope this patch can be rolled
into the contribution from Mark.
Working on it.... Thanks for the patch!
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Thu, Mar 17, 2022 at 12:36 PM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:
On Mar 17, 2022, at 9:29 AM, Joshua Brindle <joshua.brindle@crunchydata.com> wrote:
I hope this patch can be rolled
into the contribution from Mark.Working on it.... Thanks for the patch!
Great, thanks.
I missed one objectId reference (InvokeObjectDropHookStr), fixed
version attached.
Attachments:
v2-0001-Add-String-object-access-hooks.patchapplication/octet-stream; name=v2-0001-Add-String-object-access-hooks.patchDownload
From dd0ed26c6eb8ff454ce5faf0cc743b3a0d287c3c Mon Sep 17 00:00:00 2001
From: Joshua Brindle <joshua.brindle@crunchydata.com>
Date: Thu, 17 Mar 2022 09:24:57 -0700
Subject: [PATCH] Add String object access hooks
The first user of these will be the GUC access controls
Signed-off-by: Joshua Brindle <joshua.brindle@crunchydata.com>
---
src/backend/catalog/objectaccess.c | 128 +++++++++++++++++++++++++++++
src/backend/utils/misc/guc.c | 4 +-
src/include/catalog/objectaccess.h | 70 +++++++++++++++-
3 files changed, 199 insertions(+), 3 deletions(-)
diff --git a/src/backend/catalog/objectaccess.c b/src/backend/catalog/objectaccess.c
index 549fac4539..72ad6e9a90 100644
--- a/src/backend/catalog/objectaccess.c
+++ b/src/backend/catalog/objectaccess.c
@@ -20,6 +20,8 @@
* and logging plugins.
*/
object_access_hook_type object_access_hook = NULL;
+object_access_hook_type_str object_access_hook_str = NULL;
+
/*
* RunObjectPostCreateHook
@@ -143,3 +145,129 @@ RunFunctionExecuteHook(Oid objectId)
ProcedureRelationId, objectId, 0,
NULL);
}
+
+/* String versions */
+
+
+/*
+ * RunObjectPostCreateHook
+ *
+ * It is entrypoint of OAT_POST_CREATE event
+ */
+void
+RunObjectPostCreateHookStr(Oid classId, const char *objectName, int subId,
+ bool is_internal)
+{
+ ObjectAccessPostCreate pc_arg;
+
+ /* caller should check, but just in case... */
+ Assert(object_access_hook_str != NULL);
+
+ memset(&pc_arg, 0, sizeof(ObjectAccessPostCreate));
+ pc_arg.is_internal = is_internal;
+
+ (*object_access_hook_str) (OAT_POST_CREATE,
+ classId, objectName, subId,
+ (void *) &pc_arg);
+}
+
+/*
+ * RunObjectDropHook
+ *
+ * It is entrypoint of OAT_DROP event
+ */
+void
+RunObjectDropHookStr(Oid classId, const char *objectName, int subId,
+ int dropflags)
+{
+ ObjectAccessDrop drop_arg;
+
+ /* caller should check, but just in case... */
+ Assert(object_access_hook_str != NULL);
+
+ memset(&drop_arg, 0, sizeof(ObjectAccessDrop));
+ drop_arg.dropflags = dropflags;
+
+ (*object_access_hook_str) (OAT_DROP,
+ classId, objectName, subId,
+ (void *) &drop_arg);
+}
+
+/*
+ * RunObjectTruncateHook
+ *
+ * It is the entrypoint of OAT_TRUNCATE event
+ */
+void
+RunObjectTruncateHookStr(const char *objectName)
+{
+ /* caller should check, but just in case... */
+ Assert(object_access_hook_str != NULL);
+
+ (*object_access_hook_str) (OAT_TRUNCATE,
+ RelationRelationId, objectName, 0,
+ NULL);
+}
+
+/*
+ * RunObjectPostAlterHook
+ *
+ * It is entrypoint of OAT_POST_ALTER event
+ */
+void
+RunObjectPostAlterHookStr(Oid classId, const char *objectName, int subId,
+ Oid auxiliaryId, bool is_internal)
+{
+ ObjectAccessPostAlter pa_arg;
+
+ /* caller should check, but just in case... */
+ Assert(object_access_hook_str != NULL);
+
+ memset(&pa_arg, 0, sizeof(ObjectAccessPostAlter));
+ pa_arg.auxiliary_id = auxiliaryId;
+ pa_arg.is_internal = is_internal;
+
+ (*object_access_hook_str) (OAT_POST_ALTER,
+ classId, objectName, subId,
+ (void *) &pa_arg);
+}
+
+/*
+ * RunNamespaceSearchHook
+ *
+ * It is entrypoint of OAT_NAMESPACE_SEARCH event
+ */
+bool
+RunNamespaceSearchHookStr(const char *objectName, bool ereport_on_violation)
+{
+ ObjectAccessNamespaceSearch ns_arg;
+
+ /* caller should check, but just in case... */
+ Assert(object_access_hook_str != NULL);
+
+ memset(&ns_arg, 0, sizeof(ObjectAccessNamespaceSearch));
+ ns_arg.ereport_on_violation = ereport_on_violation;
+ ns_arg.result = true;
+
+ (*object_access_hook_str) (OAT_NAMESPACE_SEARCH,
+ NamespaceRelationId, objectName, 0,
+ (void *) &ns_arg);
+
+ return ns_arg.result;
+}
+
+/*
+ * RunFunctionExecuteHook
+ *
+ * It is entrypoint of OAT_FUNCTION_EXECUTE event
+ */
+void
+RunFunctionExecuteHookStr(const char *objectName)
+{
+ /* caller should check, but just in case... */
+ Assert(object_access_hook_str != NULL);
+
+ (*object_access_hook_str) (OAT_FUNCTION_EXECUTE,
+ ProcedureRelationId, objectName, 0,
+ NULL);
+}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 683031db9a..8edee70394 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -8853,7 +8853,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
* transactional. If the hook aborts our transaction, it will be cleaner
* to do so before we touch any files.
*/
- InvokeObjectPostAlterHookArg(SettingAclRelationId, settingId,
+ InvokeObjectPostAlterHookArgStr(SettingAclRelationId, name,
ACL_ALTER_SYSTEM, altersysstmt->setstmt->kind,
false);
@@ -9057,7 +9057,7 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
* abuse the notion of subId to pass the kind of alteration (set vs. alter
* system), and auxiliaryId to pass the VariableSetKind.
*/
- InvokeObjectPostAlterHookArg(SettingAclRelationId, settingId,
+ InvokeObjectPostAlterHookArgStr(SettingAclRelationId, stmt->name,
ACL_SET_VALUE, stmt->kind, false);
}
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index 508dfd0a6b..4d54ae2a7d 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -121,15 +121,23 @@ typedef struct
bool result;
} ObjectAccessNamespaceSearch;
-/* Plugin provides a hook function matching this signature. */
+/* Plugin provides a hook function matching one or both of these signatures. */
typedef void (*object_access_hook_type) (ObjectAccessType access,
Oid classId,
Oid objectId,
int subId,
void *arg);
+typedef void (*object_access_hook_type_str) (ObjectAccessType access,
+ Oid classId,
+ const char *objectStr,
+ int subId,
+ void *arg);
+
/* Plugin sets this variable to a suitable hook function. */
extern PGDLLIMPORT object_access_hook_type object_access_hook;
+extern PGDLLIMPORT object_access_hook_type_str object_access_hook_str;
+
/* Core code uses these functions to call the hook (see macros below). */
extern void RunObjectPostCreateHook(Oid classId, Oid objectId, int subId,
@@ -142,6 +150,18 @@ extern void RunObjectPostAlterHook(Oid classId, Oid objectId, int subId,
extern bool RunNamespaceSearchHook(Oid objectId, bool ereport_on_violation);
extern void RunFunctionExecuteHook(Oid objectId);
+/* String versions */
+extern void RunObjectPostCreateHookStr(Oid classId, const char *objectStr, int subId,
+ bool is_internal);
+extern void RunObjectDropHookStr(Oid classId, const char *objectStr, int subId,
+ int dropflags);
+extern void RunObjectTruncateHookStr(const char *objectStr);
+extern void RunObjectPostAlterHookStr(Oid classId, const char *objectStr, int subId,
+ Oid auxiliaryId, bool is_internal);
+extern bool RunNamespaceSearchHookStr(const char *objectStr, bool ereport_on_violation);
+extern void RunFunctionExecuteHookStr(const char *objectStr);
+
+
/*
* The following macros are wrappers around the functions above; these should
* normally be used to invoke the hook in lieu of calling the above functions
@@ -194,4 +214,52 @@ extern void RunFunctionExecuteHook(Oid objectId);
RunFunctionExecuteHook(objectId); \
} while(0)
+
+#define InvokeObjectPostCreateHookStr(classId,objectName,subId) \
+ InvokeObjectPostCreateHookArgStr((classId),(objectName),(subId),false)
+#define InvokeObjectPostCreateHookArgStr(classId,objectName,subId,is_internal) \
+ do { \
+ if (object_access_hook_str) \
+ RunObjectPostCreateHookStr((classId),(objectName),(subId), \
+ (is_internal)); \
+ } while(0)
+
+#define InvokeObjectDropHookStr(classId,objectName,subId) \
+ InvokeObjectDropHookArgStr((classId),(objectName),(subId),0)
+#define InvokeObjectDropHookArgStr(classId,objectName,subId,dropflags) \
+ do { \
+ if (object_access_hook_str) \
+ RunObjectDropHookStr((classId),(objectName),(subId), \
+ (dropflags)); \
+ } while(0)
+
+#define InvokeObjectTruncateHookStr(objectName) \
+ do { \
+ if (object_access_hook_str) \
+ RunObjectTruncateHookStr(objectName); \
+ } while(0)
+
+#define InvokeObjectPostAlterHookStr(className,objectName,subId) \
+ InvokeObjectPostAlterHookArgStr((classId),(objectName),(subId), \
+ InvalidOid,false)
+#define InvokeObjectPostAlterHookArgStr(classId,objectName,subId, \
+ auxiliaryId,is_internal) \
+ do { \
+ if (object_access_hook_str) \
+ RunObjectPostAlterHookStr((classId),(objectName),(subId), \
+ (auxiliaryId),(is_internal)); \
+ } while(0)
+
+#define InvokeNamespaceSearchHookStr(objectName, ereport_on_violation) \
+ (!object_access_hook_str \
+ ? true \
+ : RunNamespaceSearchHookStr((objectName), (ereport_on_violation)))
+
+#define InvokeFunctionExecuteHookStr(objectName) \
+ do { \
+ if (object_access_hook_str) \
+ RunFunctionExecuteHookStr(objectName); \
+ } while(0)
+
+
#endif /* OBJECTACCESS_H */
--
2.35.1
On Mar 17, 2022, at 8:41 AM, Andrew Dunstan <andrew@dunslane.net> wrote:
If we abandoned that for this form of GRANT/REVOKE I think we could
probably get away withGRANT { SET | ALTER SYSTEM } ON setting_name ...
I haven't tried it, so I could be all wrong.
Version 12 of the patch uses SET and ALTER SYSTEM as the names of the privileges, and PARAMETER as the name of the thing on which the privilege is granted. The catalog table which tracks these grants is now named pg_parameter_acl, and various other parts of the patch have been adjusted to use a "parameter" based, rather than a "setting" based, naming scheme. One exception to this rule is the "setacl" column in pg_parameter_acl, which is much more compact than the "parameteracl" name would be, so that remains under the old name.
The tab completion for grants and revokes of "set" and "alter system" on parameters has been extended compared to the previously submitted patch. The tab completion for grants and revokes of other privilege types remains fairly thin, as I resisted the urge to fix that generally, given that it is unrelated to this patch. However, in making tab completion for grant/revoke on parameters work, this patch incidentally improves the coverage for other privilege types, in so far as the prefixes for those commands match the prefixes of these new commands.
Version 12 also introduces \dcp (pneumonic, "Describe Configuration Parameter") for listing parameters, with \dcp+ also showing the acl, like:
mark.dilger=# \dcp+ %client%
List of configuration parameters
Parameter | Context | Setting | Unit | Access privileges
----------------------------------+---------+---------+------+--------------------------------
client_connection_check_interval | user | 0 | ms |
client_encoding | user | UTF8 | | "mark.dilger"=sA/"mark.dilger"
client_min_messages | user | notice | | "mark.dilger"=sA/"mark.dilger"+
| | | | =s/"mark.dilger" +
| | | | alice=A*/"mark.dilger"
(3 rows)
The "Access privileges" column is blank for parameters which have neither been granted nor revoked. In the example above, that's true for client_connection_check_interval.
\dcp[+] only shows "user" and "superuser" parameters:
mark.dilger=# \dcp %wal%
List of configuration parameters
Parameter | Context
--------------------------+-----------
track_wal_io_timing | superuser
wal_compression | superuser
wal_consistency_checking | superuser
wal_init_zero | superuser
wal_recycle | superuser
wal_sender_timeout | user
wal_skip_threshold | user
(7 rows)
whereas \dcpS[+] also shows all parameters (so, also "sighup", "backend", "superuser-backend", "postmaster", and "internal".)
mark.dilger=# \dcpS %wal%
List of configuration parameters
Parameter | Context
-------------------------------+------------
max_slot_wal_keep_size | sighup
max_wal_senders | postmaster
max_wal_size | sighup
min_wal_size | sighup
track_wal_io_timing | superuser
wal_block_size | internal
wal_buffers | postmaster
wal_compression | superuser
wal_consistency_checking | superuser
wal_init_zero | superuser
wal_keep_size | sighup
wal_level | postmaster
wal_log_hints | postmaster
wal_receiver_create_temp_slot | sighup
wal_receiver_status_interval | sighup
wal_receiver_timeout | sighup
wal_recycle | superuser
wal_retrieve_retry_interval | sighup
wal_segment_size | internal
wal_sender_timeout | user
wal_skip_threshold | user
wal_sync_method | sighup
wal_writer_delay | sighup
wal_writer_flush_after | sighup
(24 rows)
Attachments:
v12-0001-Allow-grant-and-revoke-of-privileges-on-paramete.patchapplication/octet-stream; name=v12-0001-Allow-grant-and-revoke-of-privileges-on-paramete.patch; x-unix-mode=0644Download
From 3572e5f02dcb0bf7c9215ba1c2920e9533cde684 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Mon, 21 Mar 2022 17:48:09 -0700
Subject: [PATCH v12] 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.
Add new \dcp command in psql to show configuration parameter
privileges and other properties.
---
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 | 261 +++++++++++++
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 | 289 ++++++++++++++
src/backend/utils/cache/lsyscache.c | 20 +
src/backend/utils/cache/syscache.c | 23 ++
src/backend/utils/misc/guc.c | 195 +++++++--
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/command.c | 10 +-
src/bin/psql/describe.c | 69 ++++
src/bin/psql/describe.h | 4 +
src/bin/psql/help.c | 1 +
src/bin/psql/tab-complete.c | 178 ++++++++-
src/include/catalog/dependency.h | 1 +
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 | 3 +
src/include/utils/lsyscache.h | 1 +
src/include/utils/syscache.h | 2 +
.../expected/test_oat_hooks.out | 4 +-
.../modules/test_oat_hooks/test_oat_hooks.c | 6 +-
src/test/modules/test_pg_dump/t/001_base.pl | 60 +++
src/test/regress/expected/guc_privs.out | 369 ++++++++++++++++++
src/test/regress/expected/privileges.out | 56 ++-
src/test/regress/expected/rules.out | 13 +
src/test/regress/parallel_schedule | 2 +-
src/test/regress/sql/guc_privs.sql | 168 ++++++++
src/test/regress/sql/privileges.sql | 44 ++-
50 files changed, 2331 insertions(+), 68 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/regress/expected/guc_privs.out
create mode 100644 src/test/regress/sql/guc_privs.sql
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 2a8cd02664..44602d7026 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>
@@ -12578,11 +12641,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..0c98354351 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><literal>\dcp+</literal></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 8a802fb225..774501de1b 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 a897712de2..c12f243748 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..c95be04b48 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,139 @@ 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);
+ 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);
+
+ /*
+ * If the acl is null, we need to create a more permissive default acl
+ * for userset variables than for any others.
+ */
+ if (isNull)
+ {
+ const char *parameter;
+
+ parameter = text_to_cstring(&pg_parameter_acl_tuple->parameter);
+ old_acl = aclparameterdefault(find_option_context(parameter) == 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 */
+ InvokeObjectPostAlterHook(ParameterAclRelationId, parameterId, 0);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ table_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3255,6 +3434,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 +3475,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 +3515,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 +3758,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 +4195,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 +4961,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 ab9e42d7d1..e832458dd9 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 */
@@ -1504,6 +1506,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:
@@ -2862,6 +2865,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 d7ce063997..45957a4b0b 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"
@@ -2281,6 +2282,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:
@@ -3866,6 +3868,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;
@@ -4531,6 +4549,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;
@@ -5677,6 +5699,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..1fab58105e
--- /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 */
+ InvokeObjectPostCreateHook(ParameterAclRelationId, parameterId, 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 bd48ee7bd2..ab9fe6bb10 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -595,6 +595,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 80faae985e..af9be26c9b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -12654,6 +12654,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 0036c2f9e2..89013ec967 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -363,8 +363,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
@@ -716,7 +716,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
@@ -6981,6 +6981,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:
@@ -7014,6 +7028,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;
+ }
;
@@ -7073,6 +7117,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);
@@ -7082,6 +7133,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.
@@ -15860,6 +15932,7 @@ unreserved_keyword:
| OWNED
| OWNER
| PARALLEL
+ | PARAMETER
| PARSER
| PARTIAL
| PARTITION
@@ -16438,6 +16511,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 0a16f8156c..91099ca7a6 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,17 @@ 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 = text_to_cstring(PG_GETARG_TEXT_P(0));
+
+ PG_RETURN_ACL_P(aclparameterdefault(find_option_context(parameter) ==
+ PGC_USERSET));
+}
/*
* Update an ACL array to add or remove specified privileges.
@@ -1602,6 +1661,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 +1761,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 +4496,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
@@ -4670,6 +4922,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 f4e7819f1e..140d7e4294 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.c b/src/backend/utils/misc/guc.c
index f70f7f5c01..27ae71546f 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"
@@ -5057,6 +5058,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",
@@ -5456,25 +5461,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().)
+ *
+ * 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"
@@ -5491,8 +5500,25 @@ 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;
+}
+
+
+/*
+ * 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().)
+ */
+static bool
+valid_custom_variable_name(const char *name)
+{
+ int partcnt;
+
+ return (valid_variable_name(name, &partcnt) && partcnt > 1);
}
/*
@@ -7546,6 +7572,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\"",
@@ -7554,7 +7598,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;
}
@@ -8144,6 +8216,23 @@ set_config_option(const char *name, const char *value,
}
+/*
+ * Get the context required to set the variable, or -1 if the given name cannot
+ * be found.
+ */
+GucContext
+find_option_context(const char *name)
+{
+ struct config_generic *conf;
+
+ conf = find_option(name, false, false, ERROR);
+ if (conf)
+ return conf->context;
+
+ return -1; /* Not reached */
+}
+
+
/*
* Set the fields for source file and line number the setting came from.
*/
@@ -8588,6 +8677,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
{
char *name;
char *value;
+ Oid parameterId = InvalidOid;
bool resetall = false;
ConfigVariable *head = NULL;
ConfigVariable *tail = NULL;
@@ -8595,16 +8685,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:
@@ -8738,16 +8843,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,
@@ -8809,6 +8915,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
@@ -8819,6 +8928,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:
@@ -8827,7 +8954,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;
@@ -8912,7 +9039,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;
@@ -8921,9 +9048,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);
}
/*
@@ -9684,6 +9811,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, false, 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,
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 e5816c4cce..5367e6e9fc 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -14206,6 +14206,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",
@@ -14249,7 +14252,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/command.c b/src/bin/psql/command.c
index 079f4a1a76..e2889435ca 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -780,7 +780,15 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
success = describeTablespaces(pattern, show_verbose);
break;
case 'c':
- success = listConversions(pattern, show_verbose, show_system);
+ switch (cmd[2])
+ {
+ case 'p':
+ success = describeConfigurationParameters(pattern, show_verbose, show_system);
+ break;
+ default:
+ success = listConversions(pattern, show_verbose, show_system);
+ break;
+ }
break;
case 'C':
success = listCasts(pattern, show_verbose);
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 714097cad1..9572f28d24 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -738,6 +738,75 @@ map_typename_pattern(const char *pattern)
}
+/*
+ * \dcp
+ * describe configuration parameters
+ */
+bool
+describeConfigurationParameters(const char *pattern, bool verbose,
+ bool showSystem)
+{
+ PQExpBufferData buf;
+ PGresult *res;
+ printQueryOpt myopt = pset.popt;
+
+ initPQExpBuffer(&buf);
+
+ if (verbose)
+ {
+ printfPQExpBuffer(&buf,
+ "SELECT ss.lname AS \"%s\", ss.context AS \"%s\", "
+ "ss.setting AS \"%s\", ss.unit AS \"%s\", ",
+ gettext_noop("Parameter"),
+ gettext_noop("Context"),
+ gettext_noop("Setting"),
+ gettext_noop("Unit"));
+ printACLColumn(&buf, "ss.setacl");
+ appendPQExpBufferStr(&buf,
+ " FROM (SELECT pg_catalog.lower(s.name) AS lname, s.context,\n"
+ " s.setting, s.unit, p.setacl\n"
+ " FROM pg_catalog.pg_settings s\n"
+ " LEFT JOIN pg_catalog.pg_parameter_acl p\n"
+ " ON s.name = p.parameter\n");
+ if (!showSystem)
+ appendPQExpBufferStr(&buf, " AND s.context IN ('user', 'superuser')\n");
+ appendPQExpBuffer(&buf,
+ ") AS ss\n"
+ "WHERE ss.lname LIKE '%s'\n"
+ "ORDER BY 1, 2;",
+ pattern);
+ }
+ else
+ {
+ printfPQExpBuffer(&buf,
+ "SELECT pg_catalog.lower(s.name) AS \"%s\", s.context AS \"%s\"\n"
+ " FROM pg_catalog.pg_settings s\n"
+ " WHERE name LIKE '%s'\n",
+ gettext_noop("Parameter"),
+ gettext_noop("Context"),
+ pattern);
+ if (!showSystem)
+ appendPQExpBufferStr(&buf, " AND s.context IN ('user', 'superuser')\n");
+
+ appendPQExpBufferStr(&buf, "ORDER BY 1, 2;");
+ }
+
+ res = PSQLexec(buf.data);
+ termPQExpBuffer(&buf);
+ if (!res)
+ return false;
+
+ myopt.nullPrint = NULL;
+ myopt.title = _("List of configuration parameters");
+ myopt.translate_header = true;
+
+ printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
+
+ PQclear(res);
+ return true;
+}
+
+
/*
* \do
* Describe operators
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index fd6079679c..6c97b6719b 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -26,6 +26,10 @@ extern bool describeFunctions(const char *functypes, const char *func_pattern,
/* \dT */
extern bool describeTypes(const char *pattern, bool verbose, bool showSystem);
+/* \dcp */
+extern bool describeConfigurationParameters(const char *pattern, bool verbose,
+ bool showSystem);
+
/* \do */
extern bool describeOperators(const char *oper_pattern,
char **arg_patterns, int num_arg_patterns,
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 56afa6817e..8760315175 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -231,6 +231,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\dAp[+] [AMPTRN [OPFPTRN]] list support functions of operator families\n"));
fprintf(output, _(" \\db[+] [PATTERN] list tablespaces\n"));
fprintf(output, _(" \\dc[S+] [PATTERN] list conversions\n"));
+ fprintf(output, _(" \\dcp[+] [PATTERN] list configuration parameters\n"));
fprintf(output, _(" \\dC[+] [PATTERN] list casts\n"));
fprintf(output, _(" \\dd[S] [PATTERN] show object descriptions not displayed elsewhere\n"));
fprintf(output, _(" \\dD[S+] [PATTERN] list domains\n"));
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 5c064595a9..84f9611496 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3713,7 +3713,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
@@ -3725,8 +3729,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",
@@ -3739,12 +3746,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");
@@ -3761,7 +3895,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
@@ -3791,13 +3926,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");
/*
@@ -3806,7 +3943,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);
@@ -3844,6 +3982,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,
@@ -3855,7 +4012,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");
@@ -3864,7 +4022,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");
@@ -3873,7 +4032,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/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..8b9242d8c1
--- /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 d8e8715ed1..2f7d53020f 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',
@@ -7207,6 +7211,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 2f618cb229..127b6c4674 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
@@ -1816,6 +1816,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 bcef7eed2f..f5ed8c2082 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -307,6 +307,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..e3baa87535 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -381,7 +381,10 @@ 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);
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/test/modules/test_oat_hooks/expected/test_oat_hooks.out b/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out
index 45ff276f7e..716026dd1b 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
@@ -120,10 +120,10 @@ NOTICE: in object_access_hook_str: non-superuser finished alter (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"
RESET SESSION AUTHORIZATION;
NOTICE: in process utility: non-superuser attempting set
NOTICE: in object_access_hook_str: superuser attempting alter (set) [session_authorization]
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..7277ec6d88 100644
--- a/src/test/modules/test_oat_hooks/test_oat_hooks.c
+++ b/src/test/modules/test_oat_hooks/test_oat_hooks.c
@@ -250,7 +250,7 @@ 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)
{
if (REGRESS_deny_set_variable && !superuser_arg(GetUserId()))
ereport(ERROR,
@@ -265,7 +265,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,7 +860,7 @@ accesstype_to_string(ObjectAccessType access, int subId)
type = "UNRECOGNIZED ObjectAccessType";
}
- if (subId & ACL_SET_VALUE)
+ if (subId & ACL_SET)
return psprintf("%s (set)", type);
if (subId & ACL_ALTER_SYSTEM)
return psprintf("%s (alter system set)", type);
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 c73bd37835..ed6cb96f7e 100644
--- a/src/test/modules/test_pg_dump/t/001_base.pl
+++ b/src/test/modules/test_pg_dump/t/001_base.pl
@@ -317,6 +317,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/regress/expected/guc_privs.out b/src/test/regress/expected/guc_privs.out
new file mode 100644
index 0000000000..7e5f3d54ef
--- /dev/null
+++ b/src/test/regress/expected/guc_privs.out
@@ -0,0 +1,369 @@
+-- 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;
+-- 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
+-------------------------
+
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'zero_damaged_pages', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+
+(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 maintenance_work_mem
+privileges for parameter autovacuum_work_mem
+privileges for parameter hash_mem_multiplier
+privileges for parameter logical_decoding_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 maintenance_work_mem
+privileges for parameter autovacuum_work_mem
+privileges for parameter hash_mem_multiplier
+privileges for parameter logical_decoding_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 maintenance_work_mem
+privileges for parameter autovacuum_work_mem
+privileges for parameter hash_mem_multiplier
+privileges for parameter logical_decoding_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
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 291e21d7a6..c4a826e2c0 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -54,6 +54,36 @@ REVOKE pg_read_all_settings FROM regress_priv_user8;
DROP USER regress_priv_user10;
DROP USER regress_priv_user9;
DROP USER regress_priv_user8;
+GRANT SET ON PARAMETER enable_memoize TO regress_priv_user6;
+GRANT SET ON PARAMETER enable_nestloop TO regress_priv_user6;
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+GRANT ALTER SYSTEM ON PARAMETER enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON PARAMETER sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON PARAMETER maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON PARAMETER no_such_param TO regress_priv_user7; -- ok
+ERROR: unrecognized configuration parameter "no_such_param"
+GRANT ALTER SYSTEM ON PARAMETER no_such_extension.no_such_param TO regress_priv_user7; -- ok
+ERROR: unrecognized configuration parameter "no_such_extension.no_such_param"
+GRANT ALTER SYSTEM ON PARAMETER no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+ERROR: unrecognized configuration parameter "no_such_extension.no_such_param.longer.than.maximum.namedata.length"
+GRANT ALTER SYSTEM ON PARAMETER "" TO regress_priv_user7; -- bad name
+ERROR: zero-length delimited identifier at or near """"
+LINE 1: GRANT ALTER SYSTEM ON PARAMETER "" TO regress_priv_user7;
+ ^
+GRANT ALTER SYSTEM ON PARAMETER " " TO regress_priv_user7; -- bad name
+ERROR: invalid parameter name " "
+GRANT ALTER SYSTEM ON PARAMETER " foo " TO regress_priv_user7; -- bad name
+ERROR: invalid parameter name " foo "
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
ALTER GROUP regress_priv_group1 ADD USER regress_priv_user4;
@@ -2355,10 +2385,32 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+ERROR: role "regress_priv_user6" cannot be dropped because some objects depend on it
+DETAIL: privileges for parameter enable_memoize
+privileges for parameter enable_nestloop
+DROP USER regress_priv_user7; -- privileges remain
+ERROR: role "regress_priv_user7" cannot be dropped because some objects depend on it
+DETAIL: privileges for table persons2
+privileges for parameter enable_seqscan
+privileges for parameter work_mem
+privileges for parameter maintenance_work_mem
DROP USER regress_priv_user8; -- does not exist
ERROR: role "regress_priv_user8" does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON PARAMETER enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON PARAMETER work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON PARAMETER vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON PARAMETER no_such_param FROM regress_priv_user7; -- ok
+ERROR: unrecognized configuration parameter "no_such_param"
+REVOKE ALTER SYSTEM ON PARAMETER no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+ERROR: unrecognized configuration parameter "no_such_extension.no_such_param"
+REVOKE ALTER SYSTEM ON PARAMETER no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+ERROR: unrecognized configuration parameter "no_such_extension.no_such_param.longer.than.maximum.namedata.length"
+DROP USER regress_priv_user7; -- ok
+REVOKE SET ON PARAMETER enable_memoize FROM regress_priv_user6;
+REVOKE SET ON PARAMETER enable_nestloop FROM regress_priv_user6;
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
CREATE TABLE lock_table (a int);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 6cb6388880..02f7b7e59a 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1386,6 +1386,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,
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 6d8f524ae9..4dfa0642c0 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -86,7 +86,7 @@ test: brin_bloom brin_multi
# psql depends on create_am
# amutils depends on geometry, create_index_spgist, hash_index, brin
# ----------
-test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role
+test: create_table_like alter_generic alter_operator misc async dbsize misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role guc_privs
# collate.*.utf8 tests cannot be run in parallel with each other
test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8
diff --git a/src/test/regress/sql/guc_privs.sql b/src/test/regress/sql/guc_privs.sql
new file mode 100644
index 0000000000..cfa557e143
--- /dev/null
+++ b/src/test/regress/sql/guc_privs.sql
@@ -0,0 +1,168 @@
+-- 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;
+-- 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
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index c8c545b64c..5fea5f85d5 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -66,6 +66,33 @@ DROP USER regress_priv_user10;
DROP USER regress_priv_user9;
DROP USER regress_priv_user8;
+GRANT SET ON PARAMETER enable_memoize TO regress_priv_user6;
+GRANT SET ON PARAMETER enable_nestloop TO regress_priv_user6;
+
+SET ROLE regress_priv_user6;
+SET enable_memoize TO false;
+SET enable_nestloop TO false;
+RESET enable_memoize;
+RESET enable_nestloop;
+RESET ROLE;
+
+GRANT ALTER SYSTEM ON PARAMETER enable_seqscan TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON PARAMETER sort_mem TO regress_priv_user7; -- old name for "work_mem"
+GRANT ALTER SYSTEM ON PARAMETER maintenance_work_mem TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON PARAMETER no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON PARAMETER no_such_extension.no_such_param TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON PARAMETER no_such_extension.no_such_param.longer.than.maximum.namedata.length TO regress_priv_user7; -- ok
+GRANT ALTER SYSTEM ON PARAMETER "" TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON PARAMETER " " TO regress_priv_user7; -- bad name
+GRANT ALTER SYSTEM ON PARAMETER " foo " TO regress_priv_user7; -- bad name
+
+GRANT SELECT ON public.persons2 TO regress_priv_user7;
+
+SET ROLE regress_priv_user7;
+ALTER SYSTEM SET enable_seqscan = OFF;
+ALTER SYSTEM RESET enable_seqscan;
+RESET ROLE;
+
CREATE GROUP regress_priv_group1;
CREATE GROUP regress_priv_group2 WITH USER regress_priv_user1, regress_priv_user2;
@@ -1426,10 +1453,23 @@ DROP USER regress_priv_user2;
DROP USER regress_priv_user3;
DROP USER regress_priv_user4;
DROP USER regress_priv_user5;
-DROP USER regress_priv_user6;
-DROP USER regress_priv_user7;
+DROP USER regress_priv_user6; -- privileges remain
+DROP USER regress_priv_user7; -- privileges remain
DROP USER regress_priv_user8; -- does not exist
+REVOKE SELECT ON public.persons2 FROM regress_priv_user7;
+REVOKE ALTER SYSTEM ON PARAMETER enable_seqscan FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON PARAMETER work_mem FROM regress_priv_user7; -- ok, use new name
+REVOKE ALTER SYSTEM ON PARAMETER vacuum_mem FROM regress_priv_user7; -- ok, use old name
+REVOKE ALTER SYSTEM ON PARAMETER no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON PARAMETER no_such_extension.no_such_param FROM regress_priv_user7; -- ok
+REVOKE ALTER SYSTEM ON PARAMETER no_such_extension.no_such_param.longer.than.maximum.namedata.length FROM regress_priv_user7; -- ok
+DROP USER regress_priv_user7; -- ok
+
+REVOKE SET ON PARAMETER enable_memoize FROM regress_priv_user6;
+REVOKE SET ON PARAMETER enable_nestloop FROM regress_priv_user6;
+
+DROP USER regress_priv_user6; -- ok
-- permissions with LOCK TABLE
CREATE USER regress_locktable_user;
--
2.35.1
On 3/24/22 12:49, Mark Dilger wrote:
On Mar 17, 2022, at 8:41 AM, Andrew Dunstan <andrew@dunslane.net> wrote:
If we abandoned that for this form of GRANT/REVOKE I think we could
probably get away withGRANT { SET | ALTER SYSTEM } ON setting_name ...
I haven't tried it, so I could be all wrong.
Version 12 of the patch uses SET and ALTER SYSTEM as the names of the privileges, and PARAMETER as the name of the thing on which the privilege is granted. The catalog table which tracks these grants is now named pg_parameter_acl, and various other parts of the patch have been adjusted to use a "parameter" based, rather than a "setting" based, naming scheme. One exception to this rule is the "setacl" column in pg_parameter_acl, which is much more compact than the "parameteracl" name would be, so that remains under the old name.
I can live with it I guess, but it seems perverse to me to have
pg_settings but pg_paramater_acl effectively referring to the same set
of things. If we're going to do this perhaps we should create a
pg_parameters view which is identical to pg_settings and deprecate
pg_settings. I don;t want to hold up this patch, I think this can
probably be managed as a follow up item.
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
On Mar 24, 2022, at 12:06 PM, Andrew Dunstan <andrew@dunslane.net> wrote:
On 3/24/22 12:49, Mark Dilger wrote:
On Mar 17, 2022, at 8:41 AM, Andrew Dunstan <andrew@dunslane.net> wrote:
If we abandoned that for this form of GRANT/REVOKE I think we could
probably get away withGRANT { SET | ALTER SYSTEM } ON setting_name ...
I haven't tried it, so I could be all wrong.
Version 12 of the patch uses SET and ALTER SYSTEM as the names of the privileges, and PARAMETER as the name of the thing on which the privilege is granted. The catalog table which tracks these grants is now named pg_parameter_acl, and various other parts of the patch have been adjusted to use a "parameter" based, rather than a "setting" based, naming scheme. One exception to this rule is the "setacl" column in pg_parameter_acl, which is much more compact than the "parameteracl" name would be, so that remains under the old name.
I can live with it I guess, but it seems perverse to me to have
pg_settings but pg_paramater_acl effectively referring to the same set
of things. If we're going to do this perhaps we should create a
pg_parameters view which is identical to pg_settings and deprecate
pg_settings. I don;t want to hold up this patch, I think this can
probably be managed as a follow up item.
Right, the version 12 patch was following Peter's and Tom's comments upthread:
On Mar 17, 2022, at 7:47 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Well, I still say that "SET" is sufficient for the one privilege name
(unless we really can't make Bison handle that, which I doubt). But
I'm willing to yield on using "ALTER SYSTEM" for the other.If we go with s/SETTING/PARAMETER/ as per your other message, then
that would be adequately consistent with the docs I think. So it'd
beGRANT { SET | ALTER SYSTEM } ON PARAMETER foo TO ...
and the new catalog would be pg_parameter_acl, and so on.
We could debate that again, but it seems awfully late in the development cycle. I'd rather just get this committed, barring any objections?
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
I've started reviewing this patch in earnest, and almost immediately
hit a serious problem: these regression tests aren't even a little bit
committable. For one thing, they fail if you do "make installcheck"
twice in a row. This seems to be because the first run leaves some
cruft behind in pg_parameter_acl that affects the results of
subsequent runs. But the bigger problem is that it is ABSOLUTELY NOT
OKAY to test ALTER SYSTEM during an "installcheck" run. That would be
true even if you cleaned up all the settings by the end of the run, as
the patch fails to do (and for extra demerit, it leaves a superuser
role laying around). Even transient effects on the behavior of
sessions in other DBs aren't acceptable in installcheck mode, IMO.
I think we probably have to trash the core-regression-tests part of
the patch altogether and instead use a TAP test for whatever testing
we want to do. It might be all right to test SET privileges without
testing ALTER SYSTEM, but I'm not sure there's much point in that.
regards, tom lane
On Mar 28, 2022, at 11:31 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I think we probably have to trash the core-regression-tests part of
the patch altogether and instead use a TAP test for whatever testing
we want to do. It might be all right to test SET privileges without
testing ALTER SYSTEM, but I'm not sure there's much point in that.
How about putting them under src/test/modules/unsafe_tests ?
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Mark Dilger <mark.dilger@enterprisedb.com> writes:
Version 12 also introduces \dcp (pneumonic, "Describe Configuration Parameter") for listing parameters, with \dcp+ also showing the acl, like:
The fact that that code is not dry behind the ears is painfully obvious.
It's not documented in psql-ref, not tested anywhere AFAICS, and its
handling of the pattern parameter is inconsistent with every other
\d command. The wildcard character should be * not %. It only
accidentally fails to dump core if no pattern is given, too.
\dcp[+] only shows "user" and "superuser" parameters:
Why make that restriction? Also, I find it astonishing that this doesn't
show the GUC's value by default. The non-plus form of the command seems
useless as it stands, or at least its intended use-case is so narrow
I can't see it. If we're to have it at all, it seems like it ought to
be a reasonably useful shortcut for interrogating pg_settings. I'd
expect the base set of columns to be name, value, and possibly unit;
then add ACL with +. I'm not sure that GucContext belongs in this at all,
but if it does, it's a + column.
On the whole perhaps this should be taken out again; it's a bit
late in the cycle to be introducing new features, especially ones
as subject to bikeshedding as a \d command is. My ideas about what
columns to show probably wouldn't match anyone else's ... and we
haven't even gotten to whether \dcp is an okay choice of name.
regards, tom lane
Mark Dilger <mark.dilger@enterprisedb.com> writes:
On Mar 28, 2022, at 11:31 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I think we probably have to trash the core-regression-tests part of
the patch altogether and instead use a TAP test for whatever testing
we want to do. It might be all right to test SET privileges without
testing ALTER SYSTEM, but I'm not sure there's much point in that.
How about putting them under src/test/modules/unsafe_tests ?
Ah, that could work; I'd forgotten about that subdirectory.
It'd still be a good idea if they didn't fail when run twice in a row.
regards, tom lane
On Mar 28, 2022, at 12:11 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Mark Dilger <mark.dilger@enterprisedb.com> writes:
Version 12 also introduces \dcp (pneumonic, "Describe Configuration Parameter") for listing parameters, with \dcp+ also showing the acl, like:
The fact that that code is not dry behind the ears is painfully obvious.
<snip>
On the whole perhaps this should be taken out again
I added the \dcp command after noticing that doc/src/sgml/ddl.sgml's "Summary of Access Privileges" table had an empty column for the "psql Command" column for the "PARAMETER" row. Maybe that's ok. I thought it would be better if I added a command for that.
I don't feel too strongly about it, so I'm removing \dcp from this patch with the intention of introducing it as a separate patch for the postgres 16 development cycle. For now, I'm just putting "none" in that table cell.
This patch also moves the grant/revoke tests in guc_privs.sql into the unsafe_tests directory, fixes them up to be repeatable, and drops the superuser role that had been negligently left around in prior patches. The tests that had been added in privileges.sql are simply removed.
Attachments:
v13-0001-Allow-grant-and-revoke-of-privileges-on-paramete.patchapplication/octet-stream; name=v13-0001-Allow-grant-and-revoke-of-privileges-on-paramete.patch; x-unix-mode=0644Download
From 2b691a658d6f54bb978b042fbdad0f4ade2fd3b0 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 v13] 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.
---
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 | 261 ++++++++++++
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 | 289 ++++++++++++++
src/backend/utils/cache/lsyscache.c | 20 +
src/backend/utils/cache/syscache.c | 23 ++
src/backend/utils/misc/guc.c | 195 +++++++--
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/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 | 3 +
src/include/utils/lsyscache.h | 1 +
src/include/utils/syscache.h | 2 +
.../expected/test_oat_hooks.out | 4 +-
.../modules/test_oat_hooks/test_oat_hooks.c | 6 +-
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 +
44 files changed, 2164 insertions(+), 63 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/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 94f01e4099..6f3ae55f92 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>
@@ -12672,11 +12735,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 8a802fb225..774501de1b 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 a897712de2..c12f243748 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..c95be04b48 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,139 @@ 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);
+ 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);
+
+ /*
+ * If the acl is null, we need to create a more permissive default acl
+ * for userset variables than for any others.
+ */
+ if (isNull)
+ {
+ const char *parameter;
+
+ parameter = text_to_cstring(&pg_parameter_acl_tuple->parameter);
+ old_acl = aclparameterdefault(find_option_context(parameter) == 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 */
+ InvokeObjectPostAlterHook(ParameterAclRelationId, parameterId, 0);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ table_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3255,6 +3434,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 +3475,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 +3515,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 +3758,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 +4195,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 +4961,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..1fab58105e
--- /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 */
+ InvokeObjectPostCreateHook(ParameterAclRelationId, parameterId, 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 9570a53e7b..8e5827af21 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 124b9961dc..6923408b30 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 9399fff610..a41165b255 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -364,8 +364,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
@@ -752,7 +752,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
@@ -7024,6 +7024,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:
@@ -7057,6 +7071,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;
+ }
;
@@ -7116,6 +7160,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);
@@ -7125,6 +7176,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.
@@ -16364,6 +16436,7 @@ unreserved_keyword:
| OWNED
| OWNER
| PARALLEL
+ | PARAMETER
| PARSER
| PARTIAL
| PARTITION
@@ -16956,6 +17029,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 0a16f8156c..91099ca7a6 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,17 @@ 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 = text_to_cstring(PG_GETARG_TEXT_P(0));
+
+ PG_RETURN_ACL_P(aclparameterdefault(find_option_context(parameter) ==
+ PGC_USERSET));
+}
/*
* Update an ACL array to add or remove specified privileges.
@@ -1602,6 +1661,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 +1761,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 +4496,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
@@ -4670,6 +4922,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.c b/src/backend/utils/misc/guc.c
index b86137dc38..32b1562732 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().)
+ *
+ * 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,8 +5512,25 @@ 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;
+}
+
+
+/*
+ * 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().)
+ */
+static bool
+valid_custom_variable_name(const char *name)
+{
+ int partcnt;
+
+ return (valid_variable_name(name, &partcnt) && partcnt > 1);
}
/*
@@ -7558,6 +7584,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 +7610,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 +8228,23 @@ set_config_option(const char *name, const char *value,
}
+/*
+ * Get the context required to set the variable, or -1 if the given name cannot
+ * be found.
+ */
+GucContext
+find_option_context(const char *name)
+{
+ struct config_generic *conf;
+
+ conf = find_option(name, false, false, ERROR);
+ if (conf)
+ return conf->context;
+
+ return -1; /* Not reached */
+}
+
+
/*
* Set the fields for source file and line number the setting came from.
*/
@@ -8600,6 +8689,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
{
char *name;
char *value;
+ Oid parameterId = InvalidOid;
bool resetall = false;
ConfigVariable *head = NULL;
ConfigVariable *tail = NULL;
@@ -8607,16 +8697,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:
@@ -8750,16 +8855,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 +8927,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 +8940,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 +8966,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 +9051,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 +9060,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 +9823,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, false, 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,
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 c97d3e87f0..a89ce866e1 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3734,7 +3734,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
@@ -3746,8 +3750,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",
@@ -3760,12 +3767,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");
@@ -3782,7 +3916,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
@@ -3812,13 +3947,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");
/*
@@ -3827,7 +3964,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);
@@ -3865,6 +4003,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,
@@ -3876,7 +4033,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");
@@ -3885,7 +4043,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");
@@ -3894,7 +4053,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/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..8b9242d8c1
--- /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 deb00307f6..22847a0826 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',
@@ -7207,6 +7211,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 6bf212b01a..972434c280 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
@@ -1976,6 +1976,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 10829941e0..3b0275dd48 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -317,6 +317,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..e3baa87535 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -381,7 +381,10 @@ 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);
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/test/modules/test_oat_hooks/expected/test_oat_hooks.out b/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out
index 45ff276f7e..716026dd1b 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
@@ -120,10 +120,10 @@ NOTICE: in object_access_hook_str: non-superuser finished alter (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"
RESET SESSION AUTHORIZATION;
NOTICE: in process utility: non-superuser attempting set
NOTICE: in object_access_hook_str: superuser attempting alter (set) [session_authorization]
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..7277ec6d88 100644
--- a/src/test/modules/test_oat_hooks/test_oat_hooks.c
+++ b/src/test/modules/test_oat_hooks/test_oat_hooks.c
@@ -250,7 +250,7 @@ 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)
{
if (REGRESS_deny_set_variable && !superuser_arg(GetUserId()))
ereport(ERROR,
@@ -265,7 +265,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,7 +860,7 @@ accesstype_to_string(ObjectAccessType access, int subId)
type = "UNRECOGNIZED ObjectAccessType";
}
- if (subId & ACL_SET_VALUE)
+ if (subId & ACL_SET)
return psprintf("%s (set)", type);
if (subId & ACL_ALTER_SYSTEM)
return psprintf("%s (alter system set)", type);
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 92e1a2f6d8..d0f4b59521 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1386,6 +1386,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
I just came across something odd in v12 that is still there in v13:
ExecGrant_Parameter uses InvokeObjectPostAlterHook not
InvokeObjectPostAlterHookArgStr. This seems pretty inconsistent.
Is there a good argument for it?
... or, for that matter, why is there any such call at all?
No other GRANT/REVOKE operation calls such a hook.
regards, tom lane
On Mar 28, 2022, at 2:16 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I just came across something odd in v12 that is still there in v13:
ExecGrant_Parameter uses InvokeObjectPostAlterHook not
InvokeObjectPostAlterHookArgStr. This seems pretty inconsistent.
Is there a good argument for it?
For SET and ALTER SYSTEM, the target of the action may not have an entry in pg_parameter_acl, nor an assigned Oid anywhere, so the only consistent way to pass the argument to the hook is by name. For GRANT/REVOKE, the parameter must have an Oid, at least by the time the hook gets called. Upthread there was some discussion of a hook not being able to assume a snapshot and working transaction, and hence not being able to query the catalogs. I would think that in a GRANT or REVOKE that hasn't already errored, the hook would have a transaction and could look up whatever it likes? There is a CommandCounterIncrement() call issued in objectNamesToOids() for new parameters, so by the time the hook is running it should be able to see the parameter.
Am I reasoning about this the wrong way?
... or, for that matter, why is there any such call at all?
No other GRANT/REVOKE operation calls such a hook.
I think ALTER DEFAULT PRIVILEGES does, though that's not quite the same thing. I don't have a strong opinion on this. Joshua, what's your take?
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Mark Dilger <mark.dilger@enterprisedb.com> writes:
On Mar 28, 2022, at 2:16 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I just came across something odd in v12 that is still there in v13:
ExecGrant_Parameter uses InvokeObjectPostAlterHook not
InvokeObjectPostAlterHookArgStr. This seems pretty inconsistent.
Is there a good argument for it?
For SET and ALTER SYSTEM, the target of the action may not have an entry
in pg_parameter_acl, nor an assigned Oid anywhere, so the only
consistent way to pass the argument to the hook is by name. For
GRANT/REVOKE, the parameter must have an Oid, at least by the time the
hook gets called.
Yeah, I know it's *possible* to make this work. The question is why is
it good to do it like this rather than to use the string API, now that
we have the latter. AFAICS this way just guarantees that the hook must
do a catalog lookup in order to figure out what you're talking about.
The core point here is that the actual identity of a GUC is its name.
Any OID that may exist in pg_parameter_acl is just a nonce alias that
means nothing to anybody. Anyone who's trying to, say, enforce that
Joe Blow can't change shared_buffers is going to need to see the GUC
name. (I am, btw, busy doing a lot of renaming in the patch to try
to clarify that these OIDs are not identifiers for GUCs; imagining
that they are just risks confusion.)
regards, tom lane
On Mar 28, 2022, at 2:54 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Yeah, I know it's *possible* to make this work. The question is why is
it good to do it like this rather than to use the string API, now that
we have the latter. AFAICS this way just guarantees that the hook must
do a catalog lookup in order to figure out what you're talking about.
Ok, thanks for clarifying. I took the *HookStr versions of the hooks to be an alternative to be used when no Oid was present, something of a last resort. I never thought much about using them under other circumstances.
The core point here is that the actual identity of a GUC is its name.
Any OID that may exist in pg_parameter_acl is just a nonce alias that
means nothing to anybody. Anyone who's trying to, say, enforce that
Joe Blow can't change shared_buffers is going to need to see the GUC
name. (I am, btw, busy doing a lot of renaming in the patch to try
to clarify that these OIDs are not identifiers for GUCs; imagining
that they are just risks confusion.)
I was about to write another patch using the HookStr form, but if you are already editing, then I'll let you make the change. I don't see a problem with what you are proposing.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
I'm going to be mostly unavailable till Wednesday, but I'll leave
you with another thing to chew on:
regression=# create user joe;
CREATE ROLE
regression=# grant set on parameter plpgsql.extra_warnings to joe;
ERROR: unrecognized configuration parameter "plpgsql.extra_warnings"
This is problematic, because once plpgsql is loaded it works:
regression=# load 'plpgsql';
LOAD
regression=# grant set on parameter plpgsql.extra_warnings to joe;
GRANT
If we then do
$ pg_dumpall -g
it falls over:
pg_dumpall: error: query failed: ERROR: unrecognized configuration parameter "plpgsql.extra_warnings"
apparently because aclparameterdefault() is allergic to being asked about
unknown GUCs, and plpgsql is not loaded in pg_dumpall's session. But
if pg_dumpall hadn't failed, it'd produce a dump script containing that
same command, which would fail at load time (because, again, plpgsql
isn't going to be loaded in the backend reading the restore script).
This is what I meant by saying that you can't just refuse to GRANT on
unknown GUCs. It makes custom GUCs into a time bomb for dump/restore.
And that means you need a strategy for dealing with the possibility
that you don't know whether the GUC is USERSET or not. I think though
that it might work to just assume that it isn't, in which case dumps
on unrecognized GUCs that really are USERSET will end up issuing an
explicit GRANT SET TO PUBLIC that we didn't actually need to do, but it
won't hurt anything. (Testing that assertion would be a good thing
to do.)
regards, tom lane
Mark Dilger <mark.dilger@enterprisedb.com> writes:
I was about to write another patch using the HookStr form, but if you are already editing, then I'll let you make the change. I don't see a problem with what you are proposing.
Don't sweat about that, I can easily rebase what I've done so far
over your updates.
regards, tom lane
On Mar 28, 2022, at 3:31 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
This is what I meant by saying that you can't just refuse to GRANT on
unknown GUCs. It makes custom GUCs into a time bomb for dump/restore.
And that means you need a strategy for dealing with the possibility
that you don't know whether the GUC is USERSET or not. I think though
that it might work to just assume that it isn't, in which case dumps
on unrecognized GUCs that really are USERSET will end up issuing an
explicit GRANT SET TO PUBLIC that we didn't actually need to do, but it
won't hurt anything. (Testing that assertion would be a good thing
to do.)
Ok, I returned to the idea upthread for a solution to this problem. A grant or revoke on an unrecognized custom parameter will create a SUSET placeholder, which is not quite right in some cases. However, the installation scripts for modules have been updated to manually grant SET privilege on their custom USERSET parameters, which cleans up the problem, with one exception: if the user executes a "revoke set on parameter some.such from public" prior to loading the module which defines parameter some.such, that revoke won't be retained. That doesn't seem entirely wrong to me, since no privilege to set the parameter existed when the revoke was performed, but rather was granted along with the creation of the parameter, but it also doesn't seem entirely right. Maybe revoke commands (but not grant commands) should error on unrecognized custom parameters? I didn't implement that here, but can do so if you think that makes more sense than this new behavior.
I changed add_placeholder_variable() to take a GucContext argument. It previously always used PGC_USERSET, which is what all pre-existing call sites now pass into it, but that seems a bit inappropriate where we're creating a placeholder that we intend to treat as a SUSET variable until such time as a module gets installed saying otherwise. Not changing add_placeholder_variable in this fashion seems to work just fine. I just didn't feel comfortable with doing it that way. But if you feel it generates needless code churn, I could be talked out of doing this.
I also changed the patch to use the ...HookStr functions for parameters. I would really like a comment on this from Joshua, to be sure what I'm doing comports with what he wanted. In particular, I'm uncertain that simply passing the AclMode (in other words, the istmt->privileges field) for the grant/revoke to the hook is sufficient. For one, how does the hook want to distinguish grants from revokes? Do we want a bit for that? And what about distinguishing WITH GRANT OPTION? I think the hooks are usable right now, but they might be made better.
Attachments:
v14-0001-Allow-grant-and-revoke-of-privileges-on-paramete.patchapplication/octet-stream; name=v14-0001-Allow-grant-and-revoke-of-privileges-on-paramete.patch; x-unix-mode=0644Download
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
On Tue, Mar 29, 2022 at 9:00 PM Mark Dilger <mark.dilger@enterprisedb.com>
wrote:
A grant or revoke on an unrecognized custom parameter will create a SUSET
placeholder,
[...]
which cleans up the problem, with one exception: if the user executes a
"revoke set on parameter some.such from public" prior to loading the module
which defines parameter some.such, that revoke won't be retained. That
doesn't seem entirely wrong to me, since no privilege to set the parameter
existed when the revoke was performed, but rather was granted along with
the creation of the parameter, but it also doesn't seem entirely right.
Maybe revoke commands (but not grant commands) should error on unrecognized
custom parameters?
The only revoke role target that makes sense here is the default grant
given to public. Aside from that technicality the grant system is purely
additive and so only the GRANTS end up retained so far as perpetual state
is concerned.
For a revoke that doesn't target public we should remove the corresponding
unrecognized setting grant if one is present. We don't generally/always
raise a notice if a revoke doesn't actually cause something to be ungranted
- though I'm partial to being so informed. I suppose we could distinguish
the cases where the not-yet-loaded setting name is unrecognized by the
system from the one where it is recognized but the grant is actually on a
different role (or a revoke entry from public for the setting name is
present).
For a revoke of an unknown setting from public we should keep an entry
somewhere that tells the system that the default grant to public for that
setting has been revoked. Maybe there isn't the same timing concern here
as there is for GRANT, but if only for symmetry it seems like a good thing
to implement.
I have the impression I'm missing something in what I wrote above but
cannot quite figure out what. In any case as a first pass at this the
behavior described is kinda what I'm expecting.
David J.
P.S.
Skimming the patch we are, to my agreement, not touching the ALTER DEFAULT
PRIVILEGES command to work with this feature. Should the omission be noted
explicitly? At least in the commit message I would think. Though the
sentence in [1]https://www.postgresql.org/docs/current/ddl-priv.html "Also, these default privilege settings can be overridden
using the ALTER DEFAULT PRIVILEGES command." is rendered only partially
correct.
[1]: https://www.postgresql.org/docs/current/ddl-priv.html
P.P.S.
+ 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.
Can we rephrase this to something like:
By default, PUBLIC has no privileges on parameters in the postmaster, ...,
and superuser contexts.
pg_settings.context exists and those are the values found there. My
initial interpretation of the wording was the postmaster, etc..., were
themselves parameters, not containers for many parameters.
On Wed, Mar 30, 2022 at 12:00 AM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:
On Mar 28, 2022, at 3:31 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
This is what I meant by saying that you can't just refuse to GRANT on
unknown GUCs. It makes custom GUCs into a time bomb for dump/restore.
And that means you need a strategy for dealing with the possibility
that you don't know whether the GUC is USERSET or not. I think though
that it might work to just assume that it isn't, in which case dumps
on unrecognized GUCs that really are USERSET will end up issuing an
explicit GRANT SET TO PUBLIC that we didn't actually need to do, but it
won't hurt anything. (Testing that assertion would be a good thing
to do.)Ok, I returned to the idea upthread for a solution to this problem. A grant or revoke on an unrecognized custom parameter will create a SUSET placeholder, which is not quite right in some cases. However, the installation scripts for modules have been updated to manually grant SET privilege on their custom USERSET parameters, which cleans up the problem, with one exception: if the user executes a "revoke set on parameter some.such from public" prior to loading the module which defines parameter some.such, that revoke won't be retained. That doesn't seem entirely wrong to me, since no privilege to set the parameter existed when the revoke was performed, but rather was granted along with the creation of the parameter, but it also doesn't seem entirely right. Maybe revoke commands (but not grant commands) should error on unrecognized custom parameters? I didn't implement that here, but can do so if you think that makes more sense than this new behavior.
I changed add_placeholder_variable() to take a GucContext argument. It previously always used PGC_USERSET, which is what all pre-existing call sites now pass into it, but that seems a bit inappropriate where we're creating a placeholder that we intend to treat as a SUSET variable until such time as a module gets installed saying otherwise. Not changing add_placeholder_variable in this fashion seems to work just fine. I just didn't feel comfortable with doing it that way. But if you feel it generates needless code churn, I could be talked out of doing this.
I also changed the patch to use the ...HookStr functions for parameters. I would really like a comment on this from Joshua, to be sure what I'm doing comports with what he wanted. In particular, I'm uncertain that simply passing the AclMode (in other words, the istmt->privileges field) for the grant/revoke to the hook is sufficient. For one, how does the hook want to distinguish grants from revokes? Do we want a bit for that? And what about distinguishing WITH GRANT OPTION? I think the hooks are usable right now, but they might be made better.
I had not even been thinking about hooking grant/revoke TBH. In
SELinux-type MAC systems we don't always try to control DAC permission
changes, and when we do it's not granular, all permission changes just
boil down to setattr permission or something of that nature.
Thank you.
After sleeping on it, I have a modest proposal for simplifying
these issues. Consider this design:
1. In the SET code path, we assume (without any catalog lookup)
that USERSET GUCs can be set. Only for SUSET GUCs do we perform
a permissions lookup. (ALTER SYSTEM does a lookup in both cases.)
2. Given this, the default ACL for any GUC can be empty, greatly
simplifying all these management issues. Superusers could do what
they want anyway, so modeling an "owner's default grant" becomes
unnecessary.
What this loses is the ability to revoke public SET permissions
on USERSET GUCs. I claim that that is not so valuable as to
justify all the complication needed to deal with it. (If a GUC
seems to require some defenses, why is it USERSET?) Avoiding
a permissions lookup in the default SET code path seems like
a pretty important benefit, too. If we force that to happen
it's going to be a noticeable drag on functions with SET clauses.
regards, tom lane
On Mar 30, 2022, at 6:26 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Consider this design:
Isn't that just the design I had implemented in v8 several months ago?
Subject: [PATCH v8] Allow GRANT of SET and ALTER SYSTEM SET for gucs
Allow granting of privilege to set or alter system set variables
which otherwise can only be managed by superusers. Each
(role,variable,privilege) triple is independently grantable, so a
user may be granted privilege to SET but not to ALTER SYSTEM SET on
a variable, or vice versa. The privilege to SET a userset variable
may be granted, though doing so has no practical effect, since any
role can set userset variables anyway. Worse, there is no way to
revoke the privilege to SET a userset variable. To remedy that,
most core userset variables have been changed to suset, with
explicit grants to set the variable to public.
I don't think v9 ever got posted to the list, but v10 has:
Subject: [PATCH v10] Allow grant and revoke of privileges on settings
Allow grant and revoke of privileges to set or alter system set
configuration variables. Each (role,variable,privilege) triple can
be independently granted or revoked, so a user may be granted
privilege to SET but not to ALTER SYSTEM SET on a variable, or vice
versa. Privilege to SET a userset variable is implicitly granted to
public, but may be revoked.
If we want to backtrack to v8, that's fine. I can rebase that, port some of the other changes from v14 to it, and repost it as v15. We should review the conversation from December and January which included some arguments for allowing revokes of SET on USERSET from PUBLIC. I don't want to keep going around in circles on this.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Mar 30, 2022, at 6:59 AM, Mark Dilger <mark.dilger@enterprisedb.com> wrote:
We should review the conversation from December and January which included some arguments for allowing revokes of SET on USERSET from PUBLIC. I don't want to keep going around in circles on this.
Hmm, I guess that conversation was mostly off-list at the PGConn in December. I made a reference to it upthread:
On Mar 6, 2022, at 2:40 PM, Mark Dilger <mark.dilger@enterprisedb.com> wrote:
Userset variables are implicitly settable by any user. There was a request, off-list as I recall, to make it possible to revoke the privilege to set variables such as "work_mem". To make that possible, but not change the default behavior vis-a-vis prior releases, I upgraded most userset variables to suset with a corresponding grant to public on the variable. Sites which wish to have a more restrictive policy on such variables can revoke that privilege from public and instead issue more restrictive grants. There were a few variables where such treatment didn't seem sensible, such as ones to do with client connections, and I left them alone. I didn't insist on a defense for why any particular setting needed to be revocable in order to apply this treatment. My assumption was that sites should be allowed to determine their own security policies per setting unless there is a technical difficulty for a given setting that would make it overly burdensome to implement.
Your proposal to just punt on supporting revocation of set on userset from public seems fine. We could revisit that in the next development cycle if anyone really wants to defend it. In particular, I don't see that committing this feature without that part would create any additional backward compatibility problems when implementing that later.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 3/30/22 09:26, Tom Lane wrote:
After sleeping on it, I have a modest proposal for simplifying
these issues. Consider this design:1. In the SET code path, we assume (without any catalog lookup)
that USERSET GUCs can be set. Only for SUSET GUCs do we perform
a permissions lookup. (ALTER SYSTEM does a lookup in both cases.)2. Given this, the default ACL for any GUC can be empty, greatly
simplifying all these management issues. Superusers could do what
they want anyway, so modeling an "owner's default grant" becomes
unnecessary.What this loses is the ability to revoke public SET permissions
on USERSET GUCs. I claim that that is not so valuable as to
justify all the complication needed to deal with it. (If a GUC
seems to require some defenses, why is it USERSET?) Avoiding
a permissions lookup in the default SET code path seems like
a pretty important benefit, too. If we force that to happen
it's going to be a noticeable drag on functions with SET clauses.
The last point is telling, so +1
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
On Wed, Mar 30, 2022 at 8:12 AM Andrew Dunstan <andrew@dunslane.net> wrote:
On 3/30/22 09:26, Tom Lane wrote:
What this loses is the ability to revoke public SET permissions
on USERSET GUCs. I claim that that is not so valuable as to
justify all the complication needed to deal with it.
Agreed, and in line with my thinking from last night. These default public
set grants are indeed the complication and I'm good with the status quo
where they are non-revocable.
I'm finding it curious that we are choosing to document every (all 6)
context that doesn't have this default privilege instead of saying that
only the user context variables are granted this default, and now
irrevocable, default set privilege. This is in addition to making sure we
distinguish between parameter and context in my earlier email.
Avoiding
a permissions lookup in the default SET code path seems like
a pretty important benefit, too. If we force that to happen
it's going to be a noticeable drag on functions with SET clauses.The last point is telling, so +1
Indeed. +1
David J.
Mark Dilger <mark.dilger@enterprisedb.com> writes:
Your proposal to just punt on supporting revocation of set on userset from public seems fine. We could revisit that in the next development cycle if anyone really wants to defend it. In particular, I don't see that committing this feature without that part would create any additional backward compatibility problems when implementing that later.
Yeah. Also, as you noted, we could mark some individual built-in
variables as SUSET and add a default GRANT. I don't want to do that with
a blunderbuss, but perhaps there's an argument to do it for specific
cases (search_path comes to mind, though the performance cost could be
significant, since I think setting that in function SET clauses is
common).
For now, though, saying that you can't restrict SET for USERSET variables
seems fine --- there's certainly no loss of capability compared to where
we stand today. I'd prefer to get the feature committed in that form
and then look at whether we want to tighten things around the margins.
regards, tom lane
On Wed, Mar 30, 2022 at 8:46 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
I don't want to do that with
a blunderbuss, but perhaps there's an argument to do it for specific
cases (search_path comes to mind, though the performance cost could be
significant, since I think setting that in function SET clauses is
common).
I suspect it became considerably moreso when we fixed the search_path CVE
since we basically told people that doing so, despite the possible
performance hit, was the easiest solution to their immediate dump/restore
failures. But ISTM that because that SET has a function invocation context
it could bypass any such check. Though maybe the DO command exposes a flaw
in that idea.
David J.
Mark Dilger <mark.dilger@enterprisedb.com> writes:
If we want to backtrack to v8, that's fine. I can rebase that, port
some of the other changes from v14 to it, and repost it as v15.
Are you working on that? I've set aside time this week to hopefully
get this over the finish line, but I don't want to find out that
I've been duplicating your effort.
regards, tom lane
On Apr 4, 2022, at 8:36 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Mark Dilger <mark.dilger@enterprisedb.com> writes:
If we want to backtrack to v8, that's fine. I can rebase that, port
some of the other changes from v14 to it, and repost it as v15.Are you working on that? I've set aside time this week to hopefully
get this over the finish line, but I don't want to find out that
I've been duplicating your effort.
Yes, I expect to be posting the latest in maybe an hour? I believe the latest patch (just reviewing, adjusting code comments, etc.) that I'm preparing to post has all the changes we've discussed, aside from your parameterId renaming.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Apr 4, 2022, at 8:47 AM, Mark Dilger <mark.dilger@enterprisedb.com> wrote:
posting the latest in maybe an hour
Here it is:
Attachments:
v15-0001-Allow-grant-and-revoke-of-privileges-on-paramete.patchapplication/octet-stream; name=v15-0001-Allow-grant-and-revoke-of-privileges-on-paramete.patch; x-unix-mode=0644Download
From 0cb2443149756ac9e159bc0ee499fab9a53facc6 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 v15] Allow grant and revoke of privileges on parameters
Add new SET and ALTER SYSTEM privileges for configuration parameters
(GUCs), and a new catalog, pg_parameter_acl, for tracking grants of
these privileges.
The privilege to SET a parameter marked USERSET is inherent in that
parameter's marking and cannot be revoked. This saves cycles when
performing SET operations, as looking up privileges in the catalog
can be skipped. If we find that administrators need to revoke SET
privilege on a particular variable from public, that variable can be
redefined in future releases as SUSET with a default grant of SET to
PUBLIC issued.
---
doc/src/sgml/catalogs.sgml | 84 ++-
doc/src/sgml/ddl.sgml | 51 +-
doc/src/sgml/func.sgml | 44 +-
doc/src/sgml/ref/alter_system.sgml | 3 +-
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 | 332 +++++++++++-
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 | 104 ++++
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 | 294 ++++++++++
src/backend/utils/cache/lsyscache.c | 20 +
src/backend/utils/cache/syscache.c | 23 +
src/backend/utils/misc/guc.c | 166 +++++-
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 | 10 +-
src/include/utils/guc.h | 4 +
src/include/utils/lsyscache.h | 1 +
src/include/utils/syscache.h | 2 +
.../expected/test_oat_hooks.out | 228 +++++---
.../test_oat_hooks/sql/test_oat_hooks.sql | 48 +-
.../modules/test_oat_hooks/test_oat_hooks.c | 80 ++-
src/test/modules/test_pg_dump/t/001_base.pl | 50 ++
src/test/modules/unsafe_tests/Makefile | 2 +-
.../unsafe_tests/expected/guc_privs.out | 506 ++++++++++++++++++
.../modules/unsafe_tests/sql/guc_privs.sql | 211 ++++++++
src/test/regress/expected/rules.out | 13 +
47 files changed, 2683 insertions(+), 125 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/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 7f4f79d1b5..269b06ac10 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,73 @@ 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>oid</structfield> <type>oid</type>
+ </para>
+ <para>
+ Row identifier
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>parameter</structfield> <type>text</type>
+ </para>
+ <para>
+ Name of the configuration parameter
+ </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 +12851,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..84771ab6c6 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
@@ -1977,6 +1998,10 @@ REVOKE ALL ON accounts FROM PUBLIC;
large objects,
schemas,
or tablespaces.
+ Some parameters are inherently settable by <literal>PUBLIC</literal>, as if
+ <literal>SET</literal> were granted, but this is not implemented as a grant,
+ nor can the ability for <literal>PUBLIC</literal> to set these parameters be
+ revoked.
For other types of objects, the default privileges
granted to <literal>PUBLIC</literal> are as follows:
<literal>CONNECT</literal> and <literal>TEMPORARY</literal> (create
@@ -2097,6 +2122,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>
@@ -2167,6 +2202,12 @@ REVOKE ALL ON accounts FROM PUBLIC;
<entry>none</entry>
<entry><literal>\dl+</literal></entry>
</row>
+ <row>
+ <entry><literal>PARAMETER</literal></entry>
+ <entry><literal>sA</literal></entry>
+ <entry>none</entry>
+ <entry>none</entry>
+ </row>
<row>
<entry><literal>SCHEMA</literal></entry>
<entry><literal>UC</literal></entry>
@@ -2274,6 +2315,14 @@ 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>
+ No privileges are required for <literal>PUBLIC</literal> to
+ <command>SET</command> and <command>RESET</command> a
+ <literal>user</literal> parameter. 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 4001cb2bda..3394d6e7e2 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -22701,8 +22701,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');
@@ -22836,6 +22835,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>
@@ -23145,6 +23161,30 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute');
</para></entry>
</row>
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>aclparameterdefault</primary>
+ </indexterm>
+ <function>aclparameterdefault</function> (
+ <parameter>parameter_name</parameter> <type>text</type> )
+ <returnvalue>aclitem[]</returnvalue>
+ </para>
+ <para>
+ Constructs an <type>aclitem</type> array holding the default access
+ privileges for the configuration parameter named
+ <parameter>parameter_name</parameter>. This represents the access
+ privileges that will be assumed when the parameter's ACL entry is null,
+ or when the parameter has no entry in the catalogs.
+ </para>
+ <para>
+ An unrecognized custom parameter name will be treated as if it is not a
+ <literal>user</literal> parameter. Loading an extension which defines
+ the parameter as a <literal>user</literal> parameter will cause the
+ default privileges returned by this function to change.
+ </para></entry>
+ </row>
+
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/doc/src/sgml/ref/alter_system.sgml b/doc/src/sgml/ref/alter_system.sgml
index 5e41f7f644..6f8bd39eaf 100644
--- a/doc/src/sgml/ref/alter_system.sgml
+++ b/doc/src/sgml/ref/alter_system.sgml
@@ -55,7 +55,8 @@ ALTER SYSTEM RESET ALL
</para>
<para>
- Only superusers can use <command>ALTER SYSTEM</command>. Also, since
+ Only superusers and users granted <literal>ALTER SYSTEM</literal> privilege
+ on a parameter can change it using <command>ALTER SYSTEM</command>. Also, since
this command acts directly on the file system and cannot be rolled back,
it is not allowed inside a transaction block or function.
</para>
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..d1387fdc5d 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,11 +113,14 @@ 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);
static List *objectNamesToOids(ObjectType objtype, List *objnames);
+static List *parameterNamesToOids(List *names, bool create_missing);
+
static List *objectsInSchemaToOids(ObjectType objtype, List *nspnames);
static List *getRelationsInNamespace(Oid namespaceId, char relkind);
static void expand_col_privileges(List *colnames, Oid table_oid,
@@ -259,6 +263,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 */
@@ -390,7 +397,22 @@ ExecuteGrantStmt(GrantStmt *stmt)
switch (stmt->targtype)
{
case ACL_TARGET_OBJECT:
- istmt.objects = objectNamesToOids(stmt->objtype, stmt->objects);
+ if (stmt->objtype == OBJECT_PARAMETER)
+ {
+ bool do_create = true;
+
+ /*
+ * If this is a revoke command, we needn't create entries in
+ * the catalogs for parameters that don't yet have them, as an
+ * empty Acl contains nothing to be removed.
+ */
+ if (!stmt->is_grant)
+ do_create = false;
+
+ istmt.objects = parameterNamesToOids(stmt->objects, do_create);
+ }
+ else
+ istmt.objects = objectNamesToOids(stmt->objtype, stmt->objects);
break;
case ACL_TARGET_ALL_IN_SCHEMA:
istmt.objects = objectsInSchemaToOids(stmt->objtype, stmt->objects);
@@ -498,6 +520,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 +626,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 +788,7 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_PARAMETER: /* should be handled by parameterNamesToOids */
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -767,6 +797,44 @@ objectNamesToOids(ObjectType objtype, List *objnames)
return objects;
}
+
+/*
+ * parameterNamesToOids
+ *
+ * Turn a list of parameter names into an Oid list. If requested, new catalog
+ * entries will be created for parameter names not yet tracked in the catalog.
+ * Otherwise, unrecognized names will be filtered from the list.
+ */
+static List *
+parameterNamesToOids(List *names, bool create_missing)
+{
+ List *objects = NIL;
+ ListCell *cell;
+
+ foreach(cell, names)
+ {
+ char *parameter = strVal(lfirst(cell));
+ Oid parameterId = get_parameter_oid(parameter, true);
+
+ if (!OidIsValid(parameterId) && create_missing)
+ {
+ 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();
+ }
+
+ if (OidIsValid(parameterId))
+ objects = lappend_oid(objects, parameterId);
+ }
+
+ return objects;
+}
+
/*
* objectsInSchemaToOids
*
@@ -1494,6 +1562,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 +3296,161 @@ 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;
+ 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;
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers = NULL;
+ Oid *newmembers = NULL;
+
+ 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, use the default for the parameter, by name. */
+ if (isNull)
+ {
+ old_acl = aclparameterdefault(parameter);
+ /* There are no old member roles according to the catalogs */
+ noldmembers = 0;
+ }
+ 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, being careful not to destroy the old ACL. */
+ new_acl = merge_acl_with_grant(aclcopy(old_acl), istmt->is_grant,
+ istmt->grant_option, istmt->behavior,
+ istmt->grantees, this_privileges,
+ grantorId, InvalidOid);
+
+ /* Make the Acl include irrevocable inherent privileges, if missing. */
+ new_acl = reconcile_parameter_privs(parameter, new_acl);
+
+ /*
+ * If the new Acl is equal to the default for this parameter, we don't
+ * need the catalog entry any longer. Delete it rather than updating
+ * it, to avoid leaving a degenerate entry.
+ */
+ if (aclequal(new_acl, aclparameterdefault(parameter)))
+ {
+ CatalogTupleDelete(relation, &tuple->t_self);
+ }
+
+ /*
+ * If the new Acl is the same as the old, no update is needed, and if
+ * the old Acl in the catalog is also NULL, it can be deleted.
+ */
+ else if (aclequal(old_acl, new_acl))
+ {
+ if (isNull)
+ CatalogTupleDelete(relation, &tuple->t_self);
+ }
+ else
+ {
+ HeapTuple newtuple;
+ Datum values[Natts_pg_parameter_acl];
+ bool nulls[Natts_pg_parameter_acl];
+ bool replaces[Natts_pg_parameter_acl];
+
+ /* 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);
+ }
+
+ /*
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information.
+ */
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /* 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 +3481,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 +3522,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 +3562,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 +3805,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:
@@ -3653,6 +3895,8 @@ pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum, Oid roleid,
case OBJECT_LARGEOBJECT:
return pg_largeobject_aclmask_snapshot(table_oid, roleid,
mask, how, NULL);
+ case OBJECT_PARAMETER:
+ return pg_parameter_aclmask(table_oid, roleid, mask, how);
case OBJECT_SCHEMA:
return pg_namespace_aclmask(table_oid, roleid, mask, how);
case OBJECT_STATISTIC_EXT:
@@ -4000,6 +4244,79 @@ 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_aclmask(Oid config_oid, Oid roleid, AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ HeapTuple tuple;
+ Form_pg_parameter_acl paramform;
+ char *parameter;
+ Datum aclDatum;
+ bool isNull;
+ Acl *default_acl;
+ 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)));
+
+ /*
+ * Get the default ACL based on this parameter's GUC context, or a
+ * conservative guess if GUC parameter is unknown.
+ */
+ paramform = (Form_pg_parameter_acl) GETSTRUCT(tuple);
+ parameter = text_to_cstring(¶mform->parameter);
+ default_acl = aclparameterdefault(parameter);
+
+ aclDatum = SysCacheGetAttr(PARAMETEROID, tuple, Anum_pg_parameter_acl_setacl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so use the default ACL based on the GUC context */
+ acl = aclcopy(default_acl);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+
+ /*
+ * Merge with the default ACL, in case the catalog's Acl is missing
+ * inherent privileges for the parameter. This can happen, for
+ * example, if privileges are recorded in pg_parameter_acl for a custom
+ * parameter prior to installing an extension which defines the
+ * parameter as userset.
+ */
+ acl = aclmerge(acl, default_acl, BOOTSTRAP_SUPERUSERID);
+ }
+
+ result = aclmask(acl, roleid, InvalidOid, mask, how);
+
+ /* If we have our own copy, free it. */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+
+ pfree(parameter);
+ pfree(default_acl);
+
+ return result;
+}
+
/*
* Exported routine for examining a user's privileges for a function
*/
@@ -4713,6 +5030,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_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..301591e321
--- /dev/null
+++ b/src/backend/catalog/pg_parameter_acl.c
@@ -0,0 +1,104 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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);
+
+ /* 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 7febb5018f..5e116594ed 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 e5a3c528aa..fef4bd86de 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
@@ -799,7 +799,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
@@ -7073,6 +7073,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:
@@ -7106,6 +7120,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;
+ }
;
@@ -7165,6 +7209,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);
@@ -7174,6 +7225,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.
@@ -16761,6 +16833,7 @@ unreserved_keyword:
| OWNED
| OWNER
| PARALLEL
+ | PARAMETER
| PARSER
| PARTIAL
| PARTITION
@@ -17373,6 +17446,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..16664708a0 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_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,68 @@ acldefault(ObjectType objtype, Oid ownerId)
return acl;
}
+/*
+ * reconcile_parameter_privs()
+ *
+ * Given a parameter and an ACL describing the access permissions for the
+ * parameter, add any missing privileges to the ACL that are inherent to the
+ * parameter's context. Currently, the only inherent privilege is that PUBLIC
+ * always has privilege to SET a USERSET parameter.
+ *
+ * If the named parameter does not exist, no privileges will be added. The acl
+ * may need to be updated if the parameter is later created with USERSET
+ * context.
+ */
+Acl *
+reconcile_parameter_privs(const char *parameter, Acl *old_acl)
+{
+ Acl *default_acl;
+ Acl *new_acl;
+
+ default_acl = aclparameterdefault(parameter);
+
+ if (old_acl && default_acl)
+ {
+ new_acl = aclmerge(old_acl, default_acl, BOOTSTRAP_SUPERUSERID);
+ return new_acl;
+ }
+
+ if (old_acl)
+ return old_acl;
+
+ return default_acl;
+}
+
+/*
+ * aclparameterdefault()
+ *
+ * Creates and returns an ACL describing the default access permissions for a
+ * parameter, taking into account whether it is a PGC_USERSET parameter.
+ * Unrecognized parameters are treated as not being PGC_USERSET.
+ */
+Acl *
+aclparameterdefault(const char *parameter)
+{
+ Acl *acl;
+
+ /* Treat all parameters as belonging to the bootstrap superuser. */
+ acl = acldefault(OBJECT_PARAMETER, BOOTSTRAP_SUPERUSERID);
+
+ /* Special case for USERSET gucs. By default, public can SET. */
+ if (find_option_context(parameter, PGC_SUSET) == PGC_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
@@ -873,6 +948,9 @@ acldefault_sql(PG_FUNCTION_ARGS)
case 'L':
objtype = OBJECT_LARGEOBJECT;
break;
+ case 'p':
+ objtype = OBJECT_PARAMETER;
+ break;
case 'n':
objtype = OBJECT_SCHEMA;
break;
@@ -895,6 +973,14 @@ acldefault_sql(PG_FUNCTION_ARGS)
PG_RETURN_ACL_P(acldefault(objtype, owner));
}
+/*
+ * SQL-accessible version of aclparameterdefault().
+ */
+Datum
+aclparameterdefault_sql(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_ACL_P(aclparameterdefault(text_to_cstring(PG_GETARG_TEXT_P(0))));
+}
/*
* Update an ACL array to add or remove specified privileges.
@@ -1602,6 +1688,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 +1788,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 +4523,169 @@ 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 the parameter doesn't exist.
+ */
+
+/*
+ * has_param_priv_byoid
+ *
+ * Helper function to check user privileges on a parameter given the
+ * role by Oid, parameter by Oid, and privileges by text.
+ */
+static bool
+has_param_priv_byoid(Oid roleid, Oid parameteroid, AclMode priv)
+{
+ AclResult aclresult;
+
+ aclresult = pg_parameter_acl_aclcheck(parameteroid, roleid, priv);
+
+ return aclresult == ACLCHECK_OK;
+}
+
+/*
+ * has_param_priv_byname
+ *
+ * Helper function to check user privileges on a parameter given the
+ * role by Oid, parameter by text, and privileges by text.
+ */
+static bool
+has_param_priv_byname(Oid roleid, const text *parameter, AclMode priv)
+{
+ char *paramstr = text_to_cstring(parameter);
+ Oid parameteroid = get_parameter_oid(paramstr, true);
+ Acl *acl;
+
+ if (OidIsValid(parameteroid))
+ return has_param_priv_byoid(roleid, parameteroid, priv);
+
+ acl = aclparameterdefault(paramstr);
+ return aclmask(acl, roleid, InvalidOid, priv, ACLMASK_ANY) != 0;
+}
+
+/*
+ * 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);
+ AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(2));
+ Oid roleid = get_role_oid_or_public(NameStr(*username));
+
+ PG_RETURN_BOOL(has_param_priv_byname(roleid, parameter, priv));
+}
+
+/*
+ * 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);
+ AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(1));
+
+ PG_RETURN_BOOL(has_param_priv_byname(GetUserId(), parameter, priv));
+}
+
+/*
+ * 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);
+ AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(2));
+
+ PG_RETURN_BOOL(has_param_priv_byname(roleid, parameter, priv));
+}
+
+/*
+ * 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);
+ AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(2));
+ Oid roleid = get_role_oid_or_public(NameStr(*username));
+
+ if (!SearchSysCacheExists1(PARAMETEROID, ObjectIdGetDatum(parameteroid)))
+ PG_RETURN_NULL();
+ PG_RETURN_BOOL(has_param_priv_byoid(roleid, parameteroid, priv));
+}
+
+/*
+ * 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);
+ AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(1));
+
+ if (!SearchSysCacheExists1(PARAMETEROID, ObjectIdGetDatum(parameteroid)))
+ PG_RETURN_NULL();
+ PG_RETURN_BOOL(has_param_priv_byoid(GetUserId(), parameteroid, priv));
+}
+
+/*
+ * 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);
+ AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(2));
+
+ if (!SearchSysCacheExists1(PARAMETEROID, ObjectIdGetDatum(parameteroid)))
+ PG_RETURN_NULL();
+ PG_RETURN_BOOL(has_param_priv_byoid(roleid, parameteroid, priv));
+}
+
+/*
+ * 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_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_text, parameter_priv_map);
+}
/*
* pg_has_role variants
@@ -4665,6 +4922,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.c b/src/backend/utils/misc/guc.c
index 9e8ab1420d..bdaf5a431a 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"
@@ -5079,6 +5080,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",
@@ -5478,25 +5483,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().)
+ *
+ * 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"
@@ -5513,8 +5522,25 @@ 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;
+}
+
+
+/*
+ * 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().)
+ */
+static bool
+valid_custom_variable_name(const char *name)
+{
+ int partcnt;
+
+ return (valid_variable_name(name, &partcnt) && partcnt > 1);
}
/*
@@ -7568,6 +7594,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\"",
@@ -8166,6 +8210,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, false, 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.
*/
@@ -8610,6 +8697,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
{
char *name;
char *value;
+ Oid parameterId = InvalidOid;
bool resetall = false;
ConfigVariable *head = NULL;
ConfigVariable *tail = NULL;
@@ -8617,16 +8705,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:
@@ -8760,16 +8863,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,
@@ -8943,9 +9047,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);
}
/*
@@ -9706,6 +9810,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, false, 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,
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 6f040674a2..e07bbfc0a3 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3737,7 +3737,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
@@ -3749,8 +3753,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",
@@ -3763,12 +3770,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");
@@ -3785,7 +3919,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
@@ -3815,13 +3950,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");
/*
@@ -3830,7 +3967,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);
@@ -3868,6 +4006,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,
@@ -3879,7 +4036,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");
@@ -3888,7 +4046,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");
@@ -3897,7 +4056,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 25304430f4..be8f5deaa9 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 8a9ccf6221..5fe346cded 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
@@ -2072,6 +2072,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 a73032b319..836f984406 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -327,6 +327,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..621eee2764 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,8 @@ typedef enum
* routines used internally
*/
extern Acl *acldefault(ObjectType objtype, Oid ownerId);
+extern Acl *aclparameterdefault(const char *parameter);
+extern Acl *reconcile_parameter_privs(const char *parameter, Acl *acl);
extern Acl *get_user_default_acl(ObjectType objtype, Oid ownerId,
Oid nsp_oid);
extern void recordDependencyOnNewAcl(Oid classId, Oid objectId, int32 objsubId,
@@ -225,6 +228,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 +250,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_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 +280,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/test/modules/test_oat_hooks/expected/test_oat_hooks.out b/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out
index 45ff276f7e..86d2d2670f 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,98 @@
+-- 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.
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 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 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 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 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
+-- Check when the hooks fire relative to dependency based abort of a drop
+DROP ROLE regress_role_joe;
+NOTICE: in process utility: superuser attempting DropRoleStmt
+NOTICE: in object access: superuser attempting drop (subId=0x0) []
+NOTICE: in object access: superuser finished drop (subId=0x0) []
+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
+-- Check the behavior of the hooks relative to do-nothing grants and revokes
+GRANT SET ON PARAMETER work_mem TO PUBLIC;
+NOTICE: in process utility: superuser attempting GrantStmt
+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 GrantStmt
+REVOKE ALTER SYSTEM ON PARAMETER work_mem FROM PUBLIC;
+NOTICE: in process utility: superuser attempting GrantStmt
+NOTICE: in process utility: superuser finished GrantStmt
+-- Check the behavior of the hooks relative to unrecognized parameters
+GRANT ALL ON PARAMETER "none.such" TO PUBLIC;
+NOTICE: in process utility: superuser attempting GrantStmt
+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
+-- Check relative to an operation that causes the catalog entry to be deleted
+REVOKE ALL ON PARAMETER "none.such" FROM PUBLIC;
+NOTICE: in process utility: superuser attempting GrantStmt
+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
-- 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 +101,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 +125,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 +172,89 @@ 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"
+SET test_oat_hooks.user_var1 = 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_var1]
+NOTICE: in object_access_hook_str: non-superuser finished alter (subId=0x1000, set) [test_oat_hooks.user_var1]
+NOTICE: in process utility: non-superuser finished set
+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"
+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 +273,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..f67204d94d 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,39 @@
+-- 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.
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;
+
+-- Check when the hooks fire relative to dependency based abort of a drop
+DROP ROLE regress_role_joe;
+
+-- Check the behavior of the hooks relative to do-nothing grants and revokes
+GRANT SET ON PARAMETER work_mem TO PUBLIC;
+REVOKE ALTER SYSTEM ON PARAMETER work_mem FROM PUBLIC;
+
+-- Check the behavior of the hooks relative to unrecognized parameters
+GRANT ALL ON PARAMETER "none.such" TO PUBLIC;
+
+-- Check relative to an operation that causes the catalog entry to be deleted
+REVOKE ALL ON PARAMETER "none.such" FROM PUBLIC;
+
-- Create objects for use in the test
CREATE USER regress_test_user;
CREATE TABLE regress_test_table (t text);
@@ -30,6 +59,16 @@ SET work_mem = 8192;
RESET work_mem;
ALTER SYSTEM SET work_mem = 8192;
ALTER SYSTEM RESET work_mem;
+
+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;
+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 +90,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..be0705ca64 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,56 @@ 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, },
+ },
+
+ '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..f1d2046989
--- /dev/null
+++ b/src/test/modules/unsafe_tests/expected/guc_privs.out
@@ -0,0 +1,506 @@
+-- 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)
+
+-- Revoke privileges from a non-existent custom guc. This should not create
+-- entries in the catalog.
+REVOKE ALL ON PARAMETER "none.such" FROM regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parameter = 'none.such';
+ ?column?
+----------
+(0 rows)
+
+-- Grant and then revoke privileges on the non-existent custom guc. Check that
+-- a do-nothing entry is not left in the catalogs after the revoke.
+GRANT ALL ON PARAMETER "none.such" TO regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parameter = 'none.such';
+ ?column?
+----------
+ 1
+(1 row)
+
+REVOKE ALL ON PARAMETER "none.such" FROM regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parameter = 'none.such';
+ ?column?
+----------
+(0 rows)
+
+-- Check that GRANT SET...TO PUBLIC on a userset parameter for which no other
+-- grants have been performed is a no-op. Check that granting ALL PRIVILEGES
+-- is meaningful, but that subsequently revoking ALTER SYSTEM removes the entry
+-- from the catalogs rather than leaving a do-nothing grant on SET.
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT 1 FROM pg_parameter_acl WHERE parameter = 'enable_material';
+ ?column?
+----------
+(0 rows)
+
+GRANT SET ON PARAMETER enable_material TO PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT 1 FROM pg_parameter_acl WHERE parameter = 'enable_material';
+ ?column?
+----------
+(0 rows)
+
+GRANT ALL ON PARAMETER enable_material TO PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT 1 FROM pg_parameter_acl WHERE parameter = 'enable_material';
+ ?column?
+----------
+ 1
+(1 row)
+
+REVOKE ALTER SYSTEM ON PARAMETER enable_material FROM PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT 1 FROM pg_parameter_acl WHERE parameter = 'enable_material';
+ ?column?
+----------
+(0 rows)
+
+-- Only "ALTER SYSTEM" (if previously granted) can be revoked from public on a
+-- userset variable; "SET" is implicit for public. We don't complain about
+-- this phrasing, though
+REVOKE ALL ON PARAMETER temp_buffers FROM PUBLIC;
+REVOKE SET ON PARAMETER temp_buffers FROM PUBLIC;
+-- 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;
+-- 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); -- ok, despite privilege revocation above
+ set_config
+------------
+ 64MB
+(1 row)
+
+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 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
+privileges for parameter work_mem
+-- 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 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
+privileges for parameter work_mem
+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 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
+privileges for parameter work_mem
+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..5bb65ba859
--- /dev/null
+++ b/src/test/modules/unsafe_tests/sql/guc_privs.sql
@@ -0,0 +1,211 @@
+-- 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');
+-- Revoke privileges from a non-existent custom guc. This should not create
+-- entries in the catalog.
+REVOKE ALL ON PARAMETER "none.such" FROM regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parameter = 'none.such';
+-- Grant and then revoke privileges on the non-existent custom guc. Check that
+-- a do-nothing entry is not left in the catalogs after the revoke.
+GRANT ALL ON PARAMETER "none.such" TO regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parameter = 'none.such';
+REVOKE ALL ON PARAMETER "none.such" FROM regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parameter = 'none.such';
+-- Check that GRANT SET...TO PUBLIC on a userset parameter for which no other
+-- grants have been performed is a no-op. Check that granting ALL PRIVILEGES
+-- is meaningful, but that subsequently revoking ALTER SYSTEM removes the entry
+-- from the catalogs rather than leaving a do-nothing grant on SET.
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+SELECT 1 FROM pg_parameter_acl WHERE parameter = 'enable_material';
+GRANT SET ON PARAMETER enable_material TO PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+SELECT 1 FROM pg_parameter_acl WHERE parameter = 'enable_material';
+GRANT ALL ON PARAMETER enable_material TO PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+SELECT 1 FROM pg_parameter_acl WHERE parameter = 'enable_material';
+REVOKE ALTER SYSTEM ON PARAMETER enable_material FROM PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+SELECT 1 FROM pg_parameter_acl WHERE parameter = 'enable_material';
+-- Only "ALTER SYSTEM" (if previously granted) can be revoked from public on a
+-- userset variable; "SET" is implicit for public. We don't complain about
+-- this phrasing, though
+REVOKE ALL ON PARAMETER temp_buffers FROM PUBLIC;
+REVOKE SET ON PARAMETER temp_buffers FROM PUBLIC;
+-- 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;
+-- 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); -- ok, despite privilege revocation above
+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
Mark Dilger <mark.dilger@enterprisedb.com> writes:
[ v15 patch ]
Thanks. As I'm working through this, I'm kind of inclined to drop
the has_parameter_privilege() variants that take an OID as object
identifier. This gets back to the fact that I don't think
pg_parameter_acl OIDs have any outside use; we wouldn't even have
them except that we need a way to track their role dependencies
in pg_shdepend. AFAICS users will only ever be interested in
looking up a GUC by name. Any objections?
Another thought here is that I see you're expending some code
to store the canonical name of a GUC in pg_parameter_acl, but
I think that's probably going too far. In particular, for the
legacy mixed-case names like "DateStyle", what ends up in the
table is the mixed-case name, which seems problematic. It would
definitely be problematic if an extension used such a name,
because we might or might not be aware of the idiosyncratic
casing at the time a GRANT is issued. I'm thinking that we
really want to avoid looking up custom GUCs at all during GRANT,
because that can't do anything except create hazards.
So I think that instead of what you've got here, we should
(1) apply the map_old_guc_names[] mapping, which is constant
(for any one PG release anyway)
(2) smash to lower case
(3) verify validity per valid_variable_name.
This also simplifies life on the lookup side, where it's sufficient
to do steps (1) and (2) before performing a catalog search.
Thoughts?
regards, tom lane
On 4/4/22 17:26, Tom Lane wrote:
Mark Dilger <mark.dilger@enterprisedb.com> writes:
[ v15 patch ]
Thanks. As I'm working through this, I'm kind of inclined to drop
the has_parameter_privilege() variants that take an OID as object
identifier. This gets back to the fact that I don't think
pg_parameter_acl OIDs have any outside use; we wouldn't even have
them except that we need a way to track their role dependencies
in pg_shdepend. AFAICS users will only ever be interested in
looking up a GUC by name. Any objections?
Not from me
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
On Apr 4, 2022, at 2:26 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Thanks. As I'm working through this, I'm kind of inclined to drop
the has_parameter_privilege() variants that take an OID as object
identifier. This gets back to the fact that I don't think
pg_parameter_acl OIDs have any outside use; we wouldn't even have
them except that we need a way to track their role dependencies
in pg_shdepend. AFAICS users will only ever be interested in
looking up a GUC by name. Any objections?
None.
Another thought here is that I see you're expending some code
to store the canonical name of a GUC in pg_parameter_acl, but
I think that's probably going too far. In particular, for the
legacy mixed-case names like "DateStyle", what ends up in the
table is the mixed-case name, which seems problematic. It would
definitely be problematic if an extension used such a name,
because we might or might not be aware of the idiosyncratic
casing at the time a GRANT is issued. I'm thinking that we
really want to avoid looking up custom GUCs at all during GRANT,
because that can't do anything except create hazards.
Yikes. It took a few tries to see what you mean. Yes, if the GRANT happens before the LOAD, that can have bad consequences. So I agree something should be changed.
So I think that instead of what you've got here, we should
(1) apply the map_old_guc_names[] mapping, which is constant
(for any one PG release anyway)
(2) smash to lower case
(3) verify validity per valid_variable_name.This also simplifies life on the lookup side, where it's sufficient
to do steps (1) and (2) before performing a catalog search.Thoughts?
That sounds right. Do you already have something like that coded, or would you like me to post a patch?
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Mark Dilger <mark.dilger@enterprisedb.com> writes:
On Apr 4, 2022, at 2:26 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
So I think that instead of what you've got here, we should
(1) apply the map_old_guc_names[] mapping, which is constant
(for any one PG release anyway)
(2) smash to lower case
(3) verify validity per valid_variable_name.This also simplifies life on the lookup side, where it's sufficient
to do steps (1) and (2) before performing a catalog search.Thoughts?
That sounds right. Do you already have something like that coded, or would you like me to post a patch?
Wrote it already, no need for you to do it.
regards, tom lane
On Apr 4, 2022, at 5:12 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Wrote it already, no need for you to do it.
Thanks!
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Here's a v16, which I have editorialized on rather heavily.
I have not yet really looked at the docs, but I'm satisfied
with the code and tests now.
The main non-cosmetic changes I made:
* I got rid of the mechanisms that tried to make has_parameter_privilege()
act like USERSET GUCs have a forced public SET grant. I think that's
fundamentally unworkable for custom GUCs, and it's just going to create
confusion and inconsistency. As the code stands, it'll report SET
privilege if you're superuser or have been explicitly granted that
privilege, but having that privilege only matters if the GUC is SUSET.
Need to craft some documentation wording to explain that, but I'd rather
explain that than say "it might or might not tell you that SET privileges
exist". In any case I think this is perfectly symmetrical with the
ALTER SYSTEM privilege: you can grant that all you want, but it still
won't let you set variables whose context value forbids it.
* That meant we have only one default ACL setting, so I was able to
get rid of aclparameterdefault() altogether.
* I took out the post-alter hook call in ExecGrant_Parameter. I'm
prepared to listen to arguments why it should be put back in, but
said arguments would have to explain why GRANT ON PARAMETER should
do that when no other form of GRANT does. (Or, possibly, it'd be
sane for all the forms to call that hook; but that would be material
for a different patch IMV.)
* I took out the pg_parameter_privileges view, which was undocumented
anyway, and which I think people would not find useful. The fact that
it's missing entries for most GUCs and might contain bogus entries
for nonexistent GUCs seems like a strong reason why it's not very
useful in this form. (But see below.)
* I thought the tab-completion stuff was completely excessive.
I got rid of the support for tab completion with both privileges
spelled out, because I think people are just going to write ALL
instead. We don't have tab completion support for commands listing
multiple privileges of other object types, and I don't see why
PARAMETER is the place to start. I also merged a bunch of duplicative
rules by relying on MatchAny where possible.
Aside from documentation, there is one loose end that perhaps would
be best done in a follow-up patch: I'm somewhat of the opinion that
we ought to reinvent a \dcp command for psql. I'd define it as being
a compact version of the pg_settings view, showing the name, value,
and unit columns and probably not much else by default. With "+",
we could add on the privileges if any, and perhaps the context since
you need that to interpret the privileges. I think this'd be more
useful than the pg_parameter_privileges view, and it'd be handier
than pg_settings itself since you could do things like
\dcp autovac*
with a lot less typing than it takes to get the same info
out of pg_settings directly.
Anyway, I'm out of patience with this for today, and I'm throwing
it up for the cfbot to have a look at. I'll hit the docs tomorrow.
regards, tom lane
Attachments:
v16-0001-Allow-grant-and-revoke-of-privileges-on-paramete.patchtext/x-diff; charset=us-ascii; name*0=v16-0001-Allow-grant-and-revoke-of-privileges-on-paramete.p; name*1=atchDownload
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 7f4f79d1b5..6fab8714b5 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 for which privileges have been granted</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>
@@ -5450,6 +5455,73 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</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 for which privileges have been 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>oid</structfield> <type>oid</type>
+ </para>
+ <para>
+ Row identifier
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>parname</structfield> <type>text</type>
+ </para>
+ <para>
+ The name of a configuration parameter for which privileges are granted
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>paracl</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-partitioned-table">
<title><structname>pg_partitioned_table</structname></title>
@@ -12780,7 +12852,9 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
<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
+ or within a session via the <command>SET</command> command; but only
+ superusers and users with <literal>SET</literal> privilege granted
+ on the specific setting
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>.
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 166b7a352d..84771ab6c6 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
@@ -1977,6 +1998,10 @@ REVOKE ALL ON accounts FROM PUBLIC;
large objects,
schemas,
or tablespaces.
+ Some parameters are inherently settable by <literal>PUBLIC</literal>, as if
+ <literal>SET</literal> were granted, but this is not implemented as a grant,
+ nor can the ability for <literal>PUBLIC</literal> to set these parameters be
+ revoked.
For other types of objects, the default privileges
granted to <literal>PUBLIC</literal> are as follows:
<literal>CONNECT</literal> and <literal>TEMPORARY</literal> (create
@@ -2097,6 +2122,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>
@@ -2167,6 +2202,12 @@ REVOKE ALL ON accounts FROM PUBLIC;
<entry>none</entry>
<entry><literal>\dl+</literal></entry>
</row>
+ <row>
+ <entry><literal>PARAMETER</literal></entry>
+ <entry><literal>sA</literal></entry>
+ <entry>none</entry>
+ <entry>none</entry>
+ </row>
<row>
<entry><literal>SCHEMA</literal></entry>
<entry><literal>UC</literal></entry>
@@ -2274,6 +2315,14 @@ 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>
+ No privileges are required for <literal>PUBLIC</literal> to
+ <command>SET</command> and <command>RESET</command> a
+ <literal>user</literal> parameter. 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 4001cb2bda..22ea39b842 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -22701,8 +22701,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');
@@ -22836,6 +22835,24 @@ 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>,
+ <parameter>privilege</parameter> <type>text</type> )
+ <returnvalue>boolean</returnvalue>
+ </para>
+ <para>
+ Does user have privilege for configuration 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/alter_system.sgml b/doc/src/sgml/ref/alter_system.sgml
index 5e41f7f644..6f8bd39eaf 100644
--- a/doc/src/sgml/ref/alter_system.sgml
+++ b/doc/src/sgml/ref/alter_system.sgml
@@ -55,7 +55,8 @@ ALTER SYSTEM RESET ALL
</para>
<para>
- Only superusers can use <command>ALTER SYSTEM</command>. Also, since
+ Only superusers and users granted <literal>ALTER SYSTEM</literal> privilege
+ on a parameter can change it using <command>ALTER SYSTEM</command>. Also, since
this command acts directly on the file system and cannot be rolled back,
it is not allowed inside a transaction block or function.
</para>
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..89a0221ec9 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -38,6 +38,7 @@ OBJS = \
pg_largeobject.o \
pg_namespace.o \
pg_operator.o \
+ pg_parameter_acl.o \
pg_proc.o \
pg_publication.o \
pg_range.o \
@@ -68,7 +69,8 @@ CATALOG_HEADERS := \
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
pg_foreign_table.h pg_policy.h pg_replication_origin.h \
pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \
- pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
+ pg_collation.h pg_parameter_acl.h pg_partitioned_table.h \
+ pg_range.h pg_transform.h \
pg_sequence.h pg_publication.h pg_publication_namespace.h \
pg_publication_rel.h pg_subscription.h pg_subscription_rel.h
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 1dd03a8e51..5f1726c095 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -48,6 +48,7 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
@@ -112,11 +113,13 @@ 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);
-static List *objectNamesToOids(ObjectType objtype, List *objnames);
+static List *objectNamesToOids(ObjectType objtype, List *objnames,
+ bool is_grant);
static List *objectsInSchemaToOids(ObjectType objtype, List *nspnames);
static List *getRelationsInNamespace(Oid namespaceId, char relkind);
static void expand_col_privileges(List *colnames, Oid table_oid,
@@ -259,6 +262,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_ACL:
+ whole_mask = ACL_ALL_RIGHTS_PARAMETER_ACL;
+ break;
default:
elog(ERROR, "unrecognized object type: %d", objtype);
/* not reached, but keep compiler quiet */
@@ -390,7 +396,8 @@ ExecuteGrantStmt(GrantStmt *stmt)
switch (stmt->targtype)
{
case ACL_TARGET_OBJECT:
- istmt.objects = objectNamesToOids(stmt->objtype, stmt->objects);
+ istmt.objects = objectNamesToOids(stmt->objtype, stmt->objects,
+ stmt->is_grant);
break;
case ACL_TARGET_ALL_IN_SCHEMA:
istmt.objects = objectsInSchemaToOids(stmt->objtype, stmt->objects);
@@ -498,6 +505,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_ACL:
+ all_privileges = ACL_ALL_RIGHTS_PARAMETER_ACL;
+ errormsg = gettext_noop("invalid privilege type %s for parameter");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
@@ -600,6 +611,9 @@ ExecGrantStmt_oids(InternalGrant *istmt)
case OBJECT_TABLESPACE:
ExecGrant_Tablespace(istmt);
break;
+ case OBJECT_PARAMETER_ACL:
+ ExecGrant_Parameter(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -626,7 +640,7 @@ ExecGrantStmt_oids(InternalGrant *istmt)
* to fail.
*/
static List *
-objectNamesToOids(ObjectType objtype, List *objnames)
+objectNamesToOids(ObjectType objtype, List *objnames, bool is_grant)
{
List *objects = NIL;
ListCell *cell;
@@ -759,6 +773,37 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_PARAMETER_ACL:
+ foreach(cell, objnames)
+ {
+ /*
+ * In this code we represent a GUC by the OID of its entry in
+ * pg_parameter_acl, which we have to manufacture here if it
+ * doesn't exist yet. (That's a hack for sure, but it avoids
+ * messing with all the GRANT/REVOKE infrastructure that
+ * expects to use OIDs for object identities.) However, if
+ * this is a REVOKE, we can instead just ignore any GUCs that
+ * don't have such an entry, as they must not have any
+ * privileges needing removal.
+ */
+ char *parameter = strVal(lfirst(cell));
+ Oid parameterId = ParameterAclLookup(parameter, true);
+
+ if (!OidIsValid(parameterId) && is_grant)
+ {
+ parameterId = ParameterAclCreate(parameter);
+
+ /*
+ * Prevent error when processing duplicate objects, and
+ * make this new entry visible so that ExecGrant_Parameter
+ * can update it.
+ */
+ CommandCounterIncrement();
+ }
+ if (OidIsValid(parameterId))
+ objects = lappend_oid(objects, parameterId);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -1494,6 +1539,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case ForeignDataWrapperRelationId:
istmt.objtype = OBJECT_FDW;
break;
+ case ParameterAclRelationId:
+ istmt.objtype = OBJECT_PARAMETER_ACL;
+ break;
default:
elog(ERROR, "unexpected object class %u", classid);
break;
@@ -3225,6 +3273,154 @@ 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_ACL;
+
+ relation = table_open(ParameterAclRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid parameterId = lfirst_oid(cell);
+ Datum nameDatum;
+ const char *parname;
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ Oid ownerId;
+ HeapTuple tuple;
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(PARAMETERACLOID, ObjectIdGetDatum(parameterId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for parameter ACL %u",
+ parameterId);
+
+ /* We'll need the GUC's name */
+ nameDatum = SysCacheGetAttr(PARAMETERACLOID, tuple,
+ Anum_pg_parameter_acl_parname,
+ &isNull);
+ Assert(!isNull);
+ parname = TextDatumGetCString(nameDatum);
+
+ /* Treat all parameters as belonging to the bootstrap superuser. */
+ ownerId = BOOTSTRAP_SUPERUSERID;
+
+ /*
+ * Get working copy of existing ACL. If there's no ACL, substitute the
+ * proper default.
+ */
+ aclDatum = SysCacheGetAttr(PARAMETERACLOID, tuple,
+ Anum_pg_parameter_acl_paracl,
+ &isNull);
+
+ if (isNull)
+ {
+ old_acl = acldefault(istmt->objtype, ownerId);
+ /* 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, ownerId,
+ &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_ACL,
+ parname,
+ 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, ownerId);
+
+ /*
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information.
+ */
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /*
+ * If the new ACL is equal to the default, we don't need the catalog
+ * entry any longer. Delete it rather than updating it, to avoid
+ * leaving a degenerate entry.
+ */
+ if (aclequal(new_acl, acldefault(istmt->objtype, ownerId)))
+ {
+ CatalogTupleDelete(relation, &tuple->t_self);
+ }
+ else
+ {
+ /* finished building new ACL value, now insert it */
+ HeapTuple newtuple;
+ Datum values[Natts_pg_parameter_acl];
+ bool nulls[Natts_pg_parameter_acl];
+ bool replaces[Natts_pg_parameter_acl];
+
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ replaces[Anum_pg_parameter_acl_paracl - 1] = true;
+ values[Anum_pg_parameter_acl_paracl - 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, parameterId, 0,
+ ownerId,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ table_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3255,6 +3451,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 +3492,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);
}
@@ -3376,6 +3580,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_OPFAMILY:
msg = gettext_noop("permission denied for operator family %s");
break;
+ case OBJECT_PARAMETER_ACL:
+ msg = gettext_noop("permission denied for parameter %s");
+ break;
case OBJECT_POLICY:
msg = gettext_noop("permission denied for policy %s");
break;
@@ -3567,6 +3774,7 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
case OBJECT_DOMCONSTRAINT:
+ case OBJECT_PARAMETER_ACL:
case OBJECT_PUBLICATION_NAMESPACE:
case OBJECT_PUBLICATION_REL:
case OBJECT_ROLE:
@@ -3653,6 +3861,8 @@ pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum, Oid roleid,
case OBJECT_LARGEOBJECT:
return pg_largeobject_aclmask_snapshot(table_oid, roleid,
mask, how, NULL);
+ case OBJECT_PARAMETER_ACL:
+ return pg_parameter_acl_aclmask(table_oid, roleid, mask, how);
case OBJECT_SCHEMA:
return pg_namespace_aclmask(table_oid, roleid, mask, how);
case OBJECT_STATISTIC_EXT:
@@ -4000,6 +4210,121 @@ pg_database_aclmask(Oid db_oid, Oid roleid,
return result;
}
+/*
+ * Exported routine for examining a user's privileges for a configuration
+ * parameter (GUC), identified by GUC name.
+ */
+AclMode
+pg_parameter_aclmask(const char *name, Oid roleid, AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ char *parname;
+ text *partext;
+ HeapTuple tuple;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /* Convert name to the form it should have in pg_parameter_acl... */
+ parname = convert_GUC_name_for_parameter_acl(name);
+ partext = cstring_to_text(parname);
+
+ /* ... and look it up */
+ tuple = SearchSysCache1(PARAMETERACLNAME, PointerGetDatum(partext));
+
+ if (!HeapTupleIsValid(tuple))
+ {
+ /* If no entry, GUC has no permissions for non-superusers */
+ result = ACL_NO_RIGHTS;
+ }
+ else
+ {
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+
+ aclDatum = SysCacheGetAttr(PARAMETERACLNAME, tuple,
+ Anum_pg_parameter_acl_paracl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_PARAMETER_ACL, BOOTSTRAP_SUPERUSERID);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, BOOTSTRAP_SUPERUSERID, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+ }
+
+ pfree(parname);
+ pfree(partext);
+
+ return result;
+}
+
+/*
+ * Exported routine for examining a user's privileges for a configuration
+ * parameter (GUC), identified by the OID of its pg_parameter_acl entry.
+ */
+AclMode
+pg_parameter_acl_aclmask(Oid acl_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 ACL from pg_parameter_acl */
+ tuple = SearchSysCache1(PARAMETERACLOID, ObjectIdGetDatum(acl_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("parameter ACL with OID %u does not exist",
+ acl_oid)));
+
+ aclDatum = SysCacheGetAttr(PARAMETERACLOID, tuple,
+ Anum_pg_parameter_acl_paracl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_PARAMETER_ACL, BOOTSTRAP_SUPERUSERID);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, BOOTSTRAP_SUPERUSERID, 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 +5038,32 @@ 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 (GUC), identified by GUC name.
+ */
+AclResult
+pg_parameter_aclcheck(const char *name, Oid roleid, AclMode mode)
+{
+ if (pg_parameter_aclmask(name, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
+/*
+ * Exported routine for checking a user's access privileges to a configuration
+ * parameter (GUC), identified by the OID of its pg_parameter_acl entry.
+ */
+AclResult
+pg_parameter_acl_aclcheck(Oid acl_oid, Oid roleid, AclMode mode)
+{
+ if (pg_parameter_acl_aclmask(acl_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..520f77971b 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -33,6 +33,7 @@
#include "catalog/pg_db_role_setting.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_namespace.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_replication_origin.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
@@ -247,32 +248,35 @@ IsSharedRelation(Oid relationId)
if (relationId == AuthIdRelationId ||
relationId == AuthMemRelationId ||
relationId == DatabaseRelationId ||
- relationId == SharedDescriptionRelationId ||
- relationId == SharedDependRelationId ||
- relationId == SharedSecLabelRelationId ||
- relationId == TableSpaceRelationId ||
relationId == DbRoleSettingRelationId ||
+ relationId == ParameterAclRelationId ||
relationId == ReplicationOriginRelationId ||
- relationId == SubscriptionRelationId)
+ relationId == SharedDependRelationId ||
+ relationId == SharedDescriptionRelationId ||
+ relationId == SharedSecLabelRelationId ||
+ relationId == SubscriptionRelationId ||
+ relationId == TableSpaceRelationId)
return true;
/* These are their indexes */
- if (relationId == AuthIdRolnameIndexId ||
- relationId == AuthIdOidIndexId ||
- relationId == AuthMemRoleMemIndexId ||
+ if (relationId == AuthIdOidIndexId ||
+ relationId == AuthIdRolnameIndexId ||
relationId == AuthMemMemRoleIndexId ||
+ relationId == AuthMemRoleMemIndexId ||
relationId == DatabaseNameIndexId ||
relationId == DatabaseOidIndexId ||
- relationId == SharedDescriptionObjIndexId ||
- relationId == SharedDependDependerIndexId ||
- relationId == SharedDependReferenceIndexId ||
- relationId == SharedSecLabelObjectIndexId ||
- relationId == TablespaceOidIndexId ||
- relationId == TablespaceNameIndexId ||
relationId == DbRoleSettingDatidRolidIndexId ||
+ relationId == ParameterAclOidIndexId ||
+ relationId == ParameterAclParnameIndexId ||
relationId == ReplicationOriginIdentIndex ||
relationId == ReplicationOriginNameIndex ||
+ relationId == SharedDependDependerIndexId ||
+ relationId == SharedDependReferenceIndexId ||
+ relationId == SharedDescriptionObjIndexId ||
+ relationId == SharedSecLabelObjectIndexId ||
+ relationId == SubscriptionNameIndexId ||
relationId == SubscriptionObjectIndexId ||
- relationId == SubscriptionNameIndexId)
+ relationId == TablespaceNameIndexId ||
+ relationId == TablespaceOidIndexId)
return true;
/* These are their toast tables and toast indexes */
if (relationId == PgAuthidToastTable ||
@@ -281,6 +285,8 @@ IsSharedRelation(Oid relationId)
relationId == PgDatabaseToastIndex ||
relationId == PgDbRoleSettingToastTable ||
relationId == PgDbRoleSettingToastIndex ||
+ relationId == PgParameterAclToastTable ||
+ relationId == PgParameterAclToastIndex ||
relationId == PgReplicationOriginToastTable ||
relationId == PgReplicationOriginToastIndex ||
relationId == PgShdescriptionToastTable ||
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 25fe56d310..de10923391 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -46,6 +46,7 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_policy.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_publication.h"
@@ -178,6 +179,7 @@ static const Oid object_classes[] = {
DefaultAclRelationId, /* OCLASS_DEFACL */
ExtensionRelationId, /* OCLASS_EXTENSION */
EventTriggerRelationId, /* OCLASS_EVENT_TRIGGER */
+ ParameterAclRelationId, /* OCLASS_PARAMETER_ACL */
PolicyRelationId, /* OCLASS_POLICY */
PublicationNamespaceRelationId, /* OCLASS_PUBLICATION_NAMESPACE */
PublicationRelationId, /* OCLASS_PUBLICATION */
@@ -1507,6 +1509,7 @@ doDeletion(const ObjectAddress *object, int flags)
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_SUBSCRIPTION:
+ case OCLASS_PARAMETER_ACL:
elog(ERROR, "global objects cannot be deleted by doDeletion");
break;
@@ -2861,6 +2864,9 @@ getObjectClass(const ObjectAddress *object)
case EventTriggerRelationId:
return OCLASS_EVENT_TRIGGER;
+ case ParameterAclRelationId:
+ return OCLASS_PARAMETER_ACL;
+
case PolicyRelationId:
return OCLASS_POLICY;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 3fd17ea64f..31c80f7209 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -45,6 +45,7 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_policy.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_publication.h"
@@ -818,6 +819,10 @@ static const struct object_type_map
{
"event trigger", OBJECT_EVENT_TRIGGER
},
+ /* OCLASS_PARAMETER_ACL */
+ {
+ "parameter ACL", OBJECT_PARAMETER_ACL
+ },
/* OCLASS_POLICY */
{
"policy", OBJECT_POLICY
@@ -1014,6 +1019,7 @@ get_object_address(ObjectType objtype, Node *object,
case OBJECT_FDW:
case OBJECT_FOREIGN_SERVER:
case OBJECT_EVENT_TRIGGER:
+ case OBJECT_PARAMETER_ACL:
case OBJECT_ACCESS_METHOD:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
@@ -1315,6 +1321,11 @@ get_object_address_unqualified(ObjectType objtype,
address.objectId = get_event_trigger_oid(name, missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_PARAMETER_ACL:
+ address.classId = ParameterAclRelationId;
+ address.objectId = ParameterAclLookup(name, missing_ok);
+ address.objectSubId = 0;
+ break;
case OBJECT_PUBLICATION:
address.classId = PublicationRelationId;
address.objectId = get_publication_oid(name, missing_ok);
@@ -2307,6 +2318,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_FDW:
case OBJECT_FOREIGN_SERVER:
case OBJECT_LANGUAGE:
+ case OBJECT_PARAMETER_ACL:
case OBJECT_PUBLICATION:
case OBJECT_ROLE:
case OBJECT_SCHEMA:
@@ -2597,6 +2609,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
case OBJECT_ACCESS_METHOD:
+ case OBJECT_PARAMETER_ACL:
/* We treat these object types as being owned by superusers */
if (!superuser_arg(roleid))
ereport(ERROR,
@@ -3880,6 +3893,32 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
break;
}
+ case OCLASS_PARAMETER_ACL:
+ {
+ HeapTuple tup;
+ Datum nameDatum;
+ bool isNull;
+ char *parname;
+
+ tup = SearchSysCache1(PARAMETERACLOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for parameter ACL %u",
+ object->objectId);
+ break;
+ }
+ nameDatum = SysCacheGetAttr(PARAMETERACLOID, tup,
+ Anum_pg_parameter_acl_parname,
+ &isNull);
+ Assert(!isNull);
+ parname = TextDatumGetCString(nameDatum);
+ appendStringInfo(&buffer, _("parameter %s"), parname);
+ ReleaseSysCache(tup);
+ break;
+ }
+
case OCLASS_POLICY:
{
Relation policy_rel;
@@ -4547,6 +4586,10 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "event trigger");
break;
+ case OCLASS_PARAMETER_ACL:
+ appendStringInfoString(&buffer, "parameter ACL");
+ break;
+
case OCLASS_POLICY:
appendStringInfoString(&buffer, "policy");
break;
@@ -5693,6 +5736,34 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_PARAMETER_ACL:
+ {
+ HeapTuple tup;
+ Datum nameDatum;
+ bool isNull;
+ char *parname;
+
+ tup = SearchSysCache1(PARAMETERACLOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for parameter ACL %u",
+ object->objectId);
+ break;
+ }
+ nameDatum = SysCacheGetAttr(PARAMETERACLOID, tup,
+ Anum_pg_parameter_acl_parname,
+ &isNull);
+ Assert(!isNull);
+ parname = TextDatumGetCString(nameDatum);
+ appendStringInfoString(&buffer, parname);
+ if (objname)
+ *objname = list_make1(parname);
+ ReleaseSysCache(tup);
+ 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..2decee909b
--- /dev/null
+++ b/src/backend/catalog/pg_parameter_acl.c
@@ -0,0 +1,118 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_parameter_acl.c
+ * routines to support manipulation of the pg_parameter_acl relation
+ *
+ * Portions Copyright (c) 1996-2022, 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"
+#include "utils/syscache.h"
+
+
+/*
+ * ParameterAclLookup - Given a configuration parameter name,
+ * look up the associated configuration parameter ACL's OID.
+ *
+ * If missing_ok is false, throw an error if ACL entry not found. If
+ * true, just return InvalidOid.
+ */
+Oid
+ParameterAclLookup(const char *parameter, bool missing_ok)
+{
+ Oid oid;
+ char *parname;
+
+ /* Convert name to the form it should have in pg_parameter_acl... */
+ parname = convert_GUC_name_for_parameter_acl(parameter);
+
+ /* ... and look it up */
+ oid = GetSysCacheOid1(PARAMETERACLNAME, Anum_pg_parameter_acl_oid,
+ PointerGetDatum(cstring_to_text(parname)));
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("parameter ACL \"%s\" does not exist", parameter)));
+
+ pfree(parname);
+
+ return oid;
+}
+
+/*
+ * ParameterAclCreate
+ *
+ * Add a new tuple to pg_parameter_acl.
+ *
+ * parameter: the parameter name to create an entry for.
+ * Caller should have verified that there's no such entry already.
+ *
+ * Returns the new entry's OID.
+ */
+Oid
+ParameterAclCreate(const char *parameter)
+{
+ Oid parameterId;
+ char *parname;
+ Relation rel;
+ TupleDesc tupDesc;
+ HeapTuple tuple;
+ Datum values[Natts_pg_parameter_acl];
+ bool nulls[Natts_pg_parameter_acl];
+
+ /*
+ * To prevent cluttering pg_parameter_acl with useless entries, insist
+ * that the name be valid.
+ */
+ if (!check_GUC_name_for_parameter_acl(parameter))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid parameter name \"%s\"",
+ parameter)));
+
+ /* Convert name to the form it should have in pg_parameter_acl. */
+ parname = convert_GUC_name_for_parameter_acl(parameter);
+
+ /*
+ * Create and insert a new record containing a null 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));
+ parameterId = GetNewOidWithIndex(rel,
+ ParameterAclOidIndexId,
+ Anum_pg_parameter_acl_oid);
+ values[Anum_pg_parameter_acl_oid - 1] = ObjectIdGetDatum(parameterId);
+ values[Anum_pg_parameter_acl_parname - 1] =
+ PointerGetDatum(cstring_to_text(parname));
+ nulls[Anum_pg_parameter_acl_paracl - 1] = true;
+ tuple = heap_form_tuple(tupDesc, values, nulls);
+ CatalogTupleInsert(rel, tuple);
+
+ /* Close pg_parameter_acl, but keep lock till commit. */
+ heap_freetuple(tuple);
+ table_close(rel, NoLock);
+
+ return parameterId;
+}
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 1f64c8aa51..5456b8222b 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_ACL:
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..4642527881 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -940,6 +940,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_DATABASE:
case OBJECT_TABLESPACE:
case OBJECT_ROLE:
+ case OBJECT_PARAMETER_ACL:
/* no support for global objects */
return false;
case OBJECT_EVENT_TRIGGER:
@@ -1015,6 +1016,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
+ case OCLASS_PARAMETER_ACL:
/* no support for global objects */
return false;
case OCLASS_EVENT_TRIGGER:
@@ -2042,6 +2044,8 @@ stringify_grant_objtype(ObjectType objtype)
return "LARGE OBJECT";
case OBJECT_SCHEMA:
return "SCHEMA";
+ case OBJECT_PARAMETER_ACL:
+ return "PARAMETER";
case OBJECT_PROCEDURE:
return "PROCEDURE";
case OBJECT_ROUTINE:
@@ -2153,6 +2157,7 @@ stringify_adefprivs_objtype(ObjectType objtype)
case OBJECT_OPCLASS:
case OBJECT_OPERATOR:
case OBJECT_OPFAMILY:
+ case OBJECT_PARAMETER_ACL:
case OBJECT_POLICY:
case OBJECT_PUBLICATION:
case OBJECT_PUBLICATION_NAMESPACE:
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 7a62d547e2..7ae19b98bb 100644
--- a/src/backend/commands/seclabel.c
+++ b/src/backend/commands/seclabel.c
@@ -78,6 +78,7 @@ SecLabelSupportsObjectType(ObjectType objtype)
case OBJECT_OPCLASS:
case OBJECT_OPERATOR:
case OBJECT_OPFAMILY:
+ case OBJECT_PARAMETER_ACL:
case OBJECT_POLICY:
case OBJECT_PUBLICATION_NAMESPACE:
case OBJECT_PUBLICATION_REL:
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7febb5018f..a241b44497 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_ACL:
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 e5a3c528aa..af243dc2b9 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_name_list
%type <chr> am_type
@@ -799,8 +799,8 @@ 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
- POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
+ PARALLEL PARAMETER PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS
+ POLICY POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION
QUOTE QUOTES
@@ -7165,6 +7165,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 = NIL;
+ $$ = n;
+ }
| ColId opt_column_list
{
AccessPriv *n = makeNode(AccessPriv);
@@ -7174,6 +7181,28 @@ privilege: SELECT opt_column_list
}
;
+parameter_name_list:
+ parameter_name
+ {
+ $$ = list_make1(makeString($1));
+ }
+ | parameter_name_list ',' 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.
@@ -7275,6 +7304,14 @@ privilege_target:
n->objs = $3;
$$ = n;
}
+ | PARAMETER parameter_name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = OBJECT_PARAMETER_ACL;
+ n->objs = $2;
+ $$ = n;
+ }
| SCHEMA name_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -16761,6 +16798,7 @@ unreserved_keyword:
| OWNED
| OWNER
| PARALLEL
+ | PARAMETER
| PARSER
| PARTIAL
| PARTITION
@@ -17373,6 +17411,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..9263fa2901 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_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_ACL:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_PARAMETER_ACL;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -873,6 +886,9 @@ acldefault_sql(PG_FUNCTION_ARGS)
case 'L':
objtype = OBJECT_LARGEOBJECT;
break;
+ case 'p':
+ objtype = OBJECT_PARAMETER_ACL;
+ break;
case 'n':
objtype = OBJECT_SCHEMA;
break;
@@ -1602,6 +1618,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 +1718,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 +4453,96 @@ 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 with
+ * user name, user OID, or implicit user = current_user.
+ *
+ * The result is a boolean value: true if user has been granted
+ * the indicated privilege or false if not.
+ */
+
+/*
+ * has_param_priv_byname
+ *
+ * Helper function to check user privileges on a parameter given the
+ * role by Oid, parameter by text name, and privileges as AclMode.
+ */
+static bool
+has_param_priv_byname(Oid roleid, const text *parameter, AclMode priv)
+{
+ char *paramstr = text_to_cstring(parameter);
+
+ return pg_parameter_aclcheck(paramstr, roleid, priv) == ACLCHECK_OK;
+}
+
+/*
+ * 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);
+ AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(2));
+ Oid roleid = get_role_oid_or_public(NameStr(*username));
+
+ PG_RETURN_BOOL(has_param_priv_byname(roleid, parameter, priv));
+}
+
+/*
+ * 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);
+ AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(1));
+
+ PG_RETURN_BOOL(has_param_priv_byname(GetUserId(), parameter, priv));
+}
+
+/*
+ * 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);
+ AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(2));
+
+ PG_RETURN_BOOL(has_param_priv_byname(roleid, parameter, priv));
+}
+
+/*
+ * 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_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_text, parameter_priv_map);
+}
/*
* pg_has_role variants
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index a675877d19..8d265f2d23 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -47,6 +47,7 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_partitioned_table.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_publication.h"
@@ -574,6 +575,28 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {ParameterAclRelationId, /* PARAMETERACLNAME */
+ ParameterAclParnameIndexId,
+ 1,
+ {
+ Anum_pg_parameter_acl_parname,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+ {ParameterAclRelationId, /* PARAMETERACLOID */
+ ParameterAclOidIndexId,
+ 1,
+ {
+ Anum_pg_parameter_acl_oid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{PartitionedRelationId, /* PARTRELID */
PartitionedRelidIndexId,
1,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 9e8ab1420d..265008bdc6 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"
@@ -5713,6 +5714,65 @@ guc_name_compare(const char *namea, const char *nameb)
}
+/*
+ * Convert a GUC name to the form that should be used in pg_parameter_acl.
+ *
+ * We need to canonicalize entries since, for example, case should not be
+ * significant. In addition, we apply the map_old_guc_names[] mapping so that
+ * any obsolete names will be converted when stored in a new PG version.
+ * Note however that this function does not verify legality of the name.
+ *
+ * The result is a palloc'd string.
+ */
+char *
+convert_GUC_name_for_parameter_acl(const char *name)
+{
+ char *result;
+
+ /* Apply old-GUC-name mapping. */
+ for (int i = 0; map_old_guc_names[i] != NULL; i += 2)
+ {
+ if (guc_name_compare(name, map_old_guc_names[i]) == 0)
+ {
+ name = map_old_guc_names[i + 1];
+ break;
+ }
+ }
+
+ /* Apply case-folding that matches guc_name_compare(). */
+ result = pstrdup(name);
+ for (char *ptr = result; *ptr != '\0'; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch >= 'A' && ch <= 'Z')
+ {
+ ch += 'a' - 'A';
+ *ptr = ch;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Check whether we should allow creation of a pg_parameter_acl entry
+ * for the given name. (This can be applied either before or after
+ * canonicalizing it.)
+ */
+bool
+check_GUC_name_for_parameter_acl(const char *name)
+{
+ /* OK if the GUC exists. */
+ if (find_option(name, false, true, DEBUG1) != NULL)
+ return true;
+ /* Otherwise, it'd better be a valid custom GUC name. */
+ if (valid_custom_variable_name(name))
+ return true;
+ return false;
+}
+
+
/*
* Initialize GUC options during program startup.
*
@@ -7568,6 +7628,17 @@ 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 been granted privilege
+ * to set this GUC.
+ */
+ AclResult aclresult;
+
+ aclresult = pg_parameter_aclcheck(name, 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\"",
@@ -8617,11 +8688,6 @@ 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
*/
@@ -8649,6 +8715,29 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
break;
}
+ /*
+ * Check permission to run ALTER SYSTEM on the target variable
+ */
+ if (!superuser())
+ {
+ if (resetall)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to perform ALTER SYSTEM RESET ALL")));
+ else
+ {
+ AclResult aclresult;
+
+ aclresult = pg_parameter_aclcheck(name, GetUserId(),
+ ACL_ALTER_SYSTEM);
+ if (aclresult != ACLCHECK_OK)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+ }
+ }
+
/*
* Unless it's RESET_ALL, validate the target variable and value
*/
@@ -8760,13 +8849,18 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
}
/*
- * Invoke the post-alter hook for altering this GUC variable.
+ * Invoke the post-alter hook for setting this GUC variable. GUCs
+ * typically do not have corresponding entries in pg_parameter_acl, so we
+ * call the hook using the name rather than a potentially-non-existent
+ * OID. Nonetheless, we pass ParameterAclRelationId so that this call
+ * context can be distinguished from others. (Note that "name" will be
+ * NULL in the RESET ALL case.)
*
* 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,
+ InvokeObjectPostAlterHookArgStr(ParameterAclRelationId, name,
ACL_ALTER_SYSTEM,
altersysstmt->setstmt->kind,
false);
@@ -8943,9 +9037,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);
}
/*
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_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 9c9f7c6d63..2dc3362763 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -18,6 +18,7 @@
#include <time.h>
#include <unistd.h>
+#include "catalog/pg_authid_d.h"
#include "common/connect.h"
#include "common/file_utils.h"
#include "common/logging.h"
@@ -36,6 +37,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 +587,10 @@ main(int argc, char *argv[])
/* Dump role memberships */
dumpRoleMembership(conn);
+
+ /* Dump role GUC privileges */
+ if (server_version >= 150000 && !skip_acls)
+ dumpRoleGUCPrivs(conn);
}
/* Dump tablespaces */
@@ -989,6 +995,65 @@ dumpRoleMembership(PGconn *conn)
}
+/*
+ * 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 that have non-default acls defined.
+ */
+ res = executeQuery(conn, "SELECT parname, "
+ "pg_catalog.pg_get_userbyid(" CppAsString2(BOOTSTRAP_SUPERUSERID) ") AS parowner, "
+ "paracl, "
+ "pg_catalog.acldefault('p', " CppAsString2(BOOTSTRAP_SUPERUSERID) ") AS acldefault "
+ "FROM pg_catalog.pg_parameter_acl "
+ "ORDER BY 1");
+
+ if (PQntuples(res) > 0)
+ fprintf(OPF, "--\n-- Role privileges on configuration parameters\n--\n\n");
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ PQExpBuffer buf = createPQExpBuffer();
+ char *parname = PQgetvalue(res, i, 0);
+ char *parowner = PQgetvalue(res, i, 1);
+ char *paracl = PQgetvalue(res, i, 2);
+ char *acldefault = PQgetvalue(res, i, 3);
+ char *fparname;
+
+ /* needed for buildACLCommands() */
+ fparname = pg_strdup(fmtId(parname));
+
+ if (!buildACLCommands(fparname, NULL, NULL, "PARAMETER",
+ paracl, acldefault,
+ parowner, "", server_version, buf))
+ {
+ pg_log_error("could not parse ACL list (%s) for parameter \"%s\"",
+ paracl, parname);
+ PQfinish(conn);
+ exit_nicely(1);
+ }
+
+ fprintf(OPF, "%s", buf->data);
+
+ free(fparname);
+ destroyPQExpBuffer(buf);
+ }
+
+ PQclear(res);
+ fprintf(OPF, "\n\n");
+}
+
+
/*
* Drop tablespaces.
*/
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 6f040674a2..39e091b1fb 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3737,7 +3737,8 @@ 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("GRANT|REVOKE") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR"))
{
/*
* With ALTER DEFAULT PRIVILEGES, restrict completion to grantable
@@ -3749,6 +3750,7 @@ psql_completion(const char *text, int start, int end)
"EXECUTE", "USAGE", "ALL");
else
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
+ "GRANT",
"SELECT",
"INSERT",
"UPDATE",
@@ -3761,14 +3763,48 @@ psql_completion(const char *text, int start, int end)
"TEMPORARY",
"EXECUTE",
"USAGE",
+ "SET",
+ "ALTER SYSTEM",
"ALL");
}
+ else if (TailMatches("REVOKE", "GRANT"))
+ COMPLETE_WITH("OPTION FOR");
+ else if (TailMatches("REVOKE", "GRANT", "OPTION"))
+ COMPLETE_WITH("FOR");
+
+ 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", MatchAny, "ON", "PARAMETER") ||
+ TailMatches("GRANT|REVOKE", MatchAny, MatchAny, "ON", "PARAMETER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "PARAMETER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, MatchAny, "ON", "PARAMETER"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_alter_system_set_vars);
+
+ else if (TailMatches("GRANT", MatchAny, "ON", "PARAMETER", MatchAny) ||
+ TailMatches("GRANT", MatchAny, MatchAny, "ON", "PARAMETER", MatchAny))
+ COMPLETE_WITH("TO");
+
+ else if (TailMatches("REVOKE", MatchAny, "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", MatchAny, MatchAny, "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, MatchAny, "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");
@@ -3785,7 +3821,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
@@ -3807,6 +3844,7 @@ psql_completion(const char *text, int start, int end)
"FUNCTION",
"LANGUAGE",
"LARGE OBJECT",
+ "PARAMETER",
"PROCEDURE",
"ROUTINE",
"SCHEMA",
@@ -3815,13 +3853,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");
/*
@@ -3830,7 +3870,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);
@@ -3868,6 +3909,22 @@ 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);
+
+ /*
+ * Offer grant options after that.
+ */
+ 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", MatchAny, "OPTION"))
+ COMPLETE_WITH("GRANTED BY");
+ else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH", MatchAny, "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,
@@ -3879,7 +3936,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");
@@ -3888,7 +3946,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");
@@ -3897,7 +3956,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..d027075a4c 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_ACL, /* 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_parameter_acl.h b/src/include/catalog/pg_parameter_acl.h
new file mode 100644
index 0000000000..8316391e51
--- /dev/null
+++ b/src/include/catalog/pg_parameter_acl.h
@@ -0,0 +1,62 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_parameter_acl.h
+ * definition of the "configuration parameter ACL" system catalog
+ * (pg_parameter_acl).
+ *
+ *
+ * Portions Copyright (c) 1996-2022, 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 */
+
+#ifdef CATALOG_VARLEN /* variable-length fields start here */
+ /* name of parameter */
+ text parname BKI_FORCE_NOT_NULL;
+
+ /* access permissions */
+ aclitem paracl[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_parname_index, 8927, ParameterAclParnameIndexId, on pg_parameter_acl using btree(parname text_ops));
+DECLARE_UNIQUE_INDEX_PKEY(pg_parameter_acl_oid_index, 8928, ParameterAclOidIndexId, on pg_parameter_acl using btree(oid oid_ops));
+
+
+extern Oid ParameterAclLookup(const char *parameter, bool missing_ok);
+extern Oid ParameterAclCreate(const char *parameter);
+
+#endif /* PG_PARAMETER_ACL_H */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 25304430f4..4d285ece8b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7213,6 +7213,22 @@
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 user oid, parameter name',
+ proname => 'has_parameter_privilege', provolatile => 's',
+ prorettype => 'bool', proargtypes => 'oid text text',
+ prosrc => 'has_parameter_privilege_id_name' },
+{ oid => '8052',
+ 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 => '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 8a9ccf6221..cf5a967694 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -92,8 +92,8 @@ 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_ALTER_SYSTEM (1<<13) /* 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
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
@@ -2072,6 +2072,7 @@ typedef enum ObjectType
OBJECT_OPCLASS,
OBJECT_OPERATOR,
OBJECT_OPFAMILY,
+ OBJECT_PARAMETER_ACL,
OBJECT_POLICY,
OBJECT_PROCEDURE,
OBJECT_PUBLICATION,
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index a73032b319..836f984406 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -327,6 +327,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..48f7d72add 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 */
@@ -164,6 +164,7 @@ typedef struct ArrayType Acl;
#define ACL_ALL_RIGHTS_FUNCTION (ACL_EXECUTE)
#define ACL_ALL_RIGHTS_LANGUAGE (ACL_USAGE)
#define ACL_ALL_RIGHTS_LARGEOBJECT (ACL_SELECT|ACL_UPDATE)
+#define ACL_ALL_RIGHTS_PARAMETER_ACL (ACL_SET|ACL_ALTER_SYSTEM)
#define ACL_ALL_RIGHTS_SCHEMA (ACL_USAGE|ACL_CREATE)
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
#define ACL_ALL_RIGHTS_TYPE (ACL_USAGE)
@@ -245,6 +246,10 @@ 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_aclmask(const char *name, Oid roleid,
+ AclMode mask, AclMaskHow how);
+extern AclMode pg_parameter_acl_aclmask(Oid acl_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 +278,10 @@ 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_aclcheck(const char *name, Oid roleid,
+ AclMode mode);
+extern AclResult pg_parameter_acl_aclcheck(Oid acl_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..3446334e90 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -364,6 +364,8 @@ extern const char *GetConfigOption(const char *name, bool missing_ok,
extern const char *GetConfigOptionResetString(const char *name);
extern int GetConfigOptionFlags(const char *name, bool missing_ok);
extern void ProcessConfigFile(GucContext context);
+extern char *convert_GUC_name_for_parameter_acl(const char *name);
+extern bool check_GUC_name_for_parameter_acl(const char *name);
extern void InitializeGUCOptions(void);
extern bool SelectConfigFiles(const char *userDoption, const char *progname);
extern void ResetAllOptions(void);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 9c1a76e8bb..4463ea66be 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -72,6 +72,8 @@ enum SysCacheIdentifier
OPEROID,
OPFAMILYAMNAMENSP,
OPFAMILYOID,
+ PARAMETERACLNAME,
+ PARAMETERACLOID,
PARTRELID,
PROCNAMEARGSNSP,
PROCOID,
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..39b274b8fa 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,84 @@
+-- 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.
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 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 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 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 process utility: superuser finished GrantStmt
+-- Check when the hooks fire relative to dependency based abort of a drop
+DROP ROLE regress_role_joe;
+NOTICE: in process utility: superuser attempting DropRoleStmt
+NOTICE: in object access: superuser attempting drop (subId=0x0) []
+NOTICE: in object access: superuser finished drop (subId=0x0) []
+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
+-- Check the behavior of the hooks relative to do-nothing grants and revokes
+GRANT SET ON PARAMETER work_mem TO PUBLIC;
+NOTICE: in process utility: superuser attempting GrantStmt
+NOTICE: in process utility: superuser finished GrantStmt
+REVOKE ALTER SYSTEM ON PARAMETER work_mem FROM PUBLIC;
+NOTICE: in process utility: superuser attempting GrantStmt
+NOTICE: in process utility: superuser finished GrantStmt
+-- Check the behavior of the hooks relative to unrecognized parameters
+GRANT ALL ON PARAMETER "none.such" TO PUBLIC;
+NOTICE: in process utility: superuser attempting GrantStmt
+NOTICE: in process utility: superuser finished GrantStmt
+-- Check relative to an operation that causes the catalog entry to be deleted
+REVOKE ALL ON PARAMETER "none.such" FROM PUBLIC;
+NOTICE: in process utility: superuser attempting GrantStmt
+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 +87,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 +111,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 +158,89 @@ 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"
+SET test_oat_hooks.user_var1 = 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_var1]
+NOTICE: in object_access_hook_str: non-superuser finished alter (subId=0x1000, set) [test_oat_hooks.user_var1]
+NOTICE: in process utility: non-superuser finished set
+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"
+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 +259,43 @@ 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
+-- Clean up
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; -- fails
+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..8b6d5373aa 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,39 @@
+-- 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.
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;
+
+-- Check when the hooks fire relative to dependency based abort of a drop
+DROP ROLE regress_role_joe;
+
+-- Check the behavior of the hooks relative to do-nothing grants and revokes
+GRANT SET ON PARAMETER work_mem TO PUBLIC;
+REVOKE ALTER SYSTEM ON PARAMETER work_mem FROM PUBLIC;
+
+-- Check the behavior of the hooks relative to unrecognized parameters
+GRANT ALL ON PARAMETER "none.such" TO PUBLIC;
+
+-- Check relative to an operation that causes the catalog entry to be deleted
+REVOKE ALL ON PARAMETER "none.such" FROM PUBLIC;
+
-- Create objects for use in the test
CREATE USER regress_test_user;
CREATE TABLE regress_test_table (t text);
@@ -30,6 +59,16 @@ SET work_mem = 8192;
RESET work_mem;
ALTER SYSTEM SET work_mem = 8192;
ALTER SYSTEM RESET work_mem;
+
+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;
+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
@@ -48,6 +87,14 @@ RESET work_mem;
ALTER SYSTEM SET work_mem = 8192;
ALTER SYSTEM RESET work_mem;
+-- Clean up
RESET SESSION AUTHORIZATION;
SET test_oat_hooks.audit = false;
+DROP ROLE regress_role_joe; -- fails
+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..551da5d498 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;
+/*
+ * GUCs 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..d842f934a3 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,38 @@ 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 Custom.Knob TO regress_dump_test_role WITH GRANT OPTION'
+ => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET, ALTER SYSTEM ON PARAMETER Custom.Knob TO regress_dump_test_role WITH GRANT OPTION;',
+ regexp =>
+ # "set" plus "alter system" is "all" privileges on parameters
+ qr/^GRANT ALL ON PARAMETER "custom.knob" TO regress_dump_test_role WITH GRANT OPTION;/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..f62807fd05
--- /dev/null
+++ b/src/test/modules/unsafe_tests/expected/guc_privs.out
@@ -0,0 +1,525 @@
+--
+-- Tests for privileges on GUCs.
+-- This is unsafe because changes will affect other databases in the cluster.
+--
+-- Test with a superuser role.
+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
+-- 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
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+ has_parameter_privilege
+-------------------------
+ f
+(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
+-------------------------
+ f
+(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
+-------------------------
+ f
+(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
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+-- Revoke privileges from a non-existent custom GUC. This should not create
+-- entries in the catalog.
+REVOKE ALL ON PARAMETER "none.such" FROM regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'none.such';
+ ?column?
+----------
+(0 rows)
+
+-- Grant and then revoke privileges on the non-existent custom GUC. Check that
+-- a do-nothing entry is not left in the catalogs after the revoke.
+GRANT ALL ON PARAMETER none.such TO regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'none.such';
+ ?column?
+----------
+ 1
+(1 row)
+
+REVOKE ALL ON PARAMETER "None.Such" FROM regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'none.such';
+ ?column?
+----------
+(0 rows)
+
+-- Can't grant on a non-existent core GUC.
+GRANT ALL ON PARAMETER no_such_guc TO regress_host_resource_admin; -- fail
+ERROR: invalid parameter name "no_such_guc"
+-- Initially there are no privileges and no catalog entry for this GUC.
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+ ?column?
+----------
+(0 rows)
+
+-- GRANT SET creates an entry:
+GRANT SET ON PARAMETER enable_material TO PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+ ?column?
+----------
+ 1
+(1 row)
+
+-- Now grant ALTER SYSTEM:
+GRANT ALL ON PARAMETER enable_material TO PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+ ?column?
+----------
+ 1
+(1 row)
+
+-- REVOKE ALTER SYSTEM brings us back to just the SET privilege:
+REVOKE ALTER SYSTEM ON PARAMETER enable_material FROM PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+ ?column?
+----------
+ 1
+(1 row)
+
+-- And this should remove the entry altogether:
+REVOKE SET ON PARAMETER enable_material FROM PUBLIC;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+ ?column?
+----------
+(0 rows)
+
+-- Grant privileges on parameters to the new non-superuser role
+GRANT SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, max_stack_depth,
+ shared_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- 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((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('hash_mem_multiplier', 'set');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+-- Check object identity functions
+SELECT pg_describe_object(tableoid, oid, 0)
+FROM pg_parameter_acl WHERE parname = 'work_mem';
+ pg_describe_object
+--------------------
+ parameter work_mem
+(1 row)
+
+SELECT pg_identify_object(tableoid, oid, 0)
+FROM pg_parameter_acl WHERE parname = 'work_mem';
+ pg_identify_object
+------------------------------
+ ("parameter ACL",,,work_mem)
+(1 row)
+
+SELECT pg_identify_object_as_address(tableoid, oid, 0)
+FROM pg_parameter_acl WHERE parname = 'work_mem';
+ pg_identify_object_as_address
+---------------------------------
+ ("parameter ACL",{work_mem},{})
+(1 row)
+
+SELECT classid::regclass,
+ (SELECT parname FROM pg_parameter_acl WHERE oid = goa.objid) AS parname,
+ objsubid
+FROM pg_get_object_address('parameter ACL', '{work_mem}', '{}') goa;
+ classid | parname | objsubid
+------------------+----------+----------
+ pg_parameter_acl | work_mem | 0
+(1 row)
+
+-- Perform some 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
+RESET TimeZone; -- ok
+SET max_stack_depth = 2048; -- ok, privileges have been granted
+RESET max_stack_depth; -- ok, privileges have been granted
+ALTER SYSTEM SET max_stack_depth = 2048; -- ok, privileges have been granted
+ALTER SYSTEM RESET max_stack_depth; -- ok, privileges have been granted
+SET lc_messages = 'C'; -- fail, insufficient privileges
+ERROR: permission denied to set parameter "lc_messages"
+RESET lc_messages; -- fail, insufficient privileges
+ERROR: permission denied to set parameter "lc_messages"
+ALTER SYSTEM SET lc_messages = 'C'; -- fail, insufficient privileges
+ERROR: permission denied to set parameter "lc_messages"
+ALTER SYSTEM RESET lc_messages; -- fail, insufficient privileges
+ERROR: permission denied to set parameter "lc_messages"
+SELECT set_config ('temp_buffers', '8192', false); -- ok
+ set_config
+------------
+ 64MB
+(1 row)
+
+ALTER SYSTEM RESET autovacuum_work_mem; -- ok, privileges have been granted
+ALTER SYSTEM RESET ALL; -- fail, insufficient privileges
+ERROR: permission denied to perform ALTER SYSTEM RESET ALL
+-- Check dropping/revoking behavior
+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 autovacuum_work_mem
+privileges for parameter hash_mem_multiplier
+privileges for parameter max_stack_depth
+privileges for parameter shared_buffers
+privileges for parameter temp_file_limit
+privileges for parameter work_mem
+-- 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, max_stack_depth,
+ 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, max_stack_depth,
+ 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 autovacuum_work_mem
+privileges for parameter hash_mem_multiplier
+privileges for parameter max_stack_depth
+privileges for parameter shared_buffers
+privileges for parameter temp_file_limit
+privileges for parameter work_mem
+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
+-- Check that "reassign owned" doesn't affect privileges
+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, max_stack_depth,
+ 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 autovacuum_work_mem
+privileges for parameter hash_mem_multiplier
+privileges for parameter max_stack_depth
+privileges for parameter shared_buffers
+privileges for parameter temp_file_limit
+privileges for parameter work_mem
+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
+-- Clean up
+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..b38622803d
--- /dev/null
+++ b/src/test/modules/unsafe_tests/sql/guc_privs.sql
@@ -0,0 +1,236 @@
+--
+-- Tests for privileges on GUCs.
+-- This is unsafe because changes will affect other databases in the cluster.
+--
+
+-- Test with a superuser role.
+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
+
+-- 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');
+
+-- Revoke privileges from a non-existent custom GUC. This should not create
+-- entries in the catalog.
+REVOKE ALL ON PARAMETER "none.such" FROM regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'none.such';
+-- Grant and then revoke privileges on the non-existent custom GUC. Check that
+-- a do-nothing entry is not left in the catalogs after the revoke.
+GRANT ALL ON PARAMETER none.such TO regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'none.such';
+REVOKE ALL ON PARAMETER "None.Such" FROM regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'none.such';
+-- Can't grant on a non-existent core GUC.
+GRANT ALL ON PARAMETER no_such_guc TO regress_host_resource_admin; -- fail
+
+-- Initially there are no privileges and no catalog entry for this GUC.
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+-- GRANT SET creates an entry:
+GRANT SET ON PARAMETER enable_material TO PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+-- Now grant ALTER SYSTEM:
+GRANT ALL ON PARAMETER enable_material TO PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+-- REVOKE ALTER SYSTEM brings us back to just the SET privilege:
+REVOKE ALTER SYSTEM ON PARAMETER enable_material FROM PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+-- And this should remove the entry altogether:
+REVOKE SET ON PARAMETER enable_material FROM PUBLIC;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+
+-- Grant privileges on parameters to the new non-superuser role
+GRANT SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, max_stack_depth,
+ shared_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- 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((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET');
+SELECT has_parameter_privilege('hash_mem_multiplier', 'set');
+
+-- Check object identity functions
+SELECT pg_describe_object(tableoid, oid, 0)
+FROM pg_parameter_acl WHERE parname = 'work_mem';
+SELECT pg_identify_object(tableoid, oid, 0)
+FROM pg_parameter_acl WHERE parname = 'work_mem';
+SELECT pg_identify_object_as_address(tableoid, oid, 0)
+FROM pg_parameter_acl WHERE parname = 'work_mem';
+SELECT classid::regclass,
+ (SELECT parname FROM pg_parameter_acl WHERE oid = goa.objid) AS parname,
+ objsubid
+FROM pg_get_object_address('parameter ACL', '{work_mem}', '{}') goa;
+
+-- Perform some 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
+RESET TimeZone; -- ok
+SET max_stack_depth = 2048; -- ok, privileges have been granted
+RESET max_stack_depth; -- ok, privileges have been granted
+ALTER SYSTEM SET max_stack_depth = 2048; -- ok, privileges have been granted
+ALTER SYSTEM RESET max_stack_depth; -- ok, privileges have been granted
+SET lc_messages = 'C'; -- fail, insufficient privileges
+RESET lc_messages; -- fail, insufficient privileges
+ALTER SYSTEM SET lc_messages = 'C'; -- fail, insufficient privileges
+ALTER SYSTEM RESET lc_messages; -- fail, insufficient privileges
+SELECT set_config ('temp_buffers', '8192', false); -- ok
+ALTER SYSTEM RESET autovacuum_work_mem; -- ok, privileges have been granted
+ALTER SYSTEM RESET ALL; -- fail, insufficient privileges
+
+-- Check dropping/revoking behavior
+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, max_stack_depth,
+ 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, max_stack_depth,
+ 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
+
+-- Check that "reassign owned" doesn't affect privileges
+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, max_stack_depth,
+ 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
+
+-- Clean up
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_admin; -- ok
I wrote:
Anyway, I'm out of patience with this for today, and I'm throwing
it up for the cfbot to have a look at. I'll hit the docs tomorrow.
... and of course, the cfbot gave me the raspberry, because of a
merge conflict in gram.y. Here's v17 rebased up to HEAD.
regards, tom lane
Attachments:
v17-0001-Allow-grant-and-revoke-of-privileges-on-paramete.patchtext/x-diff; charset=us-ascii; name*0=v17-0001-Allow-grant-and-revoke-of-privileges-on-paramete.p; name*1=atchDownload
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 7f4f79d1b5..6fab8714b5 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 for which privileges have been granted</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>
@@ -5450,6 +5455,73 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</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 for which privileges have been 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>oid</structfield> <type>oid</type>
+ </para>
+ <para>
+ Row identifier
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>parname</structfield> <type>text</type>
+ </para>
+ <para>
+ The name of a configuration parameter for which privileges are granted
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>paracl</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-partitioned-table">
<title><structname>pg_partitioned_table</structname></title>
@@ -12780,7 +12852,9 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
<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
+ or within a session via the <command>SET</command> command; but only
+ superusers and users with <literal>SET</literal> privilege granted
+ on the specific setting
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>.
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 166b7a352d..84771ab6c6 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
@@ -1977,6 +1998,10 @@ REVOKE ALL ON accounts FROM PUBLIC;
large objects,
schemas,
or tablespaces.
+ Some parameters are inherently settable by <literal>PUBLIC</literal>, as if
+ <literal>SET</literal> were granted, but this is not implemented as a grant,
+ nor can the ability for <literal>PUBLIC</literal> to set these parameters be
+ revoked.
For other types of objects, the default privileges
granted to <literal>PUBLIC</literal> are as follows:
<literal>CONNECT</literal> and <literal>TEMPORARY</literal> (create
@@ -2097,6 +2122,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>
@@ -2167,6 +2202,12 @@ REVOKE ALL ON accounts FROM PUBLIC;
<entry>none</entry>
<entry><literal>\dl+</literal></entry>
</row>
+ <row>
+ <entry><literal>PARAMETER</literal></entry>
+ <entry><literal>sA</literal></entry>
+ <entry>none</entry>
+ <entry>none</entry>
+ </row>
<row>
<entry><literal>SCHEMA</literal></entry>
<entry><literal>UC</literal></entry>
@@ -2274,6 +2315,14 @@ 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>
+ No privileges are required for <literal>PUBLIC</literal> to
+ <command>SET</command> and <command>RESET</command> a
+ <literal>user</literal> parameter. 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 4001cb2bda..22ea39b842 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -22701,8 +22701,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');
@@ -22836,6 +22835,24 @@ 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>,
+ <parameter>privilege</parameter> <type>text</type> )
+ <returnvalue>boolean</returnvalue>
+ </para>
+ <para>
+ Does user have privilege for configuration 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/alter_system.sgml b/doc/src/sgml/ref/alter_system.sgml
index 5e41f7f644..6f8bd39eaf 100644
--- a/doc/src/sgml/ref/alter_system.sgml
+++ b/doc/src/sgml/ref/alter_system.sgml
@@ -55,7 +55,8 @@ ALTER SYSTEM RESET ALL
</para>
<para>
- Only superusers can use <command>ALTER SYSTEM</command>. Also, since
+ Only superusers and users granted <literal>ALTER SYSTEM</literal> privilege
+ on a parameter can change it using <command>ALTER SYSTEM</command>. Also, since
this command acts directly on the file system and cannot be rolled back,
it is not allowed inside a transaction block or function.
</para>
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..89a0221ec9 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -38,6 +38,7 @@ OBJS = \
pg_largeobject.o \
pg_namespace.o \
pg_operator.o \
+ pg_parameter_acl.o \
pg_proc.o \
pg_publication.o \
pg_range.o \
@@ -68,7 +69,8 @@ CATALOG_HEADERS := \
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
pg_foreign_table.h pg_policy.h pg_replication_origin.h \
pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \
- pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
+ pg_collation.h pg_parameter_acl.h pg_partitioned_table.h \
+ pg_range.h pg_transform.h \
pg_sequence.h pg_publication.h pg_publication_namespace.h \
pg_publication_rel.h pg_subscription.h pg_subscription_rel.h
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 1dd03a8e51..5f1726c095 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -48,6 +48,7 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
@@ -112,11 +113,13 @@ 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);
-static List *objectNamesToOids(ObjectType objtype, List *objnames);
+static List *objectNamesToOids(ObjectType objtype, List *objnames,
+ bool is_grant);
static List *objectsInSchemaToOids(ObjectType objtype, List *nspnames);
static List *getRelationsInNamespace(Oid namespaceId, char relkind);
static void expand_col_privileges(List *colnames, Oid table_oid,
@@ -259,6 +262,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_ACL:
+ whole_mask = ACL_ALL_RIGHTS_PARAMETER_ACL;
+ break;
default:
elog(ERROR, "unrecognized object type: %d", objtype);
/* not reached, but keep compiler quiet */
@@ -390,7 +396,8 @@ ExecuteGrantStmt(GrantStmt *stmt)
switch (stmt->targtype)
{
case ACL_TARGET_OBJECT:
- istmt.objects = objectNamesToOids(stmt->objtype, stmt->objects);
+ istmt.objects = objectNamesToOids(stmt->objtype, stmt->objects,
+ stmt->is_grant);
break;
case ACL_TARGET_ALL_IN_SCHEMA:
istmt.objects = objectsInSchemaToOids(stmt->objtype, stmt->objects);
@@ -498,6 +505,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_ACL:
+ all_privileges = ACL_ALL_RIGHTS_PARAMETER_ACL;
+ errormsg = gettext_noop("invalid privilege type %s for parameter");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
@@ -600,6 +611,9 @@ ExecGrantStmt_oids(InternalGrant *istmt)
case OBJECT_TABLESPACE:
ExecGrant_Tablespace(istmt);
break;
+ case OBJECT_PARAMETER_ACL:
+ ExecGrant_Parameter(istmt);
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
@@ -626,7 +640,7 @@ ExecGrantStmt_oids(InternalGrant *istmt)
* to fail.
*/
static List *
-objectNamesToOids(ObjectType objtype, List *objnames)
+objectNamesToOids(ObjectType objtype, List *objnames, bool is_grant)
{
List *objects = NIL;
ListCell *cell;
@@ -759,6 +773,37 @@ objectNamesToOids(ObjectType objtype, List *objnames)
objects = lappend_oid(objects, srvid);
}
break;
+ case OBJECT_PARAMETER_ACL:
+ foreach(cell, objnames)
+ {
+ /*
+ * In this code we represent a GUC by the OID of its entry in
+ * pg_parameter_acl, which we have to manufacture here if it
+ * doesn't exist yet. (That's a hack for sure, but it avoids
+ * messing with all the GRANT/REVOKE infrastructure that
+ * expects to use OIDs for object identities.) However, if
+ * this is a REVOKE, we can instead just ignore any GUCs that
+ * don't have such an entry, as they must not have any
+ * privileges needing removal.
+ */
+ char *parameter = strVal(lfirst(cell));
+ Oid parameterId = ParameterAclLookup(parameter, true);
+
+ if (!OidIsValid(parameterId) && is_grant)
+ {
+ parameterId = ParameterAclCreate(parameter);
+
+ /*
+ * Prevent error when processing duplicate objects, and
+ * make this new entry visible so that ExecGrant_Parameter
+ * can update it.
+ */
+ CommandCounterIncrement();
+ }
+ if (OidIsValid(parameterId))
+ objects = lappend_oid(objects, parameterId);
+ }
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) objtype);
@@ -1494,6 +1539,9 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
case ForeignDataWrapperRelationId:
istmt.objtype = OBJECT_FDW;
break;
+ case ParameterAclRelationId:
+ istmt.objtype = OBJECT_PARAMETER_ACL;
+ break;
default:
elog(ERROR, "unexpected object class %u", classid);
break;
@@ -3225,6 +3273,154 @@ 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_ACL;
+
+ relation = table_open(ParameterAclRelationId, RowExclusiveLock);
+
+ foreach(cell, istmt->objects)
+ {
+ Oid parameterId = lfirst_oid(cell);
+ Datum nameDatum;
+ const char *parname;
+ Datum aclDatum;
+ bool isNull;
+ AclMode avail_goptions;
+ AclMode this_privileges;
+ Acl *old_acl;
+ Acl *new_acl;
+ Oid grantorId;
+ Oid ownerId;
+ HeapTuple tuple;
+ int noldmembers;
+ int nnewmembers;
+ Oid *oldmembers;
+ Oid *newmembers;
+
+ tuple = SearchSysCache1(PARAMETERACLOID, ObjectIdGetDatum(parameterId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for parameter ACL %u",
+ parameterId);
+
+ /* We'll need the GUC's name */
+ nameDatum = SysCacheGetAttr(PARAMETERACLOID, tuple,
+ Anum_pg_parameter_acl_parname,
+ &isNull);
+ Assert(!isNull);
+ parname = TextDatumGetCString(nameDatum);
+
+ /* Treat all parameters as belonging to the bootstrap superuser. */
+ ownerId = BOOTSTRAP_SUPERUSERID;
+
+ /*
+ * Get working copy of existing ACL. If there's no ACL, substitute the
+ * proper default.
+ */
+ aclDatum = SysCacheGetAttr(PARAMETERACLOID, tuple,
+ Anum_pg_parameter_acl_paracl,
+ &isNull);
+
+ if (isNull)
+ {
+ old_acl = acldefault(istmt->objtype, ownerId);
+ /* 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, ownerId,
+ &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_ACL,
+ parname,
+ 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, ownerId);
+
+ /*
+ * We need the members of both old and new ACLs so we can correct the
+ * shared dependency information.
+ */
+ nnewmembers = aclmembers(new_acl, &newmembers);
+
+ /*
+ * If the new ACL is equal to the default, we don't need the catalog
+ * entry any longer. Delete it rather than updating it, to avoid
+ * leaving a degenerate entry.
+ */
+ if (aclequal(new_acl, acldefault(istmt->objtype, ownerId)))
+ {
+ CatalogTupleDelete(relation, &tuple->t_self);
+ }
+ else
+ {
+ /* finished building new ACL value, now insert it */
+ HeapTuple newtuple;
+ Datum values[Natts_pg_parameter_acl];
+ bool nulls[Natts_pg_parameter_acl];
+ bool replaces[Natts_pg_parameter_acl];
+
+ MemSet(values, 0, sizeof(values));
+ MemSet(nulls, false, sizeof(nulls));
+ MemSet(replaces, false, sizeof(replaces));
+
+ replaces[Anum_pg_parameter_acl_paracl - 1] = true;
+ values[Anum_pg_parameter_acl_paracl - 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, parameterId, 0,
+ ownerId,
+ noldmembers, oldmembers,
+ nnewmembers, newmembers);
+
+ ReleaseSysCache(tuple);
+ pfree(new_acl);
+
+ /* prevent error when processing duplicate objects */
+ CommandCounterIncrement();
+ }
+
+ table_close(relation, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3255,6 +3451,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 +3492,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);
}
@@ -3376,6 +3580,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_OPFAMILY:
msg = gettext_noop("permission denied for operator family %s");
break;
+ case OBJECT_PARAMETER_ACL:
+ msg = gettext_noop("permission denied for parameter %s");
+ break;
case OBJECT_POLICY:
msg = gettext_noop("permission denied for policy %s");
break;
@@ -3567,6 +3774,7 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_DEFAULT:
case OBJECT_DEFACL:
case OBJECT_DOMCONSTRAINT:
+ case OBJECT_PARAMETER_ACL:
case OBJECT_PUBLICATION_NAMESPACE:
case OBJECT_PUBLICATION_REL:
case OBJECT_ROLE:
@@ -3653,6 +3861,8 @@ pg_aclmask(ObjectType objtype, Oid table_oid, AttrNumber attnum, Oid roleid,
case OBJECT_LARGEOBJECT:
return pg_largeobject_aclmask_snapshot(table_oid, roleid,
mask, how, NULL);
+ case OBJECT_PARAMETER_ACL:
+ return pg_parameter_acl_aclmask(table_oid, roleid, mask, how);
case OBJECT_SCHEMA:
return pg_namespace_aclmask(table_oid, roleid, mask, how);
case OBJECT_STATISTIC_EXT:
@@ -4000,6 +4210,121 @@ pg_database_aclmask(Oid db_oid, Oid roleid,
return result;
}
+/*
+ * Exported routine for examining a user's privileges for a configuration
+ * parameter (GUC), identified by GUC name.
+ */
+AclMode
+pg_parameter_aclmask(const char *name, Oid roleid, AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ char *parname;
+ text *partext;
+ HeapTuple tuple;
+
+ /* Superusers bypass all permission checking. */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /* Convert name to the form it should have in pg_parameter_acl... */
+ parname = convert_GUC_name_for_parameter_acl(name);
+ partext = cstring_to_text(parname);
+
+ /* ... and look it up */
+ tuple = SearchSysCache1(PARAMETERACLNAME, PointerGetDatum(partext));
+
+ if (!HeapTupleIsValid(tuple))
+ {
+ /* If no entry, GUC has no permissions for non-superusers */
+ result = ACL_NO_RIGHTS;
+ }
+ else
+ {
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+
+ aclDatum = SysCacheGetAttr(PARAMETERACLNAME, tuple,
+ Anum_pg_parameter_acl_paracl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_PARAMETER_ACL, BOOTSTRAP_SUPERUSERID);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, BOOTSTRAP_SUPERUSERID, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+ }
+
+ pfree(parname);
+ pfree(partext);
+
+ return result;
+}
+
+/*
+ * Exported routine for examining a user's privileges for a configuration
+ * parameter (GUC), identified by the OID of its pg_parameter_acl entry.
+ */
+AclMode
+pg_parameter_acl_aclmask(Oid acl_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 ACL from pg_parameter_acl */
+ tuple = SearchSysCache1(PARAMETERACLOID, ObjectIdGetDatum(acl_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("parameter ACL with OID %u does not exist",
+ acl_oid)));
+
+ aclDatum = SysCacheGetAttr(PARAMETERACLOID, tuple,
+ Anum_pg_parameter_acl_paracl,
+ &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(OBJECT_PARAMETER_ACL, BOOTSTRAP_SUPERUSERID);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ result = aclmask(acl, roleid, BOOTSTRAP_SUPERUSERID, 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 +5038,32 @@ 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 (GUC), identified by GUC name.
+ */
+AclResult
+pg_parameter_aclcheck(const char *name, Oid roleid, AclMode mode)
+{
+ if (pg_parameter_aclmask(name, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
+/*
+ * Exported routine for checking a user's access privileges to a configuration
+ * parameter (GUC), identified by the OID of its pg_parameter_acl entry.
+ */
+AclResult
+pg_parameter_acl_aclcheck(Oid acl_oid, Oid roleid, AclMode mode)
+{
+ if (pg_parameter_acl_aclmask(acl_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..520f77971b 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -33,6 +33,7 @@
#include "catalog/pg_db_role_setting.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_namespace.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_replication_origin.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
@@ -247,32 +248,35 @@ IsSharedRelation(Oid relationId)
if (relationId == AuthIdRelationId ||
relationId == AuthMemRelationId ||
relationId == DatabaseRelationId ||
- relationId == SharedDescriptionRelationId ||
- relationId == SharedDependRelationId ||
- relationId == SharedSecLabelRelationId ||
- relationId == TableSpaceRelationId ||
relationId == DbRoleSettingRelationId ||
+ relationId == ParameterAclRelationId ||
relationId == ReplicationOriginRelationId ||
- relationId == SubscriptionRelationId)
+ relationId == SharedDependRelationId ||
+ relationId == SharedDescriptionRelationId ||
+ relationId == SharedSecLabelRelationId ||
+ relationId == SubscriptionRelationId ||
+ relationId == TableSpaceRelationId)
return true;
/* These are their indexes */
- if (relationId == AuthIdRolnameIndexId ||
- relationId == AuthIdOidIndexId ||
- relationId == AuthMemRoleMemIndexId ||
+ if (relationId == AuthIdOidIndexId ||
+ relationId == AuthIdRolnameIndexId ||
relationId == AuthMemMemRoleIndexId ||
+ relationId == AuthMemRoleMemIndexId ||
relationId == DatabaseNameIndexId ||
relationId == DatabaseOidIndexId ||
- relationId == SharedDescriptionObjIndexId ||
- relationId == SharedDependDependerIndexId ||
- relationId == SharedDependReferenceIndexId ||
- relationId == SharedSecLabelObjectIndexId ||
- relationId == TablespaceOidIndexId ||
- relationId == TablespaceNameIndexId ||
relationId == DbRoleSettingDatidRolidIndexId ||
+ relationId == ParameterAclOidIndexId ||
+ relationId == ParameterAclParnameIndexId ||
relationId == ReplicationOriginIdentIndex ||
relationId == ReplicationOriginNameIndex ||
+ relationId == SharedDependDependerIndexId ||
+ relationId == SharedDependReferenceIndexId ||
+ relationId == SharedDescriptionObjIndexId ||
+ relationId == SharedSecLabelObjectIndexId ||
+ relationId == SubscriptionNameIndexId ||
relationId == SubscriptionObjectIndexId ||
- relationId == SubscriptionNameIndexId)
+ relationId == TablespaceNameIndexId ||
+ relationId == TablespaceOidIndexId)
return true;
/* These are their toast tables and toast indexes */
if (relationId == PgAuthidToastTable ||
@@ -281,6 +285,8 @@ IsSharedRelation(Oid relationId)
relationId == PgDatabaseToastIndex ||
relationId == PgDbRoleSettingToastTable ||
relationId == PgDbRoleSettingToastIndex ||
+ relationId == PgParameterAclToastTable ||
+ relationId == PgParameterAclToastIndex ||
relationId == PgReplicationOriginToastTable ||
relationId == PgReplicationOriginToastIndex ||
relationId == PgShdescriptionToastTable ||
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 25fe56d310..de10923391 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -46,6 +46,7 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_policy.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_publication.h"
@@ -178,6 +179,7 @@ static const Oid object_classes[] = {
DefaultAclRelationId, /* OCLASS_DEFACL */
ExtensionRelationId, /* OCLASS_EXTENSION */
EventTriggerRelationId, /* OCLASS_EVENT_TRIGGER */
+ ParameterAclRelationId, /* OCLASS_PARAMETER_ACL */
PolicyRelationId, /* OCLASS_POLICY */
PublicationNamespaceRelationId, /* OCLASS_PUBLICATION_NAMESPACE */
PublicationRelationId, /* OCLASS_PUBLICATION */
@@ -1507,6 +1509,7 @@ doDeletion(const ObjectAddress *object, int flags)
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_SUBSCRIPTION:
+ case OCLASS_PARAMETER_ACL:
elog(ERROR, "global objects cannot be deleted by doDeletion");
break;
@@ -2861,6 +2864,9 @@ getObjectClass(const ObjectAddress *object)
case EventTriggerRelationId:
return OCLASS_EVENT_TRIGGER;
+ case ParameterAclRelationId:
+ return OCLASS_PARAMETER_ACL;
+
case PolicyRelationId:
return OCLASS_POLICY;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 3fd17ea64f..31c80f7209 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -45,6 +45,7 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_policy.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_publication.h"
@@ -818,6 +819,10 @@ static const struct object_type_map
{
"event trigger", OBJECT_EVENT_TRIGGER
},
+ /* OCLASS_PARAMETER_ACL */
+ {
+ "parameter ACL", OBJECT_PARAMETER_ACL
+ },
/* OCLASS_POLICY */
{
"policy", OBJECT_POLICY
@@ -1014,6 +1019,7 @@ get_object_address(ObjectType objtype, Node *object,
case OBJECT_FDW:
case OBJECT_FOREIGN_SERVER:
case OBJECT_EVENT_TRIGGER:
+ case OBJECT_PARAMETER_ACL:
case OBJECT_ACCESS_METHOD:
case OBJECT_PUBLICATION:
case OBJECT_SUBSCRIPTION:
@@ -1315,6 +1321,11 @@ get_object_address_unqualified(ObjectType objtype,
address.objectId = get_event_trigger_oid(name, missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_PARAMETER_ACL:
+ address.classId = ParameterAclRelationId;
+ address.objectId = ParameterAclLookup(name, missing_ok);
+ address.objectSubId = 0;
+ break;
case OBJECT_PUBLICATION:
address.classId = PublicationRelationId;
address.objectId = get_publication_oid(name, missing_ok);
@@ -2307,6 +2318,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_FDW:
case OBJECT_FOREIGN_SERVER:
case OBJECT_LANGUAGE:
+ case OBJECT_PARAMETER_ACL:
case OBJECT_PUBLICATION:
case OBJECT_ROLE:
case OBJECT_SCHEMA:
@@ -2597,6 +2609,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
case OBJECT_ACCESS_METHOD:
+ case OBJECT_PARAMETER_ACL:
/* We treat these object types as being owned by superusers */
if (!superuser_arg(roleid))
ereport(ERROR,
@@ -3880,6 +3893,32 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
break;
}
+ case OCLASS_PARAMETER_ACL:
+ {
+ HeapTuple tup;
+ Datum nameDatum;
+ bool isNull;
+ char *parname;
+
+ tup = SearchSysCache1(PARAMETERACLOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for parameter ACL %u",
+ object->objectId);
+ break;
+ }
+ nameDatum = SysCacheGetAttr(PARAMETERACLOID, tup,
+ Anum_pg_parameter_acl_parname,
+ &isNull);
+ Assert(!isNull);
+ parname = TextDatumGetCString(nameDatum);
+ appendStringInfo(&buffer, _("parameter %s"), parname);
+ ReleaseSysCache(tup);
+ break;
+ }
+
case OCLASS_POLICY:
{
Relation policy_rel;
@@ -4547,6 +4586,10 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "event trigger");
break;
+ case OCLASS_PARAMETER_ACL:
+ appendStringInfoString(&buffer, "parameter ACL");
+ break;
+
case OCLASS_POLICY:
appendStringInfoString(&buffer, "policy");
break;
@@ -5693,6 +5736,34 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_PARAMETER_ACL:
+ {
+ HeapTuple tup;
+ Datum nameDatum;
+ bool isNull;
+ char *parname;
+
+ tup = SearchSysCache1(PARAMETERACLOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ {
+ if (!missing_ok)
+ elog(ERROR, "cache lookup failed for parameter ACL %u",
+ object->objectId);
+ break;
+ }
+ nameDatum = SysCacheGetAttr(PARAMETERACLOID, tup,
+ Anum_pg_parameter_acl_parname,
+ &isNull);
+ Assert(!isNull);
+ parname = TextDatumGetCString(nameDatum);
+ appendStringInfoString(&buffer, parname);
+ if (objname)
+ *objname = list_make1(parname);
+ ReleaseSysCache(tup);
+ 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..2decee909b
--- /dev/null
+++ b/src/backend/catalog/pg_parameter_acl.c
@@ -0,0 +1,118 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_parameter_acl.c
+ * routines to support manipulation of the pg_parameter_acl relation
+ *
+ * Portions Copyright (c) 1996-2022, 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"
+#include "utils/syscache.h"
+
+
+/*
+ * ParameterAclLookup - Given a configuration parameter name,
+ * look up the associated configuration parameter ACL's OID.
+ *
+ * If missing_ok is false, throw an error if ACL entry not found. If
+ * true, just return InvalidOid.
+ */
+Oid
+ParameterAclLookup(const char *parameter, bool missing_ok)
+{
+ Oid oid;
+ char *parname;
+
+ /* Convert name to the form it should have in pg_parameter_acl... */
+ parname = convert_GUC_name_for_parameter_acl(parameter);
+
+ /* ... and look it up */
+ oid = GetSysCacheOid1(PARAMETERACLNAME, Anum_pg_parameter_acl_oid,
+ PointerGetDatum(cstring_to_text(parname)));
+
+ if (!OidIsValid(oid) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("parameter ACL \"%s\" does not exist", parameter)));
+
+ pfree(parname);
+
+ return oid;
+}
+
+/*
+ * ParameterAclCreate
+ *
+ * Add a new tuple to pg_parameter_acl.
+ *
+ * parameter: the parameter name to create an entry for.
+ * Caller should have verified that there's no such entry already.
+ *
+ * Returns the new entry's OID.
+ */
+Oid
+ParameterAclCreate(const char *parameter)
+{
+ Oid parameterId;
+ char *parname;
+ Relation rel;
+ TupleDesc tupDesc;
+ HeapTuple tuple;
+ Datum values[Natts_pg_parameter_acl];
+ bool nulls[Natts_pg_parameter_acl];
+
+ /*
+ * To prevent cluttering pg_parameter_acl with useless entries, insist
+ * that the name be valid.
+ */
+ if (!check_GUC_name_for_parameter_acl(parameter))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid parameter name \"%s\"",
+ parameter)));
+
+ /* Convert name to the form it should have in pg_parameter_acl. */
+ parname = convert_GUC_name_for_parameter_acl(parameter);
+
+ /*
+ * Create and insert a new record containing a null 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));
+ parameterId = GetNewOidWithIndex(rel,
+ ParameterAclOidIndexId,
+ Anum_pg_parameter_acl_oid);
+ values[Anum_pg_parameter_acl_oid - 1] = ObjectIdGetDatum(parameterId);
+ values[Anum_pg_parameter_acl_parname - 1] =
+ PointerGetDatum(cstring_to_text(parname));
+ nulls[Anum_pg_parameter_acl_paracl - 1] = true;
+ tuple = heap_form_tuple(tupDesc, values, nulls);
+ CatalogTupleInsert(rel, tuple);
+
+ /* Close pg_parameter_acl, but keep lock till commit. */
+ heap_freetuple(tuple);
+ table_close(rel, NoLock);
+
+ return parameterId;
+}
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 1f64c8aa51..5456b8222b 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_ACL:
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..4642527881 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -940,6 +940,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_DATABASE:
case OBJECT_TABLESPACE:
case OBJECT_ROLE:
+ case OBJECT_PARAMETER_ACL:
/* no support for global objects */
return false;
case OBJECT_EVENT_TRIGGER:
@@ -1015,6 +1016,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_DATABASE:
case OCLASS_TBLSPACE:
case OCLASS_ROLE:
+ case OCLASS_PARAMETER_ACL:
/* no support for global objects */
return false;
case OCLASS_EVENT_TRIGGER:
@@ -2042,6 +2044,8 @@ stringify_grant_objtype(ObjectType objtype)
return "LARGE OBJECT";
case OBJECT_SCHEMA:
return "SCHEMA";
+ case OBJECT_PARAMETER_ACL:
+ return "PARAMETER";
case OBJECT_PROCEDURE:
return "PROCEDURE";
case OBJECT_ROUTINE:
@@ -2153,6 +2157,7 @@ stringify_adefprivs_objtype(ObjectType objtype)
case OBJECT_OPCLASS:
case OBJECT_OPERATOR:
case OBJECT_OPFAMILY:
+ case OBJECT_PARAMETER_ACL:
case OBJECT_POLICY:
case OBJECT_PUBLICATION:
case OBJECT_PUBLICATION_NAMESPACE:
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 7a62d547e2..7ae19b98bb 100644
--- a/src/backend/commands/seclabel.c
+++ b/src/backend/commands/seclabel.c
@@ -78,6 +78,7 @@ SecLabelSupportsObjectType(ObjectType objtype)
case OBJECT_OPCLASS:
case OBJECT_OPERATOR:
case OBJECT_OPFAMILY:
+ case OBJECT_PARAMETER_ACL:
case OBJECT_POLICY:
case OBJECT_PUBLICATION_NAMESPACE:
case OBJECT_PUBLICATION_REL:
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7febb5018f..a241b44497 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_ACL:
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 7e3f4a5d27..2cc92a8943 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_name_list
%type <chr> am_type
@@ -827,7 +827,8 @@ 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 PATH PLACING PLAN PLANS POLICY
+ PARALLEL PARAMETER PARSER PARTIAL PARTITION PASSING PASSWORD PATH
+ PLACING PLAN PLANS POLICY
POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION
@@ -7197,6 +7198,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 = NIL;
+ $$ = n;
+ }
| ColId opt_column_list
{
AccessPriv *n = makeNode(AccessPriv);
@@ -7206,6 +7214,28 @@ privilege: SELECT opt_column_list
}
;
+parameter_name_list:
+ parameter_name
+ {
+ $$ = list_make1(makeString($1));
+ }
+ | parameter_name_list ',' 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.
@@ -7307,6 +7337,14 @@ privilege_target:
n->objs = $3;
$$ = n;
}
+ | PARAMETER parameter_name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = OBJECT_PARAMETER_ACL;
+ n->objs = $2;
+ $$ = n;
+ }
| SCHEMA name_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -17065,6 +17103,7 @@ unreserved_keyword:
| OWNED
| OWNER
| PARALLEL
+ | PARAMETER
| PARSER
| PARTIAL
| PARTITION
@@ -17682,6 +17721,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..9263fa2901 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_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_ACL:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_PARAMETER_ACL;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
@@ -873,6 +886,9 @@ acldefault_sql(PG_FUNCTION_ARGS)
case 'L':
objtype = OBJECT_LARGEOBJECT;
break;
+ case 'p':
+ objtype = OBJECT_PARAMETER_ACL;
+ break;
case 'n':
objtype = OBJECT_SCHEMA;
break;
@@ -1602,6 +1618,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 +1718,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 +4453,96 @@ 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 with
+ * user name, user OID, or implicit user = current_user.
+ *
+ * The result is a boolean value: true if user has been granted
+ * the indicated privilege or false if not.
+ */
+
+/*
+ * has_param_priv_byname
+ *
+ * Helper function to check user privileges on a parameter given the
+ * role by Oid, parameter by text name, and privileges as AclMode.
+ */
+static bool
+has_param_priv_byname(Oid roleid, const text *parameter, AclMode priv)
+{
+ char *paramstr = text_to_cstring(parameter);
+
+ return pg_parameter_aclcheck(paramstr, roleid, priv) == ACLCHECK_OK;
+}
+
+/*
+ * 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);
+ AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(2));
+ Oid roleid = get_role_oid_or_public(NameStr(*username));
+
+ PG_RETURN_BOOL(has_param_priv_byname(roleid, parameter, priv));
+}
+
+/*
+ * 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);
+ AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(1));
+
+ PG_RETURN_BOOL(has_param_priv_byname(GetUserId(), parameter, priv));
+}
+
+/*
+ * 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);
+ AclMode priv = convert_parameter_priv_string(PG_GETARG_TEXT_PP(2));
+
+ PG_RETURN_BOOL(has_param_priv_byname(roleid, parameter, priv));
+}
+
+/*
+ * 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_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_text, parameter_priv_map);
+}
/*
* pg_has_role variants
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index a675877d19..8d265f2d23 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -47,6 +47,7 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
+#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_partitioned_table.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_publication.h"
@@ -574,6 +575,28 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {ParameterAclRelationId, /* PARAMETERACLNAME */
+ ParameterAclParnameIndexId,
+ 1,
+ {
+ Anum_pg_parameter_acl_parname,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+ {ParameterAclRelationId, /* PARAMETERACLOID */
+ ParameterAclOidIndexId,
+ 1,
+ {
+ Anum_pg_parameter_acl_oid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
{PartitionedRelationId, /* PARTRELID */
PartitionedRelidIndexId,
1,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 9e8ab1420d..265008bdc6 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"
@@ -5713,6 +5714,65 @@ guc_name_compare(const char *namea, const char *nameb)
}
+/*
+ * Convert a GUC name to the form that should be used in pg_parameter_acl.
+ *
+ * We need to canonicalize entries since, for example, case should not be
+ * significant. In addition, we apply the map_old_guc_names[] mapping so that
+ * any obsolete names will be converted when stored in a new PG version.
+ * Note however that this function does not verify legality of the name.
+ *
+ * The result is a palloc'd string.
+ */
+char *
+convert_GUC_name_for_parameter_acl(const char *name)
+{
+ char *result;
+
+ /* Apply old-GUC-name mapping. */
+ for (int i = 0; map_old_guc_names[i] != NULL; i += 2)
+ {
+ if (guc_name_compare(name, map_old_guc_names[i]) == 0)
+ {
+ name = map_old_guc_names[i + 1];
+ break;
+ }
+ }
+
+ /* Apply case-folding that matches guc_name_compare(). */
+ result = pstrdup(name);
+ for (char *ptr = result; *ptr != '\0'; ptr++)
+ {
+ char ch = *ptr;
+
+ if (ch >= 'A' && ch <= 'Z')
+ {
+ ch += 'a' - 'A';
+ *ptr = ch;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Check whether we should allow creation of a pg_parameter_acl entry
+ * for the given name. (This can be applied either before or after
+ * canonicalizing it.)
+ */
+bool
+check_GUC_name_for_parameter_acl(const char *name)
+{
+ /* OK if the GUC exists. */
+ if (find_option(name, false, true, DEBUG1) != NULL)
+ return true;
+ /* Otherwise, it'd better be a valid custom GUC name. */
+ if (valid_custom_variable_name(name))
+ return true;
+ return false;
+}
+
+
/*
* Initialize GUC options during program startup.
*
@@ -7568,6 +7628,17 @@ 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 been granted privilege
+ * to set this GUC.
+ */
+ AclResult aclresult;
+
+ aclresult = pg_parameter_aclcheck(name, 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\"",
@@ -8617,11 +8688,6 @@ 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
*/
@@ -8649,6 +8715,29 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
break;
}
+ /*
+ * Check permission to run ALTER SYSTEM on the target variable
+ */
+ if (!superuser())
+ {
+ if (resetall)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to perform ALTER SYSTEM RESET ALL")));
+ else
+ {
+ AclResult aclresult;
+
+ aclresult = pg_parameter_aclcheck(name, GetUserId(),
+ ACL_ALTER_SYSTEM);
+ if (aclresult != ACLCHECK_OK)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied to set parameter \"%s\"",
+ name)));
+ }
+ }
+
/*
* Unless it's RESET_ALL, validate the target variable and value
*/
@@ -8760,13 +8849,18 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
}
/*
- * Invoke the post-alter hook for altering this GUC variable.
+ * Invoke the post-alter hook for setting this GUC variable. GUCs
+ * typically do not have corresponding entries in pg_parameter_acl, so we
+ * call the hook using the name rather than a potentially-non-existent
+ * OID. Nonetheless, we pass ParameterAclRelationId so that this call
+ * context can be distinguished from others. (Note that "name" will be
+ * NULL in the RESET ALL case.)
*
* 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,
+ InvokeObjectPostAlterHookArgStr(ParameterAclRelationId, name,
ACL_ALTER_SYSTEM,
altersysstmt->setstmt->kind,
false);
@@ -8943,9 +9037,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);
}
/*
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_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 9c9f7c6d63..2dc3362763 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -18,6 +18,7 @@
#include <time.h>
#include <unistd.h>
+#include "catalog/pg_authid_d.h"
#include "common/connect.h"
#include "common/file_utils.h"
#include "common/logging.h"
@@ -36,6 +37,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 +587,10 @@ main(int argc, char *argv[])
/* Dump role memberships */
dumpRoleMembership(conn);
+
+ /* Dump role GUC privileges */
+ if (server_version >= 150000 && !skip_acls)
+ dumpRoleGUCPrivs(conn);
}
/* Dump tablespaces */
@@ -989,6 +995,65 @@ dumpRoleMembership(PGconn *conn)
}
+/*
+ * 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 that have non-default acls defined.
+ */
+ res = executeQuery(conn, "SELECT parname, "
+ "pg_catalog.pg_get_userbyid(" CppAsString2(BOOTSTRAP_SUPERUSERID) ") AS parowner, "
+ "paracl, "
+ "pg_catalog.acldefault('p', " CppAsString2(BOOTSTRAP_SUPERUSERID) ") AS acldefault "
+ "FROM pg_catalog.pg_parameter_acl "
+ "ORDER BY 1");
+
+ if (PQntuples(res) > 0)
+ fprintf(OPF, "--\n-- Role privileges on configuration parameters\n--\n\n");
+
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ PQExpBuffer buf = createPQExpBuffer();
+ char *parname = PQgetvalue(res, i, 0);
+ char *parowner = PQgetvalue(res, i, 1);
+ char *paracl = PQgetvalue(res, i, 2);
+ char *acldefault = PQgetvalue(res, i, 3);
+ char *fparname;
+
+ /* needed for buildACLCommands() */
+ fparname = pg_strdup(fmtId(parname));
+
+ if (!buildACLCommands(fparname, NULL, NULL, "PARAMETER",
+ paracl, acldefault,
+ parowner, "", server_version, buf))
+ {
+ pg_log_error("could not parse ACL list (%s) for parameter \"%s\"",
+ paracl, parname);
+ PQfinish(conn);
+ exit_nicely(1);
+ }
+
+ fprintf(OPF, "%s", buf->data);
+
+ free(fparname);
+ destroyPQExpBuffer(buf);
+ }
+
+ PQclear(res);
+ fprintf(OPF, "\n\n");
+}
+
+
/*
* Drop tablespaces.
*/
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 645d46ab75..32d0b4855f 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3737,7 +3737,8 @@ 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("GRANT|REVOKE") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR"))
{
/*
* With ALTER DEFAULT PRIVILEGES, restrict completion to grantable
@@ -3749,6 +3750,7 @@ psql_completion(const char *text, int start, int end)
"EXECUTE", "USAGE", "ALL");
else
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
+ "GRANT",
"SELECT",
"INSERT",
"UPDATE",
@@ -3761,14 +3763,48 @@ psql_completion(const char *text, int start, int end)
"TEMPORARY",
"EXECUTE",
"USAGE",
+ "SET",
+ "ALTER SYSTEM",
"ALL");
}
+ else if (TailMatches("REVOKE", "GRANT"))
+ COMPLETE_WITH("OPTION FOR");
+ else if (TailMatches("REVOKE", "GRANT", "OPTION"))
+ COMPLETE_WITH("FOR");
+
+ 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", MatchAny, "ON", "PARAMETER") ||
+ TailMatches("GRANT|REVOKE", MatchAny, MatchAny, "ON", "PARAMETER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "PARAMETER") ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, MatchAny, "ON", "PARAMETER"))
+ COMPLETE_WITH_QUERY(Query_for_list_of_alter_system_set_vars);
+
+ else if (TailMatches("GRANT", MatchAny, "ON", "PARAMETER", MatchAny) ||
+ TailMatches("GRANT", MatchAny, MatchAny, "ON", "PARAMETER", MatchAny))
+ COMPLETE_WITH("TO");
+
+ else if (TailMatches("REVOKE", MatchAny, "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", MatchAny, MatchAny, "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "PARAMETER", MatchAny) ||
+ TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, MatchAny, "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");
@@ -3785,7 +3821,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
@@ -3807,6 +3844,7 @@ psql_completion(const char *text, int start, int end)
"FUNCTION",
"LANGUAGE",
"LARGE OBJECT",
+ "PARAMETER",
"PROCEDURE",
"ROUTINE",
"SCHEMA",
@@ -3815,13 +3853,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");
/*
@@ -3830,7 +3870,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);
@@ -3868,6 +3909,22 @@ 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);
+
+ /*
+ * Offer grant options after that.
+ */
+ 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", MatchAny, "OPTION"))
+ COMPLETE_WITH("GRANTED BY");
+ else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH", MatchAny, "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,
@@ -3879,7 +3936,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");
@@ -3888,7 +3946,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");
@@ -3897,7 +3956,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..d027075a4c 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_ACL, /* 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_parameter_acl.h b/src/include/catalog/pg_parameter_acl.h
new file mode 100644
index 0000000000..8316391e51
--- /dev/null
+++ b/src/include/catalog/pg_parameter_acl.h
@@ -0,0 +1,62 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_parameter_acl.h
+ * definition of the "configuration parameter ACL" system catalog
+ * (pg_parameter_acl).
+ *
+ *
+ * Portions Copyright (c) 1996-2022, 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 */
+
+#ifdef CATALOG_VARLEN /* variable-length fields start here */
+ /* name of parameter */
+ text parname BKI_FORCE_NOT_NULL;
+
+ /* access permissions */
+ aclitem paracl[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_parname_index, 8927, ParameterAclParnameIndexId, on pg_parameter_acl using btree(parname text_ops));
+DECLARE_UNIQUE_INDEX_PKEY(pg_parameter_acl_oid_index, 8928, ParameterAclOidIndexId, on pg_parameter_acl using btree(oid oid_ops));
+
+
+extern Oid ParameterAclLookup(const char *parameter, bool missing_ok);
+extern Oid ParameterAclCreate(const char *parameter);
+
+#endif /* PG_PARAMETER_ACL_H */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 25304430f4..4d285ece8b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7213,6 +7213,22 @@
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 user oid, parameter name',
+ proname => 'has_parameter_privilege', provolatile => 's',
+ prorettype => 'bool', proargtypes => 'oid text text',
+ prosrc => 'has_parameter_privilege_id_name' },
+{ oid => '8052',
+ 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 => '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 4a2ca81f3c..8998d34560 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -92,8 +92,8 @@ 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_ALTER_SYSTEM (1<<13) /* 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
/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
@@ -2162,6 +2162,7 @@ typedef enum ObjectType
OBJECT_OPCLASS,
OBJECT_OPERATOR,
OBJECT_OPFAMILY,
+ OBJECT_PARAMETER_ACL,
OBJECT_POLICY,
OBJECT_PROCEDURE,
OBJECT_PUBLICATION,
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 0caa7310f2..8a2ab405a2 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -329,6 +329,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..48f7d72add 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 */
@@ -164,6 +164,7 @@ typedef struct ArrayType Acl;
#define ACL_ALL_RIGHTS_FUNCTION (ACL_EXECUTE)
#define ACL_ALL_RIGHTS_LANGUAGE (ACL_USAGE)
#define ACL_ALL_RIGHTS_LARGEOBJECT (ACL_SELECT|ACL_UPDATE)
+#define ACL_ALL_RIGHTS_PARAMETER_ACL (ACL_SET|ACL_ALTER_SYSTEM)
#define ACL_ALL_RIGHTS_SCHEMA (ACL_USAGE|ACL_CREATE)
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
#define ACL_ALL_RIGHTS_TYPE (ACL_USAGE)
@@ -245,6 +246,10 @@ 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_aclmask(const char *name, Oid roleid,
+ AclMode mask, AclMaskHow how);
+extern AclMode pg_parameter_acl_aclmask(Oid acl_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 +278,10 @@ 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_aclcheck(const char *name, Oid roleid,
+ AclMode mode);
+extern AclResult pg_parameter_acl_aclcheck(Oid acl_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..3446334e90 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -364,6 +364,8 @@ extern const char *GetConfigOption(const char *name, bool missing_ok,
extern const char *GetConfigOptionResetString(const char *name);
extern int GetConfigOptionFlags(const char *name, bool missing_ok);
extern void ProcessConfigFile(GucContext context);
+extern char *convert_GUC_name_for_parameter_acl(const char *name);
+extern bool check_GUC_name_for_parameter_acl(const char *name);
extern void InitializeGUCOptions(void);
extern bool SelectConfigFiles(const char *userDoption, const char *progname);
extern void ResetAllOptions(void);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 9c1a76e8bb..4463ea66be 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -72,6 +72,8 @@ enum SysCacheIdentifier
OPEROID,
OPFAMILYAMNAMENSP,
OPFAMILYOID,
+ PARAMETERACLNAME,
+ PARAMETERACLOID,
PARTRELID,
PROCNAMEARGSNSP,
PROCOID,
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..39b274b8fa 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,84 @@
+-- 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.
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 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 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 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 process utility: superuser finished GrantStmt
+-- Check when the hooks fire relative to dependency based abort of a drop
+DROP ROLE regress_role_joe;
+NOTICE: in process utility: superuser attempting DropRoleStmt
+NOTICE: in object access: superuser attempting drop (subId=0x0) []
+NOTICE: in object access: superuser finished drop (subId=0x0) []
+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
+-- Check the behavior of the hooks relative to do-nothing grants and revokes
+GRANT SET ON PARAMETER work_mem TO PUBLIC;
+NOTICE: in process utility: superuser attempting GrantStmt
+NOTICE: in process utility: superuser finished GrantStmt
+REVOKE ALTER SYSTEM ON PARAMETER work_mem FROM PUBLIC;
+NOTICE: in process utility: superuser attempting GrantStmt
+NOTICE: in process utility: superuser finished GrantStmt
+-- Check the behavior of the hooks relative to unrecognized parameters
+GRANT ALL ON PARAMETER "none.such" TO PUBLIC;
+NOTICE: in process utility: superuser attempting GrantStmt
+NOTICE: in process utility: superuser finished GrantStmt
+-- Check relative to an operation that causes the catalog entry to be deleted
+REVOKE ALL ON PARAMETER "none.such" FROM PUBLIC;
+NOTICE: in process utility: superuser attempting GrantStmt
+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 +87,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 +111,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 +158,89 @@ 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"
+SET test_oat_hooks.user_var1 = 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_var1]
+NOTICE: in object_access_hook_str: non-superuser finished alter (subId=0x1000, set) [test_oat_hooks.user_var1]
+NOTICE: in process utility: non-superuser finished set
+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"
+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 +259,43 @@ 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
+-- Clean up
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; -- fails
+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..8b6d5373aa 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,39 @@
+-- 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.
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;
+
+-- Check when the hooks fire relative to dependency based abort of a drop
+DROP ROLE regress_role_joe;
+
+-- Check the behavior of the hooks relative to do-nothing grants and revokes
+GRANT SET ON PARAMETER work_mem TO PUBLIC;
+REVOKE ALTER SYSTEM ON PARAMETER work_mem FROM PUBLIC;
+
+-- Check the behavior of the hooks relative to unrecognized parameters
+GRANT ALL ON PARAMETER "none.such" TO PUBLIC;
+
+-- Check relative to an operation that causes the catalog entry to be deleted
+REVOKE ALL ON PARAMETER "none.such" FROM PUBLIC;
+
-- Create objects for use in the test
CREATE USER regress_test_user;
CREATE TABLE regress_test_table (t text);
@@ -30,6 +59,16 @@ SET work_mem = 8192;
RESET work_mem;
ALTER SYSTEM SET work_mem = 8192;
ALTER SYSTEM RESET work_mem;
+
+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;
+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
@@ -48,6 +87,14 @@ RESET work_mem;
ALTER SYSTEM SET work_mem = 8192;
ALTER SYSTEM RESET work_mem;
+-- Clean up
RESET SESSION AUTHORIZATION;
SET test_oat_hooks.audit = false;
+DROP ROLE regress_role_joe; -- fails
+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..551da5d498 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;
+/*
+ * GUCs 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..d842f934a3 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,38 @@ 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 Custom.Knob TO regress_dump_test_role WITH GRANT OPTION'
+ => {
+ create_order => 2,
+ create_sql =>
+ 'GRANT SET, ALTER SYSTEM ON PARAMETER Custom.Knob TO regress_dump_test_role WITH GRANT OPTION;',
+ regexp =>
+ # "set" plus "alter system" is "all" privileges on parameters
+ qr/^GRANT ALL ON PARAMETER "custom.knob" TO regress_dump_test_role WITH GRANT OPTION;/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..f62807fd05
--- /dev/null
+++ b/src/test/modules/unsafe_tests/expected/guc_privs.out
@@ -0,0 +1,525 @@
+--
+-- Tests for privileges on GUCs.
+-- This is unsafe because changes will affect other databases in the cluster.
+--
+-- Test with a superuser role.
+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
+-- 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
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'SET');
+ has_parameter_privilege
+-------------------------
+ f
+(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
+-------------------------
+ f
+(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
+-------------------------
+ f
+(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
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'work_mem', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+-- Revoke privileges from a non-existent custom GUC. This should not create
+-- entries in the catalog.
+REVOKE ALL ON PARAMETER "none.such" FROM regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'none.such';
+ ?column?
+----------
+(0 rows)
+
+-- Grant and then revoke privileges on the non-existent custom GUC. Check that
+-- a do-nothing entry is not left in the catalogs after the revoke.
+GRANT ALL ON PARAMETER none.such TO regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'none.such';
+ ?column?
+----------
+ 1
+(1 row)
+
+REVOKE ALL ON PARAMETER "None.Such" FROM regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'none.such';
+ ?column?
+----------
+(0 rows)
+
+-- Can't grant on a non-existent core GUC.
+GRANT ALL ON PARAMETER no_such_guc TO regress_host_resource_admin; -- fail
+ERROR: invalid parameter name "no_such_guc"
+-- Initially there are no privileges and no catalog entry for this GUC.
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+ ?column?
+----------
+(0 rows)
+
+-- GRANT SET creates an entry:
+GRANT SET ON PARAMETER enable_material TO PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+ ?column?
+----------
+ 1
+(1 row)
+
+-- Now grant ALTER SYSTEM:
+GRANT ALL ON PARAMETER enable_material TO PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+ ?column?
+----------
+ 1
+(1 row)
+
+-- REVOKE ALTER SYSTEM brings us back to just the SET privilege:
+REVOKE ALTER SYSTEM ON PARAMETER enable_material FROM PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ f
+(1 row)
+
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+ ?column?
+----------
+ 1
+(1 row)
+
+-- And this should remove the entry altogether:
+REVOKE SET ON PARAMETER enable_material FROM PUBLIC;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+ ?column?
+----------
+(0 rows)
+
+-- Grant privileges on parameters to the new non-superuser role
+GRANT SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, max_stack_depth,
+ shared_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- 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((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('hash_mem_multiplier', 'set');
+ has_parameter_privilege
+-------------------------
+ t
+(1 row)
+
+-- Check object identity functions
+SELECT pg_describe_object(tableoid, oid, 0)
+FROM pg_parameter_acl WHERE parname = 'work_mem';
+ pg_describe_object
+--------------------
+ parameter work_mem
+(1 row)
+
+SELECT pg_identify_object(tableoid, oid, 0)
+FROM pg_parameter_acl WHERE parname = 'work_mem';
+ pg_identify_object
+------------------------------
+ ("parameter ACL",,,work_mem)
+(1 row)
+
+SELECT pg_identify_object_as_address(tableoid, oid, 0)
+FROM pg_parameter_acl WHERE parname = 'work_mem';
+ pg_identify_object_as_address
+---------------------------------
+ ("parameter ACL",{work_mem},{})
+(1 row)
+
+SELECT classid::regclass,
+ (SELECT parname FROM pg_parameter_acl WHERE oid = goa.objid) AS parname,
+ objsubid
+FROM pg_get_object_address('parameter ACL', '{work_mem}', '{}') goa;
+ classid | parname | objsubid
+------------------+----------+----------
+ pg_parameter_acl | work_mem | 0
+(1 row)
+
+-- Perform some 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
+RESET TimeZone; -- ok
+SET max_stack_depth = 2048; -- ok, privileges have been granted
+RESET max_stack_depth; -- ok, privileges have been granted
+ALTER SYSTEM SET max_stack_depth = 2048; -- ok, privileges have been granted
+ALTER SYSTEM RESET max_stack_depth; -- ok, privileges have been granted
+SET lc_messages = 'C'; -- fail, insufficient privileges
+ERROR: permission denied to set parameter "lc_messages"
+RESET lc_messages; -- fail, insufficient privileges
+ERROR: permission denied to set parameter "lc_messages"
+ALTER SYSTEM SET lc_messages = 'C'; -- fail, insufficient privileges
+ERROR: permission denied to set parameter "lc_messages"
+ALTER SYSTEM RESET lc_messages; -- fail, insufficient privileges
+ERROR: permission denied to set parameter "lc_messages"
+SELECT set_config ('temp_buffers', '8192', false); -- ok
+ set_config
+------------
+ 64MB
+(1 row)
+
+ALTER SYSTEM RESET autovacuum_work_mem; -- ok, privileges have been granted
+ALTER SYSTEM RESET ALL; -- fail, insufficient privileges
+ERROR: permission denied to perform ALTER SYSTEM RESET ALL
+-- Check dropping/revoking behavior
+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 autovacuum_work_mem
+privileges for parameter hash_mem_multiplier
+privileges for parameter max_stack_depth
+privileges for parameter shared_buffers
+privileges for parameter temp_file_limit
+privileges for parameter work_mem
+-- 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, max_stack_depth,
+ 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, max_stack_depth,
+ 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 autovacuum_work_mem
+privileges for parameter hash_mem_multiplier
+privileges for parameter max_stack_depth
+privileges for parameter shared_buffers
+privileges for parameter temp_file_limit
+privileges for parameter work_mem
+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
+-- Check that "reassign owned" doesn't affect privileges
+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, max_stack_depth,
+ 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 autovacuum_work_mem
+privileges for parameter hash_mem_multiplier
+privileges for parameter max_stack_depth
+privileges for parameter shared_buffers
+privileges for parameter temp_file_limit
+privileges for parameter work_mem
+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
+-- Clean up
+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..b38622803d
--- /dev/null
+++ b/src/test/modules/unsafe_tests/sql/guc_privs.sql
@@ -0,0 +1,236 @@
+--
+-- Tests for privileges on GUCs.
+-- This is unsafe because changes will affect other databases in the cluster.
+--
+
+-- Test with a superuser role.
+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
+
+-- 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');
+
+-- Revoke privileges from a non-existent custom GUC. This should not create
+-- entries in the catalog.
+REVOKE ALL ON PARAMETER "none.such" FROM regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'none.such';
+-- Grant and then revoke privileges on the non-existent custom GUC. Check that
+-- a do-nothing entry is not left in the catalogs after the revoke.
+GRANT ALL ON PARAMETER none.such TO regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'none.such';
+REVOKE ALL ON PARAMETER "None.Such" FROM regress_host_resource_admin;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'none.such';
+-- Can't grant on a non-existent core GUC.
+GRANT ALL ON PARAMETER no_such_guc TO regress_host_resource_admin; -- fail
+
+-- Initially there are no privileges and no catalog entry for this GUC.
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+-- GRANT SET creates an entry:
+GRANT SET ON PARAMETER enable_material TO PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+-- Now grant ALTER SYSTEM:
+GRANT ALL ON PARAMETER enable_material TO PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+-- REVOKE ALTER SYSTEM brings us back to just the SET privilege:
+REVOKE ALTER SYSTEM ON PARAMETER enable_material FROM PUBLIC;
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'ALTER SYSTEM');
+SELECT has_parameter_privilege('regress_host_resource_admin', 'enable_material', 'SET, ALTER SYSTEM');
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+-- And this should remove the entry altogether:
+REVOKE SET ON PARAMETER enable_material FROM PUBLIC;
+SELECT 1 FROM pg_parameter_acl WHERE parname = 'enable_material';
+
+-- Grant privileges on parameters to the new non-superuser role
+GRANT SET, ALTER SYSTEM ON PARAMETER
+ autovacuum_work_mem, hash_mem_multiplier, max_stack_depth,
+ shared_buffers, temp_file_limit, work_mem
+TO regress_host_resource_admin;
+-- 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((SELECT oid FROM pg_catalog.pg_authid WHERE rolname = 'regress_host_resource_admin'),
+ 'max_stack_depth',
+ 'SET');
+SELECT has_parameter_privilege('hash_mem_multiplier', 'set');
+
+-- Check object identity functions
+SELECT pg_describe_object(tableoid, oid, 0)
+FROM pg_parameter_acl WHERE parname = 'work_mem';
+SELECT pg_identify_object(tableoid, oid, 0)
+FROM pg_parameter_acl WHERE parname = 'work_mem';
+SELECT pg_identify_object_as_address(tableoid, oid, 0)
+FROM pg_parameter_acl WHERE parname = 'work_mem';
+SELECT classid::regclass,
+ (SELECT parname FROM pg_parameter_acl WHERE oid = goa.objid) AS parname,
+ objsubid
+FROM pg_get_object_address('parameter ACL', '{work_mem}', '{}') goa;
+
+-- Perform some 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
+RESET TimeZone; -- ok
+SET max_stack_depth = 2048; -- ok, privileges have been granted
+RESET max_stack_depth; -- ok, privileges have been granted
+ALTER SYSTEM SET max_stack_depth = 2048; -- ok, privileges have been granted
+ALTER SYSTEM RESET max_stack_depth; -- ok, privileges have been granted
+SET lc_messages = 'C'; -- fail, insufficient privileges
+RESET lc_messages; -- fail, insufficient privileges
+ALTER SYSTEM SET lc_messages = 'C'; -- fail, insufficient privileges
+ALTER SYSTEM RESET lc_messages; -- fail, insufficient privileges
+SELECT set_config ('temp_buffers', '8192', false); -- ok
+ALTER SYSTEM RESET autovacuum_work_mem; -- ok, privileges have been granted
+ALTER SYSTEM RESET ALL; -- fail, insufficient privileges
+
+-- Check dropping/revoking behavior
+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, max_stack_depth,
+ 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, max_stack_depth,
+ 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
+
+-- Check that "reassign owned" doesn't affect privileges
+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, max_stack_depth,
+ 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
+
+-- Clean up
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_admin; -- ok