From 0ff4364bd4dcd1c9e5ed40b047112be4a7cb9311 Mon Sep 17 00:00:00 2001 From: Mark Dilger 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; INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER, CREATE, CONNECT, TEMPORARY, - EXECUTE, and USAGE. + EXECUTE, USAGE, SET VALUE + and ALTER SYSTEM. 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; + + + SET VALUE + + + Allows non-superuser roles to use the SET command to + change run-time configuration parameters. + + + + + + ALTER SYSTEM + + + Allows non-superuser roles to use the ALTER SYSTEM + SET command to change server configuration parameters. + + + The privileges required by other commands are listed on the @@ -2026,6 +2047,16 @@ REVOKE ALL ON accounts FROM PUBLIC; TYPE + + SET VALUE + s + configuration parameter + + + ALTER SYSTEM + A + configuration parameter + @@ -2132,6 +2163,12 @@ REVOKE ALL ON accounts FROM PUBLIC; U \dT+ + + Configuration parameter + sA + none + + 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 role_specification [, ...] [ WITH GRANT OPTION ] [ GRANTED BY role_specification ] +GRANT { SET VALUE | ALTER SYSTEM } + ON configuration_parameter [, ...] + TO role_specification [, ...] [ WITH GRANT OPTION ] + [ GRANTED BY role_specification ] + GRANT role_name [, ...] TO role_specification [, ...] [ WITH ADMIN OPTION ] [ GRANTED BY role_specification ] @@ -185,6 +190,8 @@ GRANT role_name [, ...] TO TEMPORARY EXECUTE USAGE + SET VALUE + ALTER SYSTEM Specific types of privileges, as defined in . 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 { timezone can be changed on-the-fly with SET. - (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 SET + VALUE privileges to change, and others cannot be changed after + server or session start.) SET only affects the value used by the current session. 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 foreign_server_version opt_foreign_server_version %type opt_in_database -%type OptSchemaName -%type OptSchemaEltList +%type OptSchemaName cfgparam_name +%type OptSchemaEltList cfgparam_target %type am_type @@ -387,8 +387,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type iso_level opt_encoding %type grantee %type grantee_list -%type privilege -%type privileges privilege_list +%type privilege guc_priv +%type privileges privilege_list guc_priv_list %type privilege_target %type function_with_argtypes aggregate_with_argtypes operator_with_argtypes %type 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< 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)