diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 3b2008e0f1..d35c9817cd 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -72,8 +72,13 @@ typedef struct PartitionRangeBound
 
 typedef struct PartitionMap
 {
-	int from;
-	int to;
+	int			nparts;			/* number of partitions */
+	int		   *merged_indexes;	/* indexes of merged partitions */
+	bool	   *merged;			/* flags to indicate whether partitions are
+								 * merged with non-dummy partitions */
+	bool		did_remapping;	/* did we remap partitions? */
+	int		   *old_indexes;	/* old indexes of merged partitions if
+								 * did_remapping */
 } PartitionMap;
 
 static int32 qsort_partition_hbound_cmp(const void *a, const void *b);
@@ -115,20 +120,49 @@ static void get_range_key_properties(PartitionKey key, int keynum,
 									 Expr **keyCol,
 									 Const **lower_val, Const **upper_val);
 static List *get_range_nulltest(PartitionKey key);
-static PartitionBoundInfo partition_range_bounds_merge(
+static PartitionBoundInfo partition_range_bounds_merge(int partnatts, FmgrInfo *partsupfuncs,
+							 Oid *partcollations,
 							 RelOptInfo *outer_rel, RelOptInfo *inner_rel,
-							 List **outer_parts, List **inner_parts,
-							 JoinType jointype, int partnatts,
-							 FmgrInfo *supfuncs, Oid *collations);
+							 JoinType jointype,
+							 List **outer_parts, List **inner_parts);
 static PartitionBoundInfo partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *collations,
 							RelOptInfo *outer_rel, RelOptInfo *inner_rel,
-							List **outer_parts, List **inner_parts,
-							JoinType jointype);
+							JoinType jointype,
+							List **outer_parts, List **inner_parts);
+static void init_partition_map(RelOptInfo *rel, PartitionMap *map);
+static void free_partition_map(PartitionMap *map);
+static int map_and_merge_partitions(PartitionMap *outer_map, PartitionMap *inner_map,
+						 int outer_part, int inner_part, int *next_index);
+static int merge_partition_with_dummy(PartitionMap *map, int index,
+						   int *next_index);
+static bool process_outer_partition(PartitionMap *outer_map,
+						PartitionMap *inner_map,
+						bool outer_has_default,
+						bool inner_has_default,
+						int outer_index,
+						int inner_default,
+						JoinType jointype,
+						char strategy,
+						int *next_index,
+						int *default_index,
+						int *merged_index);
+static bool process_inner_partition(PartitionMap *outer_map,
+						PartitionMap *inner_map,
+						bool outer_has_default,
+						bool inner_has_default,
+						int inner_index,
+						int outer_default,
+						JoinType jointype,
+						char strategy,
+						int *next_index,
+						int *default_index,
+						int *merged_index);
+static void fix_merged_indexes(PartitionMap *outer_map, PartitionMap *inner_map,
+				   int nmerged, List *merged_indexes);
 static void generate_matching_part_pairs(RelOptInfo *rel1,
 							 RelOptInfo *rel2,
-							 PartitionMap *partmaps1,
-							 PartitionMap *partmaps2,
-							 int nparts1, int nparts2,
+							 PartitionMap *map1,
+							 PartitionMap *map2,
 							 int nparts,
 							 List **matched_parts1,
 							 List **matched_parts2);
@@ -136,9 +170,6 @@ static PartitionBoundInfo build_merged_partition_bounds(char strategy,
 							  List *merged_datums, List *merged_indexes,
 							  List *merged_contents, int null_index,
 							  int default_index);
-static int map_and_merge_partitions(PartitionMap *outer_maps,
-										PartitionMap *inner_maps,
-										int index1, int index2, int *next_index);
 static int32 partition_range_bound_cmp(int partnatts, FmgrInfo *partsupfunc,
 						  Oid *collations, PartitionRangeBound *bound1,
 						  PartitionRangeBound *bound2);
@@ -153,19 +184,21 @@ static bool partition_range_merge_next_lb(int partnatts, FmgrInfo *supfuncs,
 							  PartitionRangeDatumKind *next_lb_kind,
 							  List **merged_datums, List **merged_kinds,
 							  List **merged_indexes);
-static bool merge_default_partitions(PartitionBoundInfo outer_bi,
-						 PartitionBoundInfo inner_bi,
-						 PartitionMap *outer_maps,
-						 PartitionMap *inner_maps,
+static bool merge_default_partitions(PartitionMap *outer_map,
+						 PartitionMap *inner_map,
+						 bool outer_has_default,
+						 int outer_default,
+						 bool inner_has_default,
+						 int inner_default,
 						 JoinType jointype,
-						 int *next_index, int *default_index);
-static bool merge_null_partitions(PartitionBoundInfo outer_bi,
-								  PartitionBoundInfo inner_bi,
-								  PartitionMap *outer_maps,
-								  PartitionMap *inner_maps,
-								  JoinType jointype,
-								  int *next_index, int *null_index,
-								  int *default_index);
+						 int *next_index,
+						 int *default_index);
+static bool merge_null_partitions(PartitionBoundInfo outer_bi, PartitionBoundInfo inner_bi,
+					  PartitionMap *outer_map, PartitionMap *inner_map,
+					  bool outer_has_default, bool inner_has_default,
+					  bool outer_has_null, bool inner_has_null,
+					  JoinType jointype, int *next_index,
+					  int *default_index, int *null_index);
 
 /*
  * get_qual_from_partbound
@@ -3081,8 +3114,8 @@ partition_bounds_merge(int partnatts,
 					   int16 *parttyplen, bool *parttypbyval,
 					   FmgrInfo *partsupfunc, Oid *partcollation,
 					   RelOptInfo *outer_rel, RelOptInfo *inner_rel,
-					   JoinType jointype,
-					   List **outer_parts, List **inner_parts)
+					   JoinType jointype, List **outer_parts,
+					   List **inner_parts)
 {
 	PartitionBoundInfo 	merged_bounds;
 	PartitionBoundInfo 	outer_binfo = outer_rel->boundinfo,
@@ -3110,17 +3143,22 @@ partition_bounds_merge(int partnatts,
 		case PARTITION_STRATEGY_LIST:
 			merged_bounds = partition_list_bounds_merge(partsupfunc,
 														partcollation,
-														outer_rel, inner_rel,
-														outer_parts, inner_parts,
-														jointype);
+														outer_rel,
+														inner_rel,
+														jointype,
+														outer_parts,
+														inner_parts);
 			break;
 
 		case PARTITION_STRATEGY_RANGE:
-			merged_bounds = partition_range_bounds_merge(outer_rel, inner_rel,
-														 outer_parts, inner_parts,
-														 jointype, partnatts,
+			merged_bounds = partition_range_bounds_merge(partnatts,
 														 partsupfunc,
-														 partcollation);
+														 partcollation,
+														 outer_rel,
+														 inner_rel,
+														 jointype,
+														 outer_parts,
+														 inner_parts);
 			break;
 
 		default:
@@ -3335,8 +3373,6 @@ partition_range_merge(int partnatts, FmgrInfo *partsupfuncs,
 		default:
 			elog(ERROR, "unexpected join type %d", jointype);
 	}
-
-	return;
 }
 
 /*
@@ -3396,157 +3432,34 @@ partition_range_merge_next_lb(int partnatts, FmgrInfo *partsupfuncs,
 	return true;
 }
 
-/*
- * handle_missing_partition
- *
- * If a range appears in one of the joining relations but not the other, a row
- * in the corresponding partition will not have any join partner in the other
- * relation, unless the other relation has a default partition. If a given list
- * value is present in one joining relation but not the other, the default
- * partition on the other side may contain that value.
- *
- * In both these cases, such an extra partition forms a joining pair with the
- * default partition, if any,  on the other side.
- *
- * If the default partition happens to be on the outer side of the join, the
- * resultant partition will act as the default partition of the join relation.
- * Otherwise the resultant partition will be associated with the range.
- *
- * When the default partition is not present in the other relation, the rows in
- * the extra partition will be included in the bounds of the join result, if it
- * appears on the outer side of the join, since all rows from the outer side
- * are included in the join result.
- *
- * This function handles all these cases.
- *
- * maps_with_missing and missing_side_default are the partition maps (See
- * partition_range/list_bounds_merge() for details) and the index of default
- * partition respectively corresponding the side with missing partition.
- *
- * maps_with_extra and extra_part are the partition maps (See
- * partition_range/list_bounds_merge() for details) and the index of extra
- * partition respectively corresponding to the side with the extra partition.
- *
- * It returns true if the matching succeeds, otherwise returns false.
- */
-static bool
-handle_missing_partition(PartitionMap *maps_with_missing,
-						 PartitionMap *maps_with_extra,
-						 int missing_side_default,
-						 int extra_part,
-						 bool missing_side_outer,
-						 bool missing_side_inner,
-						 int *next_index, int *default_index,
-						 int *merged_index)
-{
-	bool missing_has_default = (missing_side_default != -1);
-
-	if (missing_has_default)
-	{
-		*merged_index = map_and_merge_partitions(maps_with_missing,
-												 maps_with_extra,
-												 missing_side_default,
-												 extra_part,
-												 next_index);
-		if (*merged_index < 0)
-			return false;
-
-		if (missing_side_outer)
-		{
-			/*
-			 * Default partition on the outer side forms the default
-			 * partition of the join result.
-			 */
-			if (*default_index < 0)
-				*default_index = *merged_index;
-			else if(*default_index != *merged_index)
-			{
-				/*
-				 * Ended up with default partition on the outer side
-				 * being joined with multiple partitions on the inner
-				 * side. We don't support this case.
-				 */
-				return false;
-			}
-
-			/*
-			 * Since the merged partition acts as a default partition, it
-			 * doesn't need a separate index.
-			 */
-			*merged_index = -1;
-		}
-	}
-	else if (missing_side_inner)
-	{
-		/*
-		 * If this partition has already been mapped (say because we
-		 * found an overlapping range earlier), we know where does it
-		 * fit in the join result. Nothing to do in that case. Else
-		 * create a new merged partition.
-		 */
-		PartitionMap *extra_map = &maps_with_extra[extra_part];
-		if (extra_map->to < 0)
-		{
-			extra_map->to = *next_index;
-			*next_index = *next_index + 1;
-			*merged_index = extra_map->to;
-		}
-	}
-	else
-		*merged_index = -1;
-
-	return true;
-}
-
-static PartitionMap*
-init_partition_map(RelOptInfo *rel)
-{
-	int i, nparts = rel->nparts;
-	PartitionMap *map;
-
-	map = (PartitionMap *) palloc(sizeof(PartitionMap) * nparts);
-
-	for (i = 0; i < nparts; i++)
-	{
-		map[i].from = -1;
-		map[i].to = -1;
-	}
-
-	return map;
-}
-
 /*
  * partition_range_bounds_merge
  *
  * partition_bounds_merge()'s arm for range partitioned tables.
  */
 static PartitionBoundInfo
-partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel,
-							 List **outer_parts, List **inner_parts,
-							 JoinType jointype, int partnatts,
-							 FmgrInfo *partsupfuncs, Oid *partcollations)
-
+partition_range_bounds_merge(int partnatts, FmgrInfo *partsupfuncs,
+							 Oid *partcollations,
+							 RelOptInfo *outer_rel, RelOptInfo *inner_rel,
+							 JoinType jointype,
+							 List **outer_parts, List **inner_parts)
 {
-	PartitionMap *outer_maps = NULL;
-	PartitionMap *inner_maps = NULL;
-	int			outer_part = 0;
-	int			inner_part = 0;
 	PartitionBoundInfo merged_bounds = NULL;
-	int			outer_lb_index;
-	int			inner_lb_index;
-	int			next_index;
+	PartitionBoundInfo outer_bi = outer_rel->boundinfo;
+	PartitionBoundInfo inner_bi = inner_rel->boundinfo;
+	bool		outer_has_default = partition_bound_has_default(outer_bi);
+	int			outer_default = outer_bi->default_index;
+	bool		inner_has_default = partition_bound_has_default(inner_bi);
+	int			inner_default = inner_bi->default_index;
+	PartitionMap outer_map;
+	PartitionMap inner_map;
+	int			next_index = 0;
 	int			default_index = -1;
 	List	   *merged_datums = NIL;
-	List	   *merged_indexes = NIL;
 	List	   *merged_kinds = NIL;
-	PartitionBoundInfo outer_bi = outer_rel->boundinfo,
-					   inner_bi = inner_rel->boundinfo;
-	int			inner_default = inner_bi->default_index;
-	int			outer_default = outer_bi->default_index;
-	bool		inner_has_default = partition_bound_has_default(inner_bi);
-	bool		outer_has_default = partition_bound_has_default(outer_bi);
-	int 			   outer_nparts = outer_rel->nparts,
-					   inner_nparts = inner_rel->nparts;
+	List	   *merged_indexes = NIL;
+	int			outer_lb_index;
+	int			inner_lb_index;
 
 	Assert(outer_bi->strategy == inner_bi->strategy &&
 		   outer_bi->strategy == PARTITION_STRATEGY_RANGE);
@@ -3554,8 +3467,8 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel,
 	Assert(*outer_parts == NIL);
 	Assert(*inner_parts == NIL);
 
-	outer_maps = init_partition_map(outer_rel);
-	inner_maps = init_partition_map(inner_rel);
+	init_partition_map(outer_rel, &outer_map);
+	init_partition_map(inner_rel, &inner_map);
 
 	/*
 	 * Merge the ranges (partitions) from both sides. Every iteration compares
@@ -3566,40 +3479,30 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel,
 	 * lb_index, for inner or outer side, keeps track of the index of lower bound
 	 * datum in PartitionBoundInfo::datums of that side.
 	 */
-	outer_lb_index = 0;
-	inner_lb_index = 0;
-	next_index = 0;
+	outer_lb_index = inner_lb_index = 0;
 	while (outer_lb_index < outer_bi->ndatums ||
 		   inner_lb_index < inner_bi->ndatums)
 	{
-		PartitionRangeBound outer_lb, outer_ub,
-							inner_lb, inner_ub,
-							*merged_lb = NULL,
-							*merged_ub = NULL;
-
+		PartitionRangeBound *merged_lb = NULL;
+		PartitionRangeBound *merged_ub = NULL;
 		int			merged_index = -1;
+		PartitionRangeBound outer_lb;
+		PartitionRangeBound outer_ub;
+		PartitionRangeBound inner_lb;
+		PartitionRangeBound inner_ub;
+		int			outer_part = -1;
+		int			inner_part = -1;
 		bool		overlap;
-		bool		finished_outer = false;
-		bool		finished_inner = false;
-
-		/* Result of bounds comparison per partition_rbound_cmp(). */
-		int			ub_cmpval;	/* Upper bounds comparison result. */
-		int			lb_cmpval;	/* Lower bounds comparison result. */
+		int			ub_cmpval;
+		int			lb_cmpval;
 
 		/* Get the range bounds of the next pair of partitions. */
 		if (outer_lb_index < outer_bi->ndatums)
 			outer_part = partition_get_range_bounds(outer_bi, outer_lb_index,
-												&outer_lb, &outer_ub);
-		else
-			finished_outer = true;
-
+													&outer_lb, &outer_ub);
 		if (inner_lb_index < inner_bi->ndatums)
 			inner_part = partition_get_range_bounds(inner_bi, inner_lb_index,
-												&inner_lb, &inner_ub);
-		else
-			finished_inner = true;
-
-		Assert(!finished_outer || !finished_inner);
+													&inner_lb, &inner_ub);
 
 		/*
 		 * We run this loop till both the sides finish. This allows to avoid
@@ -3611,13 +3514,13 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel,
 		 * side. That way we advance the partitions on that side till all of
 		 * them are  exhausted.
 		 */
-		if (finished_outer)
+		if (outer_lb_index >= outer_bi->ndatums)
 		{
 			overlap = false;
 			ub_cmpval = 1;
 			lb_cmpval = 1;
 		}
-		else if (finished_inner)
+		else if (inner_lb_index >= inner_bi->ndatums)
 		{
 			overlap = false;
 			ub_cmpval = -1;
@@ -3641,7 +3544,7 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel,
 								  jointype, &outer_lb, &outer_ub, &inner_lb,
 								  &inner_ub, &merged_lb, &merged_ub);
 
-			merged_index = map_and_merge_partitions(outer_maps, inner_maps,
+			merged_index = map_and_merge_partitions(&outer_map, &inner_map,
 													outer_part, inner_part,
 													&next_index);
 
@@ -3680,6 +3583,8 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel,
 			Assert(overlap);
 
 			/* Move to the next pair of partitions. */
+			Assert(outer_lb_index < outer_bi->ndatums);
+			Assert(inner_lb_index < inner_bi->ndatums);
 			outer_lb_index = partition_range_get_next_lb_index(outer_bi,
 															   outer_lb_index);
 			inner_lb_index = partition_range_get_next_lb_index(inner_bi,
@@ -3695,37 +3600,28 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel,
 			}
 			else
 			{
-				/* A range missing from the inner side. */
-				bool		missing_side_outer;
-				bool		missing_side_inner;
+				if (inner_has_default || IS_OUTER_JOIN(jointype))
+				{
+					if (!process_outer_partition(&outer_map,
+												 &inner_map,
+												 outer_has_default,
+												 inner_has_default,
+												 outer_part,
+												 inner_default,
+												 jointype,
+												 outer_bi->strategy,
+												 &next_index,
+												 &default_index,
+												 &merged_index))
+						return NULL;
+				}
 
 				merged_lb = &outer_lb;
 				merged_ub = &outer_ub;
-
-				/*
-				 * For a FULL join, inner relation acts as both OUTER and INNER
-				 * relation.  For LEFT and ANTI join the inner relation acts as
-				 * INNER relation. For INNER and SEMI join OUTER and INNER
-				 * differentiation is immaterial.
-				 */
-				missing_side_inner = (jointype == JOIN_FULL ||
-									  jointype == JOIN_LEFT ||
-									  jointype == JOIN_ANTI);
-				missing_side_outer = (jointype == JOIN_FULL);
-				if (!handle_missing_partition(inner_maps,
-											  outer_maps,
-											  inner_default,
-											  outer_part,
-											  missing_side_outer,
-											  missing_side_inner,
-											  &next_index,
-											  &default_index,
-											  &merged_index))
-					return NULL;
 			}
 
 			/* Move to the next partition on the outer side. */
-			Assert(!finished_outer);
+			Assert(outer_lb_index < outer_bi->ndatums);
 			outer_lb_index = partition_range_get_next_lb_index(outer_bi,
 															   outer_lb_index);
 		}
