diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 477b9f7fb8..6ac8d9767b 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -102,8 +102,9 @@ static void generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel,
 static Path *get_cheapest_parameterized_child_path(PlannerInfo *root,
 									  RelOptInfo *rel,
 									  Relids required_outer);
-static void accumulate_append_subpath(Path *path,
-						  List **subpaths, List **special_subpaths);
+static void accumulate_append_subpath(RelOptInfo *parentrel,
+						  RelOptInfo *childrel, Path *path, List **subpaths,
+						  List **special_subpaths);
 static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
 					  Index rti, RangeTblEntry *rte);
 static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel,
@@ -1395,17 +1396,6 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 	 * AppendPath generated for partitioned tables must record the RT indexes
 	 * of partitioned tables that are direct or indirect children of this
 	 * Append rel.
-	 *
-	 * AppendPath may be for a sub-query RTE (UNION ALL), in which case, 'rel'
-	 * itself does not represent a partitioned relation, but the child sub-
-	 * queries may contain references to partitioned relations.  The loop
-	 * below will look for such children and collect them in a list to be
-	 * passed to the path creation function.  (This assumes that we don't need
-	 * to look through multiple levels of subquery RTEs; if we ever do, we
-	 * could consider stuffing the list we generate here into sub-query RTE's
-	 * RelOptInfo, just like we do for partitioned rels, which would be used
-	 * when populating our parent rel with paths.  For the present, that
-	 * appears to be unnecessary.)
 	 */
 	if (rel->part_scheme != NULL)
 	{
@@ -1435,10 +1425,16 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 
 		Assert(list_length(partitioned_rels) >= 1);
 	}
-	else if (rel->rtekind == RTE_SUBQUERY)
-		build_partitioned_rels = true;
 
 	/*
+	 * For childrels that are themselves inheritance or UNION ALL parents,
+	 * we recursively pullup their Append and MergeAppend subpaths into rel's
+	 * path lists.  This effectively flattens the hierarchy and stops nested
+	 * Append/MergeAppend paths forming.  This is not done for UNION ALL
+	 * parents with partitioned tables in their subpaths.  These are left
+	 * unflattened as run-time partition pruning requires partitioned_rels to
+	 * only contain partitions which belong to a single hierarchy.
+	 *
 	 * For every non-dummy child, remember the cheapest path.  Also, identify
 	 * all pathkeys (orderings) and parameterizations (required_outer sets)
 	 * available for the non-dummy member relations.
@@ -1471,7 +1467,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 		 */
 		if (childrel->pathlist != NIL &&
 			childrel->cheapest_total_path->param_info == NULL)
