Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Allow adjusting session_authorization and role in parallel workers.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 10 Aug 2024 19:51:28 +0000 (15:51 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 10 Aug 2024 19:51:28 +0000 (15:51 -0400)
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 059de3ca4: 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

src/backend/utils/misc/guc.c
src/include/utils/guc.h
src/test/regress/expected/select_parallel.out
src/test/regress/sql/select_parallel.sql

index 0ac30a859e0023a864dcff78d494aa754786c9dc..18669ffb4ad8b440dd3cabc4859cbd6cf94f2b26 100644 (file)
@@ -1130,7 +1130,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,
@@ -6939,10 +6939,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
@@ -6982,6 +6984,15 @@ set_config_option(const char *name, const char *value,
            elevel = ERROR;
    }
 
+   record = find_option(name, true, elevel);
+   if (record == NULL)
+   {
+       ereport(elevel,
+               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                errmsg("unrecognized configuration parameter \"%s\"", name)));
+       return 0;
+   }
+
    /*
     * GUC_ACTION_SAVE changes are acceptable during a parallel operation,
     * because the current worker will also pop the change.  We're probably
@@ -6989,22 +7000,17 @@ set_config_option(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, elevel);
-   if (record == NULL)
-   {
-       ereport(elevel,
-               (errcode(ERRCODE_UNDEFINED_OBJECT),
-                errmsg("unrecognized configuration parameter \"%s\"", name)));
+                errmsg("parameter \"%s\" cannot be set during a parallel operation",
+                       name)));
        return 0;
    }
 
index 2819282181678b30fdaea676f07a5c7a78249b15..3142595733b8a64a8f3eb349b621d931b68a5fc8 100644 (file)
@@ -228,6 +228,7 @@ typedef enum
 #define GUC_UNIT_TIME         0xF0000  /* mask for time-related units */
 
 #define GUC_EXPLAIN              0x100000  /* include in explain */
+#define GUC_ALLOW_IN_PARALLEL 0x200000 /* allow setting in parallel mode */
 
 #define GUC_UNIT               (GUC_UNIT_MEMORY | GUC_UNIT_TIME)
 
index cdbaa4a7b6144a60141f150f399bdddcb125e2f3..7906a3a5c0c99fc6f99754f236b979c5e1af8c7c 100644 (file)
@@ -1221,3 +1221,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;
index 43adb05b7a30170673323557ca8de62d7c313467..39870f0141d258ac364de55789a4387f0273575d 100644 (file)
@@ -464,3 +464,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;