diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 43c3b7ea48..4ab218641c 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -1339,25 +1339,31 @@ 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);
-	int			nparts;
+	PartitionScheme part_scheme;
+	PartitionBoundInfo join_boundinfo;
+	List	   *parts1;
+	List	   *parts2;
+	ListCell   *lc1;
+	ListCell   *lc2;
 	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 (!IS_PARTITIONED_REL(joinrel))
+	if (joinrel->part_scheme == NULL)
 		return;
 
 	/* The join relation should have consider_partitionwise_join set. */
 	Assert(joinrel->consider_partitionwise_join);
 
 	/*
-	 * Since this join relation is partitioned, all the base relations
-	 * participating in this join must be partitioned and so are all the
-	 * intermediate join relations.
+	 * We can not perform partition-wise join if either of the joining
+	 * relations is not partitioned.
 	 */
-	Assert(IS_PARTITIONED_REL(rel1) && IS_PARTITIONED_REL(rel2));
+	if (!IS_PARTITIONED_REL(rel1) || !IS_PARTITIONED_REL(rel2))
+		return;
+
 	Assert(REL_HAS_ALL_PART_PROPS(rel1) && REL_HAS_ALL_PART_PROPS(rel2));
 
 	/* The joining relations should have consider_partitionwise_join set. */
@@ -1370,36 +1376,67 @@ 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;
 
 	/*
-	 * Since we allow partitionwise join only when the partition bounds of the
-	 * joining relations exactly match, the partition bounds of the join
-	 * should match those of the joining relations.
+	 * 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.
 	 */
-	Assert(partition_bounds_equal(joinrel->part_scheme->partnatts,
-								  joinrel->part_scheme->parttyplen,
-								  joinrel->part_scheme->parttypbyval,
-								  joinrel->boundinfo, rel1->boundinfo));
-	Assert(partition_bounds_equal(joinrel->part_scheme->partnatts,
-								  joinrel->part_scheme->parttyplen,
-								  joinrel->part_scheme->parttypbyval,
-								  joinrel->boundinfo, rel2->boundinfo));
+	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;
 
-	nparts = joinrel->nparts;
+	if (joinrel->boundinfo == NULL)
+	{
+		Assert(joinrel->nparts == 0 && joinrel->part_rels == NULL);
+		joinrel->boundinfo = join_boundinfo;
+		joinrel->nparts = list_length(parts1);
+		Assert(joinrel->nparts == list_length(parts2));
+		joinrel->part_rels =
+			(RelOptInfo **) palloc0(sizeof(RelOptInfo *) *
+									joinrel->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->part_rels);
+	}
 
 	/*
 	 * Create child-join relations for this partitioned join, if those don't
 	 * exist. Add paths to child-joins for a pair of child relations
 	 * corresponding to the given pair of parent relations.
 	 */
-	for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++)
+	cnt_parts = -1;
+	forboth(lc1, parts1, lc2, parts2)
 	{
-		RelOptInfo *child_rel1 = rel1->part_rels[cnt_parts];
-		RelOptInfo *child_rel2 = 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));
+		int			part1 = lfirst_int(lc1);
+		int			part2 = lfirst_int(lc2);
+		RelOptInfo *child_rel1;
+		RelOptInfo *child_rel2;
+		bool		rel1_empty;
+		bool		rel2_empty;
 		SpecialJoinInfo *child_sjinfo;
 		List	   *child_restrictlist;
 		RelOptInfo *child_joinrel;
@@ -1407,6 +1444,13 @@ 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++;
+
 		/*
 		 * Check for cases where we can prove that this segment of the join
 		 * returns no rows, due to one or both inputs being empty (including
@@ -1506,12 +1550,23 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 			joinrel->part_rels[cnt_parts] = child_joinrel;
 		}
 
+		/*
+		 * 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));
 }
 
 /*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 6054bd2b53..ead871180f 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -1623,7 +1623,7 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
 	 * of the way the query planner deduces implied equalities and reorders
 	 * the joins.  Please see optimizer/README for details.
 	 */
-	if (!IS_PARTITIONED_REL(outer_rel) || !IS_PARTITIONED_REL(inner_rel) ||
+	if (outer_rel->part_scheme == NULL || inner_rel->part_scheme == NULL ||
 		!outer_rel->consider_partitionwise_join ||
 		!inner_rel->consider_partitionwise_join ||
 		outer_rel->part_scheme != inner_rel->part_scheme ||
@@ -1636,24 +1636,6 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
 
 	part_scheme = outer_rel->part_scheme;
 
-	Assert(REL_HAS_ALL_PART_PROPS(outer_rel) &&
-		   REL_HAS_ALL_PART_PROPS(inner_rel));
-
-	/*
-	 * For now, our partition matching algorithm can match partitions only
-	 * when the partition bounds of the joining relations are exactly same.
-	 * So, bail out otherwise.
-	 */
-	if (outer_rel->nparts != inner_rel->nparts ||
-		!partition_bounds_equal(part_scheme->partnatts,
-								part_scheme->parttyplen,
-								part_scheme->parttypbyval,
-								outer_rel->boundinfo, inner_rel->boundinfo))
-	{
-		Assert(!IS_PARTITIONED_REL(joinrel));
-		return;
-	}
-
 	/*
 	 * This function will be called only once for each joinrel, hence it
 	 * should not have partition scheme, partition bounds, partition key
@@ -1665,17 +1647,20 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
 
 	/*
 	 * Join relation is partitioned using the same partitioning scheme as the
-	 * joining relations and has same bounds.
+	 * joining relations.
+	 *
+	 * Because of restrictions in partition_bounds_merge(), not every pair of
+	 * joining relations (including the one presented to this function) for the
+	 * same joinrel can use partition-wise join or has both the relations
+	 * partitioned. Hence we calculate the partition bounds, number of
+	 * partitions and child-join relations of the join relation when and if we
+	 * find a suitable pair in try_partition_wise_join().
 	 */
 	joinrel->part_scheme = part_scheme;
-	joinrel->boundinfo = outer_rel->boundinfo;
 	partnatts = joinrel->part_scheme->partnatts;
 	joinrel->partexprs = (List **) palloc0(sizeof(List *) * partnatts);
 	joinrel->nullable_partexprs =
 		(List **) palloc0(sizeof(List *) * partnatts);
-	joinrel->nparts = outer_rel->nparts;
-	joinrel->part_rels =
-		(RelOptInfo **) palloc0(sizeof(RelOptInfo *) * joinrel->nparts);
 
 	/*
 	 * Set the consider_partitionwise_join flag.
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 13d576c379..1084ec8f69 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -25,6 +25,7 @@
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
+#include "nodes/pathnodes.h"
 #include "parser/parse_coerce.h"
 #include "partitioning/partbounds.h"
 #include "partitioning/partdesc.h"
@@ -69,6 +70,12 @@ typedef struct PartitionRangeBound
 	bool		lower;			/* this is the lower (vs upper) bound */
 } PartitionRangeBound;
 
+typedef struct PartitionMap
+{
+	int from;
+	int to;
+} PartitionMap;
+
 static int32 qsort_partition_hbound_cmp(const void *a, const void *b);
 static int32 qsort_partition_list_value_cmp(const void *a, const void *b,
 											void *arg);
@@ -108,6 +115,58 @@ 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(
+							 RelOptInfo *outer_rel, RelOptInfo *inner_rel,
+							 List **outer_parts, List **inner_parts,
+							 JoinType jointype, int partnatts,
+							 FmgrInfo *supfuncs, Oid *collations);
+static PartitionBoundInfo partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *collations,
+							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 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);
+static bool partition_range_cmp(int partnatts, FmgrInfo *supfuncs,
+						   Oid *collations, PartitionRangeBound *lower_bound1,
+						   PartitionRangeBound *upper_bound1,
+						   PartitionRangeBound *lower_bound2,
+						   PartitionRangeBound *upper_bound2, int *ub_cmpval,
+						   int *lb_cmpval);
+static bool partition_range_merge_next_lb(int partnatts, FmgrInfo *supfuncs,
+							  Oid *collations, Datum *next_lb_datums,
+							  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,
+						 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);
 
 /*
  * get_qual_from_partbound
@@ -2995,3 +3054,1621 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
 
 	PG_RETURN_BOOL(rowHash % modulus == remainder);
 }
+
+/*
+ * partition_bounds_merge
+ *
+ * The function produces the partition bounds for a join between two relations
+ * whose partition bounds are given. The function also returns two lists of
+ * partition indexes one for each of the joining relations. Both the lists
+ * contain the same number of elements. The partition indexes at the same
+ * positions in the lists indicate the pair partitions, one from each side, to
+ * be joined and the position itself corresponds to the index of partition
+ * produced by that child-join in the partitioned join.
+ *
+ * The function returns NULL if we can not find the matching pair of
+ * partitions. This happens if 1. multiple partitions on one side match with
+ * one partition on the other side. 2. a given partition on the outer side
+ * doesn't have a matching partition on the inner side. We can not support the
+ * first case since we don't have a way to represent multiple partitions as a
+ * single relation (RelOptInfo) and then perform join using the ganged
+ * relation. We can not support the second case since the missing inner
+ * partition needs to be represented as an empty relation and we don't have a
+ * way to introduce empty relation during join planning after creating paths
+ * for all the base relations.
+ */
+PartitionBoundInfo
+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)
+{
+	PartitionBoundInfo 	merged_bounds;
+	PartitionBoundInfo 	outer_binfo = outer_rel->boundinfo,
+						inner_binfo = inner_rel->boundinfo;
+	char				strategy = outer_binfo->strategy;
+
+	/* Bail out if partitioning strategies are different. */
+	if (outer_binfo->strategy != inner_binfo->strategy)
+		return NULL;
+
+	if (jointype != JOIN_LEFT && jointype != JOIN_INNER &&
+		jointype != JOIN_SEMI && jointype != JOIN_ANTI &&
+		jointype != JOIN_FULL)
+		elog(ERROR, "unexpected join type %d", jointype);
+
+	*outer_parts = NIL;
+	*inner_parts = NIL;
+	switch (strategy)
+	{
+		case PARTITION_STRATEGY_LIST:
+			merged_bounds = partition_list_bounds_merge(partsupfunc,
+														partcollation,
+														outer_rel, inner_rel,
+														outer_parts, inner_parts,
+														jointype);
+			break;
+
+		case PARTITION_STRATEGY_RANGE:
+			merged_bounds = partition_range_bounds_merge(outer_rel, inner_rel,
+														 outer_parts, inner_parts,
+														 jointype, partnatts,
+														 partsupfunc,
+														 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);
+	}
+
+	Assert(merged_bounds || (*outer_parts == NIL && *inner_parts == NIL));
+
+	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;
+}
+
+/*
+ * partition_get_range_bounds
+ *
+ * Given the index of lower bound in datums array, return lower and upper
+ * bounds and the index of the partition with that lower bound.
+ */
+static int
+partition_get_range_bounds(PartitionBoundInfo bi, int lb_index,
+						   PartitionRangeBound *lower,
+						   PartitionRangeBound *upper)
+{
+	int			part_index;
+
+	/* A lower bound should have at least one more bound after it. */
+	Assert(lb_index < bi->ndatums - 1);
+
+	/* The lower bound should correspond to a valid partition. */
+	part_index = bi->indexes[lb_index + 1];
+	Assert(part_index >= 0);
+
+	lower->kind = bi->kind[lb_index];
+	lower->datums = bi->datums[lb_index];
+	lower->lower = true;
+	upper->kind = bi->kind[lb_index + 1];
+	upper->datums = bi->datums[lb_index + 1];
+	upper->lower = false;
+
+	return part_index;
+}
+
+/*
+ * partition_range_get_next_lb_index
+ *
+ * Given the index of lower bound in datums array return the
+ * index of lower bound of the next partition. When the given index corresponds
+ * to the last partition, return number of datums (ndatums).
+ */
+static int
+partition_range_get_next_lb_index(PartitionBoundInfo bi, int lb_index)
+{
+	/* A lower bound should have at least one more bound after it. */
+	Assert(lb_index < bi->ndatums - 1);
+
+	/* The partition index corresponding to the upper bound should be valid. */
+	Assert(bi->indexes[lb_index + 1] >= 0);
+
+	/*
+	 * If there are no bounds left beyond the upper bound, we have reached the
+	 * last partition.
+	 */
+	if (lb_index + 2 < bi->ndatums)
+	{
+		/*
+		 * If the bound next to the upper bound corresponds to no partition,
+		 * that's the next lower bound of the next partition. Otherwise, the
+		 * current upper bound is the lower bound of the next partition.
+		 */
+		if (bi->indexes[lb_index + 2] < 0)
+			return lb_index + 2;
+		else
+			return lb_index + 1;
+	}
+	else
+		return bi->ndatums;
+}
+
+static int32
+partition_range_bound_cmp(int partnatts, FmgrInfo *partsupfunc,
+						  Oid *partcollations, PartitionRangeBound *bound1,
+						  PartitionRangeBound *bound2)
+{
+	return partition_rbound_cmp(partnatts, partsupfunc, partcollations,
+								bound1->datums, bound1->kind, bound1->lower,
+								bound2);
+}
+
+/*
+ * partition_range_cmp
+ *
+ * Compare the bounds of two range partitions. Set ub_cmpval <, = or > 0, if the
+ * first partition's upper bound is lower than, equal to or higher than the
+ * second partition's upper bound resp. Similarly set lb_cmpval <, =  or > 0,
+ * if the first partition's lower bound is lower than, equal to or higher than
+ * the second partition's lower bound resp.
+ *
+ * Return true, if the ranges overlap, otherwise return false.
+ */
+static bool
+partition_range_cmp(int partnatts, FmgrInfo *partsupfuncs, Oid *partcollations,
+					PartitionRangeBound *lower_bound1,
+					PartitionRangeBound *upper_bound1,
+					PartitionRangeBound *lower_bound2,
+					PartitionRangeBound *upper_bound2, int *ub_cmpval,
+					int *lb_cmpval)
+{
+	bool		overlap;
+
+	/*
+	 * Compare upper bound of the first partition with the lower bound of the
+	 * second and vice-versa. If lower bound is higher than the upper bound,
+	 * the partitions are not overlapping. All other cases indicate overlapping
+	 * partitions.
+	 */
+	if (partition_range_bound_cmp(partnatts, partsupfuncs, partcollations,
+								  lower_bound1, upper_bound2) > 0)
+	{
+		overlap = false;
+		*ub_cmpval = 1;
+		*lb_cmpval = 1;
+	}
+	else if (partition_range_bound_cmp(partnatts, partsupfuncs, partcollations,
+									   lower_bound2, upper_bound1) > 0)
+	{
+		overlap = false;
+		*ub_cmpval = -1;
+		*lb_cmpval = -1;
+	}
+	else
+	{
+		overlap = true;
+		*ub_cmpval = partition_range_bound_cmp(partnatts, partsupfuncs,
+											   partcollations, upper_bound1,
+											   upper_bound2);
+		*lb_cmpval = partition_range_bound_cmp(partnatts, partsupfuncs,
+											   partcollations, lower_bound1,
+											   lower_bound2);
+	}
+
+	return overlap;
+}
+
+/*
+ * partition_range_merge
+ *
+ * Merge the partition bounds of given two partitions such that the join
+ * between the given two partitions fits merged bounds.
+ *
+ * "merged_upper" will be set to one of the given upper bounds and
+ * "merged_lower" will be set to one of the given lower bounds.
+ */
+static void
+partition_range_merge(int partnatts, FmgrInfo *partsupfuncs,
+					  Oid *partcollations, JoinType jointype,
+					  PartitionRangeBound *left_lb,
+					  PartitionRangeBound *left_ub,
+					  PartitionRangeBound *right_lb,
+					  PartitionRangeBound *right_ub,
+					  PartitionRangeBound **merged_lb,
+					  PartitionRangeBound **merged_ub)
+{
+	/*
+	 * An outer join will have all the rows from the outer side, so merged
+	 * bounds will be same as the outer bounds. An inner join will have rows
+	 * that fit both the bounds, thus lower merged bound will be higher of two
+	 * lower bounds and upper merged bound will be lower of the two upper
+	 * bounds.
+	 */
+	switch (jointype)
+	{
+		case JOIN_LEFT:
+		case JOIN_ANTI:
+			*merged_ub = left_ub;
+			*merged_lb = left_lb;
+			break;
+
+		case JOIN_INNER:
+		case JOIN_SEMI:
+			if (partition_range_bound_cmp(partnatts, partsupfuncs,
+										  partcollations, left_ub,
+										  right_ub) < 0)
+				*merged_ub = left_ub;
+			else
+				*merged_ub = right_ub;
+
+			if (partition_range_bound_cmp(partnatts, partsupfuncs,
+										  partcollations, left_lb,
+										  right_lb) > 0)
+				*merged_lb = left_lb;
+			else
+				*merged_lb = right_lb;
+			break;
+
+		case JOIN_FULL:
+			if (partition_range_bound_cmp(partnatts, partsupfuncs,
+										  partcollations, left_ub,
+										  right_ub) > 0)
+				*merged_ub = left_ub;
+			else
+				*merged_ub = right_ub;
+
+			if (partition_range_bound_cmp(partnatts, partsupfuncs,
+										  partcollations, left_lb,
+										  right_lb) < 0)
+				*merged_lb = left_lb;
+			else
+				*merged_lb = right_lb;
+			break;
+
+		default:
+			elog(ERROR, "unexpected join type %d", jointype);
+	}
+
+	return;
+}
+
+/*
+ * Add the lower bound of the next range to the list of bounds, if the lower
+ * bound is higher or equal to the previous upper bound. If successful return
+ * true, otherwise false.
+ */
+static bool
+partition_range_merge_next_lb(int partnatts, FmgrInfo *partsupfuncs,
+							  Oid *partcollations, Datum *next_lb_datums,
+							  PartitionRangeDatumKind *next_lb_kind,
+							  List **merged_datums, List **merged_kinds,
+							  List **merged_indexes)
+{
+	int			cmpval;
+
+	if (!*merged_datums)
+	{
+		Assert(!*merged_kinds && !*merged_indexes);
+		cmpval = 1;
+	}
+	else
+	{
+		PartitionRangeBound	prev_ub;
+
+		prev_ub.datums = llast(*merged_datums);
+		prev_ub.kind = llast(*merged_kinds);
+		prev_ub.lower = false;
+
+		cmpval = partition_rbound_cmp(partnatts, partsupfuncs, partcollations,
+									  next_lb_datums, next_lb_kind, false,
+									  &prev_ub);
+	}
+
+	/*
+	 * The lower bound is lower than the last upper bound, thus does not fit
+	 * the bounds created so far and hence can not be merged with the existing
+	 * bounds.
+	 */
+	if (cmpval < 0)
+		return false;
+
+	/*
+	 * Add bounds of the new merged partition. If the next lower bound is
+	 * higher than the last upper bound, add new range with index
+	 * corresponding to the lower bound as -1. If the merged lower bound
+	 * is same as the last merged upper bound, the last upper bound will be
+	 * reused as the lower bound of the next range.
+	 */
+	if (cmpval > 0)
+	{
+		*merged_datums = lappend(*merged_datums, next_lb_datums);
+		*merged_kinds = lappend(*merged_kinds, next_lb_kind);
+		*merged_indexes = lappend_int(*merged_indexes, -1);
+	}
+
+	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;
+}
+
+/*
+ * Allocate and initialize partition maps. We maintain four maps, two maps
+ * for each joining relation. pmap[i] gives the partition from the other
+ * relation which would join with ith partition of the given relation.
+ * Partition i from the given relation will join with partition pmap[i]
+ * from the other relation to produce partition mmap[i] of the join (merged
+ * partition).
+ *
+ * pmap[i] = -1 indicates that ith partition of a given relation does not
+ * have a matching partition from the other relation.
+ *
+ * mmap[i] = -1 indicates that ith partition of a given relation does not
+ * contribute to the join result. That can happen only when the given
+ * relation is the inner relation and it doesn't have a matching partition
+ * from the outer relation, hence pmap[i] should be -1.
+ *
+ * In case of an outer join, every partition of the outer join will appear
+ * in the join result, and thus has mmap[i] set for all i. But it's not
+ * necessary that every partition on the outer side will have a matching
+ * partition on the inner side. In such a case, we end up with pmap[i] = -1
+ * and mmap[i] != -1.
+ */
+
+/*
+ * 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)
+
+{
+	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;
+	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;
+
+	Assert(outer_bi->strategy == inner_bi->strategy &&
+		   outer_bi->strategy == PARTITION_STRATEGY_RANGE);
+
+	Assert(*outer_parts == NIL);
+	Assert(*inner_parts == NIL);
+
+	outer_maps = init_partition_map(outer_rel);
+	inner_maps = init_partition_map(inner_rel);
+
+	/*
+	 * Merge the ranges (partitions) from both sides. Every iteration compares
+	 * a pair of ranges, one from each side, advancing to the next range from
+	 * the side with smaller upper range bound. If upper bounds of ranges from
+	 * both sides match exactly, both the sides are advanced. For a given pair
+	 * of ranges, we decide whether the corresponding partition match or not.
+	 * 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;
+	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;
+
+		int			merged_index = -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. */
+
+		/* 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;
+
+		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);
+
+		/*
+		 * We run this loop till both the sides finish. This allows to avoid
+		 * duplicating code to handle the remaining partitions on the side
+		 * which finishes later. For that we set the comparison parameters
+		 * overlap, ub_cmpval and lb_cmpval in such a way that it appears as if
+		 * the side which finishes earlier has an extra partition with lower
+		 * and upper bounds higher than any other partition of the unfinished
+		 * side. That way we advance the partitions on that side till all of
+		 * them are  exhausted.
+		 */
+		if (finished_outer)
+		{
+			overlap = false;
+			ub_cmpval = 1;
+			lb_cmpval = 1;
+		}
+		else if (finished_inner)
+		{
+			overlap = false;
+			ub_cmpval = -1;
+			lb_cmpval = -1;
+		}
+		else
+			overlap = partition_range_cmp(partnatts, partsupfuncs, partcollations,
+										  &outer_lb, &outer_ub, &inner_lb,
+										  &inner_ub, &ub_cmpval, &lb_cmpval);
+
+		if (overlap)
+		{
+			/*
+			 * The rows from overlapping portion of ranges on both sides may
+			 * join, hence the corresponding pair of partitions form a joining
+			 * pair. Match them and produce the bounds of the joint partition
+			 * and its index by merging the bounds according to the type of
+			 * join.
+			 */
+			partition_range_merge(partnatts, partsupfuncs, partcollations,
+								  jointype, &outer_lb, &outer_ub, &inner_lb,
+								  &inner_ub, &merged_lb, &merged_ub);
+
+			merged_index = map_and_merge_partitions(outer_maps, inner_maps,
+													outer_part, inner_part,
+													&next_index);
+
+			if (merged_index < 0)
+			{
+				/* Failed to match the partitions. */
+				return NULL;
+			}
+
+			/*
+			 * If the ranges overlap but don't exactly match, a row from
+			 * non-overlapping portion of the range from one side of join may
+			 * find its join partner in the previous or next overlapping
+			 * partition or default partition on the other side , if such a
+			 * partition exists. All those cases, if true, will cause one
+			 * partition from that side to match at least two partitions on the
+			 * other side; a case that we do not support now. Previous
+			 * partition has been delt with in the previous iteration of this
+			 * loop, next partition will be delt in the next iteration. We will
+			 * deal with the default partition here.
+			 */
+			if ((lb_cmpval < 0 && inner_has_default) ||
+				/* Non-overlapping range on the lower side of outer range. */
+				(lb_cmpval > 0 && outer_has_default) ||
+				/* Non-overlapping range on the lower side of inner range. */
+				(ub_cmpval < 0 && outer_has_default) ||
+				/* Non-overlapping range on the upper side of inner range. */
+				(ub_cmpval > 0 && inner_has_default))
+				/* Non-overlapping range on the upper side of outer range. */
+				return NULL;
+		}
+
+		if (ub_cmpval == 0)
+		{
+			/* Upper bounds of both the ranges match. */
+			Assert(overlap);
+
+			/* Move to the next pair of partitions. */
+			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,
+															   inner_lb_index);
+		}
+		else if (ub_cmpval < 0)
+		{
+			/* Upper bound of inner range higher than that of the outer. */
+
+			if (overlap)
+			{
+				/* We have already dealt with overlapping ranges. */
+			}
+			else
+			{
+				/* A range missing from the inner side. */
+				bool		missing_side_outer;
+				bool		missing_side_inner;
+
+				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);
+			outer_lb_index = partition_range_get_next_lb_index(outer_bi,
+															   outer_lb_index);
+		}
+		else
+		{
+			Assert(ub_cmpval > 0);
+
+			/* Upper bound of outer range higher than that of the inner. */
+			if (overlap)
+			{
+				/* We have already dealt with overlapping ranges. */
+			}
+			else
+			{
+				/* A range missing from the outer side. */
+				bool		missing_side_outer;
+				bool		missing_side_inner;
+
+				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);
+			inner_lb_index = partition_range_get_next_lb_index(inner_bi,
+															   inner_lb_index);
+		}
+
+		if (merged_index < 0)
+		{
+			/* We didn't find a new merged partition. */
+			continue;
+		}
+
+		/*
+		 * We have a valid partition index for the next partition of join. The
+		 * partition should have valid range.
+		 */
+		Assert(merged_lb && merged_ub);
+
+		/* Try merging new lower bound with the last upper bound. */
+		if (!partition_range_merge_next_lb(partnatts, partsupfuncs,
+										   partcollations,
+										   merged_lb->datums,
+										   merged_lb->kind, &merged_datums,
+										   &merged_kinds, &merged_indexes))
+			return NULL;
+
+		/* Add upper bound with the merged partition index. */
+		merged_datums = lappend(merged_datums, merged_ub->datums);
+		merged_kinds = lappend(merged_kinds, merged_ub->kind);
+		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;
+
+	/* Use maps to match partition from the joining relations. */
+	generate_matching_part_pairs(outer_maps, inner_maps,
+								 outer_nparts, inner_nparts,
+								 jointype, next_index,
+								 outer_parts, inner_parts);
+
+	/* Craft a PartitionBoundInfo to return. */
+	if (*outer_parts && *inner_parts)
+	{
+		Assert(list_length(*outer_parts) == list_length(*inner_parts));
+		Assert(list_length(*outer_parts) == next_index);
+		merged_bounds = build_merged_partition_bounds(outer_bi->strategy,
+													  merged_datums,
+													  merged_indexes,
+													  merged_kinds,
+													  -1, default_index);
+	}
+
+	/* Free any memory we used in this function. */
+	list_free(merged_datums);
+	list_free(merged_indexes);
+	list_free(merged_kinds);
+
+	return merged_bounds;
+}
+
+/*
+ * partition_list_bounds_merge
+ *
+ * partition_bounds_merge()'s arm for list partitioned tables.
+ *
+ */
+static PartitionBoundInfo
+partition_list_bounds_merge(FmgrInfo *partsupfunc, Oid *partcollation,
+							RelOptInfo *outer_rel, RelOptInfo *inner_rel,
+							List **outer_parts, List **inner_parts,
+							JoinType jointype)
+{
+	PartitionMap *outer_maps = NULL;
+	PartitionMap *inner_maps = NULL;
+	int			cnto;
+	int			cnti;
+	List	   *merged_datums = NIL;
+	List	   *merged_indexes = NIL;
+	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;
+
+	Assert(*outer_parts == NIL);
+	Assert(*inner_parts == NIL);
+
+	Assert(outer_bi->strategy == inner_bi->strategy &&
+		   outer_bi->strategy == PARTITION_STRATEGY_LIST);
+
+	/* 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);
+
+	/*
+	 * Merge the list value datums from both sides. Every iteration compares a
+	 * pair of datums, one from each side, advancing to the next datum from the
+	 * side with smaller datum. If datums from both sides match exactly, both
+	 * the sides are advanced. For a given pair of datums, we decide whether
+	 * the corresponding partition match or not.
+	 */
+	cnto = cnti = 0;
+	while (cnto < outer_bi->ndatums || cnti < inner_bi->ndatums)
+	{
+		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;
+
+		/*
+		 * We run this loop till both the sides finish. This allows to avoid
+		 * duplicating code to handle the remaining datums on the side which
+		 * finishes later. For that we set the comparison parameter cmpval in
+		 * such a way that it appears as if the side which finishes earlier has
+		 * an extra datum higher than any other datum on the unfinished side.
+		 * That way we advance the datums on the unfinished side till all of
+		 * 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)
+			cmpval = -1;
+		else
+		{
+			/* Every list datum should map to a valid partition index. */
+			Assert(o_index >= 0 && i_index >= 0 &&
+				   odatums != NULL && idatums != NULL);
+
+			cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
+													 partcollation[0],
+													 odatums[0], idatums[0]));
+		}
+
+		if (cmpval == 0)
+		{
+			/*
+			 * Datums match. Rows on either side with these datums as partition
+			 * key value will join and will be part of the partition of the
+			 * join result produced by joining the corresponding partitions.
+			 * Match the corresponding partitions and if successful, add the
+			 * datum to the list of merged datums with index of merged
+			 * partition containing it.
+			 */
+			merged_datum = odatums;
+			merged_index = map_and_merge_partitions(outer_maps, inner_maps,
+													o_index, i_index,
+													&next_index);
+
+			if (merged_index < 0)
+				return NULL;
+
+			/* Move to the next pair of bounds. */
+			cnto++;
+			cnti++;
+		}
+		else if (cmpval < 0)
+		{
+			bool		missing_side_outer;
+			bool		missing_side_inner;
+
+			/* 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;
+
+			/* 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);
+
+			/* 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;
+
+			/* Move to the next datum on the right side. */
+			Assert(!finished_inner);
+			cnti++;
+		}
+
+		/*
+		 * Add the list value with appropriate index in the list of datums, if
+		 * we have associated a partition with this list value.
+		 */
+		if (merged_index >= 0)
+		{
+			merged_indexes = lappend_int(merged_indexes, merged_index);
+			merged_datums = lappend(merged_datums, merged_datum);
+		}
+	}
+
+	if (!merge_null_partitions(outer_bi, inner_bi,
+							   outer_maps, inner_maps,
+							   jointype, &next_index, &null_index,
+							   &default_index))
+		return NULL;
+
+	if (!merge_default_partitions(outer_bi, inner_bi,
+								  outer_maps, inner_maps,
+								  jointype, &next_index,
+								  &default_index))
+		return NULL;
+
+	/* Use maps to match partition from the joining relations. */
+	generate_matching_part_pairs(outer_maps, inner_maps,
+								 outer_nparts, inner_nparts,
+								 jointype, next_index,
+								 outer_parts, inner_parts);
+
+	/* Craft a PartitionBoundInfo to return. */
+	if (*outer_parts && *inner_parts)
+	{
+		Assert(list_length(*outer_parts) == list_length(*inner_parts));
+		Assert(list_length(*outer_parts) == next_index);
+		merged_bounds = build_merged_partition_bounds(outer_bi->strategy,
+													  merged_datums,
+													  merged_indexes, NIL,
+													  null_index, default_index);
+	}
+
+	/* Free up all extra memory before returning from this function. */
+	list_free(merged_datums);
+	list_free(merged_indexes);
+
+	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
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+static int
+map_and_merge_partitions(PartitionMap *partmaps1, PartitionMap *partmaps2,
+						 int index1, int index2, int *next_index)
+{
+	PartitionMap 	*partmap1 = &partmaps1[index1];
+	PartitionMap 	*partmap2 = &partmaps2[index2];
+	int				merged_index;
+
+	/*
+	 * If both the partitions are not mapped to each other, update the
+	 * maps.
+	 */
+	if (partmap1->from < 0 && partmap2->from < 0)
+	{
+		partmap1->from = index2;
+		partmap2->from = index1;
+	}
+
+	/*
+	 * If the given to partitions map to each other, find the corresponding
+	 * merged partition index .
+	 */
+	if (partmap1->from == index2 && partmap2->from == index1)
+	{
+		/*
+		 * If both the partitions are mapped to the same merged partition, get
+		 * the index of merged partition.
+		 */
+		if (partmap1->to == partmap2->to)
+		{
+			merged_index = partmap1->to;
+
+			/*
+			 * If the given two partitions do not have a merged partition
+			 * associated with them, allocate a new merged partition.
+			 */
+			if (merged_index < 0)
+			{
+				merged_index = *next_index;
+				*next_index = *next_index + 1;
+				partmap1->to = merged_index;
+				partmap2->to = merged_index;
+			}
+		}
+
+		/*
+		 * 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.
+		 */
+		else if (partmap1->to >= 0 && partmap2->to < 0)
+		{
+			partmap2->to = partmap1->to;
+			merged_index = partmap1->to;
+		}
+		else if (partmap1->to < 0 && partmap2->to >= 0)
+		{
+			partmap1->to = partmap2->to;
+			merged_index = partmap2->to;
+		}
+		else
+		{
+			Assert(partmap1->to != partmap2->to &&
+				   partmap1->to >= 0 && partmap2->to >= 0);
+
+			/*
+			 * 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;
+		}
+	}
+	else
+	{
+		/*
+		 * 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).
+		 */
+		merged_index = -1;
+	}
+
+	return merged_index;
+}
+
+/*
+ * 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.
+ *
+ * nparts is the number of merged partitions.
+ *
+ * If successful, the pairs of partitions are returned as two separate lists,
+ * parts1 and parts2 resp., one for each side. Otherwise, those lists will be
+ * set to NIL.
+ */
+static void
+generate_matching_part_pairs(PartitionMap *partmaps1, PartitionMap *partmaps2,
+							 int nparts1, int nparts2,
+							 JoinType jointype, int nparts,
+							 List **matched_parts1, List **matched_parts2)
+{
+	bool		merged = true;
+	int		   *matching1,
+			   *matching2;
+	int 		i;
+	int			max_nparts;
+
+	*matched_parts1 = NIL;
+	*matched_parts2 = NIL;
+
+	matching1 = (int *) palloc(sizeof(int) * nparts),
+	matching2 = (int *) palloc(sizeof(int) * nparts);
+	for (i = 0; i < nparts; i++)
+		matching1[i] = matching2[i] = -1;
+
+	/* Set pairs of matching partitions. */
+	max_nparts = Max(nparts1, nparts2);
+	for (i = 0; i < max_nparts; i++)
+	{
+		if (i < nparts1)
+		{
+			PartitionMap outer_map = partmaps1[i];
+
+			if (outer_map.to >= 0)
+			{
+				Assert(outer_map.to < nparts);
+				matching1[outer_map.to] = i;
+			}
+		}
+
+		if (i < nparts2)
+		{
+			PartitionMap inner_map = partmaps2[i];
+
+			if (inner_map.to >= 0)
+			{
+				Assert(inner_map.to < nparts);
+				matching2[inner_map.to] = i;
+			}
+		}
+	}
+
+	/*
+	 * 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];
+		int			part2 = matching2[i];
+
+		/* 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);
+	}
+
+	pfree(matching1);
+	pfree(matching2);
+
+	if (!merged)
+	{
+		list_free(*matched_parts1);
+		list_free(*matched_parts2);
+		*matched_parts1 = NIL;
+		*matched_parts2 = NIL;
+	}
+}
+
+static PartitionBoundInfo
+build_merged_partition_bounds(char strategy, List *merged_datums,
+							  List *merged_indexes, List *merged_kinds,
+							  int null_index, int default_index)
+{
+	int			cnt;
+	PartitionBoundInfo merged_bounds;
+	ListCell   *lc;
+
+	/* We expect the same number of elements in datums and indexes lists. */
+	Assert(list_length(merged_datums) == list_length(merged_indexes));
+
+	merged_bounds = (PartitionBoundInfo) palloc(sizeof(PartitionBoundInfoData));
+	merged_bounds->strategy = strategy;
+	merged_bounds->ndatums = list_length(merged_datums);
+
+	if (strategy == PARTITION_STRATEGY_RANGE)
+	{
+		Assert(list_length(merged_datums) == list_length(merged_kinds));
+		merged_bounds->kind =
+			(PartitionRangeDatumKind **) palloc(sizeof(PartitionRangeDatumKind *) *
+												list_length(merged_kinds));
+		cnt = 0;
+		foreach(lc, merged_kinds)
+			merged_bounds->kind[cnt++] = lfirst(lc);
+
+		/* There are ndatums+1 indexes in case of range partitions */
+		merged_indexes = lappend_int(merged_indexes, -1);
+	}
+	else
+		merged_bounds->kind = NULL;
+
+	cnt = 0;
+	merged_bounds->datums = (Datum **) palloc(sizeof(Datum *) *
+											  list_length(merged_datums));
+	foreach(lc, merged_datums)
+		merged_bounds->datums[cnt++] = lfirst(lc);
+
+	merged_bounds->indexes = (int *) palloc(sizeof(int) *
+											list_length(merged_indexes));
+	cnt = 0;
+	foreach(lc, merged_indexes)
+		merged_bounds->indexes[cnt++] = lfirst_int(lc);
+
+	merged_bounds->null_index = null_index;
+	merged_bounds->default_index = default_index;
+
+	return merged_bounds;
+}
+
+/*
+ * Merge default partitions from both sides, if any, and assign the default
+ * partition for the join result, if necessary.
+ *
+ * If both the relations have default partitions, try mapping those to each
+ * other. If the mapping succeeds corresponding merged partition will act as
+ * the default partition of the join result.
+ *
+ * If inner side of the join has default but not the outer side, rows in it
+ * won't appear in the join result. So don't create a default partition. If
+ * outer side of the join has default but not the inner side, rows in it will
+ * 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)
+{
+	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;
+
+	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 (jointype == JOIN_LEFT || jointype == JOIN_FULL ||
+			jointype == JOIN_ANTI)
+		{
+			if (outer_default_map->to < 0)
+			{
+				outer_default_map->to = *next_index;
+				*next_index = *next_index + 1;
+				Assert(*default_index < 0);
+				*default_index = outer_default_map->to;
+			}
+			else
+				Assert(*default_index == outer_default_map->to);
+		}
+		else
+			Assert(*default_index < 0);
+	}
+	else if (outer_default_map == NULL && inner_default_map != NULL)
+	{
+		if (jointype == JOIN_FULL)
+		{
+			if (inner_default_map->to < 0)
+			{
+				inner_default_map->to = *next_index;
+				*next_index = *next_index + 1;
+				Assert(*default_index < 0);
+				*default_index = inner_default_map->to;
+			}
+			else
+				Assert(*default_index == inner_default_map->to);
+		}
+		else
+			Assert(*default_index < 0);
+	}
+	else
+	{
+		Assert(outer_has_default && inner_has_default);
+
+		*default_index = map_and_merge_partitions(outer_maps, inner_maps,
+												  outer_default, inner_default,
+												  next_index);
+
+		if (*default_index < 0)
+			merged = false;
+	}
+
+	return merged;
+}
+
+/*
+ * merge_null_partitions
+ *
+ * Merge NULL partitions, i.e. a partition that can hold NULL values for a list
+ * partitioned table, if any. Find the index of merged partition to which the
+ * NULL values would belong in the join result. If one joining relation has a
+ * NULL partition but not the other, try matching it with the default partition
+ * from the other relation since the default partition may have rows with NULL
+ * partition key. We can eliminate a NULL partition when it appears only on the
+ * inner side of the join and the outer side doesn't have a default partition.
+ *
+ * When the equality operator used for join is strict, two NULL values will not
+ * be considered as equal, and thus a NULL partition can be eliminated for an
+ * inner join. But we don't check the strictness operator here.
+ */
+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)
+{
+	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;
+
+	if (!outer_has_null && !inner_has_null)
+		Assert(*null_index < 0);
+	else 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 join.
+		 */
+		if (missing_side_inner)
+			*null_index = outer_maps[outer_ni].to;
+	}
+	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
+		 * the join.
+		 */
+		if (missing_side_inner)
+			*null_index = inner_maps[inner_ni].to;
+	}
+	else
+	{
+		/* Both the relations have NULL partitions, try merging them. */
+		*null_index = map_and_merge_partitions(outer_maps,
+											   inner_maps,
+											   outer_ni,
+											   inner_ni,
+											   next_index);
+		if (*null_index < 0)
+			merged = false;
+	}
+
+	return merged;
+}
diff --git a/src/include/partitioning/partbounds.h b/src/include/partitioning/partbounds.h
index 8585c29c92..636087d83e 100644
--- a/src/include/partitioning/partbounds.h
+++ b/src/include/partitioning/partbounds.h
@@ -16,6 +16,7 @@
 #include "nodes/pg_list.h"
 #include "partitioning/partdefs.h"
 #include "utils/relcache.h"
