diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e6ce8e2110..b7849560b7 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2281,6 +2281,8 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node)
 	WRITE_BOOL_FIELD(has_eclass_joins);
 	WRITE_BOOL_FIELD(consider_partitionwise_join);
 	WRITE_BITMAPSET_FIELD(top_parent_relids);
+	WRITE_BOOL_FIELD(merged);
+	WRITE_BITMAPSET_FIELD(all_partrels);
 	WRITE_NODE_FIELD(partitioned_child_rels);
 }
 
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 52abd8a12f..8442e9d186 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -48,6 +48,9 @@ static SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root,
 												Relids left_relids, Relids right_relids);
 static int	match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel,
 										 bool strict_op);
+static void get_matching_part_pairs(PlannerInfo *root, RelOptInfo *joinrel,
+						RelOptInfo *rel1, RelOptInfo *rel2,
+						List **parts1, List **parts2);
 
 
 /*
@@ -1357,19 +1360,18 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 {
 	bool		rel1_is_simple = IS_SIMPLE_REL(rel1);
 	bool		rel2_is_simple = IS_SIMPLE_REL(rel2);
-	PartitionScheme part_scheme;
-	PartitionBoundInfo join_boundinfo;
-	List	   *parts1;
-	List	   *parts2;
-	ListCell   *lc1;
-	ListCell   *lc2;
+	bool		merged = false;
+	List	   *parts1 = NIL;
+	List	   *parts2 = NIL;
+	ListCell   *lcr1 = NULL;
+	ListCell   *lcr2 = NULL;
 	int			cnt_parts;
 
 	/* Guard against stack overflow due to overly deep partition hierarchy. */
 	check_stack_depth();
 
 	/* Nothing to do, if the join relation is not partitioned. */
-	if (joinrel->part_scheme == NULL)
+	if (joinrel->part_scheme == NULL || joinrel->nparts == 0)
 		return;
 
 	/* The join relation should have consider_partitionwise_join set. */
@@ -1394,51 +1396,100 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 	 */
 	Assert(joinrel->part_scheme == rel1->part_scheme &&
 		   joinrel->part_scheme == rel2->part_scheme);
-	part_scheme = joinrel->part_scheme;
 
 	/*
-	 * Get the list of matching partitions to be joined along with the
-	 * partition bounds of the join relation. Because of the restrictions
-	 * imposed by partition matching algorithm, not every pair of joining
-	 * relations for this join will be able to use partition-wise join. But all
-	 * those pairs which can use partition-wise join will produce the same
-	 * partition bounds for the join relation.
+	 * If we don't have the partition bounds for the join rel yet, try to
+	 * create it along with pairs of partitions to be joined; else generate
+	 * those using the partitioning info for the join rel we already have.
 	 */
