postgres_fdw: Fix subabort cleanup of connections used in asynchronous execution.
authorEtsuro Fujita <efujita@postgresql.org>
Fri, 21 Jan 2022 08:45:00 +0000 (17:45 +0900)
committerEtsuro Fujita <efujita@postgresql.org>
Fri, 21 Jan 2022 08:45:00 +0000 (17:45 +0900)
Commit 27e1f1456 resets the per-connection states of connections used to
scan foreign tables asynchronously during abort cleanup at main
transaction end, but it failed to do so during subabort cleanup at
subtransaction end, leading to a segmentation fault when re-executing an
asynchronous-foreign-table-scan query in a transaction that was
cancelled in a subtransaction of it.

Fix by modifying pgfdw_abort_cleanup() to reset the per-connection state
of a given connection also when called for subabort cleanup.  Also,
modify that function to do the reset in both the abort-cleanup and
subabort-cleanup cases if necessary, to save cycles, and improve a
comment on it a little bit.

Back-patch to v14 where the aforesaid commit came in.

Reviewed by Alexander Pyhalov

Discussion: https://postgr.es/m/CAPmGK14cCV-JA7kNsyt2EUTKvZ4xkr2LNRthi1U1C3cqfGppAw@mail.gmail.com

contrib/postgres_fdw/connection.c

index 4d64dfebf6ef7a541a3666cd1bd7229ea275ae68..29fcb6a76eb3814b751883e08e319e0f9dab4249 100644 (file)
@@ -1441,10 +1441,17 @@ pgfdw_abort_cleanup(ConnCacheEntry *entry, const char *sql, bool toplevel)
 
        entry->have_prep_stmt = false;
        entry->have_error = false;
-       /* Also reset per-connection state */
-       memset(&entry->state, 0, sizeof(entry->state));
    }
 
+   /*
+    * If pendingAreq of the per-connection state is not NULL, it means that
+    * an asynchronous fetch begun by fetch_more_data_begin() was not done
+    * successfully and thus the per-connection state was not reset in
+    * fetch_more_data(); in that case reset the per-connection state here.
+    */
+   if (entry->state.pendingAreq)
+       memset(&entry->state, 0, sizeof(entry->state));
+
    /* Disarm changing_xact_state if it all worked */
    entry->changing_xact_state = false;
 }