+struct RelOptInfo;				/* avoid including pathnodes.h here */
 
 
 /*
@@ -109,5 +110,11 @@ extern int	partition_range_datum_bsearch(FmgrInfo *partsupfunc,
 										  int nvalues, Datum *values, bool *is_equal);
 extern int	partition_hash_bsearch(PartitionBoundInfo boundinfo,
 								   int modulus, int remainder);
+extern PartitionBoundInfo partition_bounds_merge(int partnatts,
+					   int16 *parttyplen, bool *parttypbyval,
+					   FmgrInfo *partsupfunc, Oid *partcollation,
+					   struct RelOptInfo *outer_rel, struct RelOptInfo *inner_rel,
+					   JoinType jointype,
+					   List **outer_parts, List **inner_parts);
 
 #endif							/* PARTBOUNDS_H */
diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out
index 1296edcdae..d26be918aa 100644
--- a/src/test/regress/expected/partition_join.out
+++ b/src/test/regress/expected/partition_join.out
@@ -8,59 +8,86 @@ SET enable_partitionwise_join to true;
 -- partitioned by a single column
 --
 CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a);
+CREATE TABLE prt1_p0 PARTITION OF prt1 FOR VALUES FROM (MINVALUE) TO (0);
 CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250);
 CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600);
 CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500);
-INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 2 = 0;
+CREATE TABLE prt1_p4 PARTITION OF prt1 FOR VALUES FROM (600) TO (800);
+INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 2 = 0;
+CREATE INDEX iprt1_p0_a on prt1_p0(a);
 CREATE INDEX iprt1_p1_a on prt1_p1(a);
 CREATE INDEX iprt1_p2_a on prt1_p2(a);
 CREATE INDEX iprt1_p3_a on prt1_p3(a);
+CREATE INDEX iprt1_p4_a on prt1_p4(a);
 ANALYZE prt1;
+-- prt2 have missing starting MINVALUE to -250 range and
+-- extra bounds from 800 to MAXVALUE
 CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b);
+CREATE TABLE prt2_p0 PARTITION OF prt2 FOR VALUES FROM (-250) TO (0);
 CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250);
 CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500);
 CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600);
-INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 3 = 0;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (MAXVALUE);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 3 = 0;
+CREATE INDEX iprt2_p0_b on prt2_p0(b);
 CREATE INDEX iprt2_p1_b on prt2_p1(b);
 CREATE INDEX iprt2_p2_b on prt2_p2(b);
 CREATE INDEX iprt2_p3_b on prt2_p3(b);
+CREATE INDEX iprt2_p4_b on prt2_p4(b);
 ANALYZE prt2;
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
 -- inner join
 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;
-                    QUERY PLAN                    
---------------------------------------------------
+                          QUERY PLAN                           
+---------------------------------------------------------------
  Sort
    Sort Key: t1.a
    ->  Append
          ->  Hash Join
                Hash Cond: (t2.b = t1.a)
-               ->  Seq Scan on prt2_p1 t2
+               ->  Seq Scan on prt2_p0 t2
                ->  Hash
-                     ->  Seq Scan on prt1_p1 t1
+                     ->  Seq Scan on prt1_p0 t1
                            Filter: (b = 0)
          ->  Hash Join
                Hash Cond: (t2_1.b = t1_1.a)
-               ->  Seq Scan on prt2_p2 t2_1
+               ->  Seq Scan on prt2_p1 t2_1
                ->  Hash
-                     ->  Seq Scan on prt1_p2 t1_1
+                     ->  Seq Scan on prt1_p1 t1_1
                            Filter: (b = 0)
          ->  Hash Join
                Hash Cond: (t2_2.b = t1_2.a)
-               ->  Seq Scan on prt2_p3 t2_2
+               ->  Seq Scan on prt2_p2 t2_2
                ->  Hash
-                     ->  Seq Scan on prt1_p3 t1_2
+                     ->  Seq Scan on prt1_p2 t1_2
                            Filter: (b = 0)
-(21 rows)
+         ->  Nested Loop
+               ->  Seq Scan on prt1_p3 t1_3
+                     Filter: (b = 0)
+               ->  Index Scan using iprt2_p3_b on prt2_p3 t2_3
+                     Index Cond: (b = t1_3.a)
+         ->  Hash Join
+               Hash Cond: (t2_4.b = t1_4.a)
+               ->  Seq Scan on prt2_p4 t2_4
+               ->  Hash
+                     ->  Seq Scan on prt1_p4 t1_4
+                           Filter: (b = 0)
+(32 rows)
 
 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;
-  a  |  c   |  b  |  c   
------+------+-----+------
-   0 | 0000 |   0 | 0000
- 150 | 0150 | 150 | 0150
- 300 | 0300 | 300 | 0300
- 450 | 0450 | 450 | 0450
-(4 rows)
+  a   |   c   |  b   |   c   
+------+-------+------+-------
+ -150 | -0150 | -150 | -0150
+    0 | 0000  |    0 | 0000
+  150 | 0150  |  150 | 0150
+  300 | 0300  |  300 | 0300
+  450 | 0450  |  450 | 0450
+  600 | 0600  |  600 | 0600
+  750 | 0750  |  750 | 0750
+(7 rows)
 
 -- left outer join, with whole-row reference; partitionwise join does not apply
 EXPLAIN (COSTS OFF)
@@ -72,35 +99,50 @@ SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER
    ->  Hash Right Join
          Hash Cond: (t2.b = t1.a)
          ->  Append
-               ->  Seq Scan on prt2_p1 t2
-               ->  Seq Scan on prt2_p2 t2_1
-               ->  Seq Scan on prt2_p3 t2_2
+               ->  Seq Scan on prt2_p0 t2
+               ->  Seq Scan on prt2_p1 t2_1
+               ->  Seq Scan on prt2_p2 t2_2
+               ->  Seq Scan on prt2_p3 t2_3
+               ->  Seq Scan on prt2_p4 t2_4
          ->  Hash
                ->  Append
-                     ->  Seq Scan on prt1_p1 t1
+                     ->  Seq Scan on prt1_p0 t1
                            Filter: (b = 0)
-                     ->  Seq Scan on prt1_p2 t1_1
+                     ->  Seq Scan on prt1_p1 t1_1
                            Filter: (b = 0)
-                     ->  Seq Scan on prt1_p3 t1_2
+                     ->  Seq Scan on prt1_p2 t1_2
                            Filter: (b = 0)
-(16 rows)
+                     ->  Seq Scan on prt1_p3 t1_3
+                           Filter: (b = 0)
+                     ->  Seq Scan on prt1_p4 t1_4
+                           Filter: (b = 0)
+(22 rows)
 
 SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;
-      t1      |      t2      
---------------+--------------
- (0,0,0000)   | (0,0,0000)
- (50,0,0050)  | 
- (100,0,0100) | 
- (150,0,0150) | (0,150,0150)
- (200,0,0200) | 
- (250,0,0250) | 
- (300,0,0300) | (0,300,0300)
- (350,0,0350) | 
- (400,0,0400) | 
- (450,0,0450) | (0,450,0450)
- (500,0,0500) | 
- (550,0,0550) | 
-(12 rows)
+       t1       |       t2       
+----------------+----------------
+ (-250,0,-0250) | 
+ (-200,0,-0200) | 
+ (-150,0,-0150) | (0,-150,-0150)
+ (-100,0,-0100) | 
+ (-50,0,-0050)  | 
+ (0,0,0000)     | (0,0,0000)
+ (50,0,0050)    | 
+ (100,0,0100)   | 
+ (150,0,0150)   | (0,150,0150)
+ (200,0,0200)   | 
+ (250,0,0250)   | 
+ (300,0,0300)   | (0,300,0300)
+ (350,0,0350)   | 
+ (400,0,0400)   | 
+ (450,0,0450)   | (0,450,0450)
+ (500,0,0500)   | 
+ (550,0,0550)   | 
+ (600,0,0600)   | (0,600,0600)
+ (650,0,0650)   | 
+ (700,0,0700)   | 
+ (750,0,0750)   | (0,750,0750)
+(21 rows)
 
 -- right outer join
 EXPLAIN (COSTS OFF)
@@ -112,35 +154,53 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHE
    ->  Append
          ->  Hash Right Join
                Hash Cond: (t1.a = t2.b)
-               ->  Seq Scan on prt1_p1 t1
+               ->  Seq Scan on prt1_p0 t1
                ->  Hash
-                     ->  Seq Scan on prt2_p1 t2
+                     ->  Seq Scan on prt2_p0 t2
                            Filter: (a = 0)
          ->  Hash Right Join
                Hash Cond: (t1_1.a = t2_1.b)
-               ->  Seq Scan on prt1_p2 t1_1
+               ->  Seq Scan on prt1_p1 t1_1
+               ->  Hash
+                     ->  Seq Scan on prt2_p1 t2_1
+                           Filter: (a = 0)
+         ->  Hash Right Join
+               Hash Cond: (t1_2.a = t2_2.b)
+               ->  Seq Scan on prt1_p2 t1_2
                ->  Hash
-                     ->  Seq Scan on prt2_p2 t2_1
+                     ->  Seq Scan on prt2_p2 t2_2
                            Filter: (a = 0)
          ->  Nested Loop Left Join
-               ->  Seq Scan on prt2_p3 t2_2
+               ->  Seq Scan on prt2_p3 t2_3
                      Filter: (a = 0)
-               ->  Index Scan using iprt1_p3_a on prt1_p3 t1_2
-                     Index Cond: (a = t2_2.b)
-(20 rows)
+               ->  Index Scan using iprt1_p3_a on prt1_p3 t1_3
+                     Index Cond: (a = t2_3.b)
+         ->  Hash Right Join
+               Hash Cond: (t1_4.a = t2_4.b)
+               ->  Seq Scan on prt1_p4 t1_4
+               ->  Hash
+                     ->  Seq Scan on prt2_p4 t2_4
+                           Filter: (a = 0)
+(32 rows)
 
 SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b;
-  a  |  c   |  b  |  c   
------+------+-----+------
-   0 | 0000 |   0 | 0000
- 150 | 0150 | 150 | 0150
- 300 | 0300 | 300 | 0300
- 450 | 0450 | 450 | 0450
-     |      |  75 | 0075
-     |      | 225 | 0225
-     |      | 375 | 0375
-     |      | 525 | 0525
-(8 rows)
+  a   |   c   |  b   |   c   
+------+-------+------+-------
+ -150 | -0150 | -150 | -0150
+    0 | 0000  |    0 | 0000
+  150 | 0150  |  150 | 0150
+  300 | 0300  |  300 | 0300
+  450 | 0450  |  450 | 0450
+  600 | 0600  |  600 | 0600
+  750 | 0750  |  750 | 0750
+      |       | -225 | -0225
+      |       |  -75 | -0075
+      |       |   75 | 0075
+      |       |  225 | 0225
+      |       |  375 | 0375
+      |       |  525 | 0525
+      |       |  675 | 0675
+(14 rows)
 
 -- full outer join, with placeholder vars
 EXPLAIN (COSTS OFF)
@@ -148,8 +208,16 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0)
                             QUERY PLAN                            
 ------------------------------------------------------------------
  Sort
-   Sort Key: prt1_p1.a, prt2_p1.b
+   Sort Key: prt1_p0.a, prt2_p0.b
    ->  Append
+         ->  Hash Full Join
+               Hash Cond: (prt1_p0.a = prt2_p0.b)
+               Filter: (((50) = prt1_p0.a) OR ((75) = prt2_p0.b))
+               ->  Seq Scan on prt1_p0
+                     Filter: (b = 0)
+               ->  Hash
+                     ->  Seq Scan on prt2_p0
+                           Filter: (a = 0)
          ->  Hash Full Join
                Hash Cond: (prt1_p1.a = prt2_p1.b)
                Filter: (((50) = prt1_p1.a) OR ((75) = prt2_p1.b))
@@ -174,7 +242,15 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0)
                ->  Hash
                      ->  Seq Scan on prt2_p3
                            Filter: (a = 0)
-(27 rows)
+         ->  Hash Full Join
+               Hash Cond: (prt1_p4.a = prt2_p4.b)
+               Filter: (((50) = prt1_p4.a) OR ((75) = prt2_p4.b))
+               ->  Seq Scan on prt1_p4
+                     Filter: (b = 0)
+               ->  Hash
+                     ->  Seq Scan on prt2_p4
+                           Filter: (a = 0)
+(43 rows)
 
 SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.a OR t2.phv = t2.b ORDER BY t1.a, t2.b;
  a  |  c   | b  |  c   
@@ -211,35 +287,44 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JO
                         QUERY PLAN                         
 -----------------------------------------------------------
  Sort
-   Sort Key: prt1_p1.a, prt2_p2.b
+   Sort Key: prt1_p0.a, prt2_p2.b
    ->  Hash Right Join
-         Hash Cond: (prt2_p2.b = prt1_p1.a)
+         Hash Cond: (prt2_p2.b = prt1_p0.a)
          ->  Append
                ->  Seq Scan on prt2_p2
                      Filter: (b > 250)
                ->  Seq Scan on prt2_p3
                      Filter: (b > 250)
+               ->  Seq Scan on prt2_p4
+                     Filter: (b > 250)
          ->  Hash
                ->  Append
+                     ->  Seq Scan on prt1_p0
+                           Filter: ((a < 450) AND (b = 0))
                      ->  Seq Scan on prt1_p1
                            Filter: ((a < 450) AND (b = 0))
                      ->  Seq Scan on prt1_p2
                            Filter: ((a < 450) AND (b = 0))
-(15 rows)
+(19 rows)
 
 SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;
-  a  |  c   |  b  |  c   
------+------+-----+------
-   0 | 0000 |     | 
-  50 | 0050 |     | 
- 100 | 0100 |     | 
- 150 | 0150 |     | 
- 200 | 0200 |     | 
- 250 | 0250 |     | 
- 300 | 0300 | 300 | 0300
- 350 | 0350 |     | 
- 400 | 0400 |     | 
-(9 rows)
+  a   |   c   |  b  |  c   
+------+-------+-----+------
+ -250 | -0250 |     | 
+ -200 | -0200 |     | 
+ -150 | -0150 |     | 
+ -100 | -0100 |     | 
+  -50 | -0050 |     | 
+    0 | 0000  |     | 
+   50 | 0050  |     | 
+  100 | 0100  |     | 
+  150 | 0150  |     | 
+  200 | 0200  |     | 
+  250 | 0250  |     | 
+  300 | 0300  | 300 | 0300
+  350 | 0350  |     | 
+  400 | 0400  |     | 
+(14 rows)
 
 -- Currently we can't do partitioned join if nullable-side partitions are pruned
 EXPLAIN (COSTS OFF)
@@ -247,11 +332,13 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JO
                       QUERY PLAN                      
 ------------------------------------------------------
  Sort
-   Sort Key: prt1_p1.a, prt2_p2.b
+   Sort Key: prt1_p0.a, prt2_p2.b
    ->  Hash Full Join
-         Hash Cond: (prt1_p1.a = prt2_p2.b)
-         Filter: ((prt1_p1.b = 0) OR (prt2_p2.a = 0))
+         Hash Cond: (prt1_p0.a = prt2_p2.b)
+         Filter: ((prt1_p0.b = 0) OR (prt2_p2.a = 0))
          ->  Append
+               ->  Seq Scan on prt1_p0
+                     Filter: (a < 450)
                ->  Seq Scan on prt1_p1
                      Filter: (a < 450)
                ->  Seq Scan on prt1_p2
@@ -262,64 +349,147 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JO
                            Filter: (b > 250)
                      ->  Seq Scan on prt2_p3
                            Filter: (b > 250)
-(16 rows)
+                     ->  Seq Scan on prt2_p4
+                           Filter: (b > 250)
+(20 rows)
 
 SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 OR t2.a = 0 ORDER BY t1.a, t2.b;