-			accumulate_append_subpath(childrel->cheapest_total_path,
+			accumulate_append_subpath(rel, childrel, childrel->cheapest_total_path,
 									  &subpaths, NULL);
 		else
 			subpaths_valid = false;
@@ -1480,7 +1476,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 		if (childrel->partial_pathlist != NIL)
 		{
 			cheapest_partial_path = linitial(childrel->partial_pathlist);
-			accumulate_append_subpath(cheapest_partial_path,
+			accumulate_append_subpath(rel, childrel, cheapest_partial_path,
 									  &partial_subpaths, NULL);
 		}
 		else
@@ -1508,7 +1504,8 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 			{
 				/* Partial path is cheaper or the only option. */
 				Assert(cheapest_partial_path != NULL);
-				accumulate_append_subpath(cheapest_partial_path,
+				accumulate_append_subpath(rel, childrel,
+										  cheapest_partial_path,
 										  &pa_partial_subpaths,
 										  &pa_nonpartial_subpaths);
 
@@ -1528,7 +1525,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 				 * be given to different workers.  For now, we don't try to
 				 * figure that out.
 				 */
-				accumulate_append_subpath(nppath,
+				accumulate_append_subpath(rel, childrel, nppath,
 										  &pa_nonpartial_subpaths,
 										  NULL);
 			}
@@ -1754,7 +1751,8 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 				subpaths_valid = false;
 				break;
 			}
-			accumulate_append_subpath(subpath, &subpaths, NULL);
+			accumulate_append_subpath(rel, childrel, subpath, &subpaths,
+									  NULL);
 		}
 
 		if (subpaths_valid)
@@ -1845,9 +1843,9 @@ generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel,
 			if (cheapest_startup != cheapest_total)
 				startup_neq_total = true;
 
-			accumulate_append_subpath(cheapest_startup,
+			accumulate_append_subpath(rel, childrel, cheapest_startup,
 									  &startup_subpaths, NULL);
-			accumulate_append_subpath(cheapest_total,
+			accumulate_append_subpath(rel, childrel, cheapest_total,
 									  &total_subpaths, NULL);
 		}
 
@@ -1946,10 +1944,13 @@ get_cheapest_parameterized_child_path(PlannerInfo *root, RelOptInfo *rel,
  * accumulate_append_subpath
  *		Add a subpath to the list being built for an Append or MergeAppend.
  *
- * It's possible that the child is itself an Append or MergeAppend path, in
- * which case we can "cut out the middleman" and just add its child paths to
- * our own list.  (We don't try to do this earlier because we need to apply
- * both levels of transformation to the quals.)
+ * For UNION ALL or inheritance parent childrels, we pullup the childrels
+ * Append and MergeAppend subpaths into 'subpaths' effectively bypassing the
+ * childrel's Append and MergeAppend paths.  We don't do this for partitioned
+ * childrels which are parented by UNION ALL parents as mixed partition
+ * hierarchies are not compatible with run-time partition pruning.
+ * (We perform the pullup operation here rather than earlier because we need
+ * to apply both levels of transformation to the quals.)
  *
  * Note that if we omit a child MergeAppend in this way, we are effectively
  * omitting a sort step, which seems fine: if the parent is to be an Append,
@@ -1965,8 +1966,15 @@ get_cheapest_parameterized_child_path(PlannerInfo *root, RelOptInfo *rel,
  * paths).
  */
 static void
-accumulate_append_subpath(Path *path, List **subpaths, List **special_subpaths)
+accumulate_append_subpath(RelOptInfo *parentrel, RelOptInfo *childrel,
+						  Path *path, List **subpaths,
+						  List **special_subpaths)
 {
+	if (parentrel->rtekind == RTE_SUBQUERY && childrel->part_scheme != NULL)
+	{
+		*subpaths = lappend(*subpaths, path);
+		return;
+	}
 	if (IsA(path, AppendPath))
 	{
 		AppendPath *apath = (AppendPath *) path;
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index cf82b7052d..b848897693 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -1065,6 +1065,12 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
 		return plan;
 	}
 
+	/*
+	 * Ensure that partitioned_rels is set for partitioned tables, and never
+	 * set otherwise.
+	 */
+	Assert((best_path->partitioned_rels != NIL) == (rel->part_scheme != NULL));
+
 	/* Build the plan for each child */
 	foreach(subpaths, best_path->subpaths)
 	{
@@ -1077,8 +1083,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
 		subplans = lappend(subplans, subplan);
 	}
 
-	if (enable_partition_pruning &&
-		rel->reloptkind == RELOPT_BASEREL &&
+	if (enable_partition_pruning && IS_SIMPLE_REL(rel) &&
 		best_path->partitioned_rels != NIL)
 	{
 		List	   *prunequal;
diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
index ab32c7d67e..6ad59a1afe 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -2768,6 +2768,27 @@ select * from boolp where a = (select value from boolvalues where not value);
          Filter: (a = $0)
 (9 rows)
 
+-- Ensure runtime pruning works when partitioned tables are parented by
+-- UNION ALL parents
+explain (analyze, costs off, summary off, timing off)
+select * from (select * from boolp union all select * from boolp) b where a = (select true);
+                            QUERY PLAN                             
+-------------------------------------------------------------------
+ Append (actual rows=0 loops=1)
+   InitPlan 1 (returns $0)
+     ->  Result (actual rows=1 loops=1)
+   ->  Append (actual rows=0 loops=1)
+         ->  Seq Scan on boolp_f (never executed)
+               Filter: (a = $0)
+         ->  Seq Scan on boolp_t (actual rows=0 loops=1)
+               Filter: (a = $0)
+   ->  Append (actual rows=0 loops=1)
+         ->  Seq Scan on boolp_f boolp_f_1 (never executed)
+               Filter: (a = $0)
+         ->  Seq Scan on boolp_t boolp_t_1 (actual rows=0 loops=1)
+               Filter: (a = $0)
+(13 rows)
+
 drop table boolp;
 reset enable_indexonlyscan;
 --
diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql
index 609fe09aeb..a7610ef550 100644
--- a/src/test/regress/sql/partition_prune.sql
+++ b/src/test/regress/sql/partition_prune.sql
@@ -701,6 +701,11 @@ select * from boolp where a = (select value from boolvalues where value);
 explain (analyze, costs off, summary off, timing off)
 select * from boolp where a = (select value from boolvalues where not value);
 
+-- Ensure runtime pruning works when partitioned tables are parented by
+-- UNION ALL parents
+explain (analyze, costs off, summary off, timing off)
+select * from (select * from boolp union all select * from boolp) b where a = (select true);
+
 drop table boolp;
 
 reset enable_indexonlyscan;
