diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index e3ee30f1aa..402882f0d2 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -10456,6 +10456,88 @@ DROP TABLE local_tbl;
 DROP INDEX base_tbl1_idx;
 DROP INDEX base_tbl2_idx;
 DROP INDEX async_p3_idx;
+-- UNION queries
+EXPLAIN (VERBOSE, COSTS OFF)
+INSERT INTO result_tbl
+(SELECT * FROM async_p1 ORDER BY a LIMIT 10)
+UNION
+(SELECT * FROM async_p2 WHERE b < 10);
+                                                   QUERY PLAN                                                    
+-----------------------------------------------------------------------------------------------------------------
+ Insert on public.result_tbl
+   ->  HashAggregate
+         Output: async_p1.a, async_p1.b, async_p1.c
+         Group Key: async_p1.a, async_p1.b, async_p1.c
+         ->  Append
+               ->  Async Foreign Scan on public.async_p1
+                     Output: async_p1.a, async_p1.b, async_p1.c
+                     Remote SQL: SELECT a, b, c FROM public.base_tbl1 ORDER BY a ASC NULLS LAST LIMIT 10::bigint
+               ->  Async Foreign Scan on public.async_p2
+                     Output: async_p2.a, async_p2.b, async_p2.c
+                     Remote SQL: SELECT a, b, c FROM public.base_tbl2 WHERE ((b < 10))
+(11 rows)
+
+INSERT INTO result_tbl
+(SELECT * FROM async_p1 ORDER BY a LIMIT 10)
+UNION
+(SELECT * FROM async_p2 WHERE b < 10);
+SELECT * FROM result_tbl ORDER BY a;
+  a   | b  |  c   
+------+----+------
+ 1000 |  0 | 0000
+ 1005 |  5 | 0005
+ 1010 | 10 | 0010
+ 1015 | 15 | 0015
+ 1020 | 20 | 0020
+ 1025 | 25 | 0025
+ 1030 | 30 | 0030
+ 1035 | 35 | 0035
+ 1040 | 40 | 0040
+ 1045 | 45 | 0045
+ 2000 |  0 | 0000
+ 2005 |  5 | 0005
+(12 rows)
+
+DELETE FROM result_tbl;
+EXPLAIN (VERBOSE, COSTS OFF)
+INSERT INTO result_tbl
+(SELECT * FROM async_p1 ORDER BY a LIMIT 10)
+UNION ALL
+(SELECT * FROM async_p2 WHERE b < 10);
+                                                QUERY PLAN                                                 
+-----------------------------------------------------------------------------------------------------------
+ Insert on public.result_tbl
+   ->  Append
+         ->  Async Foreign Scan on public.async_p1
+               Output: async_p1.a, async_p1.b, async_p1.c
+               Remote SQL: SELECT a, b, c FROM public.base_tbl1 ORDER BY a ASC NULLS LAST LIMIT 10::bigint
+         ->  Async Foreign Scan on public.async_p2
+               Output: async_p2.a, async_p2.b, async_p2.c
+               Remote SQL: SELECT a, b, c FROM public.base_tbl2 WHERE ((b < 10))
+(8 rows)
+
+INSERT INTO result_tbl
+(SELECT * FROM async_p1 ORDER BY a LIMIT 10)
+UNION ALL
+(SELECT * FROM async_p2 WHERE b < 10);
+SELECT * FROM result_tbl ORDER BY a;
+  a   | b  |  c   
+------+----+------
+ 1000 |  0 | 0000
+ 1005 |  5 | 0005
+ 1010 | 10 | 0010
+ 1015 | 15 | 0015
+ 1020 | 20 | 0020
+ 1025 | 25 | 0025
+ 1030 | 30 | 0030
+ 1035 | 35 | 0035
+ 1040 | 40 | 0040
+ 1045 | 45 | 0045
+ 2000 |  0 | 0000
+ 2005 |  5 | 0005
+(12 rows)
+
+DELETE FROM result_tbl;
 -- Test that pending requests are processed properly
 SET enable_mergejoin TO false;
 SET enable_hashjoin TO false;
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 30b5175da5..22848710c5 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -3322,6 +3322,33 @@ DROP INDEX base_tbl1_idx;
 DROP INDEX base_tbl2_idx;
 DROP INDEX async_p3_idx;
 