-  a  |  c   |  b  |  c   
------+------+-----+------
-   0 | 0000 |     | 
-  50 | 0050 |     | 
- 100 | 0100 |     | 
- 150 | 0150 |     | 
- 200 | 0200 |     | 
- 250 | 0250 |     | 
- 300 | 0300 | 300 | 0300
- 350 | 0350 |     | 
- 400 | 0400 |     | 
-     |      | 375 | 0375
-     |      | 450 | 0450
-     |      | 525 | 0525
-(12 rows)
+  a   |   c   |  b  |  c   
+------+-------+-----+------
+ -250 | -0250 |     | 
+ -200 | -0200 |     | 
+ -150 | -0150 |     | 
+ -100 | -0100 |     | 
+  -50 | -0050 |     | 
+    0 | 0000  |     | 
+   50 | 0050  |     | 
+  100 | 0100  |     | 
+  150 | 0150  |     | 
+  200 | 0200  |     | 
+  250 | 0250  |     | 
+  300 | 0300  | 300 | 0300
+  350 | 0350  |     | 
+  400 | 0400  |     | 
+      |       | 375 | 0375
+      |       | 450 | 0450
+      |       | 525 | 0525
+      |       | 600 | 0600
+      |       | 675 | 0675
+      |       | 750 | 0750
+(20 rows)
 
 -- Semi-join
 EXPLAIN (COSTS OFF)
 SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a;
-                    QUERY PLAN                    
---------------------------------------------------
+                          QUERY PLAN                           
+---------------------------------------------------------------
  Sort
    Sort Key: t1.a
    ->  Append
          ->  Hash Semi Join
                Hash Cond: (t1.a = t2.b)
-               ->  Seq Scan on prt1_p1 t1
+               ->  Seq Scan on prt1_p0 t1
                      Filter: (b = 0)
                ->  Hash
-                     ->  Seq Scan on prt2_p1 t2
+                     ->  Seq Scan on prt2_p0 t2
                            Filter: (a = 0)
          ->  Hash Semi Join
                Hash Cond: (t1_1.a = t2_1.b)
-               ->  Seq Scan on prt1_p2 t1_1
+               ->  Seq Scan on prt1_p1 t1_1
                      Filter: (b = 0)
                ->  Hash
-                     ->  Seq Scan on prt2_p2 t2_1
+                     ->  Seq Scan on prt2_p1 t2_1
                            Filter: (a = 0)
-         ->  Nested Loop Semi Join
-               Join Filter: (t1_2.a = t2_2.b)
-               ->  Seq Scan on prt1_p3 t1_2
+         ->  Hash Semi Join
+               Hash Cond: (t1_2.a = t2_2.b)
+               ->  Seq Scan on prt1_p2 t1_2
                      Filter: (b = 0)
-               ->  Materialize
-                     ->  Seq Scan on prt2_p3 t2_2
+               ->  Hash
+                     ->  Seq Scan on prt2_p2 t2_2
                            Filter: (a = 0)
-(24 rows)
+         ->  Nested Loop
+               ->  HashAggregate
+                     Group Key: t2_3.b
+                     ->  Seq Scan on prt2_p3 t2_3
+                           Filter: (a = 0)
+               ->  Index Scan using iprt1_p3_a on prt1_p3 t1_3
+                     Index Cond: (a = t2_3.b)
+                     Filter: (b = 0)
+         ->  Hash Semi Join
+               Hash Cond: (t1_4.a = t2_4.b)
+               ->  Seq Scan on prt1_p4 t1_4
+                     Filter: (b = 0)
+               ->  Hash
+                     ->  Seq Scan on prt2_p4 t2_4
+                           Filter: (a = 0)
+(39 rows)
 
 SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a;
-  a  | b |  c   
------+---+------
-   0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
-(4 rows)
+  a   | b |   c   
+------+---+-------
+ -150 | 0 | -0150
+    0 | 0 | 0000
+  150 | 0 | 0150
+  300 | 0 | 0300
+  450 | 0 | 0450
+  600 | 0 | 0600
+  750 | 0 | 0750
+(7 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t1.b
+   ->  Append
+         ->  Hash Semi Join
+               Hash Cond: (t1.b = t2.a)
+               ->  Seq Scan on prt2_p0 t1
+                     Filter: (a = 0)
+               ->  Hash
+                     ->  Seq Scan on prt1_p0 t2
+                           Filter: (b = 0)
+         ->  Hash Semi Join
+               Hash Cond: (t1_1.b = t2_1.a)
+               ->  Seq Scan on prt2_p1 t1_1
+                     Filter: (a = 0)
+               ->  Hash
+                     ->  Seq Scan on prt1_p1 t2_1
+                           Filter: (b = 0)
+         ->  Hash Semi Join
+               Hash Cond: (t1_2.b = t2_2.a)
+               ->  Seq Scan on prt2_p2 t1_2
+                     Filter: (a = 0)
+               ->  Hash
+                     ->  Seq Scan on prt1_p2 t2_2
+                           Filter: (b = 0)
+         ->  Nested Loop Semi Join
+               Join Filter: (t1_3.b = t2_3.a)
+               ->  Seq Scan on prt2_p3 t1_3
+                     Filter: (a = 0)
+               ->  Seq Scan on prt1_p3 t2_3
+                     Filter: (b = 0)
+         ->  Hash Semi Join
+               Hash Cond: (t1_4.b = t2_4.a)
+               ->  Seq Scan on prt2_p4 t1_4
+                     Filter: (a = 0)
+               ->  Hash
+                     ->  Seq Scan on prt1_p4 t2_4
+                           Filter: (b = 0)
+(37 rows)
+
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b;
+ a |  b   |   c   
+---+------+-------
+ 0 | -150 | -0150
+ 0 |    0 | 0000
+ 0 |  150 | 0150
+ 0 |  300 | 0300
+ 0 |  450 | 0450
+ 0 |  600 | 0600
+ 0 |  750 | 0750
+(7 rows)
 
 -- Anti-join with aggregates
 EXPLAIN (COSTS OFF)
@@ -330,27 +500,82 @@ SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS
    ->  Append
          ->  Hash Anti Join
                Hash Cond: (t1.a = t2.b)
-               ->  Seq Scan on prt1_p1 t1
+               ->  Seq Scan on prt1_p0 t1
                ->  Hash
-                     ->  Seq Scan on prt2_p1 t2
+                     ->  Seq Scan on prt2_p0 t2
          ->  Hash Anti Join
                Hash Cond: (t1_1.a = t2_1.b)
-               ->  Seq Scan on prt1_p2 t1_1
+               ->  Seq Scan on prt1_p1 t1_1
                ->  Hash
-                     ->  Seq Scan on prt2_p2 t2_1
+                     ->  Seq Scan on prt2_p1 t2_1
          ->  Hash Anti Join
                Hash Cond: (t1_2.a = t2_2.b)
-               ->  Seq Scan on prt1_p3 t1_2
+               ->  Seq Scan on prt1_p2 t1_2
                ->  Hash
-                     ->  Seq Scan on prt2_p3 t2_2
-(17 rows)
+                     ->  Seq Scan on prt2_p2 t2_2
+         ->  Hash Anti Join
+               Hash Cond: (t1_3.a = t2_3.b)
+               ->  Seq Scan on prt1_p3 t1_3
+               ->  Hash
+                     ->  Seq Scan on prt2_p3 t2_3
+         ->  Hash Anti Join
+               Hash Cond: (t1_4.a = t2_4.b)
+               ->  Seq Scan on prt1_p4 t1_4
+               ->  Hash
+                     ->  Seq Scan on prt2_p4 t2_4
+(27 rows)
 
 SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b);
-  sum  |         avg          | sum  |         avg         
--------+----------------------+------+---------------------
- 60000 | 300.0000000000000000 | 2400 | 12.0000000000000000
+  sum  |         avg          | sum  |        avg         
+-------+----------------------+------+--------------------
+ 95550 | 273.0000000000000000 | 2200 | 6.2857142857142857
 (1 row)
 
+EXPLAIN (COSTS OFF)
+SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0;
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Append
+   ->  Nested Loop Anti Join
+         ->  Seq Scan on prt2_p0 t1
+               Filter: (a = 0)
+         ->  Index Only Scan using iprt1_p0_a on prt1_p0 t2
+               Index Cond: (a = t1.b)
+   ->  Hash Anti Join
+         Hash Cond: (t1_1.b = t2_1.a)
+         ->  Seq Scan on prt2_p1 t1_1
+               Filter: (a = 0)
+         ->  Hash
+               ->  Seq Scan on prt1_p1 t2_1
+   ->  Nested Loop Anti Join
+         ->  Seq Scan on prt2_p2 t1_2
+               Filter: (a = 0)
+         ->  Index Only Scan using iprt1_p2_a on prt1_p2 t2_2
+               Index Cond: (a = t1_2.b)
+   ->  Nested Loop Anti Join
+         ->  Seq Scan on prt2_p3 t1_3
+               Filter: (a = 0)
+         ->  Index Only Scan using iprt1_p3_a on prt1_p3 t2_3
+               Index Cond: (a = t1_3.b)
+   ->  Nested Loop Anti Join
+         ->  Seq Scan on prt2_p4 t1_4
+               Filter: (a = 0)
+         ->  Index Only Scan using iprt1_p4_a on prt1_p4 t2_4
+               Index Cond: (a = t1_4.b)
+(27 rows)
+
+SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0;
+  b   |   c   
+------+-------
+ -225 | -0225
+  -75 | -0075
+   75 | 0075
+  225 | 0225
+  375 | 0375
+  525 | 0525
+  675 | 0675
+(7 rows)
+
 -- lateral reference
 EXPLAIN (COSTS OFF)
 SELECT * FROM prt1 t1 LEFT JOIN LATERAL
@@ -362,49 +587,74 @@ SELECT * FROM prt1 t1 LEFT JOIN LATERAL
    Sort Key: t1.a
    ->  Append
          ->  Nested Loop Left Join
-               ->  Seq Scan on prt1_p1 t1
+               ->  Seq Scan on prt1_p0 t1
                      Filter: (b = 0)
                ->  Nested Loop
-                     ->  Index Only Scan using iprt1_p1_a on prt1_p1 t2
+                     ->  Index Only Scan using iprt1_p0_a on prt1_p0 t2
                            Index Cond: (a = t1.a)
-                     ->  Index Scan using iprt2_p1_b on prt2_p1 t3
+                     ->  Index Scan using iprt2_p0_b on prt2_p0 t3
                            Index Cond: (b = t2.a)
          ->  Nested Loop Left Join
-               ->  Seq Scan on prt1_p2 t1_1
+               ->  Seq Scan on prt1_p1 t1_1
                      Filter: (b = 0)
                ->  Nested Loop
-                     ->  Index Only Scan using iprt1_p2_a on prt1_p2 t2_1
+                     ->  Index Only Scan using iprt1_p1_a on prt1_p1 t2_1
                            Index Cond: (a = t1_1.a)
-                     ->  Index Scan using iprt2_p2_b on prt2_p2 t3_1
+                     ->  Index Scan using iprt2_p1_b on prt2_p1 t3_1
                            Index Cond: (b = t2_1.a)
          ->  Nested Loop Left Join
-               ->  Seq Scan on prt1_p3 t1_2
+               ->  Seq Scan on prt1_p2 t1_2
                      Filter: (b = 0)
                ->  Nested Loop
-                     ->  Index Only Scan using iprt1_p3_a on prt1_p3 t2_2
+                     ->  Index Only Scan using iprt1_p2_a on prt1_p2 t2_2
                            Index Cond: (a = t1_2.a)
-                     ->  Index Scan using iprt2_p3_b on prt2_p3 t3_2
+                     ->  Index Scan using iprt2_p2_b on prt2_p2 t3_2
                            Index Cond: (b = t2_2.a)
-(27 rows)
+         ->  Nested Loop Left Join
+               ->  Seq Scan on prt1_p3 t1_3
+                     Filter: (b = 0)
+               ->  Nested Loop
+                     ->  Index Only Scan using iprt1_p3_a on prt1_p3 t2_3
+                           Index Cond: (a = t1_3.a)
+                     ->  Index Scan using iprt2_p3_b on prt2_p3 t3_3
+                           Index Cond: (b = t2_3.a)
+         ->  Nested Loop Left Join
+               ->  Seq Scan on prt1_p4 t1_4
+                     Filter: (b = 0)
+               ->  Nested Loop
+                     ->  Index Only Scan using iprt1_p4_a on prt1_p4 t2_4
+                           Index Cond: (a = t1_4.a)
+                     ->  Index Scan using iprt2_p4_b on prt2_p4 t3_4
+                           Index Cond: (b = t2_4.a)
+(43 rows)
 
 SELECT * FROM prt1 t1 LEFT JOIN LATERAL
 			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.b) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
 			  ON t1.a = ss.t2a WHERE t1.b = 0 ORDER BY t1.a;
-  a  | b |  c   | t2a | t3a | least 
------+---+------+-----+-----+-------
-   0 | 0 | 0000 |   0 |   0 |     0
-  50 | 0 | 0050 |     |     |      
- 100 | 0 | 0100 |     |     |      
- 150 | 0 | 0150 | 150 |   0 |   150
- 200 | 0 | 0200 |     |     |      
- 250 | 0 | 0250 |     |     |      
- 300 | 0 | 0300 | 300 |   0 |   300
- 350 | 0 | 0350 |     |     |      
- 400 | 0 | 0400 |     |     |      
- 450 | 0 | 0450 | 450 |   0 |   450
- 500 | 0 | 0500 |     |     |      
- 550 | 0 | 0550 |     |     |      
-(12 rows)
+  a   | b |   c   | t2a  | t3a | least 
+------+---+-------+------+-----+-------
+ -250 | 0 | -0250 |      |     |      
+ -200 | 0 | -0200 |      |     |      
+ -150 | 0 | -0150 | -150 |   0 |  -150
+ -100 | 0 | -0100 |      |     |      
+  -50 | 0 | -0050 |      |     |      
+    0 | 0 | 0000  |    0 |   0 |     0
+   50 | 0 | 0050  |      |     |      
+  100 | 0 | 0100  |      |     |      
+  150 | 0 | 0150  |  150 |   0 |   150
+  200 | 0 | 0200  |      |     |      
+  250 | 0 | 0250  |      |     |      
+  300 | 0 | 0300  |  300 |   0 |   300
+  350 | 0 | 0350  |      |     |      
+  400 | 0 | 0400  |      |     |      
+  450 | 0 | 0450  |  450 |   0 |   450
+  500 | 0 | 0500  |      |     |      
+  550 | 0 | 0550  |      |     |      
+  600 | 0 | 0600  |  600 |   0 |   600
+  650 | 0 | 0650  |      |     |      
+  700 | 0 | 0700  |      |     |      
+  750 | 0 | 0750  |  750 |   0 |   750
+(21 rows)
 
 EXPLAIN (COSTS OFF)
 SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL
@@ -418,64 +668,95 @@ SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL
          Hash Cond: ((t1.c)::text = (t2.c)::text)
          Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
          ->  Append
-               ->  Seq Scan on prt1_p1 t1
-               ->  Seq Scan on prt1_p2 t1_1
-               ->  Seq Scan on prt1_p3 t1_2
+               ->  Seq Scan on prt1_p0 t1
+               ->  Seq Scan on prt1_p1 t1_1
+               ->  Seq Scan on prt1_p2 t1_2
+               ->  Seq Scan on prt1_p3 t1_3
+               ->  Seq Scan on prt1_p4 t1_4
          ->  Hash
                ->  Append
                      ->  Hash Join
                            Hash Cond: (t2.a = t3.b)
-                           ->  Seq Scan on prt1_p1 t2
+                           ->  Seq Scan on prt1_p0 t2
                            ->  Hash
-                                 ->  Seq Scan on prt2_p1 t3
+                                 ->  Seq Scan on prt2_p0 t3
                      ->  Hash Join
                            Hash Cond: (t2_1.a = t3_1.b)
-                           ->  Seq Scan on prt1_p2 t2_1
+                           ->  Seq Scan on prt1_p1 t2_1
                            ->  Hash
-                                 ->  Seq Scan on prt2_p2 t3_1
+                                 ->  Seq Scan on prt2_p1 t3_1
                      ->  Hash Join
                            Hash Cond: (t2_2.a = t3_2.b)
-                           ->  Seq Scan on prt1_p3 t2_2
+                           ->  Seq Scan on prt1_p2 t2_2
                            ->  Hash
-                                 ->  Seq Scan on prt2_p3 t3_2
-(26 rows)
+                                 ->  Seq Scan on prt2_p2 t3_2
+                     ->  Hash Join
+                           Hash Cond: (t2_3.a = t3_3.b)
+                           ->  Seq Scan on prt1_p3 t2_3
+                           ->  Hash
+                                 ->  Seq Scan on prt2_p3 t3_3
+                     ->  Hash Join
+                           Hash Cond: (t2_4.a = t3_4.b)
+                           ->  Seq Scan on prt1_p4 t2_4
+                           ->  Hash
+                                 ->  Seq Scan on prt2_p4 t3_4
+(38 rows)
 
 SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL
 			  (SELECT t2.a AS t2a, t3.a AS t3a, t2.b t2b, t2.c t2c, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
 			  ON t1.c = ss.t2c WHERE (t1.b + coalesce(ss.t2b, 0)) = 0 ORDER BY t1.a;
-  a  | t2a | t2c  
------+-----+------
-   0 |   0 | 0000
-  50 |     | 
- 100 |     | 
- 150 | 150 | 0150
- 200 |     | 
- 250 |     | 
- 300 | 300 | 0300
- 350 |     | 
- 400 |     | 
- 450 | 450 | 0450
- 500 |     | 
- 550 |     | 
-(12 rows)
+  a   | t2a  |  t2c  
+------+------+-------
+ -250 |      | 
+ -200 |      | 
+ -150 | -150 | -0150
+ -100 |      | 
+  -50 |      | 
+    0 |    0 | 0000
+   50 |      | 
+  100 |      | 
+  150 |  150 | 0150
+  200 |      | 
+  250 |      | 
+  300 |  300 | 0300
+  350 |      | 
+  400 |      | 
+  450 |  450 | 0450
+  500 |      | 
+  550 |      | 
+  600 |  600 | 0600
+  650 |      | 
+  700 |      | 
+  750 |  750 | 0750
+(21 rows)
 
 --
 -- partitioned by expression
 --
 CREATE TABLE prt1_e (a int, b int, c int) PARTITION BY RANGE(((a + b)/2));
+CREATE TABLE prt1_e_p0 PARTITION OF prt1_e FOR VALUES FROM (MINVALUE) TO (0);
 CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES FROM (0) TO (250);
 CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES FROM (250) TO (500);
 CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES FROM (500) TO (600);
+CREATE TABLE prt1_e_p4 PARTITION OF prt1_e FOR VALUES FROM (600) TO (MAXVALUE);
 INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i;
+INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 2) i;
+INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(600, 799, 2) i;
+CREATE INDEX iprt1_e_p0_ab2 on prt1_e_p1(((a+b)/2));
 CREATE INDEX iprt1_e_p1_ab2 on prt1_e_p1(((a+b)/2));
 CREATE INDEX iprt1_e_p2_ab2 on prt1_e_p2(((a+b)/2));
 CREATE INDEX iprt1_e_p3_ab2 on prt1_e_p3(((a+b)/2));
+CREATE INDEX iprt1_e_p4_ab2 on prt1_e_p1(((a+b)/2));
 ANALYZE prt1_e;
 CREATE TABLE prt2_e (a int, b int, c int) PARTITION BY RANGE(((b + a)/2));
+CREATE TABLE prt2_e_p0 PARTITION OF prt2_e FOR VALUES FROM (MINVALUE) TO (0);
 CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES FROM (0) TO (250);
 CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES FROM (250) TO (500);
 CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES FROM (500) TO (600);
+CREATE TABLE prt2_e_p4 PARTITION OF prt2_e FOR VALUES FROM (600) TO (MAXVALUE);
 INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i;
+INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 3) i;
+INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(600, 799, 3) i;
 ANALYZE prt2_e;
 EXPLAIN (COSTS OFF)
 SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.c = 0 ORDER BY t1.a, t2.b;
@@ -486,32 +767,49 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 =
    ->  Append
          ->  Hash Join
                Hash Cond: (((t2.b + t2.a) / 2) = ((t1.a + t1.b) / 2))
-               ->  Seq Scan on prt2_e_p1 t2
+               ->  Seq Scan on prt2_e_p0 t2
                ->  Hash
-                     ->  Seq Scan on prt1_e_p1 t1
+                     ->  Seq Scan on prt1_e_p0 t1
                            Filter: (c = 0)
          ->  Hash Join
-               Hash Cond: (((t2_1.b + t2_1.a) / 2) = ((t1_1.a + t1_1.b) / 2))
-               ->  Seq Scan on prt2_e_p2 t2_1
+               Hash Cond: (((t1_1.a + t1_1.b) / 2) = ((t2_1.b + t2_1.a) / 2))
+               ->  Seq Scan on prt1_e_p1 t1_1
+                     Filter: (c = 0)
                ->  Hash
-                     ->  Seq Scan on prt1_e_p2 t1_1
-                           Filter: (c = 0)
+                     ->  Seq Scan on prt2_e_p1 t2_1
          ->  Hash Join
                Hash Cond: (((t2_2.b + t2_2.a) / 2) = ((t1_2.a + t1_2.b) / 2))
-               ->  Seq Scan on prt2_e_p3 t2_2
+               ->  Seq Scan on prt2_e_p2 t2_2
                ->  Hash
-                     ->  Seq Scan on prt1_e_p3 t1_2
+                     ->  Seq Scan on prt1_e_p2 t1_2
                            Filter: (c = 0)
-(21 rows)
+         ->  Hash Join
+               Hash Cond: (((t2_3.b + t2_3.a) / 2) = ((t1_3.a + t1_3.b) / 2))
+               ->  Seq Scan on prt2_e_p3 t2_3
+               ->  Hash
+                     ->  Seq Scan on prt1_e_p3 t1_3
+                           Filter: (c = 0)
+         ->  Hash Join
+               Hash Cond: (((t2_4.b + t2_4.a) / 2) = ((t1_4.a + t1_4.b) / 2))
+               ->  Seq Scan on prt2_e_p4 t2_4
+               ->  Hash
+                     ->  Seq Scan on prt1_e_p4 t1_4
+                           Filter: (c = 0)
+(33 rows)
 
 SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.c = 0 ORDER BY t1.a, t2.b;
-  a  | c |  b  | c 
------+---+-----+---
-   0 | 0 |   0 | 0
- 150 | 0 | 150 | 0
- 300 | 0 | 300 | 0
- 450 | 0 | 450 | 0
-(4 rows)
+  a   | c |  b   | c 
+------+---+------+---
+ -250 | 0 | -250 | 0
+ -100 | 0 | -100 | 0
+    0 | 0 |    0 | 0
+    0 | 0 |    0 | 0
+  150 | 0 |  150 | 0
+  300 | 0 |  300 | 0
+  450 | 0 |  450 | 0
+  600 | 0 |  600 | 0
+  750 | 0 |  750 | 0
+(9 rows)
 
 --
 -- N-way join
@@ -524,154 +822,232 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t
    Sort Key: t1.a
    ->  Append
          ->  Nested Loop
-               Join Filter: (t1.a = ((t3.a + t3.b) / 2))
+               Join Filter: (t1.a = t2.b)
                ->  Hash Join
-                     Hash Cond: (t2.b = t1.a)
-                     ->  Seq Scan on prt2_p1 t2
+                     Hash Cond: (((t3.a + t3.b) / 2) = t1.a)
+                     ->  Seq Scan on prt1_e_p0 t3
                      ->  Hash
-                           ->  Seq Scan on prt1_p1 t1
+                           ->  Seq Scan on prt1_p0 t1
                                  Filter: (b = 0)
-               ->  Index Scan using iprt1_e_p1_ab2 on prt1_e_p1 t3
-                     Index Cond: (((a + b) / 2) = t2.b)
+               ->  Index Scan using iprt2_p0_b on prt2_p0 t2
+                     Index Cond: (b = ((t3.a + t3.b) / 2))
          ->  Nested Loop
                Join Filter: (t1_1.a = ((t3_1.a + t3_1.b) / 2))
                ->  Hash Join
                      Hash Cond: (t2_1.b = t1_1.a)
-                     ->  Seq Scan on prt2_p2 t2_1
+                     ->  Seq Scan on prt2_p1 t2_1
                      ->  Hash
-                           ->  Seq Scan on prt1_p2 t1_1
+                           ->  Seq Scan on prt1_p1 t1_1
                                  Filter: (b = 0)
-               ->  Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t3_1
+               ->  Index Scan using iprt1_e_p4_ab2 on prt1_e_p1 t3_1
                      Index Cond: (((a + b) / 2) = t2_1.b)
          ->  Nested Loop
                Join Filter: (t1_2.a = ((t3_2.a + t3_2.b) / 2))
                ->  Hash Join
                      Hash Cond: (t2_2.b = t1_2.a)
-                     ->  Seq Scan on prt2_p3 t2_2
+                     ->  Seq Scan on prt2_p2 t2_2
                      ->  Hash
-                           ->  Seq Scan on prt1_p3 t1_2
+                           ->  Seq Scan on prt1_p2 t1_2
                                  Filter: (b = 0)
-               ->  Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_2
+               ->  Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t3_2
                      Index Cond: (((a + b) / 2) = t2_2.b)
-(33 rows)
+         ->  Nested Loop
+               Join Filter: (t1_3.a = ((t3_3.a + t3_3.b) / 2))
+               ->  Nested Loop
+                     ->  Seq Scan on prt1_p3 t1_3
+                           Filter: (b = 0)
+                     ->  Index Scan using iprt2_p3_b on prt2_p3 t2_3
+                           Index Cond: (b = t1_3.a)
+               ->  Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_3
+                     Index Cond: (((a + b) / 2) = t2_3.b)
+         ->  Nested Loop
+               Join Filter: (t1_4.a = t2_4.b)
+               ->  Hash Join
+                     Hash Cond: (((t3_4.a + t3_4.b) / 2) = t1_4.a)
+                     ->  Seq Scan on prt1_e_p4 t3_4
+                     ->  Hash
+                           ->  Seq Scan on prt1_p4 t1_4
+                                 Filter: (b = 0)
+               ->  Index Scan using iprt2_p4_b on prt2_p4 t2_4
+                     Index Cond: (b = ((t3_4.a + t3_4.b) / 2))
+(52 rows)
 
 SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.b = 0 ORDER BY t1.a, t2.b;
-  a  |  c   |  b  |  c   | ?column? | c 
------+------+-----+------+----------+---
-   0 | 0000 |   0 | 0000 |        0 | 0
- 150 | 0150 | 150 | 0150 |      300 | 0
- 300 | 0300 | 300 | 0300 |      600 | 0
- 450 | 0450 | 450 | 0450 |      900 | 0
-(4 rows)
+  a   |   c   |  b   |   c   | ?column? | c 
+------+-------+------+-------+----------+---
+ -150 | -0150 | -150 | -0150 |     -300 | 0
+    0 | 0000  |    0 | 0000  |        0 | 0
+    0 | 0000  |    0 | 0000  |        0 | 0
+  150 | 0150  |  150 | 0150  |      300 | 0
+  300 | 0300  |  300 | 0300  |      600 | 0
+  450 | 0450  |  450 | 0450  |      900 | 0
+  600 | 0600  |  600 | 0600  |     1200 | 0
+  750 | 0750  |  750 | 0750  |     1500 | 0
+(8 rows)
 
 EXPLAIN (COSTS OFF)
 SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
-                          QUERY PLAN                          
---------------------------------------------------------------
+                             QUERY PLAN                              
+---------------------------------------------------------------------
  Sort
    Sort Key: t1.a, t2.b, ((t3.a + t3.b))
    ->  Append
          ->  Hash Right Join
                Hash Cond: (((t3.a + t3.b) / 2) = t1.a)
-               ->  Seq Scan on prt1_e_p1 t3
+               ->  Seq Scan on prt1_e_p0 t3
                ->  Hash
                      ->  Hash Right Join
                            Hash Cond: (t2.b = t1.a)
-                           ->  Seq Scan on prt2_p1 t2
+                           ->  Seq Scan on prt2_p0 t2
                            ->  Hash
-                                 ->  Seq Scan on prt1_p1 t1
+                                 ->  Seq Scan on prt1_p0 t1
                                        Filter: (b = 0)
          ->  Hash Right Join
                Hash Cond: (((t3_1.a + t3_1.b) / 2) = t1_1.a)
-               ->  Seq Scan on prt1_e_p2 t3_1
+               ->  Seq Scan on prt1_e_p1 t3_1
                ->  Hash
                      ->  Hash Right Join
                            Hash Cond: (t2_1.b = t1_1.a)
