Skip to content

Commit 9e283fc

Browse files
author
Etsuro Fujita
committed
postgres_fdw: Fix handling of a pending asynchronous request in postgresReScanForeignScan().
Commit 27e1f14 failed to process a pending asynchronous request made for a given ForeignScan node in postgresReScanForeignScan() (if any) in cases where we would only reset the next_tuple counter in that function, contradicting the assumption that there should be no pending asynchronous requests that have been made for async-capable subplans for the parent Append node after ReScan. This led to an assert failure in an assert-enabled build. I think this would also lead to mis-rewinding the cursor in that function in the case where we have already fetched one batch for the ForeignScan node and the asynchronous request has been made for the second batch, because even in that case we would just reset the counter when called from that function, so we would fail to execute MOVE BACKWARD ALL. To fix, modify that function to process the asynchronous request before restarting the scan. While at it, add a comment to a function to match other places. Per bug #17344 from Alexander Lakhin. Back-patch to v14 where the aforesaid commit came in. Patch by me. Test case by Alexander Lakhin, adjusted by me. Reviewed and tested by Alexander Lakhin and Dmitry Dolgov. Discussion: https://postgr.es/m/17344-226b78b00de73a7e@postgresql.org
1 parent ce6d793 commit 9e283fc

File tree

3 files changed

+68
-0
lines changed

3 files changed

+68
-0
lines changed

contrib/postgres_fdw/expected/postgres_fdw.out

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10800,6 +10800,43 @@ DROP TABLE base_tbl1;
1080010800
DROP TABLE base_tbl2;
1080110801
DROP TABLE result_tbl;
1080210802
DROP TABLE join_tbl;
10803+
-- Test that an asynchronous fetch is processed before restarting the scan in
10804+
-- ReScanForeignScan
10805+
CREATE TABLE base_tbl (a int, b int);
10806+
INSERT INTO base_tbl VALUES (1, 11), (2, 22), (3, 33);
10807+
CREATE FOREIGN TABLE foreign_tbl (b int)
10808+
SERVER loopback OPTIONS (table_name 'base_tbl');
10809+
CREATE FOREIGN TABLE foreign_tbl2 () INHERITS (foreign_tbl)
10810+
SERVER loopback OPTIONS (table_name 'base_tbl');
10811+
EXPLAIN (VERBOSE, COSTS OFF)
10812+
SELECT a FROM base_tbl WHERE a IN (SELECT a FROM foreign_tbl);
10813+
QUERY PLAN
10814+
-----------------------------------------------------------------------------
10815+
Seq Scan on public.base_tbl
10816+
Output: base_tbl.a
10817+
Filter: (SubPlan 1)
10818+
SubPlan 1
10819+
-> Result
10820+
Output: base_tbl.a
10821+
-> Append
10822+
-> Async Foreign Scan on public.foreign_tbl foreign_tbl_1
10823+
Remote SQL: SELECT NULL FROM public.base_tbl
10824+
-> Async Foreign Scan on public.foreign_tbl2 foreign_tbl_2
10825+
Remote SQL: SELECT NULL FROM public.base_tbl
10826+
(11 rows)
10827+
10828+
SELECT a FROM base_tbl WHERE a IN (SELECT a FROM foreign_tbl);
10829+
a
10830+
---
10831+
1
10832+
2
10833+
3
10834+
(3 rows)
10835+
10836+
-- Clean up
10837+
DROP FOREIGN TABLE foreign_tbl CASCADE;
10838+
NOTICE: drop cascades to foreign table foreign_tbl2
10839+
DROP TABLE base_tbl;
1080310840
ALTER SERVER loopback OPTIONS (DROP async_capable);
1080410841
ALTER SERVER loopback2 OPTIONS (DROP async_capable);
1080510842
-- ===================================================================

contrib/postgres_fdw/postgres_fdw.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1651,6 +1651,18 @@ postgresReScanForeignScan(ForeignScanState *node)
16511651
if (!fsstate->cursor_exists)
16521652
return;
16531653

1654+
/*
1655+
* If the node is async-capable, and an asynchronous fetch for it has been
1656+
* begun, the asynchronous fetch might not have yet completed. Check if
1657+
* the node is async-capable, and an asynchronous fetch for it is still in
1658+
* progress; if so, complete the asynchronous fetch before restarting the
1659+
* scan.
1660+
*/
1661+
if (fsstate->async_capable &&
1662+
fsstate->conn_state->pendingAreq &&
1663+
fsstate->conn_state->pendingAreq->requestee == (PlanState *) node)
1664+
fetch_more_data(node);
1665+
16541666
/*
16551667
* If any internal parameters affecting this node have changed, we'd
16561668
* better destroy and recreate the cursor. Otherwise, rewinding it should
@@ -6999,6 +7011,8 @@ produce_tuple_asynchronously(AsyncRequest *areq, bool fetch)
69997011
ExecAsyncRequestDone(areq, result);
70007012
return;
70017013
}
7014+
7015+
/* We must have run out of tuples */
70027016
Assert(fsstate->next_tuple >= fsstate->num_tuples);
70037017

70047018
/* Fetch some more tuples, if we've not detected EOF yet */

contrib/postgres_fdw/sql/postgres_fdw.sql

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3431,6 +3431,23 @@ DROP TABLE base_tbl2;
34313431
DROP TABLE result_tbl;
34323432
DROP TABLE join_tbl;
34333433

3434+
-- Test that an asynchronous fetch is processed before restarting the scan in
3435+
-- ReScanForeignScan
3436+
CREATE TABLE base_tbl (a int, b int);
3437+
INSERT INTO base_tbl VALUES (1, 11), (2, 22), (3, 33);
3438+
CREATE FOREIGN TABLE foreign_tbl (b int)
3439+
SERVER loopback OPTIONS (table_name 'base_tbl');
3440+
CREATE FOREIGN TABLE foreign_tbl2 () INHERITS (foreign_tbl)
3441+
SERVER loopback OPTIONS (table_name 'base_tbl');
3442+
3443+
EXPLAIN (VERBOSE, COSTS OFF)
3444+
SELECT a FROM base_tbl WHERE a IN (SELECT a FROM foreign_tbl);
3445+
SELECT a FROM base_tbl WHERE a IN (SELECT a FROM foreign_tbl);
3446+
3447+
-- Clean up
3448+
DROP FOREIGN TABLE foreign_tbl CASCADE;
3449+
DROP TABLE base_tbl;
3450+
34343451
ALTER SERVER loopback OPTIONS (DROP async_capable);
34353452
ALTER SERVER loopback2 OPTIONS (DROP async_capable);
34363453

0 commit comments

Comments
 (0)