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)