-                           ->  Seq Scan on prt2_p2 t2_1
+                           ->  Seq Scan on prt2_p1 t2_1
                            ->  Hash
-                                 ->  Seq Scan on prt1_p2 t1_1
+                                 ->  Seq Scan on prt1_p1 t1_1
                                        Filter: (b = 0)
          ->  Hash Right Join
                Hash Cond: (((t3_2.a + t3_2.b) / 2) = t1_2.a)
-               ->  Seq Scan on prt1_e_p3 t3_2
+               ->  Seq Scan on prt1_e_p2 t3_2
                ->  Hash
                      ->  Hash Right Join
                            Hash Cond: (t2_2.b = t1_2.a)
-                           ->  Seq Scan on prt2_p3 t2_2
+                           ->  Seq Scan on prt2_p2 t2_2
                            ->  Hash
-                                 ->  Seq Scan on prt1_p3 t1_2
+                                 ->  Seq Scan on prt1_p2 t1_2
                                        Filter: (b = 0)
-(33 rows)
+         ->  Nested Loop Left Join
+               ->  Nested Loop Left Join
+                     ->  Seq Scan on prt1_p3 t1_3
+                           Filter: (b = 0)
+                     ->  Index Scan using iprt2_p3_b on prt2_p3 t2_3
+                           Index Cond: (b = t1_3.a)
+               ->  Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_3
+                     Index Cond: (((a + b) / 2) = t1_3.a)
+         ->  Hash Right Join
+               Hash Cond: (((t3_4.a + t3_4.b) / 2) = t1_4.a)
+               ->  Seq Scan on prt1_e_p4 t3_4
+               ->  Hash
+                     ->  Hash Right Join
+                           Hash Cond: (t2_4.b = t1_4.a)
+                           ->  Seq Scan on prt2_p4 t2_4
+                           ->  Hash
+                                 ->  Seq Scan on prt1_p4 t1_4
+                                       Filter: (b = 0)
+(51 rows)
 
 SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
-  a  |  c   |  b  |  c   | ?column? | c 
------+------+-----+------+----------+---
-   0 | 0000 |   0 | 0000 |        0 | 0
-  50 | 0050 |     |      |      100 | 0
- 100 | 0100 |     |      |      200 | 0
- 150 | 0150 | 150 | 0150 |      300 | 0
- 200 | 0200 |     |      |      400 | 0
- 250 | 0250 |     |      |      500 | 0
- 300 | 0300 | 300 | 0300 |      600 | 0
- 350 | 0350 |     |      |      700 | 0
- 400 | 0400 |     |      |      800 | 0
- 450 | 0450 | 450 | 0450 |      900 | 0
- 500 | 0500 |     |      |     1000 | 0
- 550 | 0550 |     |      |     1100 | 0
-(12 rows)
+  a   |   c   |  b   |   c   | ?column? | c 
+------+-------+------+-------+----------+---
+ -250 | -0250 |      |       |     -500 | 0
+ -200 | -0200 |      |       |     -400 | 0
+ -150 | -0150 | -150 | -0150 |     -300 | 0
+ -100 | -0100 |      |       |     -200 | 0
+  -50 | -0050 |      |       |     -100 | 0
+    0 | 0000  |    0 | 0000  |        0 | 0
+    0 | 0000  |    0 | 0000  |        0 | 0
+   50 | 0050  |      |       |      100 | 0
+  100 | 0100  |      |       |      200 | 0
+  150 | 0150  |  150 | 0150  |      300 | 0
+  200 | 0200  |      |       |      400 | 0
+  250 | 0250  |      |       |      500 | 0
+  300 | 0300  |  300 | 0300  |      600 | 0
+  350 | 0350  |      |       |      700 | 0
+  400 | 0400  |      |       |      800 | 0
+  450 | 0450  |  450 | 0450  |      900 | 0
+  500 | 0500  |      |       |     1000 | 0
+  550 | 0550  |      |       |     1100 | 0
+  600 | 0600  |  600 | 0600  |     1200 | 0
+  650 | 0650  |      |       |     1300 | 0
+  700 | 0700  |      |       |     1400 | 0
+  750 | 0750  |  750 | 0750  |     1500 | 0
+(22 rows)
 
 EXPLAIN (COSTS OFF)
 SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
-                            QUERY PLAN                             
--------------------------------------------------------------------
+                             QUERY PLAN                              
+---------------------------------------------------------------------
  Sort
    Sort Key: t1.a, t2.b, ((t3.a + t3.b))
    ->  Append
          ->  Nested Loop Left Join
                ->  Hash Right Join
                      Hash Cond: (t1.a = ((t3.a + t3.b) / 2))
-                     ->  Seq Scan on prt1_p1 t1
+                     ->  Seq Scan on prt1_p0 t1
                      ->  Hash
-                           ->  Seq Scan on prt1_e_p1 t3
+                           ->  Seq Scan on prt1_e_p0 t3
                                  Filter: (c = 0)
-               ->  Index Scan using iprt2_p1_b on prt2_p1 t2
+               ->  Index Scan using iprt2_p0_b on prt2_p0 t2
                      Index Cond: (b = t1.a)
          ->  Nested Loop Left Join
                ->  Hash Right Join
                      Hash Cond: (t1_1.a = ((t3_1.a + t3_1.b) / 2))
-                     ->  Seq Scan on prt1_p2 t1_1
+                     ->  Seq Scan on prt1_p1 t1_1
                      ->  Hash
-                           ->  Seq Scan on prt1_e_p2 t3_1
+                           ->  Seq Scan on prt1_e_p1 t3_1
                                  Filter: (c = 0)
-               ->  Index Scan using iprt2_p2_b on prt2_p2 t2_1
+               ->  Index Scan using iprt2_p1_b on prt2_p1 t2_1
                      Index Cond: (b = t1_1.a)
          ->  Nested Loop Left Join
                ->  Hash Right Join
                      Hash Cond: (t1_2.a = ((t3_2.a + t3_2.b) / 2))
-                     ->  Seq Scan on prt1_p3 t1_2
+                     ->  Seq Scan on prt1_p2 t1_2
                      ->  Hash
-                           ->  Seq Scan on prt1_e_p3 t3_2
+                           ->  Seq Scan on prt1_e_p2 t3_2
                                  Filter: (c = 0)
-               ->  Index Scan using iprt2_p3_b on prt2_p3 t2_2
+               ->  Index Scan using iprt2_p2_b on prt2_p2 t2_2
                      Index Cond: (b = t1_2.a)
-(30 rows)
+         ->  Nested Loop Left Join
+               ->  Nested Loop Left Join
+                     ->  Seq Scan on prt1_e_p3 t3_3
+                           Filter: (c = 0)
+                     ->  Index Scan using iprt1_p3_a on prt1_p3 t1_3
+                           Index Cond: (a = ((t3_3.a + t3_3.b) / 2))
+               ->  Index Scan using iprt2_p3_b on prt2_p3 t2_3
+                     Index Cond: (b = t1_3.a)
+         ->  Nested Loop Left Join
+               ->  Hash Right Join
+                     Hash Cond: (t1_4.a = ((t3_4.a + t3_4.b) / 2))
+                     ->  Seq Scan on prt1_p4 t1_4
+                     ->  Hash
+                           ->  Seq Scan on prt1_e_p4 t3_4
+                                 Filter: (c = 0)
+               ->  Index Scan using iprt2_p4_b on prt2_p4 t2_4
+                     Index Cond: (b = t1_4.a)
+(47 rows)
 
 SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
-  a  |  c   |  b  |  c   | ?column? | c 
------+------+-----+------+----------+---
-   0 | 0000 |   0 | 0000 |        0 | 0
-  50 | 0050 |     |      |      100 | 0
- 100 | 0100 |     |      |      200 | 0
- 150 | 0150 | 150 | 0150 |      300 | 0
- 200 | 0200 |     |      |      400 | 0
- 250 | 0250 |     |      |      500 | 0
- 300 | 0300 | 300 | 0300 |      600 | 0
- 350 | 0350 |     |      |      700 | 0
- 400 | 0400 |     |      |      800 | 0
- 450 | 0450 | 450 | 0450 |      900 | 0
- 500 | 0500 |     |      |     1000 | 0
- 550 | 0550 |     |      |     1100 | 0
-(12 rows)
+  a   |   c   |  b   |   c   | ?column? | c 
+------+-------+------+-------+----------+---
+ -250 | -0250 |      |       |     -500 | 0
+ -200 | -0200 |      |       |     -400 | 0
+ -150 | -0150 | -150 | -0150 |     -300 | 0
+ -100 | -0100 |      |       |     -200 | 0
+  -50 | -0050 |      |       |     -100 | 0
+    0 | 0000  |    0 | 0000  |        0 | 0
+    0 | 0000  |    0 | 0000  |        0 | 0
+   50 | 0050  |      |       |      100 | 0
+  100 | 0100  |      |       |      200 | 0
+  150 | 0150  |  150 | 0150  |      300 | 0
+  200 | 0200  |      |       |      400 | 0
+  250 | 0250  |      |       |      500 | 0
+  300 | 0300  |  300 | 0300  |      600 | 0
+  350 | 0350  |      |       |      700 | 0
+  400 | 0400  |      |       |      800 | 0
+  450 | 0450  |  450 | 0450  |      900 | 0
+  500 | 0500  |      |       |     1000 | 0
+  550 | 0550  |      |       |     1100 | 0
+  600 | 0600  |  600 | 0600  |     1200 | 0
+  650 | 0650  |      |       |     1300 | 0
+  700 | 0700  |      |       |     1400 | 0
+  750 | 0750  |  750 | 0750  |     1500 | 0
+(22 rows)
 
 -- Cases with non-nullable expressions in subquery results;
 -- make sure these go to null as expected
@@ -680,21 +1056,34 @@ SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * F
                                                    QUERY PLAN                                                   
 ----------------------------------------------------------------------------------------------------------------
  Sort
-   Sort Key: prt1_p1.a, prt2_p1.b, ((prt1_e_p1.a + prt1_e_p1.b))
+   Sort Key: prt1_p0.a, prt2_p0.b, ((prt1_e_p0.a + prt1_e_p0.b))
    ->  Append
          ->  Hash Full Join
-               Hash Cond: (prt1_p1.a = ((prt1_e_p1.a + prt1_e_p1.b) / 2))
-               Filter: ((prt1_p1.a = (50)) OR (prt2_p1.b = (75)) OR (((prt1_e_p1.a + prt1_e_p1.b) / 2) = (50)))
+               Hash Cond: (prt1_p0.a = ((prt1_e_p0.a + prt1_e_p0.b) / 2))
+               Filter: ((prt1_p0.a = (50)) OR (prt2_p0.b = (75)) OR (((prt1_e_p0.a + prt1_e_p0.b) / 2) = (50)))
                ->  Hash Full Join
-                     Hash Cond: (prt1_p1.a = prt2_p1.b)
-                     ->  Seq Scan on prt1_p1
+                     Hash Cond: (prt1_p0.a = prt2_p0.b)
+                     ->  Seq Scan on prt1_p0
                            Filter: (b = 0)
                      ->  Hash
-                           ->  Seq Scan on prt2_p1
+                           ->  Seq Scan on prt2_p0
                                  Filter: (a = 0)
                ->  Hash
-                     ->  Seq Scan on prt1_e_p1
+                     ->  Seq Scan on prt1_e_p0
                            Filter: (c = 0)
+         ->  Hash Full Join
+               Hash Cond: (((prt1_e_p1.a + prt1_e_p1.b) / 2) = prt1_p1.a)
+               Filter: ((prt1_p1.a = (50)) OR (prt2_p1.b = (75)) OR (((prt1_e_p1.a + prt1_e_p1.b) / 2) = (50)))
+               ->  Seq Scan on prt1_e_p1
+                     Filter: (c = 0)
+               ->  Hash
+                     ->  Hash Full Join
+                           Hash Cond: (prt1_p1.a = prt2_p1.b)
+                           ->  Seq Scan on prt1_p1
+                                 Filter: (b = 0)
+                           ->  Hash
+                                 ->  Seq Scan on prt2_p1
+                                       Filter: (a = 0)
          ->  Hash Full Join
                Hash Cond: (prt1_p2.a = ((prt1_e_p2.a + prt1_e_p2.b) / 2))
                Filter: ((prt1_p2.a = (50)) OR (prt2_p2.b = (75)) OR (((prt1_e_p2.a + prt1_e_p2.b) / 2) = (50)))
@@ -721,7 +1110,20 @@ SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * F
                ->  Hash
                      ->  Seq Scan on prt1_e_p3
                            Filter: (c = 0)
-(42 rows)
+         ->  Hash Full Join
+               Hash Cond: (prt1_p4.a = ((prt1_e_p4.a + prt1_e_p4.b) / 2))
+               Filter: ((prt1_p4.a = (50)) OR (prt2_p4.b = (75)) OR (((prt1_e_p4.a + prt1_e_p4.b) / 2) = (50)))
+               ->  Hash Full Join
+                     Hash Cond: (prt1_p4.a = prt2_p4.b)
+                     ->  Seq Scan on prt1_p4
+                           Filter: (b = 0)
+                     ->  Hash
+                           ->  Seq Scan on prt2_p4
+                                 Filter: (a = 0)
+               ->  Hash
+                     ->  Seq Scan on prt1_e_p4
+                           Filter: (c = 0)
+(68 rows)
 
 SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.c = 0) t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.a = t1.phv OR t2.b = t2.phv OR (t3.a + t3.b)/2 = t3.phv ORDER BY t1.a, t2.b, t3.a + t3.b;
  a  | phv | b  | phv | ?column? | phv 
@@ -739,172 +1141,260 @@ SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHER
    Sort Key: t1.a
    ->  Append
          ->  Nested Loop
-               Join Filter: (t1.a = t1_3.b)
+               Join Filter: (t1.a = t1_5.b)
                ->  HashAggregate
-                     Group Key: t1_3.b
+                     Group Key: t1_5.b
                      ->  Hash Join
-                           Hash Cond: (((t2.a + t2.b) / 2) = t1_3.b)
-                           ->  Seq Scan on prt1_e_p1 t2
+                           Hash Cond: (((t2.a + t2.b) / 2) = t1_5.b)
+                           ->  Seq Scan on prt1_e_p0 t2
                            ->  Hash
-                                 ->  Seq Scan on prt2_p1 t1_3
+                                 ->  Seq Scan on prt2_p0 t1_5
                                        Filter: (a = 0)
-               ->  Index Scan using iprt1_p1_a on prt1_p1 t1
+               ->  Index Scan using iprt1_p0_a on prt1_p0 t1
                      Index Cond: (a = ((t2.a + t2.b) / 2))
                      Filter: (b = 0)
          ->  Nested Loop
-               Join Filter: (t1_1.a = t1_4.b)
+               Join Filter: (t1_1.a = t1_6.b)
                ->  HashAggregate
-                     Group Key: t1_4.b
+                     Group Key: t1_6.b
                      ->  Hash Join
-                           Hash Cond: (((t2_1.a + t2_1.b) / 2) = t1_4.b)
-                           ->  Seq Scan on prt1_e_p2 t2_1
+                           Hash Cond: (((t2_1.a + t2_1.b) / 2) = t1_6.b)
+                           ->  Seq Scan on prt1_e_p1 t2_1
                            ->  Hash
-                                 ->  Seq Scan on prt2_p2 t1_4
+                                 ->  Seq Scan on prt2_p1 t1_6
                                        Filter: (a = 0)
-               ->  Index Scan using iprt1_p2_a on prt1_p2 t1_1
+               ->  Index Scan using iprt1_p1_a on prt1_p1 t1_1
                      Index Cond: (a = ((t2_1.a + t2_1.b) / 2))
                      Filter: (b = 0)
          ->  Nested Loop
-               Join Filter: (t1_2.a = t1_5.b)
+               Join Filter: (t1_2.a = t1_7.b)
                ->  HashAggregate
-                     Group Key: t1_5.b
+                     Group Key: t1_7.b
                      ->  Nested Loop
-                           ->  Seq Scan on prt2_p3 t1_5
+                           ->  Seq Scan on prt2_p2 t1_7
                                  Filter: (a = 0)
-                           ->  Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t2_2
-                                 Index Cond: (((a + b) / 2) = t1_5.b)
-               ->  Index Scan using iprt1_p3_a on prt1_p3 t1_2
+                           ->  Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t2_2
+                                 Index Cond: (((a + b) / 2) = t1_7.b)
+               ->  Index Scan using iprt1_p2_a on prt1_p2 t1_2
                      Index Cond: (a = ((t2_2.a + t2_2.b) / 2))
                      Filter: (b = 0)
-(41 rows)
+         ->  Nested Loop
+               Join Filter: (t1_3.a = t1_8.b)
+               ->  HashAggregate
+                     Group Key: t1_8.b
+                     ->  Nested Loop
+                           ->  Seq Scan on prt2_p3 t1_8
+                                 Filter: (a = 0)
+                           ->  Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t2_3
+                                 Index Cond: (((a + b) / 2) = t1_8.b)
+               ->  Index Scan using iprt1_p3_a on prt1_p3 t1_3
+                     Index Cond: (a = ((t2_3.a + t2_3.b) / 2))
+                     Filter: (b = 0)
+         ->  Nested Loop
+               Join Filter: (t1_4.a = t1_9.b)
+               ->  HashAggregate
+                     Group Key: t1_9.b
+                     ->  Hash Join
+                           Hash Cond: (((t2_4.a + t2_4.b) / 2) = t1_9.b)
+                           ->  Seq Scan on prt1_e_p4 t2_4
+                           ->  Hash
+                                 ->  Seq Scan on prt2_p4 t1_9
+                                       Filter: (a = 0)
+               ->  Index Scan using iprt1_p4_a on prt1_p4 t1_4
+                     Index Cond: (a = ((t2_4.a + t2_4.b) / 2))
+                     Filter: (b = 0)
+(66 rows)
 
 SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.a = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.b = 0 ORDER BY t1.a;
-  a  | b |  c   
------+---+------
-   0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
-(4 rows)
+  a   | b |   c   
+------+---+-------
+ -150 | 0 | -0150
+    0 | 0 | 0000
+  150 | 0 | 0150
+  300 | 0 | 0300
+  450 | 0 | 0450
+  600 | 0 | 0600
+  750 | 0 | 0750
+(7 rows)
 
 EXPLAIN (COSTS OFF)
 SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
-                               QUERY PLAN                                
--------------------------------------------------------------------------
+                                QUERY PLAN                                 
+---------------------------------------------------------------------------
  Sort
    Sort Key: t1.a
    ->  Append
          ->  Nested Loop
                ->  HashAggregate
-                     Group Key: t1_3.b
+                     Group Key: t1_5.b
                      ->  Hash Semi Join
-                           Hash Cond: (t1_3.b = ((t1_6.a + t1_6.b) / 2))
-                           ->  Seq Scan on prt2_p1 t1_3
+                           Hash Cond: (t1_5.b = ((t1_10.a + t1_10.b) / 2))
+                           ->  Seq Scan on prt2_p0 t1_5
                            ->  Hash
-                                 ->  Seq Scan on prt1_e_p1 t1_6
+                                 ->  Seq Scan on prt1_e_p0 t1_10
                                        Filter: (c = 0)
-               ->  Index Scan using iprt1_p1_a on prt1_p1 t1
-                     Index Cond: (a = t1_3.b)
+               ->  Index Scan using iprt1_p0_a on prt1_p0 t1
+                     Index Cond: (a = t1_5.b)
                      Filter: (b = 0)
          ->  Nested Loop
                ->  HashAggregate
-                     Group Key: t1_4.b
+                     Group Key: t1_6.b
                      ->  Hash Semi Join
-                           Hash Cond: (t1_4.b = ((t1_7.a + t1_7.b) / 2))
-                           ->  Seq Scan on prt2_p2 t1_4
+                           Hash Cond: (t1_6.b = ((t1_11.a + t1_11.b) / 2))
+                           ->  Seq Scan on prt2_p1 t1_6
                            ->  Hash
-                                 ->  Seq Scan on prt1_e_p2 t1_7
+                                 ->  Seq Scan on prt1_e_p1 t1_11
                                        Filter: (c = 0)
-               ->  Index Scan using iprt1_p2_a on prt1_p2 t1_1
-                     Index Cond: (a = t1_4.b)
+               ->  Index Scan using iprt1_p1_a on prt1_p1 t1_1
+                     Index Cond: (a = t1_6.b)
                      Filter: (b = 0)
          ->  Nested Loop
                ->  HashAggregate
-                     Group Key: t1_5.b
+                     Group Key: t1_7.b
                      ->  Hash Semi Join
-                           Hash Cond: (t1_5.b = ((t1_8.a + t1_8.b) / 2))
-                           ->  Seq Scan on prt2_p3 t1_5
+                           Hash Cond: (t1_7.b = ((t1_12.a + t1_12.b) / 2))
+                           ->  Seq Scan on prt2_p2 t1_7
                            ->  Hash
-                                 ->  Seq Scan on prt1_e_p3 t1_8
+                                 ->  Seq Scan on prt1_e_p2 t1_12
                                        Filter: (c = 0)
-               ->  Index Scan using iprt1_p3_a on prt1_p3 t1_2
-                     Index Cond: (a = t1_5.b)
+               ->  Index Scan using iprt1_p2_a on prt1_p2 t1_2
+                     Index Cond: (a = t1_7.b)
                      Filter: (b = 0)
-(39 rows)
-
-SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
-  a  | b |  c   
------+---+------
-   0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
-(4 rows)
+         ->  Nested Loop
+               ->  HashAggregate
+                     Group Key: t1_8.b
+                     ->  Hash Semi Join
+                           Hash Cond: (t1_8.b = ((t1_13.a + t1_13.b) / 2))
+                           ->  Seq Scan on prt2_p3 t1_8
+                           ->  Hash
+                                 ->  Seq Scan on prt1_e_p3 t1_13
+                                       Filter: (c = 0)
+               ->  Index Scan using iprt1_p3_a on prt1_p3 t1_3
+                     Index Cond: (a = t1_8.b)
+                     Filter: (b = 0)
+         ->  Nested Loop
+               ->  HashAggregate
+                     Group Key: t1_9.b
+                     ->  Hash Semi Join
+                           Hash Cond: (t1_9.b = ((t1_14.a + t1_14.b) / 2))
+                           ->  Seq Scan on prt2_p4 t1_9
+                           ->  Hash
+                                 ->  Seq Scan on prt1_e_p4 t1_14
+                                       Filter: (c = 0)
+               ->  Index Scan using iprt1_p4_a on prt1_p4 t1_4
+                     Index Cond: (a = t1_9.b)
+                     Filter: (b = 0)
+(63 rows)
+
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
+  a   | b |   c   
+------+---+-------
+ -150 | 0 | -0150
+    0 | 0 | 0000
+  150 | 0 | 0150
+  300 | 0 | 0300
+  450 | 0 | 0450
+  600 | 0 | 0600
+  750 | 0 | 0750
+(7 rows)
 
 -- test merge joins
 SET enable_hashjoin TO off;
 SET enable_nestloop TO off;
 EXPLAIN (COSTS OFF)
 SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
-                           QUERY PLAN                           
-----------------------------------------------------------------
+                            QUERY PLAN                            
+------------------------------------------------------------------
  Merge Append
    Sort Key: t1.a
    ->  Merge Semi Join
-         Merge Cond: (t1.a = t1_3.b)
+         Merge Cond: (t1.a = t1_5.b)
          ->  Sort
                Sort Key: t1.a
-               ->  Seq Scan on prt1_p1 t1
+               ->  Seq Scan on prt1_p0 t1
                      Filter: (b = 0)
          ->  Merge Semi Join
-               Merge Cond: (t1_3.b = (((t1_6.a + t1_6.b) / 2)))
+               Merge Cond: (t1_5.b = (((t1_10.a + t1_10.b) / 2)))
                ->  Sort
-                     Sort Key: t1_3.b
-                     ->  Seq Scan on prt2_p1 t1_3
+                     Sort Key: t1_5.b
+                     ->  Seq Scan on prt2_p0 t1_5
                ->  Sort
-                     Sort Key: (((t1_6.a + t1_6.b) / 2))
-                     ->  Seq Scan on prt1_e_p1 t1_6
+                     Sort Key: (((t1_10.a + t1_10.b) / 2))
+                     ->  Seq Scan on prt1_e_p0 t1_10
                            Filter: (c = 0)
    ->  Merge Semi Join
-         Merge Cond: (t1_1.a = t1_4.b)
+         Merge Cond: (t1_1.a = t1_6.b)
          ->  Sort
                Sort Key: t1_1.a
-               ->  Seq Scan on prt1_p2 t1_1
+               ->  Seq Scan on prt1_p1 t1_1
                      Filter: (b = 0)
          ->  Merge Semi Join
-               Merge Cond: (t1_4.b = (((t1_7.a + t1_7.b) / 2)))
+               Merge Cond: (t1_6.b = (((t1_11.a + t1_11.b) / 2)))
                ->  Sort
-                     Sort Key: t1_4.b
-                     ->  Seq Scan on prt2_p2 t1_4
+                     Sort Key: t1_6.b
+                     ->  Seq Scan on prt2_p1 t1_6
                ->  Sort
-                     Sort Key: (((t1_7.a + t1_7.b) / 2))
-                     ->  Seq Scan on prt1_e_p2 t1_7
+                     Sort Key: (((t1_11.a + t1_11.b) / 2))
+                     ->  Seq Scan on prt1_e_p1 t1_11
                            Filter: (c = 0)
    ->  Merge Semi Join
-         Merge Cond: (t1_2.a = t1_5.b)
+         Merge Cond: (t1_2.a = t1_7.b)
          ->  Sort
                Sort Key: t1_2.a
-               ->  Seq Scan on prt1_p3 t1_2
+               ->  Seq Scan on prt1_p2 t1_2
                      Filter: (b = 0)
          ->  Merge Semi Join
-               Merge Cond: (t1_5.b = (((t1_8.a + t1_8.b) / 2)))
+               Merge Cond: (t1_7.b = (((t1_12.a + t1_12.b) / 2)))
                ->  Sort
-                     Sort Key: t1_5.b
-                     ->  Seq Scan on prt2_p3 t1_5
+                     Sort Key: t1_7.b
+                     ->  Seq Scan on prt2_p2 t1_7
                ->  Sort
-                     Sort Key: (((t1_8.a + t1_8.b) / 2))
-                     ->  Seq Scan on prt1_e_p3 t1_8
+                     Sort Key: (((t1_12.a + t1_12.b) / 2))
+                     ->  Seq Scan on prt1_e_p2 t1_12
                            Filter: (c = 0)
-(47 rows)
+   ->  Merge Semi Join
+         Merge Cond: (t1_3.a = t1_8.b)
+         ->  Sort
+               Sort Key: t1_3.a
+               ->  Seq Scan on prt1_p3 t1_3
+                     Filter: (b = 0)
+         ->  Merge Semi Join
+               Merge Cond: (t1_8.b = (((t1_13.a + t1_13.b) / 2)))
+               ->  Sort
+                     Sort Key: t1_8.b
+                     ->  Seq Scan on prt2_p3 t1_8
+               ->  Sort
+                     Sort Key: (((t1_13.a + t1_13.b) / 2))
+                     ->  Seq Scan on prt1_e_p3 t1_13
+                           Filter: (c = 0)
+   ->  Merge Semi Join
+         Merge Cond: (t1_4.a = t1_9.b)
+         ->  Sort
+               Sort Key: t1_4.a
+               ->  Seq Scan on prt1_p4 t1_4
+                     Filter: (b = 0)
+         ->  Merge Semi Join
+               Merge Cond: (t1_9.b = (((t1_14.a + t1_14.b) / 2)))
+               ->  Sort
+                     Sort Key: t1_9.b
+                     ->  Seq Scan on prt2_p4 t1_9
+               ->  Sort
+                     Sort Key: (((t1_14.a + t1_14.b) / 2))
+                     ->  Seq Scan on prt1_e_p4 t1_14
+                           Filter: (c = 0)
+(77 rows)
 
 SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a;
-  a  | b |  c   
------+---+------
-   0 | 0 | 0000
- 150 | 0 | 0150
- 300 | 0 | 0300
- 450 | 0 | 0450
-(4 rows)
+  a   | b |   c   
+------+---+-------
+ -150 | 0 | -0150
+    0 | 0 | 0000
+  150 | 0 | 0150
+  300 | 0 | 0300
+  450 | 0 | 0450
+  600 | 0 | 0600
+  750 | 0 | 0750
+(7 rows)
 
 EXPLAIN (COSTS OFF)
 SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