@@ -3740,38 +3636,28 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel,
 			}
 			else
 			{
-				/* A range missing from the outer side. */
-				bool		missing_side_outer;
-				bool		missing_side_inner;
+				if (outer_has_default || jointype == JOIN_FULL)
+				{
+					if (!process_inner_partition(&outer_map,
+												 &inner_map,
+												 outer_has_default,
+												 inner_has_default,
+												 inner_part,
+												 outer_default,
+												 jointype,
+												 outer_bi->strategy,
+												 &next_index,
+												 &default_index,
+												 &merged_index))
+						return NULL;
+				}
 
 				merged_lb = &inner_lb;
 				merged_ub = &inner_ub;
-
-				/*
-				 * For a FULL join, outer relation acts as both OUTER and INNER
-				 * relation.  For LEFT and ANTI join the outer relation acts as
-				 * OUTER relation. For INNER and SEMI join OUTER and INNER
-				 * differentiation is immaterial.
-				 */
-				missing_side_outer = (jointype == JOIN_FULL ||
-									  jointype == JOIN_LEFT ||
-									  jointype == JOIN_ANTI);
-				missing_side_inner = (jointype == JOIN_FULL);
-
-				if (!handle_missing_partition(outer_maps,
-											  inner_maps,
-											  outer_default,
-											  inner_part,
-											  missing_side_outer,
-											  missing_side_inner,
-											  &next_index,
-											  &default_index,
-											  &merged_index))
-					return NULL;
 			}
 
 			/* Move to the next partition on the inner side. */
-			Assert (!finished_inner);
+			Assert(inner_lb_index < inner_bi->ndatums);
 			inner_lb_index = partition_range_get_next_lb_index(inner_bi,
 															   inner_lb_index);
 		}
@@ -3802,16 +3688,28 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel,
 		merged_indexes = lappend_int(merged_indexes, merged_index);
 	}
 
-	if (!merge_default_partitions(outer_bi, inner_bi,
-										  outer_maps, inner_maps,
-										  jointype, &next_index,
-										  &default_index))
-		return NULL;
+	/* Merge default partitions if any. */
+	if (outer_has_default || inner_has_default)
+	{
+		if (!merge_default_partitions(&outer_map,
+									  &inner_map,
+									  outer_has_default,
+									  outer_default,
+									  inner_has_default,
+									  inner_default,
+									  jointype,
+									  &next_index,
+									  &default_index))
+			return NULL;
+	}
+	else
+		Assert(default_index == -1);
+
+	Assert(!outer_map.did_remapping && !inner_map.did_remapping);
 
 	/* Use maps to match partition from the joining relations. */
 	generate_matching_part_pairs(outer_rel, inner_rel,
-								 outer_maps, inner_maps,
-								 outer_nparts, inner_nparts,
+								 &outer_map, &inner_map,
 								 next_index,
 								 outer_parts, inner_parts);
 
@@ -3828,6 +3726,8 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel,
 	}
 
 	/* Free any memory we used in this function. */
+	free_partition_map(&outer_map);
+	free_partition_map(&inner_map);
 	list_free(merged_datums);
 	list_free(merged_indexes);
 	list_free(merged_kinds);
@@ -3844,27 +3744,27 @@ partition_range_bounds_merge(RelOptInfo *outer_rel, RelOptInfo *inner_rel,
 static PartitionBoundInfo
 partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation,
 							RelOptInfo *outer_rel, RelOptInfo *inner_rel,
-							List **outer_parts, List **inner_parts,
-							JoinType jointype)
+							JoinType jointype,
+							List **outer_parts, List **inner_parts)
 {
-	PartitionMap *outer_maps = NULL;
-	PartitionMap *inner_maps = NULL;
-	int			cnto;
-	int			cnti;
-	List	   *merged_datums = NIL;
-	List	   *merged_indexes = NIL;
+	PartitionBoundInfo merged_bounds = NULL;
+	PartitionBoundInfo outer_bi = outer_rel->boundinfo;
+	PartitionBoundInfo inner_bi = inner_rel->boundinfo;
+	bool		outer_has_default = partition_bound_has_default(outer_bi);
+	int			outer_default = outer_bi->default_index;
+	bool		inner_has_default = partition_bound_has_default(inner_bi);
+	int			inner_default = inner_bi->default_index;
+	bool		outer_has_null = partition_bound_accepts_nulls(outer_bi);
+	bool		inner_has_null = partition_bound_accepts_nulls(inner_bi);
+	PartitionMap outer_map;
+	PartitionMap inner_map;
 	int			next_index = 0;
 	int			null_index = -1;
 	int			default_index = -1;
-	PartitionBoundInfo merged_bounds = NULL;
-	PartitionBoundInfo outer_bi = outer_rel->boundinfo,
-					   inner_bi = inner_rel->boundinfo;
-	int			      *outer_indexes = outer_bi->indexes;
-	int			      *inner_indexes = inner_bi->indexes;
-	int				   outer_default = outer_bi->default_index;
-	int				   inner_default = inner_bi->default_index;
-	int 			   outer_nparts = outer_rel->nparts,
-					   inner_nparts = inner_rel->nparts;
+	List	   *merged_datums = NIL;
+	List	   *merged_indexes = NIL;
+	int			cnto;
+	int			cnti;
 
 	Assert(*outer_parts == NIL);
 	Assert(*inner_parts == NIL);
@@ -3875,8 +3775,8 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation,
 	/* List partitions do not require unbounded ranges. */
 	Assert(!outer_bi->kind && !inner_bi->kind);
 
-	outer_maps = init_partition_map(outer_rel);
-	inner_maps = init_partition_map(inner_rel);
+	init_partition_map(outer_rel, &outer_map);
+	init_partition_map(inner_rel, &inner_map);
 
 	/*
 	 * Merge the list value datums from both sides. Every iteration compares a
@@ -3888,15 +3788,15 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation,
 	cnto = cnti = 0;
 	while (cnto < outer_bi->ndatums || cnti < inner_bi->ndatums)
 	{
+		Datum	   *merged_datum = NULL;
+		int			merged_index = -1;
 		Datum	   *odatums;
 		Datum	   *idatums;
-		int			o_index;
-		int			i_index;
 		int			cmpval;
-		int			merged_index = -1;
-		Datum	   *merged_datum;
-		bool		finished_inner;
-		bool		finished_outer;
+
+		/* Get the list datums of the next pair of partitions. */
+		odatums = cnto < outer_bi->ndatums ? outer_bi->datums[cnto] : NULL;
+		idatums = cnti < inner_bi->ndatums ? inner_bi->datums[cnti] : NULL;
 
 		/*
 		 * We run this loop till both the sides finish. This allows to avoid
@@ -3908,51 +3808,23 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation,
 		 * its datums are exhausted.
 		 */
 		if (cnto >= outer_bi->ndatums)
-		{
-			finished_outer = true;
-			odatums = NULL;
-			o_index = -1;
-		}
-		else
-		{
-			finished_outer = false;
-			odatums = outer_bi->datums[cnto];
-			o_index = outer_indexes[cnto];
-		}
-
-		if (cnti >= inner_bi->ndatums)
-		{
-			finished_inner = true;
-			idatums = NULL;
-			i_index = -1;
-		}
-		else
-		{
-			finished_inner = false;
-			idatums = inner_bi->datums[cnti];
-			i_index = inner_indexes[cnti];
-		}
-
-		/* If we exhausted both the sides, we won't enter the loop. */
-		Assert(!finished_inner || !finished_outer);
-
-		if (finished_outer)
 			cmpval = 1;
-		else if (finished_inner)
+		else if (cnti >= inner_bi->ndatums)
 			cmpval = -1;
 		else
 		{
-			/* Every list datum should map to a valid partition index. */
-			Assert(o_index >= 0 && i_index >= 0 &&
-				   odatums != NULL && idatums != NULL);
-
+			Assert(odatums != NULL && idatums != NULL);
 			cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
 													 partcollation[0],
-													 odatums[0], idatums[0]));
+													 odatums[0],
+													 idatums[0]));
 		}
 
 		if (cmpval == 0)
 		{
+			int			o_index = outer_bi->indexes[cnto];
+			int			i_index = inner_bi->indexes[cnti];
+
 			/*
 			 * Datums match. Rows on either side with these datums as partition
 			 * key value will join and will be part of the partition of the
@@ -3962,7 +3834,8 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation,
 			 * partition containing it.
 			 */
 			merged_datum = odatums;
-			merged_index = map_and_merge_partitions(outer_maps, inner_maps,
+			Assert(o_index >= 0 && i_index >= 0);
+			merged_index = map_and_merge_partitions(&outer_map, &inner_map,
 													o_index, i_index,
 													&next_index);
 
@@ -3975,74 +3848,61 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation,
 		}
 		else if (cmpval < 0)
 		{
-			bool		missing_side_outer;
-			bool		missing_side_inner;
+			Assert(cnto < outer_bi->ndatums);
 
 			/* A datum missing from the inner side. */
-			merged_index = -1;
 			merged_datum = odatums;
 
-			/*
-			 * For a FULL join, inner relation acts as both OUTER and INNER
-			 * relation.  For LEFT and ANTI join the inner relation acts as
-			 * INNER relation. For INNER and SEMI join OUTER and INNER
-			 * differentiation is immaterial.
-			 */
-			missing_side_inner = (jointype == JOIN_FULL ||
-								  jointype == JOIN_LEFT ||
-								  jointype == JOIN_ANTI);
-			missing_side_outer = (jointype == JOIN_FULL);
-
-			if (!handle_missing_partition(inner_maps,
-										  outer_maps,
-										  inner_default,
-										  o_index,
-										  missing_side_outer,
-										  missing_side_inner,
-										  &next_index,
-										  &default_index,
-										  &merged_index))
-				return NULL;
+			if (inner_has_default || IS_OUTER_JOIN(jointype))
+			{
+				int			o_index = outer_bi->indexes[cnto];
+
+				Assert(o_index >= 0);
+				if (!process_outer_partition(&outer_map,
+											 &inner_map,
+											 outer_has_default,
+											 inner_has_default,
+											 o_index,
+											 inner_default,
+											 jointype,
+											 outer_bi->strategy,
+											 &next_index,
+											 &default_index,
+											 &merged_index))
+					return NULL;
+			}
 
 			/* Move to the next datum on the outer side. */
-			Assert(!finished_outer);
 			cnto++;
 		}
 		else
 		{
-			bool		missing_side_outer;
-			bool		missing_side_inner;
-
 			Assert(cmpval > 0);
+			Assert(cnti < inner_bi->ndatums);
 
 			/* A datum missing from the outer side. */
-			merged_index = -1;
 			merged_datum = idatums;
 
-			/*
-			 * For a FULL join, outer relation acts as both OUTER and INNER
-			 * relation.  For LEFT and ANTI join the outer relation acts as
-			 * OUTER relation. For INNER and SEMI join OUTER and INNER
-			 * differentiation is immaterial.
-			 */
-			missing_side_outer = (jointype == JOIN_FULL ||
-								  jointype == JOIN_LEFT ||
-								  jointype == JOIN_ANTI);
-			missing_side_inner = (jointype == JOIN_FULL);
-
-			if (!handle_missing_partition(outer_maps,
-										  inner_maps,
-										  outer_default,
-										  i_index,
-										  missing_side_outer,
-										  missing_side_inner,
-										  &next_index,
-										  &default_index,
-										  &merged_index))
-				return NULL;
+			if (outer_has_default || jointype == JOIN_FULL)
+			{
+				int			i_index = inner_bi->indexes[cnti];
+
+				Assert(i_index >= 0);
+				if (!process_inner_partition(&outer_map,
+											 &inner_map,
+											 outer_has_default,
+											 inner_has_default,
+											 i_index,
+											 outer_default,
+											 jointype,
+											 outer_bi->strategy,
+											 &next_index,
+											 &default_index,
+											 &merged_index))
+					return NULL;
+			}
 
-			/* Move to the next datum on the right side. */
-			Assert(!finished_inner);
+			/* Move to the next datum on the inner side. */
 			cnti++;
 		}
 
@@ -4057,22 +3917,43 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation,
 		}
 	}
 