-	join_boundinfo = partition_bounds_merge(part_scheme->partnatts,
-											part_scheme->parttyplen,
-											part_scheme->parttypbyval,
-											part_scheme->partsupfunc,
-											part_scheme->partcollation,
-											rel1, rel2,
-											parent_sjinfo->jointype,
-											&parts1, &parts2);
-
-	if (join_boundinfo == NULL)
-		return;
-
-	if (joinrel->boundinfo == NULL)
+	if (joinrel->nparts == -1)
 	{
-		Assert(joinrel->nparts == 0 && joinrel->part_rels == NULL);
-		joinrel->boundinfo = join_boundinfo;
-		joinrel->nparts = list_length(parts1);
-		Assert(joinrel->nparts == list_length(parts2));
+		PartitionScheme part_scheme = joinrel->part_scheme;
+		PartitionBoundInfo boundinfo = NULL;
+		int			nparts = 0;
+
+		Assert(joinrel->boundinfo == NULL);
+		Assert(joinrel->part_rels == NULL);
+
+		/*
+		 * See if the partition bounds for inputs are exactly the same, in
+		 * which case we don't need to work hard; partitions with the same
+		 * partition indexes will form join pairs, and the join rel will have
+		 * the same partition bounds as inputs.
+		 *
+		 * In theory, the partition bounds for inputs might be exactly the
+		 * same even in the case where one or both inputs have merged
+		 * partition bounds, but it seems unlikely to be worth the cycles to
+		 * check.
+		 */
+		if (!rel1->merged &&
+			!rel2->merged &&
+			rel1->nparts == rel2->nparts &&
+			partition_bounds_equal(part_scheme->partnatts,
+								   part_scheme->parttyplen,
+								   part_scheme->parttypbyval,
+								   rel1->boundinfo, rel2->boundinfo))
+		{
+			boundinfo = rel1->boundinfo;
+			nparts = rel1->nparts;
+		}
+
+		/*
+		 * Try to create the partition bounds along with join pairs.
+		 */
+		if (boundinfo == NULL)
+		{
+			boundinfo = partition_bounds_merge(part_scheme->partnatts,
+											   part_scheme->parttyplen,
+											   part_scheme->parttypbyval,
+											   part_scheme->partsupfunc,
+											   part_scheme->partcollation,
+											   rel1, rel2,
+											   parent_sjinfo->jointype,
+											   &parts1, &parts2);
+			Assert(list_length(parts1) == list_length(parts2));
+			if (boundinfo == NULL)
+			{
+				joinrel->nparts = 0;
+				return;
+			}
+			nparts = list_length(parts1);
+			merged = true;
+		}
+
+		Assert(nparts > 0);
+		joinrel->boundinfo = boundinfo;
+		joinrel->merged = merged;
+		joinrel->nparts = nparts;
 		joinrel->part_rels =
-			(RelOptInfo **) palloc0(sizeof(RelOptInfo *) *
-									joinrel->nparts);
+			(RelOptInfo **) palloc0(sizeof(RelOptInfo *) * nparts);
 	}
 	else
 	{
-		Assert(partition_bounds_equal(part_scheme->partnatts,
-									  part_scheme->parttyplen,
-									  part_scheme->parttypbyval,
-									  join_boundinfo, joinrel->boundinfo));
+		Assert(joinrel->nparts > 0);
+		Assert(joinrel->boundinfo);
+		Assert(joinrel->part_rels);
+
 		/*
-		 * Every pair of joining relations should result in the same number
-		 * of child-joins.
+		 * If the partition bounds for the join rel are not merged ones,
+		 * inputs are guaranteed to have the same partition bounds, so
+		 * partitions with the same partition indexes will form join pairs;
+		 * else let get_matching_part_pairs() do the work.
 		 */
-		Assert(joinrel->nparts == list_length(parts1));
-		Assert(joinrel->nparts == list_length(parts2));
-		Assert(joinrel->part_rels);
+		if (joinrel->merged)
+		{
+			get_matching_part_pairs(root, joinrel, rel1, rel2,
+									&parts1, &parts2);
+			Assert(list_length(parts1) == joinrel->nparts);
+			Assert(list_length(parts2) == joinrel->nparts);
+			merged = true;
+		}
+	}
+
+	if (merged)
+	{
+		lcr1 = list_head(parts1);
+		lcr2 = list_head(parts2);
 	}
 
 	/*
@@ -1446,15 +1497,14 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 	 * exist. Add paths to child-joins for a pair of child relations
 	 * corresponding to the given pair of parent relations.
 	 */
-	cnt_parts = -1;
-	forboth(lc1, parts1, lc2, parts2)
+	for (cnt_parts = 0; cnt_parts < joinrel->nparts; cnt_parts++)
 	{
-		int			part1 = lfirst_int(lc1);
-		int			part2 = lfirst_int(lc2);
-		RelOptInfo *child_rel1;
-		RelOptInfo *child_rel2;
-		bool		rel1_empty;
-		bool		rel2_empty;
+		RelOptInfo *child_rel1 = merged ? (RelOptInfo *) lfirst(lcr1) : rel1->part_rels[cnt_parts];
+		RelOptInfo *child_rel2 = merged ? (RelOptInfo *) lfirst(lcr2) : rel2->part_rels[cnt_parts];
+		bool		rel1_empty = (child_rel1 == NULL ||
+								  IS_DUMMY_REL(child_rel1));
+		bool		rel2_empty = (child_rel2 == NULL ||
+								  IS_DUMMY_REL(child_rel2));
 		SpecialJoinInfo *child_sjinfo;
 		List	   *child_restrictlist;
 		RelOptInfo *child_joinrel;
@@ -1462,12 +1512,11 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 		AppendRelInfo **appinfos;
 		int			nappinfos;
 
-		Assert(part1 >= 0 && part2 >= 0);
-		child_rel1 = rel1->part_rels[part1];
-		child_rel2 = rel2->part_rels[part2];
-		rel1_empty = (child_rel1 == NULL || IS_DUMMY_REL(child_rel1));
-		rel2_empty = (child_rel2 == NULL || IS_DUMMY_REL(child_rel2));
-		cnt_parts++;
+		if (merged)
+		{
+			lcr1 = lnext(parts1, lcr1);
+			lcr2 = lnext(parts2, lcr2);
+		}
 
 		/*
 		 * Check for cases where we can prove that this segment of the join
@@ -1566,25 +1615,17 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 												 child_sjinfo,
 												 child_sjinfo->jointype);
 			joinrel->part_rels[cnt_parts] = child_joinrel;
+			joinrel->all_partrels = bms_add_members(joinrel->all_partrels,
+													child_joinrel->relids);
 		}
 
-		/*
-		 * For every pair of joining relations, the set of matching partitions
-		 * would change. However, the base relation partitions constituting
-		 * the given child should remain same for all the joining pairs. Since
-		 * the order in which children are stored in the array of child-joins,
-		 * depends upon partition bounds of the join, which are same for all
-		 * the joining pairs, every joining pair yields the child-joins in the
-		 * same order.
-		 */
 		Assert(bms_equal(child_joinrel->relids, child_joinrelids));
 
 		populate_joinrel_with_paths(root, child_rel1, child_rel2,
 									child_joinrel, child_sjinfo,
 									child_restrictlist);
-	}
 
