Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Fix oversight in commit 1ec7fca8592178281cd5cdada0f27a340fb813fc.
authorEtsuro Fujita <efujita@postgresql.org>
Mon, 2 Aug 2021 03:45:01 +0000 (12:45 +0900)
committerEtsuro Fujita <efujita@postgresql.org>
Mon, 2 Aug 2021 03:45:01 +0000 (12:45 +0900)
I failed to account for the possibility that when
ExecAppendAsyncEventWait() notifies multiple async-capable nodes using
postgres_fdw, a preceding node might invoke process_pending_request() to
process a pending asynchronous request made by a succeeding node.  In
that case the succeeding node should produce a tuple to return to the
parent Append node from tuples fetched by process_pending_request() when
notified.  Repair.

Per buildfarm via Michael Paquier.  Back-patch to v14, like the previous
commit.

Thanks to Tom Lane for testing.

Discussion: https://postgr.es/m/YQP0UPT8KmPiHTMs%40paquier.xyz

contrib/postgres_fdw/postgres_fdw.c
src/backend/executor/nodeAppend.c

index bd8fda9e91ce597dc3d9ec8a6c74ff572ccf0f29..3bdf7ab18281b6900820b64676d4c889b0fe18e3 100644 (file)
@@ -6896,12 +6896,26 @@ postgresForeignAsyncNotify(AsyncRequest *areq)
    ForeignScanState *node = (ForeignScanState *) areq->requestee;
    PgFdwScanState *fsstate = (PgFdwScanState *) node->fdw_state;
 
-   /* The request should be currently in-process */
-   Assert(fsstate->conn_state->pendingAreq == areq);
-
    /* The core code would have initialized the callback_pending flag */
    Assert(!areq->callback_pending);
 
+   /*
+    * If process_pending_request() has been invoked on the given request
+    * before we get here, we might have some tuples already; in which case
+    * produce the next tuple
+    */
+   if (fsstate->next_tuple < fsstate->num_tuples)
+   {
+       produce_tuple_asynchronously(areq, true);
+       return;
+   }
+
+   /* We must have run out of tuples */
+   Assert(fsstate->next_tuple >= fsstate->num_tuples);
+
+   /* The request should be currently in-process */
+   Assert(fsstate->conn_state->pendingAreq == areq);
+
    /* On error, report the original query, not the FETCH. */
    if (!PQconsumeInput(fsstate->conn))
        pgfdw_report_error(ERROR, NULL, fsstate->conn, false, fsstate->query);
@@ -7027,7 +7041,7 @@ process_pending_request(AsyncRequest *areq)
    /*
     * If we didn't get any tuples, must be end of data; complete the request
     * now.  Otherwise, we postpone completing the request until we are called
-    * from postgresForeignAsyncConfigureWait().
+    * from postgresForeignAsyncConfigureWait()/postgresForeignAsyncNotify().
     */
    if (fsstate->next_tuple >= fsstate->num_tuples)
    {
index a4eef19d7f4f8af9476e989d3f86f3c6ff67b853..6a2daa6e767d7f845a431605d691de5ae5fddc4c 100644 (file)
@@ -1082,16 +1082,18 @@ ExecAppendAsyncEventWait(AppendState *node)
        {
            AsyncRequest *areq = (AsyncRequest *) w->user_data;
 
-           /*
-            * Mark it as no longer needing a callback.  We must do this
-            * before dispatching the callback in case the callback resets the
-            * flag.
-            */
-           Assert(areq->callback_pending);
-           areq->callback_pending = false;
-
-           /* Do the actual work. */
-           ExecAsyncNotify(areq);
+           if (areq->callback_pending)
+           {
+               /*
+                * Mark it as no longer needing a callback.  We must do this
+                * before dispatching the callback in case the callback resets
+                * the flag.
+                */
+               areq->callback_pending = false;
+
+               /* Do the actual work. */
+               ExecAsyncNotify(areq);
+           }
        }
    }
 }