-	if (!merge_null_partitions(outer_bi, inner_bi,
-							   outer_maps, inner_maps,
-							   jointype, &next_index, &null_index,
-							   &default_index))
-		return NULL;
+	/* Merge null partitions if any. */
+	if (outer_has_null || inner_has_null)
+	{
+		if (!merge_null_partitions(outer_bi, inner_bi,
+								   &outer_map, &inner_map,
+								   outer_has_default, inner_has_default,
+								   outer_has_null, inner_has_null,
+								   jointype, &next_index, &default_index,
+								   &null_index))
+			return NULL;
+	}
+	else
+		Assert(null_index == -1);
 
-	if (!merge_default_partitions(outer_bi, inner_bi,
-								  outer_maps, inner_maps,
-								  jointype, &next_index,
-								  &default_index))
-		return NULL;
+	/* Merge default partitions if any. */
+	if (outer_has_default || inner_has_default)
+	{
+		if (!merge_default_partitions(&outer_map, &inner_map,
+									  outer_has_default, outer_default,
+									  inner_has_default, inner_default,
+									  jointype, &next_index, &default_index))
+			return NULL;
+	}
+	else
+		Assert(default_index == -1);
+
+	/* Fix the merged_indexes list if necessary. */
+	if (outer_map.did_remapping || inner_map.did_remapping)
+	{
+		Assert(jointype == JOIN_FULL);
+		fix_merged_indexes(&outer_map, &inner_map,
+						   next_index, merged_indexes);
+	}
 
 	/* Use maps to match partition from the joining relations. */
 	generate_matching_part_pairs(outer_rel, inner_rel,
-								 outer_maps, inner_maps,
-								 outer_nparts, inner_nparts,
+								 &outer_map, &inner_map,
 								 next_index,
 								 outer_parts, inner_parts);
 
@@ -4080,7 +3961,7 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation,
 	if (*outer_parts && *inner_parts)
 	{
 		Assert(list_length(*outer_parts) == list_length(*inner_parts));
-		Assert(list_length(*outer_parts) == next_index);
+		Assert(list_length(*outer_parts) <= next_index);
 		merged_bounds = build_merged_partition_bounds(outer_bi->strategy,
 													  merged_datums,
 													  merged_indexes, NIL,
@@ -4088,6 +3969,8 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation,
 	}
 
 	/* Free up all extra memory before returning from this function. */
+	free_partition_map(&outer_map);
+	free_partition_map(&inner_map);
 	list_free(merged_datums);
 	list_free(merged_indexes);
 
@@ -4095,132 +3978,408 @@ partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation,
 }
 
 /*
- * map_and_merge_partitions
- *
- * If the two given partitions (given by index1 and index2 resp.) are
- * already mapped to each other return the index of corresponding partition in
- * the merged set of partitions.  If they do not have a merged partition
- * associated with them, assign a new merged partition index.  If the
- * partitions are already mapped and their mapped partitions are different from
- * each other, they can not be merged, so return -1.
- *
- * partmaps1[i] gives the mapping of partitions for both relations. It
- * describes which partition of relation 2 matches ith partition of relation 1,
- * and which partition in the merged set matches ith partition of relation 1
- * maps to. Similarly for partmap2.
+ * init_partition_map
  *
- * index1 and index2 are the indexes of matching partition from respective
- * relations.
- *
- * *next_index is used and incremented when the given partitions require a new
- * merged partition.
+ * Initialize a PartitionMap struct for given relation.
  */