-	Assert(cnt_parts == (joinrel->nparts - 1));
+	}
 }
 
 /*
@@ -1793,3 +1834,97 @@ match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel, bool strict_op)
 
 	return -1;
 }
+
+/*
+ * get_matching_part_pairs
+ *		Generate join pairs of partitions for the two inputs for the join rel
+ */
+static void
+get_matching_part_pairs(PlannerInfo *root, RelOptInfo *joinrel,
+						RelOptInfo *rel1, RelOptInfo *rel2,
+						List **parts1, List **parts2)
+{
+	bool		rel1_is_simple = IS_SIMPLE_REL(rel1);
+	bool		rel2_is_simple = IS_SIMPLE_REL(rel2);
+	int 		cnt_parts;
+
+	*parts1 = NIL;
+	*parts2 = NIL;
+
+	for (cnt_parts = 0; cnt_parts < joinrel->nparts; cnt_parts++)
+	{
+		RelOptInfo *child_joinrel = joinrel->part_rels[cnt_parts];
+		RelOptInfo *child_rel1;
+		RelOptInfo *child_rel2;
+		Relids		child_relids1;
+		Relids		child_relids2;
+
+		/*
+		 * If this segment of the join is empty, it means that this segment
+		 * was ignored when previously creating child-join paths for it in
+		 * try_partitionwise_join() as it would not contribute to the join
+		 * result, due to one or both inputs being empty; add NULL to each of
+		 * the given lists so that this segment will be ignored again in that
+		 * function.
+		 */
+		if (!child_joinrel)
+		{
+			*parts1 = lappend(*parts1, NULL);
+			*parts2 = lappend(*parts2, NULL);
+			continue;
+		}
+
+		/*
+		 * Get a relids set of partition(s) involved in this join segment that
+		 * are from the rel1 side.
+		 */
+		child_relids1 = bms_intersect(child_joinrel->relids,
+									  rel1->all_partrels);
+		Assert(bms_num_members(child_relids1) == bms_num_members(rel1->relids));
+		/*
+		 * Get a child rel for rel1 with the relids.  Note that we should have
+		 * the child rel even if rel1 is a join rel, because in that case the
+		 * partitions specified in the relids would have matching/overlapping
+		 * boundaries, so those partitions should be considered as ones to be
+		 * joined even when planning partitionwise joins of rel1, meaning that
+		 * the child rel would have been built by the time we get here.
+		 */
+		if (rel1_is_simple)
+		{
+			int			varno = bms_singleton_member(child_relids1);
+
+			child_rel1 = find_base_rel(root, varno);
+		}
+		else
+			child_rel1 = find_join_rel(root, child_relids1);
+		Assert(child_rel1);
+
+		/*
+		 * Get a relids set of partition(s) involved in this join segment that
+		 * are from the rel2 side.
+		 */
+		child_relids2 = bms_intersect(child_joinrel->relids,
+									  rel2->all_partrels);
+		Assert(bms_num_members(child_relids2) == bms_num_members(rel2->relids));
+		/*
+		 * Get a child rel for rel2 with the relids.  See above comments.
+		 */
+		if (rel2_is_simple)
+		{
+			int			varno = bms_singleton_member(child_relids2);
+
+			child_rel2 = find_base_rel(root, varno);
+		}
+		else
+			child_rel2 = find_join_rel(root, child_relids2);
+		Assert(child_rel2);
+
+		/*
+		 * The join of rel1 and rel2 is legal, so is the join of the child
+		 * rels obtained above; add them to the given lists as a join pair
+		 * producing this join segment.
+		 */
+		*parts1 = lappend(*parts1, child_rel1);
+		*parts2 = lappend(*parts2, child_rel2);
+	}
+}
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 38bc61e687..caf6039c10 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -376,6 +376,8 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
 		/* Create the otherrel RelOptInfo too. */
 		childrelinfo = build_simple_rel(root, childRTindex, relinfo);
 		relinfo->part_rels[i] = childrelinfo;