@@ -921,14 +1411,14 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
                            Merge Cond: ((((t3.a + t3.b) / 2)) = t1.a)
                            ->  Sort
                                  Sort Key: (((t3.a + t3.b) / 2))
-                                 ->  Seq Scan on prt1_e_p1 t3
+                                 ->  Seq Scan on prt1_e_p0 t3
                                        Filter: (c = 0)
                            ->  Sort
                                  Sort Key: t1.a
-                                 ->  Seq Scan on prt1_p1 t1
+                                 ->  Seq Scan on prt1_p0 t1
                ->  Sort
                      Sort Key: t2.b
-                     ->  Seq Scan on prt2_p1 t2
+                     ->  Seq Scan on prt2_p0 t2
          ->  Merge Left Join
                Merge Cond: (t1_1.a = t2_1.b)
                ->  Sort
@@ -937,14 +1427,14 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
                            Merge Cond: ((((t3_1.a + t3_1.b) / 2)) = t1_1.a)
                            ->  Sort
                                  Sort Key: (((t3_1.a + t3_1.b) / 2))
-                                 ->  Seq Scan on prt1_e_p2 t3_1
+                                 ->  Seq Scan on prt1_e_p1 t3_1
                                        Filter: (c = 0)
                            ->  Sort
                                  Sort Key: t1_1.a
-                                 ->  Seq Scan on prt1_p2 t1_1
+                                 ->  Seq Scan on prt1_p1 t1_1
                ->  Sort
                      Sort Key: t2_1.b
-                     ->  Seq Scan on prt2_p2 t2_1
+                     ->  Seq Scan on prt2_p1 t2_1
          ->  Merge Left Join
                Merge Cond: (t1_2.a = t2_2.b)
                ->  Sort
@@ -953,32 +1443,74 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
                            Merge Cond: ((((t3_2.a + t3_2.b) / 2)) = t1_2.a)
                            ->  Sort
                                  Sort Key: (((t3_2.a + t3_2.b) / 2))
-                                 ->  Seq Scan on prt1_e_p3 t3_2
+                                 ->  Seq Scan on prt1_e_p2 t3_2
                                        Filter: (c = 0)
                            ->  Sort
                                  Sort Key: t1_2.a
-                                 ->  Seq Scan on prt1_p3 t1_2
+                                 ->  Seq Scan on prt1_p2 t1_2
                ->  Sort
                      Sort Key: t2_2.b
-                     ->  Seq Scan on prt2_p3 t2_2
-(51 rows)
+                     ->  Seq Scan on prt2_p2 t2_2
+         ->  Merge Left Join
+               Merge Cond: (t1_3.a = t2_3.b)
+               ->  Sort
+                     Sort Key: t1_3.a
+                     ->  Merge Left Join
+                           Merge Cond: ((((t3_3.a + t3_3.b) / 2)) = t1_3.a)
+                           ->  Sort
+                                 Sort Key: (((t3_3.a + t3_3.b) / 2))
+                                 ->  Seq Scan on prt1_e_p3 t3_3
+                                       Filter: (c = 0)
+                           ->  Sort
+                                 Sort Key: t1_3.a
+                                 ->  Seq Scan on prt1_p3 t1_3
+               ->  Sort
+                     Sort Key: t2_3.b
+                     ->  Seq Scan on prt2_p3 t2_3
+         ->  Merge Left Join
+               Merge Cond: (t1_4.a = t2_4.b)
+               ->  Sort
+                     Sort Key: t1_4.a
+                     ->  Merge Left Join
+                           Merge Cond: ((((t3_4.a + t3_4.b) / 2)) = t1_4.a)
+                           ->  Sort
+                                 Sort Key: (((t3_4.a + t3_4.b) / 2))
+                                 ->  Seq Scan on prt1_e_p4 t3_4
+                                       Filter: (c = 0)
+                           ->  Sort
+                                 Sort Key: t1_4.a
+                                 ->  Seq Scan on prt1_p4 t1_4
+               ->  Sort
+                     Sort Key: t2_4.b
+                     ->  Seq Scan on prt2_p4 t2_4
+(83 rows)
 
 SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
-  a  |  c   |  b  |  c   | ?column? | c 
------+------+-----+------+----------+---
-   0 | 0000 |   0 | 0000 |        0 | 0
-  50 | 0050 |     |      |      100 | 0
- 100 | 0100 |     |      |      200 | 0
- 150 | 0150 | 150 | 0150 |      300 | 0
- 200 | 0200 |     |      |      400 | 0
- 250 | 0250 |     |      |      500 | 0
- 300 | 0300 | 300 | 0300 |      600 | 0
- 350 | 0350 |     |      |      700 | 0
- 400 | 0400 |     |      |      800 | 0
- 450 | 0450 | 450 | 0450 |      900 | 0
- 500 | 0500 |     |      |     1000 | 0
- 550 | 0550 |     |      |     1100 | 0
-(12 rows)
+  a   |   c   |  b   |   c   | ?column? | c 
+------+-------+------+-------+----------+---
+ -250 | -0250 |      |       |     -500 | 0
+ -200 | -0200 |      |       |     -400 | 0
+ -150 | -0150 | -150 | -0150 |     -300 | 0
+ -100 | -0100 |      |       |     -200 | 0
+  -50 | -0050 |      |       |     -100 | 0
+    0 | 0000  |    0 | 0000  |        0 | 0
+    0 | 0000  |    0 | 0000  |        0 | 0
+   50 | 0050  |      |       |      100 | 0
+  100 | 0100  |      |       |      200 | 0
+  150 | 0150  |  150 | 0150  |      300 | 0
+  200 | 0200  |      |       |      400 | 0
+  250 | 0250  |      |       |      500 | 0
+  300 | 0300  |  300 | 0300  |      600 | 0
+  350 | 0350  |      |       |      700 | 0
+  400 | 0400  |      |       |      800 | 0
+  450 | 0450  |  450 | 0450  |      900 | 0
+  500 | 0500  |      |       |     1000 | 0
+  550 | 0550  |      |       |     1100 | 0
+  600 | 0600  |  600 | 0600  |     1200 | 0
+  650 | 0650  |      |       |     1300 | 0
+  700 | 0700  |      |       |     1400 | 0
+  750 | 0750  |  750 | 0750  |     1500 | 0
+(22 rows)
 
 -- MergeAppend on nullable column
 -- This should generate a partitionwise join, but currently fails to
@@ -987,12 +1519,14 @@ SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT *
                         QUERY PLAN                         
 -----------------------------------------------------------
  Sort
-   Sort Key: prt1_p1.a, prt2_p2.b
+   Sort Key: prt1_p0.a, prt2_p2.b
    ->  Merge Left Join
-         Merge Cond: (prt1_p1.a = prt2_p2.b)
+         Merge Cond: (prt1_p0.a = prt2_p2.b)
          ->  Sort
-               Sort Key: prt1_p1.a
+               Sort Key: prt1_p0.a
                ->  Append
+                     ->  Seq Scan on prt1_p0
+                           Filter: ((a < 450) AND (b = 0))
                      ->  Seq Scan on prt1_p1
                            Filter: ((a < 450) AND (b = 0))
                      ->  Seq Scan on prt1_p2
@@ -1004,21 +1538,28 @@ SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT *
                            Filter: (b > 250)
                      ->  Seq Scan on prt2_p3
                            Filter: (b > 250)
-(18 rows)
+                     ->  Seq Scan on prt2_p4
+                           Filter: (b > 250)
+(22 rows)
 
 SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;
-  a  |  b  
------+-----
-   0 |    
-  50 |    
- 100 |    
- 150 |    
- 200 |    
- 250 |    
- 300 | 300
- 350 |    
- 400 |    
-(9 rows)
+  a   |  b  
+------+-----
+ -250 |    
+ -200 |    
+ -150 |    
+ -100 |    
+  -50 |    
+    0 |    
+   50 |    
+  100 |    
+  150 |    
+  200 |    
+  250 |    
+  300 | 300
+  350 |    
+  400 |    
+(14 rows)
 
 -- merge join when expression with whole-row reference needs to be sorted;
 -- partitionwise join does not apply
@@ -1032,175 +1573,2412 @@ SELECT t1.a, t2.b FROM prt1 t1, prt2 t2 WHERE t1::text = t2::text AND t1.a = t2.
          Sort Key: t1.a, ((((t1.*)::prt1))::text)
          ->  Result
                ->  Append
-                     ->  Seq Scan on prt1_p1 t1
-                     ->  Seq Scan on prt1_p2 t1_1
-                     ->  Seq Scan on prt1_p3 t1_2
+                     ->  Seq Scan on prt1_p0 t1
+                     ->  Seq Scan on prt1_p1 t1_1
+                     ->  Seq Scan on prt1_p2 t1_2
+                     ->  Seq Scan on prt1_p3 t1_3
+                     ->  Seq Scan on prt1_p4 t1_4
    ->  Sort
          Sort Key: t2.b, ((((t2.*)::prt2))::text)
          ->  Result
                ->  Append
-                     ->  Seq Scan on prt2_p1 t2
-                     ->  Seq Scan on prt2_p2 t2_1
-                     ->  Seq Scan on prt2_p3 t2_2
-(16 rows)
+                     ->  Seq Scan on prt2_p0 t2
+                     ->  Seq Scan on prt2_p1 t2_1
+                     ->  Seq Scan on prt2_p2 t2_2
+                     ->  Seq Scan on prt2_p3 t2_3
+                     ->  Seq Scan on prt2_p4 t2_4
+(20 rows)
 
 SELECT t1.a, t2.b FROM prt1 t1, prt2 t2 WHERE t1::text = t2::text AND t1.a = t2.b ORDER BY t1.a;
