While looking at the latest version of the patch, I've asked myself, why
consider_async_append decisions are a bit different now for Append and
MergeAppend.
Perhaps, Append shouldn't completely avoid introducing async foreign
scans when it deals with range-partitioned tables?
--
Best regards,
Alexander Pyhalov,
Postgres Professional
From 7b19b80a7f231e46184e378d76b3157f0e856b4d Mon Sep 17 00:00:00 2001
From: Alexander Pyhalov <[email protected]>
Date: Tue, 3 Nov 2026 16:31:37 +0300
Subject: [PATCH] Make Append subplans async capable in more cases
---
.../postgres_fdw/expected/postgres_fdw.out | 38 +++++++++++++++++++
contrib/postgres_fdw/sql/postgres_fdw.sql | 4 ++
src/backend/optimizer/plan/createplan.c | 6 ++-
3 files changed, 46 insertions(+), 2 deletions(-)
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index cb0c525bd2d..a23ddf3f3d8 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -12365,6 +12365,44 @@ SELECT * FROM result_tbl ORDER BY a;
(2 rows)
DELETE FROM result_tbl;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM async_pt WHERE b % 100 = 0 ORDER BY a;
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------
+ Append
+ -> Async Foreign Scan on public.async_p1 async_pt_1
+ Output: async_pt_1.a, async_pt_1.b, async_pt_1.c
+ Remote SQL: SELECT a, b, c FROM public.base_tbl1 WHERE (((b % 100) = 0)) ORDER BY a ASC NULLS LAST
+ -> Async Foreign Scan on public.async_p2 async_pt_2
+ Output: async_pt_2.a, async_pt_2.b, async_pt_2.c
+ Remote SQL: SELECT a, b, c FROM public.base_tbl2 WHERE (((b % 100) = 0)) ORDER BY a ASC NULLS LAST
+(7 rows)
+
+SELECT * FROM async_pt WHERE b % 100 = 0 ORDER BY a;
+ a | b | c
+------+-----+------
+ 1000 | 0 | 0000
+ 1100 | 100 | 0100
+ 1200 | 200 | 0200
+ 1300 | 300 | 0300
+ 1400 | 400 | 0400
+ 1500 | 500 | 0500
+ 1600 | 600 | 0600
+ 1700 | 700 | 0700
+ 1800 | 800 | 0800
+ 1900 | 900 | 0900
+ 2000 | 0 | 0000
+ 2100 | 100 | 0100
+ 2200 | 200 | 0200
+ 2300 | 300 | 0300
+ 2400 | 400 | 0400
+ 2500 | 500 | 0500
+ 2600 | 600 | 0600
+ 2700 | 700 | 0700
+ 2800 | 800 | 0800
+ 2900 | 900 | 0900
+(20 rows)
+
-- Merge Append cases
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM async_pt WHERE b % 100 = 0 ORDER BY b, a;
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 5d6caf0e41e..2c70dec2213 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -4291,6 +4291,10 @@ INSERT INTO result_tbl SELECT a, b, 'AAA' || c FROM async_pt WHERE b === 505;
SELECT * FROM result_tbl ORDER BY a;
DELETE FROM result_tbl;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM async_pt WHERE b % 100 = 0 ORDER BY a;
+SELECT * FROM async_pt WHERE b % 100 = 0 ORDER BY a;
+
-- Merge Append cases
EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM async_pt WHERE b % 100 = 0 ORDER BY b, a;
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 73438e76945..2b9d00e1b9c 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -1298,7 +1298,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
}
/* If appropriate, consider async append */
- consider_async = (enable_async_append && pathkeys == NIL &&
+ consider_async = (enable_async_append &&
!best_path->path.parallel_safe &&
list_length(best_path->subpaths) > 1);
@@ -1307,6 +1307,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
{
Path *subpath = (Path *) lfirst(subpaths);
Plan *subplan;
+ bool created_sort_plan = false;
/* Must insist that all children return the same tlist */
subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
@@ -1390,11 +1391,12 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
}
subplan = sort_plan;
+ created_sort_plan = true;
}
}
/* If needed, check to see if subplan can be executed asynchronously */
- if (consider_async && mark_async_capable_plan(subplan, subpath))
+ if (consider_async && !created_sort_plan && mark_async_capable_plan(subplan, subpath))
{
Assert(subplan->async_capable);
++nasyncplans;
--
2.43.0