+		relinfo->all_partrels = bms_add_members(relinfo->all_partrels,
+												childrelinfo->relids);
 
 		/* If this child is itself partitioned, recurse */
 		if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index ce85082bb3..248a9e9093 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -240,10 +240,12 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->has_eclass_joins = false;
 	rel->consider_partitionwise_join = false;	/* might get changed later */
 	rel->part_scheme = NULL;
-	rel->nparts = 0;
+	rel->nparts = -1;
 	rel->boundinfo = NULL;
+	rel->merged = false;
 	rel->partition_qual = NIL;
 	rel->part_rels = NULL;
+	rel->all_partrels = NULL;
 	rel->partexprs = NULL;
 	rel->nullable_partexprs = NULL;
 	rel->partitioned_child_rels = NIL;
@@ -653,10 +655,12 @@ build_join_rel(PlannerInfo *root,
 	joinrel->consider_partitionwise_join = false;	/* might get changed later */
 	joinrel->top_parent_relids = NULL;
 	joinrel->part_scheme = NULL;
-	joinrel->nparts = 0;
+	joinrel->nparts = -1;
 	joinrel->boundinfo = NULL;
+	joinrel->merged = false;
 	joinrel->partition_qual = NIL;
 	joinrel->part_rels = NULL;
+	joinrel->all_partrels = NULL;
 	joinrel->partexprs = NULL;
 	joinrel->nullable_partexprs = NULL;
 	joinrel->partitioned_child_rels = NIL;
@@ -829,10 +833,12 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->consider_partitionwise_join = false;	/* might get changed later */
 	joinrel->top_parent_relids = NULL;
 	joinrel->part_scheme = NULL;
-	joinrel->nparts = 0;
+	joinrel->nparts = -1;
 	joinrel->boundinfo = NULL;
+	joinrel->merged = false;
 	joinrel->partition_qual = NIL;
 	joinrel->part_rels = NULL;
+	joinrel->all_partrels = NULL;
 	joinrel->partexprs = NULL;
 	joinrel->nullable_partexprs = NULL;
 	joinrel->partitioned_child_rels = NIL;
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index a8c85966af..f06fa0827c 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -124,15 +124,14 @@ static PartitionBoundInfo partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid
 							RelOptInfo *outer_rel, RelOptInfo *inner_rel,
 							List **outer_parts, List **inner_parts,
 							JoinType jointype);