- a  | b  
-----+----
-  0 |  0
-  6 |  6
- 12 | 12
- 18 | 18
- 24 | 24
-(5 rows)
+  a  |  b  
+-----+-----
+ -24 | -24
+ -18 | -18
+ -12 | -12
+  -6 |  -6
+   0 |   0
+   6 |   6
+  12 |  12
+  18 |  18
+  24 |  24
+(9 rows)
+
+RESET enable_hashjoin;
+RESET enable_nestloop;
+-- test default partition behavior for range, partition-wise join is not
+-- possible since more than one partition on one side matches default partition
+-- on the other side. Default partition from prt1 matches default partition and
+-- prt2_p4 from prt2 and default partition from prt2 matches default partition
+-- and prt1_p0 from prt1
+ALTER TABLE prt1 DETACH PARTITION prt1_p3;
+ALTER TABLE prt1 ATTACH PARTITION prt1_p3 DEFAULT;
+ANALYZE prt1;
+ALTER TABLE prt2 DETACH PARTITION prt2_p3;
+ALTER TABLE prt2 ATTACH PARTITION prt2_p3 DEFAULT;
+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;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t1.a
+   ->  Hash Join
+         Hash Cond: (t2.b = t1.a)
+         ->  Append
+               ->  Seq Scan on prt2_p0 t2
+               ->  Seq Scan on prt2_p1 t2_1
+               ->  Seq Scan on prt2_p2 t2_2
+               ->  Seq Scan on prt2_p4 t2_3
+               ->  Seq Scan on prt2_p3 t2_4
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on prt1_p0 t1
+                           Filter: (b = 0)
+                     ->  Seq Scan on prt1_p1 t1_1
+                           Filter: (b = 0)
+                     ->  Seq Scan on prt1_p2 t1_2
+                           Filter: (b = 0)
+                     ->  Seq Scan on prt1_p4 t1_3
+                           Filter: (b = 0)
+                     ->  Seq Scan on prt1_p3 t1_4
+                           Filter: (b = 0)
+(22 rows)
+
+-- partition-wise join should be possible when we drop the first and last
+-- partitions from both sides
+ALTER TABLE prt1 DETACH PARTITION prt1_p0;
+ALTER TABLE prt1 DETACH PARTITION prt1_p4;
+ANALYZE prt1;
+ALTER TABLE prt2 DETACH PARTITION prt2_p0;
+ALTER TABLE prt2 DETACH PARTITION prt2_p4;
+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;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t1.a
+   ->  Append
+         ->  Hash Join
+               Hash Cond: (t2.b = t1.a)
+               ->  Seq Scan on prt2_p1 t2
+               ->  Hash
+                     ->  Seq Scan on prt1_p1 t1
+                           Filter: (b = 0)
+         ->  Hash Join
+               Hash Cond: (t2_1.b = t1_1.a)
+               ->  Seq Scan on prt2_p2 t2_1
+               ->  Hash
+                     ->  Seq Scan on prt1_p2 t1_1
+                           Filter: (b = 0)
+         ->  Hash Join
+               Hash Cond: (t2_2.b = t1_2.a)
+               ->  Seq Scan on prt2_p3 t2_2
+               ->  Hash
+                     ->  Seq Scan on prt1_p3 t1_2
+                           Filter: (b = 0)
+(21 rows)
+
+-- restore the partitioned tables for rest of the tests
+ALTER TABLE prt1 ATTACH PARTITION prt1_p0 FOR VALUES FROM (MINVALUE) TO (0);
+ALTER TABLE prt1 ATTACH PARTITION prt1_p4 FOR VALUES FROM (600) TO (800);
+ALTER TABLE prt1 DETACH PARTITION prt1_p3;
+ALTER TABLE prt1 ATTACH PARTITION prt1_p3 FOR VALUES FROM (500) TO (600);
+ANALYZE prt1;
+ALTER TABLE prt2 ATTACH PARTITION prt2_p0 FOR VALUES FROM (-250) TO (0);
+ALTER TABLE prt2 ATTACH PARTITION prt2_p4 FOR VALUES FROM (600) TO (MAXVALUE);
+ALTER TABLE prt2 DETACH PARTITION prt2_p3;
+ALTER TABLE prt2 ATTACH PARTITION prt2_p3 FOR VALUES FROM (500) TO (600);
+ANALYZE prt2;
+-- Add an extra partition to prt2 , Partition-wise join is possible with
+-- extra partitions on inner side are allowed
+DROP TABLE prt2_p4;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (800);
+CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (800) TO (1000);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999) i WHERE i % 3 = 0;
+ANALYZE prt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+                          QUERY PLAN                           
+---------------------------------------------------------------
+ Sort
+   Sort Key: t1.a
+   ->  Append
+         ->  Hash Join
+               Hash Cond: (t2.b = t1.a)
+               ->  Seq Scan on prt2_p0 t2
+               ->  Hash
+                     ->  Seq Scan on prt1_p0 t1
+                           Filter: (b = 0)
+         ->  Hash Join
+               Hash Cond: (t2_1.b = t1_1.a)
+               ->  Seq Scan on prt2_p1 t2_1
+               ->  Hash
+                     ->  Seq Scan on prt1_p1 t1_1
+                           Filter: (b = 0)
+         ->  Hash Join
+               Hash Cond: (t2_2.b = t1_2.a)
+               ->  Seq Scan on prt2_p2 t2_2
+               ->  Hash
+                     ->  Seq Scan on prt1_p2 t1_2
+                           Filter: (b = 0)
+         ->  Nested Loop
+               ->  Seq Scan on prt1_p3 t1_3
+                     Filter: (b = 0)
+               ->  Index Scan using iprt2_p3_b on prt2_p3 t2_3
+                     Index Cond: (b = t1_3.a)
+         ->  Hash Join
+               Hash Cond: (t2_4.b = t1_4.a)
+               ->  Seq Scan on prt2_p4 t2_4
+               ->  Hash
+                     ->  Seq Scan on prt1_p4 t1_4
+                           Filter: (b = 0)
+(32 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+  a   |   c   | a |   c   
+------+-------+---+-------
+ -150 | -0150 | 0 | -0150
+    0 | 0000  | 0 | 0000
+  150 | 0150  | 0 | 0150
+  300 | 0300  | 0 | 0300
+  450 | 0450  | 0 | 0450
+  600 | 0600  | 0 | 0600
+  750 | 0750  | 0 | 0750
+(7 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+                          QUERY PLAN                           
+---------------------------------------------------------------
+ Sort
+   Sort Key: t1.a
+   ->  Append
+         ->  Hash Right Join
+               Hash Cond: (t2.b = t1.a)
+               ->  Seq Scan on prt2_p0 t2
+               ->  Hash
+                     ->  Seq Scan on prt1_p0 t1
+                           Filter: (b = 0)
+         ->  Hash Right Join
+               Hash Cond: (t2_1.b = t1_1.a)
+               ->  Seq Scan on prt2_p1 t2_1
+               ->  Hash
+                     ->  Seq Scan on prt1_p1 t1_1
+                           Filter: (b = 0)
+         ->  Hash Right Join
+               Hash Cond: (t2_2.b = t1_2.a)
+               ->  Seq Scan on prt2_p2 t2_2
+               ->  Hash
+                     ->  Seq Scan on prt1_p2 t1_2
+                           Filter: (b = 0)
+         ->  Nested Loop Left Join
+               ->  Seq Scan on prt1_p3 t1_3
+                     Filter: (b = 0)
+               ->  Index Scan using iprt2_p3_b on prt2_p3 t2_3
+                     Index Cond: (b = t1_3.a)
+         ->  Hash Right Join
+               Hash Cond: (t2_4.b = t1_4.a)
+               ->  Seq Scan on prt2_p4 t2_4
+               ->  Hash
+                     ->  Seq Scan on prt1_p4 t1_4
+                           Filter: (b = 0)
+(32 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+  a   |   c   | a |   c   
+------+-------+---+-------
+ -250 | -0250 |   | 
+ -200 | -0200 |   | 
+ -150 | -0150 | 0 | -0150
+ -100 | -0100 |   | 
+  -50 | -0050 |   | 
+    0 | 0000  | 0 | 0000
+   50 | 0050  |   | 
+  100 | 0100  |   | 
+  150 | 0150  | 0 | 0150
+  200 | 0200  |   | 
+  250 | 0250  |   | 
+  300 | 0300  | 0 | 0300
+  350 | 0350  |   | 
+  400 | 0400  |   | 
+  450 | 0450  | 0 | 0450
+  500 | 0500  |   | 
+  550 | 0550  |   | 
+  600 | 0600  | 0 | 0600
+  650 | 0650  |   | 
+  700 | 0700  |   | 
+  750 | 0750  | 0 | 0750
+(21 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                             QUERY PLAN                             
+--------------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Append
+         ->  Hash Semi Join
+               Hash Cond: (t1.a = t2.b)
+               ->  Seq Scan on prt1_p0 t1
+                     Filter: (b = 0)
+               ->  Hash
+                     ->  Seq Scan on prt2_p0 t2
+         ->  Hash Semi Join
+               Hash Cond: (t1_1.a = t2_1.b)
+               ->  Seq Scan on prt1_p1 t1_1
+                     Filter: (b = 0)
+               ->  Hash
+                     ->  Seq Scan on prt2_p1 t2_1
+         ->  Hash Semi Join
+               Hash Cond: (t1_2.a = t2_2.b)
+               ->  Seq Scan on prt1_p2 t1_2
+                     Filter: (b = 0)
+               ->  Hash
+                     ->  Seq Scan on prt2_p2 t2_2
+         ->  Nested Loop Semi Join
+               ->  Seq Scan on prt1_p3 t1_3
+                     Filter: (b = 0)
+               ->  Index Only Scan using iprt2_p3_b on prt2_p3 t2_3
+                     Index Cond: (b = t1_3.a)
+         ->  Hash Semi Join
+               Hash Cond: (t1_4.a = t2_4.b)
+               ->  Seq Scan on prt1_p4 t1_4
+                     Filter: (b = 0)
+               ->  Hash
+                     ->  Seq Scan on prt2_p4 t2_4
+(32 rows)
+
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+  a   | b |   c   
+------+---+-------
+ -150 | 0 | -0150
+    0 | 0 | 0000
+  150 | 0 | 0150
+  300 | 0 | 0300
+  450 | 0 | 0450
+  600 | 0 | 0600
+  750 | 0 | 0750
+(7 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+                             QUERY PLAN                             
+--------------------------------------------------------------------
+ Sort
+   Sort Key: t1.b, t1.c
+   ->  Append
+         ->  Nested Loop Semi Join
+               ->  Seq Scan on prt2_p0 t1
+                     Filter: (a = 0)
+               ->  Index Only Scan using iprt1_p0_a on prt1_p0 t2
+                     Index Cond: (a = t1.b)
+         ->  Nested Loop Semi Join
+               ->  Seq Scan on prt2_p1 t1_1
+                     Filter: (a = 0)
+               ->  Index Only Scan using iprt1_p1_a on prt1_p1 t2_1
+                     Index Cond: (a = t1_1.b)
+         ->  Nested Loop Semi Join
+               ->  Seq Scan on prt2_p2 t1_2
+                     Filter: (a = 0)
+               ->  Index Only Scan using iprt1_p2_a on prt1_p2 t2_2
+                     Index Cond: (a = t1_2.b)
+         ->  Nested Loop Semi Join
+               ->  Seq Scan on prt2_p3 t1_3
+                     Filter: (a = 0)
+               ->  Index Only Scan using iprt1_p3_a on prt1_p3 t2_3
+                     Index Cond: (a = t1_3.b)
+         ->  Nested Loop Semi Join
+               ->  Seq Scan on prt2_p4 t1_4
+                     Filter: (a = 0)
+               ->  Index Only Scan using iprt1_p4_a on prt1_p4 t2_4
+                     Index Cond: (a = t1_4.b)
+(28 rows)
+
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+ a |  b   |   c   
+---+------+-------
+ 0 | -150 | -0150
+ 0 |    0 | 0000
+ 0 |  150 | 0150
+ 0 |  300 | 0300
+ 0 |  450 | 0450
+ 0 |  600 | 0600
+ 0 |  750 | 0750
+(7 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                             QUERY PLAN                             
+--------------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Append
+         ->  Hash Anti Join
+               Hash Cond: (t1.a = t2.b)
+               ->  Seq Scan on prt1_p0 t1
+                     Filter: (b = 0)
+               ->  Hash
+                     ->  Seq Scan on prt2_p0 t2
+         ->  Hash Anti Join
+               Hash Cond: (t1_1.a = t2_1.b)
+               ->  Seq Scan on prt1_p1 t1_1
+                     Filter: (b = 0)
+               ->  Hash
+                     ->  Seq Scan on prt2_p1 t2_1
+         ->  Hash Anti Join
+               Hash Cond: (t1_2.a = t2_2.b)
+               ->  Seq Scan on prt1_p2 t1_2
+                     Filter: (b = 0)
+               ->  Hash
+                     ->  Seq Scan on prt2_p2 t2_2
+         ->  Nested Loop Anti Join
+               ->  Seq Scan on prt1_p3 t1_3
+                     Filter: (b = 0)
+               ->  Index Only Scan using iprt2_p3_b on prt2_p3 t2_3
+                     Index Cond: (b = t1_3.a)
+         ->  Hash Anti Join
+               Hash Cond: (t1_4.a = t2_4.b)
+               ->  Seq Scan on prt1_p4 t1_4
+                     Filter: (b = 0)
+               ->  Hash
+                     ->  Seq Scan on prt2_p4 t2_4
+(32 rows)
+
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+  a   | b |   c   
+------+---+-------
+ -250 | 0 | -0250
+ -200 | 0 | -0200
+ -100 | 0 | -0100
+  -50 | 0 | -0050
+   50 | 0 | 0050
+  100 | 0 | 0100
+  200 | 0 | 0200
+  250 | 0 | 0250
+  350 | 0 | 0350
+  400 | 0 | 0400
+  500 | 0 | 0500
+  550 | 0 | 0550
+  650 | 0 | 0650
+  700 | 0 | 0700
+(14 rows)
+
+-- 3-way join when not every pair of joining relation can use partition-wise
+-- join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a, t3.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON (t1.a = t2.b) INNER JOIN prt1 t3 ON (t2.b = t3.a) WHERE t2.a = 0 ORDER BY t1.a, t2.a, t3.c;
+                             QUERY PLAN                              
+---------------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t3.c
+   ->  Append
+         ->  Nested Loop Left Join
+               ->  Nested Loop
+                     ->  Seq Scan on prt2_p0 t2
+                           Filter: (a = 0)
+                     ->  Index Scan using iprt1_p0_a on prt1_p0 t3
+                           Index Cond: (a = t2.b)
+               ->  Index Only Scan using iprt1_p0_a on prt1_p0 t1
+                     Index Cond: (a = t2.b)
+         ->  Hash Right Join
+               Hash Cond: (t1_1.a = t2_1.b)
+               ->  Seq Scan on prt1_p1 t1_1
+               ->  Hash
+                     ->  Hash Join
+                           Hash Cond: (t3_1.a = t2_1.b)
+                           ->  Seq Scan on prt1_p1 t3_1
+                           ->  Hash
+                                 ->  Seq Scan on prt2_p1 t2_1
+                                       Filter: (a = 0)
+         ->  Nested Loop Left Join
+               ->  Nested Loop
+                     ->  Seq Scan on prt2_p2 t2_2
+                           Filter: (a = 0)
+                     ->  Index Scan using iprt1_p2_a on prt1_p2 t3_2
+                           Index Cond: (a = t2_2.b)
+               ->  Index Only Scan using iprt1_p2_a on prt1_p2 t1_2
+                     Index Cond: (a = t2_2.b)
+         ->  Nested Loop Left Join
+               ->  Nested Loop
+                     ->  Seq Scan on prt2_p3 t2_3
+                           Filter: (a = 0)
+                     ->  Index Scan using iprt1_p3_a on prt1_p3 t3_3
+                           Index Cond: (a = t2_3.b)
+               ->  Index Only Scan using iprt1_p3_a on prt1_p3 t1_3
+                     Index Cond: (a = t2_3.b)
+         ->  Hash Right Join
+               Hash Cond: (t1_4.a = t2_4.b)
+               ->  Seq Scan on prt1_p4 t1_4
+               ->  Hash
+                     ->  Hash Join
+                           Hash Cond: (t3_4.a = t2_4.b)
+                           ->  Seq Scan on prt1_p4 t3_4
+                           ->  Hash
+                                 ->  Seq Scan on prt2_p4 t2_4
+                                       Filter: (a = 0)
+(47 rows)
+
+SELECT t1.a, t2.a, t3.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON (t1.a = t2.b) INNER JOIN prt1 t3 ON (t2.b = t3.a) WHERE t2.a = 0 ORDER BY t1.a, t2.a, t3.c;
+  a   | a |   c   
+------+---+-------
+ -150 | 0 | -0150
+    0 | 0 | 0000
+  150 | 0 | 0150
+  300 | 0 | 0300
+  450 | 0 | 0450
+  600 | 0 | 0600
+  750 | 0 | 0750
+(7 rows)
+
+-- partition-wise join can not handle missing partition on the inner side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.b;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t2.b
+   ->  Hash Right Join
+         Hash Cond: (t1.a = t2.b)
+         ->  Append
+               ->  Seq Scan on prt1_p0 t1
+               ->  Seq Scan on prt1_p1 t1_1
+               ->  Seq Scan on prt1_p2 t1_2
+               ->  Seq Scan on prt1_p3 t1_3
+               ->  Seq Scan on prt1_p4 t1_4
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on prt2_p0 t2
+                           Filter: (a = 0)
+                     ->  Seq Scan on prt2_p1 t2_1
+                           Filter: (a = 0)
+                     ->  Seq Scan on prt2_p2 t2_2
+                           Filter: (a = 0)
+                     ->  Seq Scan on prt2_p3 t2_3
+                           Filter: (a = 0)
+                     ->  Seq Scan on prt2_p4 t2_4
+                           Filter: (a = 0)
+                     ->  Seq Scan on prt2_p5 t2_5
+                           Filter: (a = 0)
+(24 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE coalesce(t1.b, 0) + coalesce(t2.a, 0) = 0 ORDER BY t1.a, t2.a;
+                          QUERY PLAN                           
+---------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t2.a
+   ->  Hash Full Join
+         Hash Cond: (t1.a = t2.b)
+         Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.a, 0)) = 0)
+         ->  Append
+               ->  Seq Scan on prt1_p0 t1
+               ->  Seq Scan on prt1_p1 t1_1
+               ->  Seq Scan on prt1_p2 t1_2
+               ->  Seq Scan on prt1_p3 t1_3
+               ->  Seq Scan on prt1_p4 t1_4
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on prt2_p0 t2
+                     ->  Seq Scan on prt2_p1 t2_1
+                     ->  Seq Scan on prt2_p2 t2_2
+                     ->  Seq Scan on prt2_p3 t2_3
+                     ->  Seq Scan on prt2_p4 t2_4
+                     ->  Seq Scan on prt2_p5 t2_5
+(19 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t1.b, t1.c
+   ->  Hash Anti Join
+         Hash Cond: (t1.b = t2.a)
+         ->  Append
+               ->  Seq Scan on prt2_p0 t1
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p1 t1_1
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p2 t1_2
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p3 t1_3
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p4 t1_4
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p5 t1_5
+                     Filter: (a = 0)
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on prt1_p0 t2
+                     ->  Seq Scan on prt1_p1 t2_1
+                     ->  Seq Scan on prt1_p2 t2_2
+                     ->  Seq Scan on prt1_p3 t2_3
+                     ->  Seq Scan on prt1_p4 t2_4
+(24 rows)
+
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE prt2_p4;
+DROP TABLE prt2_p5;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (700);
+CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (700) TO (1000);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999, 3) i;
+ANALYZE prt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t1.a
+   ->  Hash Join
+         Hash Cond: (t2.b = t1.a)
+         ->  Append
+               ->  Seq Scan on prt2_p0 t2
+               ->  Seq Scan on prt2_p1 t2_1
+               ->  Seq Scan on prt2_p2 t2_2
+               ->  Seq Scan on prt2_p3 t2_3
+               ->  Seq Scan on prt2_p4 t2_4
+               ->  Seq Scan on prt2_p5 t2_5
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on prt1_p0 t1
+                           Filter: (b = 0)
+                     ->  Seq Scan on prt1_p1 t1_1
+                           Filter: (b = 0)
+                     ->  Seq Scan on prt1_p2 t1_2
+                           Filter: (b = 0)
+                     ->  Seq Scan on prt1_p3 t1_3
+                           Filter: (b = 0)
+                     ->  Seq Scan on prt1_p4 t1_4
+                           Filter: (b = 0)
+(23 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t1.a
+   ->  Hash Right Join
+         Hash Cond: (t2.b = t1.a)
+         ->  Append
+               ->  Seq Scan on prt2_p0 t2
+               ->  Seq Scan on prt2_p1 t2_1
+               ->  Seq Scan on prt2_p2 t2_2
+               ->  Seq Scan on prt2_p3 t2_3
+               ->  Seq Scan on prt2_p4 t2_4
+               ->  Seq Scan on prt2_p5 t2_5
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on prt1_p0 t1
+                           Filter: (b = 0)
+                     ->  Seq Scan on prt1_p1 t1_1
+                           Filter: (b = 0)
+                     ->  Seq Scan on prt1_p2 t1_2
+                           Filter: (b = 0)
+                     ->  Seq Scan on prt1_p3 t1_3
+                           Filter: (b = 0)
+                     ->  Seq Scan on prt1_p4 t1_4
+                           Filter: (b = 0)
+(23 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.a;
+                 QUERY PLAN                 
+--------------------------------------------
+ Hash Right Join
+   Hash Cond: (t1.a = t2.b)
+   ->  Append
+         ->  Seq Scan on prt1_p0 t1
+         ->  Seq Scan on prt1_p1 t1_1
+         ->  Seq Scan on prt1_p2 t1_2
+         ->  Seq Scan on prt1_p3 t1_3
+         ->  Seq Scan on prt1_p4 t1_4
+   ->  Hash
+         ->  Append
+               ->  Seq Scan on prt2_p0 t2
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p1 t2_1
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p2 t2_2
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p3 t2_3
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p4 t2_4
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p5 t2_5
+                     Filter: (a = 0)
+(22 rows)
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t1.a, t2.a
+   ->  Hash Join
+         Hash Cond: (t1.a = t2.b)
+         Join Filter: ((t1.b + t2.a) = 0)
+         ->  Append
+               ->  Seq Scan on prt1_p0 t1
+               ->  Seq Scan on prt1_p1 t1_1
+               ->  Seq Scan on prt1_p2 t1_2
+               ->  Seq Scan on prt1_p3 t1_3
+               ->  Seq Scan on prt1_p4 t1_4
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on prt2_p0 t2
+                     ->  Seq Scan on prt2_p1 t2_1
+                     ->  Seq Scan on prt2_p2 t2_2
+                     ->  Seq Scan on prt2_p3 t2_3
+                     ->  Seq Scan on prt2_p4 t2_4
+                     ->  Seq Scan on prt2_p5 t2_5
+(19 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Hash Semi Join
+         Hash Cond: (t1.a = t2.b)
+         ->  Append
+               ->  Seq Scan on prt1_p0 t1
+                     Filter: (b = 0)
+               ->  Seq Scan on prt1_p1 t1_1
+                     Filter: (b = 0)
+               ->  Seq Scan on prt1_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on prt1_p3 t1_3
+                     Filter: (b = 0)
+               ->  Seq Scan on prt1_p4 t1_4
+                     Filter: (b = 0)
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on prt2_p0 t2
+                     ->  Seq Scan on prt2_p1 t2_1
+                     ->  Seq Scan on prt2_p2 t2_2
+                     ->  Seq Scan on prt2_p3 t2_3
+                     ->  Seq Scan on prt2_p4 t2_4
+                     ->  Seq Scan on prt2_p5 t2_5
+(23 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t1.b, t1.c
+   ->  Hash Semi Join
+         Hash Cond: (t1.b = t2.a)
+         ->  Append
+               ->  Seq Scan on prt2_p0 t1
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p1 t1_1
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p2 t1_2
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p3 t1_3
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p4 t1_4
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p5 t1_5
+                     Filter: (a = 0)
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on prt1_p0 t2
+                     ->  Seq Scan on prt1_p1 t2_1
+                     ->  Seq Scan on prt1_p2 t2_2
+                     ->  Seq Scan on prt1_p3 t2_3
+                     ->  Seq Scan on prt1_p4 t2_4
+(24 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Hash Anti Join
+         Hash Cond: (t1.a = t2.b)
+         ->  Append
+               ->  Seq Scan on prt1_p0 t1
+                     Filter: (b = 0)
+               ->  Seq Scan on prt1_p1 t1_1
+                     Filter: (b = 0)
+               ->  Seq Scan on prt1_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on prt1_p3 t1_3
+                     Filter: (b = 0)
+               ->  Seq Scan on prt1_p4 t1_4
+                     Filter: (b = 0)
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on prt2_p0 t2
+                     ->  Seq Scan on prt2_p1 t2_1
+                     ->  Seq Scan on prt2_p2 t2_2
+                     ->  Seq Scan on prt2_p3 t2_3
+                     ->  Seq Scan on prt2_p4 t2_4
+                     ->  Seq Scan on prt2_p5 t2_5
+(23 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t1.b, t1.c
+   ->  Hash Anti Join
+         Hash Cond: (t1.b = t2.a)
+         ->  Append
+               ->  Seq Scan on prt2_p0 t1
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p1 t1_1
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p2 t1_2
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p3 t1_3
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p4 t1_4
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p5 t1_5
+                     Filter: (a = 0)
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on prt1_p0 t2
+                     ->  Seq Scan on prt1_p1 t2_1
+                     ->  Seq Scan on prt1_p2 t2_2
+                     ->  Seq Scan on prt1_p3 t2_3
+                     ->  Seq Scan on prt1_p4 t2_4
+(24 rows)
+
+--
+-- partitioned by multiple columns
+--
+CREATE TABLE prt1_m (a int, b int, c int) PARTITION BY RANGE(a, ((a + b)/2));
+CREATE TABLE prt1_m_p1 PARTITION OF prt1_m FOR VALUES FROM (0, 0) TO (250, 250);
+CREATE TABLE prt1_m_p2 PARTITION OF prt1_m FOR VALUES FROM (250, 250) TO (500, 500);
+CREATE TABLE prt1_m_p3 PARTITION OF prt1_m FOR VALUES FROM (500, 500) TO (600, 600);
+INSERT INTO prt1_m SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i;
+ANALYZE prt1_m;
+CREATE TABLE prt2_m (a int, b int, c int) PARTITION BY RANGE(((b + a)/2), b);
+CREATE TABLE prt2_m_p1 PARTITION OF prt2_m FOR VALUES FROM (0, 0) TO (250, 250);
+CREATE TABLE prt2_m_p2 PARTITION OF prt2_m FOR VALUES FROM (250, 250) TO (500, 500);
+CREATE TABLE prt2_m_p3 PARTITION OF prt2_m FOR VALUES FROM (500, 500) TO (600, 600);
+INSERT INTO prt2_m SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i;
+ANALYZE prt2_m;
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
+                                                             QUERY PLAN                                                             
+------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Sort Key: prt1_m_p1.a, prt2_m_p1.b
+   ->  Append
+         ->  Hash Full Join
+               Hash Cond: ((prt1_m_p1.a = ((prt2_m_p1.b + prt2_m_p1.a) / 2)) AND (((prt1_m_p1.a + prt1_m_p1.b) / 2) = prt2_m_p1.b))
+               ->  Seq Scan on prt1_m_p1
+                     Filter: (c = 0)
+               ->  Hash
+                     ->  Seq Scan on prt2_m_p1
+                           Filter: (c = 0)
+         ->  Hash Full Join
+               Hash Cond: ((prt1_m_p2.a = ((prt2_m_p2.b + prt2_m_p2.a) / 2)) AND (((prt1_m_p2.a + prt1_m_p2.b) / 2) = prt2_m_p2.b))
+               ->  Seq Scan on prt1_m_p2
+                     Filter: (c = 0)
+               ->  Hash
+                     ->  Seq Scan on prt2_m_p2
+                           Filter: (c = 0)
+         ->  Hash Full Join
+               Hash Cond: ((prt1_m_p3.a = ((prt2_m_p3.b + prt2_m_p3.a) / 2)) AND (((prt1_m_p3.a + prt1_m_p3.b) / 2) = prt2_m_p3.b))
+               ->  Seq Scan on prt1_m_p3
+                     Filter: (c = 0)
+               ->  Hash
+                     ->  Seq Scan on prt2_m_p3
+                           Filter: (c = 0)
+(24 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
+  a  | c |  b  | c 
+-----+---+-----+---
+   0 | 0 |   0 | 0
+  50 | 0 |     |  
+ 100 | 0 |     |  
+ 150 | 0 | 150 | 0
+ 200 | 0 |     |  
+ 250 | 0 |     |  
+ 300 | 0 | 300 | 0
+ 350 | 0 |     |  
+ 400 | 0 |     |  
+ 450 | 0 | 450 | 0
+ 500 | 0 |     |  
+ 550 | 0 |     |  
+     |   |  75 | 0
+     |   | 225 | 0
+     |   | 375 | 0
+     |   | 525 | 0
+(16 rows)
+
+--
+-- tests for list partitioned tables.
+--
+\set part_mod 17
+\set cond_mod 47
+\set num_rows 500
+CREATE TABLE plt1 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0001','0002','0003');
+CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0008','0009');
+CREATE TABLE plt1_p4 PARTITION OF plt1 FOR VALUES IN ('0000','0010');
+INSERT INTO plt1 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (7, 11, 12, 13, 14, 15, 16);
+ANALYSE plt1;
+-- plt2 have missing starting 0001, additional 0007, missing ending 0010
+-- and additional 0011 and 0012 bounds
+CREATE TABLE plt2 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0002','0003');
+CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0007','0008','0009');
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000','0011','0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 10, 13, 14, 15, 16);
+ANALYSE plt2;
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+                         QUERY PLAN                         
+------------------------------------------------------------
+ Sort
+   Sort Key: t1.a
+   ->  Append
+         ->  Hash Join
+               Hash Cond: ((t1.c)::text = (t2.c)::text)
+               Join Filter: ((t1.b + t2.b) = 0)
+               ->  Seq Scan on plt1_p4 t1
+               ->  Hash
+                     ->  Seq Scan on plt2_p4 t2
+         ->  Hash Join
+               Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+               Join Filter: ((t1_1.b + t2_1.b) = 0)
+               ->  Seq Scan on plt2_p1 t2_1
+               ->  Hash
+                     ->  Seq Scan on plt1_p1 t1_1
+         ->  Hash Join
+               Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+               Join Filter: ((t1_2.b + t2_2.b) = 0)
+               ->  Seq Scan on plt1_p2 t1_2
+               ->  Hash
+                     ->  Seq Scan on plt2_p2 t2_2
+         ->  Hash Join
+               Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+               Join Filter: ((t1_3.b + t2_3.b) = 0)
+               ->  Seq Scan on plt1_p3 t1_3
+               ->  Hash
+                     ->  Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+  a  |  c   |  a  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  94 | 0009 |  94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+                         QUERY PLAN                         
+------------------------------------------------------------
+ Sort
+   Sort Key: t1.a
+   ->  Append
+         ->  Hash Left Join
+               Hash Cond: ((t1.c)::text = (t2.c)::text)
+               Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+               ->  Seq Scan on plt1_p4 t1
+               ->  Hash
+                     ->  Seq Scan on plt2_p4 t2
+         ->  Hash Right Join
+               Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+               Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0)
+               ->  Seq Scan on plt2_p1 t2_1
+               ->  Hash
+                     ->  Seq Scan on plt1_p1 t1_1
+         ->  Hash Left Join
+               Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+               Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0)
+               ->  Seq Scan on plt1_p2 t1_2
+               ->  Hash
+                     ->  Seq Scan on plt2_p2 t2_2
+         ->  Hash Left Join
+               Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+               Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0)
+               ->  Seq Scan on plt1_p3 t1_3
+               ->  Hash
+                     ->  Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+  a  |  c   |  a  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  94 | 0009 |  94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 |     | 
+ 282 | 0010 |     | 
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(7 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+                         QUERY PLAN                         
+------------------------------------------------------------
+ Sort
+   Sort Key: t2.a
+   ->  Append
+         ->  Hash Right Join
+               Hash Cond: ((t1.c)::text = (t2.c)::text)
+               Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+               ->  Seq Scan on plt1_p4 t1
+               ->  Hash
+                     ->  Seq Scan on plt2_p4 t2
+         ->  Hash Left Join
+               Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+               Filter: ((COALESCE(t1_1.b, 0) + t2_1.b) = 0)
+               ->  Seq Scan on plt2_p1 t2_1
+               ->  Hash
+                     ->  Seq Scan on plt1_p1 t1_1
+         ->  Hash Left Join
+               Hash Cond: ((t2_2.c)::text = (t1_2.c)::text)
+               Filter: ((COALESCE(t1_2.b, 0) + t2_2.b) = 0)
+               ->  Seq Scan on plt2_p2 t2_2
+               ->  Hash
+                     ->  Seq Scan on plt1_p2 t1_2
+         ->  Hash Right Join
+               Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+               Filter: ((COALESCE(t1_3.b, 0) + t2_3.b) = 0)
+               ->  Seq Scan on plt1_p3 t1_3
+               ->  Hash
+                     ->  Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+  a  |  c   |  a  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  94 | 0009 |  94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+     |      | 470 | 0011
+(6 rows)
+
+-- full join
+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.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+                               QUERY PLAN                                
+-------------------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t2.a
+   ->  Append
+         ->  Hash Full Join
+               Hash Cond: ((t1.c)::text = (t2.c)::text)
+               Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+               ->  Seq Scan on plt1_p4 t1
+               ->  Hash
+                     ->  Seq Scan on plt2_p4 t2
+         ->  Hash Full Join
+               Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+               Filter: ((COALESCE(t1_1.b, 0) + COALESCE(t2_1.b, 0)) = 0)
+               ->  Seq Scan on plt2_p1 t2_1
+               ->  Hash
+                     ->  Seq Scan on plt1_p1 t1_1
+         ->  Hash Full Join
+               Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+               Filter: ((COALESCE(t1_2.b, 0) + COALESCE(t2_2.b, 0)) = 0)
+               ->  Seq Scan on plt1_p2 t1_2
+               ->  Hash
+                     ->  Seq Scan on plt2_p2 t2_2
+         ->  Hash Full Join
+               Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+               Filter: ((COALESCE(t1_3.b, 0) + COALESCE(t2_3.b, 0)) = 0)
+               ->  Seq Scan on plt1_p3 t1_3
+               ->  Hash
+                     ->  Seq Scan on plt2_p3 t2_3
+(27 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.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+  a  |  c   |  a  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  94 | 0009 |  94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 |     | 
+ 282 | 0010 |     | 
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+     |      | 470 | 0011
+(8 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Append
+         ->  Hash Join
+               Hash Cond: ((t2.c)::text = (t1.c)::text)
+               ->  HashAggregate
+                     Group Key: (t2.c)::text
+                     ->  Seq Scan on plt2_p4 t2
+               ->  Hash
+                     ->  Seq Scan on plt1_p4 t1
+                           Filter: (b = 0)
+         ->  Nested Loop
+               Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+               ->  HashAggregate
+                     Group Key: (t2_1.c)::text
+                     ->  Seq Scan on plt2_p1 t2_1
+               ->  Materialize
+                     ->  Seq Scan on plt1_p1 t1_1
+                           Filter: (b = 0)
+         ->  Nested Loop Semi Join
+               Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+               ->  Seq Scan on plt1_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p2 t2_2
+         ->  Nested Loop Semi Join
+               Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+               ->  Seq Scan on plt1_p3 t1_3
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p3 t2_3
+(29 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+  a  | b |  c   
+-----+---+------
+   0 | 0 | 0000
+  94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Append
+         ->  Nested Loop
+               Join Filter: ((t1.c)::text = (t2.c)::text)
+               ->  HashAggregate
+                     Group Key: (t2.c)::text
+                     ->  Seq Scan on plt1_p4 t2
+               ->  Materialize
+                     ->  Seq Scan on plt2_p4 t1
+                           Filter: (b = 0)
+         ->  Nested Loop Semi Join
+               Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+               ->  Seq Scan on plt2_p1 t1_1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p1 t2_1
+         ->  Nested Loop Semi Join
+               Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+               ->  Seq Scan on plt2_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p2 t2_2
+         ->  Nested Loop Semi Join
+               Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+               ->  Seq Scan on plt2_p3 t1_3
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p3 t2_3
+(26 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+  a  | b |  c   
+-----+---+------
+   0 | 0 | 0000
+  94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Append
+         ->  Nested Loop Anti Join
+               Join Filter: ((t1.c)::text = (t2.c)::text)
+               ->  Seq Scan on plt1_p4 t1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p4 t2
+         ->  Hash Anti Join
+               Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+               ->  Seq Scan on plt1_p1 t1_1
+                     Filter: (b = 0)
+               ->  Hash
+                     ->  Seq Scan on plt2_p1 t2_1
+         ->  Nested Loop Anti Join
+               Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+               ->  Seq Scan on plt1_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p2 t2_2
+         ->  Nested Loop Anti Join
+               Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+               ->  Seq Scan on plt1_p3 t1_3
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p3 t2_3
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+  a  | b |  c   
+-----+---+------
+ 188 | 0 | 0001
+ 282 | 0 | 0010
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Append
+         ->  Hash Anti Join
+               Hash Cond: ((t1.c)::text = (t2.c)::text)
+               ->  Seq Scan on plt2_p4 t1
+                     Filter: (b = 0)
+               ->  Hash
+                     ->  Seq Scan on plt1_p4 t2
+         ->  Nested Loop Anti Join
+               Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+               ->  Seq Scan on plt2_p1 t1_1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p1 t2_1
+         ->  Nested Loop Anti Join
+               Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+               ->  Seq Scan on plt2_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p2 t2_2
+         ->  Nested Loop Anti Join
+               Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+               ->  Seq Scan on plt2_p3 t1_3
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p3 t2_3
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+  a  | b |  c   
+-----+---+------
+ 470 | 0 | 0011
+(1 row)
+
+--
+-- list partitioned by expression
+--
+CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A'));
+CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0002', '0003');
+CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0004', '0005', '0006');
+CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0008', '0009');
+CREATE TABLE plt1_e_p4 PARTITION OF plt1_e FOR VALUES IN ('0000');
+INSERT INTO plt1_e SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 7, 10, 11, 12, 13, 14, 15, 16);
+ANALYZE plt1_e;
+-- test partition matching with N-way join
+EXPLAIN (COSTS OFF)
+SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
+                                           QUERY PLAN                                           
+------------------------------------------------------------------------------------------------
+ GroupAggregate
+   Group Key: t1.c, t2.c, t3.c
+   ->  Sort
+         Sort Key: t1.c, t3.c
+         ->  Append
+               ->  Hash Join
+                     Hash Cond: ((t1.c)::text = ltrim(t3.c, 'A'::text))
+                     ->  Hash Join
+                           Hash Cond: ((t2.b = t1.b) AND ((t2.c)::text = (t1.c)::text))
+                           ->  Seq Scan on plt2_p4 t2
+                           ->  Hash
+                                 ->  Seq Scan on plt1_p4 t1
+                     ->  Hash
+                           ->  Seq Scan on plt1_e_p4 t3
+               ->  Hash Join
+                     Hash Cond: ((t1_1.c)::text = ltrim(t3_1.c, 'A'::text))
+                     ->  Hash Join
+                           Hash Cond: ((t1_1.b = t2_1.b) AND ((t1_1.c)::text = (t2_1.c)::text))
+                           ->  Seq Scan on plt1_p1 t1_1
+                           ->  Hash
+                                 ->  Seq Scan on plt2_p1 t2_1
+                     ->  Hash
+                           ->  Seq Scan on plt1_e_p1 t3_1
+               ->  Hash Join
+                     Hash Cond: ((t1_2.c)::text = ltrim(t3_2.c, 'A'::text))
+                     ->  Hash Join
+                           Hash Cond: ((t1_2.b = t2_2.b) AND ((t1_2.c)::text = (t2_2.c)::text))
+                           ->  Seq Scan on plt1_p2 t1_2
+                           ->  Hash
+                                 ->  Seq Scan on plt2_p2 t2_2
+                     ->  Hash
+                           ->  Seq Scan on plt1_e_p2 t3_2
+               ->  Hash Join
+                     Hash Cond: ((t1_3.c)::text = ltrim(t3_3.c, 'A'::text))
+                     ->  Hash Join
+                           Hash Cond: ((t2_3.b = t1_3.b) AND ((t2_3.c)::text = (t1_3.c)::text))
+                           ->  Seq Scan on plt2_p3 t2_3
+                           ->  Hash
+                                 ->  Seq Scan on plt1_p3 t1_3
+                     ->  Hash
+                           ->  Seq Scan on plt1_e_p3 t3_3
+(41 rows)
+
+SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
+         avg          |         avg         |         avg          |  c   |  c   |  c   
+----------------------+---------------------+----------------------+------+------+------
+ 246.5000000000000000 | 22.4666666666666667 | 268.9666666666666667 | 0000 | 0000 | 0000
+ 248.5000000000000000 | 21.3333333333333333 | 269.8333333333333333 | 0002 | 0002 | 0002
+ 249.5000000000000000 | 22.3333333333333333 | 271.8333333333333333 | 0003 | 0003 | 0003
+ 250.5000000000000000 | 23.3333333333333333 | 273.8333333333333333 | 0004 | 0004 | 0004
+ 251.5000000000000000 | 22.7666666666666667 | 274.2666666666666667 | 0005 | 0005 | 0005
+ 252.5000000000000000 | 22.2000000000000000 | 274.7000000000000000 | 0006 | 0006 | 0006
+ 246.0000000000000000 | 23.9655172413793103 | 269.9655172413793103 | 0008 | 0008 | 0008
+ 247.0000000000000000 | 23.3448275862068966 | 270.3448275862068966 | 0009 | 0009 | 0009
+(8 rows)
+
+-- Add an extra partition to plt2 , Partition-wise join is possible with
+-- partitions on inner side are allowed
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (13, 14);
+ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+                         QUERY PLAN                         
+------------------------------------------------------------
+ Sort
+   Sort Key: t1.a
+   ->  Append
+         ->  Hash Join
+               Hash Cond: ((t1.c)::text = (t2.c)::text)
+               Join Filter: ((t1.b + t2.b) = 0)
+               ->  Seq Scan on plt1_p4 t1
+               ->  Hash
+                     ->  Seq Scan on plt2_p4 t2
+         ->  Hash Join
+               Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+               Join Filter: ((t1_1.b + t2_1.b) = 0)
+               ->  Seq Scan on plt2_p1 t2_1
+               ->  Hash
+                     ->  Seq Scan on plt1_p1 t1_1
+         ->  Hash Join
+               Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+               Join Filter: ((t1_2.b + t2_2.b) = 0)
+               ->  Seq Scan on plt1_p2 t1_2
+               ->  Hash
+                     ->  Seq Scan on plt2_p2 t2_2
+         ->  Hash Join
+               Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+               Join Filter: ((t1_3.b + t2_3.b) = 0)
+               ->  Seq Scan on plt1_p3 t1_3
+               ->  Hash
+                     ->  Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+  a  |  c   |  a  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  94 | 0009 |  94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+                         QUERY PLAN                         
+------------------------------------------------------------
+ Sort
+   Sort Key: t1.a
+   ->  Append
+         ->  Hash Left Join
+               Hash Cond: ((t1.c)::text = (t2.c)::text)
+               Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+               ->  Seq Scan on plt1_p4 t1
+               ->  Hash
+                     ->  Seq Scan on plt2_p4 t2
+         ->  Hash Right Join
+               Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+               Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0)
+               ->  Seq Scan on plt2_p1 t2_1
+               ->  Hash
+                     ->  Seq Scan on plt1_p1 t1_1
+         ->  Hash Left Join
+               Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+               Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0)
+               ->  Seq Scan on plt1_p2 t1_2
+               ->  Hash
+                     ->  Seq Scan on plt2_p2 t2_2
+         ->  Hash Left Join
+               Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+               Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0)
+               ->  Seq Scan on plt1_p3 t1_3
+               ->  Hash
+                     ->  Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+  a  |  c   |  a  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  94 | 0009 |  94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 |     | 
+ 282 | 0010 |     | 
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(7 rows)
+
+-- right join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t2.a
+   ->  Hash Right Join
+         Hash Cond: ((t1.c)::text = (t2.c)::text)
+         Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+         ->  Append
+               ->  Seq Scan on plt1_p4 t1
+               ->  Seq Scan on plt1_p1 t1_1
+               ->  Seq Scan on plt1_p2 t1_2
+               ->  Seq Scan on plt1_p3 t1_3
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on plt2_p4 t2
+                     ->  Seq Scan on plt2_p1 t2_1
+                     ->  Seq Scan on plt2_p2 t2_2
+                     ->  Seq Scan on plt2_p3 t2_3
+                     ->  Seq Scan on plt2_p5 t2_4
+(17 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+  a  |  c   |  a  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  94 | 0009 |  94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+     |      |  47 | 0013
+     |      | 470 | 0011
+     |      | 235 | 0014
+(8 rows)
+
+-- full join, partition-wise join can not handle extra partition on the outer
+-- side
+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.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+                          QUERY PLAN                           
+---------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t2.a
+   ->  Hash Full Join
+         Hash Cond: ((t1.c)::text = (t2.c)::text)
+         Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+         ->  Append
+               ->  Seq Scan on plt1_p4 t1
+               ->  Seq Scan on plt1_p1 t1_1
+               ->  Seq Scan on plt1_p2 t1_2
+               ->  Seq Scan on plt1_p3 t1_3
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on plt2_p4 t2
+                     ->  Seq Scan on plt2_p1 t2_1
+                     ->  Seq Scan on plt2_p2 t2_2
+                     ->  Seq Scan on plt2_p3 t2_3
+                     ->  Seq Scan on plt2_p5 t2_4
+(17 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.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+  a  |  c   |  a  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  94 | 0009 |  94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 |     | 
+ 282 | 0010 |     | 
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+     |      |  47 | 0013
+     |      | 235 | 0014
+     |      | 470 | 0011
+(10 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Append
+         ->  Hash Join
+               Hash Cond: ((t2.c)::text = (t1.c)::text)
+               ->  HashAggregate
+                     Group Key: (t2.c)::text
+                     ->  Seq Scan on plt2_p4 t2
+               ->  Hash
+                     ->  Seq Scan on plt1_p4 t1
+                           Filter: (b = 0)
+         ->  Nested Loop
+               Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+               ->  HashAggregate
+                     Group Key: (t2_1.c)::text
+                     ->  Seq Scan on plt2_p1 t2_1
+               ->  Materialize
+                     ->  Seq Scan on plt1_p1 t1_1
+                           Filter: (b = 0)
+         ->  Nested Loop Semi Join
+               Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+               ->  Seq Scan on plt1_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p2 t2_2
+         ->  Nested Loop Semi Join
+               Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+               ->  Seq Scan on plt1_p3 t1_3
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p3 t2_3
+(29 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+  a  | b |  c   
+-----+---+------
+   0 | 0 | 0000
+  94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Append
+         ->  Nested Loop
+               Join Filter: ((t1.c)::text = (t2.c)::text)
+               ->  HashAggregate
+                     Group Key: (t2.c)::text
+                     ->  Seq Scan on plt1_p4 t2
+               ->  Materialize
+                     ->  Seq Scan on plt2_p4 t1
+                           Filter: (b = 0)
+         ->  Nested Loop Semi Join
+               Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+               ->  Seq Scan on plt2_p1 t1_1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p1 t2_1
+         ->  Nested Loop Semi Join
+               Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+               ->  Seq Scan on plt2_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p2 t2_2
+         ->  Nested Loop Semi Join
+               Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+               ->  Seq Scan on plt2_p3 t1_3
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p3 t2_3
+(26 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+  a  | b |  c   
+-----+---+------
+   0 | 0 | 0000
+  94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Append
+         ->  Nested Loop Anti Join
+               Join Filter: ((t1.c)::text = (t2.c)::text)
+               ->  Seq Scan on plt1_p4 t1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p4 t2
+         ->  Hash Anti Join
+               Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+               ->  Seq Scan on plt1_p1 t1_1
+                     Filter: (b = 0)
+               ->  Hash
+                     ->  Seq Scan on plt2_p1 t2_1
+         ->  Nested Loop Anti Join
+               Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+               ->  Seq Scan on plt1_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p2 t2_2
+         ->  Nested Loop Anti Join
+               Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+               ->  Seq Scan on plt1_p3 t1_3
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p3 t2_3
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+  a  | b |  c   
+-----+---+------
+ 188 | 0 | 0001
+ 282 | 0 | 0010
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Hash Anti Join
+         Hash Cond: ((t1.c)::text = (t2.c)::text)
+         ->  Append
+               ->  Seq Scan on plt2_p4 t1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p1 t1_1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p3 t1_3
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p5 t1_4
+                     Filter: (b = 0)
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on plt1_p4 t2
+                     ->  Seq Scan on plt1_p1 t2_1
+                     ->  Seq Scan on plt1_p2 t2_2
+                     ->  Seq Scan on plt1_p3 t2_3
+(21 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+  a  | b |  c   
+-----+---+------
+  47 | 0 | 0013
+ 235 | 0 | 0014
+ 470 | 0 | 0011
+(3 rows)
+
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt2_p5;
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0001','0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (1, 13, 14);
+ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t1.a
+   ->  Hash Join
+         Hash Cond: ((t1.c)::text = (t2.c)::text)
+         Join Filter: ((t1.b + t2.b) = 0)
+         ->  Append
+               ->  Seq Scan on plt1_p4 t1
+               ->  Seq Scan on plt1_p1 t1_1
+               ->  Seq Scan on plt1_p2 t1_2
+               ->  Seq Scan on plt1_p3 t1_3
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on plt2_p4 t2
+                     ->  Seq Scan on plt2_p5 t2_1
+                     ->  Seq Scan on plt2_p1 t2_2
+                     ->  Seq Scan on plt2_p2 t2_3
+                     ->  Seq Scan on plt2_p3 t2_4
+(17 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t1.a
+   ->  Hash Left Join
+         Hash Cond: ((t1.c)::text = (t2.c)::text)
+         Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+         ->  Append
+               ->  Seq Scan on plt1_p4 t1
+               ->  Seq Scan on plt1_p1 t1_1
+               ->  Seq Scan on plt1_p2 t1_2
+               ->  Seq Scan on plt1_p3 t1_3
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on plt2_p4 t2
+                     ->  Seq Scan on plt2_p5 t2_1
+                     ->  Seq Scan on plt2_p1 t2_2
+                     ->  Seq Scan on plt2_p2 t2_3
+                     ->  Seq Scan on plt2_p3 t2_4
+(17 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t2.a
+   ->  Hash Right Join
+         Hash Cond: ((t1.c)::text = (t2.c)::text)
+         Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+         ->  Append
+               ->  Seq Scan on plt1_p4 t1
+               ->  Seq Scan on plt1_p1 t1_1
+               ->  Seq Scan on plt1_p2 t1_2
+               ->  Seq Scan on plt1_p3 t1_3
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on plt2_p4 t2
+                     ->  Seq Scan on plt2_p5 t2_1
+                     ->  Seq Scan on plt2_p1 t2_2
+                     ->  Seq Scan on plt2_p2 t2_3
+                     ->  Seq Scan on plt2_p3 t2_4
+(17 rows)
+
+-- full join
+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.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+                          QUERY PLAN                           
+---------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t2.a
+   ->  Hash Full Join
+         Hash Cond: ((t1.c)::text = (t2.c)::text)
+         Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+         ->  Append
+               ->  Seq Scan on plt1_p4 t1
+               ->  Seq Scan on plt1_p1 t1_1
+               ->  Seq Scan on plt1_p2 t1_2
+               ->  Seq Scan on plt1_p3 t1_3
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on plt2_p4 t2
+                     ->  Seq Scan on plt2_p5 t2_1
+                     ->  Seq Scan on plt2_p1 t2_2
+                     ->  Seq Scan on plt2_p2 t2_3
+                     ->  Seq Scan on plt2_p3 t2_4
+(17 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Hash Join
+         Hash Cond: ((t1.c)::text = (t2.c)::text)
+         ->  Append
+               ->  Seq Scan on plt1_p4 t1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p1 t1_1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p3 t1_3
+                     Filter: (b = 0)
+         ->  Hash
+               ->  HashAggregate
+                     Group Key: (t2.c)::text
+                     ->  Result
+                           ->  Append
+                                 ->  Seq Scan on plt2_p4 t2
+                                 ->  Seq Scan on plt2_p5 t2_1
+                                 ->  Seq Scan on plt2_p1 t2_2
+                                 ->  Seq Scan on plt2_p2 t2_3
+                                 ->  Seq Scan on plt2_p3 t2_4
+(23 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Hash Join
+         Hash Cond: ((t1.c)::text = (t2.c)::text)
+         ->  Append
+               ->  Seq Scan on plt2_p4 t1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p5 t1_1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p1 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p2 t1_3
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p3 t1_4
+                     Filter: (b = 0)
+         ->  Hash
+               ->  HashAggregate
+                     Group Key: (t2.c)::text
+                     ->  Result
+                           ->  Append
+                                 ->  Seq Scan on plt1_p4 t2
+                                 ->  Seq Scan on plt1_p1 t2_1
+                                 ->  Seq Scan on plt1_p2 t2_2
+                                 ->  Seq Scan on plt1_p3 t2_3
+(24 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                     QUERY PLAN                     
+----------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Nested Loop Anti Join
+         Join Filter: ((t1.c)::text = (t2.c)::text)
+         ->  Append
+               ->  Seq Scan on plt1_p4 t1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p1 t1_1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p3 t1_3
+                     Filter: (b = 0)
+         ->  Materialize
+               ->  Append
+                     ->  Seq Scan on plt2_p4 t2
+                     ->  Seq Scan on plt2_p5 t2_1
+                     ->  Seq Scan on plt2_p1 t2_2
+                     ->  Seq Scan on plt2_p2 t2_3
+                     ->  Seq Scan on plt2_p3 t2_4
+(20 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Hash Anti Join
+         Hash Cond: ((t1.c)::text = (t2.c)::text)
+         ->  Append
+               ->  Seq Scan on plt2_p4 t1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p5 t1_1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p1 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p2 t1_3
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p3 t1_4
+                     Filter: (b = 0)
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on plt1_p4 t2
+                     ->  Seq Scan on plt1_p1 t2_1
+                     ->  Seq Scan on plt1_p2 t2_2
+                     ->  Seq Scan on plt1_p3 t2_3
+(21 rows)
+
+-- partition have a NULL on one side, Partition-wise join is possible with
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- in this case NULL will be treated as addition partition bounds.
+DROP TABLE plt2_p5;
+DROP TABLE plt2_p4;
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000',NULL,'0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, case when i % :part_mod = 11 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (0,11,12);
+ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+                         QUERY PLAN                         
+------------------------------------------------------------
+ Sort
+   Sort Key: t1.a
+   ->  Append
+         ->  Hash Join
+               Hash Cond: ((t2.c)::text = (t1.c)::text)
+               Join Filter: ((t1.b + t2.b) = 0)
+               ->  Seq Scan on plt2_p4 t2
+               ->  Hash
+                     ->  Seq Scan on plt1_p4 t1
+         ->  Hash Join
+               Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+               Join Filter: ((t1_1.b + t2_1.b) = 0)
+               ->  Seq Scan on plt2_p1 t2_1
+               ->  Hash
+                     ->  Seq Scan on plt1_p1 t1_1
+         ->  Hash Join
+               Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+               Join Filter: ((t1_2.b + t2_2.b) = 0)
+               ->  Seq Scan on plt1_p2 t1_2
+               ->  Hash
+                     ->  Seq Scan on plt2_p2 t2_2
+         ->  Hash Join
+               Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+               Join Filter: ((t1_3.b + t2_3.b) = 0)
+               ->  Seq Scan on plt1_p3 t1_3
+               ->  Hash
+                     ->  Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+  a  |  c   |  a  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  94 | 0009 |  94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(5 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+                         QUERY PLAN                         
+------------------------------------------------------------
+ Sort
+   Sort Key: t1.a
+   ->  Append
+         ->  Hash Right Join
+               Hash Cond: ((t2.c)::text = (t1.c)::text)
+               Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+               ->  Seq Scan on plt2_p4 t2
+               ->  Hash
+                     ->  Seq Scan on plt1_p4 t1
+         ->  Hash Right Join
+               Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+               Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0)
+               ->  Seq Scan on plt2_p1 t2_1
+               ->  Hash
+                     ->  Seq Scan on plt1_p1 t1_1
+         ->  Hash Left Join
+               Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+               Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0)
+               ->  Seq Scan on plt1_p2 t1_2
+               ->  Hash
+                     ->  Seq Scan on plt2_p2 t2_2
+         ->  Hash Left Join
+               Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+               Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0)
+               ->  Seq Scan on plt1_p3 t1_3
+               ->  Hash
+                     ->  Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+  a  |  c   |  a  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  94 | 0009 |  94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 |     | 
+ 282 | 0010 |     | 
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+(7 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+                         QUERY PLAN                         
+------------------------------------------------------------
+ Sort
+   Sort Key: t2.a
+   ->  Append
+         ->  Hash Left Join
+               Hash Cond: ((t2.c)::text = (t1.c)::text)
+               Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+               ->  Seq Scan on plt2_p4 t2
+               ->  Hash
+                     ->  Seq Scan on plt1_p4 t1
+         ->  Hash Left Join
+               Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+               Filter: ((COALESCE(t1_1.b, 0) + t2_1.b) = 0)
+               ->  Seq Scan on plt2_p1 t2_1
+               ->  Hash
+                     ->  Seq Scan on plt1_p1 t1_1
+         ->  Hash Left Join
+               Hash Cond: ((t2_2.c)::text = (t1_2.c)::text)
+               Filter: ((COALESCE(t1_2.b, 0) + t2_2.b) = 0)
+               ->  Seq Scan on plt2_p2 t2_2
+               ->  Hash
+                     ->  Seq Scan on plt1_p2 t1_2
+         ->  Hash Right Join
+               Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+               Filter: ((COALESCE(t1_3.b, 0) + t2_3.b) = 0)
+               ->  Seq Scan on plt1_p3 t1_3
+               ->  Hash
+                     ->  Seq Scan on plt2_p3 t2_3
+(27 rows)
+
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+  a  |  c   |  a  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  94 | 0009 |  94 | 0009
+ 141 | 0005 | 141 | 0005
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+     |      | 470 | 
+(6 rows)
+
+-- full join
+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.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+                               QUERY PLAN                                
+-------------------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t2.a
+   ->  Append
+         ->  Hash Full Join
+               Hash Cond: ((t2.c)::text = (t1.c)::text)
+               Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+               ->  Seq Scan on plt2_p4 t2
+               ->  Hash
+                     ->  Seq Scan on plt1_p4 t1
+         ->  Hash Full Join
+               Hash Cond: ((t2_1.c)::text = (t1_1.c)::text)
+               Filter: ((COALESCE(t1_1.b, 0) + COALESCE(t2_1.b, 0)) = 0)
+               ->  Seq Scan on plt2_p1 t2_1
+               ->  Hash
+                     ->  Seq Scan on plt1_p1 t1_1
+         ->  Hash Full Join
+               Hash Cond: ((t1_2.c)::text = (t2_2.c)::text)
+               Filter: ((COALESCE(t1_2.b, 0) + COALESCE(t2_2.b, 0)) = 0)
+               ->  Seq Scan on plt1_p2 t1_2
+               ->  Hash
+                     ->  Seq Scan on plt2_p2 t2_2
+         ->  Hash Full Join
+               Hash Cond: ((t1_3.c)::text = (t2_3.c)::text)
+               Filter: ((COALESCE(t1_3.b, 0) + COALESCE(t2_3.b, 0)) = 0)
+               ->  Seq Scan on plt1_p3 t1_3
+               ->  Hash
+                     ->  Seq Scan on plt2_p3 t2_3
+(27 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.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+  a  |  c   |  a  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  94 | 0009 |  94 | 0009
+ 141 | 0005 | 141 | 0005
+ 188 | 0001 |     | 
+ 282 | 0010 |     | 
+ 329 | 0006 | 329 | 0006
+ 376 | 0002 | 376 | 0002
+     |      | 470 | 
+(8 rows)
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Append
+         ->  Nested Loop
+               Join Filter: ((t1.c)::text = (t2.c)::text)
+               ->  HashAggregate
+                     Group Key: (t2.c)::text
+                     ->  Seq Scan on plt2_p4 t2
+               ->  Materialize
+                     ->  Seq Scan on plt1_p4 t1
+                           Filter: (b = 0)
+         ->  Nested Loop
+               Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+               ->  HashAggregate
+                     Group Key: (t2_1.c)::text
+                     ->  Seq Scan on plt2_p1 t2_1
+               ->  Materialize
+                     ->  Seq Scan on plt1_p1 t1_1
+                           Filter: (b = 0)
+         ->  Nested Loop Semi Join
+               Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+               ->  Seq Scan on plt1_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p2 t2_2
+         ->  Nested Loop Semi Join
+               Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+               ->  Seq Scan on plt1_p3 t1_3
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p3 t2_3
+(29 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+  a  | b |  c   
+-----+---+------
+   0 | 0 | 0000
+  94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Append
+         ->  Nested Loop
+               Join Filter: ((t1.c)::text = (t2.c)::text)
+               ->  HashAggregate
+                     Group Key: (t2.c)::text
+                     ->  Seq Scan on plt1_p4 t2
+               ->  Materialize
+                     ->  Seq Scan on plt2_p4 t1
+                           Filter: (b = 0)
+         ->  Nested Loop Semi Join
+               Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+               ->  Seq Scan on plt2_p1 t1_1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p1 t2_1
+         ->  Nested Loop Semi Join
+               Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+               ->  Seq Scan on plt2_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p2 t2_2
+         ->  Nested Loop Semi Join
+               Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+               ->  Seq Scan on plt2_p3 t1_3
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p3 t2_3
+(26 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+  a  | b |  c   
+-----+---+------
+   0 | 0 | 0000
+  94 | 0 | 0009
+ 141 | 0 | 0005
+ 329 | 0 | 0006
+ 376 | 0 | 0002
+(5 rows)
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Append
+         ->  Nested Loop Anti Join
+               Join Filter: ((t1.c)::text = (t2.c)::text)
+               ->  Seq Scan on plt1_p4 t1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p4 t2
+         ->  Hash Anti Join
+               Hash Cond: ((t1_1.c)::text = (t2_1.c)::text)
+               ->  Seq Scan on plt1_p1 t1_1
+                     Filter: (b = 0)
+               ->  Hash
+                     ->  Seq Scan on plt2_p1 t2_1
+         ->  Nested Loop Anti Join
+               Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+               ->  Seq Scan on plt1_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p2 t2_2
+         ->  Nested Loop Anti Join
+               Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+               ->  Seq Scan on plt1_p3 t1_3
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p3 t2_3
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+  a  | b |  c   
+-----+---+------
+ 188 | 0 | 0001
+ 282 | 0 | 0010
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Append
+         ->  Hash Anti Join
+               Hash Cond: ((t1.c)::text = (t2.c)::text)
+               ->  Seq Scan on plt2_p4 t1
+                     Filter: (b = 0)
+               ->  Hash
+                     ->  Seq Scan on plt1_p4 t2
+         ->  Nested Loop Anti Join
+               Join Filter: ((t1_1.c)::text = (t2_1.c)::text)
+               ->  Seq Scan on plt2_p1 t1_1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p1 t2_1
+         ->  Nested Loop Anti Join
+               Join Filter: ((t1_2.c)::text = (t2_2.c)::text)
+               ->  Seq Scan on plt2_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p2 t2_2
+         ->  Nested Loop Anti Join
+               Join Filter: ((t1_3.c)::text = (t2_3.c)::text)
+               ->  Seq Scan on plt2_p3 t1_3
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p3 t2_3
+(24 rows)
+
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+  a  | b | c 
+-----+---+---
+ 470 | 0 | 
+(1 row)
+
+-- partition have a NULL on both side with different partition bounds w.r.t other side
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt1_p3;
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN (NULL,'0008','0009');
+INSERT INTO plt1 SELECT i, i % :cond_mod, case when i % :part_mod = 7 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (7,8,9);
+ANALYZE plt1;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t1.a
+   ->  Hash Join
+         Hash Cond: ((t2.c)::text = (t1.c)::text)
+         Join Filter: ((t1.b + t2.b) = 0)
+         ->  Append
+               ->  Seq Scan on plt2_p4 t2
+               ->  Seq Scan on plt2_p1 t2_1
+               ->  Seq Scan on plt2_p2 t2_2
+               ->  Seq Scan on plt2_p3 t2_3
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on plt1_p4 t1
+                     ->  Seq Scan on plt1_p1 t1_1
+                     ->  Seq Scan on plt1_p2 t1_2
+                     ->  Seq Scan on plt1_p3 t1_3
+(16 rows)
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t1.a
+   ->  Hash Right Join
+         Hash Cond: ((t2.c)::text = (t1.c)::text)
+         Filter: ((t1.b + COALESCE(t2.b, 0)) = 0)
+         ->  Append
+               ->  Seq Scan on plt2_p4 t2
+               ->  Seq Scan on plt2_p1 t2_1
+               ->  Seq Scan on plt2_p2 t2_2
+               ->  Seq Scan on plt2_p3 t2_3
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on plt1_p4 t1
+                     ->  Seq Scan on plt1_p1 t1_1
+                     ->  Seq Scan on plt1_p2 t1_2
+                     ->  Seq Scan on plt1_p3 t1_3
+(16 rows)
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t2.a
+   ->  Hash Left Join
+         Hash Cond: ((t2.c)::text = (t1.c)::text)
+         Filter: ((COALESCE(t1.b, 0) + t2.b) = 0)
+         ->  Append
+               ->  Seq Scan on plt2_p4 t2
+               ->  Seq Scan on plt2_p1 t2_1
+               ->  Seq Scan on plt2_p2 t2_2
+               ->  Seq Scan on plt2_p3 t2_3
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on plt1_p4 t1
+                     ->  Seq Scan on plt1_p1 t1_1
+                     ->  Seq Scan on plt1_p2 t1_2
+                     ->  Seq Scan on plt1_p3 t1_3
+(16 rows)
+
+-- full join
+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.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+                          QUERY PLAN                           
+---------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t2.a
+   ->  Hash Full Join
+         Hash Cond: ((t2.c)::text = (t1.c)::text)
+         Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0)
+         ->  Append
+               ->  Seq Scan on plt2_p4 t2
+               ->  Seq Scan on plt2_p1 t2_1
+               ->  Seq Scan on plt2_p2 t2_2
+               ->  Seq Scan on plt2_p3 t2_3
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on plt1_p4 t1
+                     ->  Seq Scan on plt1_p1 t1_1
+                     ->  Seq Scan on plt1_p2 t1_2
+                     ->  Seq Scan on plt1_p3 t1_3
+(16 rows)
 
-RESET enable_hashjoin;
-RESET enable_nestloop;
---
--- partitioned by multiple columns
---
-CREATE TABLE prt1_m (a int, b int, c int) PARTITION BY RANGE(a, ((a + b)/2));
-CREATE TABLE prt1_m_p1 PARTITION OF prt1_m FOR VALUES FROM (0, 0) TO (250, 250);
-CREATE TABLE prt1_m_p2 PARTITION OF prt1_m FOR VALUES FROM (250, 250) TO (500, 500);
-CREATE TABLE prt1_m_p3 PARTITION OF prt1_m FOR VALUES FROM (500, 500) TO (600, 600);
-INSERT INTO prt1_m SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i;
-ANALYZE prt1_m;
-CREATE TABLE prt2_m (a int, b int, c int) PARTITION BY RANGE(((b + a)/2), b);
-CREATE TABLE prt2_m_p1 PARTITION OF prt2_m FOR VALUES FROM (0, 0) TO (250, 250);
-CREATE TABLE prt2_m_p2 PARTITION OF prt2_m FOR VALUES FROM (250, 250) TO (500, 500);
-CREATE TABLE prt2_m_p3 PARTITION OF prt2_m FOR VALUES FROM (500, 500) TO (600, 600);
-INSERT INTO prt2_m SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i;
-ANALYZE prt2_m;
+-- semi join
 EXPLAIN (COSTS OFF)
-SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
-                                                             QUERY PLAN                                                             
-------------------------------------------------------------------------------------------------------------------------------------
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                          QUERY PLAN                          
+--------------------------------------------------------------
  Sort
-   Sort Key: prt1_m_p1.a, prt2_m_p1.b
-   ->  Append
-         ->  Hash Full Join
-               Hash Cond: ((prt1_m_p1.a = ((prt2_m_p1.b + prt2_m_p1.a) / 2)) AND (((prt1_m_p1.a + prt1_m_p1.b) / 2) = prt2_m_p1.b))
-               ->  Seq Scan on prt1_m_p1
-                     Filter: (c = 0)
-               ->  Hash
-                     ->  Seq Scan on prt2_m_p1
-                           Filter: (c = 0)
-         ->  Hash Full Join
-               Hash Cond: ((prt1_m_p2.a = ((prt2_m_p2.b + prt2_m_p2.a) / 2)) AND (((prt1_m_p2.a + prt1_m_p2.b) / 2) = prt2_m_p2.b))
-               ->  Seq Scan on prt1_m_p2
-                     Filter: (c = 0)
-               ->  Hash
-                     ->  Seq Scan on prt2_m_p2
-                           Filter: (c = 0)
-         ->  Hash Full Join
-               Hash Cond: ((prt1_m_p3.a = ((prt2_m_p3.b + prt2_m_p3.a) / 2)) AND (((prt1_m_p3.a + prt1_m_p3.b) / 2) = prt2_m_p3.b))
-               ->  Seq Scan on prt1_m_p3
-                     Filter: (c = 0)
-               ->  Hash
-                     ->  Seq Scan on prt2_m_p3
-                           Filter: (c = 0)
-(24 rows)
+   Sort Key: t1.a, t1.c
+   ->  Hash Join
+         Hash Cond: ((t1.c)::text = (t2.c)::text)
+         ->  Append
+               ->  Seq Scan on plt1_p4 t1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p1 t1_1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p3 t1_3
+                     Filter: (b = 0)
+         ->  Hash
+               ->  HashAggregate
+                     Group Key: (t2.c)::text
+                     ->  Result
+                           ->  Append
+                                 ->  Seq Scan on plt2_p4 t2
+                                 ->  Seq Scan on plt2_p1 t2_1
+                                 ->  Seq Scan on plt2_p2 t2_2
+                                 ->  Seq Scan on plt2_p3 t2_3
+(22 rows)
 
-SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
-  a  | c |  b  | c 
------+---+-----+---
-   0 | 0 |   0 | 0
-  50 | 0 |     |  
- 100 | 0 |     |  
- 150 | 0 | 150 | 0
- 200 | 0 |     |  
- 250 | 0 |     |  
- 300 | 0 | 300 | 0
- 350 | 0 |     |  
- 400 | 0 |     |  
- 450 | 0 | 450 | 0
- 500 | 0 |     |  
- 550 | 0 |     |  
-     |   |  75 | 0
-     |   | 225 | 0
-     |   | 375 | 0
-     |   | 525 | 0
-(16 rows)
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Hash Join
+         Hash Cond: ((t1.c)::text = (t2.c)::text)
+         ->  Append
+               ->  Seq Scan on plt2_p4 t1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p1 t1_1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p3 t1_3
+                     Filter: (b = 0)
+         ->  Hash
+               ->  HashAggregate
+                     Group Key: (t2.c)::text
+                     ->  Result
+                           ->  Append
+                                 ->  Seq Scan on plt1_p4 t2
+                                 ->  Seq Scan on plt1_p1 t2_1
+                                 ->  Seq Scan on plt1_p2 t2_2
+                                 ->  Seq Scan on plt1_p3 t2_3
+(22 rows)
 
---
--- tests for list partitioned tables.
---
-CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c);
-CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) 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 ('0000', '0003', '0004', '0010');
-CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i;
-ANALYZE plt2;
---
--- list partitioned by expression
---
-CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A'));
-CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
-ANALYZE plt1_e;
--- test partition matching with N-way join
+-- anti join
 EXPLAIN (COSTS OFF)
-SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
-                                   QUERY PLAN                                   
---------------------------------------------------------------------------------
- GroupAggregate
-   Group Key: t1.c, t2.c, t3.c
-   ->  Sort
-         Sort Key: t1.c, t3.c
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Hash Anti Join
+         Hash Cond: ((t1.c)::text = (t2.c)::text)
          ->  Append
-               ->  Hash Join
-                     Hash Cond: (t1.c = ltrim(t3.c, 'A'::text))
-                     ->  Hash Join
-                           Hash Cond: ((t1.b = t2.b) AND (t1.c = t2.c))
-                           ->  Seq Scan on plt1_p1 t1
-                           ->  Hash
-                                 ->  Seq Scan on plt2_p1 t2
-                     ->  Hash
-                           ->  Seq Scan on plt1_e_p1 t3
-               ->  Hash Join
-                     Hash Cond: (t1_1.c = ltrim(t3_1.c, 'A'::text))
-                     ->  Hash Join
-                           Hash Cond: ((t1_1.b = t2_1.b) AND (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 plt1_e_p2 t3_1
-               ->  Hash Join
-                     Hash Cond: (t1_2.c = ltrim(t3_2.c, 'A'::text))
-                     ->  Hash Join
-                           Hash Cond: ((t1_2.b = t2_2.b) AND (t1_2.c = t2_2.c))
-                           ->  Seq Scan on plt1_p3 t1_2
-                           ->  Hash
-                                 ->  Seq Scan on plt2_p3 t2_2
-                     ->  Hash
-                           ->  Seq Scan on plt1_e_p3 t3_2
-(32 rows)
+               ->  Seq Scan on plt1_p4 t1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p1 t1_1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt1_p3 t1_3
+                     Filter: (b = 0)
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on plt2_p4 t2
+                     ->  Seq Scan on plt2_p1 t2_1
+                     ->  Seq Scan on plt2_p2 t2_2
+                     ->  Seq Scan on plt2_p3 t2_3
+(19 rows)
 
-SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
-         avg          |         avg          |          avg          |  c   |  c   |   c   
-----------------------+----------------------+-----------------------+------+------+-------
-  24.0000000000000000 |  24.0000000000000000 |   48.0000000000000000 | 0000 | 0000 | A0000
-  75.0000000000000000 |  75.0000000000000000 |  148.0000000000000000 | 0001 | 0001 | A0001
- 123.0000000000000000 | 123.0000000000000000 |  248.0000000000000000 | 0002 | 0002 | A0002
- 174.0000000000000000 | 174.0000000000000000 |  348.0000000000000000 | 0003 | 0003 | A0003
- 225.0000000000000000 | 225.0000000000000000 |  448.0000000000000000 | 0004 | 0004 | A0004
- 273.0000000000000000 | 273.0000000000000000 |  548.0000000000000000 | 0005 | 0005 | A0005
- 324.0000000000000000 | 324.0000000000000000 |  648.0000000000000000 | 0006 | 0006 | A0006
- 375.0000000000000000 | 375.0000000000000000 |  748.0000000000000000 | 0007 | 0007 | A0007
- 423.0000000000000000 | 423.0000000000000000 |  848.0000000000000000 | 0008 | 0008 | A0008
- 474.0000000000000000 | 474.0000000000000000 |  948.0000000000000000 | 0009 | 0009 | A0009
- 525.0000000000000000 | 525.0000000000000000 | 1048.0000000000000000 | 0010 | 0010 | A0010
- 573.0000000000000000 | 573.0000000000000000 | 1148.0000000000000000 | 0011 | 0011 | A0011
-(12 rows)
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+                    QUERY PLAN                    
+--------------------------------------------------
+ Sort
+   Sort Key: t1.a, t1.c
+   ->  Hash Anti Join
+         Hash Cond: ((t1.c)::text = (t2.c)::text)
+         ->  Append
+               ->  Seq Scan on plt2_p4 t1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p1 t1_1
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p2 t1_2
+                     Filter: (b = 0)
+               ->  Seq Scan on plt2_p3 t1_3
+                     Filter: (b = 0)
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on plt1_p4 t2
+                     ->  Seq Scan on plt1_p1 t2_1
+                     ->  Seq Scan on plt1_p2 t2_2
+                     ->  Seq Scan on plt1_p3 t2_3
+(19 rows)
 
 -- joins where one of the relations is proven empty
 EXPLAIN (COSTS OFF)
@@ -1225,22 +4003,22 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1
 --------------------------------------------------
  Hash Left Join
    Hash Cond: (t2.b = a)
-   ->  Append
-         ->  Hash Join
-               Hash Cond: (t3.a = t2.b)
-               ->  Seq Scan on prt1_p1 t3
-               ->  Hash
-                     ->  Seq Scan on prt2_p1 t2
-         ->  Hash Join
-               Hash Cond: (t3_1.a = t2_1.b)
-               ->  Seq Scan on prt1_p2 t3_1
-               ->  Hash
-                     ->  Seq Scan on prt2_p2 t2_1
-         ->  Hash Join
-               Hash Cond: (t3_2.a = t2_2.b)
-               ->  Seq Scan on prt1_p3 t3_2
-               ->  Hash
-                     ->  Seq Scan on prt2_p3 t2_2
+   ->  Hash Join
+         Hash Cond: (t3.a = t2.b)
+         ->  Append
+               ->  Seq Scan on prt1_p0 t3
+               ->  Seq Scan on prt1_p1 t3_1
+               ->  Seq Scan on prt1_p2 t3_2
+               ->  Seq Scan on prt1_p3 t3_3
+               ->  Seq Scan on prt1_p4 t3_4
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on prt2_p0 t2
+                     ->  Seq Scan on prt2_p1 t2_1
+                     ->  Seq Scan on prt2_p2 t2_2
+                     ->  Seq Scan on prt2_p3 t2_3
+                     ->  Seq Scan on prt2_p4 t2_4
+                     ->  Seq Scan on prt2_p5 t2_5
    ->  Hash
          ->  Result
                One-Time Filter: false
@@ -1255,16 +4033,22 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1
    ->  Hash Left Join
          Hash Cond: (t2.b = a)
          ->  Append
-               ->  Seq Scan on prt2_p1 t2
+               ->  Seq Scan on prt2_p0 t2
                      Filter: (a = 0)
-               ->  Seq Scan on prt2_p2 t2_1
+               ->  Seq Scan on prt2_p1 t2_1
                      Filter: (a = 0)
-               ->  Seq Scan on prt2_p3 t2_2
+               ->  Seq Scan on prt2_p2 t2_2
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p3 t2_3
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p4 t2_4
+                     Filter: (a = 0)
+               ->  Seq Scan on prt2_p5 t2_5
                      Filter: (a = 0)
          ->  Hash
                ->  Result
                      One-Time Filter: false
-(14 rows)
+(20 rows)
 
 --
 -- tests for hash partitioned tables.
@@ -1340,41 +4124,9 @@ SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM pht1 t1, ph
  273.0000000000000000 | 273.0000000000000000 | 548.0000000000000000 | 0005 | 0005 | A0005
 (6 rows)
 
--- test default partition behavior for range
-ALTER TABLE prt1 DETACH PARTITION prt1_p3;
-ALTER TABLE prt1 ATTACH PARTITION prt1_p3 DEFAULT;
-ANALYZE prt1;
-ALTER TABLE prt2 DETACH PARTITION prt2_p3;
-ALTER TABLE prt2 ATTACH PARTITION prt2_p3 DEFAULT;
-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;
-                    QUERY PLAN                    
---------------------------------------------------
- Sort
-   Sort Key: t1.a
-   ->  Append
-         ->  Hash Join
-               Hash Cond: (t2.b = t1.a)
-               ->  Seq Scan on prt2_p1 t2
-               ->  Hash
-                     ->  Seq Scan on prt1_p1 t1
-                           Filter: (b = 0)
-         ->  Hash Join
-               Hash Cond: (t2_1.b = t1_1.a)
-               ->  Seq Scan on prt2_p2 t2_1
-               ->  Hash
-                     ->  Seq Scan on prt1_p2 t1_1
-                           Filter: (b = 0)
-         ->  Hash Join
-               Hash Cond: (t2_2.b = t1_2.a)
-               ->  Seq Scan on prt2_p3 t2_2
-               ->  Hash
-                     ->  Seq Scan on prt1_p3 t1_2
-                           Filter: (b = 0)
-(21 rows)
-
--- test default partition behavior for list
+-- test default partition behavior for list, should not use partition-wise join
+-- since default partition from one side matches multiple partitions on the
+-- other
 ALTER TABLE plt1 DETACH PARTITION plt1_p3;
 ALTER TABLE plt1 ATTACH PARTITION plt1_p3 DEFAULT;
 ANALYZE plt1;
@@ -1389,26 +4141,24 @@ SELECT avg(t1.a), avg(t2.b), t1.c, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c
    Sort Key: t1.c
    ->  HashAggregate
          Group Key: t1.c, t2.c
-         ->  Append
-               ->  Hash Join
-                     Hash Cond: (t2.c = t1.c)
-                     ->  Seq Scan on plt2_p1 t2
-                     ->  Hash
-                           ->  Seq Scan on plt1_p1 t1
+         ->  Hash Join
+               Hash Cond: ((t2.c)::text = (t1.c)::text)
+               ->  Append
+                     ->  Seq Scan on plt2_p4 t2
+                     ->  Seq Scan on plt2_p1 t2_1
+                     ->  Seq Scan on plt2_p2 t2_2
+                     ->  Seq Scan on plt2_p3 t2_3
+               ->  Hash
+                     ->  Append
+                           ->  Seq Scan on plt1_p4 t1
                                  Filter: ((a % 25) = 0)
-               ->  Hash Join
-                     Hash Cond: (t2_1.c = t1_1.c)
-                     ->  Seq Scan on plt2_p2 t2_1
-                     ->  Hash
-                           ->  Seq Scan on plt1_p2 t1_1
+                           ->  Seq Scan on plt1_p1 t1_1
                                  Filter: ((a % 25) = 0)
-               ->  Hash Join
-                     Hash Cond: (t2_2.c = t1_2.c)
-                     ->  Seq Scan on plt2_p3 t2_2
-                     ->  Hash
-                           ->  Seq Scan on plt1_p3 t1_2
+                           ->  Seq Scan on plt1_p2 t1_2
                                  Filter: ((a % 25) = 0)
-(23 rows)
+                           ->  Seq Scan on plt1_p3 t1_3
+                                 Filter: ((a % 25) = 0)
+(21 rows)
 
 --
 -- multiple levels of partitioning
@@ -1804,64 +4554,70 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2 WHERE t1.a = t2.a;
  Hash Join
    Hash Cond: (t1.a = t2.a)
    ->  Append
-         ->  Seq Scan on prt1_p1 t1
-         ->  Seq Scan on prt1_p2 t1_1
-         ->  Seq Scan on prt1_p3 t1_2
+         ->  Seq Scan on prt1_p0 t1
+         ->  Seq Scan on prt1_p1 t1_1
+         ->  Seq Scan on prt1_p2 t1_2
+         ->  Seq Scan on prt1_p3 t1_3
+         ->  Seq Scan on prt1_p4 t1_4
    ->  Hash
          ->  Append
                ->  Seq Scan on prt4_n_p1 t2
                ->  Seq Scan on prt4_n_p2 t2_1
                ->  Seq Scan on prt4_n_p3 t2_2
-(11 rows)
+(13 rows)
 
 EXPLAIN (COSTS OFF)
 SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2, prt2 t3 WHERE t1.a = t2.a and t1.a = t3.b;
-                       QUERY PLAN                       
---------------------------------------------------------
+                        QUERY PLAN                        
+----------------------------------------------------------
  Hash Join
-   Hash Cond: (t2.a = t1.a)
+   Hash Cond: (t3.b = t1.a)
    ->  Append
-         ->  Seq Scan on prt4_n_p1 t2
-         ->  Seq Scan on prt4_n_p2 t2_1
-         ->  Seq Scan on prt4_n_p3 t2_2
+         ->  Seq Scan on prt2_p0 t3
+         ->  Seq Scan on prt2_p1 t3_1
+         ->  Seq Scan on prt2_p2 t3_2
+         ->  Seq Scan on prt2_p3 t3_3
+         ->  Seq Scan on prt2_p4 t3_4
+         ->  Seq Scan on prt2_p5 t3_5
    ->  Hash
-         ->  Append
-               ->  Hash Join
-                     Hash Cond: (t1.a = t3.b)
-                     ->  Seq Scan on prt1_p1 t1
-                     ->  Hash
-                           ->  Seq Scan on prt2_p1 t3
-               ->  Hash Join
-                     Hash Cond: (t1_1.a = t3_1.b)
-                     ->  Seq Scan on prt1_p2 t1_1
-                     ->  Hash
-                           ->  Seq Scan on prt2_p2 t3_1
-               ->  Hash Join
-                     Hash Cond: (t1_2.a = t3_2.b)
-                     ->  Seq Scan on prt1_p3 t1_2
-                     ->  Hash
-                           ->  Seq Scan on prt2_p3 t3_2
+         ->  Hash Join
+               Hash Cond: (t1.a = t2.a)
+               ->  Append
+                     ->  Seq Scan on prt1_p0 t1
+                     ->  Seq Scan on prt1_p1 t1_1
+                     ->  Seq Scan on prt1_p2 t1_2
+                     ->  Seq Scan on prt1_p3 t1_3
+                     ->  Seq Scan on prt1_p4 t1_4
+               ->  Hash
+                     ->  Append
+                           ->  Seq Scan on prt4_n_p1 t2
+                           ->  Seq Scan on prt4_n_p2 t2_1
+                           ->  Seq Scan on prt4_n_p3 t2_2
 (23 rows)
 
 -- partitionwise join can not be applied if there are no equi-join conditions
 -- between partition keys
 EXPLAIN (COSTS OFF)
 SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON (t1.a < t2.b);
-                       QUERY PLAN                        
----------------------------------------------------------
+                 QUERY PLAN                 
+--------------------------------------------
  Nested Loop Left Join
+   Join Filter: (t1.a < t2.b)
    ->  Append
-         ->  Seq Scan on prt1_p1 t1
-         ->  Seq Scan on prt1_p2 t1_1
-         ->  Seq Scan on prt1_p3 t1_2
-   ->  Append
-         ->  Index Scan using iprt2_p1_b on prt2_p1 t2
-               Index Cond: (b > t1.a)
-         ->  Index Scan using iprt2_p2_b on prt2_p2 t2_1
-               Index Cond: (b > t1.a)
-         ->  Index Scan using iprt2_p3_b on prt2_p3 t2_2
-               Index Cond: (b > t1.a)
-(12 rows)
+         ->  Seq Scan on prt1_p0 t1
+         ->  Seq Scan on prt1_p1 t1_1
+         ->  Seq Scan on prt1_p2 t1_2
+         ->  Seq Scan on prt1_p3 t1_3
+         ->  Seq Scan on prt1_p4 t1_4
+   ->  Materialize
+         ->  Append
+               ->  Seq Scan on prt2_p0 t2
+               ->  Seq Scan on prt2_p1 t2_1
+               ->  Seq Scan on prt2_p2 t2_2
+               ->  Seq Scan on prt2_p3 t2_3
+               ->  Seq Scan on prt2_p4 t2_4
+               ->  Seq Scan on prt2_p5 t2_5
+(16 rows)
 
 -- equi-join with join condition on partial keys does not qualify for
 -- partitionwise join
@@ -1947,16 +4703,17 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 JOIN prt2_n t2 ON (t1.c = t2.c) JOI
          ->  Seq Scan on prt2_n_p2 t2_1
    ->  Hash
          ->  Hash Join
-               Hash Cond: (t3.c = (t1.c)::text)
+               Hash Cond: ((t3.c)::text = (t1.c)::text)
                ->  Append
-                     ->  Seq Scan on plt1_p1 t3
-                     ->  Seq Scan on plt1_p2 t3_1
-                     ->  Seq Scan on plt1_p3 t3_2
+                     ->  Seq Scan on plt1_p4 t3
+                     ->  Seq Scan on plt1_p1 t3_1
+                     ->  Seq Scan on plt1_p2 t3_2
+                     ->  Seq Scan on plt1_p3 t3_3
                ->  Hash
                      ->  Append
                            ->  Seq Scan on prt1_n_p1 t1
                            ->  Seq Scan on prt1_n_p2 t1_1
-(16 rows)
+(17 rows)
 
 -- partitionwise join can not be applied for a join between list and range
 -- partitioned table
@@ -1967,14 +4724,16 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 FULL JOIN prt1 t2 ON (t1.c = t2.c);
  Hash Full Join
    Hash Cond: ((t2.c)::text = (t1.c)::text)
    ->  Append
-         ->  Seq Scan on prt1_p1 t2
-         ->  Seq Scan on prt1_p2 t2_1
-         ->  Seq Scan on prt1_p3 t2_2
+         ->  Seq Scan on prt1_p0 t2
+         ->  Seq Scan on prt1_p1 t2_1
+         ->  Seq Scan on prt1_p2 t2_2
+         ->  Seq Scan on prt1_p3 t2_3
+         ->  Seq Scan on prt1_p4 t2_4
    ->  Hash
          ->  Append
                ->  Seq Scan on prt1_n_p1 t1
                ->  Seq Scan on prt1_n_p2 t1_1
-(10 rows)
+(12 rows)
 
 -- partitionwise join can not be applied if only one of joining table has
 -- default partition
@@ -1990,16 +4749,23 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b =
    ->  Hash Join
          Hash Cond: (t2.b = t1.a)
          ->  Append
-               ->  Seq Scan on prt2_p1 t2
-               ->  Seq Scan on prt2_p2 t2_1
-               ->  Seq Scan on prt2_p3 t2_2
+               ->  Seq Scan on prt2_p0 t2
+               ->  Seq Scan on prt2_p1 t2_1
+               ->  Seq Scan on prt2_p2 t2_2
+               ->  Seq Scan on prt2_p3 t2_3
+               ->  Seq Scan on prt2_p4 t2_4
+               ->  Seq Scan on prt2_p5 t2_5
          ->  Hash
                ->  Append
-                     ->  Seq Scan on prt1_p1 t1
+                     ->  Seq Scan on prt1_p0 t1
                            Filter: (b = 0)
-                     ->  Seq Scan on prt1_p2 t1_1
+                     ->  Seq Scan on prt1_p1 t1_1
                            Filter: (b = 0)
-                     ->  Seq Scan on prt1_p3 t1_2
+                     ->  Seq Scan on prt1_p2 t1_2
                            Filter: (b = 0)
-(16 rows)
+                     ->  Seq Scan on prt1_p3 t1_3
+                           Filter: (b = 0)
+                     ->  Seq Scan on prt1_p4 t1_4
+                           Filter: (b = 0)
+(23 rows)
 
diff --git a/src/test/regress/sql/partition_join.sql b/src/test/regress/sql/partition_join.sql
index db9a6b4a96..7597b069fa 100644
--- a/src/test/regress/sql/partition_join.sql
+++ b/src/test/regress/sql/partition_join.sql
@@ -10,25 +10,39 @@ SET enable_partitionwise_join to true;
 -- partitioned by a single column
 --
 CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a);
+CREATE TABLE prt1_p0 PARTITION OF prt1 FOR VALUES FROM (MINVALUE) TO (0);
 CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250);
 CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600);
 CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500);
-INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 2 = 0;
+CREATE TABLE prt1_p4 PARTITION OF prt1 FOR VALUES FROM (600) TO (800);
+INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 2 = 0;
+CREATE INDEX iprt1_p0_a on prt1_p0(a);
 CREATE INDEX iprt1_p1_a on prt1_p1(a);
 CREATE INDEX iprt1_p2_a on prt1_p2(a);
 CREATE INDEX iprt1_p3_a on prt1_p3(a);
+CREATE INDEX iprt1_p4_a on prt1_p4(a);
 ANALYZE prt1;
 
+-- prt2 have missing starting MINVALUE to -250 range and
+-- extra bounds from 800 to MAXVALUE
 CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b);
+CREATE TABLE prt2_p0 PARTITION OF prt2 FOR VALUES FROM (-250) TO (0);
 CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250);
 CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500);
 CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600);
-INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 3 = 0;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (MAXVALUE);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 3 = 0;
+CREATE INDEX iprt2_p0_b on prt2_p0(b);
 CREATE INDEX iprt2_p1_b on prt2_p1(b);
 CREATE INDEX iprt2_p2_b on prt2_p2(b);
 CREATE INDEX iprt2_p3_b on prt2_p3(b);
+CREATE INDEX iprt2_p4_b on prt2_p4(b);
 ANALYZE prt2;
 
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
+
 -- inner join
 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;
@@ -69,11 +83,19 @@ EXPLAIN (COSTS OFF)
 SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a;
 SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a;
 
+EXPLAIN (COSTS OFF)
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b;
+SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b;
+
 -- Anti-join with aggregates
 EXPLAIN (COSTS OFF)
 SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b);
 SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b);
 
+EXPLAIN (COSTS OFF)
+SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0;
+SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0;
+
 -- lateral reference
 EXPLAIN (COSTS OFF)
 SELECT * FROM prt1 t1 LEFT JOIN LATERAL
@@ -95,20 +117,30 @@ SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL
 -- partitioned by expression
 --
 CREATE TABLE prt1_e (a int, b int, c int) PARTITION BY RANGE(((a + b)/2));
+CREATE TABLE prt1_e_p0 PARTITION OF prt1_e FOR VALUES FROM (MINVALUE) TO (0);
 CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES FROM (0) TO (250);
 CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES FROM (250) TO (500);
 CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES FROM (500) TO (600);
+CREATE TABLE prt1_e_p4 PARTITION OF prt1_e FOR VALUES FROM (600) TO (MAXVALUE);
 INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i;
+INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 2) i;
+INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(600, 799, 2) i;
+CREATE INDEX iprt1_e_p0_ab2 on prt1_e_p1(((a+b)/2));
 CREATE INDEX iprt1_e_p1_ab2 on prt1_e_p1(((a+b)/2));
 CREATE INDEX iprt1_e_p2_ab2 on prt1_e_p2(((a+b)/2));
 CREATE INDEX iprt1_e_p3_ab2 on prt1_e_p3(((a+b)/2));
+CREATE INDEX iprt1_e_p4_ab2 on prt1_e_p1(((a+b)/2));
 ANALYZE prt1_e;
 
 CREATE TABLE prt2_e (a int, b int, c int) PARTITION BY RANGE(((b + a)/2));
+CREATE TABLE prt2_e_p0 PARTITION OF prt2_e FOR VALUES FROM (MINVALUE) TO (0);
 CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES FROM (0) TO (250);
 CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES FROM (250) TO (500);
 CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES FROM (500) TO (600);
+CREATE TABLE prt2_e_p4 PARTITION OF prt2_e FOR VALUES FROM (600) TO (MAXVALUE);
 INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i;
+INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 3) i;
+INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(600, 799, 3) i;
 ANALYZE prt2_e;
 
 EXPLAIN (COSTS OFF)
@@ -172,6 +204,128 @@ SELECT t1.a, t2.b FROM prt1 t1, prt2 t2 WHERE t1::text = t2::text AND t1.a = t2.
 RESET enable_hashjoin;
 RESET enable_nestloop;
 
+-- test default partition behavior for range, partition-wise join is not
+-- possible since more than one partition on one side matches default partition
+-- on the other side. Default partition from prt1 matches default partition and
+-- prt2_p4 from prt2 and default partition from prt2 matches default partition
+-- and prt1_p0 from prt1
+ALTER TABLE prt1 DETACH PARTITION prt1_p3;
+ALTER TABLE prt1 ATTACH PARTITION prt1_p3 DEFAULT;
+ANALYZE prt1;
+ALTER TABLE prt2 DETACH PARTITION prt2_p3;
+ALTER TABLE prt2 ATTACH PARTITION prt2_p3 DEFAULT;
+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;
+
+-- partition-wise join should be possible when we drop the first and last
+-- partitions from both sides
+ALTER TABLE prt1 DETACH PARTITION prt1_p0;
+ALTER TABLE prt1 DETACH PARTITION prt1_p4;
+ANALYZE prt1;
+ALTER TABLE prt2 DETACH PARTITION prt2_p0;
+ALTER TABLE prt2 DETACH PARTITION prt2_p4;
+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;
+
+-- restore the partitioned tables for rest of the tests
+ALTER TABLE prt1 ATTACH PARTITION prt1_p0 FOR VALUES FROM (MINVALUE) TO (0);
+ALTER TABLE prt1 ATTACH PARTITION prt1_p4 FOR VALUES FROM (600) TO (800);
+ALTER TABLE prt1 DETACH PARTITION prt1_p3;
+ALTER TABLE prt1 ATTACH PARTITION prt1_p3 FOR VALUES FROM (500) TO (600);
+ANALYZE prt1;
+ALTER TABLE prt2 ATTACH PARTITION prt2_p0 FOR VALUES FROM (-250) TO (0);
+ALTER TABLE prt2 ATTACH PARTITION prt2_p4 FOR VALUES FROM (600) TO (MAXVALUE);
+ALTER TABLE prt2 DETACH PARTITION prt2_p3;
+ALTER TABLE prt2 ATTACH PARTITION prt2_p3 FOR VALUES FROM (500) TO (600);
+ANALYZE prt2;
+
+-- Add an extra partition to prt2 , Partition-wise join is possible with
+-- extra partitions on inner side are allowed
+DROP TABLE prt2_p4;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (800);
+CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (800) TO (1000);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999) i WHERE i % 3 = 0;
+ANALYZE prt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- 3-way join when not every pair of joining relation can use partition-wise
+-- join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t2.a, t3.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON (t1.a = t2.b) INNER JOIN prt1 t3 ON (t2.b = t3.a) WHERE t2.a = 0 ORDER BY t1.a, t2.a, t3.c;
+SELECT t1.a, t2.a, t3.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON (t1.a = t2.b) INNER JOIN prt1 t3 ON (t2.b = t3.a) WHERE t2.a = 0 ORDER BY t1.a, t2.a, t3.c;
+
+-- partition-wise join can not handle missing partition on the inner side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.b;
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE coalesce(t1.b, 0) + coalesce(t2.a, 0) = 0 ORDER BY t1.a, t2.a;
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE prt2_p4;
+DROP TABLE prt2_p5;
+CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (700);
+CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (700) TO (1000);
+INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999, 3) i;
+ANALYZE prt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.a;
+
+-- full join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c;
+
 --
 -- partitioned by multiple columns
 --
@@ -196,28 +350,79 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1
 --
 -- tests for list partitioned tables.
 --
-CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c);
-CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
-ANALYZE plt1;
+\set part_mod 17
+\set cond_mod 47
+\set num_rows 500
+
+CREATE TABLE plt1 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0001','0002','0003');
+CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0008','0009');
+CREATE TABLE plt1_p4 PARTITION OF plt1 FOR VALUES IN ('0000','0010');
+INSERT INTO plt1 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (7, 11, 12, 13, 14, 15, 16);
+ANALYSE plt1;
+
+-- plt2 have missing starting 0001, additional 0007, missing ending 0010
+-- and additional 0011 and 0012 bounds
+CREATE TABLE plt2 (a int, b int, c varchar) PARTITION BY LIST(c);
+CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0002','0003');
+CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0004','0005','0006');
+CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0007','0008','0009');
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000','0011','0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 10, 13, 14, 15, 16);
+ANALYSE plt2;
+
+-- Partition-wise-join is possible with some partition bounds overlap
+-- with each other completely and some partialy for inner,left,right,
+-- full, semi and anti joins
 
-CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c);
-CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i;
-ANALYZE plt2;
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+
+-- full join
+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.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY 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.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
 
 --
 -- list partitioned by expression
 --
 CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A'));
-CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0000', '0003', '0004', '0010');
-CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0001', '0005', '0002', '0009');
-CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0006', '0007', '0008', '0011');
-INSERT INTO plt1_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
+CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0002', '0003');
+CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0004', '0005', '0006');
+CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0008', '0009');
+CREATE TABLE plt1_e_p4 PARTITION OF plt1_e FOR VALUES IN ('0000');
+INSERT INTO plt1_e SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 7, 10, 11, 12, 13, 14, 15, 16);
 ANALYZE plt1_e;
 
 -- test partition matching with N-way join
@@ -225,6 +430,175 @@ EXPLAIN (COSTS OFF)
 SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
 SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
 
+-- Add an extra partition to plt2 , Partition-wise join is possible with
+-- partitions on inner side are allowed
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (13, 14);
+ANALYZE plt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join, partition-wise join can not handle extra partition on the outer
+-- side
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+
+-- full join, partition-wise join can not handle extra partition on the outer
+-- side
+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.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY 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.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt2_p5;
+CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0001','0013','0014');
+INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (1, 13, 14);
+ANALYZE plt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+
+-- full join
+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.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- partition have a NULL on one side, Partition-wise join is possible with
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- in this case NULL will be treated as addition partition bounds.
+DROP TABLE plt2_p5;
+DROP TABLE plt2_p4;
+CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000',NULL,'0012');
+INSERT INTO plt2 SELECT i, i % :cond_mod, case when i % :part_mod = 11 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (0,11,12);
+ANALYZE plt2;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a;
+
+-- full join
+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.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY 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.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- partition have a NULL on both side with different partition bounds w.r.t other side
+-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed
+-- Partition-wise join can not handle the case when one partition from one side
+-- matches with multiple partitions on the other side
+DROP TABLE plt1_p3;
+CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN (NULL,'0008','0009');
+INSERT INTO plt1 SELECT i, i % :cond_mod, case when i % :part_mod = 7 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (7,8,9);
+ANALYZE plt1;
+
+-- inner join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a;
+
+-- left join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a;
+
+-- right join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a;
+
+-- full join
+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.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a;
+
+-- semi join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+-- anti join
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
+EXPLAIN (COSTS OFF)
+select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c;
+
 -- joins where one of the relations is proven empty
 EXPLAIN (COSTS OFF)
 SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a = 1 AND t1.a = 2;
@@ -270,27 +644,18 @@ EXPLAIN (COSTS OFF)
 SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM pht1 t1, pht2 t2, pht1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
 SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM pht1 t1, pht2 t2, pht1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c;
 
--- test default partition behavior for range
-ALTER TABLE prt1 DETACH PARTITION prt1_p3;
-ALTER TABLE prt1 ATTACH PARTITION prt1_p3 DEFAULT;
-ANALYZE prt1;
-ALTER TABLE prt2 DETACH PARTITION prt2_p3;
-ALTER TABLE prt2 ATTACH PARTITION prt2_p3 DEFAULT;
-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;
-
--- test default partition behavior for list
+-- test default partition behavior for list, should not use partition-wise join
+-- since default partition from one side matches multiple partitions on the
+-- other
 ALTER TABLE plt1 DETACH PARTITION plt1_p3;
 ALTER TABLE plt1 ATTACH PARTITION plt1_p3 DEFAULT;
 ANALYZE plt1;
 ALTER TABLE plt2 DETACH PARTITION plt2_p3;
 ALTER TABLE plt2 ATTACH PARTITION plt2_p3 DEFAULT;
 ANALYZE plt2;
-
 EXPLAIN (COSTS OFF)
 SELECT avg(t1.a), avg(t2.b), t1.c, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.a % 25 = 0 GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+
 --
 -- multiple levels of partitioning
 --
