diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e6ce8e2110..a61b1741ec 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2281,6 +2281,7 @@ _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_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..6607fae57b 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,8 +1360,6 @@ 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;
@@ -1369,7 +1370,7 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 	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 +1395,51 @@ 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 haven't created the partition bounds for the join relation yet,
+	 * create it along with the the list of matching partitions to be joined;
+	 * else generate the list of such partitions from the partitioning info
+	 * for the join relation 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));
+		PartitionBoundInfo boundinfo;
+		int			nparts;
+
+		Assert(joinrel->boundinfo == NULL);
+		boundinfo = partition_bounds_merge(joinrel->part_scheme->partnatts,
+										   joinrel->part_scheme->parttyplen,
+										   joinrel->part_scheme->parttypbyval,
+										   joinrel->part_scheme->partsupfunc,
+										   joinrel->part_scheme->partcollation,
+										   rel1, rel2,
+										   parent_sjinfo->jointype,
+										   &parts1, &parts2);
+		if (boundinfo == NULL)
+		{
+			joinrel->nparts = 0;
+			return;
+		}
+		joinrel->boundinfo = boundinfo;
+
+		Assert(list_length(parts1) == list_length(parts2));
+		nparts = list_length(parts1);
+		Assert(nparts > 0);
+		joinrel->nparts = nparts;
+		Assert(joinrel->part_rels == NULL);
 		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));
-		/*
-		 * Every pair of joining relations should result in the same number
-		 * of child-joins.
-		 */
-		Assert(joinrel->nparts == list_length(parts1));
-		Assert(joinrel->nparts == list_length(parts2));
+		Assert(joinrel->nparts > 0);
+		Assert(joinrel->boundinfo);
 		Assert(joinrel->part_rels);
+
+		get_matching_part_pairs(root, joinrel, rel1, rel2, &parts1, &parts2);
+		Assert(list_length(parts1) == joinrel->nparts);
+		Assert(list_length(parts2) == joinrel->nparts);
 	}
 
 	/*
@@ -1449,12 +1450,12 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 	cnt_parts = -1;
 	forboth(lc1, parts1, lc2, parts2)
 	{
-		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 = (RelOptInfo *) lfirst(lc1);
+		RelOptInfo *child_rel2 = (RelOptInfo *) lfirst(lc2);
+		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,11 +1463,6 @@ 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++;
 
 		/*
@@ -1566,25 +1562,19 @@ 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));
+	Assert(cnt_parts == joinrel->nparts - 1);
 }
 
 /*
@@ -1793,3 +1783,70 @@ match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel, bool strict_op)
 
 	return -1;
 }
+
+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 the segment of the join is empty, it means that the segment was
+		 * ignored in the child-join creation step in try_partitionwise_join()
+		 * since it would not contribute to the join result, due to one or
+		 * both inputs being empty; add NULL for each list so that it will be
+		 * ignored again in that step.
+		 */
+		if (!child_joinrel)
+		{
+			*parts1 = lappend(*parts1, NULL);
+			*parts2 = lappend(*parts2, NULL);
+			continue;
+		}
+
+		child_relids1 = bms_intersect(child_joinrel->relids,
+									  rel1->all_partrels);
+		Assert(bms_num_members(child_relids1) == bms_num_members(rel1->relids));
+		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);
+		/* We should have a rel for child_relids1 */
+		Assert(child_rel1);
+
+		child_relids2 = bms_intersect(child_joinrel->relids,
+									  rel2->all_partrels);
+		Assert(bms_num_members(child_relids2) == bms_num_members(rel2->relids));
+		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);
+		/* We should have a rel for child_relids2 */
+		Assert(child_rel2);
+
+		*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 1e883bc994..f9ac8ab82a 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -237,10 +237,11 @@ 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->partition_qual = NIL;
 	rel->part_rels = NULL;
+	rel->all_partrels = NULL;
 	rel->partexprs = NULL;
 	rel->nullable_partexprs = NULL;
 	rel->partitioned_child_rels = NIL;
@@ -650,10 +651,11 @@ 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->partition_qual = NIL;
 	joinrel->part_rels = NULL;
+	joinrel->all_partrels = NULL;
 	joinrel->partexprs = NULL;
 	joinrel->nullable_partexprs = NULL;
 	joinrel->partitioned_child_rels = NIL;
@@ -826,10 +828,11 @@ 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->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 88afc05a60..a5b3072c0f 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -128,11 +128,14 @@ 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,
@@ -3839,9 +3842,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 +4103,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. */
@@ -4170,8 +4175,8 @@ partition_hash_bounds_merge(int partnatts,
 	  */
 	for (cnt = 0; cnt < outer_rel->nparts; cnt++)
 	{
-		*outer_parts = lappend_int(*outer_parts, cnt);
-		*inner_parts = lappend_int(*inner_parts, cnt);
+		*outer_parts = lappend(*outer_parts, outer_rel->part_rels[cnt]);
+		*inner_parts = lappend(*inner_parts, inner_rel->part_rels[cnt]);
 	}
 
 	return outer_bi;
@@ -4312,12 +4317,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 +4362,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 +4370,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 e3c579ee44..1d2dc68201 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -581,6 +581,7 @@ typedef struct PartitionSchemeData *PartitionScheme;
  *		boundinfo - Partition bounds
  *		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
@@ -723,6 +724,7 @@ typedef struct RelOptInfo
 	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. */