-static PartitionBoundInfo partition_hash_bounds_merge(int partnatts,
-							int16 *parttyplen, bool *parttypbyval,
-							RelOptInfo *outer_rel, RelOptInfo *inner_rel,
-							List **outer_parts, List **inner_parts);
-static void generate_matching_part_pairs(PartitionMap *outer_maps,
-										 PartitionMap *inner_maps,
-										 int nparts1, int nparts2,
-										 JoinType jointype, int nparts,
-										 List **parts1, List **parts2);
+static void generate_matching_part_pairs(RelOptInfo *rel1,
+							 RelOptInfo *rel2,
+							 PartitionMap *partmaps1,
+							 PartitionMap *partmaps2,
+							 int nparts1, int nparts2,
+							 int nparts,
+							 List **matched_parts1,
+							 List **matched_parts2);
 static PartitionBoundInfo build_merged_partition_bounds(char strategy,
 							  List *merged_datums, List *merged_indexes,
 							  List *merged_contents, int null_index,
@@ -3104,6 +3103,11 @@ partition_bounds_merge(int partnatts,
 	*inner_parts = NIL;
 	switch (strategy)
 	{
+		case PARTITION_STRATEGY_HASH:
+			merged_bounds = NULL;
+
+			break;
+
 		case PARTITION_STRATEGY_LIST:
 			merged_bounds = partition_list_bounds_merge(partsupfunc,
 														partcollation,
@@ -3120,13 +3124,6 @@ partition_bounds_merge(int partnatts,
 														 partcollation);
 			break;
 
-		case PARTITION_STRATEGY_HASH:
-			merged_bounds = partition_hash_bounds_merge(partnatts, 
-														parttyplen, parttypbyval,
-														outer_rel, inner_rel,
-														outer_parts, inner_parts);
-			break;
-
 		default:
 			elog(ERROR, "unexpected partition strategy: %d", strategy);
 	}
@@ -3135,9 +3132,6 @@ partition_bounds_merge(int partnatts,
 
 	Assert(list_length(*outer_parts) == list_length(*inner_parts));
 
-	Assert((*outer_parts == NIL || *inner_parts != NIL) &&
-		   (*inner_parts == NIL || *outer_parts != NIL));
-
 	return merged_bounds;
 }
 
@@ -3839,9 +3833,10 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel,
 		return NULL;
 
 	/* Use maps to match partition from the joining relations. */
-	generate_matching_part_pairs(outer_maps, inner_maps,
+	generate_matching_part_pairs(outer_rel, inner_rel,
+								 outer_maps, inner_maps,
 								 outer_nparts, inner_nparts,
-								 jointype, next_index,
+								 next_index,
 								 outer_parts, inner_parts);
 
 	/* Craft a PartitionBoundInfo to return. */
@@ -4099,9 +4094,10 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation,
 		return NULL;
 
 	/* Use maps to match partition from the joining relations. */
-	generate_matching_part_pairs(outer_maps, inner_maps,
+	generate_matching_part_pairs(outer_rel, inner_rel,
+								 outer_maps, inner_maps,
 								 outer_nparts, inner_nparts,
-								 jointype, next_index,
+								 next_index,
 								 outer_parts, inner_parts);
 
 	/* Craft a PartitionBoundInfo to return. */
@@ -4122,61 +4118,6 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation,
 	return merged_bounds;
 }
 
-/*
- * partition_bounds_merge()'s arm for hash partitioned tables.
- *
- * If the given two hash bounds are same, the function returns the first one
- * without any change, alongwith the lists of matching partitions. Otherwise it
- * returns NULL.
- *
- * We could try merging the bounds when both the bounds have same greatest
- * modulii. But there seems to be hardly any requirement for the same.
- */
-static PartitionBoundInfo
-partition_hash_bounds_merge(int partnatts,
-							int16 *parttyplen, bool *parttypbyval,
-							RelOptInfo *outer_rel, RelOptInfo *inner_rel,
-							List **outer_parts, List **inner_parts)
-{
-	PartitionBoundInfo outer_bi = outer_rel->boundinfo,
-					   inner_bi = inner_rel->boundinfo;
-	int			cnt;
-
-	Assert(*outer_parts == NIL);
-	Assert(*inner_parts == NIL);
-
-	Assert(outer_bi->strategy == inner_bi->strategy &&
-		   outer_bi->strategy == PARTITION_STRATEGY_HASH);
-
-	/*
-	 * Hash partitioned table does not have explicit NULL accepting partition
-	 * and also does not have a default partition.
-	 */
-	Assert(!partition_bound_has_default(outer_bi) &&
-		   !partition_bound_has_default(inner_bi));
-	Assert(!partition_bound_accepts_nulls(outer_bi) &&
-		   !partition_bound_accepts_nulls(inner_bi));
-
-	if (outer_rel->nparts != inner_rel->nparts)
-		return NULL;
-
-	if (!partition_bounds_equal(partnatts, parttyplen, parttypbyval,
-								outer_bi, inner_bi))
-		return NULL;
-
-	 /*
-	  * Cook up list of matching partitions. Since bounds are exactly same the
-	  * partitions at the same position from both the relations match.
-	  */
-	for (cnt = 0; cnt < outer_rel->nparts; cnt++)
-	{
-		*outer_parts = lappend_int(*outer_parts, cnt);
-		*inner_parts = lappend_int(*inner_parts, cnt);
-	}
-
-	return outer_bi;
-}
-
 /*
  * map_and_merge_partitions
  *
@@ -4312,12 +4253,11 @@ map_and_merge_partitions(PartitionMap *partmaps1, PartitionMap *partmaps2,
  * set to NIL.
  */
 static void
-generate_matching_part_pairs(PartitionMap *partmaps1, PartitionMap *partmaps2,
-							 int nparts1, int nparts2,
-							 JoinType jointype, int nparts,
+generate_matching_part_pairs(RelOptInfo *rel1, RelOptInfo *rel2,
+							 PartitionMap *partmaps1, PartitionMap *partmaps2,
+							 int nparts1, int nparts2, int nparts,
 							 List **matched_parts1, List **matched_parts2)
 {
-	bool		merged = true;
 	int		   *matching1,
 			   *matching2;
 	int 		i;
@@ -4358,16 +4298,6 @@ generate_matching_part_pairs(PartitionMap *partmaps1, PartitionMap *partmaps2,
 		}
 	}
 
-	/*
-	 * If we have a partition missing on an inner side, we need to add a dummy
-	 * relation which joins with the outer partition. If the inner relation
-	 * happens to be a base relation, it will require adding a dummy child
-	 * base relation during join processing. Right now, we freeze the base
-	 * relation arrays like PlannerInfo::simple_rte_array after planning for
-	 * base relations. Adding a new (dummy) base relation would require some
-	 * changes to that. So, right now, we do not implement partition-wise join
-	 * in such cases.
-	 */
 	for (i = 0; i < nparts; i++)
 	{
 		int			part1 = matching1[i];
@@ -4376,52 +4306,14 @@ generate_matching_part_pairs(PartitionMap *partmaps1, PartitionMap *partmaps2,
 		/* At least one of the partitions should exist. */
 		Assert(part1 >= 0 || part2 >= 0);
 
-		switch (jointype)
-		{
-			case JOIN_INNER:
-			case JOIN_SEMI:
-
-				/*
-				 * An inner or semi join can not return any row when the
-				 * matching partition on either side is missing. We should
-				 * have eliminated all such cases while merging the bounds.
-				 */
-				Assert(part1 >= 0 && part2 >= 0);
-				break;
-
-			case JOIN_LEFT:
-			case JOIN_ANTI:
-				Assert(part1 >= 0);
-				if (part2 < 0)
-					merged = false;
-				break;
-
-			case JOIN_FULL:
-				if (part1 < 0 || part2 < 0)
-					merged = false;
-				break;
-
-			default:
-				elog(ERROR, "unrecognized join type: %d", (int) jointype);
-		}
-
-		if (!merged)
-			break;
-
-		*matched_parts1 = lappend_int(*matched_parts1, part1);
-		*matched_parts2 = lappend_int(*matched_parts2, part2);
+		*matched_parts1 = lappend(*matched_parts1,
+								  part1 >= 0 ? rel1->part_rels[part1] : NULL);
+		*matched_parts2 = lappend(*matched_parts2,
+								  part2 >= 0 ? rel2->part_rels[part2] : NULL);
 	}
 
 	pfree(matching1);
 	pfree(matching2);
-
-	if (!merged)
-	{
-		list_free(*matched_parts1);
-		list_free(*matched_parts2);
-		*matched_parts1 = NIL;
-		*matched_parts2 = NIL;
-	}
 }
 
 static PartitionBoundInfo
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 23a06d718e..e06eb9aaae 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -577,8 +577,10 @@ typedef struct PartitionSchemeData *PartitionScheme;
  *		part_scheme - Partitioning scheme of the relation
  *		nparts - Number of partitions
  *		boundinfo - Partition bounds
+ *		merged - true if partition bounds are merged ones
  *		partition_qual - Partition constraint if not the root
  *		part_rels - RelOptInfos for each partition
+ *		all_partrels - Relids set of all partition relids
  *		partexprs, nullable_partexprs - Partition key expressions
  *		partitioned_child_rels - RT indexes of unpruned partitions of
  *								 this relation that are partitioned tables
@@ -718,9 +720,12 @@ typedef struct RelOptInfo
 	PartitionScheme part_scheme;	/* Partitioning scheme. */
 	int			nparts;			/* number of partitions */
 	struct PartitionBoundInfoData *boundinfo;	/* Partition bounds */
+	bool		merged;			/* true if partition bounds were created by
+								 * partition_bounds_merge() */
 	List	   *partition_qual; /* partition constraint */
 	struct RelOptInfo **part_rels;	/* Array of RelOptInfos of partitions,
 									 * stored in the same order of bounds */
+	Relids		all_partrels;	/* Relids set of all partition relids */
 	List	  **partexprs;		/* Non-nullable partition key expressions. */
 	List	  **nullable_partexprs; /* Nullable partition key expressions. */
 	List	   *partitioned_child_rels; /* List of RT indexes. */
