diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index 42c7c5f554..c08cfdaaaa 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -138,10 +138,12 @@ typedef struct PruneStepResult
 } PruneStepResult;
 
 
+static List *add_part_rel_list(List *partrellists, List *partrellist);
 static List *make_partitionedrel_pruneinfo(PlannerInfo *root,
 										   RelOptInfo *parentrel,
+										   List *partrellist,
+										   List *prunequal,
 										   int *relid_subplan_map,
-										   Relids partrelids, List *prunequal,
 										   Bitmapset **matchedsubplans);
 static void gen_partprune_steps(RelOptInfo *rel, List *clauses,
 								PartClauseTarget target,
@@ -213,67 +215,103 @@ static void partkey_datum_from_expr(PartitionPruneContext *context,
  *
  * 'parentrel' is the RelOptInfo for an appendrel, and 'subpaths' is the list
  * of scan paths for its child rels.
- *
- * 'partitioned_rels' is a List containing Lists of relids of partitioned
- * tables (a/k/a non-leaf partitions) that are parents of some of the child
- * rels.  Here we attempt to populate the PartitionPruneInfo by adding a
- * 'prune_infos' item for each sublist in the 'partitioned_rels' list.
- * However, some of the sets of partitioned relations may not require any
- * run-time pruning.  In these cases we'll simply not include a 'prune_infos'
- * item for that set and instead we'll add all the subplans which belong to
- * that set into the PartitionPruneInfo's 'other_subplans' field.  Callers
- * will likely never want to prune subplans which are mentioned in this field.
- *
- * 'prunequal' is a list of potential pruning quals.
+ * 'prunequal' is a list of potential pruning quals (i.e., restriction
+ * clauses that are applicable to the appendrel).
  */
 PartitionPruneInfo *
 make_partition_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
-						 List *subpaths, List *partitioned_rels,
+						 List *subpaths, List *unused_partitioned_rels,
 						 List *prunequal)
 {
 	PartitionPruneInfo *pruneinfo;
 	Bitmapset  *allmatchedsubplans = NULL;
+	List	   *partrellists;
+	List	   *prunerelinfos;
 	int		   *relid_subplan_map;
 	ListCell   *lc;
-	List	   *prunerelinfos;
 	int			i;
 
 	/*
-	 * Construct a temporary array to map from planner relids to subplan
-	 * indexes.  For convenience, we use 1-based indexes here, so that zero
-	 * can represent an un-filled array entry.
+	 * Scan the subpaths to see which ones are scans of partition child
+	 * relations, and make lists of their parent partitioned rels.  (We must
+	 * restrict the parent partitioned rels to be parentrel or children of
+	 * parentrel, otherwise we couldn't translate prunequal to match.)  Also
+	 * construct a temporary array to map from partition-child-relation relid
+	 * to the index in 'subpaths' of the scan plan for that partition.  (Use
+	 * of "subplan" rather than "subpath" is a bit of a misnomer, but we'll
+	 * let it stand.)  For convenience, we use 1-based indexes here, so that
+	 * zero can represent an un-filled array entry.
 	 */
+	partrellists = NIL;
 	relid_subplan_map = palloc0(sizeof(int) * root->simple_rel_array_size);
 
-	/*
-	 * relid_subplan_map maps relid of a leaf partition to the index in
-	 * 'subpaths' of the scan plan for that partition.
-	 */
 	i = 1;
 	foreach(lc, subpaths)
 	{
 		Path	   *path = (Path *) lfirst(lc);
 		RelOptInfo *pathrel = path->parent;
 
-		Assert(IS_SIMPLE_REL(pathrel));
-		Assert(pathrel->relid < root->simple_rel_array_size);
-		/* No duplicates please */
-		Assert(relid_subplan_map[pathrel->relid] == 0);
+		/* We don't consider partitioned joins here */
+		if (pathrel->reloptkind == RELOPT_OTHER_MEMBER_REL)
+		{
+			RelOptInfo *prel = pathrel;
+			List	   *partrellist = NIL;
 
-		relid_subplan_map[pathrel->relid] = i++;
+			/*
+			 * Traverse up to the pathrel's topmost partitioned parent; but
+			 * stop if we reach parentrel.  (Normally, the topmost partitioned
+			 * parent is either parentrel or a UNION ALL appendrel child of
+			 * parentrel.  But when handling partitionwise joins of
+			 * multi-level partitioning trees, we can see an append path whose
+			 * parentrel is an intermediate partitioned table.)
+			 */
+			do
+			{
+				AppendRelInfo *appinfo;
+
+				Assert(prel->relid < root->simple_rel_array_size);
+				appinfo = root->append_rel_array[prel->relid];
+				prel = find_base_rel(root, appinfo->parent_relid);
+				if (!IS_PARTITIONED_REL(prel))
+					break;		/* reached a non-partitioned parent */
+				/* accept this level as an interesting parent */
+				partrellist = lcons(prel, partrellist);
+				if (prel == parentrel)
+					break;		/* don't traverse above appendrel's level */
+			} while (prel->reloptkind == RELOPT_OTHER_MEMBER_REL);
+
+			if (partrellist)
+			{
+				/*
+				 * Found some relevant parent partitions, which may or may not
+				 * overlap with partition trees we already found.  Add new
+				 * information to the partrellists list-of-lists.
+				 */
+				partrellists = add_part_rel_list(partrellists, partrellist);
+				/* Also record the subplan in relid_subplan_map[] */
+				/* No duplicates please */
+				Assert(relid_subplan_map[pathrel->relid] == 0);
+				relid_subplan_map[pathrel->relid] = i;
+			}
+		}
+		i++;
 	}
 
-	/* We now build a PartitionedRelPruneInfo for each partitioned rel. */
+	/*
+	 * We now build a PartitionedRelPruneInfo for each topmost partitioned rel
+	 * (omitting any that turn out not to have useful pruning quals).
+	 */
 	prunerelinfos = NIL;
-	foreach(lc, partitioned_rels)
+	foreach(lc, partrellists)
 	{
-		Relids		partrelids = (Relids) lfirst(lc);
+		List	   *partrellist = (List *) lfirst(lc);
 		List	   *pinfolist;
 		Bitmapset  *matchedsubplans = NULL;
 
 		pinfolist = make_partitionedrel_pruneinfo(root, parentrel,
+												  partrellist,
+												  prunequal,
 												  relid_subplan_map,
-												  partrelids, prunequal,
 												  &matchedsubplans);
 
 		/* When pruning is possible, record the matched subplans */
@@ -299,7 +337,7 @@ make_partition_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
 	pruneinfo->prune_infos = prunerelinfos;
 
 	/*
-	 * Some subplans may not belong to any of the listed partitioned rels.
+	 * Some subplans may not belong to any of the identified partitioned rels.
 	 * This can happen for UNION ALL queries which include a non-partitioned
 	 * table, or when some of the hierarchies aren't run-time prunable.  Build
 	 * a bitmapset of the indexes of all such subplans, so that the executor
@@ -321,28 +359,77 @@ make_partition_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
 	return pruneinfo;
 }
 
+/*
+ * add_part_rel_list
+ *		Add new partitioning info to a list of lists of partitioned rels.
+ *
+ * Within partrellists, there is one sublist for each topmost parent
+ * partitioned rel.  Within that sublist, after the topmost parent,
+ * we include each child non-leaf partition it has, omitting duplicates.
+ * Within sublists, children appear before their grandchildren.
+ *
+ * partrellist is a nonempty list of partitioned rels to add to this forest.
+ * It must list ancestors before children.
+ *
+ * Note that the result contains only rels that are parents of some scan-level
+ * relation appearing in the 'subpaths' that make_partition_pruneinfo() is
+ * dealing with.  Also, "topmost" parents are not allowed to be higher than
+ * the 'parentrel' associated with the append path.  In this way, we avoid
+ * expending cycles on partitioned rels that can't contribute useful pruning
+ * information for the problem at hand.  (Note that it is possible for
+ * 'parentrel' to be a child partitioned table, and it is also possible for
+ * scan-level relations to be child partitioned tables rather than leaf
+ * partitions.  Hence we must construct this relation set with reference to
+ * the particular append path we're dealing with, rather than looking at the
+ * full partitioning structure represented in the RelOptInfos.)
+ */
+static List *
+add_part_rel_list(List *partrellists, List *partrellist)
+{
+	RelOptInfo *targetpart = (RelOptInfo *) linitial(partrellist);
+	ListCell   *lc;
+
+	/* Look for a matching topmost parent */
+	foreach(lc, partrellists)
+	{
+		List	   *thislist = (List *) lfirst(lc);
+		RelOptInfo *thistarget = (RelOptInfo *) linitial(thislist);
+
+		if (thistarget == targetpart)
+		{
+			/* Found a match, so add any new children to this sublist */
+			thislist = list_concat_unique_ptr(thislist, partrellist);
+			lfirst(lc) = thislist;	/* not really necessary */
+			return partrellists;
+		}
+	}
+	/* No match, so begin a new sublist */
+	return lappend(partrellists, partrellist);
+}
+
 /*
  * make_partitionedrel_pruneinfo
  *		Build a List of PartitionedRelPruneInfos, one for each partitioned
  *		rel.  These can be used in the executor to allow additional partition
  *		pruning to take place.
  *
- * Here we generate partition pruning steps for 'prunequal' and also build a
- * data structure which allows mapping of partition indexes into 'subpaths'
- * indexes.
- *
- * If no non-Const expressions are being compared to the partition key in any
- * of the 'partitioned_rels', then we return NIL to indicate no run-time
- * pruning should be performed.  Run-time pruning would be useless since the
- * pruning done during planning will have pruned everything that can be.
- *
- * On non-NIL return, 'matchedsubplans' is set to the subplan indexes which
- * were matched to this partition hierarchy.
+ * parentrel: rel associated with the appendpath being considered
+ * partrellist: list of a parent partitioned table's RelOptInfo, followed
+ *   by RelOptInfos for relevant nonleaf children
+ * prunequal: potential pruning quals, represented for parentrel
+ * relid_subplan_map[]: maps child relation relids to subplan indexes
+ * matchedsubplans: on success, receives the set of subplan indexes which
+ *   were matched to this partition hierarchy
+ *
+ * If we cannot find any useful run-time pruning steps, return NIL.
+ * However, on success, each element of partrellist will have an element
+ * in the result list, even if some of them are useless.
  */
 static List *
 make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
+							  List *partrellist,
+							  List *prunequal,
 							  int *relid_subplan_map,
-							  Relids partrelids, List *prunequal,
 							  Bitmapset **matchedsubplans)
 {
 	RelOptInfo *targetpart = NULL;
@@ -351,7 +438,6 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
 	int		   *relid_subpart_map;
 	Bitmapset  *subplansfound = NULL;
 	ListCell   *lc;
-	int			rti;
 	int			i;
 
 	/*
@@ -365,10 +451,9 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
 	relid_subpart_map = palloc0(sizeof(int) * root->simple_rel_array_size);
 
 	i = 1;
-	rti = -1;
-	while ((rti = bms_next_member(partrelids, rti)) > 0)
+	foreach(lc, partrellist)
 	{
-		RelOptInfo *subpart = find_base_rel(root, rti);
+		RelOptInfo *subpart = (RelOptInfo *) lfirst(lc);
 		PartitionedRelPruneInfo *pinfo;
 		List	   *partprunequal;
 		List	   *initial_pruning_steps;
@@ -384,8 +469,8 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
 		 * partition.  We use 1-based indexes here, so that zero can represent
 		 * an un-filled array entry.
 		 */
-		Assert(rti < root->simple_rel_array_size);
-		relid_subpart_map[rti] = i++;
+		Assert(subpart->relid < root->simple_rel_array_size);
+		relid_subpart_map[subpart->relid] = i++;
 
 		/*
 		 * Translate pruning qual, if necessary, for this partition.
@@ -509,7 +594,7 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
 
 		/* Begin constructing the PartitionedRelPruneInfo for this rel */
 		pinfo = makeNode(PartitionedRelPruneInfo);
-		pinfo->rtindex = rti;
+		pinfo->rtindex = subpart->relid;
 		pinfo->initial_pruning_steps = initial_pruning_steps;
 		pinfo->exec_pruning_steps = exec_pruning_steps;
 		pinfo->execparamids = execparamids;
