Skip to content

Commit adf9808

Browse files
committed
Allow adjusting session_authorization and role in parallel workers.
The code intends to allow GUCs to be set within parallel workers via function SET clauses, but not otherwise. However, doing so fails for "session_authorization" and "role", because the assign hooks for those attempt to set the subsidiary "is_superuser" GUC, and that call falls foul of the "not otherwise" prohibition. We can't switch to using GUC_ACTION_SAVE for this, so instead add a new GUC variable flag GUC_ALLOW_IN_PARALLEL to mark is_superuser as being safe to set anyway. (This is okay because is_superuser has context PGC_INTERNAL and thus only hard-wired calls can change it. We'd need more thought before applying the flag to other GUCs; but maybe there are other use-cases.) This isn't the prettiest fix perhaps, but other alternatives we thought of would be much more invasive. While here, correct a thinko in commit 059de3c: when rejecting a GUC setting within a parallel worker, we should return 0 not -1 if the ereport doesn't longjmp. (This seems to have no consequences right now because no caller cares, but it's inconsistent.) Improve the comments to try to forestall future confusion of the same kind. Despite the lack of field complaints, this seems worth back-patching. Thanks to Nathan Bossart for the idea to invent a new flag, and for review. Discussion: https://postgr.es/m/2833457.1723229039@sss.pgh.pa.us
1 parent c17d2d6 commit adf9808

File tree

4 files changed

+78
-15
lines changed

4 files changed

+78
-15
lines changed

src/backend/utils/misc/guc.c

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,7 +1130,7 @@ static struct config_bool ConfigureNamesBool[] =
11301130
{"is_superuser", PGC_INTERNAL, UNGROUPED,
11311131
gettext_noop("Shows whether the current user is a superuser."),
11321132
NULL,
1133-
GUC_REPORT | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
1133+
GUC_REPORT | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE | GUC_ALLOW_IN_PARALLEL
11341134
},
11351135
&session_auth_is_superuser,
11361136
false,
@@ -6939,10 +6939,12 @@ parse_and_validate_value(struct config_generic *record,
69396939
*
69406940
* Return value:
69416941
* +1: the value is valid and was successfully applied.
6942-
* 0: the name or value is invalid (but see below).
6943-
* -1: the value was not applied because of context, priority, or changeVal.
6942+
* 0: the name or value is invalid, or it's invalid to try to set
6943+
* this GUC now; but elevel was less than ERROR (see below).
6944+
* -1: no error detected, but the value was not applied, either
6945+
* because changeVal is false or there is some overriding setting.
69446946
*
6945-
* If there is an error (non-existing option, invalid value) then an
6947+
* If there is an error (non-existing option, invalid value, etc) then an
69466948
* ereport(ERROR) is thrown *unless* this is called for a source for which
69476949
* we don't want an ERROR (currently, those are defaults, the config file,
69486950
* and per-database or per-user settings, as well as callers who specify
@@ -6982,29 +6984,33 @@ set_config_option(const char *name, const char *value,
69826984
elevel = ERROR;
69836985
}
69846986

6987+
record = find_option(name, true, elevel);
6988+
if (record == NULL)
6989+
{
6990+
ereport(elevel,
6991+
(errcode(ERRCODE_UNDEFINED_OBJECT),
6992+
errmsg("unrecognized configuration parameter \"%s\"", name)));
6993+
return 0;
6994+
}
6995+
69856996
/*
69866997
* GUC_ACTION_SAVE changes are acceptable during a parallel operation,
69876998
* because the current worker will also pop the change. We're probably
69886999
* dealing with a function having a proconfig entry. Only the function's
69897000
* body should observe the change, and peer workers do not share in the
69907001
* execution of a function call started by this worker.
69917002
*
7003+
* Also allow normal setting if the GUC is marked GUC_ALLOW_IN_PARALLEL.
7004+
*
69927005
* Other changes might need to affect other workers, so forbid them.
69937006
*/
6994-
if (IsInParallelMode() && changeVal && action != GUC_ACTION_SAVE)
7007+
if (IsInParallelMode() && changeVal && action != GUC_ACTION_SAVE &&
7008+
(record->flags & GUC_ALLOW_IN_PARALLEL) == 0)
69957009
{
69967010
ereport(elevel,
69977011
(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
6998-
errmsg("cannot set parameters during a parallel operation")));
6999-
return -1;
7000-
}
7001-
7002-
record = find_option(name, true, elevel);
7003-
if (record == NULL)
7004-
{
7005-
ereport(elevel,
7006-
(errcode(ERRCODE_UNDEFINED_OBJECT),
7007-
errmsg("unrecognized configuration parameter \"%s\"", name)));
7012+
errmsg("parameter \"%s\" cannot be set during a parallel operation",
7013+
name)));
70087014
return 0;
70097015
}
70107016

