diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index acbcb8bb81..dea995af09 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -1231,7 +1231,7 @@ static struct config_bool ConfigureNamesBool[] = {"is_superuser", PGC_INTERNAL, UNGROUPED, gettext_noop("Shows whether the current user is a superuser."), NULL, - GUC_REPORT | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE + GUC_REPORT | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_ALLOW_IN_PARALLEL }, &session_auth_is_superuser, false, @@ -7562,10 +7562,12 @@ parse_and_validate_value(struct config_generic *record, * * Return value: * +1: the value is valid and was successfully applied. - * 0: the name or value is invalid (but see below). - * -1: the value was not applied because of context, priority, or changeVal. + * 0: the name or value is invalid, or it's invalid to try to set + * this GUC now; but elevel was less than ERROR (see below). + * -1: no error detected, but the value was not applied, either + * because changeVal is false or there is some overriding setting. * - * If there is an error (non-existing option, invalid value) then an + * If there is an error (non-existing option, invalid value, etc) then an * ereport(ERROR) is thrown *unless* this is called for a source for which * we don't want an ERROR (currently, those are defaults, the config file, * and per-database or per-user settings, as well as callers who specify @@ -7645,6 +7647,10 @@ set_config_option_ext(const char *name, const char *value, elevel = ERROR; } + record = find_option(name, true, false, elevel); + if (record == NULL) + return 0; + /* * GUC_ACTION_SAVE changes are acceptable during a parallel operation, * because the current worker will also pop the change. We're probably @@ -7652,19 +7658,19 @@ set_config_option_ext(const char *name, const char *value, * body should observe the change, and peer workers do not share in the * execution of a function call started by this worker. * + * Also allow normal setting if the GUC is marked GUC_ALLOW_IN_PARALLEL. + * * Other changes might need to affect other workers, so forbid them. */ - if (IsInParallelMode() && changeVal && action != GUC_ACTION_SAVE) + if (IsInParallelMode() && changeVal && action != GUC_ACTION_SAVE && + (record->flags & GUC_ALLOW_IN_PARALLEL) == 0) { ereport(elevel, (errcode(ERRCODE_INVALID_TRANSACTION_STATE), - errmsg("cannot set parameters during a parallel operation"))); - return -1; - } - - record = find_option(name, true, false, elevel); - if (record == NULL) + errmsg("parameter \"%s\" cannot be set during a parallel operation", + name))); return 0; + } /* * Check if the option can be set at this time. See guc.h for the precise diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 65eafb463b..e32bb0ab45 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -238,6 +238,7 @@ typedef enum * available via 'postgres -C' if the server is not running. */ #define GUC_RUNTIME_COMPUTED 0x200000 +#define GUC_ALLOW_IN_PARALLEL 0x400000 /* allow setting in parallel mode */ #define GUC_UNIT (GUC_UNIT_MEMORY | GUC_UNIT_TIME) diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out index 91f74fe47a..2b5f9bfd40 100644 --- a/src/test/regress/expected/select_parallel.out +++ b/src/test/regress/expected/select_parallel.out @@ -1219,3 +1219,36 @@ SELECT 1 FROM tenk1_vw_sec (9 rows) rollback; +-- test that function option SET ROLE works in parallel workers. +create role regress_parallel_worker; +create function set_and_report_role() returns text as + $$ select current_setting('role') $$ language sql parallel safe + set role = regress_parallel_worker; +create function set_role_and_error(int) returns int as + $$ select 1 / $1 $$ language sql parallel safe + set role = regress_parallel_worker; +set force_parallel_mode = 0; +select set_and_report_role(); + set_and_report_role +------------------------- + regress_parallel_worker +(1 row) + +select set_role_and_error(0); +ERROR: division by zero +CONTEXT: SQL function "set_role_and_error" statement 1 +set force_parallel_mode = 1; +select set_and_report_role(); + set_and_report_role +------------------------- + regress_parallel_worker +(1 row) + +select set_role_and_error(0); +ERROR: division by zero +CONTEXT: SQL function "set_role_and_error" statement 1 +parallel worker +reset force_parallel_mode; +drop function set_and_report_role(); +drop function set_role_and_error(int); +drop role regress_parallel_worker; diff --git a/src/test/regress/sql/select_parallel.sql b/src/test/regress/sql/select_parallel.sql index 62fb68c7a0..fb7c8b393b 100644 --- a/src/test/regress/sql/select_parallel.sql +++ b/src/test/regress/sql/select_parallel.sql @@ -462,3 +462,26 @@ SELECT 1 FROM tenk1_vw_sec WHERE (SELECT sum(f1) FROM int4_tbl WHERE f1 < unique1) < 100; rollback; + +-- test that function option SET ROLE works in parallel workers. +create role regress_parallel_worker; + +create function set_and_report_role() returns text as + $$ select current_setting('role') $$ language sql parallel safe + set role = regress_parallel_worker; + +create function set_role_and_error(int) returns int as + $$ select 1 / $1 $$ language sql parallel safe + set role = regress_parallel_worker; + +set force_parallel_mode = 0; +select set_and_report_role(); +select set_role_and_error(0); +set force_parallel_mode = 1; +select set_and_report_role(); +select set_role_and_error(0); +reset force_parallel_mode; + +drop function set_and_report_role(); +drop function set_role_and_error(int); +drop role regress_parallel_worker;