Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Allow parallel workers to cope with a newly-created session user ID.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 31 Jul 2024 22:54:10 +0000 (18:54 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 31 Jul 2024 22:54:10 +0000 (18:54 -0400)
Parallel workers failed after a sequence like
BEGIN;
CREATE USER foo;
SET SESSION AUTHORIZATION foo;
because check_session_authorization could not see the uncommitted
pg_authid row for "foo".  This is because we ran RestoreGUCState()
in a separate transaction using an ordinary just-created snapshot.
The same disease afflicts any other GUC that requires catalog lookups
and isn't forgiving about the lookups failing.

To fix, postpone RestoreGUCState() into the worker's main transaction
after we've set up a snapshot duplicating the leader's.  This affects
check_transaction_isolation and check_transaction_deferrable, which
think they should only run during transaction start.  Make them
act like check_transaction_read_only, which already knows it should
silently accept the value when InitializingParallelWorker.

Per bug #18545 from Andrey Rachitskiy.  Back-patch to all
supported branches, because this has been wrong for awhile.

Discussion: https://postgr.es/m/18545-feba138862f19aaa@postgresql.org

src/backend/access/transam/parallel.c
src/backend/commands/variable.c
src/test/regress/expected/select_parallel.out
src/test/regress/sql/select_parallel.sql

index df0cd7755889a6d37f58e4c3459df747e9ccaa8f..d5bee10cd4ba2f689b4ce41810926c3806adc00c 100644 (file)
@@ -1402,10 +1402,6 @@ ParallelWorkerMain(Datum main_arg)
    libraryspace = shm_toc_lookup(toc, PARALLEL_KEY_LIBRARY, false);
    StartTransactionCommand();
    RestoreLibraryState(libraryspace);
-
-   /* Restore GUC values from launching backend. */
-   gucspace = shm_toc_lookup(toc, PARALLEL_KEY_GUC, false);
-   RestoreGUCState(gucspace);
    CommitTransactionCommand();
 
    /* Crank up a transaction state appropriate to a parallel worker. */
@@ -1447,6 +1443,14 @@ ParallelWorkerMain(Datum main_arg)
     */
    InvalidateSystemCaches();
 
+   /*
+    * Restore GUC values from launching backend.  We can't do this earlier,
+    * because GUC check hooks that do catalog lookups need to see the same
+    * database state as the leader.
+    */
+   gucspace = shm_toc_lookup(toc, PARALLEL_KEY_GUC, false);
+   RestoreGUCState(gucspace);
+
    /*
     * Restore current role id.  Skip verifying whether session user is
     * allowed to become this role and blindly restore the leader's state for
index e5ddcda0b4a0db0a3e1b217179f00e55460bfb94..b2336ad7efddb5727b1411945e97690e8434addb 100644 (file)
@@ -519,14 +519,16 @@ check_transaction_read_only(bool *newval, void **extra, GucSource source)
  * We allow idempotent changes at any time, but otherwise this can only be
  * changed in a toplevel transaction that has not yet taken a snapshot.
  *
- * As in check_transaction_read_only, allow it if not inside a transaction.
+ * As in check_transaction_read_only, allow it if not inside a transaction,
+ * or if restoring state in a parallel worker.
  */
 bool
 check_XactIsoLevel(int *newval, void **extra, GucSource source)
 {
    int         newXactIsoLevel = *newval;
 
-   if (newXactIsoLevel != XactIsoLevel && IsTransactionState())
+   if (newXactIsoLevel != XactIsoLevel &&
+       IsTransactionState() && !InitializingParallelWorker)
    {
        if (FirstSnapshotSet)
        {
@@ -561,6 +563,10 @@ check_XactIsoLevel(int *newval, void **extra, GucSource source)
 bool
 check_transaction_deferrable(bool *newval, void **extra, GucSource source)
 {
+   /* Just accept the value when restoring state in a parallel worker */
+   if (InitializingParallelWorker)
+       return true;
+
    if (IsSubTransaction())
    {
        GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION);
index 91f74fe47a3d1fef87fd1808a1ad89ef04c61315..3c6887fcb9703cad69da2c6cdc463b80c632cb73 100644 (file)
@@ -1219,3 +1219,21 @@ SELECT 1 FROM tenk1_vw_sec
 (9 rows)
 
 rollback;
+-- test that a newly-created session role propagates to workers.
+begin;
+create role regress_parallel_worker;
+set session authorization regress_parallel_worker;
+select current_setting('session_authorization');
+     current_setting     
+-------------------------
+ regress_parallel_worker
+(1 row)
+
+set force_parallel_mode = 1;
+select current_setting('session_authorization');
+     current_setting     
+-------------------------
+ regress_parallel_worker
+(1 row)
+
+rollback;
index 62fb68c7a0483ccfb2733e110a90c3e106203d39..11d861584ef69db48f7ae1a363f20c59275f106c 100644 (file)
@@ -462,3 +462,12 @@ SELECT 1 FROM tenk1_vw_sec
   WHERE (SELECT sum(f1) FROM int4_tbl WHERE f1 < unique1) < 100;
 
 rollback;
+
+-- test that a newly-created session role propagates to workers.
+begin;
+create role regress_parallel_worker;
+set session authorization regress_parallel_worker;
+select current_setting('session_authorization');
+set force_parallel_mode = 1;
+select current_setting('session_authorization');
+rollback;