+-- UNION queries
+EXPLAIN (VERBOSE, COSTS OFF)
+INSERT INTO result_tbl
+(SELECT * FROM async_p1 ORDER BY a LIMIT 10)
+UNION
+(SELECT * FROM async_p2 WHERE b < 10);
+INSERT INTO result_tbl
+(SELECT * FROM async_p1 ORDER BY a LIMIT 10)
+UNION
+(SELECT * FROM async_p2 WHERE b < 10);
+
+SELECT * FROM result_tbl ORDER BY a;
+DELETE FROM result_tbl;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+INSERT INTO result_tbl
+(SELECT * FROM async_p1 ORDER BY a LIMIT 10)
+UNION ALL
+(SELECT * FROM async_p2 WHERE b < 10);
+INSERT INTO result_tbl
+(SELECT * FROM async_p1 ORDER BY a LIMIT 10)
+UNION ALL
+(SELECT * FROM async_p2 WHERE b < 10);
+
+SELECT * FROM result_tbl ORDER BY a;
+DELETE FROM result_tbl;
+
 -- Test that pending requests are processed properly
 SET enable_mergejoin TO false;
 SET enable_hashjoin TO false;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 38251c2b8e..5ef1888407 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -626,6 +626,7 @@ _copySubqueryScan(const SubqueryScan *from)
 	 * copy remainder of node
 	 */
 	COPY_NODE_FIELD(subplan);
+	COPY_SCALAR_FIELD(status);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 87561cbb6f..0d41a9ab4f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -629,6 +629,7 @@ _outSubqueryScan(StringInfo str, const SubqueryScan *node)
 	_outScanInfo(str, (const Scan *) node);
 
 	WRITE_NODE_FIELD(subplan);
+	WRITE_ENUM_FIELD(status, SubqueryScanStatus);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 0dd1ad7dfc..ebee3b6acf 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1967,6 +1967,7 @@ _readSubqueryScan(void)
 	ReadCommonScan(&local_node->scan);
 
 	READ_NODE_FIELD(subplan);
+	READ_ENUM_FIELD(status, SubqueryScanStatus);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index a5f6d678cc..9b6a8cfb74 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -82,7 +82,7 @@ static List *get_gating_quals(PlannerInfo *root, List *quals);
 static Plan *create_gating_plan(PlannerInfo *root, Path *path, Plan *plan,
 								List *gating_quals);
 static Plan *create_join_plan(PlannerInfo *root, JoinPath *best_path);
-static bool is_async_capable_path(Path *path);
+static bool mark_async_capable_plan(Plan *plan, Path *path);
 static Plan *create_append_plan(PlannerInfo *root, AppendPath *best_path,
 								int flags);
 static Plan *create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
@@ -1091,14 +1091,25 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path)
 }
 
 /*
- * is_async_capable_path
- *		Check whether a given Path node is async-capable.
+ * mark_async_capable_plan
+ *		Check whether a given Path node is async-capable, and if so, mark the
+ *		Plan node created from it as such.
  */
 static bool
-is_async_capable_path(Path *path)
+mark_async_capable_plan(Plan *plan, Path *path)
 {
 	switch (nodeTag(path))
 	{
+		case T_SubqueryScanPath:
+			{
+				SubqueryScan *splan = (SubqueryScan *) plan;
+
+				if (trivial_subqueryscan(splan) &&
+					mark_async_capable_plan(splan->subplan,
+											((SubqueryScanPath *) path)->subpath))
+					break;
+				return false;
+			}
 		case T_ForeignPath:
 			{
 				FdwRoutine *fdwroutine = path->parent->fdwroutine;
@@ -1106,13 +1117,15 @@ is_async_capable_path(Path *path)
 				Assert(fdwroutine != NULL);
 				if (fdwroutine->IsForeignPathAsyncCapable != NULL &&
 					fdwroutine->IsForeignPathAsyncCapable((ForeignPath *) path))
-					return true;
+					break;
+				return false;
 			}
-			break;
 		default:
-			break;
+			return false;
 	}
-	return false;
+
+	plan->async_capable = true;
+	return true;
 }
 
 /*
@@ -1278,9 +1291,9 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		subplans = lappend(subplans, subplan);
 
 		/* Check to see if subplan can be executed asynchronously */