src/include/utils/guc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ typedef enum
228228
#define GUC_UNIT_TIME 0xF0000 /* mask for time-related units */
229229

230230
#define GUC_EXPLAIN 0x100000 /* include in explain */
231+
#define GUC_ALLOW_IN_PARALLEL 0x200000 /* allow setting in parallel mode */
231232

232233
#define GUC_UNIT (GUC_UNIT_MEMORY | GUC_UNIT_TIME)
233234

src/test/regress/expected/select_parallel.out

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,3 +1221,36 @@ SELECT 1 FROM tenk1_vw_sec
12211221
(9 rows)
12221222

12231223
rollback;
1224+
-- test that function option SET ROLE works in parallel workers.
1225+
create role regress_parallel_worker;
1226+
create function set_and_report_role() returns text as
1227+
$$ select current_setting('role') $$ language sql parallel safe
1228+
set role = regress_parallel_worker;
1229+
create function set_role_and_error(int) returns int as
1230+
$$ select 1 / $1 $$ language sql parallel safe
1231+
set role = regress_parallel_worker;
1232+
set force_parallel_mode = 0;
1233+
select set_and_report_role();
1234+
set_and_report_role
1235+
-------------------------
1236+
regress_parallel_worker
1237+
(1 row)
1238+
1239+
select set_role_and_error(0);
1240+
ERROR: division by zero
1241+
CONTEXT: SQL function "set_role_and_error" statement 1
1242+
set force_parallel_mode = 1;
1243+
select set_and_report_role();
1244+
set_and_report_role
1245+
-------------------------
1246+
regress_parallel_worker
1247+
(1 row)
1248+
1249+
select set_role_and_error(0);
1250+
ERROR: division by zero
1251+
CONTEXT: SQL function "set_role_and_error" statement 1
1252+
parallel worker
1253+
reset force_parallel_mode;
1254+
drop function set_and_report_role();
1255+
drop function set_role_and_error(int);
1256+
drop role regress_parallel_worker;

src/test/regress/sql/select_parallel.sql

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,3 +464,26 @@ SELECT 1 FROM tenk1_vw_sec
464464
WHERE (SELECT sum(f1) FROM int4_tbl WHERE f1 < unique1) < 100;
465465

466466
rollback;
467+
468+
-- test that function option SET ROLE works in parallel workers.
469+
create role regress_parallel_worker;
470+
471+
create function set_and_report_role() returns text as
472+
$$ select current_setting('role') $$ language sql parallel safe
473+
set role = regress_parallel_worker;
474+
475+
create function set_role_and_error(int) returns int as
476+
$$ select 1 / $1 $$ language sql parallel safe
477+
set role = regress_parallel_worker;
478+
479+
set force_parallel_mode = 0;
480+
select set_and_report_role();
481+
select set_role_and_error(0);
482+
set force_parallel_mode = 1;
483+
select set_and_report_role();
484+
select set_role_and_error(0);
485+
reset force_parallel_mode;
486+
487+
drop function set_and_report_role();
488+
drop function set_role_and_error(int);
489+
drop role regress_parallel_worker;

0 commit comments

Comments
 (0)