-
-static int
-map_and_merge_partitions(PartitionMap *partmaps1, PartitionMap *partmaps2,
-						 int index1, int index2, int *next_index)
+static void
+init_partition_map(RelOptInfo *rel, PartitionMap *map)
 {
-	PartitionMap 	*partmap1 = &partmaps1[index1];
-	PartitionMap 	*partmap2 = &partmaps2[index2];
-	int				merged_index;
+	int			nparts = rel->nparts;
+	int			i;
 
-	/*
-	 * If both the partitions are not mapped to each other, update the
-	 * maps.
-	 */
-	if (partmap1->from < 0 && partmap2->from < 0)
+	map->nparts = nparts;
+	map->merged_indexes = (int *) palloc(sizeof(int) * nparts);
+	map->merged = (bool *) palloc(sizeof(bool) * nparts);
+	map->did_remapping = false;
+	map->old_indexes = (int *) palloc(sizeof(int) * nparts);
+	for (i = 0; i < nparts; i++)
 	{
-		partmap1->from = index2;
-		partmap2->from = index1;
+		map->merged_indexes[i] = map->old_indexes[i] = -1;
+		map->merged[i] = false;
 	}
+}
+
+/*
+ * free_partition_map
+ */
+static void
+free_partition_map(PartitionMap *map)
+{
+	pfree(map->merged_indexes);
+	pfree(map->merged);
+	pfree(map->old_indexes);
+}
+
+/*
+ * map_and_merge_partitions
+ *
+ * *next_index is incremented when creating a new merged partition associated
+ * with the given partitions.
+ */
+static int
+map_and_merge_partitions(PartitionMap *outer_map, PartitionMap *inner_map,
+						 int outer_index, int inner_index, int *next_index)
+{
+	int 		outer_merged_index;
+	bool 		outer_merged;
+	int 		inner_merged_index;
+	bool 		inner_merged;
+
+	Assert(outer_index >= 0 && outer_index < outer_map->nparts);
+	outer_merged_index = outer_map->merged_indexes[outer_index];
+	outer_merged = outer_map->merged[outer_index];
+	Assert(inner_index >= 0 && inner_index < inner_map->nparts);
+	inner_merged_index = inner_map->merged_indexes[inner_index];
+	inner_merged = inner_map->merged[inner_index];
 
 	/*
-	 * If the given to partitions map to each other, find the corresponding
-	 * merged partition index .
+	 * Handle cases where both partitions are mapped to merged partitions.
 	 */
-	if (partmap1->from == index2 && partmap2->from == index1)
+	if (outer_merged_index >= 0 && inner_merged_index >= 0)
 	{
 		/*
-		 * If both the partitions are mapped to the same merged partition, get
-		 * the index of merged partition.
+		 * If the mereged partitions are the same, no need to do anything;
+		 * return the index of the merged partition.  Otherwise, if both
+		 * partitions are merged with dummy partitions, re-merge them; map
+		 * them to the merged partition with the smaller of the two merged
+		 * indexes and return the smaller index.  Otherwise they can't be
+		 * merged, so return -1.
 		 */
-		if (partmap1->to == partmap2->to)
+		if (outer_merged_index == inner_merged_index)
+		{
+			Assert(outer_merged);
+			Assert(inner_merged);
+			return outer_merged_index;
+		}
+		if (!outer_merged && !inner_merged)
 		{
-			merged_index = partmap1->to;
-
 			/*
-			 * If the given two partitions do not have a merged partition
-			 * associated with them, allocate a new merged partition.
+			 * Note that we will fix the larger index that have been added to
+			 * the merged_indexes list so far in fix_merged_indexes().
 			 */
-			if (merged_index < 0)
+			if (outer_merged_index < inner_merged_index)
+			{
+				outer_map->merged[outer_index] = true;
+				inner_map->merged_indexes[inner_index] = outer_merged_index;
+				inner_map->merged[inner_index] = true;
+				inner_map->did_remapping = true;
+				inner_map->old_indexes[inner_index] = inner_merged_index;
+				return outer_merged_index;
+			}
+			else
 			{
-				merged_index = *next_index;
-				*next_index = *next_index + 1;
-				partmap1->to = merged_index;
-				partmap2->to = merged_index;
+				inner_map->merged[inner_index] = true;
+				outer_map->merged_indexes[outer_index] = inner_merged_index;
+				outer_map->merged[outer_index] = true;
+				outer_map->did_remapping = true;
+				outer_map->old_indexes[outer_index] = outer_merged_index;
+				return inner_merged_index;
 			}
 		}
+		return -1;
+	}
+
+	/* At least one partition isn't mapped to a merged partition. */
+	Assert(outer_merged_index == -1 || inner_merged_index == -1);
+
+	/*
+	 * If neither of partitions isn't mapped, assign them a new merged
+	 * partition and return the index of the merged partition.  Otherwise, if
+	 * one of partitions is merged with a dummy relation (and the other isn't
+	 * merged), re-merge it with the other, with the same index, and return
+	 * the index.  Otherwise they can't be merged, so return -1.
+	 */
+	if (outer_merged_index == -1 && inner_merged_index == -1)
+	{
+		int 		merged_index = *next_index;
+
+		Assert(!outer_merged);
+		Assert(!inner_merged);
+		outer_map->merged_indexes[outer_index] = merged_index;
+		outer_map->merged[outer_index] = true;
+		inner_map->merged_indexes[inner_index] = merged_index;
+		inner_map->merged[inner_index] = true;
+		*next_index = *next_index + 1;
+		return merged_index;
+	}
+	if (outer_merged_index >= 0 && !outer_map->merged[outer_index])
+	{
+		Assert(inner_merged_index == -1);
+		Assert(!inner_merged);
+		inner_map->merged_indexes[inner_index] = outer_merged_index;
+		inner_map->merged[inner_index] = true;
+		outer_map->merged[outer_index] = true;
+		return outer_merged_index;
+	}
+	if (inner_merged_index >= 0 && !inner_map->merged[inner_index])
+	{
+		Assert(outer_merged_index == -1);
+		Assert(!outer_merged);
+		outer_map->merged_indexes[outer_index] = inner_merged_index;
+		outer_map->merged[outer_index] = true;
+		inner_map->merged[inner_index] = true;
+		return inner_merged_index;
+	}
+	return -1;
+}
+
+/*
+ * merge_partition_with_dummy
+ *
+ * *next_index is incremented.
+ */
+static int
+merge_partition_with_dummy(PartitionMap *map, int index, int *next_index)
+{
+	int 		merged_index = *next_index;
+
+	Assert(index >= 0 && index < map->nparts);
+	Assert(map->merged_indexes[index] == -1);
+	Assert(!map->merged[index]);
+	map->merged_indexes[index] = merged_index;
+	/* Leave the merged flag alone! */
+	*next_index = *next_index + 1;
+	return merged_index;
+}
+
+/*
+ * process_outer_partition
+ *
+ * Determine the merged partition associated with the given outer partition.
+ *
+ * *next_index is incremented when creating a new merged partition associated
+ * with the given outer partition.
+ */
+static bool
+process_outer_partition(PartitionMap *outer_map,
+						PartitionMap *inner_map,
+						bool outer_has_default,
+						bool inner_has_default,
+						int outer_index,
+						int inner_default,
+						JoinType jointype,
+						char strategy,
+						int *next_index,
+						int *default_index,
+						int *merged_index)
+{
+	Assert(outer_index >= 0);
+
+	/*
+	 * If the inner side has the default partition, the outer partition has to
+	 * be joined with the default partition; try merging them.  Otherwise, we
+	 * should in an outer join, in which case the outer partition has to be
+	 * scanned all the way anyway; if the outer partition is already mapped to
+	 * a merged partition, get it, otherwise create a new merged partition by
+	 * merging the outer partition with a dummy partition.
+	 */
+	if (inner_has_default)
+	{
+		Assert(inner_default >= 0);
+
+		/*
+		 * If the outer side has the default partition as well, we need to
+		 * merge the default partitions (see merge_default_partitions()); give
+		 * up on it.
+		 */
+		if (outer_has_default)
+			return false;
+
+		*merged_index = map_and_merge_partitions(outer_map, inner_map,
+												 outer_index, inner_default,
+												 next_index);
+		if (*merged_index == -1)
+			return false;
 
 		/*
-		 * If partition from one relation was mapped to a merged partition but
-		 * not the partition from the other relation, map the same merged
-		 * partition to the partition from other relation, since matching
-		 * partitions map to the same merged partition.
+		 * If this is a FULL join, the merged partition would act as the
+		 * default partition of the join; record the index in *default_index
+		 * if not done yet.
 		 */
-		else if (partmap1->to >= 0 && partmap2->to < 0)
+		if (jointype == JOIN_FULL)
 		{
-			partmap2->to = partmap1->to;
-			merged_index = partmap1->to;
+			if (*default_index == -1)
+				*default_index = *merged_index;
+			else
+				Assert(*merged_index == *default_index);
+			/* Don't add this index to the list of merged indexes. */
+			*merged_index = -1;
 		}
-		else if (partmap1->to < 0 && partmap2->to >= 0)
+	}
+	else
+	{
+		Assert(IS_OUTER_JOIN(jointype));
+		Assert(jointype != JOIN_RIGHT);
+
+		/*
+		 * In range partitioning, if the given outer partition is already
+		 * merged (eg, because we found an overlapping range earlier), we know
+		 * where it fits in the join result; nothing to do in that case.  Else
+		 * create a new merged partition.
+		 */
+		if (outer_map->merged_indexes[outer_index] >= 0)
 		{
-			partmap1->to = partmap2->to;
-			merged_index = partmap2->to;
+			if (strategy == PARTITION_STRATEGY_LIST)
+				*merged_index = outer_map->merged_indexes[outer_index];
+			else
+			{
+				Assert(strategy == PARTITION_STRATEGY_RANGE);
+				*merged_index = -1;
+			}
 		}
 		else
-		{
-			Assert(partmap1->to != partmap2->to &&
-				   partmap1->to >= 0 && partmap2->to >= 0);
+			*merged_index = merge_partition_with_dummy(outer_map, outer_index,
+													   next_index);
+	}
+	return true;
+}
 
-			/*
-			 * Both the partitions map to different merged partitions. This
-			 * means that multiple partitions from one relation matches to one
-			 * partition from the other relation. Partition-wise join does not
-			 * handle this case right now, since it requires ganging multiple
-			 * partitions together (into one RelOptInfo).
-			 */
-			merged_index = -1;
+/*
+ * process_inner_partition
+ *
+ * Determine the merged partition associated with the given inner partition.
+ *
+ * *next_index is incremented when creating a new merged partition associated
+ * with the given inner partition.
+ */
+static bool
+process_inner_partition(PartitionMap *outer_map,
+						PartitionMap *inner_map,
+						bool outer_has_default,
+						bool inner_has_default,
+						int inner_index,
+						int outer_default,
+						JoinType jointype,
+						char strategy,
+						int *next_index,
+						int *default_index,
+						int *merged_index)
+{
+	Assert(inner_index >= 0);
+
+	/*
+	 * If the outer side has the default partition, the inner partition has to
+	 * be joined with the default partition; try merging them.  Otherwise, we
+	 * should in an FULL join, in which case the inner partition has to be
+	 * scanned all the way anyway; if the inner partition is already mapped to
+	 * a merged partition, get it, otherwise create a new merged partition by
+	 * merging the inner partition with a dummy partition.
+	 */
+	if (outer_has_default)
+	{
+		Assert(outer_default >= 0);
+
+		/*
+		 * If the inner side has the default partition as well, we need to
+		 * merge the default partitions (see merge_default_partitions()); give
+		 * up on it.
+		 */
+		if (inner_has_default)
+			return false;
+
+		*merged_index = map_and_merge_partitions(outer_map, inner_map,
+												 outer_default, inner_index,
+												 next_index);
+		if (*merged_index == -1)
+			return false;
+
+		/*
+		 * If this is an outer join, the merged partition would act as the
+		 * default partition of the join; record the index in *default_index
+		 * if not done yet.
+		 */
+		if (jointype == JOIN_LEFT || jointype == JOIN_ANTI ||
+			jointype == JOIN_FULL)
+		{
+			if (*default_index == -1)
+				*default_index = *merged_index;
+			else
+				Assert(*merged_index == *default_index);
+			/* Don't add this index to the list of merged indexes. */
+			*merged_index = -1;
 		}
 	}
 	else
 	{
+		Assert(jointype == JOIN_FULL);
+
 		/*
-		 * Multiple partitions from one relation map to one partition from the
-		 * other relation. Partition-wise join does not handle this case right
-		 * now, since it requires ganging multiple partitions together (into
-		 * one RelOptInfo).
+		 * In range partitioning, if the given inner partition is already
+		 * merged (eg, because we found an overlapping range earlier), we know
+		 * where it fits in the join result; nothing to do in that case.  Else
+		 * create a new merged partition.
 		 */
-		merged_index = -1;
+		if (inner_map->merged_indexes[inner_index] >= 0)
+		{
+			if (strategy == PARTITION_STRATEGY_LIST)
+				*merged_index = inner_map->merged_indexes[inner_index];
+			else
+			{
+				Assert(strategy == PARTITION_STRATEGY_RANGE);
+				*merged_index = -1;
+			}
+		}
+		else
+			*merged_index = merge_partition_with_dummy(inner_map, inner_index,
+													   next_index);
 	}
+	return true;
+}
 
-	return merged_index;
+/*
+ * fix_merged_indexes
+ */
+static void
+fix_merged_indexes(PartitionMap *outer_map, PartitionMap *inner_map,
+				   int nmerged, List *merged_indexes)
+{
+	int		   *new_indexes;
+	int			merged_index;
+	int			i;
+	ListCell   *lc;
+
+	new_indexes = (int *) palloc(sizeof(int) * nmerged);
+	for (i = 0; i < nmerged; i++)
+		new_indexes[i] = -1;
+
+	/* Build the mapping of old merged indexes to new merged indexes. */
+	if (outer_map->did_remapping)
+	{
+		for (i = 0; i < outer_map->nparts; i++)
+		{
+			merged_index = outer_map->old_indexes[i];
+			if (merged_index >= 0)
+				new_indexes[merged_index] = outer_map->merged_indexes[i];
+		}
+	}
+	if (inner_map->did_remapping)
+	{
+		for (i = 0; i < inner_map->nparts; i++)
+		{
+			merged_index = inner_map->old_indexes[i];
+			if (merged_index >= 0)
+				new_indexes[merged_index] = inner_map->merged_indexes[i];
+		}
+	}
+
+	/* Fix the merged_indexes list using the mapping. */
+	foreach(lc, merged_indexes)
+	{
+		merged_index = lfirst_int(lc);
+		Assert(merged_index >= 0);
+		if (new_indexes[merged_index] >= 0)
+			lfirst_int(lc) = new_indexes[merged_index];
+	}
+
+	pfree(new_indexes);
 }
 
 /*
  * generate_matching_part_pairs
  *
- * partmaps1 map each partition from either side of the join to a merged
- * partition resp. E.g. partmaps1[i].to gives the merged partition to which ith
- * partition of first relation maps. Similarly for partmap2. If
- * partmaps1[i].to == partmaps2[j].to, i and j form the matching pair of
- * partitions.
- *
- * Given these maps this function produces the list pairs of partitions which
- * when joined produce the merged partitions in the order of merged partition
- * indexes.
- *
- * nparts1 and nparts2 are the number of partitions of the joining relations
- * resp.
+ * This function produces the list pairs of partitions which produce merged
+ * partitions in the order of merged partition indexes.
  *
  * nparts is the number of merged partitions.
  *
@@ -4230,15 +4389,20 @@ map_and_merge_partitions(PartitionMap *partmaps1, PartitionMap *partmaps2,
  */
 static void
 generate_matching_part_pairs(RelOptInfo *rel1, RelOptInfo *rel2,
-							 PartitionMap *partmaps1, PartitionMap *partmaps2,
-							 int nparts1, int nparts2, int nparts,
+							 PartitionMap *map1, PartitionMap *map2,
+							 int nparts,
 							 List **matched_parts1, List **matched_parts2)
 {
+	int			nparts1 = map1->nparts;
+	int			nparts2 = map2->nparts;
 	int		   *matching1,
 			   *matching2;
 	int 		i;
 	int			max_nparts;
 
+	Assert(map1->nparts == rel1->nparts);
+	Assert(map2->nparts == rel2->nparts);
+
 	*matched_parts1 = NIL;
 	*matched_parts2 = NIL;
 
@@ -4253,23 +4417,22 @@ generate_matching_part_pairs(RelOptInfo *rel1, RelOptInfo *rel2,
 	{
 		if (i < nparts1)
 		{
-			PartitionMap outer_map = partmaps1[i];
+			int 		merged_index = map1->merged_indexes[i];
 
-			if (outer_map.to >= 0)
+			if (merged_index >= 0)
 			{
-				Assert(outer_map.to < nparts);
-				matching1[outer_map.to] = i;
+				Assert(merged_index < nparts);
+				matching1[merged_index] = i;
 			}
 		}
-
 		if (i < nparts2)
 		{
-			PartitionMap inner_map = partmaps2[i];
+			int 		merged_index = map2->merged_indexes[i];
 
-			if (inner_map.to >= 0)
+			if (merged_index >= 0)
 			{
-				Assert(inner_map.to < nparts);
-				matching2[inner_map.to] = i;
+				Assert(merged_index < nparts);
+				matching2[merged_index] = i;
 			}
 		}
 	}
@@ -4279,8 +4442,8 @@ generate_matching_part_pairs(RelOptInfo *rel1, RelOptInfo *rel2,
 		int			part1 = matching1[i];
 		int			part2 = matching2[i];
 
-		/* At least one of the partitions should exist. */
-		Assert(part1 >= 0 || part2 >= 0);
+		if (part1 == -1 && part2 == -1)
+			continue;
 
 		*matched_parts1 = lappend(*matched_parts1,
 								  part1 >= 0 ? rel1->part_rels[part1] : NULL);
@@ -4356,57 +4519,53 @@ build_merged_partition_bounds(char strategy, List *merged_datums,
  * appear in the join result, so create a default merged partition.
  */
 static bool
-merge_default_partitions(PartitionBoundInfo outer_bi, PartitionBoundInfo inner_bi,
-						 PartitionMap *outer_maps, PartitionMap *inner_maps,
-						 JoinType jointype, int *next_index, int *default_index)
+merge_default_partitions(PartitionMap *outer_map, PartitionMap *inner_map,
+						 bool outer_has_default, int outer_default,
+						 bool inner_has_default, int inner_default,
+						 JoinType jointype, int *next_index,
+						 int *default_index)
 {
-	int				outer_default = outer_bi->default_index;
-	int				inner_default = inner_bi->default_index;
-	bool			outer_has_default = partition_bound_has_default(outer_bi);
-	bool			inner_has_default = partition_bound_has_default(inner_bi);
-	bool			merged = true;
-	PartitionMap 	*outer_default_map = NULL;
-	PartitionMap 	*inner_default_map = NULL;
+	Assert(outer_has_default || inner_has_default);
 
-	if (outer_has_default)
-		outer_default_map = &outer_maps[outer_default];
-
-	if (inner_has_default)
-		inner_default_map = &inner_maps[inner_default];
-
-	if (!outer_has_default && !inner_has_default)
-		Assert(*default_index < 0);
-	else if (outer_default_map != NULL && inner_default_map == NULL)
+	if (outer_has_default && !inner_has_default)
 	{
 		if (jointype == JOIN_LEFT || jointype == JOIN_FULL ||
 			jointype == JOIN_ANTI)
 		{
-			if (outer_default_map->to < 0)
+			int			merged_index;
+
+			Assert(outer_default >= 0 && outer_default < outer_map->nparts);
+			merged_index = outer_map->merged_indexes[outer_default];
+			if (merged_index == -1)
 			{
-				outer_default_map->to = *next_index;
-				*next_index = *next_index + 1;
-				Assert(*default_index < 0);
-				*default_index = outer_default_map->to;
+				Assert(*default_index == -1);
+				*default_index = merge_partition_with_dummy(outer_map,
+															outer_default,
+															next_index);
 			}
 			else
-				Assert(*default_index == outer_default_map->to);
+				Assert(*default_index == merged_index);
 		}
 		else
 			Assert(*default_index < 0);
 	}
-	else if (outer_default_map == NULL && inner_default_map != NULL)
+	else if (!outer_has_default && inner_has_default)
 	{
 		if (jointype == JOIN_FULL)
 		{
-			if (inner_default_map->to < 0)
+			int			merged_index;
+
+			Assert(inner_default >= 0 && inner_default < inner_map->nparts);
+			merged_index = inner_map->merged_indexes[inner_default];
+			if (merged_index == -1)
 			{
-				inner_default_map->to = *next_index;
-				*next_index = *next_index + 1;
-				Assert(*default_index < 0);
-				*default_index = inner_default_map->to;
+				Assert(*default_index == -1);
+				*default_index = merge_partition_with_dummy(inner_map,
+															inner_default,
+															next_index);
 			}
 			else
-				Assert(*default_index == inner_default_map->to);
+				Assert(*default_index == merged_index);
 		}
 		else
 			Assert(*default_index < 0);
@@ -4415,15 +4574,16 @@ merge_default_partitions(PartitionBoundInfo outer_bi, PartitionBoundInfo inner_b
 	{
 		Assert(outer_has_default && inner_has_default);
 
-		*default_index = map_and_merge_partitions(outer_maps, inner_maps,
-												  outer_default, inner_default,
+		*default_index = map_and_merge_partitions(outer_map,
+												  inner_map,
+												  outer_default,
+												  inner_default,
 												  next_index);
-
-		if (*default_index < 0)
-			merged = false;
+		if (*default_index == -1)
+			return false;
 	}
 
-	return merged;
+	return true;
 }
 
 /*
@@ -4443,101 +4603,80 @@ merge_default_partitions(PartitionBoundInfo outer_bi, PartitionBoundInfo inner_b
  */
 static bool
 merge_null_partitions(PartitionBoundInfo outer_bi, PartitionBoundInfo inner_bi,
-					  PartitionMap *outer_maps, PartitionMap *inner_maps,
+					  PartitionMap *outer_map, PartitionMap *inner_map,
+					  bool outer_has_default, bool inner_has_default,
+					  bool outer_has_null, bool inner_has_null,
 					  JoinType jointype, int *next_index,
-					  int *null_index, int *default_index)
+					  int *default_index, int *null_index)
 {
-	bool		outer_has_null = partition_bound_accepts_nulls(outer_bi);
-	bool		inner_has_null = partition_bound_accepts_nulls(inner_bi);
-	int			outer_ni = outer_bi->null_index;
-	int			inner_ni = inner_bi->null_index;
-	int			outer_default = outer_bi->default_index;
-	int			inner_default = inner_bi->default_index;
-	bool		merged = true;
+	Assert(outer_has_null || inner_has_null);
+	Assert(*null_index == -1);
 
-	if (!outer_has_null && !inner_has_null)
-		Assert(*null_index < 0);
-	else if (outer_has_null && !inner_has_null)
+	if (outer_has_null && !inner_has_null)
 	{
 		int			merged_index = -1;
-		bool		missing_side_outer;
-		bool		missing_side_inner;
-
-		/*
-		 * For a FULL join, inner relation acts as both OUTER and INNER
-		 * relation.  For LEFT and ANTI join the inner relation acts as
-		 * INNER relation. For INNER and SEMI join OUTER and INNER
-		 * differentiation is immaterial.
-		 */
-		missing_side_inner = (jointype == JOIN_FULL ||
-							  jointype == JOIN_LEFT ||
-							  jointype == JOIN_ANTI);
-		missing_side_outer = (jointype == JOIN_FULL);
-
-		merged = handle_missing_partition(inner_maps,
-										  outer_maps,
-										  inner_default,
-										  outer_ni,
-										  missing_side_outer,
-										  missing_side_inner, next_index,
-										  default_index, &merged_index);
-		*null_index = merged_index;
 
 		/*
 		 * If the NULL partition was missing from the inner side of the join,
-		 * the partition of the join to which the outer null partition maps
-		 * will contain the NULL values and thus becomes the NULL partition of
+		 * the partition of the join to which the NULL partition matches will
+		 * contain the NULL values and thus become the NULL partition of the
 		 * the join.
 		 */
-		if (missing_side_inner)
-			*null_index = outer_maps[outer_ni].to;
+		if (inner_has_default || IS_OUTER_JOIN(jointype))
+		{
+			if (!process_outer_partition(outer_map,
+										 inner_map,
+										 outer_has_default,
+										 inner_has_default,
+										 outer_bi->null_index,
+										 inner_bi->default_index,
+										 jointype,
+										 outer_bi->strategy,
+										 next_index,
+										 default_index,
+										 &merged_index))
+				return false;
+		}
+		*null_index = merged_index;
 	}
 	else if (!outer_has_null && inner_has_null)
 	{
 		int			merged_index = -1;
-		bool		missing_side_outer;
-		bool		missing_side_inner;
 
 		/*
-		 * For a FULL join, outer relation acts as both OUTER and INNER
-		 * relation.  For LEFT and ANTI join the outer relation acts as OUTER
-		 * relation. For INNER and SEMI join OUTER and INNER differentiation is
-		 * immaterial.
-		 */
-		missing_side_outer = (jointype == JOIN_FULL ||
-							  jointype == JOIN_LEFT ||
-							  jointype == JOIN_ANTI);
-		missing_side_inner = (jointype == JOIN_FULL);
-		merged = handle_missing_partition(outer_maps,
-										  inner_maps,
-										  outer_default,
-										  inner_ni,
-										  missing_side_outer,
-										  missing_side_inner,
-										  next_index, default_index,
-										  &merged_index);
-		*null_index = merged_index;
-
-		/*
-		 * If the NULL partition was missing from the inner side of the join,
-		 * the partition of the join, to which the outer side null partition maps,
-		 * will contain the NULL values and thus becomes the NULL partition of
+		 * If the NULL partition was missing from the outer side of the join,
+		 * the partition of the join to which the NULL partition matches will
+		 * contain the NULL values and thus become the NULL partition of the
 		 * the join.
 		 */
-		if (missing_side_inner)
-			*null_index = inner_maps[inner_ni].to;
+		if (outer_has_default || jointype == JOIN_FULL)
+		{
+			if (!process_inner_partition(outer_map,
+										 inner_map,
+										 outer_has_default,
+										 inner_has_default,
+										 inner_bi->null_index,
+										 outer_bi->default_index,
+										 jointype,
+										 outer_bi->strategy,
+										 next_index,
+										 default_index,
+										 &merged_index))
+				return false;
+		}
+		*null_index = merged_index;
 	}
 	else
 	{
 		/* Both the relations have NULL partitions, try merging them. */
-		*null_index = map_and_merge_partitions(outer_maps,
-											   inner_maps,
-											   outer_ni,
-											   inner_ni,
+		*null_index = map_and_merge_partitions(outer_map,
+											   inner_map,
+											   outer_bi->null_index,
+											   inner_bi->null_index,
 											   next_index);
-		if (*null_index < 0)
-			merged = false;
+		if (*null_index == -1)
+			return false;
 	}
 
-	return merged;
+	return true;
 }
diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out
index b5b5c8a260..89f0c6a9ee 100644
--- a/src/test/regress/expected/partition_join.out
+++ b/src/test/regress/expected/partition_join.out
@@ -4769,3 +4769,182 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b =
                            Filter: (b = 0)
 (23 rows)
 
+DROP TABLE plt1;
+DROP TABLE plt2;
+CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c);
+CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0002');
+CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0003', '0004');
+INSERT INTO plt1 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i WHERE i % 5 IN (0, 2, 3, 4);
+ANALYZE plt1;
+CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c);
+CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0001', '0002');
+CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0003', '0004');
+INSERT INTO plt2 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i WHERE i % 5 IN (1, 2, 3, 4);
+ANALYZE plt2;
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON (t1.c = t2.c) WHERE COALESCE(t1.a, 0) % 5 != 3 AND COALESCE(t1.a, 0) % 5 != 4 ORDER BY t1.c, t1.a, t2.a;
+                                          QUERY PLAN                                           
+-----------------------------------------------------------------------------------------------
+ Sort
+   Sort Key: t1.c, t1.a, t2.a
+   ->  Append
+         ->  Hash Full Join
+               Hash Cond: (t1.c = t2.c)
+               Filter: (((COALESCE(t1.a, 0) % 5) <> 3) AND ((COALESCE(t1.a, 0) % 5) <> 4))
+               ->  Seq Scan on plt1_p1 t1
+               ->  Hash
+                     ->  Seq Scan on plt2_p1 t2
+         ->  Hash Full Join
+               Hash Cond: (t1_1.c = t2_1.c)
+               Filter: (((COALESCE(t1_1.a, 0) % 5) <> 3) AND ((COALESCE(t1_1.a, 0) % 5) <> 4))
+               ->  Seq Scan on plt1_p2 t1_1
+               ->  Hash
+                     ->  Seq Scan on plt2_p2 t2_1
+(15 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON (t1.c = t2.c) WHERE COALESCE(t1.a, 0) % 5 != 3 AND COALESCE(t1.a, 0) % 5 != 4 ORDER BY t1.c, t1.a, t2.a;
+ a  |  c   | a  |  c   
+----+------+----+------
+  0 | 0000 |    | 
+  5 | 0000 |    | 
+ 10 | 0000 |    | 
+ 15 | 0000 |    | 
+ 20 | 0000 |    | 
+  2 | 0002 |  2 | 0002
+  2 | 0002 |  7 | 0002
+  2 | 0002 | 12 | 0002
+  2 | 0002 | 17 | 0002
+  2 | 0002 | 22 | 0002
+  7 | 0002 |  2 | 0002
+  7 | 0002 |  7 | 0002
+  7 | 0002 | 12 | 0002
+  7 | 0002 | 17 | 0002
+  7 | 0002 | 22 | 0002
+ 12 | 0002 |  2 | 0002
+ 12 | 0002 |  7 | 0002
+ 12 | 0002 | 12 | 0002
+ 12 | 0002 | 17 | 0002
+ 12 | 0002 | 22 | 0002
+ 17 | 0002 |  2 | 0002
+ 17 | 0002 |  7 | 0002
+ 17 | 0002 | 12 | 0002
+ 17 | 0002 | 17 | 0002
+ 17 | 0002 | 22 | 0002
+ 22 | 0002 |  2 | 0002
+ 22 | 0002 |  7 | 0002
+ 22 | 0002 | 12 | 0002
+ 22 | 0002 | 17 | 0002
+ 22 | 0002 | 22 | 0002
+    |      |  1 | 0001
+    |      |  6 | 0001
+    |      | 11 | 0001
+    |      | 16 | 0001
+    |      | 21 | 0001
+(35 rows)
+
+DROP TABLE plt1;
+DROP TABLE plt2;
+CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c);
+CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0001', '0002');
+CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0003', '0004');
+INSERT INTO plt1 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i;
+ANALYZE plt1;
+CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c);
+CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0002');
+CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0003', '0004');
+INSERT INTO plt2 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i WHERE i % 5 IN (2, 3, 4);
+ANALYZE plt2;
+CREATE TABLE plt3 (a int, b int, c text) PARTITION BY LIST(c);
+CREATE TABLE plt3_p1 PARTITION OF plt3 FOR VALUES IN ('0001');
+CREATE TABLE plt3_p2 PARTITION OF plt3 FOR VALUES IN ('0003', '0004');
+INSERT INTO plt3 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i WHERE i % 5 IN (1, 3, 4);
+ANALYZE plt3;
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON (t1.c = t2.c)) FULL JOIN plt3 t3 ON (t1.c = t3.c) WHERE COALESCE(t1.a, 0) % 5 != 3 AND coalesce(t1.a, 0) % 5 != 4 ORDER BY t1.c, t1.a, t2.a, t3.a;
+                                          QUERY PLAN                                           
+-----------------------------------------------------------------------------------------------
+ Sort
+   Sort Key: t1.c, t1.a, t2.a, t3.a
+   ->  Append
+         ->  Hash Full Join
+               Hash Cond: (t1.c = t3.c)
+               Filter: (((COALESCE(t1.a, 0) % 5) <> 3) AND ((COALESCE(t1.a, 0) % 5) <> 4))
+               ->  Hash Left Join
+                     Hash Cond: (t1.c = t2.c)
+                     ->  Seq Scan on plt1_p1 t1
+                     ->  Hash
+                           ->  Seq Scan on plt2_p1 t2
+               ->  Hash
+                     ->  Seq Scan on plt3_p1 t3
+         ->  Hash Full Join
+               Hash Cond: (t1_1.c = t3_1.c)
+               Filter: (((COALESCE(t1_1.a, 0) % 5) <> 3) AND ((COALESCE(t1_1.a, 0) % 5) <> 4))
+               ->  Hash Left Join
+                     Hash Cond: (t1_1.c = t2_1.c)
+                     ->  Seq Scan on plt1_p2 t1_1
+                     ->  Hash
+                           ->  Seq Scan on plt2_p2 t2_1
+               ->  Hash
+                     ->  Seq Scan on plt3_p2 t3_1
+(23 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON (t1.c = t2.c)) FULL JOIN plt3 t3 ON (t1.c = t3.c) WHERE COALESCE(t1.a, 0) % 5 != 3 AND coalesce(t1.a, 0) % 5 != 4 ORDER BY t1.c, t1.a, t2.a, t3.a;
+ a  |  c   | a  |  c   | a  |  c   
+----+------+----+------+----+------
+  0 | 0000 |    |      |    | 
+  5 | 0000 |    |      |    | 
+ 10 | 0000 |    |      |    | 
+ 15 | 0000 |    |      |    | 
+ 20 | 0000 |    |      |    | 
+  1 | 0001 |    |      |  1 | 0001
+  1 | 0001 |    |      |  6 | 0001
+  1 | 0001 |    |      | 11 | 0001
+  1 | 0001 |    |      | 16 | 0001
+  1 | 0001 |    |      | 21 | 0001
+  6 | 0001 |    |      |  1 | 0001
+  6 | 0001 |    |      |  6 | 0001
+  6 | 0001 |    |      | 11 | 0001
+  6 | 0001 |    |      | 16 | 0001
+  6 | 0001 |    |      | 21 | 0001
+ 11 | 0001 |    |      |  1 | 0001
+ 11 | 0001 |    |      |  6 | 0001
+ 11 | 0001 |    |      | 11 | 0001
+ 11 | 0001 |    |      | 16 | 0001
+ 11 | 0001 |    |      | 21 | 0001
+ 16 | 0001 |    |      |  1 | 0001
+ 16 | 0001 |    |      |  6 | 0001
+ 16 | 0001 |    |      | 11 | 0001
+ 16 | 0001 |    |      | 16 | 0001
+ 16 | 0001 |    |      | 21 | 0001
+ 21 | 0001 |    |      |  1 | 0001
+ 21 | 0001 |    |      |  6 | 0001
+ 21 | 0001 |    |      | 11 | 0001
+ 21 | 0001 |    |      | 16 | 0001
+ 21 | 0001 |    |      | 21 | 0001
+  2 | 0002 |  2 | 0002 |    | 
+  2 | 0002 |  7 | 0002 |    | 
+  2 | 0002 | 12 | 0002 |    | 
+  2 | 0002 | 17 | 0002 |    | 
+  2 | 0002 | 22 | 0002 |    | 
+  7 | 0002 |  2 | 0002 |    | 
+  7 | 0002 |  7 | 0002 |    | 
+  7 | 0002 | 12 | 0002 |    | 
+  7 | 0002 | 17 | 0002 |    | 
+  7 | 0002 | 22 | 0002 |    | 
+ 12 | 0002 |  2 | 0002 |    | 
+ 12 | 0002 |  7 | 0002 |    | 
+ 12 | 0002 | 12 | 0002 |    | 
+ 12 | 0002 | 17 | 0002 |    | 
+ 12 | 0002 | 22 | 0002 |    | 
+ 17 | 0002 |  2 | 0002 |    | 
+ 17 | 0002 |  7 | 0002 |    | 
+ 17 | 0002 | 12 | 0002 |    | 
+ 17 | 0002 | 17 | 0002 |    | 
+ 17 | 0002 | 22 | 0002 |    | 
+ 22 | 0002 |  2 | 0002 |    | 
+ 22 | 0002 |  7 | 0002 |    | 
+ 22 | 0002 | 12 | 0002 |    | 
+ 22 | 0002 | 17 | 0002 |    | 
+ 22 | 0002 | 22 | 0002 |    | 
+(55 rows)
+
diff --git a/src/test/regress/sql/partition_join.sql b/src/test/regress/sql/partition_join.sql
index 34ae92135f..b0ab002eee 100644
--- a/src/test/regress/sql/partition_join.sql
+++ b/src/test/regress/sql/partition_join.sql
@@ -800,3 +800,47 @@ ANALYZE prt2;
 
 EXPLAIN (COSTS OFF)
 SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
+
+DROP TABLE plt1;
+DROP TABLE plt2;
+
+CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c);
+CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0002');
+CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0003', '0004');
+INSERT INTO plt1 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i WHERE i % 5 IN (0, 2, 3, 4);
+ANALYZE plt1;
+
+CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c);
+CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0001', '0002');
+CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0003', '0004');
+INSERT INTO plt2 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i WHERE i % 5 IN (1, 2, 3, 4);
+ANALYZE plt2;
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON (t1.c = t2.c) WHERE COALESCE(t1.a, 0) % 5 != 3 AND COALESCE(t1.a, 0) % 5 != 4 ORDER BY t1.c, t1.a, t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON (t1.c = t2.c) WHERE COALESCE(t1.a, 0) % 5 != 3 AND COALESCE(t1.a, 0) % 5 != 4 ORDER BY t1.c, t1.a, t2.a;
+
+DROP TABLE plt1;
+DROP TABLE plt2;
+
+CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c);
+CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0001', '0002');
+CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0003', '0004');
+INSERT INTO plt1 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i;
+ANALYZE plt1;
+
+CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c);
+CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0002');
+CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0003', '0004');
+INSERT INTO plt2 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i WHERE i % 5 IN (2, 3, 4);
+ANALYZE plt2;
+
+CREATE TABLE plt3 (a int, b int, c text) PARTITION BY LIST(c);
+CREATE TABLE plt3_p1 PARTITION OF plt3 FOR VALUES IN ('0001');
+CREATE TABLE plt3_p2 PARTITION OF plt3 FOR VALUES IN ('0003', '0004');
+INSERT INTO plt3 SELECT i, i, to_char(i % 5, 'FM0000') FROM generate_series(0, 24) i WHERE i % 5 IN (1, 3, 4);
+ANALYZE plt3;
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON (t1.c = t2.c)) FULL JOIN plt3 t3 ON (t1.c = t3.c) WHERE COALESCE(t1.a, 0) % 5 != 3 AND coalesce(t1.a, 0) % 5 != 4 ORDER BY t1.c, t1.a, t2.a, t3.a;
+SELECT t1.a, t1.c, t2.a, t2.c, t3.a, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON (t1.c = t2.c)) FULL JOIN plt3 t3 ON (t1.c = t3.c) WHERE COALESCE(t1.a, 0) % 5 != 3 AND coalesce(t1.a, 0) % 5 != 4 ORDER BY t1.c, t1.a, t2.a, t3.a;