-		if (consider_async && is_async_capable_path(subpath))
+		if (consider_async && mark_async_capable_plan(subplan, subpath))
 		{
-			subplan->async_capable = true;
+			Assert(subplan->async_capable);
 			++nasyncplans;
 		}
 	}
@@ -5551,6 +5564,7 @@ make_subqueryscan(List *qptlist,
 	plan->righttree = NULL;
 	node->scan.scanrelid = scanrelid;
 	node->subplan = subplan;
+	node->status = SUBQUERY_SCAN_UNKNOWN;
 
 	return node;
 }
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index e50624c465..a4455b8a44 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -115,7 +115,6 @@ static Plan *set_indexonlyscan_references(PlannerInfo *root,
 static Plan *set_subqueryscan_references(PlannerInfo *root,
 										 SubqueryScan *plan,
 										 int rtoffset);
-static bool trivial_subqueryscan(SubqueryScan *plan);
 static Plan *clean_up_removed_plan_level(Plan *parent, Plan *child);
 static void set_foreignscan_references(PlannerInfo *root,
 									   ForeignScan *fscan,
@@ -1206,13 +1205,22 @@ set_subqueryscan_references(PlannerInfo *root,
  * We can delete it if it has no qual to check and the targetlist just
  * regurgitates the output of the child plan.
  */
-static bool
+bool
 trivial_subqueryscan(SubqueryScan *plan)
 {
 	int			attrno;
 	ListCell   *lp,
 			   *lc;
 
+	/* We might have detected this already */
+	if (plan->status == SUBQUERY_SCAN_TRIVIAL)
+		return true;
+	if (plan->status == SUBQUERY_SCAN_NONTRIVIAL)
+		return false;
+	Assert(plan->status == SUBQUERY_SCAN_UNKNOWN);
+
+	plan->status = SUBQUERY_SCAN_NONTRIVIAL;
+
 	if (plan->scan.plan.qual != NIL)
 		return false;
 
@@ -1254,6 +1262,7 @@ trivial_subqueryscan(SubqueryScan *plan)
 		attrno++;
 	}
 
+	plan->status = SUBQUERY_SCAN_TRIVIAL;
 	return true;
 }
 
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index ec9a8b0c81..67f21080df 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -518,16 +518,28 @@ typedef struct TidRangeScan
  * relation, we make this a descendant of Scan anyway for code-sharing
  * purposes.
  *
+ * SubqueryScanStatus caches the trivial_subqueryscan property of the node.
+ * SUBQUERY_SCAN_UNKNOWN means not yet determined.  This is only used during
+ * planning.
+ *
  * Note: we store the sub-plan in the type-specific subplan field, not in
  * the generic lefttree field as you might expect.  This is because we do
  * not want plan-tree-traversal routines to recurse into the subplan without
  * knowing that they are changing Query contexts.
  * ----------------
  */
+typedef enum SubqueryScanStatus
+{
+	SUBQUERY_SCAN_UNKNOWN,
+	SUBQUERY_SCAN_TRIVIAL,
+	SUBQUERY_SCAN_NONTRIVIAL
+} SubqueryScanStatus;
+
 typedef struct SubqueryScan
 {
 	Scan		scan;
 	Plan	   *subplan;
+	SubqueryScanStatus status;
 } SubqueryScan;
 
 /* ----------------
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index bf1adfc52a..c908a49490 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -112,6 +112,7 @@ extern bool innerrel_is_unique(PlannerInfo *root,
  * prototypes for plan/setrefs.c
  */
 extern Plan *set_plan_references(PlannerInfo *root, Plan *plan);
+extern bool trivial_subqueryscan(SubqueryScan *plan);
 extern void record_plan_function_dependency(PlannerInfo *root, Oid funcid);
 extern void record_plan_type_dependency(PlannerInfo *root, Oid typid);
 extern bool extract_query_dependencies_walker(Node *node, PlannerInfo *root);
