diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index f17ac29..8ce704a 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -43,80 +43,20 @@
 #include "utils/builtins.h"
 #include "utils/datum.h"
 #include "utils/memutils.h"
 #include "utils/fmgroids.h"
 #include "utils/inval.h"
 #include "utils/lsyscache.h"
 #include "utils/ruleutils.h"
 #include "utils/rel.h"
 #include "utils/syscache.h"
 
-/* Type and collation information for partition key columns */
-typedef struct KeyTypeCollInfo
-{
-	Oid		*typid;
-	int32	*typmod;
-	int16	*typlen;
-	bool	*typbyval;
-	char	*typalign;
-	Oid		*typcoll;
-} KeyTypeCollInfo;
-
-/*
- * Partition key information
- */
-typedef struct PartitionKeyData
-{
-	char		strategy;		/* partition strategy */
-	int16		partnatts;		/* number of partition attributes */
-	AttrNumber *partattrs;		/* partition attnums */
-	Oid		   *partopfamily;	/* OIDs of operator families */
-	Oid		   *partopcintype;	/* OIDs of opclass declared input data types */
-	FmgrInfo   *partsupfunc;	/* lookup info for support funcs */
-	List	   *partexprs;		/* partition key expressions, if any */
-	char	  **partcolnames;	/* partition key column names */
-	KeyTypeCollInfo *tcinfo;	/* type and collation info (all columns) */
-} PartitionKeyData;
-
-/* Internal representation of a list partition bound */
-typedef struct PartitionListInfo
-{
-	int		nvalues;	/* number of values in the following array */
-	Datum  *values;		/* values contained in the list */
-	bool   *nulls;
-} PartitionListInfo;
-
-/* Internal representation of a range partition bound */
-typedef struct RangeBound
-{
-	Datum	   *val;			/* composite bound value, if any */
-	bool		infinite;		/* bound is +/- infinity */
-	bool		inclusive;		/* bound is inclusive (vs exclusive) */
-	bool		lower;			/* this is the lower (vs upper) bound */
-} RangeBound;
-
-typedef struct PartitionRangeInfo
-{
-	RangeBound	*lower;
-	RangeBound	*upper;
-} PartitionRangeInfo;
-
-/*
- * Information about a single partition
- */
-typedef struct PartitionInfoData
-{
-	Oid						oid;		/* partition OID */
-	PartitionListInfo	   *list;		/* list partition info */
-	PartitionRangeInfo	   *range;		/* range partition info */
-} PartitionInfoData;
-
 /*
  * PartitionKeyExecInfo
  *
  *		This struct holds the information needed to extract partition
  *		column values from a heap tuple.
  *
  *		Key					copy of the rd_partkey of rel
  *		ExpressionState		exec state for expressions, or NIL if none
  */
 typedef struct PartitionKeyExecInfo
@@ -226,37 +166,37 @@ static PartitionListInfo *make_list_from_spec(PartitionKey key,
 static PartitionListInfo *copy_list_info(PartitionListInfo *src,
 							PartitionKey key);
 static bool equal_list_info(PartitionKey key, PartitionListInfo *l1,
 				PartitionListInfo *l2);
 static bool partition_list_values_equal(PartitionKey key,
 						   Datum val1, Datum val2);
 
 /* Range partition related support functions */
 static PartitionRangeInfo *make_range_from_spec(PartitionKey key,
 							PartitionRangeSpec *range_spec);
-static RangeBound *make_range_bound(PartitionKey key, List *val, bool inclusive,
+static PartitionRangeBound *make_range_bound(PartitionKey key, List *val, bool inclusive,
 							bool lower);
 static PartitionRangeInfo *copy_range_info(PartitionRangeInfo *src,
 							PartitionKey key);
-static RangeBound *copy_range_bound(RangeBound *src, PartitionKey key);
+static PartitionRangeBound *copy_range_bound(PartitionRangeBound *src, PartitionKey key);
 static bool equal_range_info(PartitionKey key, PartitionRangeInfo *r1,
 				 PartitionRangeInfo *r2);
 static int32 partition_range_cmp(PartitionKey key, PartitionRangeInfo *r1,
 									  PartitionRangeInfo *r2);
-static int32 partition_range_bound_cmp(PartitionKey key, RangeBound *b1,
-							RangeBound *b2);
+static int32 partition_range_bound_cmp(PartitionKey key, PartitionRangeBound *b1,
+							PartitionRangeBound *b2);
 static int32 partition_range_tuple_cmp(PartitionKey key,
 						   Datum *val1, Datum *val2);
 static bool partition_range_overlaps(PartitionKey key,
 							PartitionRangeInfo *r1, PartitionRangeInfo *r2);
-static bool tuple_rightof_bound(PartitionKey key, Datum *tuple, RangeBound *bound);
-static bool tuple_leftof_bound(PartitionKey key, Datum *tuple, RangeBound *bound);
+static bool tuple_rightof_bound(PartitionKey key, Datum *tuple, PartitionRangeBound *bound);
+static bool tuple_leftof_bound(PartitionKey key, Datum *tuple, PartitionRangeBound *bound);
 static int range_partition_bsearch(PartitionKey key, PartitionDesc pdesc,
 						Datum *tuple);
 
 /*
  * Partition key related functions
  */
 
 /*
  * RelationBuildPartitionKey
  *		Build and attach to relcache partition key data of relation
@@ -1097,21 +1037,21 @@ list_overlaps_existing_partition(PartitionKey key,
 }
 
 
 /*
  * Is a new partition's range empty?
  */
 static bool
 partition_range_empty(PartitionKey key, PartitionRangeSpec *range_spec)
 {
 	PartitionRangeInfo *range;
-	RangeBound *lower,
+	PartitionRangeBound *lower,
 			   *upper;
 
 	range = make_range_from_spec(key, range_spec);
 	lower = range->lower;
 	upper = range->upper;
 
 	/*
 	 * Range is not empty if one (and only one) of the bounds is infinity.
 	 * Both cannot be infinite because of how the syntax is specified.
 	 */
@@ -2104,27 +2044,27 @@ make_range_from_spec(PartitionKey key, PartitionRangeSpec *range_spec)
 									range_spec->lowerinc,
 									true);
 	range->upper = make_range_bound(key,
 									range_spec->upper,
 									range_spec->upperinc,
 									false);
 
 	return range;
 }
 
-static RangeBound *
+static PartitionRangeBound *
 make_range_bound(PartitionKey key, List *val, bool inclusive, bool lower)
 {
-	RangeBound *bound;
+	PartitionRangeBound *bound;
 	ListCell *cell;
 
-	bound = (RangeBound *) palloc0(sizeof(RangeBound));
+	bound = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
 	bound->infinite = (val == NIL);
 	bound->inclusive = inclusive;
 	bound->lower = lower;
 
 	if (val)
 	{
 		int		i;
 
 		bound->val = (Datum *) palloc0(key->partnatts * sizeof(Datum));
 
@@ -2157,28 +2097,28 @@ copy_range_info(PartitionRangeInfo *src, PartitionKey key)
 {
 	PartitionRangeInfo *result;
 
 	result = (PartitionRangeInfo *) palloc0(sizeof(PartitionRangeInfo));
 	result->lower = copy_range_bound(src->lower, key);
 	result->upper = copy_range_bound(src->upper, key);
 
 	return result;
 }
 
-static RangeBound *
-copy_range_bound(RangeBound *src, PartitionKey key)
+static PartitionRangeBound *
+copy_range_bound(PartitionRangeBound *src, PartitionKey key)
 {
 	int		i;
 	int		partnatts = get_partition_key_natts(key);
-	RangeBound  *result;
+	PartitionRangeBound  *result;
 
-	result = (RangeBound *) palloc0(sizeof(RangeBound));
+	result = (PartitionRangeBound *) palloc0(sizeof(PartitionRangeBound));
 	result->infinite = src->infinite;
 	result->inclusive = src->inclusive;
 	result->lower = src->lower;
 
 	if (src->val)
 	{
 		result->val = (Datum *) palloc0(partnatts * sizeof(Datum));
 		for (i = 0; i < partnatts; i++)
 			result->val[i] = datumCopy(src->val[i],
 									   key->tcinfo->typbyval[i],
@@ -2212,21 +2152,21 @@ partition_range_cmp(PartitionKey key, PartitionRangeInfo *r1,
 
 	cmp = partition_range_bound_cmp(key, r1->lower, r2->lower);
 	if (cmp == 0)
 		cmp = partition_range_bound_cmp(key, r1->upper, r2->upper);
 
 	return cmp;
 }
 
 /* Returns for two range partition bounds whether, b1 <=, =, >= b2 */
 static int32
-partition_range_bound_cmp(PartitionKey key, RangeBound *b1, RangeBound *b2)
+partition_range_bound_cmp(PartitionKey key, PartitionRangeBound *b1, PartitionRangeBound *b2)
 {
 	int32		result;
 
 	/*
 	 * First, handle cases involving infinity, which don't require invoking
 	 * the comparison proc.
 	 */
 	if (b1->infinite && b2->infinite)
 	{
 		/*
@@ -2337,31 +2277,31 @@ range_partition_bsearch(PartitionKey key, PartitionDesc pdesc,
 		}
 
 		low = idx + 1;
 	}
 
 	return -1;
 }
 
 /* Does range key lie to the right of partition bound */
 static bool
-tuple_rightof_bound(PartitionKey key, Datum *tuple, RangeBound *bound)
+tuple_rightof_bound(PartitionKey key, Datum *tuple, PartitionRangeBound *bound)
 {
 	int32	cmpval = partition_range_tuple_cmp(key, tuple, bound->val);
 
 	if (!cmpval)
 		return bound->lower ? bound->inclusive : !bound->inclusive;
 
 	return cmpval > 0;
 }
 
 /* Does range key lie to the left of partition bound */
 static bool
-tuple_leftof_bound(PartitionKey key, Datum *tuple, RangeBound *bound)
+tuple_leftof_bound(PartitionKey key, Datum *tuple, PartitionRangeBound *bound)
 {
 	int32	cmpval = partition_range_tuple_cmp(key, tuple, bound->val);
 
 	if (!cmpval)
 		return !bound->lower ? bound->inclusive : !bound->inclusive;
 
 	return cmpval < 0;
 }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 3b8fa6b..cd14840 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -204,20 +204,31 @@ _outBitmapset(StringInfo str, const Bitmapset *bms)
 }
 
 /* for use by extensions which define extensible nodes */
 void
 outBitmapset(StringInfo str, const Bitmapset *bms)
 {
 	_outBitmapset(str, bms);
 }
 
 /*
+ * TODO: remove, used for debugging through gdb.
+ */
+char *
+bms_to_char(const Bitmapset *bms)
+{
+	StringInfo str = makeStringInfo();
+	outBitmapset(str, bms);
+	return str->data;
+}
+
+/*
  * Print the value of a Datum given its type.
  */
 void
 outDatum(StringInfo str, Datum value, int typlen, bool typbyval)
 {
 	Size		length,
 				i;
 	char	   *s;
 
 	length = datumGetSize(value, typbyval, typlen);
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 04264b4..09781be 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -119,20 +119,24 @@ static void check_output_expressions(Query *subquery,
 static void compare_tlist_datatypes(List *tlist, List *colTypes,
 						pushdown_safety_info *safetyInfo);
 static bool targetIsInAllPartitionLists(TargetEntry *tle, Query *query);
 static bool qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
 					  pushdown_safety_info *safetyInfo);
 static void subquery_push_qual(Query *subquery,
 				   RangeTblEntry *rte, Index rti, Node *qual);
 static void recurse_push_qual(Node *setOp, Query *topquery,
 				  RangeTblEntry *rte, Index rti, Node *qual);
 static void remove_unused_subquery_outputs(Query *subquery, RelOptInfo *rel);
+static void add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
+									List *live_childrels);
+static void generate_partition_wise_join_paths(PlannerInfo *root,
+											   RelOptInfo *rel);
 
 
 /*
  * make_one_rel
  *	  Finds all possible access paths for executing a query, returning a
  *	  single rel that represents the join of all base rels in the query.
  */
 RelOptInfo *
 make_one_rel(PlannerInfo *root, List *joinlist)
 {
@@ -908,20 +912,40 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 		childRTE = root->simple_rte_array[childRTindex];
 
 		/*
 		 * The child rel's RelOptInfo was already created during
 		 * add_base_rels_to_query.
 		 */
 		childrel = find_base_rel(root, childRTindex);
 		Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
 
 		/*
+		 * Copy/Modify targetlist. Partition-wise join technique may consider
+		 * an OUTER join of another child relation with this child relation.
+		 * In that case, even if this child is deemed empty, we will require
+		 * the targetlist of this child to construct the nullable side. Hence
+		 * set the targetlist before we prove that the child is empty and stop
+		 * processing further.
+		 *
+		 * NB: the resulting childrel->reltarget->exprs may contain arbitrary
+		 * expressions, which otherwise would not occur in a rel's targetlist.
+		 * Code that might be looking at an appendrel child must cope with
+		 * such.  (Normally, a rel's targetlist would only include Vars and
+		 * PlaceHolderVars.)  XXX we do not bother to update the cost or width
+		 * fields of childrel->reltarget; not clear if that would be useful.
+		 */
+		childrel->reltarget->exprs = (List *)
+			adjust_appendrel_attrs(root,
+								   (Node *) rel->reltarget->exprs,
+								   appinfo);
+
+		/*
 		 * We have to copy the parent's targetlist and quals to the child,
 		 * with appropriate substitution of variables.  However, only the
 		 * baserestrictinfo quals are needed before we can check for
 		 * constraint exclusion; so do that first and then check to see if we
 		 * can disregard this child.
 		 *
 		 * As of 8.4, the child rel's targetlist might contain non-Var
 		 * expressions, which means that substitution into the quals could
 		 * produce opportunities for const-simplification, and perhaps even
 		 * pseudoconstant quals.  To deal with this, we strip the RestrictInfo
@@ -953,38 +977,25 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 		if (relation_excluded_by_constraints(root, childrel, childRTE))
 		{
 			/*
 			 * This child need not be scanned, so we can omit it from the
 			 * appendrel.
 			 */
 			set_dummy_rel_pathlist(childrel);
 			continue;
 		}
 
-		/*
-		 * CE failed, so finish copying/modifying targetlist and join quals.
-		 *
-		 * NB: the resulting childrel->reltarget->exprs may contain arbitrary
-		 * expressions, which otherwise would not occur in a rel's targetlist.
-		 * Code that might be looking at an appendrel child must cope with
-		 * such.  (Normally, a rel's targetlist would only include Vars and
-		 * PlaceHolderVars.)  XXX we do not bother to update the cost or width
-		 * fields of childrel->reltarget; not clear if that would be useful.
-		 */
+		/* CE failed, so finish copying/modifying join quals. */
 		childrel->joininfo = (List *)
 			adjust_appendrel_attrs(root,
 								   (Node *) rel->joininfo,
 								   appinfo);
-		childrel->reltarget->exprs = (List *)
-			adjust_appendrel_attrs(root,
-								   (Node *) rel->reltarget->exprs,
-								   appinfo);
 
 		/*
 		 * We have to make child entries in the EquivalenceClass data
 		 * structures as well.  This is needed either if the parent
 		 * participates in some eclass joins (because we will want to consider
 		 * inner-indexscan joins on the individual children) or if the parent
 		 * has useful pathkeys (because we should try to build MergeAppend
 		 * paths that produce those sort orderings).
 		 */
 		if (rel->has_eclass_joins || has_useful_pathkeys(root, rel))
@@ -1115,41 +1126,32 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 /*
  * set_append_rel_pathlist
  *	  Build access paths for an "append relation"
  */
 static void
 set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 						Index rti, RangeTblEntry *rte)
 {
 	int			parentRTindex = rti;
 	List	   *live_childrels = NIL;
-	List	   *subpaths = NIL;
-	bool		subpaths_valid = true;
-	List	   *partial_subpaths = NIL;
-	bool		partial_subpaths_valid = true;
-	List	   *all_child_pathkeys = NIL;
-	List	   *all_child_outers = NIL;
 	ListCell   *l;
 
 	/*
-	 * Generate access paths for each member relation, and remember the
-	 * cheapest path for each one.  Also, identify all pathkeys (orderings)
-	 * and parameterizations (required_outer sets) available for the member
-	 * relations.
+	 * Generate access paths for each member relation and remember the
+	 * non-dummy children.
 	 */
 	foreach(l, root->append_rel_list)
 	{
 		AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
 		int			childRTindex;
 		RangeTblEntry *childRTE;
 		RelOptInfo *childrel;
-		ListCell   *lcp;
 
 		/* append_rel_list contains all append rels; ignore others */
 		if (appinfo->parent_relid != parentRTindex)
 			continue;
 
 		/* Re-locate the child RTE and RelOptInfo */
 		childRTindex = appinfo->child_relid;
 		childRTE = root->simple_rte_array[childRTindex];
 		childrel = root->simple_rel_array[childRTindex];
 
@@ -1170,20 +1172,46 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 		/*
 		 * If child is dummy, ignore it.
 		 */
 		if (IS_DUMMY_REL(childrel))
 			continue;
 
 		/*
 		 * Child is live, so add it to the live_childrels list for use below.
 		 */
 		live_childrels = lappend(live_childrels, childrel);
+	}
+
+	add_paths_to_append_rel(root, rel, live_childrels);
+}
+
+static void
+add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
+						List *live_childrels)
+{
+	List	   *subpaths = NIL;
+	bool		subpaths_valid = true;
+	List	   *partial_subpaths = NIL;
+	bool		partial_subpaths_valid = true;
+	List	   *all_child_pathkeys = NIL;
+	List	   *all_child_outers = NIL;
+	ListCell   *l;
+
+	/*
+	 * For every non-dummy child, remember the cheapest path.  Also, identify
+	 * all pathkeys (orderings) and parameterizations (required_outer sets)
+	 * available for the non-dummy member relations.
+	 */
+	foreach (l, live_childrels)
+	{
+		RelOptInfo *childrel = lfirst(l);
+		ListCell   *lcp;
 
 		/*
 		 * If child has an unparameterized cheapest-total path, add that to
 		 * the unparameterized Append path we are constructing for the parent.
 		 * If not, there's no workable unparameterized path.
 		 */
 		if (childrel->cheapest_total_path->param_info == NULL)
 			subpaths = accumulate_append_subpath(subpaths,
 											  childrel->cheapest_total_path);
 		else
@@ -2188,20 +2216,22 @@ standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
 		 * Run generate_gather_paths() for each just-processed joinrel.  We
 		 * could not do this earlier because both regular and partial paths
 		 * can get added to a particular joinrel at multiple times within
 		 * join_search_one_level.  After that, we're done creating paths for
 		 * the joinrel, so run set_cheapest().
 		 */
 		foreach(lc, root->join_rel_level[lev])
 		{
 			rel = (RelOptInfo *) lfirst(lc);
 
+			generate_partition_wise_join_paths(root, rel);
+
 			/* Create GatherPaths for any useful partial paths for rel */
 			generate_gather_paths(root, rel);
 
 			/* Find and save the cheapest paths for this rel */
 			set_cheapest(rel);
 
 #ifdef OPTIMIZER_DEBUG
 			debug_print_rel(root, rel);
 #endif
 		}
@@ -2849,20 +2879,77 @@ remove_unused_subquery_outputs(Query *subquery, RelOptInfo *rel)
 		 * OK, we don't need it.  Replace the expression with a NULL constant.
 		 * Preserve the exposed type of the expression, in case something
 		 * looks at the rowtype of the subquery's result.
 		 */
 		tle->expr = (Expr *) makeNullConst(exprType(texpr),
 										   exprTypmod(texpr),
 										   exprCollation(texpr));
 	}
 }
 
+/*
+ * generate_partition_wise_join_paths
+ * 		Create appends paths containing partition-wise join paths for given
+ * 		join relation.
+ */
+static void
+generate_partition_wise_join_paths(PlannerInfo *root, RelOptInfo *rel)
+{
+	List	   *live_children = NIL;
+	int			cnt_part;
+	PartitionOptInfo *part_info = rel->part_info;
+
+	/* Handle only join relations. */
+	if (rel->reloptkind != RELOPT_JOINREL &&
+		rel->reloptkind != RELOPT_OTHER_JOINREL)
+		return;
+
+	/* If the relation is not partitioned, nothing to do. */
+	if (!part_info)
+		return;
+
+	for (cnt_part = 0; cnt_part < part_info->nparts; cnt_part++)
+	{
+		RelOptInfo	*child_rel = part_info->part_rels[cnt_part];
+
+		/* Ignore dummy child. */
+		if (!IS_DUMMY_REL(child_rel))
+		{
+			/* Collect the paths from child joinrel. */
+			/* TODO: we need to reassess how do we do this, since the join
+			 * between parents will require append paths for the child joinrel.
+			 */
+			generate_partition_wise_join_paths(root, child_rel);
+
+			/* Find the cheapest of the paths for this rel. */
+			set_cheapest(child_rel);
+
+			/* Should we generate any gather paths? */
+#ifdef OPTIMIZER_DEBUG
+			debug_print_rel(root, rel);
+#endif
+
+			live_children = lappend(live_children, child_rel);
+		}
+	}
+
+	/*
+	 * Create append paths by collecting sub paths from live children. Even if
+	 * there are no live children, we should create an append path with no
+	 * subpaths i.e. a dummy access path.
+	 */
+	add_paths_to_append_rel(root, rel, live_children);
+
+	if (live_children)
+		pfree(live_children);
+}
+
 /*****************************************************************************
  *			DEBUG SUPPORT
  *****************************************************************************/
 
 #ifdef OPTIMIZER_DEBUG
 
 static void
 print_relids(PlannerInfo *root, Relids relids)
 {
 	int			x;
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 2a49639..a23da1c 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -119,20 +119,21 @@ bool		enable_seqscan = true;
 bool		enable_indexscan = true;
 bool		enable_indexonlyscan = true;
 bool		enable_bitmapscan = true;
 bool		enable_tidscan = true;
 bool		enable_sort = true;
 bool		enable_hashagg = true;
 bool		enable_nestloop = true;
 bool		enable_material = true;
 bool		enable_mergejoin = true;
 bool		enable_hashjoin = true;
+bool		enable_partition_wise_join = true;
 
 typedef struct
 {
 	PlannerInfo *root;
 	QualCost	total;
 } cost_qual_eval_context;
 
 static List *extract_nonindex_conditions(List *qual_clauses, List *indexquals);
 static MergeScanSelCache *cached_scansel(PlannerInfo *root,
 			   RestrictInfo *rinfo,
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 0e50ad5..62a3f3a 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -2359,20 +2359,22 @@ eclass_useful_for_merging(PlannerInfo *root,
 
 	/*
 	 * Note we don't test ec_broken; if we did, we'd need a separate code path
 	 * to look through ec_sources.  Checking the members anyway is OK as a
 	 * possibly-overoptimistic heuristic.
 	 */
 
 	/* If specified rel is a child, we must consider the topmost parent rel */
 	if (rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
 		relids = find_childrel_top_parent(root, rel)->relids;
+	else if (rel->reloptkind == RELOPT_OTHER_JOINREL)
+		relids = rel->parent_relids;
 	else
 		relids = rel->relids;
 
 	/* If rel already includes all members of eclass, no point in searching */
 	if (bms_is_subset(eclass->ec_relids, relids))
 		return false;
 
 	/* To join, we need a member not in the given rel */
 	foreach(lc, eclass->ec_members)
 	{
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index cc7384f..8b34ab9 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -18,23 +18,29 @@
 
 #include "executor/executor.h"
 #include "foreign/fdwapi.h"
 #include "optimizer/cost.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 
 /* Hook for plugins to get control in add_paths_to_joinrel() */
 set_join_pathlist_hook_type set_join_pathlist_hook = NULL;
 
-#define PATH_PARAM_BY_REL(path, rel)  \
+#define PATH_PARAM_BY_PARENT(path, rel)	\
+	((path)->param_info && bms_overlap(PATH_REQ_OUTER(path),	\
+									   (rel)->parent_relids))
+#define PATH_PARAM_BY_REL_SELF(path, rel)  \
 	((path)->param_info && bms_overlap(PATH_REQ_OUTER(path), (rel)->relids))
 
+#define PATH_PARAM_BY_REL(path, rel)	\
+	(PATH_PARAM_BY_REL_SELF(path, rel) || PATH_PARAM_BY_PARENT(path, rel))
+
 static void sort_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel,
 					 RelOptInfo *outerrel, RelOptInfo *innerrel,
 					 JoinType jointype, JoinPathExtraData *extra);
 static void match_unsorted_outer(PlannerInfo *root, RelOptInfo *joinrel,
 					 RelOptInfo *outerrel, RelOptInfo *innerrel,
 					 JoinType jointype, JoinPathExtraData *extra);
 static void consider_parallel_nestloop(PlannerInfo *root,
 						   RelOptInfo *joinrel,
 						   RelOptInfo *outerrel,
 						   RelOptInfo *innerrel,
@@ -121,20 +127,22 @@ add_paths_to_joinrel(PlannerInfo *root,
 	 * Decide whether it's sensible to generate parameterized paths for this
 	 * joinrel, and if so, which relations such paths should require.  There
 	 * is usually no need to create a parameterized result path unless there
 	 * is a join order restriction that prevents joining one of our input rels
 	 * directly to the parameter source rel instead of joining to the other
 	 * input rel.  (But see allow_star_schema_join().)	This restriction
 	 * reduces the number of parameterized paths we have to deal with at
 	 * higher join levels, without compromising the quality of the resulting
 	 * plan.  We express the restriction as a Relids set that must overlap the
 	 * parameterization of any proposed join path.
+	 * TODO: do we need sjinfo created for children to added to the list in the
+	 * planner info? OR how do we handle the code below?
 	 */
 	foreach(lc, root->join_info_list)
 	{
 		SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc);
 
 		/*
 		 * SJ is relevant to this join if we have some part of its RHS
 		 * (possibly not all of it), and haven't yet joined to its LHS.  (This
 		 * test is pretty simplistic, but should be sufficient considering the
 		 * join has already been proven legal.)  If the SJ is relevant, it
@@ -272,20 +280,35 @@ try_nestloop_path(PlannerInfo *root,
 				  Path *outer_path,
 				  Path *inner_path,
 				  List *pathkeys,
 				  JoinType jointype,
 				  JoinPathExtraData *extra)
 {
 	Relids		required_outer;
 	JoinCostWorkspace workspace;
 
 	/*
+	 * An inner path parameterized by the parent relation of outer
+	 * relation needs to be reparameterized by the outer relation to be used
+	 * for parameterized nested loop join.
+	 */
+	if (PATH_PARAM_BY_PARENT(inner_path, outer_path->parent))
+	{
+		inner_path = reparameterize_path_for_child(root, inner_path,
+												   outer_path->parent);
+
+		/* If we could not translate the path, don't produce nest loop path. */
+		if (!inner_path)
+			return;
+	}
+
+	/*
 	 * Check to see if proposed path is still parameterized, and reject if the
 	 * parameterization wouldn't be sensible --- unless allow_star_schema_join
 	 * says to allow it anyway.  Also, we must reject if have_dangerous_phv
 	 * doesn't like the look of it, which could only happen if the nestloop is
 	 * still parameterized.
 	 */
 	required_outer = calc_nestloop_required_outer(outer_path,
 												  inner_path);
 	if (required_outer &&
 		((!bms_overlap(required_outer, extra->param_source_rels) &&
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 01d4fea..6378d2e 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -7,38 +7,71 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
  *	  src/backend/optimizer/path/joinrels.c
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
+#include "optimizer/clauses.h"
 #include "optimizer/joininfo.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
+#include "optimizer/prep.h"
+#include "optimizer/cost.h"
 #include "utils/memutils.h"
 
 
 static void make_rels_by_clause_joins(PlannerInfo *root,
 						  RelOptInfo *old_rel,
 						  ListCell *other_rels);
 static void make_rels_by_clauseless_joins(PlannerInfo *root,
 							  RelOptInfo *old_rel,
 							  ListCell *other_rels);
 static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
 static bool has_legal_joinclause(PlannerInfo *root, RelOptInfo *rel);
 static bool is_dummy_rel(RelOptInfo *rel);
 static void mark_dummy_rel(RelOptInfo *rel);
 static bool restriction_is_constant_false(List *restrictlist,
 							  bool only_pushed_down);
+static void populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
+							RelOptInfo *rel2, RelOptInfo *joinrel,
+							SpecialJoinInfo *sjinfo, List *restrictlist);
+static void try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1,
+						  RelOptInfo *rel2, RelOptInfo *joinrel,
+						  SpecialJoinInfo *parent_sjinfo,
+						  List *parent_restrictlist);
+static SpecialJoinInfo * build_partition_join_sjinfo(PlannerInfo *root,
+										 SpecialJoinInfo *parent_sjinfo,
+										 List *append_rel_infos1,
+										 List *append_rel_infos2);
+static PartitionOptInfo *build_joinrel_part_info(RelOptInfo *rel1,
+							   RelOptInfo *rel2, JoinType jointype,
+							   List *restrictlist);
+static bool have_same_part_info(PartitionOptInfo *part_info1,
+								PartitionOptInfo *part_info2);
+static bool have_partkey_equi_join(RelOptInfo *rel1, RelOptInfo *rel2,
+						   JoinType jointype, List *restrictlist);
+static int match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel);
+static bool partkey_types_match(PartitionOptInfo *part_info1,
+					PartitionOptInfo *part_info2);
+static bool have_same_partition_lists(PartitionOptInfo *part_info1,
+					  PartitionOptInfo *part_info2);
+static bool have_same_partition_bounds(PartitionOptInfo *part_info1,
+					  PartitionOptInfo *part_info2);
+static bool compare_partition_bounds(PartitionOptInfo *part_info,
+					PartitionRangeBound *bound1, PartitionRangeBound *bound2);
+static bool match_joinrel_part_info(RelOptInfo *joinrel, RelOptInfo *rel1,
+						  RelOptInfo *rel2, JoinType jointype,
+						  List *restrictlist);
 
 
 /*
  * join_search_one_level
  *	  Consider ways to produce join relations containing exactly 'level'
  *	  jointree items.  (This is one step of the dynamic-programming method
  *	  embodied in standard_join_search.)  Join rel nodes for each feasible
  *	  combination of lower-level rels are created and returned in a list.
  *	  Implementation paths are created for each such joinrel, too.
  *
@@ -717,20 +750,44 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
 	/*
 	 * If we've already proven this join is empty, we needn't consider any
 	 * more paths for it.
 	 */
 	if (is_dummy_rel(joinrel))
 	{
 		bms_free(joinrelids);
 		return joinrel;
 	}
 
+	/* Add paths to the join relation. */
+	populate_joinrel_with_paths(root, rel1, rel2, joinrel, sjinfo,
+								restrictlist);
+
+	/* Apply partition-wise join technique, if possible. */
+	try_partition_wise_join(root, rel1, rel2, joinrel, sjinfo, restrictlist);
+
+	bms_free(joinrelids);
+
+	return joinrel;
+}
+
+/*
+ * populate_joinrel_with_paths
+ *	  Create paths to join given input relation and add those to the given
+ *	  joinrel. The SpecialJoinInfo provides details about the join and the
+ *	  restrictlist contains the join clauses and the other clauses applicable
+ *	  for given pair of the joining relations.
+ */
+static void
+populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
+							RelOptInfo *rel2, RelOptInfo *joinrel,
+							SpecialJoinInfo *sjinfo, List *restrictlist)
+{
 	/*
 	 * Consider paths using each rel as both outer and inner.  Depending on
 	 * the join type, a provably empty outer or inner rel might mean the join
 	 * is provably empty too; in which case throw away any previously computed
 	 * paths and mark the join as dummy.  (We do it this way since it's
 	 * conceivable that dummy-ness of a multi-element join might only be
 	 * noticeable for certain construction paths.)
 	 *
 	 * Also, a provably constant-false join restriction typically means that
 	 * we can skip evaluating one or both sides of the join.  We do this by
@@ -861,27 +918,22 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
 				mark_dummy_rel(rel2);
 			add_paths_to_joinrel(root, joinrel, rel1, rel2,
 								 JOIN_ANTI, sjinfo,
 								 restrictlist);
 			break;
 		default:
 			/* other values not expected here */
 			elog(ERROR, "unrecognized join type: %d", (int) sjinfo->jointype);
 			break;
 	}
-
-	bms_free(joinrelids);
-
-	return joinrel;
 }
 
-
 /*
  * have_join_order_restriction
  *		Detect whether the two relations should be joined to satisfy
  *		a join-order restriction arising from special or lateral joins.
  *
  * In practice this is always used with have_relevant_joinclause(), and so
  * could be merged with that function, but it seems clearer to separate the
  * two concerns.  We need this test because there are degenerate cases where
  * a clauseless join must be performed to satisfy join-order restrictions.
  * Also, if one rel has a lateral reference to the other, or both are needed
@@ -1242,10 +1294,744 @@ restriction_is_constant_false(List *restrictlist, bool only_pushed_down)
 
 			/* constant NULL is as good as constant FALSE for our purposes */
 			if (con->constisnull)
 				return true;
 			if (!DatumGetBool(con->constvalue))
 				return true;
 		}
 	}
 	return false;
 }
+
+/*
+ * If the join between the given two relations can be executed as
+ * partition-wise join create the join relations for partition-wise join,
+ * create paths for those and then create append paths to combine
+ * partition-wise join results.
+ *
+ * TODO: naming convention in this function is screwd up, sometimes we use
+ * part_ to indicate a partition variable and sometimes we use parent_ to
+ * indicate parent variable. Need to organize it better.
+ */
+static void
+try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
+						RelOptInfo *joinrel, SpecialJoinInfo *parent_sjinfo,
+						List *parent_restrictlist)
+{
+	PartitionOptInfo *part_info1 = rel1->part_info;
+	PartitionOptInfo *part_info2 = rel2->part_info;
+	int		nparts;
+	int		cnt_parts;
+	StringInfo	rel1_desc;
+	StringInfo	rel2_desc;
+	PartitionOptInfo *part_info;
+
+	if (!joinrel->part_info)
+	{
+		/* Can this join be executed using partition-wise join? */
+		joinrel->part_info = build_joinrel_part_info(rel1, rel2,
+													 parent_sjinfo->jointype,
+													 parent_restrictlist);
+
+		/* If partition-wise join is not possible, nothing to do. */
+		if (!joinrel->part_info)
+			return;
+	}
+	else
+	{
+		/*
+		 * Generally, we should check whether the partition scheme for the join
+		 * is same as the partitioning scheme of both the joining relations. If
+		 * not, we should create a new PartitionOptInfo for new partitioning
+		 * scheme. But we allow partition-wise join only in those cases, when
+		 * the partitioning scheme of resultant relation is same irrespective
+		 * of the order in which the relations are joined. Hence make sure that
+		 * the already computed partition scheme for join matches that required
+		 * by the current pair of joining relations.
+		 */
+		Assert(match_joinrel_part_info(joinrel, rel1, rel2,
+									   parent_sjinfo->jointype,
+									   parent_restrictlist));
+	}
+
+	part_info = joinrel->part_info;
+	nparts = part_info->nparts;
+	rel1_desc = makeStringInfo();
+	rel2_desc = makeStringInfo();
+
+	/* TODO: remove this notice when finalising the patch. */
+	outBitmapset(rel1_desc, rel1->relids);
+	outBitmapset(rel2_desc, rel2->relids);
+	elog(NOTICE, "join between relations %s and %s is considered for partition-wise join.",
+		 rel1_desc->data, rel2_desc->data);
+
+	/*
+	 * Create join relations for the partition relations, if they do not exist
+	 * already. Add paths to those for the given pair of joining relations.
+	 */
+	for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++)
+	{
+		RelOptInfo	*part_joinrel;
+		RelOptInfo	*part_rel1 = part_info1->part_rels[cnt_parts];
+		RelOptInfo	*part_rel2 = part_info2->part_rels[cnt_parts];
+		SpecialJoinInfo *sjinfo;
+		List		*restrictlist;
+		List		*join_aris;
+		List		*ari1;
+		List		*ari2;
+		Relids	joinrelids;
+
+		/* We should never try to join two overlapping sets of rels. */
+		Assert(!bms_overlap(part_rel1->relids, part_rel2->relids));
+
+		/* Construct Relids set that identifies the joinrel. */
+		joinrelids = bms_union(part_rel1->relids, part_rel2->relids);
+
+		/*
+		 * Gather the AppendRelInfos for base partition relations
+		 * partiticipating in the given partition relations. We need them
+		 * construct partition-wise join relation, special join info and
+		 * restriction list by substituting the Var and relids from parent to
+		 * child.
+		 */
+		ari1 = find_appendrelinfos_by_relids(root, part_rel1->relids);
+		ari2 = find_appendrelinfos_by_relids(root, part_rel2->relids);
+
+		/*
+		 * Construct SpecialJoinInfo from parent join relations's
+		 * SpecialJoinInfo.
+		 */
+		sjinfo = build_partition_join_sjinfo(root, parent_sjinfo, ari1, ari2);
+
+		/* Construct the parent-child relid map for the join relation. */
+		join_aris = list_concat(ari1, ari2);
+
+		/*
+		 * Construct restrictions applicable to the partition-wise join from
+		 * those applicable to the join between the parents.
+		 */
+		restrictlist = (List *) adjust_partitionrel_attrs(root,
+													(Node *)parent_restrictlist,
+													join_aris);
+
+		part_joinrel = part_info->part_rels[cnt_parts];
+
+		/* Construct the join relation for given partition of the join. */
+		if (!part_joinrel)
+		{
+			part_joinrel = make_joinrel(root, RELOPT_OTHER_JOINREL,
+										joinrelids);
+
+			/*
+			 * Fill partition join relation by translating information from the
+			 * parent join relation.
+			 */
+			fill_partition_join_rel(root, part_rel1, part_rel2, part_joinrel,
+									joinrel, sjinfo, restrictlist, join_aris);
+
+			part_info->part_rels[cnt_parts] = part_joinrel;
+
+			add_joinrel_to_list(root, part_joinrel);
+		}
+
+		/* TODO: set_append_rel_size() calls add_child_rel_equivalences() for
+		 * adding child equivalence classes one parent at a time. So, we do not
+		 * add equivalence classes with multiple child relations. Do we need to
+		 * do that? Possibily because of the parent node translation above we
+		 * don't need it. Anyway, is there a way to verify it?
+		 */
+		Assert(bms_equal(part_joinrel->relids, joinrelids));
+
+		bms_free(joinrelids);
+
+		/*
+		 * If we've already proven that this join is empty, we needn't consider
+		 * any more paths for it.
+		 */
+		if (is_dummy_rel(part_joinrel))
+			continue;
+
+		populate_joinrel_with_paths(root, part_rel1, part_rel2, part_joinrel,
+									sjinfo, restrictlist);
+
+		/*
+		 * If the partition themselves are partitioned, try partition-wise join
+		 * recursively.
+		 * TODO: we need to decide the correct place for this function to be
+		 * called. The recursive nature of this call probably requires
+		 * AppendPath to be added before we try to populate the other kinds of
+		 * join paths for parent relations. So, we may have to call this
+		 * function before populate_joinrel_with_paths().
+		 */
+		try_partition_wise_join(root, part_rel1, part_rel2, part_joinrel, sjinfo,
+								restrictlist);
+	}
+}
+
+/*
+ * Construct the SpecialJoinInfo for the partition-wise join using parents'
+ * special join info. Also, instead of
+ * constructing an sjinfo everytime, we should probably save it in
+ * root->join_info_list and search within it like join_is_legal?
+ */
+static SpecialJoinInfo *
+build_partition_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo,
+							List *append_rel_infos1, List *append_rel_infos2)
+{
+	SpecialJoinInfo *sjinfo = copyObject(parent_sjinfo);
+	sjinfo->min_lefthand = adjust_partition_relids(sjinfo->min_lefthand,
+												   append_rel_infos1);
+	sjinfo->min_righthand = adjust_partition_relids(sjinfo->min_righthand,
+													append_rel_infos2);
+	sjinfo->syn_lefthand = adjust_partition_relids(sjinfo->syn_lefthand,
+												   append_rel_infos1);
+	sjinfo->syn_righthand = adjust_partition_relids(sjinfo->syn_righthand,
+													append_rel_infos2);
+
+	/* Replace the Var nodes of parent with those of children in expressions. */
+	sjinfo->semi_rhs_exprs = (List *) adjust_partitionrel_attrs(root,
+											   (Node *) sjinfo->semi_rhs_exprs,
+															append_rel_infos2);
+	return sjinfo;
+}
+
+/*
+ * have_same_partition_lists
+ *
+ * For given list partitioned relations, return true if lists for all the
+ * partitions of both the relations match. Return false otherwise.
+ */
+static bool
+have_same_partition_lists(PartitionOptInfo *part_info1,
+						  PartitionOptInfo *part_info2)
+{
+	int		cnt_parts;
+
+	Assert(part_info1->strategy == PARTITION_STRAT_LIST &&
+		   part_info2->strategy == PARTITION_STRAT_LIST);
+
+	Assert(part_info1->nparts == part_info2->nparts);
+
+	/* List partition has only one partition key. */
+	Assert(part_info1->partnatts == 1 && part_info2->partnatts == 1);
+
+	for (cnt_parts = 0; cnt_parts < part_info1->nparts; cnt_parts++)
+	{
+		PartitionListInfo *list1 = part_info1->lists[cnt_parts];
+		PartitionListInfo *list2 = part_info2->lists[cnt_parts];
+		int		cnt;
+
+		/*
+		 * If number of items in the lists do not match or the one of the lists
+		 * contains NULL value and other doesn't, lists do not match.
+		 *
+		 * TODO: This condition can be relaxed to accomodate subset of items,
+		 * but probably not in the first cut.
+		 */
+		if (list1->nvalues != list2->nvalues)
+			return false;
+
+		/* Compare actual values in the list. */
+		for (cnt = 0; cnt < list1->nvalues; cnt++)
+		{
+			Datum	cmpval;
+
+			if (list1->nulls[cnt] != list2->nulls[cnt])
+				return false;
+
+			/* Skip NULL value comparisons. */
+			if (list1->nulls[cnt])
+				continue;
+
+			cmpval = FunctionCall2Coll(&part_info1->partsupfunc[0],
+									   part_info1->tcinfo->typcoll[0],
+									   list1->values[cnt],
+									   list2->values[cnt]);
+			if (DatumGetInt32(cmpval))
+				return false;
+		}
+	}
+
+	/* Ok, everything matches, return true. */
+	return true;
+}
+
+/*
+ * have_same_partition_bounds
+ *
+ * For given partitioned relations, return true if the bounds of all the
+ * partitions of the both the relations match. Return false otherwise.
+ */
+static bool
+have_same_partition_bounds(PartitionOptInfo *part_info1,
+						   PartitionOptInfo *part_info2)
+{
+	int		cnt_parts;
+	Assert(part_info1->strategy == PARTITION_STRAT_RANGE &&
+		   part_info2->strategy == PARTITION_STRAT_RANGE);
+
+	Assert(part_info1->nparts == part_info2->nparts);
+
+	for (cnt_parts = 0; cnt_parts < part_info1->nparts; cnt_parts++)
+	{
+		PartitionRangeBound *lower1 = part_info1->ranges[cnt_parts]->lower;
+		PartitionRangeBound *lower2 = part_info2->ranges[cnt_parts]->lower;
+		PartitionRangeBound *upper1 = part_info1->ranges[cnt_parts]->upper;
+		PartitionRangeBound *upper2 = part_info2->ranges[cnt_parts]->upper;
+
+		if (!compare_partition_bounds(part_info1, lower1, lower2) ||
+			!compare_partition_bounds(part_info1, upper1, upper2))
+			return false;
+	}
+
+	/* Ok, everything matches. */
+	return true;
+}
+
+/*
+ * compare_partition_bounds
+ *
+ * Return true if given bound specifications match.
+ */
+static bool
+compare_partition_bounds(PartitionOptInfo *part_info,
+						 PartitionRangeBound *bound1,
+						 PartitionRangeBound *bound2)
+{
+	int		cnt_pks;
+	Datum  *val1 = bound1->val;
+	Datum  *val2 = bound2->val;
+
+	if (bound1->infinite != bound2->infinite ||
+		bound1->inclusive != bound2->inclusive	||
+		bound1->lower != bound2->lower)
+		return false;
+
+	/*
+	 * TODO:
+	 * Following part is common with compare_range_keys(), but that takes
+	 * PartitionKey as argument, which we don't have here.
+	 */
+	for (cnt_pks = 0; cnt_pks < part_info->partnatts; cnt_pks++)
+	{
+		Datum	cmpval;
+
+		cmpval = FunctionCall2Coll(&part_info->partsupfunc[cnt_pks],
+								   part_info->tcinfo->typcoll[cnt_pks],
+								   val1[cnt_pks], val2[cnt_pks]);
+
+		if (DatumGetInt32(cmpval))
+			return false;
+	}
+
+	/* Ok, everything matches. */
+	return true;
+}
+
+/*
+ * partkey_types_match
+ *
+ * Returns true if the partition key datatype and collation match for given
+ * partitioned relations. Returns false otherwise.
+ */
+static bool
+partkey_types_match(PartitionOptInfo *part_info1, PartitionOptInfo *part_info2)
+{
+	int		cnt_pks;
+	Oid	   *partopfamily1 = part_info1->partopfamily;
+	Oid	   *partopfamily2 = part_info2->partopfamily;
+	Oid	   *partopcintype1 = part_info1->partopcintype;
+	Oid	   *partopcintype2 = part_info2->partopcintype;
+	FmgrInfo *partsupfunc1 = part_info1->partsupfunc;
+	FmgrInfo *partsupfunc2 = part_info2->partsupfunc;
+	KeyTypeCollInfo *tcinfo1 = part_info1->tcinfo;
+	KeyTypeCollInfo *tcinfo2 = part_info2->tcinfo;
+
+	if (part_info1->partnatts != part_info1->partnatts)
+		return false;
+
+	for (cnt_pks = 0; cnt_pks < part_info1->partnatts; cnt_pks++)
+	{
+		/*
+		 * It suffices to check the OID of support function as it always has
+		 * two arguemnts and returns boolean. For types, it suffices to match
+		 * the type id, mod and collation; len, byval and align are depedent on
+		 * the first two.
+		 */
+		if (partopfamily1[cnt_pks] != partopfamily2[cnt_pks] ||
+			partopcintype1[cnt_pks] != partopcintype2[cnt_pks] ||
+			partsupfunc1[cnt_pks].fn_oid != partsupfunc2[cnt_pks].fn_oid ||
+			tcinfo1->typid[cnt_pks] != tcinfo2->typid[cnt_pks] ||
+			tcinfo1->typmod[cnt_pks] != tcinfo2->typmod[cnt_pks] ||
+			tcinfo1->typcoll[cnt_pks] != tcinfo2->typcoll[cnt_pks])
+			return false;
+	}
+
+	/* Everything matches. */
+	return true;
+}
+
+/*
+ * Substitute oldrelids with newrelids in the given Relids set. It recycles the
+ * given relids input.
+ */
+Relids
+adjust_partition_relids(Relids relids, List *append_rel_infos)
+{
+	ListCell	*lc;
+	foreach (lc, append_rel_infos)
+	{
+		AppendRelInfo	*ari = lfirst(lc);
+
+		/* Remove old, add new */
+		if (bms_is_member(ari->parent_relid, relids))
+		{
+			relids = bms_del_member(relids, ari->parent_relid);
+			relids = bms_add_member(relids, ari->child_relid);
+		}
+	}
+	return relids;
+}
+
+/*
+ * Check whether the given two partitioning schemes are same.
+ */
+static bool
+have_same_part_info(PartitionOptInfo *part_info1, PartitionOptInfo *part_info2)
+{
+	int		cnt;
+
+	/*
+	 * If number of partitions, number of partition keys and partitioning
+	 * strategy of the joining relations do not match, we can not apply
+	 * partition-wise join.
+	 *
+	 * TODO: it should be possible to push an inner join down even if the number of
+	 * partitions differ but the common partitions match. In such a case pushing
+	 * down outer joins would be tricky, but still doable using empty relation
+	 * for non-existing partition.
+	 */
+	if (!part_info1 || !part_info2 ||
+		part_info1->nparts != part_info2->nparts ||
+		part_info1->strategy != part_info2->strategy ||
+		part_info1->partnatts != part_info2->partnatts)
+		return false;
+
+	/*
+	 * Partition-wise join between multi-level partitioned tables is not
+	 * supported, since the partition hierarchy doesn't get translated into the
+	 * corresponding RelOptInfo hierarchy. Since the partition hierarchy is
+	 * flattened, if we add append paths to intermediate partition relations,
+	 * the corresponding rows will be added multiple times.
+	 */
+	for (cnt = 0; cnt < part_info1->nparts; cnt++)
+	{
+		RelOptInfo *part_rel1 = part_info1->part_rels[cnt];
+		RelOptInfo *part_rel2 = part_info2->part_rels[cnt];
+		if (!part_rel1 || !part_rel2 || part_rel1->part_info ||
+			part_rel2->part_info)
+			return false;
+	}
+
+	/*
+	 * Partition-wise join can not be applied if datatypes and collations of
+	 * the partition keys do not match.
+	 */
+	if (!partkey_types_match(part_info1, part_info2))
+		return false;
+
+	/*
+	 * Partition-wise join can not be applied if the partition bounds or lists
+	 * of joining relations do not match.
+	 */
+	switch (part_info1->strategy)
+	{
+		case PARTITION_STRAT_LIST:
+			if (!have_same_partition_lists(part_info1, part_info2))
+				return false;
+			break;
+
+		case PARTITION_STRAT_RANGE:
+			if (!have_same_partition_bounds(part_info1, part_info2))
+				return false;
+			break;
+
+		default:
+			/* Unknown partition strategy. */
+			return false;
+	}
+
+	return true;
+}
+
+/*
+ * build_joinrel_part_info
+ *
+ * Given partition description of two joining relations, construct partition
+ * description for join between those relations. If we can execute
+ * partition-wise join the resultant join is partitioned in the same way as the
+ * joining relations. Otherwise, it is not partitioned and in such case the
+ * function returns NULL.
+ *
+ * TODO find the right place for this function.
+ *
+ * TODO:
+ * This function and
+ * build_partition_info() should be named consistently e.g.
+ * build_joinrel_partition_info and build_simplerel_partition_info or
+ * build_baserel_partition_info resp.
+ *
+ * TODO: this code can be simplified a lot if we precompute the partitioning
+ * compatibility between relations. See Robert's idea in 9th August mail on
+ * thread "Is any ordering of N-way join always partition-wise joinable?"
+ */
+static PartitionOptInfo *
+build_joinrel_part_info(RelOptInfo *rel1, RelOptInfo *rel2,
+						JoinType jointype, List *restrictlist)
+{
+	PartitionOptInfo	*part_info;
+	PartitionOptInfo	*part_info1 = rel1->part_info;
+	PartitionOptInfo	*part_info2 = rel2->part_info;
+	int		cnt;
+	int		num_pks;
+
+	/* Do nothing, if user doesn't want to try partition-wise join. */
+	if (!enable_partition_wise_join)
+		return NULL;
+
+	if (!have_same_part_info(part_info1, part_info2))
+		return NULL;
+
+	/*
+	 * Partition-wise join can not be applied if there is no equi-join
+	 * condition between partition keys.
+	 */
+	if (!have_partkey_equi_join(rel1, rel2, jointype, restrictlist))
+		return NULL;
+
+	/* The join is partitioned the similar to the joining relations. */
+	part_info = makeNode(PartitionOptInfo);
+
+	/* Information related to the partititions. */
+	part_info->nparts = part_info1->nparts;
+	/* Allocate space for partition RelOptInfos, which will be filled later. */
+	part_info->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) *
+												  part_info->nparts);
+	part_info->lists = part_info1->lists;
+	part_info->ranges = part_info1->ranges;
+
+	/* Information related to partition keys. */
+	part_info->strategy = part_info1->strategy;
+	part_info->partnatts = part_info1->partnatts;
+	part_info->partopfamily = part_info1->partopfamily;
+	part_info->partopcintype = part_info1->partopcintype;
+	part_info->partsupfunc = part_info1->partsupfunc;
+	part_info->tcinfo = part_info1->tcinfo;
+	num_pks = part_info->partnatts;
+	part_info->partexprs = (List **) palloc0(sizeof(List *) * num_pks);
+	for (cnt = 0; cnt < num_pks; cnt++)
+	{
+		List *pkexpr = list_copy(part_info1->partexprs[cnt]);
+
+		pkexpr = list_concat(pkexpr,
+							 list_copy(part_info2->partexprs[cnt]));
+		part_info->partexprs[cnt] = pkexpr;
+	}
+
+	return part_info;
+}
+
+/*
+ * Returns true if the given relations have equi-join clauses on all the
+ * corresponding partition keys.
+ */
+static bool
+have_partkey_equi_join(RelOptInfo *rel1, RelOptInfo *rel2,
+					 JoinType jointype, List *restrictlist)
+{
+	PartitionOptInfo	*part_info1 = rel1->part_info;
+	PartitionOptInfo	*part_info2 = rel2->part_info;
+	ListCell	*lc;
+	int		cnt_pks;
+	int		num_pks;
+	bool   *pk_has_clause;
+
+	Assert(part_info1->partnatts == part_info2->partnatts);
+	num_pks = part_info1->partnatts;
+	pk_has_clause = (bool *) palloc0(sizeof(bool) * num_pks);
+
+	foreach (lc, restrictlist)
+	{
+		RestrictInfo *rinfo = lfirst(lc);
+		OpExpr		 *opexpr;
+		Expr		 *expr1;
+		Expr		 *expr2;
+		int		ipk1;
+		int		ipk2;
+
+		/* If processing an outer join, only use its own join clauses. */
+		if (IS_OUTER_JOIN(jointype) && rinfo->is_pushed_down)
+			continue;
+
+		/* Skip non-equi-join clauses. */
+		if (!rinfo->can_join ||
+			rinfo->hashjoinoperator == InvalidOid ||
+			!rinfo->mergeopfamilies)
+			continue;
+
+		opexpr = (OpExpr *) rinfo->clause;
+		Assert(is_opclause(opexpr));
+
+		/*
+		 * If clause of form rel1_expr op rel2_expr OR rel2_expr op rel1_expr,
+		 * match the operands to the relations. Otherwise, the clause is
+		 * not an equi-join between partition keys of joining relations.
+		 */
+		if (bms_is_subset(rinfo->left_relids, rel1->relids) &&
+			bms_is_subset(rinfo->right_relids, rel2->relids))
+		{
+			expr1 = linitial(opexpr->args);
+			expr2 = lsecond(opexpr->args);
+		}
+		else if (bms_is_subset(rinfo->left_relids, rel2->relids) &&
+				 bms_is_subset(rinfo->right_relids, rel1->relids))
+		{
+			expr1 = lsecond(opexpr->args);
+			expr2 = linitial(opexpr->args);
+		}
+		else
+			continue;
+
+		/* Associate matching clauses with partition keys. */
+		ipk1 = match_expr_to_partition_keys(expr1, rel1);
+		ipk2 = match_expr_to_partition_keys(expr2, rel2);
+
+		if (ipk1 == ipk2)
+			pk_has_clause[ipk1] = true;
+	}
+
+	/*
+	 * If every pair of partition key from either of the joining relation has
+	 * at least one equi-join clause associated with it, we have an equi-join
+	 * between all corresponding partition keys.
+	 */
+	for (cnt_pks = 0; cnt_pks < num_pks; cnt_pks++)
+	{
+		if (!pk_has_clause[cnt_pks])
+		{
+			pfree(pk_has_clause);
+			return false;
+		}
+	}
+
+	pfree(pk_has_clause);
+	return true;
+}
+
+/*
+ * match_expr_to_partition_keys
+ *
+ * Find the partition key which is same as the given expression. If found,
+ * return the index of the partition key, else return -1.
+ */
+static int
+match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel)
+{
+	PartitionOptInfo *part_info = rel->part_info;
+	int		cnt_pks;
+
+	/*
+	 * Remove the relabel decoration. We can assume that there is at most one
+	 * RelabelType node; eval_const_expressions() will have simplied if more
+	 * than one.
+	 */
+	if (IsA(expr, RelabelType))
+		expr = (Expr *) ((RelabelType *) expr)->arg;
+
+	for (cnt_pks = 0; cnt_pks < part_info->partnatts; cnt_pks++)
+	{
+		List	 *pkexprs = part_info->partexprs[cnt_pks];
+		ListCell *lc;
+
+		foreach(lc, pkexprs)
+		{
+			Expr *pkexpr = lfirst(lc);
+			if (equal(pkexpr, expr))
+				return cnt_pks;
+		}
+	}
+
+	return -1;
+}
+
+/*
+ * Checks
+ * 1. if the partitioning scheme of the join relation match that of the joining
+ *    relations.
+ * 2. if there exists equi-join condition between the partition keys of the
+ *    joining relations.
+ * 3. if the partition keys of the join contain the partition keys of both the
+ *    relations.
+ */
+static bool
+match_joinrel_part_info(RelOptInfo *joinrel, RelOptInfo *rel1,
+						RelOptInfo *rel2, JoinType jointype,
+						List *restrictlist)
+{
+	int		cnt;
+	int		num_pks;
+	PartitionOptInfo *join_part_info = joinrel->part_info;
+	PartitionOptInfo *part_info1 = rel1->part_info;
+	PartitionOptInfo *part_info2= rel1->part_info;
+
+	Assert(enable_partition_wise_join);
+
+	/*
+	 * If partitioning scheme of the join doesn't match that of the joining
+	 * relations, join relations is not partitioned in the same way as one or
+	 * both of the joining relations.
+	 */
+	if (!have_same_part_info(join_part_info, part_info1) ||
+		!have_same_part_info(join_part_info, part_info2))
+		return false;
+
+	/*
+	 * If an equi-join condition between the partition keys of the joining
+	 * relations does not exist, the given partition scheme can not be used for
+	 * partition-wise join between these two relations.
+	 */
+	if (!have_partkey_equi_join(rel1, rel2, jointype, restrictlist))
+		return false;
+
+	/*
+	 * If the partition keys from either of the joining relations are not part
+	 * of the partition keys of the join relation, again we can not use the
+	 * given partition scheme for joining the given relations partition-wise.
+	 */
+	Assert(join_part_info->partnatts == part_info1->partnatts);
+	Assert(join_part_info->partnatts == part_info2->partnatts);
+
+	num_pks = join_part_info->partnatts;
+	for (cnt = 0; cnt < num_pks; cnt++)
+	{
+		List *pkexpr = join_part_info->partexprs[cnt];
+		List *pkexpr1 = part_info1->partexprs[cnt];
+		List *pkexpr2 = part_info2->partexprs[cnt];
+		List *diff_list;
+
+		diff_list = list_difference(pkexpr1, pkexpr);
+		if (diff_list)
+		{
+			list_free(diff_list);
+			return false;
+		}
+
+		diff_list = list_difference(pkexpr2, pkexpr);
+		if (diff_list)
+		{
+			list_free(diff_list);
+			return false;
+		}
+	}
+
+	return true;
+}
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 4436ac1..dba6772 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -1081,26 +1081,38 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 								List *mergeclauses,
 								RelOptInfo *joinrel)
 {
 	List	   *pathkeys = NIL;
 	int			nClauses = list_length(mergeclauses);
 	EquivalenceClass **ecs;
 	int		   *scores;
 	int			necs;
 	ListCell   *lc;
 	int			j;
+	Relids		relids;
 
 	/* Might have no mergeclauses */
 	if (nClauses == 0)
 		return NIL;
 
 	/*
+	 * Code below scores equivalence classes by how many equivalence members
+	 * can produce join clauses for this join relation. Equivalence members
+	 * which do not cover the parents of a partition-wise join relation, can
+	 * produce join clauses for partition-wise join relation.
+	 */
+	if (joinrel->reloptkind == RELOPT_OTHER_JOINREL)
+		relids = joinrel->parent_relids;
+	else
+		relids = joinrel->relids;
+
+	/*
 	 * Make arrays of the ECs used by the mergeclauses (dropping any
 	 * duplicates) and their "popularity" scores.
 	 */
 	ecs = (EquivalenceClass **) palloc(nClauses * sizeof(EquivalenceClass *));
 	scores = (int *) palloc(nClauses * sizeof(int));
 	necs = 0;
 
 	foreach(lc, mergeclauses)
 	{
 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
@@ -1126,21 +1138,21 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 			continue;
 
 		/* compute score */
 		score = 0;
 		foreach(lc2, oeclass->ec_members)
 		{
 			EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
 
 			/* Potential future join partner? */
 			if (!em->em_is_const && !em->em_is_child &&
-				!bms_overlap(em->em_relids, joinrel->relids))
+				!bms_overlap(em->em_relids, relids))
 				score++;
 		}
 
 		ecs[necs] = oeclass;
 		scores[necs] = score;
 		necs++;
 	}
 
 	/*
 	 * Find out if we have all the ECs mentioned in query_pathkeys; if so we
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 32f4031..b221e2c 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -235,21 +235,22 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 						   const AttrNumber *reqColIdx,
 						   bool adjust_tlist_in_place,
 						   int *p_numsortkeys,
 						   AttrNumber **p_sortColIdx,
 						   Oid **p_sortOperators,
 						   Oid **p_collations,
 						   bool **p_nullsFirst);
 static EquivalenceMember *find_ec_member_for_tle(EquivalenceClass *ec,
 					   TargetEntry *tle,
 					   Relids relids);
-static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys);
+static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
+									 Relids relids);
 static Sort *make_sort_from_groupcols(List *groupcls,
 						 AttrNumber *grpColIdx,
 						 Plan *lefttree);
 static Material *make_material(Plan *lefttree);
 static WindowAgg *make_windowagg(List *tlist, Index winref,
 			   int partNumCols, AttrNumber *partColIdx, Oid *partOperators,
 			   int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators,
 			   int frameOptions, Node *startOffset, Node *endOffset,
 			   Plan *lefttree);
 static Group *make_group(List *tlist, List *qual, int numGroupCols,
@@ -1507,21 +1508,21 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 	Plan	   *subplan;
 
 	/*
 	 * We don't want any excess columns in the sorted tuples, so request a
 	 * smaller tlist.  Otherwise, since Sort doesn't project, tlist
 	 * requirements pass through.
 	 */
 	subplan = create_plan_recurse(root, best_path->subpath,
 								  flags | CP_SMALL_TLIST);
 
-	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys);
+	plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys, NULL);
 
 	copy_generic_path_info(&plan->plan, (Path *) best_path);
 
 	return plan;
 }
 
 /*
  * create_group_plan
  *
  *	  Create a Group plan for 'best_path' and (recursively) plans
@@ -3517,31 +3518,33 @@ create_mergejoin_plan(PlannerInfo *root,
 	List	   *innerpathkeys;
 	int			nClauses;
 	Oid		   *mergefamilies;
 	Oid		   *mergecollations;
 	int		   *mergestrategies;
 	bool	   *mergenullsfirst;
 	int			i;
 	ListCell   *lc;
 	ListCell   *lop;
 	ListCell   *lip;
+	Path	   *outer_path = best_path->jpath.outerjoinpath;
+	Path	   *inner_path = best_path->jpath.innerjoinpath;
 
 	/*
 	 * MergeJoin can project, so we don't have to demand exact tlists from the
 	 * inputs.  However, if we're intending to sort an input's result, it's
 	 * best to request a small tlist so we aren't sorting more data than
 	 * necessary.
 	 */
-	outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath,
+	outer_plan = create_plan_recurse(root, outer_path,
 					 (best_path->outersortkeys != NIL) ? CP_SMALL_TLIST : 0);
 
-	inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath,
+	inner_plan = create_plan_recurse(root, inner_path,
 					 (best_path->innersortkeys != NIL) ? CP_SMALL_TLIST : 0);
 
 	/* Sort join qual clauses into best execution order */
 	/* NB: do NOT reorder the mergeclauses */
 	joinclauses = order_qual_clauses(root, best_path->jpath.joinrestrictinfo);
 
 	/* Get the join qual clauses (in plain expression form) */
 	/* Any pseudoconstant clauses are ignored here */
 	if (IS_OUTER_JOIN(best_path->jpath.jointype))
 	{
@@ -3573,48 +3576,52 @@ create_mergejoin_plan(PlannerInfo *root,
 		otherclauses = (List *)
 			replace_nestloop_params(root, (Node *) otherclauses);
 	}
 
 	/*
 	 * Rearrange mergeclauses, if needed, so that the outer variable is always
 	 * on the left; mark the mergeclause restrictinfos with correct
 	 * outer_is_left status.
 	 */
 	mergeclauses = get_switched_clauses(best_path->path_mergeclauses,
-							 best_path->jpath.outerjoinpath->parent->relids);
+							 outer_path->parent->relids);
 
 	/*
 	 * Create explicit sort nodes for the outer and inner paths if necessary.
 	 */
 	if (best_path->outersortkeys)
 	{
+		Relids		outer_relids = outer_path->parent->relids;
 		Sort	   *sort = make_sort_from_pathkeys(outer_plan,
-												   best_path->outersortkeys);
+												   best_path->outersortkeys,
+												   outer_relids);
 
 		label_sort_with_costsize(root, sort, -1.0);
 		outer_plan = (Plan *) sort;
 		outerpathkeys = best_path->outersortkeys;
 	}
 	else
-		outerpathkeys = best_path->jpath.outerjoinpath->pathkeys;
+		outerpathkeys = outer_path->pathkeys;
 
 	if (best_path->innersortkeys)
 	{
+		Relids		inner_relids = inner_path->parent->relids;
 		Sort	   *sort = make_sort_from_pathkeys(inner_plan,
-												   best_path->innersortkeys);
+												   best_path->innersortkeys,
+												   inner_relids);
 
 		label_sort_with_costsize(root, sort, -1.0);
 		inner_plan = (Plan *) sort;
 		innerpathkeys = best_path->innersortkeys;
 	}
 	else
-		innerpathkeys = best_path->jpath.innerjoinpath->pathkeys;
+		innerpathkeys = inner_path->pathkeys;
 
 	/*
 	 * If specified, add a materialize node to shield the inner plan from the
 	 * need to handle mark/restore.
 	 */
 	if (best_path->materialize_inner)
 	{
 		Plan	   *matplan = (Plan *) make_material(inner_plan);
 
 		/*
@@ -5330,25 +5337,25 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
 
 				/*
 				 * We shouldn't be trying to sort by an equivalence class that
 				 * contains a constant, so no need to consider such cases any
 				 * further.
 				 */
 				if (em->em_is_const)
 					continue;
 
 				/*
-				 * Ignore child members unless they match the rel being
+				 * Ignore child members unless they belong to the rel being
 				 * sorted.
 				 */
 				if (em->em_is_child &&
-					!bms_equal(em->em_relids, relids))
+					!bms_is_subset(em->em_relids, relids))
 					continue;
 
 				sortexpr = em->em_expr;
 				exprvars = pull_var_clause((Node *) sortexpr,
 										   PVC_INCLUDE_AGGREGATES |
 										   PVC_INCLUDE_WINDOWFUNCS |
 										   PVC_INCLUDE_PLACEHOLDERS);
 				foreach(k, exprvars)
 				{
 					if (!tlist_member_ignore_relabel(lfirst(k), tlist))
@@ -5445,57 +5452,58 @@ find_ec_member_for_tle(EquivalenceClass *ec,
 		Expr	   *emexpr;
 
 		/*
 		 * We shouldn't be trying to sort by an equivalence class that
 		 * contains a constant, so no need to consider such cases any further.
 		 */
 		if (em->em_is_const)
 			continue;
 
 		/*
-		 * Ignore child members unless they match the rel being sorted.
+		 * Ignore child members unless they belong to the rel being sorted.
 		 */
 		if (em->em_is_child &&
-			!bms_equal(em->em_relids, relids))
+			!bms_is_subset(em->em_relids, relids))
 			continue;
 
 		/* Match if same expression (after stripping relabel) */
 		emexpr = em->em_expr;
 		while (emexpr && IsA(emexpr, RelabelType))
 			emexpr = ((RelabelType *) emexpr)->arg;
 
 		if (equal(emexpr, tlexpr))
 			return em;
 	}
 
 	return NULL;
 }
 
 /*
  * make_sort_from_pathkeys
  *	  Create sort plan to sort according to given pathkeys
  *
  *	  'lefttree' is the node which yields input tuples
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
+ *	  'relids' is the set of relations required by prepare_sort_from_pathkeys()
  */
 static Sort *
-make_sort_from_pathkeys(Plan *lefttree, List *pathkeys)
+make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
 {
 	int			numsortkeys;
 	AttrNumber *sortColIdx;
 	Oid		   *sortOperators;
 	Oid		   *collations;
 	bool	   *nullsFirst;
 
 	/* Compute sort column info, and adjust lefttree as needed */
 	lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
-										  NULL,
+										  relids,
 										  NULL,
 										  false,
 										  &numsortkeys,
 										  &sortColIdx,
 										  &sortOperators,
 										  &collations,
 										  &nullsFirst);
 
 	/* Now build the Sort node */
 	return make_sort(lefttree, numsortkeys,
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 592214b..7e3e3b8 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1873,20 +1873,79 @@ adjust_appendrel_attrs(PlannerInfo *root, Node *node, AppendRelInfo *appinfo)
 										   appinfo);
 		}
 		result = (Node *) newnode;
 	}
 	else
 		result = adjust_appendrel_attrs_mutator(node, &context);
 
 	return result;
 }
 
+/*
+ * find_appendrelinfos_by_relids
+ * 		Find AppendRelInfo structures for all relations specified by relids.
+ */
+List *
+find_appendrelinfos_by_relids(PlannerInfo *root, Relids relids)
+{
+	ListCell	*lc;
+	List		*ari_list = NIL;
+
+	foreach (lc, root->append_rel_list)
+	{
+		AppendRelInfo *ari = lfirst(lc);
+
+		if (bms_is_member(ari->child_relid, relids))
+			ari_list = lappend(ari_list, ari);
+	}
+
+	Assert(list_length(ari_list) == bms_num_members(relids));
+	return ari_list;
+}
+
+/*
+ * adjust_partitionrel_attrs
+ *	    Replace the Var nodes in given node with the corresponding Var nodes
+ *	    of the child. Given list of AppendRelInfo nodes holds the mapping
+ *	    between parent and child Var nodes.
+ *
+ * TODO:
+ * Note: This function is expected to be called only in case of partitioned
+ * tables, where the child table has the same schema as the parent table. This
+ * allows us to just restamp the Var nodes with child's relid.
+ *
+ * While doing so, we need to make sure to translate non-expression information
+ * in nodes like RestrictInfo.
+ */
+Node *
+adjust_partitionrel_attrs(PlannerInfo *root, Node *node,
+						  List *append_rel_infos)
+{
+	ListCell   *lc;
+
+	/*
+	 * TODO: for partitioned tables, since the partitions have same structure
+	 * as that of their parents, it should suffice to just restamp the Var node
+	 * rather than copying, but both require a new tree being allocated.
+	 * TODO: Instead of copying and mutating the trees one child relation at a
+	 * time, we should be able to do this en-masse for all the partitions
+	 * involved.
+	 */
+	foreach (lc, append_rel_infos)
+	{
+		AppendRelInfo	*append_rel_info = lfirst(lc);
+		node = adjust_appendrel_attrs(root, node, append_rel_info);
+	}
+
+	return node;
+}
+
 static Node *
 adjust_appendrel_attrs_mutator(Node *node,
 							   adjust_appendrel_attrs_context *context)
 {
 	AppendRelInfo *appinfo = context->appinfo;
 
 	if (node == NULL)
 		return NULL;
 	if (IsA(node, Var))
 	{
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index abb7507..4e04133 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -16,21 +16,23 @@
 
 #include <math.h>
 
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
+#include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
+#include "optimizer/tlist.h"
 #include "optimizer/var.h"
 #include "parser/parsetree.h"
 #include "utils/lsyscache.h"
 #include "utils/selfuncs.h"
 
 
 typedef enum
 {
 	COSTS_EQUAL,				/* path costs are fuzzily equal */
 	COSTS_BETTER1,				/* first path is cheaper than second */
@@ -1889,20 +1891,21 @@ calc_nestloop_required_outer(Path *outer_path, Path *inner_path)
 	/* inner_path can require rels from outer path, but not vice versa */
 	Assert(!bms_overlap(outer_paramrels, inner_path->parent->relids));
 	/* easy case if inner path is not parameterized */
 	if (!inner_paramrels)
 		return bms_copy(outer_paramrels);
 	/* else, form the union ... */
 	required_outer = bms_union(outer_paramrels, inner_paramrels);
 	/* ... and remove any mention of now-satisfied outer rels */
 	required_outer = bms_del_members(required_outer,
 									 outer_path->parent->relids);
+
 	/* maintain invariant that required_outer is exactly NULL if empty */
 	if (bms_is_empty(required_outer))
 	{
 		bms_free(required_outer);
 		required_outer = NULL;
 	}
 	return required_outer;
 }
 
 /*
@@ -3202,10 +3205,160 @@ reparameterize_path(PlannerInfo *root, Path *path,
 														 rel,
 														 spath->subpath,
 														 spath->path.pathkeys,
 														 required_outer);
 			}
 		default:
 			break;
 	}
 	return NULL;
 }
+
+/*
+ * reparameterize_path_for_child
+ * 		Given a path parameterized by the parent of the given relation,
+ * 		translate the path to be parameterized by the given child relation.
+ *
+ * The function creates a new path of the same type as the given path, but
+ * parameterized by the given child relation. If it can not reparameterize the
+ * path as required, it returns NULL.
+ */
+Path *
+reparameterize_path_for_child(PlannerInfo *root, Path *path,
+							  RelOptInfo *child_rel)
+{
+	Path	   *new_path;
+	ParamPathInfo   *new_ppi;
+	ParamPathInfo   *old_ppi;
+	List	   *child_aris;
+
+	/*
+	 * If the path is not parameterized by parent of the given relation, it
+	 * doesn't need reparameterization.
+	 */
+	if (!path->param_info ||
+		!bms_overlap(PATH_REQ_OUTER(path), child_rel->parent_relids))
+	return path;
+
+	switch (nodeTag(path))
+	{
+		case T_Path:
+			new_path = makeNode(Path);
+			memcpy(new_path, path, sizeof(Path));
+			break;
+
+		case T_HashPath:
+			new_path = (Path *) makeNode(HashPath);
+			memcpy(new_path, path, sizeof(HashPath));
+			break;
+
+		case T_MergePath:
+			new_path = (Path *) makeNode(MergePath);
+			memcpy(new_path, path, sizeof(MergePath));
+			break;
+
+		case T_NestPath:
+			new_path = (Path *) makeNode(NestPath);
+			memcpy(new_path, path, sizeof(NestPath));
+			break;
+
+		case T_IndexPath:
+			new_path = (Path *) makeNode(IndexPath);
+			memcpy(new_path, path, sizeof(IndexPath));
+			break;
+
+		case T_AppendPath:
+			new_path = (Path *) makeNode(AppendPath);
+			memcpy(new_path, path, sizeof(AppendPath));
+			break;
+
+		/*
+		 * TODO:
+		 * If this method of translation is fine add more path types here.
+		 */
+
+		default:
+			/* Path type unsupported by this function. */
+			return NULL;
+	}
+
+	/*
+	 * Gather AppendRelInfos of the base partition relations in the outer child
+	 * relation. We need those for translating parent path to that of child by
+	 * substituting parent Var nodes and relids with those of children.
+	 */
+	child_aris = find_appendrelinfos_by_relids(root, child_rel->relids);
+
+	/* Adjust the parameterization information. */
+	old_ppi = new_path->param_info;
+	new_ppi = makeNode(ParamPathInfo);
+	new_ppi->ppi_req_outer = adjust_partition_relids(bms_copy(old_ppi->ppi_req_outer),
+													 child_aris);
+	new_ppi->ppi_rows = old_ppi->ppi_rows;
+	new_ppi->ppi_clauses = (List *) adjust_partitionrel_attrs(root,
+													 (Node *) old_ppi->ppi_clauses,
+													 child_aris);
+
+	/* Adjust the path target. */
+	new_path->pathtarget = copy_pathtarget(new_path->pathtarget);
+	new_path->pathtarget->exprs = (List *) adjust_partitionrel_attrs(root,
+													(Node *) new_path->pathtarget->exprs,
+													 child_aris);
+	new_path->param_info = new_ppi;
+
+	/*
+	 * Change parameterization of sub paths recursively. Also carry out any
+	 * pathtype specific adjustments.
+	 */
+	switch (nodeTag(path))
+	{
+		case T_HashPath:
+		case T_MergePath:
+		case T_NestPath:
+			{
+				JoinPath *jpath = (JoinPath *)new_path;
+
+				jpath->outerjoinpath = reparameterize_path_for_child(root,
+														 jpath->outerjoinpath,
+														 child_rel);
+				jpath->innerjoinpath = reparameterize_path_for_child(root,
+														 jpath->innerjoinpath,
+														 child_rel);
+				jpath->joinrestrictinfo = (List *) adjust_partitionrel_attrs(root,
+															(Node *) jpath->joinrestrictinfo,
+															child_aris);
+			}
+			break;
+
+		case T_AppendPath:
+			{
+				AppendPath	*apath = (AppendPath *)new_path;
+				List		*subpaths = NIL;
+				ListCell	*lc;
+
+				foreach (lc, apath->subpaths)
+					subpaths = lappend(subpaths,
+									   reparameterize_path_for_child(root,
+																	lfirst(lc),
+																	child_rel));
+				apath->subpaths = subpaths;
+			}
+
+		case T_IndexPath:
+			{
+				IndexPath *ipath = (IndexPath *)new_path;
+
+				ipath->indexquals = (List *) adjust_partitionrel_attrs(root,
+														(Node *) ipath->indexquals,
+														child_aris);
+				ipath->indexquals = (List *) adjust_partitionrel_attrs(root,
+														(Node *) ipath->indexorderbys,
+														child_aris);
+			}
+
+		default:
+			/* Nothing to do. */
+			break;
+	}
+
+	return new_path;
+}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index deef560..cea06c7 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -8,29 +8,34 @@
  *
  *
  * IDENTIFICATION
  *	  src/backend/optimizer/util/relnode.c
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include "miscadmin.h"
+#include "catalog/heap.h"
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
+#include "nodes/makefuncs.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/placeholder.h"
 #include "optimizer/plancat.h"
+#include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
+#include "rewrite/rewriteManip.h"
 #include "utils/hsearch.h"
+#include "utils/rel.h"
 
 
 typedef struct JoinHashEntry
 {
 	Relids		join_relids;	/* hash key --- MUST BE FIRST */
 	RelOptInfo *join_rel;
 } JoinHashEntry;
 
 static void build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
 					RelOptInfo *input_rel);
@@ -40,20 +45,24 @@ static List *build_joinrel_restrictlist(PlannerInfo *root,
 						   RelOptInfo *inner_rel);
 static void build_joinrel_joinlist(RelOptInfo *joinrel,
 					   RelOptInfo *outer_rel,
 					   RelOptInfo *inner_rel);
 static List *subbuild_joinrel_restrictlist(RelOptInfo *joinrel,
 							  List *joininfo_list,
 							  List *new_restrictlist);
 static List *subbuild_joinrel_joinlist(RelOptInfo *joinrel,
 						  List *joininfo_list,
 						  List *new_joininfo);
+static PartitionOptInfo *build_partition_info(PlannerInfo *root,
+							   RelOptInfo *rel);
+static void set_foreign_rel_properties(RelOptInfo *joinrel,
+							RelOptInfo *outer_rel, RelOptInfo *inner_rel);
 
 
 /*
  * setup_simple_rel_arrays
  *	  Prepare the arrays we use for quickly accessing base relations.
  */
 void
 setup_simple_rel_arrays(PlannerInfo *root)
 {
 	Index		rti;
@@ -165,27 +174,33 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
 			break;
 		default:
 			elog(ERROR, "unrecognized RTE kind: %d",
 				 (int) rte->rtekind);
 			break;
 	}
 
 	/* Save the finished struct in the query's simple_rel_array */
 	root->simple_rel_array[relid] = rel;
 
+	/* Get the partitioning information, if any. */
+	if (rte->rtekind == RTE_RELATION)
+		rel->part_info = build_partition_info(root, rel);
+	else
+		rel->part_info = NULL;
+
 	/*
 	 * If this rel is an appendrel parent, recurse to build "other rel"
 	 * RelOptInfos for its children.  They are "other rels" because they are
 	 * not in the main join tree, but we will need RelOptInfos to plan access
 	 * to them.
 	 */
-	if (rte->inh)
+	if (!rel->part_info && rte->inh)
 	{
 		ListCell   *l;
 
 		foreach(l, root->append_rel_list)
 		{
 			AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
 
 			/* append_rel_list contains all append rels; ignore others */
 			if (appinfo->parent_relid != relid)
 				continue;
@@ -307,20 +322,70 @@ find_join_rel(PlannerInfo *root, Relids relids)
 
 			if (bms_equal(rel->relids, relids))
 				return rel;
 		}
 	}
 
 	return NULL;
 }
 
 /*
+ * set_foreign_rel_properties
+ *		Set up foreign-join fields if outer and inner relation are foreign
+ * 		tables (or joins) belonging to the same server and assigned to the same
+ *		user to check access permissions as.
+ *
+ * In addition to an exact match of userid, we allow the case where one side
+ * has zero userid (implying current user) and the other side has explicit
+ * userid that happens to equal the current user; but in that case, pushdown of
+ * the join is only valid for the current user.  The useridiscurrent field
+ * records whether we had to make such an assumption for this join or any
+ * sub-join.
+ *
+ * Otherwise these fields are left invalid, so GetForeignJoinPaths will not be
+ * called for the join relation.
+ *
+ */
+static void
+set_foreign_rel_properties(RelOptInfo *joinrel, RelOptInfo *outer_rel,
+						   RelOptInfo *inner_rel)
+{
+	if (OidIsValid(outer_rel->serverid) &&
+		inner_rel->serverid == outer_rel->serverid)
+	{
+		if (inner_rel->userid == outer_rel->userid)
+		{
+			joinrel->serverid = outer_rel->serverid;
+			joinrel->userid = outer_rel->userid;
+			joinrel->useridiscurrent = outer_rel->useridiscurrent || inner_rel->useridiscurrent;
+			joinrel->fdwroutine = outer_rel->fdwroutine;
+		}
+		else if (!OidIsValid(inner_rel->userid) &&
+				 outer_rel->userid == GetUserId())
+		{
+			joinrel->serverid = outer_rel->serverid;
+			joinrel->userid = outer_rel->userid;
+			joinrel->useridiscurrent = true;
+			joinrel->fdwroutine = outer_rel->fdwroutine;
+		}
+		else if (!OidIsValid(outer_rel->userid) &&
+				 inner_rel->userid == GetUserId())
+		{
+			joinrel->serverid = outer_rel->serverid;
+			joinrel->userid = inner_rel->userid;
+			joinrel->useridiscurrent = true;
+			joinrel->fdwroutine = outer_rel->fdwroutine;
+		}
+	}
+}
+
+/*
  * build_join_rel
  *	  Returns relation entry corresponding to the union of two given rels,
  *	  creating a new relation entry if none already exists.
  *
  * 'joinrelids' is the Relids set that uniquely identifies the join
  * 'outer_rel' and 'inner_rel' are relation nodes for the relations to be
  *		joined
  * 'sjinfo': join context info
  * 'restrictlist_ptr': result variable.  If not NULL, *restrictlist_ptr
  *		receives the list of RestrictInfo nodes that apply to this
@@ -356,21 +421,25 @@ build_join_rel(PlannerInfo *root,
 														   joinrel,
 														   outer_rel,
 														   inner_rel);
 		return joinrel;
 	}
 
 	/*
 	 * Nope, so make one.
 	 */
 	joinrel = makeNode(RelOptInfo);
+
+	Assert(!IS_OTHER_REL(outer_rel->reloptkind) &&
+		   !IS_OTHER_REL(inner_rel->reloptkind));
 	joinrel->reloptkind = RELOPT_JOINREL;
+
 	joinrel->relids = bms_copy(joinrelids);
 	joinrel->rows = 0;
 	/* cheap startup cost is interesting iff not all tuples to be retrieved */
 	joinrel->consider_startup = (root->tuple_fraction > 0);
 	joinrel->consider_param_startup = false;
 	joinrel->consider_parallel = false;
 	joinrel->reltarget = create_empty_pathtarget();
 	joinrel->pathlist = NIL;
 	joinrel->ppilist = NIL;
 	joinrel->partial_pathlist = NIL;
@@ -402,61 +471,25 @@ build_join_rel(PlannerInfo *root,
 	joinrel->serverid = InvalidOid;
 	joinrel->userid = InvalidOid;
 	joinrel->useridiscurrent = false;
 	joinrel->fdwroutine = NULL;
 	joinrel->fdw_private = NULL;
 	joinrel->baserestrictinfo = NIL;
 	joinrel->baserestrictcost.startup = 0;
 	joinrel->baserestrictcost.per_tuple = 0;
 	joinrel->joininfo = NIL;
 	joinrel->has_eclass_joins = false;
+	joinrel->part_info = NULL;
+	joinrel->parent_relids = NULL;
 
-	/*
-	 * Set up foreign-join fields if outer and inner relation are foreign
-	 * tables (or joins) belonging to the same server and assigned to the same
-	 * user to check access permissions as.  In addition to an exact match of
-	 * userid, we allow the case where one side has zero userid (implying
-	 * current user) and the other side has explicit userid that happens to
-	 * equal the current user; but in that case, pushdown of the join is only
-	 * valid for the current user.  The useridiscurrent field records whether
-	 * we had to make such an assumption for this join or any sub-join.
-	 *
-	 * Otherwise these fields are left invalid, so GetForeignJoinPaths will
-	 * not be called for the join relation.
-	 */
-	if (OidIsValid(outer_rel->serverid) &&
-		inner_rel->serverid == outer_rel->serverid)
-	{
-		if (inner_rel->userid == outer_rel->userid)
-		{
-			joinrel->serverid = outer_rel->serverid;
-			joinrel->userid = outer_rel->userid;
-			joinrel->useridiscurrent = outer_rel->useridiscurrent || inner_rel->useridiscurrent;
-			joinrel->fdwroutine = outer_rel->fdwroutine;
-		}
-		else if (!OidIsValid(inner_rel->userid) &&
-				 outer_rel->userid == GetUserId())
-		{
-			joinrel->serverid = outer_rel->serverid;
-			joinrel->userid = outer_rel->userid;
-			joinrel->useridiscurrent = true;
-			joinrel->fdwroutine = outer_rel->fdwroutine;
-		}
-		else if (!OidIsValid(outer_rel->userid) &&
-				 inner_rel->userid == GetUserId())
-		{
-			joinrel->serverid = outer_rel->serverid;
-			joinrel->userid = inner_rel->userid;
-			joinrel->useridiscurrent = true;
-			joinrel->fdwroutine = outer_rel->fdwroutine;
-		}
-	}
+	/* Computer information relevant to the foreign relations. */
+	set_foreign_rel_properties(joinrel, outer_rel, inner_rel);
 
 	/*
 	 * Create a new tlist containing just the vars that need to be output from
 	 * this join (ie, are needed for higher joinclauses or final output).
 	 *
 	 * NOTE: the tlist order for a join rel will depend on which pair of outer
 	 * and inner rels we first try to build it from.  But the contents should
 	 * be the same regardless.
 	 */
 	build_joinrel_tlist(root, joinrel, outer_rel);
@@ -510,57 +543,146 @@ build_join_rel(PlannerInfo *root,
 	 * assume this doesn't matter, because we should hit all the same baserels
 	 * and joinclauses while building up to this joinrel no matter which we
 	 * take; therefore, we should make the same decision here however we get
 	 * here.
 	 */
 	if (inner_rel->consider_parallel && outer_rel->consider_parallel &&
 		is_parallel_safe(root, (Node *) restrictlist) &&
 		is_parallel_safe(root, (Node *) joinrel->reltarget->exprs))
 		joinrel->consider_parallel = true;
 
-	/*
-	 * Add the joinrel to the query's joinrel list, and store it into the
-	 * auxiliary hashtable if there is one.  NB: GEQO requires us to append
-	 * the new joinrel to the end of the list!
-	 */
-	root->join_rel_list = lappend(root->join_rel_list, joinrel);
-
-	if (root->join_rel_hash)
-	{
-		JoinHashEntry *hentry;
-		bool		found;
-
-		hentry = (JoinHashEntry *) hash_search(root->join_rel_hash,
-											   &(joinrel->relids),
-											   HASH_ENTER,
-											   &found);
-		Assert(!found);
-		hentry->join_rel = joinrel;
-	}
+	/* Add the joinrel to the query's PlannerInfo. */
+	add_joinrel_to_list(root, joinrel);
 
 	/*
 	 * Also, if dynamic-programming join search is active, add the new joinrel
 	 * to the appropriate sublist.  Note: you might think the Assert on number
 	 * of members should be for equality, but some of the level 1 rels might
 	 * have been joinrels already, so we can only assert <=.
 	 */
 	if (root->join_rel_level)
 	{
 		Assert(root->join_cur_level > 0);
 		Assert(root->join_cur_level <= bms_num_members(joinrel->relids));
 		root->join_rel_level[root->join_cur_level] =
 			lappend(root->join_rel_level[root->join_cur_level], joinrel);
 	}
 
 	return joinrel;
 }
 
+RelOptInfo *
+make_joinrel(PlannerInfo *root, RelOptKind reloptkind, Relids joinrelids)
+{
+	RelOptInfo *joinrel = makeNode(RelOptInfo);
+	joinrel->reloptkind = RELOPT_OTHER_JOINREL;
+	joinrel->relids = bms_copy(joinrelids);
+	joinrel->rows = 0;
+	/* cheap startup cost is interesting iff not all tuples to be retrieved */
+	joinrel->consider_startup = (root->tuple_fraction > 0);
+	joinrel->consider_param_startup = false;
+	joinrel->consider_parallel = false;
+	joinrel->reltarget = create_empty_pathtarget();
+	joinrel->pathlist = NIL;
+	joinrel->ppilist = NIL;
+	joinrel->partial_pathlist = NIL;
+	joinrel->cheapest_startup_path = NULL;
+	joinrel->cheapest_total_path = NULL;
+	joinrel->cheapest_unique_path = NULL;
+	joinrel->cheapest_parameterized_paths = NIL;
+	joinrel->direct_lateral_relids = NULL;
+	joinrel->lateral_relids = NULL;
+	joinrel->relid = 0;			/* indicates not a baserel */
+	joinrel->rtekind = RTE_JOIN;
+	joinrel->min_attr = 0;
+	joinrel->max_attr = 0;
+	joinrel->attr_needed = NULL;
+	joinrel->attr_widths = NULL;
+	joinrel->lateral_vars = NIL;
+	joinrel->lateral_referencers = NULL;
+	joinrel->indexlist = NIL;
+	joinrel->pages = 0;
+	joinrel->tuples = 0;
+	joinrel->allvisfrac = 0;
+	joinrel->subroot = NULL;
+	joinrel->subplan_params = NIL;
+	joinrel->serverid = InvalidOid;
+	joinrel->userid = InvalidOid;
+	joinrel->useridiscurrent = false;
+	joinrel->fdwroutine = NULL;
+	joinrel->fdw_private = NULL;
+	joinrel->baserestrictinfo = NIL;
+	joinrel->baserestrictcost.startup = 0;
+	joinrel->baserestrictcost.per_tuple = 0;
+	joinrel->joininfo = NIL;
+	joinrel->has_eclass_joins = false;
+	joinrel->part_info = NULL;
+	joinrel->parent_relids = NULL;
+
+	return joinrel;
+}
+
+RelOptInfo *
+fill_partition_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
+						RelOptInfo *joinrel, RelOptInfo *parent_joinrel,
+						SpecialJoinInfo *sjinfo, List *restrictlist,
+						List *join_aris)
+{
+	List	   *tmp_exprs;
+
+	/* Only joins between other relations land here. */
+	Assert(IS_OTHER_REL(rel1->reloptkind) && IS_OTHER_REL(rel2->reloptkind));
+
+	joinrel->parent_relids = bms_copy(parent_joinrel->relids);
+
+	/* Computer information relevant to foreign relations. */
+	set_foreign_rel_properties(joinrel, rel1, rel2);
+
+	/*
+	 * Produce partition-wise joinrel's targetlist by translating the parent
+	 * joinrel's targetlist. This will also include the required placeholder
+	 * Vars.
+	 */
+	joinrel->reltarget = copy_pathtarget(parent_joinrel->reltarget);
+	tmp_exprs = joinrel->reltarget->exprs;
+	joinrel->reltarget->exprs = (List *) adjust_partitionrel_attrs(root, (Node *) tmp_exprs,
+															join_aris);
+
+	/*
+	 * Lateral relids directly referred in this relation will be same as that
+	 * of the parent relation.
+	 */
+	joinrel->direct_lateral_relids = (Relids) bms_copy(parent_joinrel->direct_lateral_relids);
+	joinrel->lateral_relids = (Relids) bms_copy(parent_joinrel->lateral_relids);
+
+	joinrel->joininfo = (List *) adjust_partitionrel_attrs(root,
+											 (Node *) parent_joinrel->joininfo,
+													join_aris);
+
+	/*
+	 * If the parent joinrel has pending equivalence classes, so does the
+	 * child.
+	 */
+	joinrel->has_eclass_joins = parent_joinrel->has_eclass_joins;
+
+	/*
+	 * Set estimates of the joinrel's size.
+	 */
+	set_joinrel_size_estimates(root, joinrel, rel1, rel2, sjinfo,
+							   restrictlist);
+
+	/* Child joinrel is parallel safe if parent is parallel safe. */
+	joinrel->consider_parallel = parent_joinrel->consider_parallel;
+
+	return joinrel;
+}
+
 /*
  * min_join_parameterization
  *
  * Determine the minimum possible parameterization of a joinrel, that is, the
  * set of other rels it contains LATERAL references to.  We save this value in
  * the join's RelOptInfo.  This function is split out of build_join_rel()
  * because join_is_legal() needs the value to check a prospective join.
  */
 Relids
 min_join_parameterization(PlannerInfo *root,
@@ -1313,10 +1435,196 @@ get_appendrel_parampathinfo(RelOptInfo *appendrel, Relids required_outer)
 
 	/* Else build the ParamPathInfo */
 	ppi = makeNode(ParamPathInfo);
 	ppi->ppi_req_outer = required_outer;
 	ppi->ppi_rows = 0;
 	ppi->ppi_clauses = NIL;
 	appendrel->ppilist = lappend(appendrel->ppilist, ppi);
 
 	return ppi;
 }
+
+/*
+ * build_partition_info
+ *
+ * Retrieves partitioning information for given relation.
+ *
+ * The function also builds the RelOptInfos of the partitions recursively.
+ * TODO: complete the prologue.
+ */
+static PartitionOptInfo *
+build_partition_info(PlannerInfo *root, RelOptInfo *rel)
+{
+	Relation	relation;
+	PartitionKey	part_key;
+	PartitionDesc	part_desc;
+	PartitionOptInfo *part_info;
+	RangeTblEntry  *rte;
+	ListCell	*lc;
+	int		num_pkexprs;
+	int		cnt_pke;
+	int		nparts;
+	int		cnt_parts;
+	Expr   *pkexpr;
+
+	/* The given relation should be simple relation. */
+	Assert(rel->reloptkind == RELOPT_BASEREL ||
+		   rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
+	Assert(rel->relid != 0);
+
+	rte = root->simple_rte_array[rel->relid];
+	Assert(rte);
+
+	/*
+	 * If it's not parent of the inheritance hierarchy, it can not be
+	 * partiioned relation.
+	 */
+	if (!rte->inh)
+		return NULL;
+
+	/*
+	 * We need not lock the relation since it was already locked, either
+	 * by the rewriter or when expand_inherited_rtentry() added it to
+	 * the query's rangetable.
+	 */
+	relation = heap_open(rte->relid, NoLock);
+	part_desc = RelationGetPartitionDesc(relation);
+	part_key = RelationGetPartitionKey(relation);
+
+	/* Nothing to do for an unpartitioned relation. */
+	if (!part_desc || !part_key)
+	{
+		heap_close(relation, NoLock);
+		return NULL;
+	}
+
+	part_info = makeNode(PartitionOptInfo);
+
+	/* Store partition descriptor information. */
+	nparts = part_info->nparts = part_desc->nparts;
+	/* TODO: Should we copy the contents of the these arrays? */
+	part_info->lists = (PartitionListInfo **) palloc(sizeof(PartitionListInfo *) * nparts);
+	part_info->ranges = (PartitionRangeInfo **) palloc(sizeof(PartitionRangeInfo *) * nparts);
+
+	for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++)
+	{
+		part_info->lists[cnt_parts] = part_desc->parts[cnt_parts]->list;
+		part_info->ranges[cnt_parts] = part_desc->parts[cnt_parts]->range;
+	}
+
+	/* Store partition key information. */
+	part_info->strategy = part_key->strategy;
+	part_info->partnatts = part_key->partnatts;
+	num_pkexprs = part_info->partnatts;
+	/* TODO: Should we copy the contents of these arrays? */
+	part_info->partopfamily = part_key->partopfamily;
+	part_info->partopcintype = part_key->partopcintype;
+	part_info->partsupfunc = part_key->partsupfunc;
+	part_info->tcinfo = part_key->tcinfo;
+
+	/* Store partition keys as single elements lists. */
+	part_info->partexprs = (List **) palloc(sizeof(List *) * num_pkexprs);
+	lc = list_head(part_key->partexprs);
+	for (cnt_pke = 0; cnt_pke < num_pkexprs; cnt_pke++)
+	{
+		AttrNumber attno = part_key->partattrs[cnt_pke];
+
+		if (attno != InvalidAttrNumber)
+		{
+			/* Single column partition key is stored as a Var node. */
+			Form_pg_attribute att_tup;
+
+			if (attno < 0)
+				att_tup = SystemAttributeDefinition(attno,
+												relation->rd_rel->relhasoids);
+			else
+				att_tup = relation->rd_att->attrs[attno - 1];
+
+			pkexpr = (Expr *) makeVar(rel->relid, attno, att_tup->atttypid,
+									  att_tup->atttypmod,
+									  att_tup->attcollation, 0);
+		}
+		else
+		{
+			if (!lc)
+				elog(ERROR, "wrong number of partition key expressions");
+			pkexpr = (Expr *) copyObject(lfirst(lc));
+			ChangeVarNodes((Node *) pkexpr, 1, rel->relid, 0);
+			lc = lnext(lc);
+		}
+
+		part_info->partexprs[cnt_pke] = list_make1(pkexpr);
+	}
+
+	/* Find RelOptInfo of the partitions. */
+	part_info->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) *
+												   nparts);
+	foreach(lc, root->append_rel_list)
+	{
+		AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
+		int			childRTindex = appinfo->child_relid;
+		RangeTblEntry *childRTE = root->simple_rte_array[childRTindex];
+		RelOptInfo *childrel = root->simple_rel_array[childRTindex];
+
+		/* append_rel_list contains all append rels; ignore others */
+		if (appinfo->parent_relid != rel->relid)
+			continue;
+
+		/* If we haven't created a RelOptInfo aleady, create one. */
+		if (!childrel)
+			childrel = build_simple_rel(root, childRTindex,
+									RELOPT_OTHER_MEMBER_REL);
+
+		/* Save the parent relids for parameterized path handling. */
+		childrel->parent_relids = bms_copy(rel->relids);
+
+		/*
+		 * OIDs of the partitions are arranged to match the partition bounds or
+		 * list in corresponding arrays. Arrange RelOptInfo's of partitions in
+		 * the same fashion.
+		 */
+		for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++)
+		{
+			if (part_desc->parts[cnt_parts]->oid == childRTE->relid)
+			{
+				/* Every partition can be seen only once. */
+				Assert(!part_info->part_rels[cnt_parts]);
+				part_info->part_rels[cnt_parts] = childrel;
+
+				break;
+			}
+		}
+	}
+
+	heap_close(relation, NoLock);
+
+	/* We must have found RelOptInfos of all the partitions. */
+	for (cnt_parts = 0; cnt_parts < part_info->nparts; cnt_parts++)
+		Assert(part_info->part_rels[cnt_parts]);
+
+	return part_info;
+}
+
+/*
+ * Adds given join relation to the joinrel list and also to the hashtable if
+ * there is one.
+ */
+void
+add_joinrel_to_list(PlannerInfo *root, RelOptInfo *joinrel)
+{
+	/* GEQO requires us to append the new joinrel to the end of the list! */
+	root->join_rel_list = lappend(root->join_rel_list, joinrel);
+
+	/* store it into the auxiliary hashtable if there is one. */
+	if (root->join_rel_hash)
+	{
+		JoinHashEntry *hentry;
+		bool		found;
+
+		hentry = (JoinHashEntry *) hash_search(root->join_rel_hash,
+											   &(joinrel->relids),
+											   HASH_ENTER,
+											   &found);
+		Assert(!found);
+		hentry->join_rel = joinrel;
+	}
+}
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 56943f2..16b2eac 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -3405,21 +3405,23 @@ estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows,
 			else
 			{
 				/* not time to process varinfo2 yet */
 				newvarinfos = lcons(varinfo2, newvarinfos);
 			}
 		}
 
 		/*
 		 * Sanity check --- don't divide by zero if empty relation.
 		 */
-		Assert(rel->reloptkind == RELOPT_BASEREL);
+		Assert(rel->reloptkind == RELOPT_BASEREL ||
+			   rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
+
 		if (rel->tuples > 0)
 		{
 			/*
 			 * Clamp to size of rel, or size of rel / 10 if multiple Vars. The
 			 * fudge factor is because the Vars are probably correlated but we
 			 * don't know by how much.  We should never clamp to less than the
 			 * largest ndistinct value for any of the Vars, though, since
 			 * there will surely be at least that many groups.
 			 */
 			double		clamp = rel->tuples;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index c5178f7..3412eae 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -870,20 +870,29 @@ static struct config_bool ConfigureNamesBool[] =
 	},
 	{
 		{"enable_hashjoin", PGC_USERSET, QUERY_TUNING_METHOD,
 			gettext_noop("Enables the planner's use of hash join plans."),
 			NULL
 		},
 		&enable_hashjoin,
 		true,
 		NULL, NULL, NULL
 	},
+	{
+		{"enable_partition_wise_join", PGC_USERSET, QUERY_TUNING_METHOD,
+			gettext_noop("Enables partition-wise join."),
+			NULL
+		},
+		&enable_partition_wise_join,
+		true,
+		NULL, NULL, NULL
+	},
 
 	{
 		{"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
 			gettext_noop("Enables genetic query optimization."),
 			gettext_noop("This algorithm attempts to do planning without "
 						 "exhaustive searching.")
 		},
 		&enable_geqo,
 		true,
 		NULL, NULL, NULL
diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h
index 14fd29e..8b928a3 100644
--- a/src/include/catalog/partition.h
+++ b/src/include/catalog/partition.h
@@ -12,22 +12,88 @@
  */
 #ifndef PARTITION_H
 #define PARTITION_H
 
 #include "fmgr.h"
 #include "executor/tuptable.h"
 #include "nodes/execnodes.h"
 #include "parser/parse_node.h"
 #include "utils/relcache.h"
 
+/* Type and collation information for partition key columns */
+typedef struct KeyTypeCollInfo
+{
+	Oid		*typid;
+	int32	*typmod;
+	int16	*typlen;
+	bool	*typbyval;
+	char	*typalign;
+	Oid		*typcoll;
+} KeyTypeCollInfo;
+
+/*
+ * Partition key information
+ */
+typedef struct PartitionKeyData
+{
+	char		strategy;		/* partition strategy */
+	int16		partnatts;		/* number of partition attributes */
+	AttrNumber *partattrs;		/* partition attnums */
+	Oid		   *partopfamily;	/* OIDs of operator families */
+	Oid		   *partopcintype;	/* OIDs of opclass declared input data types */
+	FmgrInfo   *partsupfunc;	/* lookup info for support funcs */
+	List	   *partexprs;		/* partition key expressions, if any */
+	char	  **partcolnames;	/* partition key column names */
+	KeyTypeCollInfo *tcinfo;	/* type and collation info (all columns) */
+} PartitionKeyData;
+
 typedef struct PartitionKeyData *PartitionKey;
 
+/* Internal representation of a list partition bound */
+typedef struct PartitionListInfo
+{
+	int		nvalues;	/* number of values in the following array */
+	Datum  *values;		/* values contained in the list */
+	bool   *nulls;
+} PartitionListInfo;
+
+/*
+ * TODO: the patch by Amit L named this structure as RangeBound, but that
+ * conflicts with a structure with the same name in rangetypes.h. Hence renamed
+ * it here. It should be changed to whatever Amit L uses in the next set of
+ * patches.
+ */
+/* Internal representation of a range partition bound */
+typedef struct PartitionRangeBound
+{
+	Datum		*val;			/* the bound value, if any */
+	bool		infinite;		/* bound is +/- infinity */
+	bool		inclusive;		/* bound is inclusive (vs exclusive) */
+	bool		lower;			/* this is the lower (vs upper) bound */
+} PartitionRangeBound;
+
+typedef struct PartitionRangeInfo
+{
+	PartitionRangeBound	*lower;
+	PartitionRangeBound	*upper;
+} PartitionRangeInfo;
+
+/*
+ * Information about a single partition
+ */
+typedef struct PartitionInfoData
+{
+	Oid						oid;		/* partition OID */
+	PartitionListInfo	   *list;		/* list partition info */
+	PartitionRangeInfo	   *range;		/* range partition info */
+} PartitionInfoData;
+
 /*
  * Information about partitions of a partitioned table.
  */
 typedef struct PartitionInfoData *PartitionInfo;
 typedef struct PartitionDescData
 {
 	int		nparts;			/* Number of partitions */
 	PartitionInfo *parts;	/* Array of PartitionInfoData pointers */
 } PartitionDescData;
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index bb62112..4bb5966 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -218,20 +218,21 @@ typedef enum NodeTag
 	T_DomainConstraintState,
 
 	/*
 	 * TAGS FOR PLANNER NODES (relation.h)
 	 */
 	T_PlannerInfo = 500,
 	T_PlannerGlobal,
 	T_RelOptInfo,
 	T_IndexOptInfo,
 	T_ForeignKeyOptInfo,
+	T_PartitionOptInfo,
 	T_ParamPathInfo,
 	T_Path,
 	T_IndexPath,
 	T_BitmapHeapPath,
 	T_BitmapAndPath,
 	T_BitmapOrPath,
 	T_TidPath,
 	T_SubqueryScanPath,
 	T_ForeignPath,
 	T_CustomPath,
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 2709cc7..fae6c1a 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -12,20 +12,21 @@
  *-------------------------------------------------------------------------
  */
 #ifndef RELATION_H
 #define RELATION_H
 
 #include "access/sdir.h"
 #include "lib/stringinfo.h"
 #include "nodes/params.h"
 #include "nodes/parsenodes.h"
 #include "storage/block.h"
+#include "catalog/partition.h"
 
 
 /*
  * Relids
  *		Set of relation identifiers (indexes into the rangetable).
  */
 typedef Bitmapset *Relids;
 
 /*
  * When looking for a "cheapest path", this enum specifies whether we want
@@ -345,20 +346,26 @@ typedef struct PlannerInfo
  * is present in the query join tree but the members are not.  The member
  * RTEs and otherrels are used to plan the scans of the individual tables or
  * subqueries of the append set; then the parent baserel is given Append
  * and/or MergeAppend paths comprising the best paths for the individual
  * member rels.  (See comments for AppendRelInfo for more information.)
  *
  * At one time we also made otherrels to represent join RTEs, for use in
  * handling join alias Vars.  Currently this is not needed because all join
  * alias Vars are expanded to non-aliased form during preprocess_expression.
  *
+ * We also have relations representing pair-wise joins between partitions of
+ * partitioned tables. These relations are not added to join_rel_level lists
+ * as they are not joined directly by the dynamic programming algorithm.
+ * Adding these two join_rel_level list also means that top level list has more
+ * than one join relation, which is symantically incorrect.
+ *
  * There is also a RelOptKind for "upper" relations, which are RelOptInfos
  * that describe post-scan/join processing steps, such as aggregation.
  * Many of the fields in these RelOptInfos are meaningless, but their Path
  * fields always hold Paths showing ways to do that processing step.
  *
  * Lastly, there is a RelOptKind for "dead" relations, which are base rels
  * that we have proven we don't need to join after all.
  *
  * Parts of this data structure are specific to various scan and join
  * mechanisms.  It didn't seem worth creating new node types for them.
@@ -464,24 +471,31 @@ typedef struct PlannerInfo
  * We store baserestrictcost in the RelOptInfo (for base relations) because
  * we know we will need it at least once (to price the sequential scan)
  * and may need it multiple times to price index scans.
  *----------
  */
 typedef enum RelOptKind
 {
 	RELOPT_BASEREL,
 	RELOPT_JOINREL,
 	RELOPT_OTHER_MEMBER_REL,
+	RELOPT_OTHER_JOINREL,
 	RELOPT_UPPER_REL,
 	RELOPT_DEADREL
 } RelOptKind;
 
+#define IS_OTHER_REL(reloptkind) \
+	((reloptkind) == RELOPT_OTHER_MEMBER_REL || \
+	 (reloptkind) == RELOPT_OTHER_JOINREL)
+
+typedef struct PartitionOptInfo PartitionOptInfo;
+
 typedef struct RelOptInfo
 {
 	NodeTag		type;
 
 	RelOptKind	reloptkind;
 
 	/* all relations included in this RelOptInfo */
 	Relids		relids;			/* set of base relids (rangetable indexes) */
 
 	/* size estimates generated by planner */
@@ -535,20 +549,29 @@ typedef struct RelOptInfo
 	struct FdwRoutine *fdwroutine;
 	void	   *fdw_private;
 
 	/* used by various scans and joins: */
 	List	   *baserestrictinfo;		/* RestrictInfo structures (if base
 										 * rel) */
 	QualCost	baserestrictcost;		/* cost of evaluating the above */
 	List	   *joininfo;		/* RestrictInfo structures for join clauses
 								 * involving this rel */
 	bool		has_eclass_joins;		/* T means joininfo is incomplete */
+
+	/* For partitioned relations, joins or base relations. NULL otherwise. */
+	/*
+	 * TODO: Notice recursive usage of RelOptInfo.
+	 */
+	PartitionOptInfo	*part_info;
+
+	/* Set only for "other" base or join relations. */
+	Relids		parent_relids;
 } RelOptInfo;
 
 /*
  * IndexOptInfo
  *		Per-index information for planning/optimization
  *
  *		indexkeys[], indexcollations[], opfamily[], and opcintype[]
  *		each have ncolumns entries.
  *
  *		sortopfamily[], reverse_sort[], and nulls_first[] likewise have
@@ -649,20 +672,45 @@ typedef struct ForeignKeyOptInfo
 	/* Derived info about whether FK's equality conditions match the query: */
 	int			nmatched_ec;	/* # of FK cols matched by ECs */
 	int			nmatched_rcols; /* # of FK cols matched by non-EC rinfos */
 	int			nmatched_ri;	/* total # of non-EC rinfos matched to FK */
 	/* Pointer to eclass matching each column's condition, if there is one */
 	struct EquivalenceClass *eclass[INDEX_MAX_KEYS];
 	/* List of non-EC RestrictInfos matching each column's condition */
 	List	   *rinfos[INDEX_MAX_KEYS];
 } ForeignKeyOptInfo;
 
+/*
+ * PartitionOptInfo
+ * 		Partitioning information for planning/optimization
+ *
+ * TODO: complete the comment
+ */
+typedef struct PartitionOptInfo
+{
+	NodeTag		type;
+
+	/* Information about partitions */
+	int			nparts;		/* number of partitions */
+	RelOptInfo			  **part_rels;	/* RelOptInfos of partitions */
+	PartitionListInfo	  **lists;		/* list bounds */
+	PartitionRangeInfo	  **ranges;	/* range lower bounds */
+
+	/* Information about partition keys */
+	char		strategy;		/* partition strategy */
+	int16		partnatts;		/* number of partition attributes */
+	List	  **partexprs;		/* partition key expressions. */
+	Oid		   *partopfamily;	/* OIDs of operator families */
+	Oid		   *partopcintype;	/* OIDs of opclass declared input data types */
+	FmgrInfo   *partsupfunc;	/* lookup info for support funcs */
+	KeyTypeCollInfo *tcinfo;	/* type and collation info (all columns) */
+} PartitionOptInfo;
 
 /*
  * EquivalenceClasses
  *
  * Whenever we can determine that a mergejoinable equality clause A = B is
  * not delayed by any outer join, we create an EquivalenceClass containing
  * the expressions A and B to record this knowledge.  If we later find another
  * equivalence B = C, we add C to the existing EquivalenceClass; this may
  * require merging two existing EquivalenceClasses.  At the end of the qual
  * distribution process, we have sets of values that are known all transitively
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index 2a4df2f..1069726 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -59,20 +59,21 @@ extern bool enable_seqscan;
 extern bool enable_indexscan;
 extern bool enable_indexonlyscan;
 extern bool enable_bitmapscan;
 extern bool enable_tidscan;
 extern bool enable_sort;
 extern bool enable_hashagg;
 extern bool enable_nestloop;
 extern bool enable_material;
 extern bool enable_mergejoin;
 extern bool enable_hashjoin;
+extern bool enable_partition_wise_join;
 extern int	constraint_exclusion;
 
 extern double clamp_row_est(double nrows);
 extern double index_pages_fetched(double tuples_fetched, BlockNumber pages,
 					double index_pages, PlannerInfo *root);
 extern void cost_seqscan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
 			 ParamPathInfo *param_info);
 extern void cost_samplescan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
 				ParamPathInfo *param_info);
 extern void cost_index(IndexPath *path, PlannerInfo *root,
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 71d9154..5a4b054 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -222,20 +222,22 @@ extern ModifyTablePath *create_modifytable_path(PlannerInfo *root,
 						List *rowMarks, OnConflictExpr *onconflict,
 						int epqParam);
 extern LimitPath *create_limit_path(PlannerInfo *root, RelOptInfo *rel,
 				  Path *subpath,
 				  Node *limitOffset, Node *limitCount,
 				  int64 offset_est, int64 count_est);
 
 extern Path *reparameterize_path(PlannerInfo *root, Path *path,
 					Relids required_outer,
 					double loop_count);
+extern Path *reparameterize_path_for_child(PlannerInfo *root, Path *path,
+					RelOptInfo *child_rel);
 
 /*
  * prototypes for relnode.c
  */
 extern void setup_simple_rel_arrays(PlannerInfo *root);
 extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid,
 				 RelOptKind reloptkind);
 extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid);
 extern RelOptInfo *find_join_rel(PlannerInfo *root, Relids relids);
 extern RelOptInfo *build_join_rel(PlannerInfo *root,
@@ -260,12 +262,20 @@ extern ParamPathInfo *get_baserel_parampathinfo(PlannerInfo *root,
 						  Relids required_outer);
 extern ParamPathInfo *get_joinrel_parampathinfo(PlannerInfo *root,
 						  RelOptInfo *joinrel,
 						  Path *outer_path,
 						  Path *inner_path,
 						  SpecialJoinInfo *sjinfo,
 						  Relids required_outer,
 						  List **restrict_clauses);
 extern ParamPathInfo *get_appendrel_parampathinfo(RelOptInfo *appendrel,
 							Relids required_outer);
+extern RelOptInfo *make_joinrel(PlannerInfo *root, RelOptKind reloptkind,
+						Relids joinrelids);
+extern RelOptInfo *fill_partition_join_rel(PlannerInfo *root, RelOptInfo *rel1,
+								  RelOptInfo *rel2, RelOptInfo *joinrel,
+								  RelOptInfo *parent_joinrel,
+								  SpecialJoinInfo *sjinfo,
+								  List *restrictlist, List *join_aris);
+extern void add_joinrel_to_list(PlannerInfo *root, RelOptInfo *joinrel);
 
 #endif   /* PATHNODE_H */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 44abe83..152db2b 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -212,11 +212,14 @@ extern List *make_inner_pathkeys_for_merge(PlannerInfo *root,
 							  List *mergeclauses,
 							  List *outer_pathkeys);
 extern List *truncate_useless_pathkeys(PlannerInfo *root,
 						  RelOptInfo *rel,
 						  List *pathkeys);
 extern bool has_useful_pathkeys(PlannerInfo *root, RelOptInfo *rel);
 extern PathKey *make_canonical_pathkey(PlannerInfo *root,
 					   EquivalenceClass *eclass, Oid opfamily,
 					   int strategy, bool nulls_first);
 
+/* TODO: need a better place to save this function signature. */
+extern Relids adjust_partition_relids(Relids relids, List *append_rel_infos);
+
 #endif   /* PATHS_H */
diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h
index fb35b68..7b149c3 100644
--- a/src/include/optimizer/prep.h
+++ b/src/include/optimizer/prep.h
@@ -21,20 +21,23 @@
 /*
  * prototypes for prepjointree.c
  */
 extern void pull_up_sublinks(PlannerInfo *root);
 extern void inline_set_returning_functions(PlannerInfo *root);
 extern void pull_up_subqueries(PlannerInfo *root);
 extern void flatten_simple_union_all(PlannerInfo *root);
 extern void reduce_outer_joins(PlannerInfo *root);
 extern Relids get_relids_in_jointree(Node *jtnode, bool include_joins);
 extern Relids get_relids_for_join(PlannerInfo *root, int joinrelid);
+extern Node *adjust_partitionrel_attrs(PlannerInfo *root, Node *node,
+									   List *append_rel_infos);
+extern List *find_appendrelinfos_by_relids(PlannerInfo *root, Relids relids);
 
 /*
  * prototypes for prepqual.c
  */
 extern Node *negate_clause(Node *node);
 extern Expr *canonicalize_qual(Expr *qual);
 
 /*
  * prototypes for prepsecurity.c
  */
diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out
new file mode 100644
index 0000000..307076e
--- /dev/null
+++ b/src/test/regress/expected/partition_join.out
@@ -0,0 +1,6838 @@
+--
+-- PARTITION_JOIN
+-- Test partition-wise join between partitioned tables
+--
+--
+-- partitioned by a single column
+--
+CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a);
+CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES START (0) END (250);
+CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES START (500) END (600);
+CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES START (250) END (500);
+INSERT INTO prt1 SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt1;
+ANALYZE prt1_p1;
+ANALYZE prt1_p2;
+ANALYZE prt1_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt1 AS SELECT * FROM prt1;
+CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b);
+CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES START (0) END (250);
+CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES START (250) END (500);
+CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES START (500) END (600);
+INSERT INTO prt2 SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+ANALYZE prt2;
+ANALYZE prt2_p1;
+ANALYZE prt2_p2;
+ANALYZE prt2_p3;
+CREATE TABLE uprt2 AS SELECT * FROM prt2;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+                       QUERY PLAN                        
+---------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c
+   Sort Key: t1.a
+   ->  Append
+         ->  Hash Join
+               Output: t1.a, t1.c, t2.b, t2.c
+               Hash Cond: (t2.b = t1.a)
+               ->  Seq Scan on public.prt2_p1 t2
+                     Output: t2.b, t2.c
+               ->  Hash
+                     Output: t1.a, t1.c
+                     ->  Seq Scan on public.prt1_p1 t1
+                           Output: t1.a, t1.c
+                           Filter: ((t1.a % 25) = 0)
+         ->  Hash Join
+               Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c
+               Hash Cond: (t2_1.b = t1_2.a)
+               ->  Seq Scan on public.prt2_p2 t2_1
+                     Output: t2_1.b, t2_1.c
+               ->  Hash
+                     Output: t1_2.a, t1_2.c
+                     ->  Seq Scan on public.prt1_p2 t1_2
+                           Output: t1_2.a, t1_2.c
+                           Filter: ((t1_2.a % 25) = 0)
+         ->  Hash Join
+               Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c
+               Hash Cond: (t2_2.b = t1_1.a)
+               ->  Seq Scan on public.prt2_p3 t2_2
+                     Output: t2_2.b, t2_2.c
+               ->  Hash
+                     Output: t1_1.a, t1_1.c
+                     ->  Seq Scan on public.prt1_p3 t1_1
+                           Output: t1_1.a, t1_1.c
+                           Filter: ((t1_1.a % 25) = 0)
+(34 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+ 150 | 0150 | 150 | 0150
+ 300 | 0300 | 300 | 0300
+ 450 | 0450 | 450 | 0450
+(4 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1, uprt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 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)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+                       QUERY PLAN                        
+---------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c
+   Sort Key: t1.a, t2.b
+   ->  Append
+         ->  Hash Right Join
+               Output: t1.a, t1.c, t2.b, t2.c
+               Hash Cond: (t2.b = t1.a)
+               ->  Seq Scan on public.prt2_p1 t2
+                     Output: t2.b, t2.c
+               ->  Hash
+                     Output: t1.a, t1.c
+                     ->  Seq Scan on public.prt1_p1 t1
+                           Output: t1.a, t1.c
+                           Filter: ((t1.a % 25) = 0)
+         ->  Hash Right Join
+               Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c
+               Hash Cond: (t2_1.b = t1_2.a)
+               ->  Seq Scan on public.prt2_p2 t2_1
+                     Output: t2_1.b, t2_1.c
+               ->  Hash
+                     Output: t1_2.a, t1_2.c
+                     ->  Seq Scan on public.prt1_p2 t1_2
+                           Output: t1_2.a, t1_2.c
+                           Filter: ((t1_2.a % 25) = 0)
+         ->  Hash Right Join
+               Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c
+               Hash Cond: (t2_2.b = t1_1.a)
+               ->  Seq Scan on public.prt2_p3 t2_2
+                     Output: t2_2.b, t2_2.c
+               ->  Hash
+                     Output: t1_1.a, t1_1.c
+                     ->  Seq Scan on public.prt1_p3 t1_1
+                           Output: t1_1.a, t1_1.c
+                           Filter: ((t1_1.a % 25) = 0)
+(34 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  50 | 0050 |     | 
+ 100 | 0100 |     | 
+ 150 | 0150 | 150 | 0150
+ 200 | 0200 |     | 
+ 250 | 0250 |     | 
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 |     | 
+ 400 | 0400 |     | 
+ 450 | 0450 | 450 | 0450
+ 500 | 0500 |     | 
+ 550 | 0550 |     | 
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  50 | 0050 |     | 
+ 100 | 0100 |     | 
+ 150 | 0150 | 150 | 0150
+ 200 | 0200 |     | 
+ 250 | 0250 |     | 
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 |     | 
+ 400 | 0400 |     | 
+ 450 | 0450 | 450 | 0450
+ 500 | 0500 |     | 
+ 550 | 0550 |     | 
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+                          QUERY PLAN                           
+---------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c
+   Sort Key: t1.a, t2.b
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c
+         ->  Append
+               ->  Hash Right Join
+                     Output: t2.b, t2.c, t1.a, t1.c
+                     Hash Cond: (t1.a = t2.b)
+                     ->  Seq Scan on public.prt1_p1 t1
+                           Output: t1.a, t1.c
+                     ->  Hash
+                           Output: t2.b, t2.c
+                           ->  Seq Scan on public.prt2_p1 t2
+                                 Output: t2.b, t2.c
+                                 Filter: ((t2.b % 25) = 0)
+               ->  Hash Right Join
+                     Output: t2_1.b, t2_1.c, t1_2.a, t1_2.c
+                     Hash Cond: (t1_2.a = t2_1.b)
+                     ->  Seq Scan on public.prt1_p2 t1_2
+                           Output: t1_2.a, t1_2.c
+                     ->  Hash
+                           Output: t2_1.b, t2_1.c
+                           ->  Seq Scan on public.prt2_p2 t2_1
+                                 Output: t2_1.b, t2_1.c
+                                 Filter: ((t2_1.b % 25) = 0)
+               ->  Hash Right Join
+                     Output: t2_2.b, t2_2.c, t1_1.a, t1_1.c
+                     Hash Cond: (t1_1.a = t2_2.b)
+                     ->  Seq Scan on public.prt1_p3 t1_1
+                           Output: t1_1.a, t1_1.c
+                     ->  Hash
+                           Output: t2_2.b, t2_2.c
+                           ->  Seq Scan on public.prt2_p3 t2_2
+                                 Output: t2_2.b, t2_2.c
+                                 Filter: ((t2_2.b % 25) = 0)
+(36 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+  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)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b WHERE t2.b % 25 = 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)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+                            QUERY PLAN                            
+------------------------------------------------------------------
+ Sort
+   Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c
+   Sort Key: prt1_p1.a, prt2_p1.b
+   ->  Append
+         ->  Hash Full Join
+               Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c
+               Hash Cond: (prt1_p1.a = prt2_p1.b)
+               ->  Seq Scan on public.prt1_p1
+                     Output: prt1_p1.a, prt1_p1.c
+                     Filter: ((prt1_p1.a % 25) = 0)
+               ->  Hash
+                     Output: prt2_p1.b, prt2_p1.c
+                     ->  Seq Scan on public.prt2_p1
+                           Output: prt2_p1.b, prt2_p1.c
+                           Filter: ((prt2_p1.b % 25) = 0)
+         ->  Hash Full Join
+               Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c
+               Hash Cond: (prt1_p2.a = prt2_p2.b)
+               ->  Seq Scan on public.prt1_p2
+                     Output: prt1_p2.a, prt1_p2.c
+                     Filter: ((prt1_p2.a % 25) = 0)
+               ->  Hash
+                     Output: prt2_p2.b, prt2_p2.c
+                     ->  Seq Scan on public.prt2_p2
+                           Output: prt2_p2.b, prt2_p2.c
+                           Filter: ((prt2_p2.b % 25) = 0)
+         ->  Hash Full Join
+               Output: prt1_p3.a, prt1_p3.c, prt2_p3.b, prt2_p3.c
+               Hash Cond: (prt1_p3.a = prt2_p3.b)
+               ->  Seq Scan on public.prt1_p3
+                     Output: prt1_p3.a, prt1_p3.c
+                     Filter: ((prt1_p3.a % 25) = 0)
+               ->  Hash
+                     Output: prt2_p3.b, prt2_p3.c
+                     ->  Seq Scan on public.prt2_p3
+                           Output: prt2_p3.b, prt2_p3.c
+                           Filter: ((prt2_p3.b % 25) = 0)
+(37 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  50 | 0050 |     | 
+ 100 | 0100 |     | 
+ 150 | 0150 | 150 | 0150
+ 200 | 0200 |     | 
+ 250 | 0250 |     | 
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 |     | 
+ 400 | 0400 |     | 
+ 450 | 0450 | 450 | 0450
+ 500 | 0500 |     | 
+ 550 | 0550 |     | 
+     |      |  75 | 0075
+     |      | 225 | 0225
+     |      | 375 | 0375
+     |      | 525 | 0525
+(16 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2 t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  50 | 0050 |     | 
+ 100 | 0100 |     | 
+ 150 | 0150 | 150 | 0150
+ 200 | 0200 |     | 
+ 250 | 0250 |     | 
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 |     | 
+ 400 | 0400 |     | 
+ 450 | 0450 | 450 | 0450
+ 500 | 0500 |     | 
+ 550 | 0550 |     | 
+     |      |  75 | 0075
+     |      | 225 | 0225
+     |      | 375 | 0375
+     |      | 525 | 0525
+(16 rows)
+
+-- Cases with non-nullable expressions in subquery results;
+-- make sure these go to null as expected
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.b OR t2.phv = t2.b ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+                            QUERY PLAN                            
+------------------------------------------------------------------
+ Sort
+   Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c
+   Sort Key: prt1_p1.a, prt2_p1.b
+   ->  Append
+         ->  Hash Full Join
+               Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c
+               Hash Cond: (prt1_p1.a = prt2_p1.b)
+               Filter: (((50) = prt1_p1.b) OR ((75) = prt2_p1.b))
+               ->  Seq Scan on public.prt1_p1
+                     Output: prt1_p1.a, prt1_p1.c, prt1_p1.b, 50
+                     Filter: ((prt1_p1.a % 25) = 0)
+               ->  Hash
+                     Output: prt2_p1.b, prt2_p1.c, (75)
+                     ->  Seq Scan on public.prt2_p1
+                           Output: prt2_p1.b, prt2_p1.c, 75
+                           Filter: ((prt2_p1.b % 25) = 0)
+         ->  Hash Full Join
+               Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c
+               Hash Cond: (prt1_p2.a = prt2_p2.b)
+               Filter: (((50) = prt1_p2.b) OR ((75) = prt2_p2.b))
+               ->  Seq Scan on public.prt1_p2
+                     Output: prt1_p2.a, prt1_p2.c, prt1_p2.b, 50
+                     Filter: ((prt1_p2.a % 25) = 0)
+               ->  Hash
+                     Output: prt2_p2.b, prt2_p2.c, (75)
+                     ->  Seq Scan on public.prt2_p2
+                           Output: prt2_p2.b, prt2_p2.c, 75
+                           Filter: ((prt2_p2.b % 25) = 0)
+         ->  Hash Full Join
+               Output: prt1_p3.a, prt1_p3.c, prt2_p3.b, prt2_p3.c
+               Hash Cond: (prt1_p3.a = prt2_p3.b)
+               Filter: (((50) = prt1_p3.b) OR ((75) = prt2_p3.b))
+               ->  Seq Scan on public.prt1_p3
+                     Output: prt1_p3.a, prt1_p3.c, prt1_p3.b, 50
+                     Filter: ((prt1_p3.a % 25) = 0)
+               ->  Hash
+                     Output: prt2_p3.b, prt2_p3.c, (75)
+                     ->  Seq Scan on public.prt2_p3
+                           Output: prt2_p3.b, prt2_p3.c, 75
+                           Filter: ((prt2_p3.b % 25) = 0)
+(40 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.b OR t2.phv = t2.b ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+ a  |  c   | b  |  c   
+----+------+----+------
+ 50 | 0050 |    | 
+    |      | 75 | 0075
+(2 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.b OR t2.phv = t2.b ORDER BY t1.a, t2.b;
+ a  |  c   | b  |  c   
+----+------+----+------
+ 50 | 0050 |    | 
+    |      | 75 | 0075
+(2 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+                                     QUERY PLAN                                     
+------------------------------------------------------------------------------------
+ Sort
+   Output: prt1_p1.a, prt1_p1.c, (25), prt2_p1.b, prt2_p1.c, (50)
+   Sort Key: prt1_p1.a, prt2_p1.b
+   ->  Result
+         Output: prt1_p1.a, prt1_p1.c, (25), prt2_p1.b, prt2_p1.c, (50)
+         ->  Append
+               ->  Hash Full Join
+                     Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c, (25), (50)
+                     Hash Cond: (prt1_p1.a = prt2_p1.b)
+                     ->  Seq Scan on public.prt1_p1
+                           Output: prt1_p1.a, prt1_p1.c, 25
+                           Filter: ((prt1_p1.a % 25) = 0)
+                     ->  Hash
+                           Output: prt2_p1.b, prt2_p1.c, (50)
+                           ->  Seq Scan on public.prt2_p1
+                                 Output: prt2_p1.b, prt2_p1.c, 50
+                                 Filter: ((prt2_p1.b % 25) = 0)
+               ->  Hash Full Join
+                     Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c, (25), (50)
+                     Hash Cond: (prt1_p2.a = prt2_p2.b)
+                     ->  Seq Scan on public.prt1_p2
+                           Output: prt1_p2.a, prt1_p2.c, 25
+                           Filter: ((prt1_p2.a % 25) = 0)
+                     ->  Hash
+                           Output: prt2_p2.b, prt2_p2.c, (50)
+                           ->  Seq Scan on public.prt2_p2
+                                 Output: prt2_p2.b, prt2_p2.c, 50
+                                 Filter: ((prt2_p2.b % 25) = 0)
+               ->  Hash Full Join
+                     Output: prt1_p3.a, prt1_p3.c, prt2_p3.b, prt2_p3.c, (25), (50)
+                     Hash Cond: (prt1_p3.a = prt2_p3.b)
+                     ->  Seq Scan on public.prt1_p3
+                           Output: prt1_p3.a, prt1_p3.c, 25
+                           Filter: ((prt1_p3.a % 25) = 0)
+                     ->  Hash
+                           Output: prt2_p3.b, prt2_p3.c, (50)
+                           ->  Seq Scan on public.prt2_p3
+                                 Output: prt2_p3.b, prt2_p3.c, 50
+                                 Filter: ((prt2_p3.b % 25) = 0)
+(39 rows)
+
+SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+  a  |  c   | phv |  b  |  c   | phv 
+-----+------+-----+-----+------+-----
+   0 | 0000 |  25 |   0 | 0000 |  50
+  50 | 0050 |  25 |     |      |    
+ 100 | 0100 |  25 |     |      |    
+ 150 | 0150 |  25 | 150 | 0150 |  50
+ 200 | 0200 |  25 |     |      |    
+ 250 | 0250 |  25 |     |      |    
+ 300 | 0300 |  25 | 300 | 0300 |  50
+ 350 | 0350 |  25 |     |      |    
+ 400 | 0400 |  25 |     |      |    
+ 450 | 0450 |  25 | 450 | 0450 |  50
+ 500 | 0500 |  25 |     |      |    
+ 550 | 0550 |  25 |     |      |    
+     |      |     |  75 | 0075 |  50
+     |      |     | 225 | 0225 |  50
+     |      |     | 375 | 0375 |  50
+     |      |     | 525 | 0525 |  50
+(16 rows)
+
+SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+  a  |  c   | phv |  b  |  c   | phv 
+-----+------+-----+-----+------+-----
+   0 | 0000 |  25 |   0 | 0000 |  50
+  50 | 0050 |  25 |     |      |    
+ 100 | 0100 |  25 |     |      |    
+ 150 | 0150 |  25 | 150 | 0150 |  50
+ 200 | 0200 |  25 |     |      |    
+ 250 | 0250 |  25 |     |      |    
+ 300 | 0300 |  25 | 300 | 0300 |  50
+ 350 | 0350 |  25 |     |      |    
+ 400 | 0400 |  25 |     |      |    
+ 450 | 0450 |  25 | 450 | 0450 |  50
+ 500 | 0500 |  25 |     |      |    
+ 550 | 0550 |  25 |     |      |    
+     |      |     |  75 | 0075 |  50
+     |      |     | 225 | 0225 |  50
+     |      |     | 375 | 0375 |  50
+     |      |     | 525 | 0525 |  50
+(16 rows)
+
+-- Join with pruned partitions from joining relations
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+                               QUERY PLAN                               
+------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c
+   Sort Key: t1.a
+   ->  Append
+         ->  Hash Join
+               Output: t1.a, t1.c, t2.b, t2.c
+               Hash Cond: (t2.b = t1.a)
+               ->  Seq Scan on public.prt2_p2 t2
+                     Output: t2.b, t2.c
+                     Filter: (t2.b > 250)
+               ->  Hash
+                     Output: t1.a, t1.c
+                     ->  Seq Scan on public.prt1_p2 t1
+                           Output: t1.a, t1.c
+                           Filter: ((t1.a < 450) AND ((t1.a % 25) = 0))
+(15 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+ 300 | 0300 | 300 | 0300
+(1 row)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1, uprt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+ 300 | 0300 | 300 | 0300
+(1 row)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+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.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+                                    QUERY PLAN                                    
+----------------------------------------------------------------------------------
+ Sort
+   Output: prt1_p1.a, prt1_p1.c, b, c
+   Sort Key: prt1_p1.a, b
+   ->  Append
+         ->  Nested Loop Left Join
+               Output: prt1_p1.a, prt1_p1.c, b, c
+               Join Filter: (prt1_p1.a = b)
+               ->  Seq Scan on public.prt1_p1
+                     Output: prt1_p1.a, prt1_p1.c
+                     Filter: ((prt1_p1.a < 450) AND ((prt1_p1.a % 25) = 0))
+               ->  Result
+                     Output: b, c
+                     One-Time Filter: false
+         ->  Hash Right Join
+               Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c
+               Hash Cond: (prt2_p2.b = prt1_p2.a)
+               ->  Seq Scan on public.prt2_p2
+                     Output: prt2_p2.b, prt2_p2.c
+                     Filter: (prt2_p2.b > 250)
+               ->  Hash
+                     Output: prt1_p2.a, prt1_p2.c
+                     ->  Seq Scan on public.prt1_p2
+                           Output: prt1_p2.a, prt1_p2.c
+                           Filter: ((prt1_p2.a < 450) AND ((prt1_p2.a % 25) = 0))
+(24 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.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+  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)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM uprt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.a % 25 = 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)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 RIGHT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 5) and (b 4) is considered for partition-wise join.
+                                       QUERY PLAN                                       
+----------------------------------------------------------------------------------------
+ Sort
+   Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c
+   Sort Key: prt1_p2.a, prt2_p2.b
+   ->  Result
+         Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c
+         ->  Append
+               ->  Hash Right Join
+                     Output: prt2_p2.b, prt2_p2.c, prt1_p2.a, prt1_p2.c
+                     Hash Cond: (prt1_p2.a = prt2_p2.b)
+                     ->  Seq Scan on public.prt1_p2
+                           Output: prt1_p2.a, prt1_p2.c
+                           Filter: (prt1_p2.a < 450)
+                     ->  Hash
+                           Output: prt2_p2.b, prt2_p2.c
+                           ->  Seq Scan on public.prt2_p2
+                                 Output: prt2_p2.b, prt2_p2.c
+                                 Filter: ((prt2_p2.b > 250) AND ((prt2_p2.a % 25) = 0))
+               ->  Nested Loop Left Join
+                     Output: prt2_p3.b, prt2_p3.c, a, c
+                     Join Filter: (a = prt2_p3.b)
+                     ->  Seq Scan on public.prt2_p3
+                           Output: prt2_p3.b, prt2_p3.c
+                           Filter: ((prt2_p3.b > 250) AND ((prt2_p3.a % 25) = 0))
+                     ->  Result
+                           Output: a, c
+                           One-Time Filter: false
+(26 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 RIGHT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 5) and (b 4) is considered for partition-wise join.
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+ 300 | 0300 | 300 | 0300
+     |      | 375 | 0375
+     |      | 450 | 0450
+     |      | 525 | 0525
+(4 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a < 450) t1 RIGHT JOIN (SELECT * FROM uprt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+ 300 | 0300 | 300 | 0300
+     |      | 375 | 0375
+     |      | 450 | 0450
+     |      | 525 | 0525
+(4 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450 AND a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250 AND b % 25 = 0) t2 ON t1.a = t2.b ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+                                    QUERY PLAN                                    
+----------------------------------------------------------------------------------
+ Sort
+   Output: prt1_p1.a, prt1_p1.c, b, c
+   Sort Key: prt1_p1.a, b
+   ->  Append
+         ->  Hash Full Join
+               Output: prt1_p1.a, prt1_p1.c, b, c
+               Hash Cond: (prt1_p1.a = b)
+               ->  Seq Scan on public.prt1_p1
+                     Output: prt1_p1.a, prt1_p1.c
+                     Filter: ((prt1_p1.a < 450) AND ((prt1_p1.a % 25) = 0))
+               ->  Hash
+                     Output: b, c
+                     ->  Result
+                           Output: b, c
+                           One-Time Filter: false
+         ->  Hash Full Join
+               Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c
+               Hash Cond: (prt1_p2.a = prt2_p2.b)
+               ->  Seq Scan on public.prt1_p2
+                     Output: prt1_p2.a, prt1_p2.c
+                     Filter: ((prt1_p2.a < 450) AND ((prt1_p2.a % 25) = 0))
+               ->  Hash
+                     Output: prt2_p2.b, prt2_p2.c
+                     ->  Seq Scan on public.prt2_p2
+                           Output: prt2_p2.b, prt2_p2.c
+                           Filter: ((prt2_p2.b > 250) AND ((prt2_p2.b % 25) = 0))
+         ->  Hash Full Join
+               Output: a, c, prt2_p3.b, prt2_p3.c
+               Hash Cond: (prt2_p3.b = a)
+               ->  Seq Scan on public.prt2_p3
+                     Output: prt2_p3.b, prt2_p3.c
+                     Filter: ((prt2_p3.b > 250) AND ((prt2_p3.b % 25) = 0))
+               ->  Hash
+                     Output: a, c
+                     ->  Result
+                           Output: a, c
+                           One-Time Filter: false
+(37 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450 AND a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250 AND b % 25 = 0) t2 ON t1.a = t2.b ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+  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)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a < 450 AND a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2 WHERE b > 250 AND b % 25 = 0) t2 ON t1.a = t2.b 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)
+
+-- Semi-join
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 1) and (b 3) is considered for partition-wise join.
+                       QUERY PLAN                        
+---------------------------------------------------------
+ Sort
+   Output: t1.a, t1.b, t1.c
+   Sort Key: t1.a
+   ->  Append
+         ->  Nested Loop Semi Join
+               Output: t1.a, t1.b, t1.c
+               Join Filter: (t1.a = t1_3.b)
+               ->  Seq Scan on public.prt1_p1 t1
+                     Output: t1.a, t1.b, t1.c
+                     Filter: ((t1.a % 25) = 0)
+               ->  Materialize
+                     Output: t1_3.b
+                     ->  Seq Scan on public.prt2_p1 t1_3
+                           Output: t1_3.b
+                           Filter: ((t1_3.b % 25) = 0)
+         ->  Nested Loop Semi Join
+               Output: t1_2.a, t1_2.b, t1_2.c
+               Join Filter: (t1_2.a = t1_4.b)
+               ->  Seq Scan on public.prt1_p2 t1_2
+                     Output: t1_2.a, t1_2.b, t1_2.c
+                     Filter: ((t1_2.a % 25) = 0)
+               ->  Materialize
+                     Output: t1_4.b
+                     ->  Seq Scan on public.prt2_p2 t1_4
+                           Output: t1_4.b
+                           Filter: ((t1_4.b % 25) = 0)
+         ->  Nested Loop Semi Join
+               Output: t1_1.a, t1_1.b, t1_1.c
+               Join Filter: (t1_1.a = t1_5.b)
+               ->  Seq Scan on public.prt1_p3 t1_1
+                     Output: t1_1.a, t1_1.b, t1_1.c
+                     Filter: ((t1_1.a % 25) = 0)
+               ->  Materialize
+                     Output: t1_5.b
+                     ->  Seq Scan on public.prt2_p3 t1_5
+                           Output: t1_5.b
+                           Filter: ((t1_5.b % 25) = 0)
+(37 rows)
+
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 1) and (b 3) is considered for partition-wise join.
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+-- lateral reference
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 4 5) is considered for partition-wise join.
+                                             QUERY PLAN                                              
+-----------------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a)), t1.a
+   Sort Key: t1.a
+   ->  Result
+         Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a)), t1.a
+         ->  Append
+               ->  Nested Loop Left Join
+                     Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a))
+                     ->  Seq Scan on public.prt1_p1 t1
+                           Output: t1.a, t1.b, t1.c
+                           Filter: ((t1.a % 25) = 0)
+                     ->  Hash Join
+                           Output: t2.a, t3.a, LEAST(t1.a, t2.a, t3.a)
+                           Hash Cond: (t3.b = t2.a)
+                           ->  Seq Scan on public.prt2_p1 t3
+                                 Output: t3.a, t3.b
+                           ->  Hash
+                                 Output: t2.a
+                                 ->  Seq Scan on public.prt1_p1 t2
+                                       Output: t2.a
+                                       Filter: (t1.a = t2.a)
+               ->  Nested Loop Left Join
+                     Output: t1_2.a, t1_2.b, t1_2.c, t2_2.a, t3_1.a, (LEAST(t1_2.a, t2_2.a, t3_1.a))
+                     ->  Seq Scan on public.prt1_p2 t1_2
+                           Output: t1_2.a, t1_2.b, t1_2.c
+                           Filter: ((t1_2.a % 25) = 0)
+                     ->  Hash Join
+                           Output: t2_2.a, t3_1.a, LEAST(t1_2.a, t2_2.a, t3_1.a)
+                           Hash Cond: (t3_1.b = t2_2.a)
+                           ->  Seq Scan on public.prt2_p2 t3_1
+                                 Output: t3_1.a, t3_1.b
+                           ->  Hash
+                                 Output: t2_2.a
+                                 ->  Seq Scan on public.prt1_p2 t2_2
+                                       Output: t2_2.a
+                                       Filter: (t1_2.a = t2_2.a)
+               ->  Nested Loop Left Join
+                     Output: t1_1.a, t1_1.b, t1_1.c, t2_1.a, t3_2.a, (LEAST(t1_1.a, t2_1.a, t3_2.a))
+                     ->  Seq Scan on public.prt1_p3 t1_1
+                           Output: t1_1.a, t1_1.b, t1_1.c
+                           Filter: ((t1_1.a % 25) = 0)
+                     ->  Hash Join
+                           Output: t2_1.a, t3_2.a, LEAST(t1_1.a, t2_1.a, t3_2.a)
+                           Hash Cond: (t3_2.b = t2_1.a)
+                           ->  Seq Scan on public.prt2_p3 t3_2
+                                 Output: t3_2.a, t3_2.b
+                           ->  Hash
+                                 Output: t2_1.a
+                                 ->  Seq Scan on public.prt1_p3 t2_1
+                                       Output: t2_1.a
+                                       Filter: (t1_1.a = t2_1.a)
+(51 rows)
+
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 4 5) is considered for partition-wise join.
+  a  |  b  |  c   | t2a | t3a | least 
+-----+-----+------+-----+-----+-------
+   0 |   0 | 0000 |   0 |   0 |     0
+  50 |  50 | 0050 |     |     |      
+ 100 | 100 | 0100 |     |     |      
+ 150 | 150 | 0150 | 150 | 150 |   150
+ 200 | 200 | 0200 |     |     |      
+ 250 | 250 | 0250 |     |     |      
+ 300 | 300 | 0300 | 300 | 300 |   300
+ 350 | 350 | 0350 |     |     |      
+ 400 | 400 | 0400 |     |     |      
+ 450 | 450 | 0450 | 450 | 450 |   450
+ 500 | 500 | 0500 |     |     |      
+ 550 | 550 | 0550 |     |     |      
+(12 rows)
+
+SELECT * FROM uprt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+  a  |  b  |  c   | t2a | t3a | least 
+-----+-----+------+-----+-----+-------
+   0 |   0 | 0000 |   0 |   0 |     0
+  50 |  50 | 0050 |     |     |      
+ 100 | 100 | 0100 |     |     |      
+ 150 | 150 | 0150 | 150 | 150 |   150
+ 200 | 200 | 0200 |     |     |      
+ 250 | 250 | 0250 |     |     |      
+ 300 | 300 | 0300 | 300 | 300 |   300
+ 350 | 350 | 0350 |     |     |      
+ 400 | 400 | 0400 |     |     |      
+ 450 | 450 | 0450 | 450 | 450 |   450
+ 500 | 500 | 0500 |     |     |      
+ 550 | 550 | 0550 |     |     |      
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+                                  QUERY PLAN                                   
+-------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a)), t1.a
+   Sort Key: t1.a
+   ->  Nested Loop Left Join
+         Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a)), t1.a
+         ->  Append
+               ->  Seq Scan on public.prt1 t1
+                     Output: t1.a, t1.b, t1.c
+                     Filter: ((t1.a % 25) = 0)
+               ->  Seq Scan on public.prt1_p1 t1_1
+                     Output: t1_1.a, t1_1.b, t1_1.c
+                     Filter: ((t1_1.a % 25) = 0)
+               ->  Seq Scan on public.prt1_p3 t1_2
+                     Output: t1_2.a, t1_2.b, t1_2.c
+                     Filter: ((t1_2.a % 25) = 0)
+               ->  Seq Scan on public.prt1_p2 t1_3
+                     Output: t1_3.a, t1_3.b, t1_3.c
+                     Filter: ((t1_3.a % 25) = 0)
+         ->  Append
+               ->  Hash Join
+                     Output: t2.a, t3.a, LEAST(t1.a, t2.a, t3.a)
+                     Hash Cond: (t3.b = t2.a)
+                     ->  Seq Scan on public.prt2_p1 t3
+                           Output: t3.a, t3.b
+                     ->  Hash
+                           Output: t2.a
+                           ->  Seq Scan on public.prt1_p1 t2
+                                 Output: t2.a
+                                 Filter: (t1.b = t2.a)
+               ->  Hash Join
+                     Output: t2_2.a, t3_1.a, LEAST(t1.a, t2_2.a, t3_1.a)
+                     Hash Cond: (t3_1.b = t2_2.a)
+                     ->  Seq Scan on public.prt2_p2 t3_1
+                           Output: t3_1.a, t3_1.b
+                     ->  Hash
+                           Output: t2_2.a
+                           ->  Seq Scan on public.prt1_p2 t2_2
+                                 Output: t2_2.a
+                                 Filter: (t1.b = t2_2.a)
+               ->  Hash Join
+                     Output: t2_1.a, t3_2.a, LEAST(t1.a, t2_1.a, t3_2.a)
+                     Hash Cond: (t3_2.b = t2_1.a)
+                     ->  Seq Scan on public.prt2_p3 t3_2
+                           Output: t3_2.a, t3_2.b
+                     ->  Hash
+                           Output: t2_1.a
+                           ->  Seq Scan on public.prt1_p3 t2_1
+                                 Output: t2_1.a
+                                 Filter: (t1.b = t2_1.a)
+(49 rows)
+
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+  a  |  b  |  c   | t2a | t3a | least 
+-----+-----+------+-----+-----+-------
+   0 |   0 | 0000 |   0 |   0 |     0
+  50 |  50 | 0050 |     |     |      
+ 100 | 100 | 0100 |     |     |      
+ 150 | 150 | 0150 | 150 | 150 |   150
+ 200 | 200 | 0200 |     |     |      
+ 250 | 250 | 0250 |     |     |      
+ 300 | 300 | 0300 | 300 | 300 |   300
+ 350 | 350 | 0350 |     |     |      
+ 400 | 400 | 0400 |     |     |      
+ 450 | 450 | 0450 | 450 | 450 |   450
+ 500 | 500 | 0500 |     |     |      
+ 550 | 550 | 0550 |     |     |      
+(12 rows)
+
+SELECT * FROM uprt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+  a  |  b  |  c   | t2a | t3a | least 
+-----+-----+------+-----+-----+-------
+   0 |   0 | 0000 |   0 |   0 |     0
+  50 |  50 | 0050 |     |     |      
+ 100 | 100 | 0100 |     |     |      
+ 150 | 150 | 0150 | 150 | 150 |   150
+ 200 | 200 | 0200 |     |     |      
+ 250 | 250 | 0250 |     |     |      
+ 300 | 300 | 0300 | 300 | 300 |   300
+ 350 | 350 | 0350 |     |     |      
+ 400 | 400 | 0400 |     |     |      
+ 450 | 450 | 0450 | 450 | 450 |   450
+ 500 | 500 | 0500 |     |     |      
+ 550 | 550 | 0550 |     |     |      
+(12 rows)
+
+--
+-- partitioned by expression
+--
+CREATE TABLE prt1_e (a int, b int, c varchar) PARTITION BY RANGE(((a + b)/2));
+CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES START (0) END (250);
+CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES START (250) END (500);
+CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES START (500) END (600);
+INSERT INTO prt1_e SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt1_e;
+ANALYZE prt1_e_p1;
+ANALYZE prt1_e_p2;
+ANALYZE prt1_e_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt1_e AS SELECT * FROM prt1_e;
+CREATE TABLE prt2_e (a int, b int, c varchar) PARTITION BY RANGE(((b + a)/2));
+CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES START (0) END (250);
+CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES START (250) END (500);
+CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES START (500) END (600);
+INSERT INTO prt2_e SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+ANALYZE prt2_e;
+ANALYZE prt2_e_p1;
+ANALYZE prt2_e_p2;
+ANALYZE prt2_e_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt2_e AS SELECT * FROM prt2_e;
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+                                  QUERY PLAN                                  
+------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c
+   Sort Key: t1.a, t2.b
+   ->  Append
+         ->  Hash Join
+               Output: t1.a, t1.c, t2.b, t2.c
+               Hash Cond: (((t2.b + t2.a) / 2) = ((t1.a + t1.b) / 2))
+               ->  Seq Scan on public.prt2_e_p1 t2
+                     Output: t2.b, t2.c, t2.a
+               ->  Hash
+                     Output: t1.a, t1.c, t1.b
+                     ->  Seq Scan on public.prt1_e_p1 t1
+                           Output: t1.a, t1.c, t1.b
+                           Filter: ((t1.a % 25) = 0)
+         ->  Hash Join
+               Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+               Hash Cond: (((t2_1.b + t2_1.a) / 2) = ((t1_1.a + t1_1.b) / 2))
+               ->  Seq Scan on public.prt2_e_p2 t2_1
+                     Output: t2_1.b, t2_1.c, t2_1.a
+               ->  Hash
+                     Output: t1_1.a, t1_1.c, t1_1.b
+                     ->  Seq Scan on public.prt1_e_p2 t1_1
+                           Output: t1_1.a, t1_1.c, t1_1.b
+                           Filter: ((t1_1.a % 25) = 0)
+         ->  Hash Join
+               Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+               Hash Cond: (((t2_2.b + t2_2.a) / 2) = ((t1_2.a + t1_2.b) / 2))
+               ->  Seq Scan on public.prt2_e_p3 t2_2
+                     Output: t2_2.b, t2_2.c, t2_2.a
+               ->  Hash
+                     Output: t1_2.a, t1_2.c, t1_2.b
+                     ->  Seq Scan on public.prt1_e_p3 t1_2
+                           Output: t1_2.a, t1_2.c, t1_2.b
+                           Filter: ((t1_2.a % 25) = 0)
+(34 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.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+ 150 | 0150 | 150 | 0150
+ 300 | 0300 | 300 | 0300
+ 450 | 0450 | 450 | 0450
+(4 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_e t1, uprt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.a % 25 = 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)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 LEFT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+                                  QUERY PLAN                                  
+------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c
+   Sort Key: t1.a, t2.b
+   ->  Append
+         ->  Hash Right Join
+               Output: t1.a, t1.c, t2.b, t2.c
+               Hash Cond: (((t2.b + t2.a) / 2) = ((t1.a + t1.b) / 2))
+               ->  Seq Scan on public.prt2_e_p1 t2
+                     Output: t2.b, t2.c, t2.a
+               ->  Hash
+                     Output: t1.a, t1.c, t1.b
+                     ->  Seq Scan on public.prt1_e_p1 t1
+                           Output: t1.a, t1.c, t1.b
+                           Filter: ((t1.a % 25) = 0)
+         ->  Hash Right Join
+               Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+               Hash Cond: (((t2_1.b + t2_1.a) / 2) = ((t1_1.a + t1_1.b) / 2))
+               ->  Seq Scan on public.prt2_e_p2 t2_1
+                     Output: t2_1.b, t2_1.c, t2_1.a
+               ->  Hash
+                     Output: t1_1.a, t1_1.c, t1_1.b
+                     ->  Seq Scan on public.prt1_e_p2 t1_1
+                           Output: t1_1.a, t1_1.c, t1_1.b
+                           Filter: ((t1_1.a % 25) = 0)
+         ->  Hash Right Join
+               Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+               Hash Cond: (((t2_2.b + t2_2.a) / 2) = ((t1_2.a + t1_2.b) / 2))
+               ->  Seq Scan on public.prt2_e_p3 t2_2
+                     Output: t2_2.b, t2_2.c, t2_2.a
+               ->  Hash
+                     Output: t1_2.a, t1_2.c, t1_2.b
+                     ->  Seq Scan on public.prt1_e_p3 t1_2
+                           Output: t1_2.a, t1_2.c, t1_2.b
+                           Filter: ((t1_2.a % 25) = 0)
+(34 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 LEFT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  50 | 0050 |     | 
+ 100 | 0100 |     | 
+ 150 | 0150 | 150 | 0150
+ 200 | 0200 |     | 
+ 250 | 0250 |     | 
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 |     | 
+ 400 | 0400 |     | 
+ 450 | 0450 | 450 | 0450
+ 500 | 0500 |     | 
+ 550 | 0550 |     | 
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_e t1 LEFT JOIN uprt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  50 | 0050 |     | 
+ 100 | 0100 |     | 
+ 150 | 0150 | 150 | 0150
+ 200 | 0200 |     | 
+ 250 | 0250 |     | 
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 |     | 
+ 400 | 0400 |     | 
+ 450 | 0450 | 450 | 0450
+ 500 | 0500 |     | 
+ 550 | 0550 |     | 
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 RIGHT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+                                     QUERY PLAN                                     
+------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c
+   Sort Key: t1.a, t2.b
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c
+         ->  Append
+               ->  Hash Right Join
+                     Output: t2.b, t2.c, t1.a, t1.c
+                     Hash Cond: (((t1.a + t1.b) / 2) = ((t2.b + t2.a) / 2))
+                     ->  Seq Scan on public.prt1_e_p1 t1
+                           Output: t1.a, t1.c, t1.b
+                     ->  Hash
+                           Output: t2.b, t2.c, t2.a
+                           ->  Seq Scan on public.prt2_e_p1 t2
+                                 Output: t2.b, t2.c, t2.a
+                                 Filter: ((t2.b % 25) = 0)
+               ->  Hash Right Join
+                     Output: t2_1.b, t2_1.c, t1_1.a, t1_1.c
+                     Hash Cond: (((t1_1.a + t1_1.b) / 2) = ((t2_1.b + t2_1.a) / 2))
+                     ->  Seq Scan on public.prt1_e_p2 t1_1
+                           Output: t1_1.a, t1_1.c, t1_1.b
+                     ->  Hash
+                           Output: t2_1.b, t2_1.c, t2_1.a
+                           ->  Seq Scan on public.prt2_e_p2 t2_1
+                                 Output: t2_1.b, t2_1.c, t2_1.a
+                                 Filter: ((t2_1.b % 25) = 0)
+               ->  Hash Right Join
+                     Output: t2_2.b, t2_2.c, t1_2.a, t1_2.c
+                     Hash Cond: (((t1_2.a + t1_2.b) / 2) = ((t2_2.b + t2_2.a) / 2))
+                     ->  Seq Scan on public.prt1_e_p3 t1_2
+                           Output: t1_2.a, t1_2.c, t1_2.b
+                     ->  Hash
+                           Output: t2_2.b, t2_2.c, t2_2.a
+                           ->  Seq Scan on public.prt2_e_p3 t2_2
+                                 Output: t2_2.b, t2_2.c, t2_2.a
+                                 Filter: ((t2_2.b % 25) = 0)
+(36 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 RIGHT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+  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)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_e t1 RIGHT JOIN uprt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t2.b % 25 = 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)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_e WHERE prt2_e.b % 25 = 0) t2 ON ((t1.a + t1.b)/2 = (t2.b + t2.a)/2) ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+                                            QUERY PLAN                                            
+--------------------------------------------------------------------------------------------------
+ Sort
+   Output: prt1_e_p1.a, prt1_e_p1.c, prt2_e_p1.b, prt2_e_p1.c
+   Sort Key: prt1_e_p1.a, prt2_e_p1.b
+   ->  Append
+         ->  Hash Full Join
+               Output: prt1_e_p1.a, prt1_e_p1.c, prt2_e_p1.b, prt2_e_p1.c
+               Hash Cond: (((prt1_e_p1.a + prt1_e_p1.b) / 2) = ((prt2_e_p1.b + prt2_e_p1.a) / 2))
+               ->  Seq Scan on public.prt1_e_p1
+                     Output: prt1_e_p1.a, prt1_e_p1.c, prt1_e_p1.b
+                     Filter: ((prt1_e_p1.a % 25) = 0)
+               ->  Hash
+                     Output: prt2_e_p1.b, prt2_e_p1.c, prt2_e_p1.a
+                     ->  Seq Scan on public.prt2_e_p1
+                           Output: prt2_e_p1.b, prt2_e_p1.c, prt2_e_p1.a
+                           Filter: ((prt2_e_p1.b % 25) = 0)
+         ->  Hash Full Join
+               Output: prt1_e_p2.a, prt1_e_p2.c, prt2_e_p2.b, prt2_e_p2.c
+               Hash Cond: (((prt1_e_p2.a + prt1_e_p2.b) / 2) = ((prt2_e_p2.b + prt2_e_p2.a) / 2))
+               ->  Seq Scan on public.prt1_e_p2
+                     Output: prt1_e_p2.a, prt1_e_p2.c, prt1_e_p2.b
+                     Filter: ((prt1_e_p2.a % 25) = 0)
+               ->  Hash
+                     Output: prt2_e_p2.b, prt2_e_p2.c, prt2_e_p2.a
+                     ->  Seq Scan on public.prt2_e_p2
+                           Output: prt2_e_p2.b, prt2_e_p2.c, prt2_e_p2.a
+                           Filter: ((prt2_e_p2.b % 25) = 0)
+         ->  Hash Full Join
+               Output: prt1_e_p3.a, prt1_e_p3.c, prt2_e_p3.b, prt2_e_p3.c
+               Hash Cond: (((prt1_e_p3.a + prt1_e_p3.b) / 2) = ((prt2_e_p3.b + prt2_e_p3.a) / 2))
+               ->  Seq Scan on public.prt1_e_p3
+                     Output: prt1_e_p3.a, prt1_e_p3.c, prt1_e_p3.b
+                     Filter: ((prt1_e_p3.a % 25) = 0)
+               ->  Hash
+                     Output: prt2_e_p3.b, prt2_e_p3.c, prt2_e_p3.a
+                     ->  Seq Scan on public.prt2_e_p3
+                           Output: prt2_e_p3.b, prt2_e_p3.c, prt2_e_p3.a
+                           Filter: ((prt2_e_p3.b % 25) = 0)
+(37 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_e WHERE prt2_e.b % 25 = 0) t2 ON ((t1.a + t1.b)/2 = (t2.b + t2.a)/2) ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  50 | 0050 |     | 
+ 100 | 0100 |     | 
+ 150 | 0150 | 150 | 0150
+ 200 | 0200 |     | 
+ 250 | 0250 |     | 
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 |     | 
+ 400 | 0400 |     | 
+ 450 | 0450 | 450 | 0450
+ 500 | 0500 |     | 
+ 550 | 0550 |     | 
+     |      |  75 | 0075
+     |      | 225 | 0225
+     |      | 375 | 0375
+     |      | 525 | 0525
+(16 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1_e t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2_e t2 WHERE t2.b % 25 = 0) t2 ON ((t1.a + t1.b)/2 = (t2.b + t2.a)/2) ORDER BY t1.a, t2.b;
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  50 | 0050 |     | 
+ 100 | 0100 |     | 
+ 150 | 0150 | 150 | 0150
+ 200 | 0200 |     | 
+ 250 | 0250 |     | 
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 |     | 
+ 400 | 0400 |     | 
+ 450 | 0450 | 450 | 0450
+ 500 | 0500 |     | 
+ 550 | 0550 |     | 
+     |      |  75 | 0075
+     |      | 225 | 0225
+     |      | 375 | 0375
+     |      | 525 | 0525
+(16 rows)
+
+--
+-- N-way join
+--
+EXPLAIN (VERBOSE, COSTS OFF)
+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.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 3) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 2) and (b 3) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 3) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2 3) and (b 1) is considered for partition-wise join.
+                                     QUERY PLAN                                     
+------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+   Sort Key: t1.a
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+         ->  Append
+               ->  Hash Join
+                     Output: t1.a, t1.c, t2.b, t2.c, t3.a, t3.b, t3.c
+                     Hash Cond: (((t3.a + t3.b) / 2) = t1.a)
+                     ->  Seq Scan on public.prt1_e_p1 t3
+                           Output: t3.a, t3.b, t3.c
+                     ->  Hash
+                           Output: t1.a, t1.c, t2.b, t2.c
+                           ->  Hash Join
+                                 Output: t1.a, t1.c, t2.b, t2.c
+                                 Hash Cond: (t2.b = t1.a)
+                                 ->  Seq Scan on public.prt2_p1 t2
+                                       Output: t2.b, t2.c
+                                 ->  Hash
+                                       Output: t1.a, t1.c
+                                       ->  Seq Scan on public.prt1_p1 t1
+                                             Output: t1.a, t1.c
+                                             Filter: ((t1.a % 25) = 0)
+               ->  Hash Join
+                     Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c
+                     Hash Cond: (((t3_1.a + t3_1.b) / 2) = t1_2.a)
+                     ->  Seq Scan on public.prt1_e_p2 t3_1
+                           Output: t3_1.a, t3_1.b, t3_1.c
+                     ->  Hash
+                           Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c
+                           ->  Hash Join
+                                 Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c
+                                 Hash Cond: (t2_1.b = t1_2.a)
+                                 ->  Seq Scan on public.prt2_p2 t2_1
+                                       Output: t2_1.b, t2_1.c
+                                 ->  Hash
+                                       Output: t1_2.a, t1_2.c
+                                       ->  Seq Scan on public.prt1_p2 t1_2
+                                             Output: t1_2.a, t1_2.c
+                                             Filter: ((t1_2.a % 25) = 0)
+               ->  Hash Join
+                     Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c
+                     Hash Cond: (((t3_2.a + t3_2.b) / 2) = t1_1.a)
+                     ->  Seq Scan on public.prt1_e_p3 t3_2
+                           Output: t3_2.a, t3_2.b, t3_2.c
+                     ->  Hash
+                           Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c
+                           ->  Hash Join
+                                 Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c
+                                 Hash Cond: (t2_2.b = t1_1.a)
+                                 ->  Seq Scan on public.prt2_p3 t2_2
+                                       Output: t2_2.b, t2_2.c
+                                 ->  Hash
+                                       Output: t1_1.a, t1_1.c
+                                       ->  Seq Scan on public.prt1_p3 t1_1
+                                             Output: t1_1.a, t1_1.c
+                                             Filter: ((t1_1.a % 25) = 0)
+(57 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.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 3) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 2) and (b 3) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 3) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2 3) and (b 1) is considered for partition-wise join.
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+(4 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM uprt1 t1, uprt2 t2, uprt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+(4 rows)
+
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 2) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+                                     QUERY PLAN                                     
+------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+   Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+         ->  Append
+               ->  Hash Right Join
+                     Output: t1.a, t1.c, t2.b, t2.c, t3.a, t3.b, t3.c
+                     Hash Cond: (((t3.a + t3.b) / 2) = t1.a)
+                     ->  Seq Scan on public.prt1_e_p1 t3
+                           Output: t3.a, t3.b, t3.c
+                     ->  Hash
+                           Output: t1.a, t1.c, t2.b, t2.c
+                           ->  Hash Right Join
+                                 Output: t1.a, t1.c, t2.b, t2.c
+                                 Hash Cond: (t2.b = t1.a)
+                                 ->  Seq Scan on public.prt2_p1 t2
+                                       Output: t2.b, t2.c
+                                 ->  Hash
+                                       Output: t1.a, t1.c
+                                       ->  Seq Scan on public.prt1_p1 t1
+                                             Output: t1.a, t1.c
+                                             Filter: ((t1.a % 25) = 0)
+               ->  Hash Right Join
+                     Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c
+                     Hash Cond: (((t3_1.a + t3_1.b) / 2) = t1_2.a)
+                     ->  Seq Scan on public.prt1_e_p2 t3_1
+                           Output: t3_1.a, t3_1.b, t3_1.c
+                     ->  Hash
+                           Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c
+                           ->  Hash Right Join
+                                 Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c
+                                 Hash Cond: (t2_1.b = t1_2.a)
+                                 ->  Seq Scan on public.prt2_p2 t2_1
+                                       Output: t2_1.b, t2_1.c
+                                 ->  Hash
+                                       Output: t1_2.a, t1_2.c
+                                       ->  Seq Scan on public.prt1_p2 t1_2
+                                             Output: t1_2.a, t1_2.c
+                                             Filter: ((t1_2.a % 25) = 0)
+               ->  Hash Right Join
+                     Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c
+                     Hash Cond: (((t3_2.a + t3_2.b) / 2) = t1_1.a)
+                     ->  Seq Scan on public.prt1_e_p3 t3_2
+                           Output: t3_2.a, t3_2.b, t3_2.c
+                     ->  Hash
+                           Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c
+                           ->  Hash Right Join
+                                 Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c
+                                 Hash Cond: (t2_2.b = t1_1.a)
+                                 ->  Seq Scan on public.prt2_p3 t2_2
+                                       Output: t2_2.b, t2_2.c
+                                 ->  Hash
+                                       Output: t1_1.a, t1_1.c
+                                       ->  Seq Scan on public.prt1_p3 t1_1
+                                             Output: t1_1.a, t1_1.c
+                                             Filter: ((t1_1.a % 25) = 0)
+(57 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 2) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+  50 | 0050 |     |      |      100 | 0050
+ 100 | 0100 |     |      |      200 | 0100
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 200 | 0200 |     |      |      400 | 0200
+ 250 | 0250 |     |      |      500 | 0250
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 350 | 0350 |     |      |      700 | 0350
+ 400 | 0400 |     |      |      800 | 0400
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+ 500 | 0500 |     |      |     1000 | 0500
+ 550 | 0550 |     |      |     1100 | 0550
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) LEFT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+  50 | 0050 |     |      |      100 | 0050
+ 100 | 0100 |     |      |      200 | 0100
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 200 | 0200 |     |      |      400 | 0200
+ 250 | 0250 |     |      |      500 | 0250
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 350 | 0350 |     |      |      700 | 0350
+ 400 | 0400 |     |      |      800 | 0400
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+ 500 | 0500 |     |      |     1000 | 0500
+ 550 | 0550 |     |      |     1100 | 0550
+(12 rows)
+
+EXPLAIN (VERBOSE, 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 (t2.b = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 2) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 2 4) is considered for partition-wise join.
+                                     QUERY PLAN                                     
+------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+   Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+         ->  Append
+               ->  Hash Right Join
+                     Output: t1.a, t1.c, t2.b, t2.c, t3.a, t3.b, t3.c
+                     Hash Cond: (((t3.a + t3.b) / 2) = t2.b)
+                     ->  Seq Scan on public.prt1_e_p1 t3
+                           Output: t3.a, t3.b, t3.c
+                     ->  Hash
+                           Output: t1.a, t1.c, t2.b, t2.c
+                           ->  Hash Right Join
+                                 Output: t1.a, t1.c, t2.b, t2.c
+                                 Hash Cond: (t2.b = t1.a)
+                                 ->  Seq Scan on public.prt2_p1 t2
+                                       Output: t2.b, t2.c
+                                 ->  Hash
+                                       Output: t1.a, t1.c
+                                       ->  Seq Scan on public.prt1_p1 t1
+                                             Output: t1.a, t1.c
+                                             Filter: ((t1.a % 25) = 0)
+               ->  Hash Right Join
+                     Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c
+                     Hash Cond: (((t3_1.a + t3_1.b) / 2) = t2_1.b)
+                     ->  Seq Scan on public.prt1_e_p2 t3_1
+                           Output: t3_1.a, t3_1.b, t3_1.c
+                     ->  Hash
+                           Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c
+                           ->  Hash Right Join
+                                 Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c
+                                 Hash Cond: (t2_1.b = t1_2.a)
+                                 ->  Seq Scan on public.prt2_p2 t2_1
+                                       Output: t2_1.b, t2_1.c
+                                 ->  Hash
+                                       Output: t1_2.a, t1_2.c
+                                       ->  Seq Scan on public.prt1_p2 t1_2
+                                             Output: t1_2.a, t1_2.c
+                                             Filter: ((t1_2.a % 25) = 0)
+               ->  Hash Right Join
+                     Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c
+                     Hash Cond: (((t3_2.a + t3_2.b) / 2) = t2_2.b)
+                     ->  Seq Scan on public.prt1_e_p3 t3_2
+                           Output: t3_2.a, t3_2.b, t3_2.c
+                     ->  Hash
+                           Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c
+                           ->  Hash Right Join
+                                 Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c
+                                 Hash Cond: (t2_2.b = t1_1.a)
+                                 ->  Seq Scan on public.prt2_p3 t2_2
+                                       Output: t2_2.b, t2_2.c
+                                 ->  Hash
+                                       Output: t1_1.a, t1_1.c
+                                       ->  Seq Scan on public.prt1_p3 t1_1
+                                             Output: t1_1.a, t1_1.c
+                                             Filter: ((t1_1.a % 25) = 0)
+(57 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 (t2.b = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 2) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 2 4) is considered for partition-wise join.
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+  50 | 0050 |     |      |          | 
+ 100 | 0100 |     |      |          | 
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 200 | 0200 |     |      |          | 
+ 250 | 0250 |     |      |          | 
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 350 | 0350 |     |      |          | 
+ 400 | 0400 |     |      |          | 
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+ 500 | 0500 |     |      |          | 
+ 550 | 0550 |     |      |          | 
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) LEFT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+  50 | 0050 |     |      |          | 
+ 100 | 0100 |     |      |          | 
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 200 | 0200 |     |      |          | 
+ 250 | 0250 |     |      |          | 
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 350 | 0350 |     |      |          | 
+ 400 | 0400 |     |      |          | 
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+ 500 | 0500 |     |      |          | 
+ 550 | 0550 |     |      |          | 
+(12 rows)
+
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+                                     QUERY PLAN                                     
+------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+   Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+         ->  Append
+               ->  Hash Right Join
+                     Output: t3.a, t3.b, t3.c, t1.a, t1.c, t2.b, t2.c
+                     Hash Cond: (t2.b = t1.a)
+                     ->  Seq Scan on public.prt2_p1 t2
+                           Output: t2.b, t2.c
+                     ->  Hash
+                           Output: t3.a, t3.b, t3.c, t1.a, t1.c
+                           ->  Hash Right Join
+                                 Output: t3.a, t3.b, t3.c, t1.a, t1.c
+                                 Hash Cond: (t1.a = ((t3.a + t3.b) / 2))
+                                 ->  Seq Scan on public.prt1_p1 t1
+                                       Output: t1.a, t1.c
+                                 ->  Hash
+                                       Output: t3.a, t3.b, t3.c
+                                       ->  Seq Scan on public.prt1_e_p1 t3
+                                             Output: t3.a, t3.b, t3.c
+                                             Filter: ((t3.a % 25) = 0)
+               ->  Hash Right Join
+                     Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c, t2_1.b, t2_1.c
+                     Hash Cond: (t2_1.b = t1_2.a)
+                     ->  Seq Scan on public.prt2_p2 t2_1
+                           Output: t2_1.b, t2_1.c
+                     ->  Hash
+                           Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c
+                           ->  Hash Right Join
+                                 Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c
+                                 Hash Cond: (t1_2.a = ((t3_1.a + t3_1.b) / 2))
+                                 ->  Seq Scan on public.prt1_p2 t1_2
+                                       Output: t1_2.a, t1_2.c
+                                 ->  Hash
+                                       Output: t3_1.a, t3_1.b, t3_1.c
+                                       ->  Seq Scan on public.prt1_e_p2 t3_1
+                                             Output: t3_1.a, t3_1.b, t3_1.c
+                                             Filter: ((t3_1.a % 25) = 0)
+               ->  Hash Right Join
+                     Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c, t2_2.b, t2_2.c
+                     Hash Cond: (t2_2.b = t1_1.a)
+                     ->  Seq Scan on public.prt2_p3 t2_2
+                           Output: t2_2.b, t2_2.c
+                     ->  Hash
+                           Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c
+                           ->  Hash Right Join
+                                 Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c
+                                 Hash Cond: (t1_1.a = ((t3_2.a + t3_2.b) / 2))
+                                 ->  Seq Scan on public.prt1_p3 t1_1
+                                       Output: t1_1.a, t1_1.c
+                                 ->  Hash
+                                       Output: t3_2.a, t3_2.b, t3_2.c
+                                       ->  Seq Scan on public.prt1_e_p3 t3_2
+                                             Output: t3_2.a, t3_2.b, t3_2.c
+                                             Filter: ((t3_2.a % 25) = 0)
+(57 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+  50 | 0050 |     |      |      100 | 0050
+ 100 | 0100 |     |      |      200 | 0100
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 200 | 0200 |     |      |      400 | 0200
+ 250 | 0250 |     |      |      500 | 0250
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 350 | 0350 |     |      |      700 | 0350
+ 400 | 0400 |     |      |      800 | 0400
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+ 500 | 0500 |     |      |     1000 | 0500
+ 550 | 0550 |     |      |     1100 | 0550
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+  50 | 0050 |     |      |      100 | 0050
+ 100 | 0100 |     |      |      200 | 0100
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 200 | 0200 |     |      |      400 | 0200
+ 250 | 0250 |     |      |      500 | 0250
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 350 | 0350 |     |      |      700 | 0350
+ 400 | 0400 |     |      |      800 | 0400
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+ 500 | 0500 |     |      |     1000 | 0500
+ 550 | 0550 |     |      |     1100 | 0550
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+                                     QUERY PLAN                                     
+------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+   Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+         ->  Append
+               ->  Hash Right Join
+                     Output: t3.a, t3.b, t3.c, t2.b, t2.c, t1.a, t1.c
+                     Hash Cond: (t1.a = t2.b)
+                     ->  Seq Scan on public.prt1_p1 t1
+                           Output: t1.a, t1.c
+                     ->  Hash
+                           Output: t3.a, t3.b, t3.c, t2.b, t2.c
+                           ->  Hash Right Join
+                                 Output: t3.a, t3.b, t3.c, t2.b, t2.c
+                                 Hash Cond: (t2.b = ((t3.a + t3.b) / 2))
+                                 ->  Seq Scan on public.prt2_p1 t2
+                                       Output: t2.b, t2.c
+                                 ->  Hash
+                                       Output: t3.a, t3.b, t3.c
+                                       ->  Seq Scan on public.prt1_e_p1 t3
+                                             Output: t3.a, t3.b, t3.c
+                                             Filter: ((t3.a % 25) = 0)
+               ->  Hash Right Join
+                     Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c, t1_2.a, t1_2.c
+                     Hash Cond: (t1_2.a = t2_1.b)
+                     ->  Seq Scan on public.prt1_p2 t1_2
+                           Output: t1_2.a, t1_2.c
+                     ->  Hash
+                           Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+                           ->  Hash Right Join
+                                 Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+                                 Hash Cond: (t2_1.b = ((t3_1.a + t3_1.b) / 2))
+                                 ->  Seq Scan on public.prt2_p2 t2_1
+                                       Output: t2_1.b, t2_1.c
+                                 ->  Hash
+                                       Output: t3_1.a, t3_1.b, t3_1.c
+                                       ->  Seq Scan on public.prt1_e_p2 t3_1
+                                             Output: t3_1.a, t3_1.b, t3_1.c
+                                             Filter: ((t3_1.a % 25) = 0)
+               ->  Hash Right Join
+                     Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c, t1_1.a, t1_1.c
+                     Hash Cond: (t1_1.a = t2_2.b)
+                     ->  Seq Scan on public.prt1_p3 t1_1
+                           Output: t1_1.a, t1_1.c
+                     ->  Hash
+                           Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+                           ->  Hash Right Join
+                                 Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+                                 Hash Cond: (t2_2.b = ((t3_2.a + t3_2.b) / 2))
+                                 ->  Seq Scan on public.prt2_p3 t2_2
+                                       Output: t2_2.b, t2_2.c
+                                 ->  Hash
+                                       Output: t3_2.a, t3_2.b, t3_2.c
+                                       ->  Seq Scan on public.prt1_e_p3 t3_2
+                                             Output: t3_2.a, t3_2.b, t3_2.c
+                                             Filter: ((t3_2.a % 25) = 0)
+(57 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+     |      |     |      |      100 | 0050
+     |      |     |      |      200 | 0100
+     |      |     |      |      400 | 0200
+     |      |     |      |      500 | 0250
+     |      |     |      |      700 | 0350
+     |      |     |      |      800 | 0400
+     |      |     |      |     1000 | 0500
+     |      |     |      |     1100 | 0550
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+     |      |     |      |      100 | 0050
+     |      |     |      |      200 | 0100
+     |      |     |      |      400 | 0200
+     |      |     |      |      500 | 0250
+     |      |     |      |      700 | 0350
+     |      |     |      |      800 | 0400
+     |      |     |      |     1000 | 0500
+     |      |     |      |     1100 | 0550
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t3 ON (t1.a = (t3.a + t3.b)/2) ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE:  join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+                                                  QUERY PLAN                                                   
+---------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c, ((prt1_e_p1.a + prt1_e_p1.b)), prt1_e_p1.c
+   Sort Key: prt1_p1.a, prt2_p1.b, ((prt1_e_p1.a + prt1_e_p1.b))
+   ->  Result
+         Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c, (prt1_e_p1.a + prt1_e_p1.b), prt1_e_p1.c
+         ->  Append
+               ->  Hash Full Join
+                     Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c, prt1_e_p1.a, prt1_e_p1.b, prt1_e_p1.c
+                     Hash Cond: (prt1_p1.a = ((prt1_e_p1.a + prt1_e_p1.b) / 2))
+                     ->  Hash Full Join
+                           Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c
+                           Hash Cond: (prt1_p1.a = prt2_p1.b)
+                           ->  Seq Scan on public.prt1_p1
+                                 Output: prt1_p1.a, prt1_p1.c
+                                 Filter: ((prt1_p1.a % 25) = 0)
+                           ->  Hash
+                                 Output: prt2_p1.b, prt2_p1.c
+                                 ->  Seq Scan on public.prt2_p1
+                                       Output: prt2_p1.b, prt2_p1.c
+                                       Filter: ((prt2_p1.b % 25) = 0)
+                     ->  Hash
+                           Output: prt1_e_p1.a, prt1_e_p1.b, prt1_e_p1.c
+                           ->  Seq Scan on public.prt1_e_p1
+                                 Output: prt1_e_p1.a, prt1_e_p1.b, prt1_e_p1.c
+                                 Filter: ((prt1_e_p1.a % 25) = 0)
+               ->  Hash Full Join
+                     Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c, prt1_e_p2.a, prt1_e_p2.b, prt1_e_p2.c
+                     Hash Cond: (prt1_p2.a = ((prt1_e_p2.a + prt1_e_p2.b) / 2))
+                     ->  Hash Full Join
+                           Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c
+                           Hash Cond: (prt1_p2.a = prt2_p2.b)
+                           ->  Seq Scan on public.prt1_p2
+                                 Output: prt1_p2.a, prt1_p2.c
+                                 Filter: ((prt1_p2.a % 25) = 0)
+                           ->  Hash
+                                 Output: prt2_p2.b, prt2_p2.c
+                                 ->  Seq Scan on public.prt2_p2
+                                       Output: prt2_p2.b, prt2_p2.c
+                                       Filter: ((prt2_p2.b % 25) = 0)
+                     ->  Hash
+                           Output: prt1_e_p2.a, prt1_e_p2.b, prt1_e_p2.c
+                           ->  Seq Scan on public.prt1_e_p2
+                                 Output: prt1_e_p2.a, prt1_e_p2.b, prt1_e_p2.c
+                                 Filter: ((prt1_e_p2.a % 25) = 0)
+               ->  Hash Full Join
+                     Output: prt1_p3.a, prt1_p3.c, prt2_p3.b, prt2_p3.c, prt1_e_p3.a, prt1_e_p3.b, prt1_e_p3.c
+                     Hash Cond: (prt1_p3.a = ((prt1_e_p3.a + prt1_e_p3.b) / 2))
+                     ->  Hash Full Join
+                           Output: prt1_p3.a, prt1_p3.c, prt2_p3.b, prt2_p3.c
+                           Hash Cond: (prt1_p3.a = prt2_p3.b)
+                           ->  Seq Scan on public.prt1_p3
+                                 Output: prt1_p3.a, prt1_p3.c
+                                 Filter: ((prt1_p3.a % 25) = 0)
+                           ->  Hash
+                                 Output: prt2_p3.b, prt2_p3.c
+                                 ->  Seq Scan on public.prt2_p3
+                                       Output: prt2_p3.b, prt2_p3.c
+                                       Filter: ((prt2_p3.b % 25) = 0)
+                     ->  Hash
+                           Output: prt1_e_p3.a, prt1_e_p3.b, prt1_e_p3.c
+                           ->  Seq Scan on public.prt1_e_p3
+                                 Output: prt1_e_p3.a, prt1_e_p3.b, prt1_e_p3.c
+                                 Filter: ((prt1_e_p3.a % 25) = 0)
+(63 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t3 ON (t1.a = (t3.a + t3.b)/2) ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE:  join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+  50 | 0050 |     |      |      100 | 0050
+ 100 | 0100 |     |      |      200 | 0100
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 200 | 0200 |     |      |      400 | 0200
+ 250 | 0250 |     |      |      500 | 0250
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 350 | 0350 |     |      |      700 | 0350
+ 400 | 0400 |     |      |      800 | 0400
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+ 500 | 0500 |     |      |     1000 | 0500
+ 550 | 0550 |     |      |     1100 | 0550
+     |      |  75 | 0075 |          | 
+     |      | 225 | 0225 |          | 
+     |      | 375 | 0375 |          | 
+     |      | 525 | 0525 |          | 
+(16 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT * FROM uprt1_e WHERE uprt1_e.a % 25 = 0) t3 ON (t1.a = (t3.a + t3.b)/2) ORDER BY t1.a, t2.b, t3.a + t3.b;
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+  50 | 0050 |     |      |      100 | 0050
+ 100 | 0100 |     |      |      200 | 0100
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 200 | 0200 |     |      |      400 | 0200
+ 250 | 0250 |     |      |      500 | 0250
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 350 | 0350 |     |      |      700 | 0350
+ 400 | 0400 |     |      |      800 | 0400
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+ 500 | 0500 |     |      |     1000 | 0500
+ 550 | 0550 |     |      |     1100 | 0550
+     |      |  75 | 0075 |          | 
+     |      | 225 | 0225 |          | 
+     |      | 375 | 0375 |          | 
+     |      | 525 | 0525 |          | 
+(16 rows)
+
+-- Cases with non-nullable expressions in subquery results;
+-- make sure these go to null as expected
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.a % 25 = 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;
+NOTICE:  join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE:  join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+                                                      QUERY PLAN                                                      
+----------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: prt1_p1.a, (50), prt2_p1.b, (75), ((prt1_e_p1.a + prt1_e_p1.b)), (50)
+   Sort Key: prt1_p1.a, prt2_p1.b, ((prt1_e_p1.a + prt1_e_p1.b))
+   ->  Result
+         Output: prt1_p1.a, (50), prt2_p1.b, (75), (prt1_e_p1.a + prt1_e_p1.b), (50)
+         ->  Append
+               ->  Hash Full Join
+                     Output: prt1_p1.a, prt2_p1.b, prt1_e_p1.a, prt1_e_p1.b, (50), (75), (50)
+                     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 Full Join
+                           Output: prt1_p1.a, prt2_p1.b, (50), (75)
+                           Hash Cond: (prt1_p1.a = prt2_p1.b)
+                           ->  Seq Scan on public.prt1_p1
+                                 Output: prt1_p1.a, 50
+                                 Filter: ((prt1_p1.a % 25) = 0)
+                           ->  Hash
+                                 Output: prt2_p1.b, (75)
+                                 ->  Seq Scan on public.prt2_p1
+                                       Output: prt2_p1.b, 75
+                                       Filter: ((prt2_p1.b % 25) = 0)
+                     ->  Hash
+                           Output: prt1_e_p1.a, prt1_e_p1.b, (50)
+                           ->  Seq Scan on public.prt1_e_p1
+                                 Output: prt1_e_p1.a, prt1_e_p1.b, 50
+                                 Filter: ((prt1_e_p1.a % 25) = 0)
+               ->  Hash Full Join
+                     Output: prt1_p2.a, prt2_p2.b, prt1_e_p2.a, prt1_e_p2.b, (50), (75), (50)
+                     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)))
+                     ->  Hash Full Join
+                           Output: prt1_p2.a, prt2_p2.b, (50), (75)
+                           Hash Cond: (prt1_p2.a = prt2_p2.b)
+                           ->  Seq Scan on public.prt1_p2
+                                 Output: prt1_p2.a, 50
+                                 Filter: ((prt1_p2.a % 25) = 0)
+                           ->  Hash
+                                 Output: prt2_p2.b, (75)
+                                 ->  Seq Scan on public.prt2_p2
+                                       Output: prt2_p2.b, 75
+                                       Filter: ((prt2_p2.b % 25) = 0)
+                     ->  Hash
+                           Output: prt1_e_p2.a, prt1_e_p2.b, (50)
+                           ->  Seq Scan on public.prt1_e_p2
+                                 Output: prt1_e_p2.a, prt1_e_p2.b, 50
+                                 Filter: ((prt1_e_p2.a % 25) = 0)
+               ->  Hash Full Join
+                     Output: prt1_p3.a, prt2_p3.b, prt1_e_p3.a, prt1_e_p3.b, (50), (75), (50)
+                     Hash Cond: (prt1_p3.a = ((prt1_e_p3.a + prt1_e_p3.b) / 2))
+                     Filter: ((prt1_p3.a = (50)) OR (prt2_p3.b = (75)) OR (((prt1_e_p3.a + prt1_e_p3.b) / 2) = (50)))
+                     ->  Hash Full Join
+                           Output: prt1_p3.a, prt2_p3.b, (50), (75)
+                           Hash Cond: (prt1_p3.a = prt2_p3.b)
+                           ->  Seq Scan on public.prt1_p3
+                                 Output: prt1_p3.a, 50
+                                 Filter: ((prt1_p3.a % 25) = 0)
+                           ->  Hash
+                                 Output: prt2_p3.b, (75)
+                                 ->  Seq Scan on public.prt2_p3
+                                       Output: prt2_p3.b, 75
+                                       Filter: ((prt2_p3.b % 25) = 0)
+                     ->  Hash
+                           Output: prt1_e_p3.a, prt1_e_p3.b, (50)
+                           ->  Seq Scan on public.prt1_e_p3
+                                 Output: prt1_e_p3.a, prt1_e_p3.b, 50
+                                 Filter: ((prt1_e_p3.a % 25) = 0)
+(66 rows)
+
+SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.a % 25 = 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;
+NOTICE:  join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE:  join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+ a  | phv | b  | phv | ?column? | phv 
+----+-----+----+-----+----------+-----
+ 50 |  50 |    |     |      100 |  50
+    |     | 75 |  75 |          |    
+(2 rows)
+
+SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM uprt1_e WHERE uprt1_e.a % 25 = 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 
+----+-----+----+-----+----------+-----
+ 50 |  50 |    |     |      100 |  50
+    |     | 75 |  75 |          |    
+(2 rows)
+
+-- Semi-join
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.b % 25 = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 3) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3 4) is considered for partition-wise join.
+                               QUERY PLAN                                
+-------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.b, t1.c
+   Sort Key: t1.a
+   ->  Append
+         ->  Nested Loop Semi Join
+               Output: t1.a, t1.b, t1.c
+               Join Filter: (t1.a = t1_3.b)
+               ->  Seq Scan on public.prt1_p1 t1
+                     Output: t1.a, t1.b, t1.c
+                     Filter: ((t1.a % 25) = 0)
+               ->  Materialize
+                     Output: t1_3.b, t2.a, t2.b
+                     ->  Hash Join
+                           Output: t1_3.b, t2.a, t2.b
+                           Hash Cond: (((t2.a + t2.b) / 2) = t1_3.b)
+                           ->  Seq Scan on public.prt1_e_p1 t2
+                                 Output: t2.a, t2.b
+                           ->  Hash
+                                 Output: t1_3.b
+                                 ->  Seq Scan on public.prt2_p1 t1_3
+                                       Output: t1_3.b
+                                       Filter: ((t1_3.b % 25) = 0)
+         ->  Nested Loop Semi Join
+               Output: t1_2.a, t1_2.b, t1_2.c
+               Join Filter: (t1_2.a = t1_4.b)
+               ->  Seq Scan on public.prt1_p2 t1_2
+                     Output: t1_2.a, t1_2.b, t1_2.c
+                     Filter: ((t1_2.a % 25) = 0)
+               ->  Materialize
+                     Output: t1_4.b, t2_1.a, t2_1.b
+                     ->  Hash Join
+                           Output: t1_4.b, t2_1.a, t2_1.b
+                           Hash Cond: (((t2_1.a + t2_1.b) / 2) = t1_4.b)
+                           ->  Seq Scan on public.prt1_e_p2 t2_1
+                                 Output: t2_1.a, t2_1.b
+                           ->  Hash
+                                 Output: t1_4.b
+                                 ->  Seq Scan on public.prt2_p2 t1_4
+                                       Output: t1_4.b
+                                       Filter: ((t1_4.b % 25) = 0)
+         ->  Nested Loop Semi Join
+               Output: t1_1.a, t1_1.b, t1_1.c
+               Join Filter: (t1_1.a = t1_5.b)
+               ->  Seq Scan on public.prt1_p3 t1_1
+                     Output: t1_1.a, t1_1.b, t1_1.c
+                     Filter: ((t1_1.a % 25) = 0)
+               ->  Materialize
+                     Output: t1_5.b, t2_2.a, t2_2.b
+                     ->  Hash Join
+                           Output: t1_5.b, t2_2.a, t2_2.b
+                           Hash Cond: (((t2_2.a + t2_2.b) / 2) = t1_5.b)
+                           ->  Seq Scan on public.prt1_e_p3 t2_2
+                                 Output: t2_2.a, t2_2.b
+                           ->  Hash
+                                 Output: t1_5.b
+                                 ->  Seq Scan on public.prt2_p3 t1_5
+                                       Output: t1_5.b
+                                       Filter: ((t1_5.b % 25) = 0)
+(58 rows)
+
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.b % 25 = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 3) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3 4) is considered for partition-wise join.
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1, uprt1_e t2 WHERE t1.b % 25 = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.a % 25 = 0 ORDER BY t1.a;
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+EXPLAIN (VERBOSE, 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+                               QUERY PLAN                                
+-------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.b, t1.c
+   Sort Key: t1.a
+   ->  Append
+         ->  Nested Loop Semi Join
+               Output: t1.a, t1.b, t1.c
+               Join Filter: (t1.a = t1_3.b)
+               ->  Seq Scan on public.prt1_p1 t1
+                     Output: t1.a, t1.b, t1.c
+                     Filter: ((t1.a % 25) = 0)
+               ->  Materialize
+                     Output: t1_3.b, t1_6.a, t1_6.b
+                     ->  Hash Semi Join
+                           Output: t1_3.b, t1_6.a, t1_6.b
+                           Hash Cond: (t1_3.b = ((t1_6.a + t1_6.b) / 2))
+                           ->  Seq Scan on public.prt2_p1 t1_3
+                                 Output: t1_3.b
+                           ->  Hash
+                                 Output: t1_6.a, t1_6.b
+                                 ->  Seq Scan on public.prt1_e_p1 t1_6
+                                       Output: t1_6.a, t1_6.b
+                                       Filter: ((t1_6.a % 25) = 0)
+         ->  Nested Loop Semi Join
+               Output: t1_2.a, t1_2.b, t1_2.c
+               Join Filter: (t1_2.a = t1_4.b)
+               ->  Seq Scan on public.prt1_p2 t1_2
+                     Output: t1_2.a, t1_2.b, t1_2.c
+                     Filter: ((t1_2.a % 25) = 0)
+               ->  Materialize
+                     Output: t1_4.b, t1_7.a, t1_7.b
+                     ->  Hash Semi Join
+                           Output: t1_4.b, t1_7.a, t1_7.b
+                           Hash Cond: (t1_4.b = ((t1_7.a + t1_7.b) / 2))
+                           ->  Seq Scan on public.prt2_p2 t1_4
+                                 Output: t1_4.b
+                           ->  Hash
+                                 Output: t1_7.a, t1_7.b
+                                 ->  Seq Scan on public.prt1_e_p2 t1_7
+                                       Output: t1_7.a, t1_7.b
+                                       Filter: ((t1_7.a % 25) = 0)
+         ->  Nested Loop Semi Join
+               Output: t1_1.a, t1_1.b, t1_1.c
+               Join Filter: (t1_1.a = t1_5.b)
+               ->  Seq Scan on public.prt1_p3 t1_1
+                     Output: t1_1.a, t1_1.b, t1_1.c
+                     Filter: ((t1_1.a % 25) = 0)
+               ->  Materialize
+                     Output: t1_5.b, t1_8.a, t1_8.b
+                     ->  Hash Semi Join
+                           Output: t1_5.b, t1_8.a, t1_8.b
+                           Hash Cond: (t1_5.b = ((t1_8.a + t1_8.b) / 2))
+                           ->  Seq Scan on public.prt2_p3 t1_5
+                                 Output: t1_5.b
+                           ->  Hash
+                                 Output: t1_8.a, t1_8.b
+                                 ->  Seq Scan on public.prt1_e_p3 t1_8
+                                       Output: t1_8.a, t1_8.b
+                                       Filter: ((t1_8.a % 25) = 0)
+(58 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM uprt1_e t1 WHERE t1.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+-- test merge joins with and without using indexes
+SET enable_hashjoin TO off;
+SET enable_nestloop TO off;
+CREATE INDEX iprt1_a on prt1(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 iprt2_b on prt2(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 iprt1_e_ab2 on prt1_e(((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));
+ANALYZE prt1;
+ANALYZE prt1_p1;
+ANALYZE prt1_p2;
+ANALYZE prt1_p3;
+ANALYZE prt2;
+ANALYZE prt2_p1;
+ANALYZE prt2_p2;
+ANALYZE prt2_p3;
+ANALYZE prt1_e;
+ANALYZE prt1_e_p1;
+ANALYZE prt1_e_p2;
+ANALYZE prt1_e_p3;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+                                             QUERY PLAN                                              
+-----------------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+   Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+         ->  Append
+               ->  Merge Left Join
+                     Output: t3.a, t3.b, t3.c, t2.b, t2.c, t1.a, t1.c
+                     Merge Cond: (t2.b = t1.a)
+                     ->  Sort
+                           Output: t3.a, t3.b, t3.c, t2.b, t2.c
+                           Sort Key: t2.b
+                           ->  Merge Left Join
+                                 Output: t3.a, t3.b, t3.c, t2.b, t2.c
+                                 Merge Cond: ((((t3.a + t3.b) / 2)) = t2.b)
+                                 ->  Sort
+                                       Output: t3.a, t3.b, t3.c, (((t3.a + t3.b) / 2))
+                                       Sort Key: (((t3.a + t3.b) / 2))
+                                       ->  Seq Scan on public.prt1_e_p1 t3
+                                             Output: t3.a, t3.b, t3.c, ((t3.a + t3.b) / 2)
+                                             Filter: ((t3.a % 25) = 0)
+                                 ->  Sort
+                                       Output: t2.b, t2.c
+                                       Sort Key: t2.b
+                                       ->  Seq Scan on public.prt2_p1 t2
+                                             Output: t2.b, t2.c
+                     ->  Sort
+                           Output: t1.a, t1.c
+                           Sort Key: t1.a
+                           ->  Seq Scan on public.prt1_p1 t1
+                                 Output: t1.a, t1.c
+               ->  Merge Left Join
+                     Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c, t1_2.a, t1_2.c
+                     Merge Cond: (t2_1.b = t1_2.a)
+                     ->  Sort
+                           Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+                           Sort Key: t2_1.b
+                           ->  Merge Left Join
+                                 Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+                                 Merge Cond: ((((t3_1.a + t3_1.b) / 2)) = t2_1.b)
+                                 ->  Sort
+                                       Output: t3_1.a, t3_1.b, t3_1.c, (((t3_1.a + t3_1.b) / 2))
+                                       Sort Key: (((t3_1.a + t3_1.b) / 2))
+                                       ->  Seq Scan on public.prt1_e_p2 t3_1
+                                             Output: t3_1.a, t3_1.b, t3_1.c, ((t3_1.a + t3_1.b) / 2)
+                                             Filter: ((t3_1.a % 25) = 0)
+                                 ->  Sort
+                                       Output: t2_1.b, t2_1.c
+                                       Sort Key: t2_1.b
+                                       ->  Seq Scan on public.prt2_p2 t2_1
+                                             Output: t2_1.b, t2_1.c
+                     ->  Sort
+                           Output: t1_2.a, t1_2.c
+                           Sort Key: t1_2.a
+                           ->  Seq Scan on public.prt1_p2 t1_2
+                                 Output: t1_2.a, t1_2.c
+               ->  Merge Left Join
+                     Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c, t1_1.a, t1_1.c
+                     Merge Cond: (t2_2.b = t1_1.a)
+                     ->  Sort
+                           Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+                           Sort Key: t2_2.b
+                           ->  Merge Left Join
+                                 Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+                                 Merge Cond: ((((t3_2.a + t3_2.b) / 2)) = t2_2.b)
+                                 ->  Sort
+                                       Output: t3_2.a, t3_2.b, t3_2.c, (((t3_2.a + t3_2.b) / 2))
+                                       Sort Key: (((t3_2.a + t3_2.b) / 2))
+                                       ->  Seq Scan on public.prt1_e_p3 t3_2
+                                             Output: t3_2.a, t3_2.b, t3_2.c, ((t3_2.a + t3_2.b) / 2)
+                                             Filter: ((t3_2.a % 25) = 0)
+                                 ->  Sort
+                                       Output: t2_2.b, t2_2.c
+                                       Sort Key: t2_2.b
+                                       ->  Seq Scan on public.prt2_p3 t2_2
+                                             Output: t2_2.b, t2_2.c
+                     ->  Sort
+                           Output: t1_1.a, t1_1.c
+                           Sort Key: t1_1.a
+                           ->  Seq Scan on public.prt1_p3 t1_1
+                                 Output: t1_1.a, t1_1.c
+(81 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+     |      |     |      |      100 | 0050
+     |      |     |      |      200 | 0100
+     |      |     |      |      400 | 0200
+     |      |     |      |      500 | 0250
+     |      |     |      |      700 | 0350
+     |      |     |      |      800 | 0400
+     |      |     |      |     1000 | 0500
+     |      |     |      |     1100 | 0550
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+     |      |     |      |      100 | 0050
+     |      |     |      |      200 | 0100
+     |      |     |      |      400 | 0200
+     |      |     |      |      500 | 0250
+     |      |     |      |      700 | 0350
+     |      |     |      |      800 | 0400
+     |      |     |      |     1000 | 0500
+     |      |     |      |     1100 | 0550
+(12 rows)
+
+EXPLAIN (VERBOSE, 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+                                   QUERY PLAN                                    
+---------------------------------------------------------------------------------
+ Merge Append
+   Sort Key: t1.a
+   ->  Merge Semi Join
+         Output: t1.a, t1.b, t1.c
+         Merge Cond: (t1.a = t1_3.b)
+         ->  Sort
+               Output: t1.a, t1.b, t1.c
+               Sort Key: t1.a
+               ->  Seq Scan on public.prt1_p1 t1
+                     Output: t1.a, t1.b, t1.c
+                     Filter: ((t1.a % 25) = 0)
+         ->  Materialize
+               Output: t1_3.b, t1_6.a, t1_6.b
+               ->  Merge Semi Join
+                     Output: t1_3.b, t1_6.a, t1_6.b
+                     Merge Cond: (t1_3.b = (((t1_6.a + t1_6.b) / 2)))
+                     ->  Sort
+                           Output: t1_3.b
+                           Sort Key: t1_3.b
+                           ->  Seq Scan on public.prt2_p1 t1_3
+                                 Output: t1_3.b
+                     ->  Sort
+                           Output: t1_6.a, t1_6.b, (((t1_6.a + t1_6.b) / 2))
+                           Sort Key: (((t1_6.a + t1_6.b) / 2))
+                           ->  Seq Scan on public.prt1_e_p1 t1_6
+                                 Output: t1_6.a, t1_6.b, ((t1_6.a + t1_6.b) / 2)
+                                 Filter: ((t1_6.a % 25) = 0)
+   ->  Merge Semi Join
+         Output: t1_2.a, t1_2.b, t1_2.c
+         Merge Cond: (t1_2.a = t1_4.b)
+         ->  Sort
+               Output: t1_2.a, t1_2.b, t1_2.c
+               Sort Key: t1_2.a
+               ->  Seq Scan on public.prt1_p2 t1_2
+                     Output: t1_2.a, t1_2.b, t1_2.c
+                     Filter: ((t1_2.a % 25) = 0)
+         ->  Materialize
+               Output: t1_4.b, t1_7.a, t1_7.b
+               ->  Merge Semi Join
+                     Output: t1_4.b, t1_7.a, t1_7.b
+                     Merge Cond: (t1_4.b = (((t1_7.a + t1_7.b) / 2)))
+                     ->  Sort
+                           Output: t1_4.b
+                           Sort Key: t1_4.b
+                           ->  Seq Scan on public.prt2_p2 t1_4
+                                 Output: t1_4.b
+                     ->  Sort
+                           Output: t1_7.a, t1_7.b, (((t1_7.a + t1_7.b) / 2))
+                           Sort Key: (((t1_7.a + t1_7.b) / 2))
+                           ->  Seq Scan on public.prt1_e_p2 t1_7
+                                 Output: t1_7.a, t1_7.b, ((t1_7.a + t1_7.b) / 2)
+                                 Filter: ((t1_7.a % 25) = 0)
+   ->  Merge Semi Join
+         Output: t1_1.a, t1_1.b, t1_1.c
+         Merge Cond: (t1_1.a = t1_5.b)
+         ->  Sort
+               Output: t1_1.a, t1_1.b, t1_1.c
+               Sort Key: t1_1.a
+               ->  Seq Scan on public.prt1_p3 t1_1
+                     Output: t1_1.a, t1_1.b, t1_1.c
+                     Filter: ((t1_1.a % 25) = 0)
+         ->  Materialize
+               Output: t1_5.b, t1_8.a, t1_8.b
+               ->  Merge Semi Join
+                     Output: t1_5.b, t1_8.a, t1_8.b
+                     Merge Cond: (t1_5.b = (((t1_8.a + t1_8.b) / 2)))
+                     ->  Sort
+                           Output: t1_5.b
+                           Sort Key: t1_5.b
+                           ->  Seq Scan on public.prt2_p3 t1_5
+                                 Output: t1_5.b
+                     ->  Sort
+                           Output: t1_8.a, t1_8.b, (((t1_8.a + t1_8.b) / 2))
+                           Sort Key: (((t1_8.a + t1_8.b) / 2))
+                           ->  Seq Scan on public.prt1_e_p3 t1_8
+                                 Output: t1_8.a, t1_8.b, ((t1_8.a + t1_8.b) / 2)
+                                 Filter: ((t1_8.a % 25) = 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM uprt1_e t1 WHERE t1.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+                                             QUERY PLAN                                              
+-----------------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+   Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+         ->  Append
+               ->  Merge Left Join
+                     Output: t3.a, t3.b, t3.c, t1.a, t1.c, t2.b, t2.c
+                     Merge Cond: (t1.a = t2.b)
+                     ->  Sort
+                           Output: t3.a, t3.b, t3.c, t1.a, t1.c
+                           Sort Key: t1.a
+                           ->  Merge Left Join
+                                 Output: t3.a, t3.b, t3.c, t1.a, t1.c
+                                 Merge Cond: ((((t3.a + t3.b) / 2)) = t1.a)
+                                 ->  Sort
+                                       Output: t3.a, t3.b, t3.c, (((t3.a + t3.b) / 2))
+                                       Sort Key: (((t3.a + t3.b) / 2))
+                                       ->  Seq Scan on public.prt1_e_p1 t3
+                                             Output: t3.a, t3.b, t3.c, ((t3.a + t3.b) / 2)
+                                             Filter: ((t3.a % 25) = 0)
+                                 ->  Sort
+                                       Output: t1.a, t1.c
+                                       Sort Key: t1.a
+                                       ->  Seq Scan on public.prt1_p1 t1
+                                             Output: t1.a, t1.c
+                     ->  Sort
+                           Output: t2.b, t2.c
+                           Sort Key: t2.b
+                           ->  Seq Scan on public.prt2_p1 t2
+                                 Output: t2.b, t2.c
+               ->  Merge Left Join
+                     Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c, t2_1.b, t2_1.c
+                     Merge Cond: (t1_2.a = t2_1.b)
+                     ->  Sort
+                           Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c
+                           Sort Key: t1_2.a
+                           ->  Merge Left Join
+                                 Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c
+                                 Merge Cond: ((((t3_1.a + t3_1.b) / 2)) = t1_2.a)
+                                 ->  Sort
+                                       Output: t3_1.a, t3_1.b, t3_1.c, (((t3_1.a + t3_1.b) / 2))
+                                       Sort Key: (((t3_1.a + t3_1.b) / 2))
+                                       ->  Seq Scan on public.prt1_e_p2 t3_1
+                                             Output: t3_1.a, t3_1.b, t3_1.c, ((t3_1.a + t3_1.b) / 2)
+                                             Filter: ((t3_1.a % 25) = 0)
+                                 ->  Sort
+                                       Output: t1_2.a, t1_2.c
+                                       Sort Key: t1_2.a
+                                       ->  Seq Scan on public.prt1_p2 t1_2
+                                             Output: t1_2.a, t1_2.c
+                     ->  Sort
+                           Output: t2_1.b, t2_1.c
+                           Sort Key: t2_1.b
+                           ->  Seq Scan on public.prt2_p2 t2_1
+                                 Output: t2_1.b, t2_1.c
+               ->  Merge Left Join
+                     Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c, t2_2.b, t2_2.c
+                     Merge Cond: (t1_1.a = t2_2.b)
+                     ->  Sort
+                           Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c
+                           Sort Key: t1_1.a
+                           ->  Merge Left Join
+                                 Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c
+                                 Merge Cond: ((((t3_2.a + t3_2.b) / 2)) = t1_1.a)
+                                 ->  Sort
+                                       Output: t3_2.a, t3_2.b, t3_2.c, (((t3_2.a + t3_2.b) / 2))
+                                       Sort Key: (((t3_2.a + t3_2.b) / 2))
+                                       ->  Seq Scan on public.prt1_e_p3 t3_2
+                                             Output: t3_2.a, t3_2.b, t3_2.c, ((t3_2.a + t3_2.b) / 2)
+                                             Filter: ((t3_2.a % 25) = 0)
+                                 ->  Sort
+                                       Output: t1_1.a, t1_1.c
+                                       Sort Key: t1_1.a
+                                       ->  Seq Scan on public.prt1_p3 t1_1
+                                             Output: t1_1.a, t1_1.c
+                     ->  Sort
+                           Output: t2_2.b, t2_2.c
+                           Sort Key: t2_2.b
+                           ->  Seq Scan on public.prt2_p3 t2_2
+                                 Output: t2_2.b, t2_2.c
+(81 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+  50 | 0050 |     |      |      100 | 0050
+ 100 | 0100 |     |      |      200 | 0100
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 200 | 0200 |     |      |      400 | 0200
+ 250 | 0250 |     |      |      500 | 0250
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 350 | 0350 |     |      |      700 | 0350
+ 400 | 0400 |     |      |      800 | 0400
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+ 500 | 0500 |     |      |     1000 | 0500
+ 550 | 0550 |     |      |     1100 | 0550
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+  50 | 0050 |     |      |      100 | 0050
+ 100 | 0100 |     |      |      200 | 0100
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 200 | 0200 |     |      |      400 | 0200
+ 250 | 0250 |     |      |      500 | 0250
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 350 | 0350 |     |      |      700 | 0350
+ 400 | 0400 |     |      |      800 | 0400
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+ 500 | 0500 |     |      |     1000 | 0500
+ 550 | 0550 |     |      |     1100 | 0550
+(12 rows)
+
+SET enable_seqscan TO off;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+                                          QUERY PLAN                                          
+----------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+   Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+         ->  Append
+               ->  Merge Left Join
+                     Output: t3.a, t3.b, t3.c, t2.b, t2.c, t1.a, t1.c
+                     Merge Cond: (t2.b = t1.a)
+                     ->  Sort
+                           Output: t3.a, t3.b, t3.c, t2.b, t2.c
+                           Sort Key: t2.b
+                           ->  Merge Left Join
+                                 Output: t3.a, t3.b, t3.c, t2.b, t2.c
+                                 Merge Cond: (((t3.a + t3.b) / 2) = t2.b)
+                                 ->  Index Scan using iprt1_e_p1_ab2 on public.prt1_e_p1 t3
+                                       Output: t3.a, t3.b, t3.c
+                                       Filter: ((t3.a % 25) = 0)
+                                 ->  Index Scan using iprt2_p1_b on public.prt2_p1 t2
+                                       Output: t2.b, t2.c
+                     ->  Index Scan using iprt1_p1_a on public.prt1_p1 t1
+                           Output: t1.a, t1.c
+               ->  Merge Left Join
+                     Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c, t1_2.a, t1_2.c
+                     Merge Cond: (t2_1.b = t1_2.a)
+                     ->  Sort
+                           Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+                           Sort Key: t2_1.b
+                           ->  Merge Left Join
+                                 Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+                                 Merge Cond: (((t3_1.a + t3_1.b) / 2) = t2_1.b)
+                                 ->  Index Scan using iprt1_e_p2_ab2 on public.prt1_e_p2 t3_1
+                                       Output: t3_1.a, t3_1.b, t3_1.c
+                                       Filter: ((t3_1.a % 25) = 0)
+                                 ->  Index Scan using iprt2_p2_b on public.prt2_p2 t2_1
+                                       Output: t2_1.b, t2_1.c
+                     ->  Index Scan using iprt1_p2_a on public.prt1_p2 t1_2
+                           Output: t1_2.a, t1_2.c
+               ->  Merge Right Join
+                     Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c, t1_1.a, t1_1.c
+                     Merge Cond: (t2_2.b = ((t3_2.a + t3_2.b) / 2))
+                     ->  Merge Left Join
+                           Output: t2_2.b, t2_2.c, t1_1.a, t1_1.c
+                           Merge Cond: (t2_2.b = t1_1.a)
+                           ->  Index Scan using iprt2_p3_b on public.prt2_p3 t2_2
+                                 Output: t2_2.b, t2_2.c
+                           ->  Index Scan using iprt1_p3_a on public.prt1_p3 t1_1
+                                 Output: t1_1.a, t1_1.c
+                     ->  Index Scan using iprt1_e_p3_ab2 on public.prt1_e_p3 t3_2
+                           Output: t3_2.a, t3_2.b, t3_2.c
+                           Filter: ((t3_2.a % 25) = 0)
+(51 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+     |      |     |      |      100 | 0050
+     |      |     |      |      200 | 0100
+     |      |     |      |      400 | 0200
+     |      |     |      |      500 | 0250
+     |      |     |      |      700 | 0350
+     |      |     |      |      800 | 0400
+     |      |     |      |     1000 | 0500
+     |      |     |      |     1100 | 0550
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+     |      |     |      |      100 | 0050
+     |      |     |      |      200 | 0100
+     |      |     |      |      400 | 0200
+     |      |     |      |      500 | 0250
+     |      |     |      |      700 | 0350
+     |      |     |      |      800 | 0400
+     |      |     |      |     1000 | 0500
+     |      |     |      |     1100 | 0550
+(12 rows)
+
+EXPLAIN (VERBOSE, 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+                                    QUERY PLAN                                    
+----------------------------------------------------------------------------------
+ Merge Append
+   Sort Key: t1.a
+   ->  Merge Semi Join
+         Output: t1.a, t1.b, t1.c
+         Merge Cond: (t1.a = t1_3.b)
+         ->  Index Scan using iprt1_p1_a on public.prt1_p1 t1
+               Output: t1.a, t1.b, t1.c
+               Filter: ((t1.a % 25) = 0)
+         ->  Materialize
+               Output: t1_3.b, t1_6.a, t1_6.b
+               ->  Merge Semi Join
+                     Output: t1_3.b, t1_6.a, t1_6.b
+                     Merge Cond: (t1_3.b = ((t1_6.a + t1_6.b) / 2))
+                     ->  Index Only Scan using iprt2_p1_b on public.prt2_p1 t1_3
+                           Output: t1_3.b
+                     ->  Index Scan using iprt1_e_p1_ab2 on public.prt1_e_p1 t1_6
+                           Output: t1_6.a, t1_6.b
+                           Filter: ((t1_6.a % 25) = 0)
+   ->  Merge Semi Join
+         Output: t1_2.a, t1_2.b, t1_2.c
+         Merge Cond: (t1_2.a = t1_4.b)
+         ->  Index Scan using iprt1_p2_a on public.prt1_p2 t1_2
+               Output: t1_2.a, t1_2.b, t1_2.c
+               Filter: ((t1_2.a % 25) = 0)
+         ->  Materialize
+               Output: t1_4.b, t1_7.a, t1_7.b
+               ->  Merge Semi Join
+                     Output: t1_4.b, t1_7.a, t1_7.b
+                     Merge Cond: (t1_4.b = ((t1_7.a + t1_7.b) / 2))
+                     ->  Index Only Scan using iprt2_p2_b on public.prt2_p2 t1_4
+                           Output: t1_4.b
+                     ->  Index Scan using iprt1_e_p2_ab2 on public.prt1_e_p2 t1_7
+                           Output: t1_7.a, t1_7.b
+                           Filter: ((t1_7.a % 25) = 0)
+   ->  Merge Semi Join
+         Output: t1_1.a, t1_1.b, t1_1.c
+         Merge Cond: (t1_1.a = t1_5.b)
+         ->  Index Scan using iprt1_p3_a on public.prt1_p3 t1_1
+               Output: t1_1.a, t1_1.b, t1_1.c
+               Filter: ((t1_1.a % 25) = 0)
+         ->  Materialize
+               Output: t1_5.b, t1_8.a, t1_8.b
+               ->  Merge Semi Join
+                     Output: t1_5.b, t1_8.a, t1_8.b
+                     Merge Cond: (t1_5.b = ((t1_8.a + t1_8.b) / 2))
+                     ->  Index Only Scan using iprt2_p3_b on public.prt2_p3 t1_5
+                           Output: t1_5.b
+                     ->  Index Scan using iprt1_e_p3_ab2 on public.prt1_e_p3 t1_8
+                           Output: t1_8.a, t1_8.b
+                           Filter: ((t1_8.a % 25) = 0)
+(50 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM uprt1_e t1 WHERE t1.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+ 150 | 150 | 0150
+ 300 | 300 | 0300
+ 450 | 450 | 0450
+(4 rows)
+
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+                                          QUERY PLAN                                          
+----------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+   Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+         ->  Append
+               ->  Merge Left Join
+                     Output: t3.a, t3.b, t3.c, t1.a, t1.c, t2.b, t2.c
+                     Merge Cond: (t1.a = t2.b)
+                     ->  Sort
+                           Output: t3.a, t3.b, t3.c, t1.a, t1.c
+                           Sort Key: t1.a
+                           ->  Merge Left Join
+                                 Output: t3.a, t3.b, t3.c, t1.a, t1.c
+                                 Merge Cond: (((t3.a + t3.b) / 2) = t1.a)
+                                 ->  Index Scan using iprt1_e_p1_ab2 on public.prt1_e_p1 t3
+                                       Output: t3.a, t3.b, t3.c
+                                       Filter: ((t3.a % 25) = 0)
+                                 ->  Index Scan using iprt1_p1_a on public.prt1_p1 t1
+                                       Output: t1.a, t1.c
+                     ->  Index Scan using iprt2_p1_b on public.prt2_p1 t2
+                           Output: t2.b, t2.c
+               ->  Merge Left Join
+                     Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c, t2_1.b, t2_1.c
+                     Merge Cond: (t1_2.a = t2_1.b)
+                     ->  Sort
+                           Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c
+                           Sort Key: t1_2.a
+                           ->  Merge Left Join
+                                 Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c
+                                 Merge Cond: (((t3_1.a + t3_1.b) / 2) = t1_2.a)
+                                 ->  Index Scan using iprt1_e_p2_ab2 on public.prt1_e_p2 t3_1
+                                       Output: t3_1.a, t3_1.b, t3_1.c
+                                       Filter: ((t3_1.a % 25) = 0)
+                                 ->  Index Scan using iprt1_p2_a on public.prt1_p2 t1_2
+                                       Output: t1_2.a, t1_2.c
+                     ->  Index Scan using iprt2_p2_b on public.prt2_p2 t2_1
+                           Output: t2_1.b, t2_1.c
+               ->  Merge Left Join
+                     Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c, t2_2.b, t2_2.c
+                     Merge Cond: (t1_1.a = t2_2.b)
+                     ->  Sort
+                           Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c
+                           Sort Key: t1_1.a
+                           ->  Merge Left Join
+                                 Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c
+                                 Merge Cond: (((t3_2.a + t3_2.b) / 2) = t1_1.a)
+                                 ->  Index Scan using iprt1_e_p3_ab2 on public.prt1_e_p3 t3_2
+                                       Output: t3_2.a, t3_2.b, t3_2.c
+                                       Filter: ((t3_2.a % 25) = 0)
+                                 ->  Index Scan using iprt1_p3_a on public.prt1_p3 t1_1
+                                       Output: t1_1.a, t1_1.c
+                     ->  Index Scan using iprt2_p3_b on public.prt2_p3 t2_2
+                           Output: t2_2.b, t2_2.c
+(54 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+  50 | 0050 |     |      |      100 | 0050
+ 100 | 0100 |     |      |      200 | 0100
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 200 | 0200 |     |      |      400 | 0200
+ 250 | 0250 |     |      |      500 | 0250
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 350 | 0350 |     |      |      700 | 0350
+ 400 | 0400 |     |      |      800 | 0400
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+ 500 | 0500 |     |      |     1000 | 0500
+ 550 | 0550 |     |      |     1100 | 0550
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+  a  |  c   |  b  |  c   | ?column? |  c   
+-----+------+-----+------+----------+------
+   0 | 0000 |   0 | 0000 |        0 | 0000
+  50 | 0050 |     |      |      100 | 0050
+ 100 | 0100 |     |      |      200 | 0100
+ 150 | 0150 | 150 | 0150 |      300 | 0150
+ 200 | 0200 |     |      |      400 | 0200
+ 250 | 0250 |     |      |      500 | 0250
+ 300 | 0300 | 300 | 0300 |      600 | 0300
+ 350 | 0350 |     |      |      700 | 0350
+ 400 | 0400 |     |      |      800 | 0400
+ 450 | 0450 | 450 | 0450 |      900 | 0450
+ 500 | 0500 |     |      |     1000 | 0500
+ 550 | 0550 |     |      |     1100 | 0550
+(12 rows)
+
+-- lateral references and parameterized paths
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 4 5) is considered for partition-wise join.
+                                QUERY PLAN                                 
+---------------------------------------------------------------------------
+ Nested Loop Left Join
+   Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a)), t1.a
+   ->  Merge Append
+         Sort Key: t1.a
+         ->  Index Scan using iprt1_a on public.prt1 t1
+               Output: t1.a, t1.b, t1.c
+               Filter: ((t1.a % 25) = 0)
+         ->  Index Scan using iprt1_p1_a on public.prt1_p1 t1_1
+               Output: t1_1.a, t1_1.b, t1_1.c
+               Filter: ((t1_1.a % 25) = 0)
+         ->  Index Scan using iprt1_p3_a on public.prt1_p3 t1_2
+               Output: t1_2.a, t1_2.b, t1_2.c
+               Filter: ((t1_2.a % 25) = 0)
+         ->  Index Scan using iprt1_p2_a on public.prt1_p2 t1_3
+               Output: t1_3.a, t1_3.b, t1_3.c
+               Filter: ((t1_3.a % 25) = 0)
+   ->  Append
+         ->  Merge Join
+               Output: t2.a, t3.a, LEAST(t1.a, t2.a, t3.a)
+               Merge Cond: (t2.a = t3.b)
+               ->  Index Only Scan using iprt1_p1_a on public.prt1_p1 t2
+                     Output: t2.a
+                     Index Cond: (t2.a = t1.a)
+               ->  Index Scan using iprt2_p1_b on public.prt2_p1 t3
+                     Output: t3.a, t3.b
+         ->  Merge Join
+               Output: t2_2.a, t3_1.a, LEAST(t1.a, t2_2.a, t3_1.a)
+               Merge Cond: (t2_2.a = t3_1.b)
+               ->  Index Only Scan using iprt1_p2_a on public.prt1_p2 t2_2
+                     Output: t2_2.a
+                     Index Cond: (t2_2.a = t1.a)
+               ->  Index Scan using iprt2_p2_b on public.prt2_p2 t3_1
+                     Output: t3_1.a, t3_1.b
+         ->  Merge Join
+               Output: t2_1.a, t3_2.a, LEAST(t1.a, t2_1.a, t3_2.a)
+               Merge Cond: (t2_1.a = t3_2.b)
+               ->  Index Only Scan using iprt1_p3_a on public.prt1_p3 t2_1
+                     Output: t2_1.a
+                     Index Cond: (t2_1.a = t1.a)
+               ->  Index Scan using iprt2_p3_b on public.prt2_p3 t3_2
+                     Output: t3_2.a, t3_2.b
+(41 rows)
+
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 4 5) is considered for partition-wise join.
+  a  |  b  |  c   | t2a | t3a | least 
+-----+-----+------+-----+-----+-------
+   0 |   0 | 0000 |   0 |   0 |     0
+  50 |  50 | 0050 |     |     |      
+ 100 | 100 | 0100 |     |     |      
+ 150 | 150 | 0150 | 150 | 150 |   150
+ 200 | 200 | 0200 |     |     |      
+ 250 | 250 | 0250 |     |     |      
+ 300 | 300 | 0300 | 300 | 300 |   300
+ 350 | 350 | 0350 |     |     |      
+ 400 | 400 | 0400 |     |     |      
+ 450 | 450 | 0450 | 450 | 450 |   450
+ 500 | 500 | 0500 |     |     |      
+ 550 | 550 | 0550 |     |     |      
+(12 rows)
+
+SELECT * FROM uprt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+  a  |  b  |  c   | t2a | t3a | least 
+-----+-----+------+-----+-----+-------
+   0 |   0 | 0000 |   0 |   0 |     0
+  50 |  50 | 0050 |     |     |      
+ 100 | 100 | 0100 |     |     |      
+ 150 | 150 | 0150 | 150 | 150 |   150
+ 200 | 200 | 0200 |     |     |      
+ 250 | 250 | 0250 |     |     |      
+ 300 | 300 | 0300 | 300 | 300 |   300
+ 350 | 350 | 0350 |     |     |      
+ 400 | 400 | 0400 |     |     |      
+ 450 | 450 | 0450 | 450 | 450 |   450
+ 500 | 500 | 0500 |     |     |      
+ 550 | 550 | 0550 |     |     |      
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+                                QUERY PLAN                                 
+---------------------------------------------------------------------------
+ Nested Loop Left Join
+   Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a)), t1.a
+   ->  Merge Append
+         Sort Key: t1.a
+         ->  Index Scan using iprt1_a on public.prt1 t1
+               Output: t1.a, t1.b, t1.c
+               Filter: ((t1.a % 25) = 0)
+         ->  Index Scan using iprt1_p1_a on public.prt1_p1 t1_1
+               Output: t1_1.a, t1_1.b, t1_1.c
+               Filter: ((t1_1.a % 25) = 0)
+         ->  Index Scan using iprt1_p3_a on public.prt1_p3 t1_2
+               Output: t1_2.a, t1_2.b, t1_2.c
+               Filter: ((t1_2.a % 25) = 0)
+         ->  Index Scan using iprt1_p2_a on public.prt1_p2 t1_3
+               Output: t1_3.a, t1_3.b, t1_3.c
+               Filter: ((t1_3.a % 25) = 0)
+   ->  Append
+         ->  Merge Join
+               Output: t2.a, t3.a, LEAST(t1.a, t2.a, t3.a)
+               Merge Cond: (t2.a = t3.b)
+               ->  Index Only Scan using iprt1_p1_a on public.prt1_p1 t2
+                     Output: t2.a
+                     Index Cond: (t2.a = t1.b)
+               ->  Index Scan using iprt2_p1_b on public.prt2_p1 t3
+                     Output: t3.a, t3.b
+         ->  Merge Join
+               Output: t2_2.a, t3_1.a, LEAST(t1.a, t2_2.a, t3_1.a)
+               Merge Cond: (t2_2.a = t3_1.b)
+               ->  Index Only Scan using iprt1_p2_a on public.prt1_p2 t2_2
+                     Output: t2_2.a
+                     Index Cond: (t2_2.a = t1.b)
+               ->  Index Scan using iprt2_p2_b on public.prt2_p2 t3_1
+                     Output: t3_1.a, t3_1.b
+         ->  Merge Join
+               Output: t2_1.a, t3_2.a, LEAST(t1.a, t2_1.a, t3_2.a)
+               Merge Cond: (t2_1.a = t3_2.b)
+               ->  Index Only Scan using iprt1_p3_a on public.prt1_p3 t2_1
+                     Output: t2_1.a
+                     Index Cond: (t2_1.a = t1.b)
+               ->  Index Scan using iprt2_p3_b on public.prt2_p3 t3_2
+                     Output: t3_2.a, t3_2.b
+(41 rows)
+
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+  a  |  b  |  c   | t2a | t3a | least 
+-----+-----+------+-----+-----+-------
+   0 |   0 | 0000 |   0 |   0 |     0
+  50 |  50 | 0050 |     |     |      
+ 100 | 100 | 0100 |     |     |      
+ 150 | 150 | 0150 | 150 | 150 |   150
+ 200 | 200 | 0200 |     |     |      
+ 250 | 250 | 0250 |     |     |      
+ 300 | 300 | 0300 | 300 | 300 |   300
+ 350 | 350 | 0350 |     |     |      
+ 400 | 400 | 0400 |     |     |      
+ 450 | 450 | 0450 | 450 | 450 |   450
+ 500 | 500 | 0500 |     |     |      
+ 550 | 550 | 0550 |     |     |      
+(12 rows)
+
+SELECT * FROM uprt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+  a  |  b  |  c   | t2a | t3a | least 
+-----+-----+------+-----+-----+-------
+   0 |   0 | 0000 |   0 |   0 |     0
+  50 |  50 | 0050 |     |     |      
+ 100 | 100 | 0100 |     |     |      
+ 150 | 150 | 0150 | 150 | 150 |   150
+ 200 | 200 | 0200 |     |     |      
+ 250 | 250 | 0250 |     |     |      
+ 300 | 300 | 0300 | 300 | 300 |   300
+ 350 | 350 | 0350 |     |     |      
+ 400 | 400 | 0400 |     |     |      
+ 450 | 450 | 0450 | 450 | 450 |   450
+ 500 | 500 | 0500 |     |     |      
+ 550 | 550 | 0550 |     |     |      
+(12 rows)
+
+RESET enable_hashjoin;
+RESET enable_nestloop;
+RESET enable_seqscan;
+--
+-- partitioned by multiple columns
+--
+CREATE TABLE prt1_m (a int, b int, c varchar) PARTITION BY RANGE(a, ((a + b)/2));
+CREATE TABLE prt1_m_p1 PARTITION OF prt1_m FOR VALUES START (0, 0) END (250, 250);
+CREATE TABLE prt1_m_p2 PARTITION OF prt1_m FOR VALUES START (250, 250) END (500, 500);
+CREATE TABLE prt1_m_p3 PARTITION OF prt1_m FOR VALUES START (500, 500) END (600, 600);
+INSERT INTO prt1_m SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt1_m;
+ANALYZE prt1_m_p1;
+ANALYZE prt1_m_p2;
+ANALYZE prt1_m_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt1_m AS SELECT * FROM prt1_m;
+CREATE TABLE prt2_m (a int, b int, c varchar) PARTITION BY RANGE(((b + a)/2), b);
+CREATE TABLE prt2_m_p1 PARTITION OF prt2_m FOR VALUES START (0, 0) END (250, 250);
+CREATE TABLE prt2_m_p2 PARTITION OF prt2_m FOR VALUES START (250, 250) END (500, 500);
+CREATE TABLE prt2_m_p3 PARTITION OF prt2_m FOR VALUES START (500, 500) END (600, 600);
+INSERT INTO prt2_m SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+ANALYZE prt2_m;
+ANALYZE prt2_m_p1;
+ANALYZE prt2_m_p2;
+ANALYZE prt2_m_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt2_m AS SELECT * FROM prt2_m;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+                                              QUERY PLAN                                              
+------------------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c
+   Sort Key: t1.a, t2.b
+   ->  Append
+         ->  Hash Join
+               Output: t1.a, t1.c, t2.b, t2.c
+               Hash Cond: ((((t2.b + t2.a) / 2) = t1.a) AND (t2.b = ((t1.a + t1.b) / 2)))
+               ->  Seq Scan on public.prt2_m_p1 t2
+                     Output: t2.b, t2.c, t2.a
+               ->  Hash
+                     Output: t1.a, t1.c, t1.b
+                     ->  Seq Scan on public.prt1_m_p1 t1
+                           Output: t1.a, t1.c, t1.b
+                           Filter: ((t1.a % 25) = 0)
+         ->  Hash Join
+               Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+               Hash Cond: ((((t2_1.b + t2_1.a) / 2) = t1_1.a) AND (t2_1.b = ((t1_1.a + t1_1.b) / 2)))
+               ->  Seq Scan on public.prt2_m_p2 t2_1
+                     Output: t2_1.b, t2_1.c, t2_1.a
+               ->  Hash
+                     Output: t1_1.a, t1_1.c, t1_1.b
+                     ->  Seq Scan on public.prt1_m_p2 t1_1
+                           Output: t1_1.a, t1_1.c, t1_1.b
+                           Filter: ((t1_1.a % 25) = 0)
+         ->  Hash Join
+               Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+               Hash Cond: ((((t2_2.b + t2_2.a) / 2) = t1_2.a) AND (t2_2.b = ((t1_2.a + t1_2.b) / 2)))
+               ->  Seq Scan on public.prt2_m_p3 t2_2
+                     Output: t2_2.b, t2_2.c, t2_2.a
+               ->  Hash
+                     Output: t1_2.a, t1_2.c, t1_2.b
+                     ->  Seq Scan on public.prt1_m_p3 t1_2
+                           Output: t1_2.a, t1_2.c, t1_2.b
+                           Filter: ((t1_2.a % 25) = 0)
+(34 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+ 150 | 0150 | 150 | 0150
+ 300 | 0300 | 300 | 0300
+ 450 | 0450 | 450 | 0450
+(4 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_m t1, uprt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 AND t1.a % 25 = 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)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+                                              QUERY PLAN                                              
+------------------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c
+   Sort Key: t1.a, t2.b
+   ->  Append
+         ->  Hash Right Join
+               Output: t1.a, t1.c, t2.b, t2.c
+               Hash Cond: ((((t2.b + t2.a) / 2) = t1.a) AND (t2.b = ((t1.a + t1.b) / 2)))
+               ->  Seq Scan on public.prt2_m_p1 t2
+                     Output: t2.b, t2.c, t2.a
+               ->  Hash
+                     Output: t1.a, t1.c, t1.b
+                     ->  Seq Scan on public.prt1_m_p1 t1
+                           Output: t1.a, t1.c, t1.b
+                           Filter: ((t1.a % 25) = 0)
+         ->  Hash Right Join
+               Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+               Hash Cond: ((((t2_1.b + t2_1.a) / 2) = t1_1.a) AND (t2_1.b = ((t1_1.a + t1_1.b) / 2)))
+               ->  Seq Scan on public.prt2_m_p2 t2_1
+                     Output: t2_1.b, t2_1.c, t2_1.a
+               ->  Hash
+                     Output: t1_1.a, t1_1.c, t1_1.b
+                     ->  Seq Scan on public.prt1_m_p2 t1_1
+                           Output: t1_1.a, t1_1.c, t1_1.b
+                           Filter: ((t1_1.a % 25) = 0)
+         ->  Hash Right Join
+               Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+               Hash Cond: ((((t2_2.b + t2_2.a) / 2) = t1_2.a) AND (t2_2.b = ((t1_2.a + t1_2.b) / 2)))
+               ->  Seq Scan on public.prt2_m_p3 t2_2
+                     Output: t2_2.b, t2_2.c, t2_2.a
+               ->  Hash
+                     Output: t1_2.a, t1_2.c, t1_2.b
+                     ->  Seq Scan on public.prt1_m_p3 t1_2
+                           Output: t1_2.a, t1_2.c, t1_2.b
+                           Filter: ((t1_2.a % 25) = 0)
+(34 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  50 | 0050 |     | 
+ 100 | 0100 |     | 
+ 150 | 0150 | 150 | 0150
+ 200 | 0200 |     | 
+ 250 | 0250 |     | 
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 |     | 
+ 400 | 0400 |     | 
+ 450 | 0450 | 450 | 0450
+ 500 | 0500 |     | 
+ 550 | 0550 |     | 
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_m t1 LEFT JOIN uprt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  50 | 0050 |     | 
+ 100 | 0100 |     | 
+ 150 | 0150 | 150 | 0150
+ 200 | 0200 |     | 
+ 250 | 0250 |     | 
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 |     | 
+ 400 | 0400 |     | 
+ 450 | 0450 | 450 | 0450
+ 500 | 0500 |     | 
+ 550 | 0550 |     | 
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 RIGHT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+                                                 QUERY PLAN                                                 
+------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c
+   Sort Key: t1.a, t2.b
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c
+         ->  Append
+               ->  Hash Right Join
+                     Output: t2.b, t2.c, t1.a, t1.c
+                     Hash Cond: ((((t1.a + t1.b) / 2) = t2.b) AND (t1.a = ((t2.b + t2.a) / 2)))
+                     ->  Seq Scan on public.prt1_m_p1 t1
+                           Output: t1.a, t1.c, t1.b
+                     ->  Hash
+                           Output: t2.b, t2.c, t2.a
+                           ->  Seq Scan on public.prt2_m_p1 t2
+                                 Output: t2.b, t2.c, t2.a
+                                 Filter: ((t2.b % 25) = 0)
+               ->  Hash Right Join
+                     Output: t2_1.b, t2_1.c, t1_1.a, t1_1.c
+                     Hash Cond: ((((t1_1.a + t1_1.b) / 2) = t2_1.b) AND (t1_1.a = ((t2_1.b + t2_1.a) / 2)))
+                     ->  Seq Scan on public.prt1_m_p2 t1_1
+                           Output: t1_1.a, t1_1.c, t1_1.b
+                     ->  Hash
+                           Output: t2_1.b, t2_1.c, t2_1.a
+                           ->  Seq Scan on public.prt2_m_p2 t2_1
+                                 Output: t2_1.b, t2_1.c, t2_1.a
+                                 Filter: ((t2_1.b % 25) = 0)
+               ->  Hash Right Join
+                     Output: t2_2.b, t2_2.c, t1_2.a, t1_2.c
+                     Hash Cond: ((((t1_2.a + t1_2.b) / 2) = t2_2.b) AND (t1_2.a = ((t2_2.b + t2_2.a) / 2)))
+                     ->  Seq Scan on public.prt1_m_p3 t1_2
+                           Output: t1_2.a, t1_2.c, t1_2.b
+                     ->  Hash
+                           Output: t2_2.b, t2_2.c, t2_2.a
+                           ->  Seq Scan on public.prt2_m_p3 t2_2
+                                 Output: t2_2.b, t2_2.c, t2_2.a
+                                 Filter: ((t2_2.b % 25) = 0)
+(36 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 RIGHT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+  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)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_m t1 RIGHT JOIN uprt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t2.b % 25 = 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)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.b % 25 = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+                                                             QUERY PLAN                                                             
+------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: prt1_m_p1.a, prt1_m_p1.c, prt2_m_p1.b, prt2_m_p1.c
+   Sort Key: prt1_m_p1.a, prt2_m_p1.b
+   ->  Append
+         ->  Hash Full Join
+               Output: prt1_m_p1.a, prt1_m_p1.c, prt2_m_p1.b, prt2_m_p1.c
+               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 public.prt1_m_p1
+                     Output: prt1_m_p1.a, prt1_m_p1.c, prt1_m_p1.b
+                     Filter: ((prt1_m_p1.a % 25) = 0)
+               ->  Hash
+                     Output: prt2_m_p1.b, prt2_m_p1.c, prt2_m_p1.a
+                     ->  Seq Scan on public.prt2_m_p1
+                           Output: prt2_m_p1.b, prt2_m_p1.c, prt2_m_p1.a
+                           Filter: ((prt2_m_p1.b % 25) = 0)
+         ->  Hash Full Join
+               Output: prt1_m_p2.a, prt1_m_p2.c, prt2_m_p2.b, prt2_m_p2.c
+               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 public.prt1_m_p2
+                     Output: prt1_m_p2.a, prt1_m_p2.c, prt1_m_p2.b
+                     Filter: ((prt1_m_p2.a % 25) = 0)
+               ->  Hash
+                     Output: prt2_m_p2.b, prt2_m_p2.c, prt2_m_p2.a
+                     ->  Seq Scan on public.prt2_m_p2
+                           Output: prt2_m_p2.b, prt2_m_p2.c, prt2_m_p2.a
+                           Filter: ((prt2_m_p2.b % 25) = 0)
+         ->  Hash Full Join
+               Output: prt1_m_p3.a, prt1_m_p3.c, prt2_m_p3.b, prt2_m_p3.c
+               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 public.prt1_m_p3
+                     Output: prt1_m_p3.a, prt1_m_p3.c, prt1_m_p3.b
+                     Filter: ((prt1_m_p3.a % 25) = 0)
+               ->  Hash
+                     Output: prt2_m_p3.b, prt2_m_p3.c, prt2_m_p3.a
+                     ->  Seq Scan on public.prt2_m_p3
+                           Output: prt2_m_p3.b, prt2_m_p3.c, prt2_m_p3.a
+                           Filter: ((prt2_m_p3.b % 25) = 0)
+(37 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.b % 25 = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  50 | 0050 |     | 
+ 100 | 0100 |     | 
+ 150 | 0150 | 150 | 0150
+ 200 | 0200 |     | 
+ 250 | 0250 |     | 
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 |     | 
+ 400 | 0400 |     | 
+ 450 | 0450 | 450 | 0450
+ 500 | 0500 |     | 
+ 550 | 0550 |     | 
+     |      |  75 | 0075
+     |      | 225 | 0225
+     |      | 375 | 0375
+     |      | 525 | 0525
+(16 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1_m t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2_m t2 WHERE t2.b % 25 = 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 | 0000 |   0 | 0000
+  50 | 0050 |     | 
+ 100 | 0100 |     | 
+ 150 | 0150 | 150 | 0150
+ 200 | 0200 |     | 
+ 250 | 0250 |     | 
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 |     | 
+ 400 | 0400 |     | 
+ 450 | 0450 | 450 | 0450
+ 500 | 0500 |     | 
+ 550 | 0550 |     | 
+     |      |  75 | 0075
+     |      | 225 | 0225
+     |      | 375 | 0375
+     |      | 525 | 0525
+(16 rows)
+
+--
+-- multi-leveled partitions
+--
+CREATE TABLE prt1_l (a int, b int, c varchar) PARTITION BY RANGE(a);
+CREATE TABLE prt1_l_p1 PARTITION OF prt1_l FOR VALUES START (0) END (250) PARTITION BY RANGE (b);
+CREATE TABLE prt1_l_p1_p1 PARTITION OF prt1_l_p1 FOR VALUES START (0) END (100);
+CREATE TABLE prt1_l_p1_p2 PARTITION OF prt1_l_p1 FOR VALUES START (100) END (250);
+CREATE TABLE prt1_l_p2 PARTITION OF prt1_l FOR VALUES START (250) END (500) PARTITION BY RANGE (c);
+CREATE TABLE prt1_l_p2_p1 PARTITION OF prt1_l_p2 FOR VALUES START ('0250') END ('0400');
+CREATE TABLE prt1_l_p2_p2 PARTITION OF prt1_l_p2 FOR VALUES START ('0400') END ('0500');
+CREATE TABLE prt1_l_p3 PARTITION OF prt1_l FOR VALUES START (500) END (600) PARTITION BY RANGE ((b + a));
+CREATE TABLE prt1_l_p3_p1 PARTITION OF prt1_l_p3 FOR VALUES START (1000) END (1100);
+CREATE TABLE prt1_l_p3_p2 PARTITION OF prt1_l_p3 FOR VALUES START (1100) END (1200);
+INSERT INTO prt1_l SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt1_l;
+ANALYZE prt1_l_p1;
+ANALYZE prt1_l_p1_p1;
+ANALYZE prt1_l_p1_p2;
+ANALYZE prt1_l_p2;
+ANALYZE prt1_l_p2_p1;
+ANALYZE prt1_l_p2_p2;
+ANALYZE prt1_l_p3;
+ANALYZE prt1_l_p3_p1;
+ANALYZE prt1_l_p3_p2;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt1_l AS SELECT * FROM prt1_l;
+CREATE TABLE prt2_l (a int, b int, c varchar) PARTITION BY RANGE(b);
+CREATE TABLE prt2_l_p1 PARTITION OF prt2_l FOR VALUES START (0) END (250) PARTITION BY RANGE (a);
+CREATE TABLE prt2_l_p1_p1 PARTITION OF prt2_l_p1 FOR VALUES START (0) END (100);
+CREATE TABLE prt2_l_p1_p2 PARTITION OF prt2_l_p1 FOR VALUES START (100) END (250);
+CREATE TABLE prt2_l_p2 PARTITION OF prt2_l FOR VALUES START (250) END (500) PARTITION BY RANGE (c);
+CREATE TABLE prt2_l_p2_p1 PARTITION OF prt2_l_p2 FOR VALUES START ('0250') END ('0400');
+CREATE TABLE prt2_l_p2_p2 PARTITION OF prt2_l_p2 FOR VALUES START ('0400') END ('0500');
+CREATE TABLE prt2_l_p3 PARTITION OF prt2_l FOR VALUES START (500) END (600) PARTITION BY RANGE ((a + b));
+CREATE TABLE prt2_l_p3_p1 PARTITION OF prt2_l_p3 FOR VALUES START (1000) END (1100);
+CREATE TABLE prt2_l_p3_p2 PARTITION OF prt2_l_p3 FOR VALUES START (1100) END (1200);
+INSERT INTO prt2_l SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+ANALYZE prt2_l;
+ANALYZE prt2_l_p1;
+ANALYZE prt2_l_p1_p1;
+ANALYZE prt2_l_p1_p2;
+ANALYZE prt2_l_p2;
+ANALYZE prt2_l_p2_p1;
+ANALYZE prt2_l_p2_p2;
+ANALYZE prt2_l_p3;
+ANALYZE prt2_l_p3_p1;
+ANALYZE prt2_l_p3_p2;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt2_l AS SELECT * FROM prt2_l;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+                                                         QUERY PLAN                                                         
+----------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c
+   Sort Key: t1.a
+   ->  Hash Join
+         Output: t1.a, t1.c, t2.b, t2.c
+         Hash Cond: ((t2.b = t1.a) AND (t2.a = t1.b) AND ((t2.c)::text = (t1.c)::text) AND ((t2.a + t2.b) = (t1.b + t1.a)))
+         ->  Append
+               ->  Seq Scan on public.prt2_l t2
+                     Output: t2.b, t2.c, t2.a
+               ->  Seq Scan on public.prt2_l_p1 t2_1
+                     Output: t2_1.b, t2_1.c, t2_1.a
+               ->  Seq Scan on public.prt2_l_p1_p1 t2_2
+                     Output: t2_2.b, t2_2.c, t2_2.a
+               ->  Seq Scan on public.prt2_l_p1_p2 t2_3
+                     Output: t2_3.b, t2_3.c, t2_3.a
+               ->  Seq Scan on public.prt2_l_p2 t2_4
+                     Output: t2_4.b, t2_4.c, t2_4.a
+               ->  Seq Scan on public.prt2_l_p2_p1 t2_5
+                     Output: t2_5.b, t2_5.c, t2_5.a
+               ->  Seq Scan on public.prt2_l_p2_p2 t2_6
+                     Output: t2_6.b, t2_6.c, t2_6.a
+               ->  Seq Scan on public.prt2_l_p3 t2_7
+                     Output: t2_7.b, t2_7.c, t2_7.a
+               ->  Seq Scan on public.prt2_l_p3_p1 t2_8
+                     Output: t2_8.b, t2_8.c, t2_8.a
+               ->  Seq Scan on public.prt2_l_p3_p2 t2_9
+                     Output: t2_9.b, t2_9.c, t2_9.a
+         ->  Hash
+               Output: t1.a, t1.c, t1.b
+               ->  Append
+                     ->  Seq Scan on public.prt1_l t1
+                           Output: t1.a, t1.c, t1.b
+                           Filter: ((t1.a % 25) = 0)
+                     ->  Seq Scan on public.prt1_l_p1 t1_1
+                           Output: t1_1.a, t1_1.c, t1_1.b
+                           Filter: ((t1_1.a % 25) = 0)
+                     ->  Seq Scan on public.prt1_l_p1_p1 t1_2
+                           Output: t1_2.a, t1_2.c, t1_2.b
+                           Filter: ((t1_2.a % 25) = 0)
+                     ->  Seq Scan on public.prt1_l_p1_p2 t1_3
+                           Output: t1_3.a, t1_3.c, t1_3.b
+                           Filter: ((t1_3.a % 25) = 0)
+                     ->  Seq Scan on public.prt1_l_p2 t1_4
+                           Output: t1_4.a, t1_4.c, t1_4.b
+                           Filter: ((t1_4.a % 25) = 0)
+                     ->  Seq Scan on public.prt1_l_p2_p1 t1_5
+                           Output: t1_5.a, t1_5.c, t1_5.b
+                           Filter: ((t1_5.a % 25) = 0)
+                     ->  Seq Scan on public.prt1_l_p2_p2 t1_6
+                           Output: t1_6.a, t1_6.c, t1_6.b
+                           Filter: ((t1_6.a % 25) = 0)
+                     ->  Seq Scan on public.prt1_l_p3 t1_7
+                           Output: t1_7.a, t1_7.c, t1_7.b
+                           Filter: ((t1_7.a % 25) = 0)
+                     ->  Seq Scan on public.prt1_l_p3_p1 t1_8
+                           Output: t1_8.a, t1_8.c, t1_8.b
+                           Filter: ((t1_8.a % 25) = 0)
+                     ->  Seq Scan on public.prt1_l_p3_p2 t1_9
+                           Output: t1_9.a, t1_9.c, t1_9.b
+                           Filter: ((t1_9.a % 25) = 0)
+(60 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b AND t1.a % 25 = 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)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_l t1, uprt2_l t2 WHERE t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b AND t1.a % 25 = 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)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+                                                         QUERY PLAN                                                         
+----------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c
+   Sort Key: t1.a, t2.b
+   ->  Hash Right Join
+         Output: t1.a, t1.c, t2.b, t2.c
+         Hash Cond: ((t2.b = t1.a) AND (t2.a = t1.b) AND ((t2.c)::text = (t1.c)::text) AND ((t2.a + t2.b) = (t1.b + t1.a)))
+         ->  Append
+               ->  Seq Scan on public.prt2_l t2
+                     Output: t2.b, t2.c, t2.a
+               ->  Seq Scan on public.prt2_l_p1 t2_1
+                     Output: t2_1.b, t2_1.c, t2_1.a
+               ->  Seq Scan on public.prt2_l_p1_p1 t2_2
+                     Output: t2_2.b, t2_2.c, t2_2.a
+               ->  Seq Scan on public.prt2_l_p1_p2 t2_3
+                     Output: t2_3.b, t2_3.c, t2_3.a
+               ->  Seq Scan on public.prt2_l_p2 t2_4
+                     Output: t2_4.b, t2_4.c, t2_4.a
+               ->  Seq Scan on public.prt2_l_p2_p1 t2_5
+                     Output: t2_5.b, t2_5.c, t2_5.a
+               ->  Seq Scan on public.prt2_l_p2_p2 t2_6
+                     Output: t2_6.b, t2_6.c, t2_6.a
+               ->  Seq Scan on public.prt2_l_p3 t2_7
+                     Output: t2_7.b, t2_7.c, t2_7.a
+               ->  Seq Scan on public.prt2_l_p3_p1 t2_8
+                     Output: t2_8.b, t2_8.c, t2_8.a
+               ->  Seq Scan on public.prt2_l_p3_p2 t2_9
+                     Output: t2_9.b, t2_9.c, t2_9.a
+         ->  Hash
+               Output: t1.a, t1.c, t1.b
+               ->  Append
+                     ->  Seq Scan on public.prt1_l t1
+                           Output: t1.a, t1.c, t1.b
+                           Filter: ((t1.a % 25) = 0)
+                     ->  Seq Scan on public.prt1_l_p1 t1_1
+                           Output: t1_1.a, t1_1.c, t1_1.b
+                           Filter: ((t1_1.a % 25) = 0)
+                     ->  Seq Scan on public.prt1_l_p1_p1 t1_2
+                           Output: t1_2.a, t1_2.c, t1_2.b
+                           Filter: ((t1_2.a % 25) = 0)
+                     ->  Seq Scan on public.prt1_l_p1_p2 t1_3
+                           Output: t1_3.a, t1_3.c, t1_3.b
+                           Filter: ((t1_3.a % 25) = 0)
+                     ->  Seq Scan on public.prt1_l_p2 t1_4
+                           Output: t1_4.a, t1_4.c, t1_4.b
+                           Filter: ((t1_4.a % 25) = 0)
+                     ->  Seq Scan on public.prt1_l_p2_p1 t1_5
+                           Output: t1_5.a, t1_5.c, t1_5.b
+                           Filter: ((t1_5.a % 25) = 0)
+                     ->  Seq Scan on public.prt1_l_p2_p2 t1_6
+                           Output: t1_6.a, t1_6.c, t1_6.b
+                           Filter: ((t1_6.a % 25) = 0)
+                     ->  Seq Scan on public.prt1_l_p3 t1_7
+                           Output: t1_7.a, t1_7.c, t1_7.b
+                           Filter: ((t1_7.a % 25) = 0)
+                     ->  Seq Scan on public.prt1_l_p3_p1 t1_8
+                           Output: t1_8.a, t1_8.c, t1_8.b
+                           Filter: ((t1_8.a % 25) = 0)
+                     ->  Seq Scan on public.prt1_l_p3_p2 t1_9
+                           Output: t1_9.a, t1_9.c, t1_9.b
+                           Filter: ((t1_9.a % 25) = 0)
+(60 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  50 | 0050 |     | 
+ 100 | 0100 |     | 
+ 150 | 0150 | 150 | 0150
+ 200 | 0200 |     | 
+ 250 | 0250 |     | 
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 |     | 
+ 400 | 0400 |     | 
+ 450 | 0450 | 450 | 0450
+ 500 | 0500 |     | 
+ 550 | 0550 |     | 
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_l t1 LEFT JOIN uprt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  50 | 0050 |     | 
+ 100 | 0100 |     | 
+ 150 | 0150 | 150 | 0150
+ 200 | 0200 |     | 
+ 250 | 0250 |     | 
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 |     | 
+ 400 | 0400 |     | 
+ 450 | 0450 | 450 | 0450
+ 500 | 0500 |     | 
+ 550 | 0550 |     | 
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+                                                         QUERY PLAN                                                         
+----------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c
+   Sort Key: t1.a, t2.b
+   ->  Hash Right Join
+         Output: t1.a, t1.c, t2.b, t2.c
+         Hash Cond: ((t1.a = t2.b) AND (t1.b = t2.a) AND ((t1.c)::text = (t2.c)::text) AND ((t1.b + t1.a) = (t2.a + t2.b)))
+         ->  Append
+               ->  Seq Scan on public.prt1_l t1
+                     Output: t1.a, t1.c, t1.b
+               ->  Seq Scan on public.prt1_l_p1 t1_1
+                     Output: t1_1.a, t1_1.c, t1_1.b
+               ->  Seq Scan on public.prt1_l_p1_p1 t1_2
+                     Output: t1_2.a, t1_2.c, t1_2.b
+               ->  Seq Scan on public.prt1_l_p1_p2 t1_3
+                     Output: t1_3.a, t1_3.c, t1_3.b
+               ->  Seq Scan on public.prt1_l_p2 t1_4
+                     Output: t1_4.a, t1_4.c, t1_4.b
+               ->  Seq Scan on public.prt1_l_p2_p1 t1_5
+                     Output: t1_5.a, t1_5.c, t1_5.b
+               ->  Seq Scan on public.prt1_l_p2_p2 t1_6
+                     Output: t1_6.a, t1_6.c, t1_6.b
+               ->  Seq Scan on public.prt1_l_p3 t1_7
+                     Output: t1_7.a, t1_7.c, t1_7.b
+               ->  Seq Scan on public.prt1_l_p3_p1 t1_8
+                     Output: t1_8.a, t1_8.c, t1_8.b
+               ->  Seq Scan on public.prt1_l_p3_p2 t1_9
+                     Output: t1_9.a, t1_9.c, t1_9.b
+         ->  Hash
+               Output: t2.b, t2.c, t2.a
+               ->  Append
+                     ->  Seq Scan on public.prt2_l t2
+                           Output: t2.b, t2.c, t2.a
+                           Filter: ((t2.b % 25) = 0)
+                     ->  Seq Scan on public.prt2_l_p1 t2_1
+                           Output: t2_1.b, t2_1.c, t2_1.a
+                           Filter: ((t2_1.b % 25) = 0)
+                     ->  Seq Scan on public.prt2_l_p1_p1 t2_2
+                           Output: t2_2.b, t2_2.c, t2_2.a
+                           Filter: ((t2_2.b % 25) = 0)
+                     ->  Seq Scan on public.prt2_l_p1_p2 t2_3
+                           Output: t2_3.b, t2_3.c, t2_3.a
+                           Filter: ((t2_3.b % 25) = 0)
+                     ->  Seq Scan on public.prt2_l_p2 t2_4
+                           Output: t2_4.b, t2_4.c, t2_4.a
+                           Filter: ((t2_4.b % 25) = 0)
+                     ->  Seq Scan on public.prt2_l_p2_p1 t2_5
+                           Output: t2_5.b, t2_5.c, t2_5.a
+                           Filter: ((t2_5.b % 25) = 0)
+                     ->  Seq Scan on public.prt2_l_p2_p2 t2_6
+                           Output: t2_6.b, t2_6.c, t2_6.a
+                           Filter: ((t2_6.b % 25) = 0)
+                     ->  Seq Scan on public.prt2_l_p3 t2_7
+                           Output: t2_7.b, t2_7.c, t2_7.a
+                           Filter: ((t2_7.b % 25) = 0)
+                     ->  Seq Scan on public.prt2_l_p3_p1 t2_8
+                           Output: t2_8.b, t2_8.c, t2_8.a
+                           Filter: ((t2_8.b % 25) = 0)
+                     ->  Seq Scan on public.prt2_l_p3_p2 t2_9
+                           Output: t2_9.b, t2_9.c, t2_9.a
+                           Filter: ((t2_9.b % 25) = 0)
+(60 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t2.b % 25 = 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)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_l t1 RIGHT JOIN uprt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t2.b % 25 = 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)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b) ORDER BY t1.a, t2.b;
+                                                                             QUERY PLAN                                                                             
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: prt1_l.a, prt1_l.c, prt2_l.b, prt2_l.c
+   Sort Key: prt1_l.a, prt2_l.b
+   ->  Hash Full Join
+         Output: prt1_l.a, prt1_l.c, prt2_l.b, prt2_l.c
+         Hash Cond: ((prt1_l.a = prt2_l.b) AND (prt1_l.b = prt2_l.a) AND ((prt1_l.c)::text = (prt2_l.c)::text) AND ((prt1_l.b + prt1_l.a) = (prt2_l.a + prt2_l.b)))
+         ->  Append
+               ->  Seq Scan on public.prt1_l
+                     Output: prt1_l.a, prt1_l.c, prt1_l.b
+                     Filter: ((prt1_l.a % 25) = 0)
+               ->  Seq Scan on public.prt1_l_p1
+                     Output: prt1_l_p1.a, prt1_l_p1.c, prt1_l_p1.b
+                     Filter: ((prt1_l_p1.a % 25) = 0)
+               ->  Seq Scan on public.prt1_l_p1_p1
+                     Output: prt1_l_p1_p1.a, prt1_l_p1_p1.c, prt1_l_p1_p1.b
+                     Filter: ((prt1_l_p1_p1.a % 25) = 0)
+               ->  Seq Scan on public.prt1_l_p1_p2
+                     Output: prt1_l_p1_p2.a, prt1_l_p1_p2.c, prt1_l_p1_p2.b
+                     Filter: ((prt1_l_p1_p2.a % 25) = 0)
+               ->  Seq Scan on public.prt1_l_p2
+                     Output: prt1_l_p2.a, prt1_l_p2.c, prt1_l_p2.b
+                     Filter: ((prt1_l_p2.a % 25) = 0)
+               ->  Seq Scan on public.prt1_l_p2_p1
+                     Output: prt1_l_p2_p1.a, prt1_l_p2_p1.c, prt1_l_p2_p1.b
+                     Filter: ((prt1_l_p2_p1.a % 25) = 0)
+               ->  Seq Scan on public.prt1_l_p2_p2
+                     Output: prt1_l_p2_p2.a, prt1_l_p2_p2.c, prt1_l_p2_p2.b
+                     Filter: ((prt1_l_p2_p2.a % 25) = 0)
+               ->  Seq Scan on public.prt1_l_p3
+                     Output: prt1_l_p3.a, prt1_l_p3.c, prt1_l_p3.b
+                     Filter: ((prt1_l_p3.a % 25) = 0)
+               ->  Seq Scan on public.prt1_l_p3_p1
+                     Output: prt1_l_p3_p1.a, prt1_l_p3_p1.c, prt1_l_p3_p1.b
+                     Filter: ((prt1_l_p3_p1.a % 25) = 0)
+               ->  Seq Scan on public.prt1_l_p3_p2
+                     Output: prt1_l_p3_p2.a, prt1_l_p3_p2.c, prt1_l_p3_p2.b
+                     Filter: ((prt1_l_p3_p2.a % 25) = 0)
+         ->  Hash
+               Output: prt2_l.b, prt2_l.c, prt2_l.a
+               ->  Append
+                     ->  Seq Scan on public.prt2_l
+                           Output: prt2_l.b, prt2_l.c, prt2_l.a
+                           Filter: ((prt2_l.b % 25) = 0)
+                     ->  Seq Scan on public.prt2_l_p1
+                           Output: prt2_l_p1.b, prt2_l_p1.c, prt2_l_p1.a
+                           Filter: ((prt2_l_p1.b % 25) = 0)
+                     ->  Seq Scan on public.prt2_l_p1_p1
+                           Output: prt2_l_p1_p1.b, prt2_l_p1_p1.c, prt2_l_p1_p1.a
+                           Filter: ((prt2_l_p1_p1.b % 25) = 0)
+                     ->  Seq Scan on public.prt2_l_p1_p2
+                           Output: prt2_l_p1_p2.b, prt2_l_p1_p2.c, prt2_l_p1_p2.a
+                           Filter: ((prt2_l_p1_p2.b % 25) = 0)
+                     ->  Seq Scan on public.prt2_l_p2
+                           Output: prt2_l_p2.b, prt2_l_p2.c, prt2_l_p2.a
+                           Filter: ((prt2_l_p2.b % 25) = 0)
+                     ->  Seq Scan on public.prt2_l_p2_p1
+                           Output: prt2_l_p2_p1.b, prt2_l_p2_p1.c, prt2_l_p2_p1.a
+                           Filter: ((prt2_l_p2_p1.b % 25) = 0)
+                     ->  Seq Scan on public.prt2_l_p2_p2
+                           Output: prt2_l_p2_p2.b, prt2_l_p2_p2.c, prt2_l_p2_p2.a
+                           Filter: ((prt2_l_p2_p2.b % 25) = 0)
+                     ->  Seq Scan on public.prt2_l_p3
+                           Output: prt2_l_p3.b, prt2_l_p3.c, prt2_l_p3.a
+                           Filter: ((prt2_l_p3.b % 25) = 0)
+                     ->  Seq Scan on public.prt2_l_p3_p1
+                           Output: prt2_l_p3_p1.b, prt2_l_p3_p1.c, prt2_l_p3_p1.a
+                           Filter: ((prt2_l_p3_p1.b % 25) = 0)
+                     ->  Seq Scan on public.prt2_l_p3_p2
+                           Output: prt2_l_p3_p2.b, prt2_l_p3_p2.c, prt2_l_p3_p2.a
+                           Filter: ((prt2_l_p3_p2.b % 25) = 0)
+(70 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b) ORDER BY t1.a, t2.b;
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  50 | 0050 |     | 
+ 100 | 0100 |     | 
+ 150 | 0150 | 150 | 0150
+ 200 | 0200 |     | 
+ 250 | 0250 |     | 
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 |     | 
+ 400 | 0400 |     | 
+ 450 | 0450 | 450 | 0450
+ 500 | 0500 |     | 
+ 550 | 0550 |     | 
+     |      |  75 | 0075
+     |      | 225 | 0225
+     |      | 375 | 0375
+     |      | 525 | 0525
+(16 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1_l t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2_l t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b) ORDER BY t1.a, t2.b;
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  50 | 0050 |     | 
+ 100 | 0100 |     | 
+ 150 | 0150 | 150 | 0150
+ 200 | 0200 |     | 
+ 250 | 0250 |     | 
+ 300 | 0300 | 300 | 0300
+ 350 | 0350 |     | 
+ 400 | 0400 |     | 
+ 450 | 0450 | 450 | 0450
+ 500 | 0500 |     | 
+ 550 | 0550 |     | 
+     |      |  75 | 0075
+     |      | 225 | 0225
+     |      | 375 | 0375
+     |      | 525 | 0525
+(16 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;
+ANALYZE plt1_p1;
+ANALYZE plt1_p2;
+ANALYZE plt1_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uplt1 AS SELECT * FROM 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;
+ANALYZE plt2_p1;
+ANALYZE plt2_p2;
+ANALYZE plt2_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uplt2 AS SELECT * FROM plt2;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.a = t2.a AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+                             QUERY PLAN                             
+--------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c
+   Sort Key: t1.a, t2.b
+   ->  Append
+         ->  Hash Join
+               Output: t1.a, t1.c, t2.b, t2.c
+               Hash Cond: ((t2.c = t1.c) AND (t2.a = t1.a))
+               ->  Seq Scan on public.plt2_p1 t2
+                     Output: t2.b, t2.c, t2.a
+               ->  Hash
+                     Output: t1.a, t1.c
+                     ->  Seq Scan on public.plt1_p1 t1
+                           Output: t1.a, t1.c
+                           Filter: ((t1.a % 25) = 0)
+         ->  Hash Join
+               Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+               Hash Cond: ((t2_1.c = t1_1.c) AND (t2_1.a = t1_1.a))
+               ->  Seq Scan on public.plt2_p2 t2_1
+                     Output: t2_1.b, t2_1.c, t2_1.a
+               ->  Hash
+                     Output: t1_1.a, t1_1.c
+                     ->  Seq Scan on public.plt1_p2 t1_1
+                           Output: t1_1.a, t1_1.c
+                           Filter: ((t1_1.a % 25) = 0)
+         ->  Hash Join
+               Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+               Hash Cond: ((t2_2.c = t1_2.c) AND (t2_2.a = t1_2.a))
+               ->  Seq Scan on public.plt2_p3 t2_2
+                     Output: t2_2.b, t2_2.c, t2_2.a
+               ->  Hash
+                     Output: t1_2.a, t1_2.c
+                     ->  Seq Scan on public.plt1_p3 t1_2
+                           Output: t1_2.a, t1_2.c
+                           Filter: ((t1_2.a % 25) = 0)
+(34 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.a = t2.a AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+ 150 | 0003 | 150 | 0003
+ 300 | 0006 | 300 | 0006
+ 450 | 0009 | 450 | 0009
+(4 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1 t1, uplt2 t2 WHERE t1.c = t2.c AND t1.a = t2.a AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+ 150 | 0003 | 150 | 0003
+ 300 | 0006 | 300 | 0006
+ 450 | 0009 | 450 | 0009
+(4 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.a AND t1.c = t2.c WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+                             QUERY PLAN                             
+--------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c
+   Sort Key: t1.a, t2.b
+   ->  Append
+         ->  Hash Right Join
+               Output: t1.a, t1.c, t2.b, t2.c
+               Hash Cond: ((t2.a = t1.a) AND (t2.c = t1.c))
+               ->  Seq Scan on public.plt2_p1 t2
+                     Output: t2.b, t2.c, t2.a
+               ->  Hash
+                     Output: t1.a, t1.c
+                     ->  Seq Scan on public.plt1_p1 t1
+                           Output: t1.a, t1.c
+                           Filter: ((t1.a % 25) = 0)
+         ->  Hash Right Join
+               Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+               Hash Cond: ((t2_1.a = t1_1.a) AND (t2_1.c = t1_1.c))
+               ->  Seq Scan on public.plt2_p2 t2_1
+                     Output: t2_1.b, t2_1.c, t2_1.a
+               ->  Hash
+                     Output: t1_1.a, t1_1.c
+                     ->  Seq Scan on public.plt1_p2 t1_1
+                           Output: t1_1.a, t1_1.c
+                           Filter: ((t1_1.a % 25) = 0)
+         ->  Hash Right Join
+               Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+               Hash Cond: ((t2_2.a = t1_2.a) AND (t2_2.c = t1_2.c))
+               ->  Seq Scan on public.plt2_p3 t2_2
+                     Output: t2_2.b, t2_2.c, t2_2.a
+               ->  Hash
+                     Output: t1_2.a, t1_2.c
+                     ->  Seq Scan on public.plt1_p3 t1_2
+                           Output: t1_2.a, t1_2.c
+                           Filter: ((t1_2.a % 25) = 0)
+(34 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.a AND t1.c = t2.c WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  50 | 0001 |     | 
+ 100 | 0002 |     | 
+ 150 | 0003 | 150 | 0003
+ 200 | 0004 |     | 
+ 250 | 0005 |     | 
+ 300 | 0006 | 300 | 0006
+ 350 | 0007 |     | 
+ 400 | 0008 |     | 
+ 450 | 0009 | 450 | 0009
+ 500 | 0010 |     | 
+ 550 | 0011 |     | 
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.a AND t1.c = t2.c WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  50 | 0001 |     | 
+ 100 | 0002 |     | 
+ 150 | 0003 | 150 | 0003
+ 200 | 0004 |     | 
+ 250 | 0005 |     | 
+ 300 | 0006 | 300 | 0006
+ 350 | 0007 |     | 
+ 400 | 0008 |     | 
+ 450 | 0009 | 450 | 0009
+ 500 | 0010 |     | 
+ 550 | 0011 |     | 
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+                                QUERY PLAN                                
+--------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c
+   Sort Key: t1.a, t2.b
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c
+         ->  Append
+               ->  Hash Right Join
+                     Output: t2.b, t2.c, t1.a, t1.c
+                     Hash Cond: ((t1.a = t2.b) AND (t1.c = t2.c))
+                     ->  Seq Scan on public.plt1_p1 t1
+                           Output: t1.a, t1.c
+                     ->  Hash
+                           Output: t2.b, t2.c
+                           ->  Seq Scan on public.plt2_p1 t2
+                                 Output: t2.b, t2.c
+                                 Filter: ((t2.b % 25) = 0)
+               ->  Hash Right Join
+                     Output: t2_1.b, t2_1.c, t1_1.a, t1_1.c
+                     Hash Cond: ((t1_1.a = t2_1.b) AND (t1_1.c = t2_1.c))
+                     ->  Seq Scan on public.plt1_p2 t1_1
+                           Output: t1_1.a, t1_1.c
+                     ->  Hash
+                           Output: t2_1.b, t2_1.c
+                           ->  Seq Scan on public.plt2_p2 t2_1
+                                 Output: t2_1.b, t2_1.c
+                                 Filter: ((t2_1.b % 25) = 0)
+               ->  Hash Right Join
+                     Output: t2_2.b, t2_2.c, t1_2.a, t1_2.c
+                     Hash Cond: ((t1_2.a = t2_2.b) AND (t1_2.c = t2_2.c))
+                     ->  Seq Scan on public.plt1_p3 t1_2
+                           Output: t1_2.a, t1_2.c
+                     ->  Hash
+                           Output: t2_2.b, t2_2.c
+                           ->  Seq Scan on public.plt2_p3 t2_2
+                                 Output: t2_2.b, t2_2.c
+                                 Filter: ((t2_2.b % 25) = 0)
+(36 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+ 150 | 0003 | 150 | 0003
+ 300 | 0006 | 300 | 0006
+ 450 | 0009 | 450 | 0009
+     |      |  75 | 0001
+     |      | 225 | 0004
+     |      | 375 | 0007
+     |      | 525 | 0010
+(8 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+ 150 | 0003 | 150 | 0003
+ 300 | 0006 | 300 | 0006
+ 450 | 0009 | 450 | 0009
+     |      |  75 | 0001
+     |      | 225 | 0004
+     |      | 375 | 0007
+     |      | 525 | 0010
+(8 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+                                   QUERY PLAN                                   
+--------------------------------------------------------------------------------
+ Sort
+   Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c
+   Sort Key: plt1_p1.a, plt2_p1.b
+   ->  Append
+         ->  Hash Full Join
+               Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c
+               Hash Cond: ((plt1_p1.a = plt2_p1.b) AND (plt1_p1.c = plt2_p1.c))
+               ->  Seq Scan on public.plt1_p1
+                     Output: plt1_p1.a, plt1_p1.c
+                     Filter: ((plt1_p1.a % 25) = 0)
+               ->  Hash
+                     Output: plt2_p1.b, plt2_p1.c
+                     ->  Seq Scan on public.plt2_p1
+                           Output: plt2_p1.b, plt2_p1.c
+                           Filter: ((plt2_p1.b % 25) = 0)
+         ->  Hash Full Join
+               Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c
+               Hash Cond: ((plt1_p2.a = plt2_p2.b) AND (plt1_p2.c = plt2_p2.c))
+               ->  Seq Scan on public.plt1_p2
+                     Output: plt1_p2.a, plt1_p2.c
+                     Filter: ((plt1_p2.a % 25) = 0)
+               ->  Hash
+                     Output: plt2_p2.b, plt2_p2.c
+                     ->  Seq Scan on public.plt2_p2
+                           Output: plt2_p2.b, plt2_p2.c
+                           Filter: ((plt2_p2.b % 25) = 0)
+         ->  Hash Full Join
+               Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c
+               Hash Cond: ((plt1_p3.a = plt2_p3.b) AND (plt1_p3.c = plt2_p3.c))
+               ->  Seq Scan on public.plt1_p3
+                     Output: plt1_p3.a, plt1_p3.c
+                     Filter: ((plt1_p3.a % 25) = 0)
+               ->  Hash
+                     Output: plt2_p3.b, plt2_p3.c
+                     ->  Seq Scan on public.plt2_p3
+                           Output: plt2_p3.b, plt2_p3.c
+                           Filter: ((plt2_p3.b % 25) = 0)
+(37 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  50 | 0001 |     | 
+ 100 | 0002 |     | 
+ 150 | 0003 | 150 | 0003
+ 200 | 0004 |     | 
+ 250 | 0005 |     | 
+ 300 | 0006 | 300 | 0006
+ 350 | 0007 |     | 
+ 400 | 0008 |     | 
+ 450 | 0009 | 450 | 0009
+ 500 | 0010 |     | 
+ 550 | 0011 |     | 
+     |      |  75 | 0001
+     |      | 225 | 0004
+     |      | 375 | 0007
+     |      | 525 | 0010
+(16 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b;
+  a  |  c   |  b  |  c   
+-----+------+-----+------
+   0 | 0000 |   0 | 0000
+  50 | 0001 |     | 
+ 100 | 0002 |     | 
+ 150 | 0003 | 150 | 0003
+ 200 | 0004 |     | 
+ 250 | 0005 |     | 
+ 300 | 0006 | 300 | 0006
+ 350 | 0007 |     | 
+ 400 | 0008 |     | 
+ 450 | 0009 | 450 | 0009
+ 500 | 0010 |     | 
+ 550 | 0011 |     | 
+     |      |  75 | 0001
+     |      | 225 | 0004
+     |      | 375 | 0007
+     |      | 525 | 0010
+(16 rows)
+
+-- Cases with non-nullable expressions in subquery results;
+-- make sure these go to null as expected
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM (SELECT 50 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ GroupAggregate
+   Output: sum(plt1_p1.a), plt1_p1.c, avg(plt2_p1.b), plt2_p1.c
+   Group Key: plt1_p1.c, plt2_p1.c
+   ->  Sort
+         Output: plt1_p1.c, plt2_p1.c, plt1_p1.a, plt2_p1.b
+         Sort Key: plt1_p1.c, plt2_p1.c
+         ->  Result
+               Output: plt1_p1.c, plt2_p1.c, plt1_p1.a, plt2_p1.b
+               ->  Append
+                     ->  Hash Full Join
+                           Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c
+                           Hash Cond: ((plt1_p1.c = plt2_p1.c) AND (plt1_p1.a = plt2_p1.b))
+                           ->  Seq Scan on public.plt1_p1
+                                 Output: plt1_p1.a, plt1_p1.c
+                                 Filter: ((plt1_p1.a % 25) = 0)
+                           ->  Hash
+                                 Output: plt2_p1.b, plt2_p1.c
+                                 ->  Seq Scan on public.plt2_p1
+                                       Output: plt2_p1.b, plt2_p1.c
+                                       Filter: ((plt2_p1.b % 25) = 0)
+                     ->  Hash Full Join
+                           Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c
+                           Hash Cond: ((plt1_p2.c = plt2_p2.c) AND (plt1_p2.a = plt2_p2.b))
+                           ->  Seq Scan on public.plt1_p2
+                                 Output: plt1_p2.a, plt1_p2.c
+                                 Filter: ((plt1_p2.a % 25) = 0)
+                           ->  Hash
+                                 Output: plt2_p2.b, plt2_p2.c
+                                 ->  Seq Scan on public.plt2_p2
+                                       Output: plt2_p2.b, plt2_p2.c
+                                       Filter: ((plt2_p2.b % 25) = 0)
+                     ->  Hash Full Join
+                           Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c
+                           Hash Cond: ((plt1_p3.c = plt2_p3.c) AND (plt1_p3.a = plt2_p3.b))
+                           ->  Seq Scan on public.plt1_p3
+                                 Output: plt1_p3.a, plt1_p3.c
+                                 Filter: ((plt1_p3.a % 25) = 0)
+                           ->  Hash
+                                 Output: plt2_p3.b, plt2_p3.c
+                                 ->  Seq Scan on public.plt2_p3
+                                       Output: plt2_p3.b, plt2_p3.c
+                                       Filter: ((plt2_p3.b % 25) = 0)
+(42 rows)
+
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM (SELECT 50 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+ sum |  c   |          avg           |  c   
+-----+------+------------------------+------
+   0 | 0000 | 0.00000000000000000000 | 0000
+  50 | 0001 |                        | 
+ 100 | 0002 |                        | 
+ 150 | 0003 |   150.0000000000000000 | 0003
+ 200 | 0004 |                        | 
+ 250 | 0005 |                        | 
+ 300 | 0006 |   300.0000000000000000 | 0006
+ 350 | 0007 |                        | 
+ 400 | 0008 |                        | 
+ 450 | 0009 |   450.0000000000000000 | 0009
+ 500 | 0010 |                        | 
+ 550 | 0011 |                        | 
+     |      |    75.0000000000000000 | 0001
+     |      |   225.0000000000000000 | 0004
+     |      |   375.0000000000000000 | 0007
+     |      |   525.0000000000000000 | 0010
+(16 rows)
+
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM (SELECT 50 phv, * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+ sum |  c   |          avg           |  c   
+-----+------+------------------------+------
+   0 | 0000 | 0.00000000000000000000 | 0000
+  50 | 0001 |                        | 
+ 100 | 0002 |                        | 
+ 150 | 0003 |   150.0000000000000000 | 0003
+ 200 | 0004 |                        | 
+ 250 | 0005 |                        | 
+ 300 | 0006 |   300.0000000000000000 | 0006
+ 350 | 0007 |                        | 
+ 400 | 0008 |                        | 
+ 450 | 0009 |   450.0000000000000000 | 0009
+ 500 | 0010 |                        | 
+ 550 | 0011 |                        | 
+     |      |    75.0000000000000000 | 0001
+     |      |   225.0000000000000000 | 0004
+     |      |   375.0000000000000000 | 0007
+     |      |   525.0000000000000000 | 0010
+(16 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, sum(t1.phv), avg(t2.b), t2.c, avg(t2.phv) FROM (SELECT 25 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ GroupAggregate
+   Output: sum(plt1_p1.a), plt1_p1.c, sum((25)), avg(plt2_p1.b), plt2_p1.c, avg((50))
+   Group Key: plt1_p1.c, plt2_p1.c
+   ->  Sort
+         Output: plt1_p1.c, plt2_p1.c, plt1_p1.a, (25), plt2_p1.b, (50)
+         Sort Key: plt1_p1.c, plt2_p1.c
+         ->  Result
+               Output: plt1_p1.c, plt2_p1.c, plt1_p1.a, (25), plt2_p1.b, (50)
+               ->  Append
+                     ->  Hash Full Join
+                           Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, (25), (50)
+                           Hash Cond: ((plt1_p1.c = plt2_p1.c) AND (plt1_p1.a = plt2_p1.b))
+                           ->  Seq Scan on public.plt1_p1
+                                 Output: plt1_p1.a, plt1_p1.c, 25
+                                 Filter: ((plt1_p1.a % 25) = 0)
+                           ->  Hash
+                                 Output: plt2_p1.b, plt2_p1.c, (50)
+                                 ->  Seq Scan on public.plt2_p1
+                                       Output: plt2_p1.b, plt2_p1.c, 50
+                                       Filter: ((plt2_p1.b % 25) = 0)
+                     ->  Hash Full Join
+                           Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c, (25), (50)
+                           Hash Cond: ((plt1_p2.c = plt2_p2.c) AND (plt1_p2.a = plt2_p2.b))
+                           ->  Seq Scan on public.plt1_p2
+                                 Output: plt1_p2.a, plt1_p2.c, 25
+                                 Filter: ((plt1_p2.a % 25) = 0)
+                           ->  Hash
+                                 Output: plt2_p2.b, plt2_p2.c, (50)
+                                 ->  Seq Scan on public.plt2_p2
+                                       Output: plt2_p2.b, plt2_p2.c, 50
+                                       Filter: ((plt2_p2.b % 25) = 0)
+                     ->  Hash Full Join
+                           Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c, (25), (50)
+                           Hash Cond: ((plt1_p3.c = plt2_p3.c) AND (plt1_p3.a = plt2_p3.b))
+                           ->  Seq Scan on public.plt1_p3
+                                 Output: plt1_p3.a, plt1_p3.c, 25
+                                 Filter: ((plt1_p3.a % 25) = 0)
+                           ->  Hash
+                                 Output: plt2_p3.b, plt2_p3.c, (50)
+                                 ->  Seq Scan on public.plt2_p3
+                                       Output: plt2_p3.b, plt2_p3.c, 50
+                                       Filter: ((plt2_p3.b % 25) = 0)
+(42 rows)
+
+SELECT sum(t1.a), t1.c, sum(t1.phv), avg(t2.b), t2.c, avg(t2.phv) FROM (SELECT 25 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+ sum |  c   | sum |          avg           |  c   |         avg         
+-----+------+-----+------------------------+------+---------------------
+   0 | 0000 |  25 | 0.00000000000000000000 | 0000 | 50.0000000000000000
+  50 | 0001 |  25 |                        |      |                    
+ 100 | 0002 |  25 |                        |      |                    
+ 150 | 0003 |  25 |   150.0000000000000000 | 0003 | 50.0000000000000000
+ 200 | 0004 |  25 |                        |      |                    
+ 250 | 0005 |  25 |                        |      |                    
+ 300 | 0006 |  25 |   300.0000000000000000 | 0006 | 50.0000000000000000
+ 350 | 0007 |  25 |                        |      |                    
+ 400 | 0008 |  25 |                        |      |                    
+ 450 | 0009 |  25 |   450.0000000000000000 | 0009 | 50.0000000000000000
+ 500 | 0010 |  25 |                        |      |                    
+ 550 | 0011 |  25 |                        |      |                    
+     |      |     |    75.0000000000000000 | 0001 | 50.0000000000000000
+     |      |     |   225.0000000000000000 | 0004 | 50.0000000000000000
+     |      |     |   375.0000000000000000 | 0007 | 50.0000000000000000
+     |      |     |   525.0000000000000000 | 0010 | 50.0000000000000000
+(16 rows)
+
+SELECT sum(t1.a), t1.c, sum(t1.phv), avg(t2.b), t2.c, avg(t2.phv) FROM (SELECT 25 phv, * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+ sum |  c   | sum |          avg           |  c   |         avg         
+-----+------+-----+------------------------+------+---------------------
+   0 | 0000 |  25 | 0.00000000000000000000 | 0000 | 50.0000000000000000
+  50 | 0001 |  25 |                        |      |                    
+ 100 | 0002 |  25 |                        |      |                    
+ 150 | 0003 |  25 |   150.0000000000000000 | 0003 | 50.0000000000000000
+ 200 | 0004 |  25 |                        |      |                    
+ 250 | 0005 |  25 |                        |      |                    
+ 300 | 0006 |  25 |   300.0000000000000000 | 0006 | 50.0000000000000000
+ 350 | 0007 |  25 |                        |      |                    
+ 400 | 0008 |  25 |                        |      |                    
+ 450 | 0009 |  25 |   450.0000000000000000 | 0009 | 50.0000000000000000
+ 500 | 0010 |  25 |                        |      |                    
+ 550 | 0011 |  25 |                        |      |                    
+     |      |     |    75.0000000000000000 | 0001 | 50.0000000000000000
+     |      |     |   225.0000000000000000 | 0004 | 50.0000000000000000
+     |      |     |   375.0000000000000000 | 0007 | 50.0000000000000000
+     |      |     |   525.0000000000000000 | 0010 | 50.0000000000000000
+(16 rows)
+
+-- Join with pruned partitions from joining relations
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.c NOT IN ('0001', '0005', '0002', '0009') AND t2.c NOT IN ('0000', '0003', '0004', '0010') GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+                                          QUERY PLAN                                           
+-----------------------------------------------------------------------------------------------
+ Sort
+   Output: (sum(t1.a)), t1.c, (avg(t2.b)), t2.c
+   Sort Key: t1.c
+   ->  HashAggregate
+         Output: sum(t1.a), t1.c, avg(t2.b), t2.c
+         Group Key: t1.c, t2.c
+         ->  Result
+               Output: t1.c, t2.c, t1.a, t2.b
+               ->  Append
+                     ->  Hash Join
+                           Output: t1.a, t1.c, t2.b, t2.c
+                           Hash Cond: (t1.c = t2.c)
+                           ->  Seq Scan on public.plt1_p3 t1
+                                 Output: t1.a, t1.c
+                                 Filter: (t1.c <> ALL ('{0001,0005,0002,0009}'::text[]))
+                           ->  Hash
+                                 Output: t2.b, t2.c
+                                 ->  Seq Scan on public.plt2_p3 t2
+                                       Output: t2.b, t2.c
+                                       Filter: (t2.c <> ALL ('{0000,0003,0004,0010}'::text[]))
+(20 rows)
+
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.c NOT IN ('0001', '0005', '0002', '0009') AND t2.c NOT IN ('0000', '0003', '0004', '0010') GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+  sum   |  c   |         avg          |  c   
+--------+------+----------------------+------
+ 137700 | 0006 | 324.0000000000000000 | 0006
+ 158950 | 0007 | 375.0000000000000000 | 0007
+ 169600 | 0008 | 424.5000000000000000 | 0008
+ 229600 | 0011 | 574.5000000000000000 | 0011
+(4 rows)
+
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM uplt1 t1, uplt2 t2 WHERE t1.c = t2.c AND t1.c NOT IN ('0001', '0005', '0002', '0009') AND t2.c NOT IN ('0000', '0003', '0004', '0010') GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+  sum   |  c   |         avg          |  c   
+--------+------+----------------------+------
+ 137700 | 0006 | 324.0000000000000000 | 0006
+ 158950 | 0007 | 375.0000000000000000 | 0007
+ 169600 | 0008 | 424.5000000000000000 | 0008
+ 229600 | 0011 | 574.5000000000000000 | 0011
+(4 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 LEFT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+                                          QUERY PLAN                                           
+-----------------------------------------------------------------------------------------------
+ Sort
+   Output: (sum(t1.a)), t1.c, (sum(b)), c
+   Sort Key: t1.c, c
+   ->  HashAggregate
+         Output: sum(t1.a), t1.c, sum(b), c
+         Group Key: t1.c, c
+         ->  Result
+               Output: t1.c, c, t1.a, b
+               ->  Append
+                     ->  Hash Left Join
+                           Output: t1.a, t1.c, b, c
+                           Hash Cond: (t1.c = c)
+                           ->  Seq Scan on public.plt1_p1 t1
+                                 Output: t1.a, t1.c
+                                 Filter: (t1.c <> ALL ('{0001,0005,0002,0009}'::text[]))
+                           ->  Hash
+                                 Output: b, c
+                                 ->  Result
+                                       Output: b, c
+                                       One-Time Filter: false
+                     ->  Hash Left Join
+                           Output: t1_1.a, t1_1.c, t2.b, t2.c
+                           Hash Cond: (t1_1.c = t2.c)
+                           ->  Seq Scan on public.plt1_p3 t1_1
+                                 Output: t1_1.a, t1_1.c
+                                 Filter: (t1_1.c <> ALL ('{0001,0005,0002,0009}'::text[]))
+                           ->  Hash
+                                 Output: t2.b, t2.c
+                                 ->  Seq Scan on public.plt2_p3 t2
+                                       Output: t2.b, t2.c
+                                       Filter: (t2.c <> ALL ('{0000,0003,0004,0010}'::text[]))
+(31 rows)
+
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 LEFT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+  sum   |  c   |  sum   |  c   
+--------+------+--------+------
+    600 | 0000 |        | 
+   4350 | 0003 |        | 
+   5600 | 0004 |        | 
+ 137700 | 0006 | 137700 | 0006
+ 158950 | 0007 | 159375 | 0007
+ 169600 | 0008 | 169800 | 0008
+  13100 | 0010 |        | 
+ 229600 | 0011 | 229800 | 0011
+(8 rows)
+
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 LEFT JOIN (SELECT * FROM uplt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+  sum   |  c   |  sum   |  c   
+--------+------+--------+------
+    600 | 0000 |        | 
+   4350 | 0003 |        | 
+   5600 | 0004 |        | 
+ 137700 | 0006 | 137700 | 0006
+ 158950 | 0007 | 159375 | 0007
+ 169600 | 0008 | 169800 | 0008
+  13100 | 0010 |        | 
+ 229600 | 0011 | 229800 | 0011
+(8 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 RIGHT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE:  join between relations (b 5) and (b 4) is considered for partition-wise join.
+                                           QUERY PLAN                                            
+-------------------------------------------------------------------------------------------------
+ Sort
+   Output: (sum(a)), c, (sum(t2.b)), t2.c
+   Sort Key: c, t2.c
+   ->  HashAggregate
+         Output: sum(a), c, sum(t2.b), t2.c
+         Group Key: c, t2.c
+         ->  Result
+               Output: c, t2.c, a, t2.b
+               ->  Append
+                     ->  Hash Left Join
+                           Output: t2.b, t2.c, a, c
+                           Hash Cond: (t2.c = c)
+                           ->  Seq Scan on public.plt2_p2 t2
+                                 Output: t2.b, t2.c
+                                 Filter: (t2.c <> ALL ('{0000,0003,0004,0010}'::text[]))
+                           ->  Hash
+                                 Output: a, c
+                                 ->  Result
+                                       Output: a, c
+                                       One-Time Filter: false
+                     ->  Hash Right Join
+                           Output: t2_1.b, t2_1.c, t1.a, t1.c
+                           Hash Cond: (t1.c = t2_1.c)
+                           ->  Seq Scan on public.plt1_p3 t1
+                                 Output: t1.a, t1.c
+                                 Filter: (t1.c <> ALL ('{0001,0005,0002,0009}'::text[]))
+                           ->  Hash
+                                 Output: t2_1.b, t2_1.c
+                                 ->  Seq Scan on public.plt2_p3 t2_1
+                                       Output: t2_1.b, t2_1.c
+                                       Filter: (t2_1.c <> ALL ('{0000,0003,0004,0010}'::text[]))
+(31 rows)
+
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 RIGHT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE:  join between relations (b 5) and (b 4) is considered for partition-wise join.
+  sum   |  c   |  sum   |  c   
+--------+------+--------+------
+ 137700 | 0006 | 137700 | 0006
+ 158950 | 0007 | 159375 | 0007
+ 169600 | 0008 | 169800 | 0008
+ 229600 | 0011 | 229800 | 0011
+        |      |   1275 | 0001
+        |      |   1992 | 0002
+        |      |   4392 | 0005
+        |      |   8058 | 0009
+(8 rows)
+
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 RIGHT JOIN (SELECT * FROM uplt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+  sum   |  c   |  sum   |  c   
+--------+------+--------+------
+ 137700 | 0006 | 137700 | 0006
+ 158950 | 0007 | 159375 | 0007
+ 169600 | 0008 | 169800 | 0008
+ 229600 | 0011 | 229800 | 0011
+        |      |   1275 | 0001
+        |      |   1992 | 0002
+        |      |   4392 | 0005
+        |      |   8058 | 0009
+(8 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 FULL JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+                                           QUERY PLAN                                            
+-------------------------------------------------------------------------------------------------
+ Sort
+   Output: (sum(t1.a)), t1.c, (sum(b)), c
+   Sort Key: t1.c, c
+   ->  HashAggregate
+         Output: sum(t1.a), t1.c, sum(b), c
+         Group Key: t1.c, c
+         ->  Result
+               Output: t1.c, c, t1.a, b
+               ->  Append
+                     ->  Hash Full Join
+                           Output: t1.a, t1.c, b, c
+                           Hash Cond: (t1.c = c)
+                           ->  Seq Scan on public.plt1_p1 t1
+                                 Output: t1.a, t1.c
+                                 Filter: (t1.c <> ALL ('{0001,0005,0002,0009}'::text[]))
+                           ->  Hash
+                                 Output: b, c
+                                 ->  Result
+                                       Output: b, c
+                                       One-Time Filter: false
+                     ->  Hash Full Join
+                           Output: a, c, t2.b, t2.c
+                           Hash Cond: (t2.c = c)
+                           ->  Seq Scan on public.plt2_p2 t2
+                                 Output: t2.b, t2.c
+                                 Filter: (t2.c <> ALL ('{0000,0003,0004,0010}'::text[]))
+                           ->  Hash
+                                 Output: a, c
+                                 ->  Result
+                                       Output: a, c
+                                       One-Time Filter: false
+                     ->  Hash Full Join
+                           Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+                           Hash Cond: (t1_1.c = t2_1.c)
+                           ->  Seq Scan on public.plt1_p3 t1_1
+                                 Output: t1_1.a, t1_1.c
+                                 Filter: (t1_1.c <> ALL ('{0001,0005,0002,0009}'::text[]))
+                           ->  Hash
+                                 Output: t2_1.b, t2_1.c
+                                 ->  Seq Scan on public.plt2_p3 t2_1
+                                       Output: t2_1.b, t2_1.c
+                                       Filter: (t2_1.c <> ALL ('{0000,0003,0004,0010}'::text[]))
+(42 rows)
+
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 FULL JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+  sum   |  c   |  sum   |  c   
+--------+------+--------+------
+    600 | 0000 |        | 
+   4350 | 0003 |        | 
+   5600 | 0004 |        | 
+ 137700 | 0006 | 137700 | 0006
+ 158950 | 0007 | 159375 | 0007
+ 169600 | 0008 | 169800 | 0008
+  13100 | 0010 |        | 
+ 229600 | 0011 | 229800 | 0011
+        |      |   1275 | 0001
+        |      |   1992 | 0002
+        |      |   4392 | 0005
+        |      |   8058 | 0009
+(12 rows)
+
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 FULL JOIN (SELECT * FROM uplt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+  sum   |  c   |  sum   |  c   
+--------+------+--------+------
+    600 | 0000 |        | 
+   4350 | 0003 |        | 
+   5600 | 0004 |        | 
+ 137700 | 0006 | 137700 | 0006
+ 158950 | 0007 | 159375 | 0007
+ 169600 | 0008 | 169800 | 0008
+  13100 | 0010 |        | 
+ 229600 | 0011 | 229800 | 0011
+        |      |   1275 | 0001
+        |      |   1992 | 0002
+        |      |   4392 | 0005
+        |      |   8058 | 0009
+(12 rows)
+
+-- Semi-join
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 1) and (b 3) is considered for partition-wise join.
+                       QUERY PLAN                        
+---------------------------------------------------------
+ Sort
+   Output: t1.a, t1.b, t1.c
+   Sort Key: t1.a
+   ->  Append
+         ->  Nested Loop Semi Join
+               Output: t1.a, t1.b, t1.c
+               Join Filter: (t1.c = t1_3.c)
+               ->  Seq Scan on public.plt1_p1 t1
+                     Output: t1.a, t1.b, t1.c
+                     Filter: ((t1.a % 25) = 0)
+               ->  Materialize
+                     Output: t1_3.c
+                     ->  Seq Scan on public.plt2_p1 t1_3
+                           Output: t1_3.c
+                           Filter: ((t1_3.b % 25) = 0)
+         ->  Nested Loop Semi Join
+               Output: t1_1.a, t1_1.b, t1_1.c
+               Join Filter: (t1_1.c = t1_4.c)
+               ->  Seq Scan on public.plt1_p2 t1_1
+                     Output: t1_1.a, t1_1.b, t1_1.c
+                     Filter: ((t1_1.a % 25) = 0)
+               ->  Materialize
+                     Output: t1_4.c
+                     ->  Seq Scan on public.plt2_p2 t1_4
+                           Output: t1_4.c
+                           Filter: ((t1_4.b % 25) = 0)
+         ->  Nested Loop Semi Join
+               Output: t1_2.a, t1_2.b, t1_2.c
+               Join Filter: (t1_2.c = t1_5.c)
+               ->  Seq Scan on public.plt1_p3 t1_2
+                     Output: t1_2.a, t1_2.b, t1_2.c
+                     Filter: ((t1_2.a % 25) = 0)
+               ->  Materialize
+                     Output: t1_5.c
+                     ->  Seq Scan on public.plt2_p3 t1_5
+                           Output: t1_5.c
+                           Filter: ((t1_5.b % 25) = 0)
+(37 rows)
+
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 1) and (b 3) is considered for partition-wise join.
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+  50 |  50 | 0001
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+(8 rows)
+
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+  50 |  50 | 0001
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+(8 rows)
+
+--
+-- 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;
+ANALYZE plt1_e_p1;
+ANALYZE plt1_e_p2;
+ANALYZE plt1_e_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uplt1_e AS SELECT * FROM plt1_e;
+CREATE TABLE plt2_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A'));
+CREATE TABLE plt2_e_p1 PARTITION OF plt2_e FOR VALUES IN ('0000', '0003', '0004', '0010');
+CREATE TABLE plt2_e_p2 PARTITION OF plt2_e FOR VALUES IN ('0001', '0005', '0002', '0009');
+CREATE TABLE plt2_e_p3 PARTITION OF plt2_e FOR VALUES IN ('0006', '0007', '0008', '0011');
+INSERT INTO plt2_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i;
+ANALYZE plt2_e;
+ANALYZE plt2_e_p1;
+ANALYZE plt2_e_p2;
+ANALYZE plt2_e_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uplt2_e AS SELECT * FROM plt2_e;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1, plt2_e t2 WHERE t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+                                               QUERY PLAN                                               
+--------------------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c
+   Sort Key: t1.a
+   ->  Append
+         ->  Hash Join
+               Output: t1.a, t1.c, t2.b, t2.c
+               Hash Cond: ((t2.b = t1.a) AND (ltrim(t2.c, 'A'::text) = ltrim(t1.c, 'A'::text)))
+               ->  Seq Scan on public.plt2_e_p1 t2
+                     Output: t2.b, t2.c
+               ->  Hash
+                     Output: t1.a, t1.c
+                     ->  Seq Scan on public.plt1_e_p1 t1
+                           Output: t1.a, t1.c
+                           Filter: ((t1.a % 25) = 0)
+         ->  Hash Join
+               Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+               Hash Cond: ((t2_1.b = t1_1.a) AND (ltrim(t2_1.c, 'A'::text) = ltrim(t1_1.c, 'A'::text)))
+               ->  Seq Scan on public.plt2_e_p2 t2_1
+                     Output: t2_1.b, t2_1.c
+               ->  Hash
+                     Output: t1_1.a, t1_1.c
+                     ->  Seq Scan on public.plt1_e_p2 t1_1
+                           Output: t1_1.a, t1_1.c
+                           Filter: ((t1_1.a % 25) = 0)
+         ->  Hash Join
+               Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+               Hash Cond: ((t2_2.b = t1_2.a) AND (ltrim(t2_2.c, 'A'::text) = ltrim(t1_2.c, 'A'::text)))
+               ->  Seq Scan on public.plt2_e_p3 t2_2
+                     Output: t2_2.b, t2_2.c
+               ->  Hash
+                     Output: t1_2.a, t1_2.c
+                     ->  Seq Scan on public.plt1_e_p3 t1_2
+                           Output: t1_2.a, t1_2.c
+                           Filter: ((t1_2.a % 25) = 0)
+(34 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1, plt2_e t2 WHERE t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+  a  |   c   |  b  |   c   
+-----+-------+-----+-------
+   0 | A0000 |   0 | A0000
+ 150 | A0003 | 150 | A0003
+ 300 | A0006 | 300 | A0006
+ 450 | A0009 | 450 | A0009
+(4 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1_e t1, uplt2_e t2 WHERE t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+  a  |   c   |  b  |   c   
+-----+-------+-----+-------
+   0 | A0000 |   0 | A0000
+ 150 | A0003 | 150 | A0003
+ 300 | A0006 | 300 | A0006
+ 450 | A0009 | 450 | A0009
+(4 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 LEFT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+                                               QUERY PLAN                                               
+--------------------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c
+   Sort Key: t1.a, t2.b
+   ->  Append
+         ->  Hash Right Join
+               Output: t1.a, t1.c, t2.b, t2.c
+               Hash Cond: ((t2.b = t1.a) AND (ltrim(t2.c, 'A'::text) = ltrim(t1.c, 'A'::text)))
+               ->  Seq Scan on public.plt2_e_p1 t2
+                     Output: t2.b, t2.c
+               ->  Hash
+                     Output: t1.a, t1.c
+                     ->  Seq Scan on public.plt1_e_p1 t1
+                           Output: t1.a, t1.c
+                           Filter: ((t1.a % 25) = 0)
+         ->  Hash Right Join
+               Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+               Hash Cond: ((t2_1.b = t1_1.a) AND (ltrim(t2_1.c, 'A'::text) = ltrim(t1_1.c, 'A'::text)))
+               ->  Seq Scan on public.plt2_e_p2 t2_1
+                     Output: t2_1.b, t2_1.c
+               ->  Hash
+                     Output: t1_1.a, t1_1.c
+                     ->  Seq Scan on public.plt1_e_p2 t1_1
+                           Output: t1_1.a, t1_1.c
+                           Filter: ((t1_1.a % 25) = 0)
+         ->  Hash Right Join
+               Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+               Hash Cond: ((t2_2.b = t1_2.a) AND (ltrim(t2_2.c, 'A'::text) = ltrim(t1_2.c, 'A'::text)))
+               ->  Seq Scan on public.plt2_e_p3 t2_2
+                     Output: t2_2.b, t2_2.c
+               ->  Hash
+                     Output: t1_2.a, t1_2.c
+                     ->  Seq Scan on public.plt1_e_p3 t1_2
+                           Output: t1_2.a, t1_2.c
+                           Filter: ((t1_2.a % 25) = 0)
+(34 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 LEFT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+  a  |   c   |  b  |   c   
+-----+-------+-----+-------
+   0 | A0000 |   0 | A0000
+  50 | A0001 |     | 
+ 100 | A0002 |     | 
+ 150 | A0003 | 150 | A0003
+ 200 | A0004 |     | 
+ 250 | A0005 |     | 
+ 300 | A0006 | 300 | A0006
+ 350 | A0007 |     | 
+ 400 | A0008 |     | 
+ 450 | A0009 | 450 | A0009
+ 500 | A0010 |     | 
+ 550 | A0011 |     | 
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1_e t1 LEFT JOIN uplt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+  a  |   c   |  b  |   c   
+-----+-------+-----+-------
+   0 | A0000 |   0 | A0000
+  50 | A0001 |     | 
+ 100 | A0002 |     | 
+ 150 | A0003 | 150 | A0003
+ 200 | A0004 |     | 
+ 250 | A0005 |     | 
+ 300 | A0006 | 300 | A0006
+ 350 | A0007 |     | 
+ 400 | A0008 |     | 
+ 450 | A0009 | 450 | A0009
+ 500 | A0010 |     | 
+ 550 | A0011 |     | 
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 RIGHT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+                                                  QUERY PLAN                                                  
+--------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c
+   Sort Key: t1.a, t2.b
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c
+         ->  Append
+               ->  Hash Right Join
+                     Output: t2.b, t2.c, t1.a, t1.c
+                     Hash Cond: ((t1.a = t2.b) AND (ltrim(t1.c, 'A'::text) = ltrim(t2.c, 'A'::text)))
+                     ->  Seq Scan on public.plt1_e_p1 t1
+                           Output: t1.a, t1.c
+                     ->  Hash
+                           Output: t2.b, t2.c
+                           ->  Seq Scan on public.plt2_e_p1 t2
+                                 Output: t2.b, t2.c
+                                 Filter: ((t2.b % 25) = 0)
+               ->  Hash Right Join
+                     Output: t2_1.b, t2_1.c, t1_1.a, t1_1.c
+                     Hash Cond: ((t1_1.a = t2_1.b) AND (ltrim(t1_1.c, 'A'::text) = ltrim(t2_1.c, 'A'::text)))
+                     ->  Seq Scan on public.plt1_e_p2 t1_1
+                           Output: t1_1.a, t1_1.c
+                     ->  Hash
+                           Output: t2_1.b, t2_1.c
+                           ->  Seq Scan on public.plt2_e_p2 t2_1
+                                 Output: t2_1.b, t2_1.c
+                                 Filter: ((t2_1.b % 25) = 0)
+               ->  Hash Right Join
+                     Output: t2_2.b, t2_2.c, t1_2.a, t1_2.c
+                     Hash Cond: ((t1_2.a = t2_2.b) AND (ltrim(t1_2.c, 'A'::text) = ltrim(t2_2.c, 'A'::text)))
+                     ->  Seq Scan on public.plt1_e_p3 t1_2
+                           Output: t1_2.a, t1_2.c
+                     ->  Hash
+                           Output: t2_2.b, t2_2.c
+                           ->  Seq Scan on public.plt2_e_p3 t2_2
+                                 Output: t2_2.b, t2_2.c
+                                 Filter: ((t2_2.b % 25) = 0)
+(36 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 RIGHT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+  a  |   c   |  b  |   c   
+-----+-------+-----+-------
+   0 | A0000 |   0 | A0000
+ 150 | A0003 | 150 | A0003
+ 300 | A0006 | 300 | A0006
+ 450 | A0009 | 450 | A0009
+     |       |  75 | A0001
+     |       | 225 | A0004
+     |       | 375 | A0007
+     |       | 525 | A0010
+(8 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1_e t1 RIGHT JOIN uplt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+  a  |   c   |  b  |   c   
+-----+-------+-----+-------
+   0 | A0000 |   0 | A0000
+ 150 | A0003 | 150 | A0003
+ 300 | A0006 | 300 | A0006
+ 450 | A0009 | 450 | A0009
+     |       |  75 | A0001
+     |       | 225 | A0004
+     |       | 375 | A0007
+     |       | 525 | A0010
+(8 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2_e WHERE plt2_e.b % 25 = 0) t2 ON (t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A')) ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+                                                         QUERY PLAN                                                         
+----------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: plt1_e_p1.a, plt1_e_p1.c, plt2_e_p1.b, plt2_e_p1.c
+   Sort Key: plt1_e_p1.a, plt2_e_p1.b
+   ->  Append
+         ->  Hash Full Join
+               Output: plt1_e_p1.a, plt1_e_p1.c, plt2_e_p1.b, plt2_e_p1.c
+               Hash Cond: ((plt1_e_p1.a = plt2_e_p1.b) AND (ltrim(plt1_e_p1.c, 'A'::text) = ltrim(plt2_e_p1.c, 'A'::text)))
+               ->  Seq Scan on public.plt1_e_p1
+                     Output: plt1_e_p1.a, plt1_e_p1.c
+                     Filter: ((plt1_e_p1.a % 25) = 0)
+               ->  Hash
+                     Output: plt2_e_p1.b, plt2_e_p1.c
+                     ->  Seq Scan on public.plt2_e_p1
+                           Output: plt2_e_p1.b, plt2_e_p1.c
+                           Filter: ((plt2_e_p1.b % 25) = 0)
+         ->  Hash Full Join
+               Output: plt1_e_p2.a, plt1_e_p2.c, plt2_e_p2.b, plt2_e_p2.c
+               Hash Cond: ((plt1_e_p2.a = plt2_e_p2.b) AND (ltrim(plt1_e_p2.c, 'A'::text) = ltrim(plt2_e_p2.c, 'A'::text)))
+               ->  Seq Scan on public.plt1_e_p2
+                     Output: plt1_e_p2.a, plt1_e_p2.c
+                     Filter: ((plt1_e_p2.a % 25) = 0)
+               ->  Hash
+                     Output: plt2_e_p2.b, plt2_e_p2.c
+                     ->  Seq Scan on public.plt2_e_p2
+                           Output: plt2_e_p2.b, plt2_e_p2.c
+                           Filter: ((plt2_e_p2.b % 25) = 0)
+         ->  Hash Full Join
+               Output: plt1_e_p3.a, plt1_e_p3.c, plt2_e_p3.b, plt2_e_p3.c
+               Hash Cond: ((plt1_e_p3.a = plt2_e_p3.b) AND (ltrim(plt1_e_p3.c, 'A'::text) = ltrim(plt2_e_p3.c, 'A'::text)))
+               ->  Seq Scan on public.plt1_e_p3
+                     Output: plt1_e_p3.a, plt1_e_p3.c
+                     Filter: ((plt1_e_p3.a % 25) = 0)
+               ->  Hash
+                     Output: plt2_e_p3.b, plt2_e_p3.c
+                     ->  Seq Scan on public.plt2_e_p3
+                           Output: plt2_e_p3.b, plt2_e_p3.c
+                           Filter: ((plt2_e_p3.b % 25) = 0)
+(37 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2_e WHERE plt2_e.b % 25 = 0) t2 ON (t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A')) ORDER BY t1.a, t2.b;
+NOTICE:  join between relations (b 4) and (b 5) is considered for partition-wise join.
+  a  |   c   |  b  |   c   
+-----+-------+-----+-------
+   0 | A0000 |   0 | A0000
+  50 | A0001 |     | 
+ 100 | A0002 |     | 
+ 150 | A0003 | 150 | A0003
+ 200 | A0004 |     | 
+ 250 | A0005 |     | 
+ 300 | A0006 | 300 | A0006
+ 350 | A0007 |     | 
+ 400 | A0008 |     | 
+ 450 | A0009 | 450 | A0009
+ 500 | A0010 |     | 
+ 550 | A0011 |     | 
+     |       |  75 | A0001
+     |       | 225 | A0004
+     |       | 375 | A0007
+     |       | 525 | A0010
+(16 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uplt1_e t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2_e t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A')) ORDER BY t1.a, t2.b;
+  a  |   c   |  b  |   c   
+-----+-------+-----+-------
+   0 | A0000 |   0 | A0000
+  50 | A0001 |     | 
+ 100 | A0002 |     | 
+ 150 | A0003 | 150 | A0003
+ 200 | A0004 |     | 
+ 250 | A0005 |     | 
+ 300 | A0006 | 300 | A0006
+ 350 | A0007 |     | 
+ 400 | A0008 |     | 
+ 450 | A0009 | 450 | A0009
+ 500 | A0010 |     | 
+ 550 | A0011 |     | 
+     |       |  75 | A0001
+     |       | 225 | A0004
+     |       | 375 | A0007
+     |       | 525 | A0010
+(16 rows)
+
+--
+-- N-way join
+--
+EXPLAIN (VERBOSE, 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.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;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 3) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 2) and (b 3) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 3) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2 3) and (b 1) is considered for partition-wise join.
+                                        QUERY PLAN                                        
+------------------------------------------------------------------------------------------
+ Sort
+   Output: (avg(t1.a)), (avg(t2.b)), (avg((t3.a + t3.b))), t1.c, t2.c, t3.c
+   Sort Key: t1.c, t3.c
+   ->  HashAggregate
+         Output: avg(t1.a), avg(t2.b), avg((t3.a + t3.b)), t1.c, t2.c, t3.c
+         Group Key: t1.c, t2.c, t3.c
+         ->  Result
+               Output: t1.c, t2.c, t3.c, t1.a, t2.b, t3.a, t3.b
+               ->  Append
+                     ->  Hash Join
+                           Output: t1.a, t1.c, t2.b, t2.c, t3.a, t3.b, t3.c
+                           Hash Cond: (t1.c = t2.c)
+                           ->  Seq Scan on public.plt1_p1 t1
+                                 Output: t1.a, t1.c
+                           ->  Hash
+                                 Output: t2.b, t2.c, t3.a, t3.b, t3.c
+                                 ->  Hash Join
+                                       Output: t2.b, t2.c, t3.a, t3.b, t3.c
+                                       Hash Cond: (t2.c = ltrim(t3.c, 'A'::text))
+                                       ->  Seq Scan on public.plt2_p1 t2
+                                             Output: t2.b, t2.c
+                                       ->  Hash
+                                             Output: t3.a, t3.b, t3.c
+                                             ->  Seq Scan on public.plt1_e_p1 t3
+                                                   Output: t3.a, t3.b, t3.c
+                     ->  Hash Join
+                           Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c
+                           Hash Cond: (t1_1.c = t2_1.c)
+                           ->  Seq Scan on public.plt1_p2 t1_1
+                                 Output: t1_1.a, t1_1.c
+                           ->  Hash
+                                 Output: t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c
+                                 ->  Hash Join
+                                       Output: t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c
+                                       Hash Cond: (t2_1.c = ltrim(t3_1.c, 'A'::text))
+                                       ->  Seq Scan on public.plt2_p2 t2_1
+                                             Output: t2_1.b, t2_1.c
+                                       ->  Hash
+                                             Output: t3_1.a, t3_1.b, t3_1.c
+                                             ->  Seq Scan on public.plt1_e_p2 t3_1
+                                                   Output: t3_1.a, t3_1.b, t3_1.c
+                     ->  Hash Join
+                           Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c
+                           Hash Cond: (t1_2.c = t2_2.c)
+                           ->  Seq Scan on public.plt1_p3 t1_2
+                                 Output: t1_2.a, t1_2.c
+                           ->  Hash
+                                 Output: t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c
+                                 ->  Hash Join
+                                       Output: t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c
+                                       Hash Cond: (t2_2.c = ltrim(t3_2.c, 'A'::text))
+                                       ->  Seq Scan on public.plt2_p3 t2_2
+                                             Output: t2_2.b, t2_2.c
+                                       ->  Hash
+                                             Output: t3_2.a, t3_2.b, t3_2.c
+                                             ->  Seq Scan on public.plt1_e_p3 t3_2
+                                                   Output: t3_2.a, t3_2.b, t3_2.c
+(57 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.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;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 3) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 2) and (b 3) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 3) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2 3) and (b 1) is considered for partition-wise join.
+         avg          |         avg          |          avg          |  c   |  c   |   c   
+----------------------+----------------------+-----------------------+------+------+-------
+  24.0000000000000000 |  24.0000000000000000 |   48.0000000000000000 | 0000 | 0000 | A0000
+  74.0000000000000000 |  75.0000000000000000 |  148.0000000000000000 | 0001 | 0001 | A0001
+ 124.0000000000000000 | 124.5000000000000000 |  248.0000000000000000 | 0002 | 0002 | A0002
+ 174.0000000000000000 | 174.0000000000000000 |  348.0000000000000000 | 0003 | 0003 | A0003
+ 224.0000000000000000 | 225.0000000000000000 |  448.0000000000000000 | 0004 | 0004 | A0004
+ 274.0000000000000000 | 274.5000000000000000 |  548.0000000000000000 | 0005 | 0005 | A0005
+ 324.0000000000000000 | 324.0000000000000000 |  648.0000000000000000 | 0006 | 0006 | A0006
+ 374.0000000000000000 | 375.0000000000000000 |  748.0000000000000000 | 0007 | 0007 | A0007
+ 424.0000000000000000 | 424.5000000000000000 |  848.0000000000000000 | 0008 | 0008 | A0008
+ 474.0000000000000000 | 474.0000000000000000 |  948.0000000000000000 | 0009 | 0009 | A0009
+ 524.0000000000000000 | 525.0000000000000000 | 1048.0000000000000000 | 0010 | 0010 | A0010
+ 574.0000000000000000 | 574.5000000000000000 | 1148.0000000000000000 | 0011 | 0011 | A0011
+(12 rows)
+
+SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM uplt1 t1, uplt2 t2, uplt1_e t3 WHERE 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
+  74.0000000000000000 |  75.0000000000000000 |  148.0000000000000000 | 0001 | 0001 | A0001
+ 124.0000000000000000 | 124.5000000000000000 |  248.0000000000000000 | 0002 | 0002 | A0002
+ 174.0000000000000000 | 174.0000000000000000 |  348.0000000000000000 | 0003 | 0003 | A0003
+ 224.0000000000000000 | 225.0000000000000000 |  448.0000000000000000 | 0004 | 0004 | A0004
+ 274.0000000000000000 | 274.5000000000000000 |  548.0000000000000000 | 0005 | 0005 | A0005
+ 324.0000000000000000 | 324.0000000000000000 |  648.0000000000000000 | 0006 | 0006 | A0006
+ 374.0000000000000000 | 375.0000000000000000 |  748.0000000000000000 | 0007 | 0007 | A0007
+ 424.0000000000000000 | 424.5000000000000000 |  848.0000000000000000 | 0008 | 0008 | A0008
+ 474.0000000000000000 | 474.0000000000000000 |  948.0000000000000000 | 0009 | 0009 | A0009
+ 524.0000000000000000 | 525.0000000000000000 | 1048.0000000000000000 | 0010 | 0010 | A0010
+ 574.0000000000000000 | 574.5000000000000000 | 1148.0000000000000000 | 0011 | 0011 | A0011
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 2) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+   Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+         ->  Append
+               ->  Hash Right Join
+                     Output: t1.a, t1.c, t2.b, t2.c, t3.a, t3.b, t3.c
+                     Hash Cond: ((t3.a = t1.a) AND (ltrim(t3.c, 'A'::text) = t1.c))
+                     ->  Seq Scan on public.plt1_e_p1 t3
+                           Output: t3.a, t3.b, t3.c
+                     ->  Hash
+                           Output: t1.a, t1.c, t2.b, t2.c
+                           ->  Hash Right Join
+                                 Output: t1.a, t1.c, t2.b, t2.c
+                                 Hash Cond: ((t2.b = t1.a) AND (t2.c = t1.c))
+                                 ->  Seq Scan on public.plt2_p1 t2
+                                       Output: t2.b, t2.c
+                                 ->  Hash
+                                       Output: t1.a, t1.c
+                                       ->  Seq Scan on public.plt1_p1 t1
+                                             Output: t1.a, t1.c
+                                             Filter: ((t1.a % 25) = 0)
+               ->  Hash Right Join
+                     Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c
+                     Hash Cond: ((t3_1.a = t1_1.a) AND (ltrim(t3_1.c, 'A'::text) = t1_1.c))
+                     ->  Seq Scan on public.plt1_e_p2 t3_1
+                           Output: t3_1.a, t3_1.b, t3_1.c
+                     ->  Hash
+                           Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+                           ->  Hash Right Join
+                                 Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+                                 Hash Cond: ((t2_1.b = t1_1.a) AND (t2_1.c = t1_1.c))
+                                 ->  Seq Scan on public.plt2_p2 t2_1
+                                       Output: t2_1.b, t2_1.c
+                                 ->  Hash
+                                       Output: t1_1.a, t1_1.c
+                                       ->  Seq Scan on public.plt1_p2 t1_1
+                                             Output: t1_1.a, t1_1.c
+                                             Filter: ((t1_1.a % 25) = 0)
+               ->  Hash Right Join
+                     Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c
+                     Hash Cond: ((t3_2.a = t1_2.a) AND (ltrim(t3_2.c, 'A'::text) = t1_2.c))
+                     ->  Seq Scan on public.plt1_e_p3 t3_2
+                           Output: t3_2.a, t3_2.b, t3_2.c
+                     ->  Hash
+                           Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+                           ->  Hash Right Join
+                                 Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+                                 Hash Cond: ((t2_2.b = t1_2.a) AND (t2_2.c = t1_2.c))
+                                 ->  Seq Scan on public.plt2_p3 t2_2
+                                       Output: t2_2.b, t2_2.c
+                                 ->  Hash
+                                       Output: t1_2.a, t1_2.c
+                                       ->  Seq Scan on public.plt1_p3 t1_2
+                                             Output: t1_2.a, t1_2.c
+                                             Filter: ((t1_2.a % 25) = 0)
+(57 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 2) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   | ?column? |   c   
+-----+------+-----+------+----------+-------
+   0 | 0000 |   0 | 0000 |        0 | A0000
+  50 | 0001 |     |      |      100 | A0001
+ 100 | 0002 |     |      |      200 | A0002
+ 150 | 0003 | 150 | 0003 |      300 | A0003
+ 200 | 0004 |     |      |      400 | A0004
+ 250 | 0005 |     |      |      500 | A0005
+ 300 | 0006 | 300 | 0006 |      600 | A0006
+ 350 | 0007 |     |      |      700 | A0007
+ 400 | 0008 |     |      |      800 | A0008
+ 450 | 0009 | 450 | 0009 |      900 | A0009
+ 500 | 0010 |     |      |     1000 | A0010
+ 550 | 0011 |     |      |     1100 | A0011
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN uplt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+  a  |  c   |  b  |  c   | ?column? |   c   
+-----+------+-----+------+----------+-------
+   0 | 0000 |   0 | 0000 |        0 | A0000
+  50 | 0001 |     |      |      100 | A0001
+ 100 | 0002 |     |      |      200 | A0002
+ 150 | 0003 | 150 | 0003 |      300 | A0003
+ 200 | 0004 |     |      |      400 | A0004
+ 250 | 0005 |     |      |      500 | A0005
+ 300 | 0006 | 300 | 0006 |      600 | A0006
+ 350 | 0007 |     |      |      700 | A0007
+ 400 | 0008 |     |      |      800 | A0008
+ 450 | 0009 | 450 | 0009 |      900 | A0009
+ 500 | 0010 |     |      |     1000 | A0010
+ 550 | 0011 |     |      |     1100 | A0011
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 2) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 2 4) is considered for partition-wise join.
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+   Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+         ->  Append
+               ->  Hash Right Join
+                     Output: t1.a, t1.c, t2.b, t2.c, t3.a, t3.b, t3.c
+                     Hash Cond: ((t3.a = t2.b) AND (ltrim(t3.c, 'A'::text) = t2.c))
+                     ->  Seq Scan on public.plt1_e_p1 t3
+                           Output: t3.a, t3.b, t3.c
+                     ->  Hash
+                           Output: t1.a, t1.c, t2.b, t2.c
+                           ->  Hash Right Join
+                                 Output: t1.a, t1.c, t2.b, t2.c
+                                 Hash Cond: ((t2.b = t1.a) AND (t2.c = t1.c))
+                                 ->  Seq Scan on public.plt2_p1 t2
+                                       Output: t2.b, t2.c
+                                 ->  Hash
+                                       Output: t1.a, t1.c
+                                       ->  Seq Scan on public.plt1_p1 t1
+                                             Output: t1.a, t1.c
+                                             Filter: ((t1.a % 25) = 0)
+               ->  Hash Right Join
+                     Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c
+                     Hash Cond: ((t3_1.a = t2_1.b) AND (ltrim(t3_1.c, 'A'::text) = t2_1.c))
+                     ->  Seq Scan on public.plt1_e_p2 t3_1
+                           Output: t3_1.a, t3_1.b, t3_1.c
+                     ->  Hash
+                           Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+                           ->  Hash Right Join
+                                 Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c
+                                 Hash Cond: ((t2_1.b = t1_1.a) AND (t2_1.c = t1_1.c))
+                                 ->  Seq Scan on public.plt2_p2 t2_1
+                                       Output: t2_1.b, t2_1.c
+                                 ->  Hash
+                                       Output: t1_1.a, t1_1.c
+                                       ->  Seq Scan on public.plt1_p2 t1_1
+                                             Output: t1_1.a, t1_1.c
+                                             Filter: ((t1_1.a % 25) = 0)
+               ->  Hash Right Join
+                     Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c
+                     Hash Cond: ((t3_2.a = t2_2.b) AND (ltrim(t3_2.c, 'A'::text) = t2_2.c))
+                     ->  Seq Scan on public.plt1_e_p3 t3_2
+                           Output: t3_2.a, t3_2.b, t3_2.c
+                     ->  Hash
+                           Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+                           ->  Hash Right Join
+                                 Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c
+                                 Hash Cond: ((t2_2.b = t1_2.a) AND (t2_2.c = t1_2.c))
+                                 ->  Seq Scan on public.plt2_p3 t2_2
+                                       Output: t2_2.b, t2_2.c
+                                 ->  Hash
+                                       Output: t1_2.a, t1_2.c
+                                       ->  Seq Scan on public.plt1_p3 t1_2
+                                             Output: t1_2.a, t1_2.c
+                                             Filter: ((t1_2.a % 25) = 0)
+(57 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 2) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 2 4) is considered for partition-wise join.
+  a  |  c   |  b  |  c   | ?column? |   c   
+-----+------+-----+------+----------+-------
+   0 | 0000 |   0 | 0000 |        0 | A0000
+  50 | 0001 |     |      |          | 
+ 100 | 0002 |     |      |          | 
+ 150 | 0003 | 150 | 0003 |      300 | A0003
+ 200 | 0004 |     |      |          | 
+ 250 | 0005 |     |      |          | 
+ 300 | 0006 | 300 | 0006 |      600 | A0006
+ 350 | 0007 |     |      |          | 
+ 400 | 0008 |     |      |          | 
+ 450 | 0009 | 450 | 0009 |      900 | A0009
+ 500 | 0010 |     |      |          | 
+ 550 | 0011 |     |      |          | 
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+  a  |  c   |  b  |  c   | ?column? |   c   
+-----+------+-----+------+----------+-------
+   0 | 0000 |   0 | 0000 |        0 | A0000
+  50 | 0001 |     |      |          | 
+ 100 | 0002 |     |      |          | 
+ 150 | 0003 | 150 | 0003 |      300 | A0003
+ 200 | 0004 |     |      |          | 
+ 250 | 0005 |     |      |          | 
+ 300 | 0006 | 300 | 0006 |      600 | A0006
+ 350 | 0007 |     |      |          | 
+ 400 | 0008 |     |      |          | 
+ 450 | 0009 | 450 | 0009 |      900 | A0009
+ 500 | 0010 |     |      |          | 
+ 550 | 0011 |     |      |          | 
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+                                               QUERY PLAN                                               
+--------------------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+   Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+         ->  Append
+               ->  Hash Right Join
+                     Output: t3.a, t3.b, t3.c, t1.a, t1.c, t2.b, t2.c
+                     Hash Cond: ((t2.b = t1.a) AND (t2.c = t1.c))
+                     ->  Seq Scan on public.plt2_p1 t2
+                           Output: t2.b, t2.c
+                     ->  Hash
+                           Output: t3.a, t3.b, t3.c, t1.a, t1.c
+                           ->  Hash Right Join
+                                 Output: t3.a, t3.b, t3.c, t1.a, t1.c
+                                 Hash Cond: ((t1.c = ltrim(t3.c, 'A'::text)) AND (t1.a = t3.a))
+                                 ->  Seq Scan on public.plt1_p1 t1
+                                       Output: t1.a, t1.c
+                                 ->  Hash
+                                       Output: t3.a, t3.b, t3.c
+                                       ->  Seq Scan on public.plt1_e_p1 t3
+                                             Output: t3.a, t3.b, t3.c
+                                             Filter: ((t3.a % 25) = 0)
+               ->  Hash Right Join
+                     Output: t3_1.a, t3_1.b, t3_1.c, t1_1.a, t1_1.c, t2_1.b, t2_1.c
+                     Hash Cond: ((t2_1.b = t1_1.a) AND (t2_1.c = t1_1.c))
+                     ->  Seq Scan on public.plt2_p2 t2_1
+                           Output: t2_1.b, t2_1.c
+                     ->  Hash
+                           Output: t3_1.a, t3_1.b, t3_1.c, t1_1.a, t1_1.c
+                           ->  Hash Right Join
+                                 Output: t3_1.a, t3_1.b, t3_1.c, t1_1.a, t1_1.c
+                                 Hash Cond: ((t1_1.c = ltrim(t3_1.c, 'A'::text)) AND (t1_1.a = t3_1.a))
+                                 ->  Seq Scan on public.plt1_p2 t1_1
+                                       Output: t1_1.a, t1_1.c
+                                 ->  Hash
+                                       Output: t3_1.a, t3_1.b, t3_1.c
+                                       ->  Seq Scan on public.plt1_e_p2 t3_1
+                                             Output: t3_1.a, t3_1.b, t3_1.c
+                                             Filter: ((t3_1.a % 25) = 0)
+               ->  Hash Right Join
+                     Output: t3_2.a, t3_2.b, t3_2.c, t1_2.a, t1_2.c, t2_2.b, t2_2.c
+                     Hash Cond: ((t2_2.b = t1_2.a) AND (t2_2.c = t1_2.c))
+                     ->  Seq Scan on public.plt2_p3 t2_2
+                           Output: t2_2.b, t2_2.c
+                     ->  Hash
+                           Output: t3_2.a, t3_2.b, t3_2.c, t1_2.a, t1_2.c
+                           ->  Hash Right Join
+                                 Output: t3_2.a, t3_2.b, t3_2.c, t1_2.a, t1_2.c
+                                 Hash Cond: ((t1_2.c = ltrim(t3_2.c, 'A'::text)) AND (t1_2.a = t3_2.a))
+                                 ->  Seq Scan on public.plt1_p3 t1_2
+                                       Output: t1_2.a, t1_2.c
+                                 ->  Hash
+                                       Output: t3_2.a, t3_2.b, t3_2.c
+                                       ->  Seq Scan on public.plt1_e_p3 t3_2
+                                             Output: t3_2.a, t3_2.b, t3_2.c
+                                             Filter: ((t3_2.a % 25) = 0)
+(57 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 1 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   | ?column? |   c   
+-----+------+-----+------+----------+-------
+   0 | 0000 |   0 | 0000 |        0 | A0000
+  50 | 0001 |     |      |      100 | A0001
+ 100 | 0002 |     |      |      200 | A0002
+ 150 | 0003 | 150 | 0003 |      300 | A0003
+ 200 | 0004 |     |      |      400 | A0004
+ 250 | 0005 |     |      |      500 | A0005
+ 300 | 0006 | 300 | 0006 |      600 | A0006
+ 350 | 0007 |     |      |      700 | A0007
+ 400 | 0008 |     |      |      800 | A0008
+ 450 | 0009 | 450 | 0009 |      900 | A0009
+ 500 | 0010 |     |      |     1000 | A0010
+ 550 | 0011 |     |      |     1100 | A0011
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+  a  |  c   |  b  |  c   | ?column? |   c   
+-----+------+-----+------+----------+-------
+   0 | 0000 |   0 | 0000 |        0 | A0000
+  50 | 0001 |     |      |      100 | A0001
+ 100 | 0002 |     |      |      200 | A0002
+ 150 | 0003 | 150 | 0003 |      300 | A0003
+ 200 | 0004 |     |      |      400 | A0004
+ 250 | 0005 |     |      |      500 | A0005
+ 300 | 0006 | 300 | 0006 |      600 | A0006
+ 350 | 0007 |     |      |      700 | A0007
+ 400 | 0008 |     |      |      800 | A0008
+ 450 | 0009 | 450 | 0009 |      900 | A0009
+ 500 | 0010 |     |      |     1000 | A0010
+ 550 | 0011 |     |      |     1100 | A0011
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+                                               QUERY PLAN                                               
+--------------------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+   Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+         ->  Append
+               ->  Hash Right Join
+                     Output: t3.a, t3.b, t3.c, t2.b, t2.c, t1.a, t1.c
+                     Hash Cond: ((t1.a = t2.b) AND (t1.c = t2.c))
+                     ->  Seq Scan on public.plt1_p1 t1
+                           Output: t1.a, t1.c
+                     ->  Hash
+                           Output: t3.a, t3.b, t3.c, t2.b, t2.c
+                           ->  Hash Right Join
+                                 Output: t3.a, t3.b, t3.c, t2.b, t2.c
+                                 Hash Cond: ((t2.b = t3.a) AND (t2.c = ltrim(t3.c, 'A'::text)))
+                                 ->  Seq Scan on public.plt2_p1 t2
+                                       Output: t2.b, t2.c
+                                 ->  Hash
+                                       Output: t3.a, t3.b, t3.c
+                                       ->  Seq Scan on public.plt1_e_p1 t3
+                                             Output: t3.a, t3.b, t3.c
+                                             Filter: ((t3.a % 25) = 0)
+               ->  Hash Right Join
+                     Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c, t1_1.a, t1_1.c
+                     Hash Cond: ((t1_1.a = t2_1.b) AND (t1_1.c = t2_1.c))
+                     ->  Seq Scan on public.plt1_p2 t1_1
+                           Output: t1_1.a, t1_1.c
+                     ->  Hash
+                           Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+                           ->  Hash Right Join
+                                 Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+                                 Hash Cond: ((t2_1.b = t3_1.a) AND (t2_1.c = ltrim(t3_1.c, 'A'::text)))
+                                 ->  Seq Scan on public.plt2_p2 t2_1
+                                       Output: t2_1.b, t2_1.c
+                                 ->  Hash
+                                       Output: t3_1.a, t3_1.b, t3_1.c
+                                       ->  Seq Scan on public.plt1_e_p2 t3_1
+                                             Output: t3_1.a, t3_1.b, t3_1.c
+                                             Filter: ((t3_1.a % 25) = 0)
+               ->  Hash Right Join
+                     Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c, t1_2.a, t1_2.c
+                     Hash Cond: ((t1_2.a = t2_2.b) AND (t1_2.c = t2_2.c))
+                     ->  Seq Scan on public.plt1_p3 t1_2
+                           Output: t1_2.a, t1_2.c
+                     ->  Hash
+                           Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+                           ->  Hash Right Join
+                                 Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+                                 Hash Cond: ((t2_2.b = t3_2.a) AND (t2_2.c = ltrim(t3_2.c, 'A'::text)))
+                                 ->  Seq Scan on public.plt2_p3 t2_2
+                                       Output: t2_2.b, t2_2.c
+                                 ->  Hash
+                                       Output: t3_2.a, t3_2.b, t3_2.c
+                                       ->  Seq Scan on public.plt1_e_p3 t3_2
+                                             Output: t3_2.a, t3_2.b, t3_2.c
+                                             Filter: ((t3_2.a % 25) = 0)
+(57 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   | ?column? |   c   
+-----+------+-----+------+----------+-------
+   0 | 0000 |   0 | 0000 |        0 | A0000
+ 150 | 0003 | 150 | 0003 |      300 | A0003
+ 300 | 0006 | 300 | 0006 |      600 | A0006
+ 450 | 0009 | 450 | 0009 |      900 | A0009
+     |      |     |      |      100 | A0001
+     |      |     |      |      200 | A0002
+     |      |     |      |      400 | A0004
+     |      |     |      |      500 | A0005
+     |      |     |      |      700 | A0007
+     |      |     |      |      800 | A0008
+     |      |     |      |     1000 | A0010
+     |      |     |      |     1100 | A0011
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+  a  |  c   |  b  |  c   | ?column? |   c   
+-----+------+-----+------+----------+-------
+   0 | 0000 |   0 | 0000 |        0 | A0000
+ 150 | 0003 | 150 | 0003 |      300 | A0003
+ 300 | 0006 | 300 | 0006 |      600 | A0006
+ 450 | 0009 | 450 | 0009 |      900 | A0009
+     |      |     |      |      100 | A0001
+     |      |     |      |      200 | A0002
+     |      |     |      |      400 | A0004
+     |      |     |      |      500 | A0005
+     |      |     |      |      700 | A0007
+     |      |     |      |      800 | A0008
+     |      |     |      |     1000 | A0010
+     |      |     |      |     1100 | A0011
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE:  join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+                                                  QUERY PLAN                                                   
+---------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, ((plt1_e_p1.a + plt1_e_p1.b)), plt1_e_p1.c
+   Sort Key: plt1_p1.a, plt2_p1.b, ((plt1_e_p1.a + plt1_e_p1.b))
+   ->  Result
+         Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, (plt1_e_p1.a + plt1_e_p1.b), plt1_e_p1.c
+         ->  Append
+               ->  Hash Full Join
+                     Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c
+                     Hash Cond: ((plt1_p1.a = plt1_e_p1.a) AND (plt1_p1.c = ltrim(plt1_e_p1.c, 'A'::text)))
+                     ->  Hash Full Join
+                           Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c
+                           Hash Cond: ((plt1_p1.a = plt2_p1.b) AND (plt1_p1.c = plt2_p1.c))
+                           ->  Seq Scan on public.plt1_p1
+                                 Output: plt1_p1.a, plt1_p1.c
+                                 Filter: ((plt1_p1.a % 25) = 0)
+                           ->  Hash
+                                 Output: plt2_p1.b, plt2_p1.c
+                                 ->  Seq Scan on public.plt2_p1
+                                       Output: plt2_p1.b, plt2_p1.c
+                                       Filter: ((plt2_p1.b % 25) = 0)
+                     ->  Hash
+                           Output: plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c
+                           ->  Seq Scan on public.plt1_e_p1
+                                 Output: plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c
+                                 Filter: ((plt1_e_p1.a % 25) = 0)
+               ->  Hash Full Join
+                     Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c, plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c
+                     Hash Cond: ((plt1_p2.a = plt1_e_p2.a) AND (plt1_p2.c = ltrim(plt1_e_p2.c, 'A'::text)))
+                     ->  Hash Full Join
+                           Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c
+                           Hash Cond: ((plt1_p2.a = plt2_p2.b) AND (plt1_p2.c = plt2_p2.c))
+                           ->  Seq Scan on public.plt1_p2
+                                 Output: plt1_p2.a, plt1_p2.c
+                                 Filter: ((plt1_p2.a % 25) = 0)
+                           ->  Hash
+                                 Output: plt2_p2.b, plt2_p2.c
+                                 ->  Seq Scan on public.plt2_p2
+                                       Output: plt2_p2.b, plt2_p2.c
+                                       Filter: ((plt2_p2.b % 25) = 0)
+                     ->  Hash
+                           Output: plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c
+                           ->  Seq Scan on public.plt1_e_p2
+                                 Output: plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c
+                                 Filter: ((plt1_e_p2.a % 25) = 0)
+               ->  Hash Full Join
+                     Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c, plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c
+                     Hash Cond: ((plt1_p3.a = plt1_e_p3.a) AND (plt1_p3.c = ltrim(plt1_e_p3.c, 'A'::text)))
+                     ->  Hash Full Join
+                           Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c
+                           Hash Cond: ((plt1_p3.a = plt2_p3.b) AND (plt1_p3.c = plt2_p3.c))
+                           ->  Seq Scan on public.plt1_p3
+                                 Output: plt1_p3.a, plt1_p3.c
+                                 Filter: ((plt1_p3.a % 25) = 0)
+                           ->  Hash
+                                 Output: plt2_p3.b, plt2_p3.c
+                                 ->  Seq Scan on public.plt2_p3
+                                       Output: plt2_p3.b, plt2_p3.c
+                                       Filter: ((plt2_p3.b % 25) = 0)
+                     ->  Hash
+                           Output: plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c
+                           ->  Seq Scan on public.plt1_e_p3
+                                 Output: plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c
+                                 Filter: ((plt1_e_p3.a % 25) = 0)
+(63 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE:  join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+  a  |  c   |  b  |  c   | ?column? |   c   
+-----+------+-----+------+----------+-------
+   0 | 0000 |   0 | 0000 |        0 | A0000
+  50 | 0001 |     |      |      100 | A0001
+ 100 | 0002 |     |      |      200 | A0002
+ 150 | 0003 | 150 | 0003 |      300 | A0003
+ 200 | 0004 |     |      |      400 | A0004
+ 250 | 0005 |     |      |      500 | A0005
+ 300 | 0006 | 300 | 0006 |      600 | A0006
+ 350 | 0007 |     |      |      700 | A0007
+ 400 | 0008 |     |      |      800 | A0008
+ 450 | 0009 | 450 | 0009 |      900 | A0009
+ 500 | 0010 |     |      |     1000 | A0010
+ 550 | 0011 |     |      |     1100 | A0011
+     |      |  75 | 0001 |          | 
+     |      | 225 | 0004 |          | 
+     |      | 375 | 0007 |          | 
+     |      | 525 | 0010 |          | 
+(16 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM uplt1_e WHERE uplt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+  a  |  c   |  b  |  c   | ?column? |   c   
+-----+------+-----+------+----------+-------
+   0 | 0000 |   0 | 0000 |        0 | A0000
+  50 | 0001 |     |      |      100 | A0001
+ 100 | 0002 |     |      |      200 | A0002
+ 150 | 0003 | 150 | 0003 |      300 | A0003
+ 200 | 0004 |     |      |      400 | A0004
+ 250 | 0005 |     |      |      500 | A0005
+ 300 | 0006 | 300 | 0006 |      600 | A0006
+ 350 | 0007 |     |      |      700 | A0007
+ 400 | 0008 |     |      |      800 | A0008
+ 450 | 0009 | 450 | 0009 |      900 | A0009
+ 500 | 0010 |     |      |     1000 | A0010
+ 550 | 0011 |     |      |     1100 | A0011
+     |      |  75 | 0001 |          | 
+     |      | 225 | 0004 |          | 
+     |      | 375 | 0007 |          | 
+     |      | 525 | 0010 |          | 
+(16 rows)
+
+-- Semi-join
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1, plt1_e t2 WHERE t1.c = ltrim(t2.c, 'A')) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 3) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3 4) is considered for partition-wise join.
+                             QUERY PLAN                             
+--------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.b, t1.c
+   Sort Key: t1.a
+   ->  Append
+         ->  Nested Loop Semi Join
+               Output: t1.a, t1.b, t1.c
+               Join Filter: (t1.c = t1_3.c)
+               ->  Seq Scan on public.plt1_p1 t1
+                     Output: t1.a, t1.b, t1.c
+                     Filter: ((t1.a % 25) = 0)
+               ->  Hash Join
+                     Output: t1_3.c, t2.c
+                     Hash Cond: (t1_3.c = ltrim(t2.c, 'A'::text))
+                     ->  Seq Scan on public.plt2_p1 t1_3
+                           Output: t1_3.c
+                     ->  Hash
+                           Output: t2.c
+                           ->  Seq Scan on public.plt1_e_p1 t2
+                                 Output: t2.c
+         ->  Nested Loop Semi Join
+               Output: t1_1.a, t1_1.b, t1_1.c
+               Join Filter: (t1_1.c = t1_4.c)
+               ->  Seq Scan on public.plt1_p2 t1_1
+                     Output: t1_1.a, t1_1.b, t1_1.c
+                     Filter: ((t1_1.a % 25) = 0)
+               ->  Hash Join
+                     Output: t1_4.c, t2_1.c
+                     Hash Cond: (t1_4.c = ltrim(t2_1.c, 'A'::text))
+                     ->  Seq Scan on public.plt2_p2 t1_4
+                           Output: t1_4.c
+                     ->  Hash
+                           Output: t2_1.c
+                           ->  Seq Scan on public.plt1_e_p2 t2_1
+                                 Output: t2_1.c
+         ->  Nested Loop Semi Join
+               Output: t1_2.a, t1_2.b, t1_2.c
+               Join Filter: (t1_2.c = t1_5.c)
+               ->  Seq Scan on public.plt1_p3 t1_2
+                     Output: t1_2.a, t1_2.b, t1_2.c
+                     Filter: ((t1_2.a % 25) = 0)
+               ->  Hash Join
+                     Output: t1_5.c, t2_2.c
+                     Hash Cond: (t1_5.c = ltrim(t2_2.c, 'A'::text))
+                     ->  Seq Scan on public.plt2_p3 t1_5
+                           Output: t1_5.c
+                     ->  Hash
+                           Output: t2_2.c
+                           ->  Seq Scan on public.plt1_e_p3 t2_2
+                                 Output: t2_2.c
+(49 rows)
+
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1, plt1_e t2 WHERE t1.c = ltrim(t2.c, 'A')) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 3) and (b 4) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3 4) is considered for partition-wise join.
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+  50 |  50 | 0001
+ 100 | 100 | 0002
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 250 | 250 | 0005
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 400 | 400 | 0008
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+ 550 | 550 | 0011
+(12 rows)
+
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1, uplt1_e t2 WHERE t1.c = ltrim(t2.c, 'A')) AND t1.a % 25 = 0 ORDER BY t1.a;
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+  50 |  50 | 0001
+ 100 | 100 | 0002
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 250 | 250 | 0005
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 400 | 400 | 0008
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+ 550 | 550 | 0011
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+                                   QUERY PLAN                                   
+--------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.b, t1.c
+   Sort Key: t1.a
+   ->  Append
+         ->  Nested Loop Semi Join
+               Output: t1.a, t1.b, t1.c
+               Join Filter: (t1.c = t1_3.c)
+               ->  Seq Scan on public.plt1_p1 t1
+                     Output: t1.a, t1.b, t1.c
+                     Filter: ((t1.a % 25) = 0)
+               ->  Hash Join
+                     Output: t1_3.c, t1_6.c
+                     Hash Cond: (t1_3.c = ltrim(t1_6.c, 'A'::text))
+                     ->  Seq Scan on public.plt2_p1 t1_3
+                           Output: t1_3.c
+                     ->  Hash
+                           Output: t1_6.c
+                           ->  HashAggregate
+                                 Output: t1_6.c
+                                 Group Key: ltrim(t1_6.c, 'A'::text)
+                                 ->  Seq Scan on public.plt1_e_p1 t1_6
+                                       Output: t1_6.c, ltrim(t1_6.c, 'A'::text)
+                                       Filter: ((t1_6.a % 25) = 0)
+         ->  Nested Loop Semi Join
+               Output: t1_1.a, t1_1.b, t1_1.c
+               Join Filter: (t1_1.c = t1_4.c)
+               ->  Seq Scan on public.plt1_p2 t1_1
+                     Output: t1_1.a, t1_1.b, t1_1.c
+                     Filter: ((t1_1.a % 25) = 0)
+               ->  Hash Join
+                     Output: t1_4.c, t1_7.c
+                     Hash Cond: (t1_4.c = ltrim(t1_7.c, 'A'::text))
+                     ->  Seq Scan on public.plt2_p2 t1_4
+                           Output: t1_4.c
+                     ->  Hash
+                           Output: t1_7.c
+                           ->  HashAggregate
+                                 Output: t1_7.c
+                                 Group Key: ltrim(t1_7.c, 'A'::text)
+                                 ->  Seq Scan on public.plt1_e_p2 t1_7
+                                       Output: t1_7.c, ltrim(t1_7.c, 'A'::text)
+                                       Filter: ((t1_7.a % 25) = 0)
+         ->  Nested Loop Semi Join
+               Output: t1_2.a, t1_2.b, t1_2.c
+               Join Filter: (t1_2.c = t1_5.c)
+               ->  Seq Scan on public.plt1_p3 t1_2
+                     Output: t1_2.a, t1_2.b, t1_2.c
+                     Filter: ((t1_2.a % 25) = 0)
+               ->  Hash Join
+                     Output: t1_5.c, t1_8.c
+                     Hash Cond: (t1_5.c = ltrim(t1_8.c, 'A'::text))
+                     ->  Seq Scan on public.plt2_p3 t1_5
+                           Output: t1_5.c
+                     ->  Hash
+                           Output: t1_8.c
+                           ->  HashAggregate
+                                 Output: t1_8.c
+                                 Group Key: ltrim(t1_8.c, 'A'::text)
+                                 ->  Seq Scan on public.plt1_e_p3 t1_8
+                                       Output: t1_8.c, ltrim(t1_8.c, 'A'::text)
+                                       Filter: ((t1_8.a % 25) = 0)
+(61 rows)
+
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+  50 |  50 | 0001
+ 100 | 100 | 0002
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 250 | 250 | 0005
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 400 | 400 | 0008
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+ 550 | 550 | 0011
+(12 rows)
+
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM uplt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+  50 |  50 | 0001
+ 100 | 100 | 0002
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 250 | 250 | 0005
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 400 | 400 | 0008
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+ 550 | 550 | 0011
+(12 rows)
+
+-- test merge join with and without index scan
+CREATE INDEX iplt1_c on plt1(c);
+CREATE INDEX iplt1_p1_c on plt1_p1(c);
+CREATE INDEX iplt1_p2_c on plt1_p2(c);
+CREATE INDEX iplt1_p3_c on plt1_p3(c);
+CREATE INDEX iplt2_c on plt2(c);
+CREATE INDEX iplt2_p1_c on plt2_p1(c);
+CREATE INDEX iplt2_p2_c on plt2_p2(c);
+CREATE INDEX iplt2_p3_c on plt2_p3(c);
+CREATE INDEX iplt1_e_c on plt1_e(ltrim(c, 'A'));
+CREATE INDEX iplt1_e_p1_c on plt1_e_p1(ltrim(c, 'A'));
+CREATE INDEX iplt1_e_p2_c on plt1_e_p2(ltrim(c, 'A'));
+CREATE INDEX iplt1_e_p3_c on plt1_e_p3(ltrim(c, 'A'));
+ANALYZE plt1;
+ANALYZE plt1_p1;
+ANALYZE plt1_p2;
+ANALYZE plt1_p3;
+ANALYZE plt2;
+ANALYZE plt2_p1;
+ANALYZE plt2_p2;
+ANALYZE plt2_p3;
+ANALYZE plt1_e;
+ANALYZE plt1_e_p1;
+ANALYZE plt1_e_p2;
+ANALYZE plt1_e_p3;
+SET enable_hashjoin TO off;
+SET enable_nestloop TO off;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+                                                QUERY PLAN                                                 
+-----------------------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+   Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+         ->  Append
+               ->  Merge Left Join
+                     Output: t3.a, t3.b, t3.c, t2.b, t2.c, t1.a, t1.c
+                     Merge Cond: ((t2.b = t1.a) AND (t2.c = t1.c))
+                     ->  Sort
+                           Output: t3.a, t3.b, t3.c, t2.b, t2.c
+                           Sort Key: t2.b, t2.c
+                           ->  Merge Left Join
+                                 Output: t3.a, t3.b, t3.c, t2.b, t2.c
+                                 Merge Cond: ((t3.a = t2.b) AND ((ltrim(t3.c, 'A'::text)) = t2.c))
+                                 ->  Sort
+                                       Output: t3.a, t3.b, t3.c, (ltrim(t3.c, 'A'::text))
+                                       Sort Key: t3.a, (ltrim(t3.c, 'A'::text))
+                                       ->  Seq Scan on public.plt1_e_p1 t3
+                                             Output: t3.a, t3.b, t3.c, ltrim(t3.c, 'A'::text)
+                                             Filter: ((t3.a % 25) = 0)
+                                 ->  Sort
+                                       Output: t2.b, t2.c
+                                       Sort Key: t2.b, t2.c
+                                       ->  Seq Scan on public.plt2_p1 t2
+                                             Output: t2.b, t2.c
+                     ->  Sort
+                           Output: t1.a, t1.c
+                           Sort Key: t1.a, t1.c
+                           ->  Seq Scan on public.plt1_p1 t1
+                                 Output: t1.a, t1.c
+               ->  Merge Left Join
+                     Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c, t1_1.a, t1_1.c
+                     Merge Cond: ((t2_1.c = t1_1.c) AND (t2_1.b = t1_1.a))
+                     ->  Sort
+                           Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+                           Sort Key: t2_1.c, t2_1.b
+                           ->  Merge Left Join
+                                 Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+                                 Merge Cond: ((t3_1.a = t2_1.b) AND ((ltrim(t3_1.c, 'A'::text)) = t2_1.c))
+                                 ->  Sort
+                                       Output: t3_1.a, t3_1.b, t3_1.c, (ltrim(t3_1.c, 'A'::text))
+                                       Sort Key: t3_1.a, (ltrim(t3_1.c, 'A'::text))
+                                       ->  Seq Scan on public.plt1_e_p2 t3_1
+                                             Output: t3_1.a, t3_1.b, t3_1.c, ltrim(t3_1.c, 'A'::text)
+                                             Filter: ((t3_1.a % 25) = 0)
+                                 ->  Sort
+                                       Output: t2_1.b, t2_1.c
+                                       Sort Key: t2_1.b, t2_1.c
+                                       ->  Seq Scan on public.plt2_p2 t2_1
+                                             Output: t2_1.b, t2_1.c
+                     ->  Sort
+                           Output: t1_1.a, t1_1.c
+                           Sort Key: t1_1.c, t1_1.a
+                           ->  Seq Scan on public.plt1_p2 t1_1
+                                 Output: t1_1.a, t1_1.c
+               ->  Merge Left Join
+                     Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c, t1_2.a, t1_2.c
+                     Merge Cond: ((t2_2.b = t1_2.a) AND (t2_2.c = t1_2.c))
+                     ->  Sort
+                           Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+                           Sort Key: t2_2.b, t2_2.c
+                           ->  Merge Left Join
+                                 Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+                                 Merge Cond: ((t3_2.a = t2_2.b) AND ((ltrim(t3_2.c, 'A'::text)) = t2_2.c))
+                                 ->  Sort
+                                       Output: t3_2.a, t3_2.b, t3_2.c, (ltrim(t3_2.c, 'A'::text))
+                                       Sort Key: t3_2.a, (ltrim(t3_2.c, 'A'::text))
+                                       ->  Seq Scan on public.plt1_e_p3 t3_2
+                                             Output: t3_2.a, t3_2.b, t3_2.c, ltrim(t3_2.c, 'A'::text)
+                                             Filter: ((t3_2.a % 25) = 0)
+                                 ->  Sort
+                                       Output: t2_2.b, t2_2.c
+                                       Sort Key: t2_2.b, t2_2.c
+                                       ->  Seq Scan on public.plt2_p3 t2_2
+                                             Output: t2_2.b, t2_2.c
+                     ->  Sort
+                           Output: t1_2.a, t1_2.c
+                           Sort Key: t1_2.a, t1_2.c
+                           ->  Seq Scan on public.plt1_p3 t1_2
+                                 Output: t1_2.a, t1_2.c
+(81 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   | ?column? |   c   
+-----+------+-----+------+----------+-------
+   0 | 0000 |   0 | 0000 |        0 | A0000
+ 150 | 0003 | 150 | 0003 |      300 | A0003
+ 300 | 0006 | 300 | 0006 |      600 | A0006
+ 450 | 0009 | 450 | 0009 |      900 | A0009
+     |      |     |      |      100 | A0001
+     |      |     |      |      200 | A0002
+     |      |     |      |      400 | A0004
+     |      |     |      |      500 | A0005
+     |      |     |      |      700 | A0007
+     |      |     |      |      800 | A0008
+     |      |     |      |     1000 | A0010
+     |      |     |      |     1100 | A0011
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+  a  |  c   |  b  |  c   | ?column? |   c   
+-----+------+-----+------+----------+-------
+   0 | 0000 |   0 | 0000 |        0 | A0000
+ 150 | 0003 | 150 | 0003 |      300 | A0003
+ 300 | 0006 | 300 | 0006 |      600 | A0006
+ 450 | 0009 | 450 | 0009 |      900 | A0009
+     |      |     |      |      100 | A0001
+     |      |     |      |      200 | A0002
+     |      |     |      |      400 | A0004
+     |      |     |      |      500 | A0005
+     |      |     |      |      700 | A0007
+     |      |     |      |      800 | A0008
+     |      |     |      |     1000 | A0010
+     |      |     |      |     1100 | A0011
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE:  join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+                                                  QUERY PLAN                                                   
+---------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, ((plt1_e_p1.a + plt1_e_p1.b)), plt1_e_p1.c
+   Sort Key: plt1_p1.a, plt2_p1.b, ((plt1_e_p1.a + plt1_e_p1.b))
+   ->  Result
+         Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, (plt1_e_p1.a + plt1_e_p1.b), plt1_e_p1.c
+         ->  Append
+               ->  Merge Full Join
+                     Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c
+                     Merge Cond: ((plt1_p1.a = plt1_e_p1.a) AND (plt1_p1.c = (ltrim(plt1_e_p1.c, 'A'::text))))
+                     ->  Sort
+                           Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c
+                           Sort Key: plt1_p1.a, plt1_p1.c
+                           ->  Merge Full Join
+                                 Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c
+                                 Merge Cond: ((plt1_p1.a = plt2_p1.b) AND (plt1_p1.c = plt2_p1.c))
+                                 ->  Sort
+                                       Output: plt1_p1.a, plt1_p1.c
+                                       Sort Key: plt1_p1.a, plt1_p1.c
+                                       ->  Seq Scan on public.plt1_p1
+                                             Output: plt1_p1.a, plt1_p1.c
+                                             Filter: ((plt1_p1.a % 25) = 0)
+                                 ->  Sort
+                                       Output: plt2_p1.b, plt2_p1.c
+                                       Sort Key: plt2_p1.b, plt2_p1.c
+                                       ->  Seq Scan on public.plt2_p1
+                                             Output: plt2_p1.b, plt2_p1.c
+                                             Filter: ((plt2_p1.b % 25) = 0)
+                     ->  Sort
+                           Output: plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c, (ltrim(plt1_e_p1.c, 'A'::text))
+                           Sort Key: plt1_e_p1.a, (ltrim(plt1_e_p1.c, 'A'::text))
+                           ->  Seq Scan on public.plt1_e_p1
+                                 Output: plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c, ltrim(plt1_e_p1.c, 'A'::text)
+                                 Filter: ((plt1_e_p1.a % 25) = 0)
+               ->  Merge Full Join
+                     Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c, plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c
+                     Merge Cond: ((plt1_p2.a = plt1_e_p2.a) AND (plt1_p2.c = (ltrim(plt1_e_p2.c, 'A'::text))))
+                     ->  Sort
+                           Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c
+                           Sort Key: plt1_p2.a, plt1_p2.c
+                           ->  Merge Full Join
+                                 Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c
+                                 Merge Cond: ((plt1_p2.a = plt2_p2.b) AND (plt1_p2.c = plt2_p2.c))
+                                 ->  Sort
+                                       Output: plt1_p2.a, plt1_p2.c
+                                       Sort Key: plt1_p2.a, plt1_p2.c
+                                       ->  Seq Scan on public.plt1_p2
+                                             Output: plt1_p2.a, plt1_p2.c
+                                             Filter: ((plt1_p2.a % 25) = 0)
+                                 ->  Sort
+                                       Output: plt2_p2.b, plt2_p2.c
+                                       Sort Key: plt2_p2.b, plt2_p2.c
+                                       ->  Seq Scan on public.plt2_p2
+                                             Output: plt2_p2.b, plt2_p2.c
+                                             Filter: ((plt2_p2.b % 25) = 0)
+                     ->  Sort
+                           Output: plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c, (ltrim(plt1_e_p2.c, 'A'::text))
+                           Sort Key: plt1_e_p2.a, (ltrim(plt1_e_p2.c, 'A'::text))
+                           ->  Seq Scan on public.plt1_e_p2
+                                 Output: plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c, ltrim(plt1_e_p2.c, 'A'::text)
+                                 Filter: ((plt1_e_p2.a % 25) = 0)
+               ->  Merge Full Join
+                     Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c, plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c
+                     Merge Cond: ((plt1_p3.a = plt1_e_p3.a) AND (plt1_p3.c = (ltrim(plt1_e_p3.c, 'A'::text))))
+                     ->  Sort
+                           Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c
+                           Sort Key: plt1_p3.a, plt1_p3.c
+                           ->  Merge Full Join
+                                 Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c
+                                 Merge Cond: ((plt1_p3.a = plt2_p3.b) AND (plt1_p3.c = plt2_p3.c))
+                                 ->  Sort
+                                       Output: plt1_p3.a, plt1_p3.c
+                                       Sort Key: plt1_p3.a, plt1_p3.c
+                                       ->  Seq Scan on public.plt1_p3
+                                             Output: plt1_p3.a, plt1_p3.c
+                                             Filter: ((plt1_p3.a % 25) = 0)
+                                 ->  Sort
+                                       Output: plt2_p3.b, plt2_p3.c
+                                       Sort Key: plt2_p3.b, plt2_p3.c
+                                       ->  Seq Scan on public.plt2_p3
+                                             Output: plt2_p3.b, plt2_p3.c
+                                             Filter: ((plt2_p3.b % 25) = 0)
+                     ->  Sort
+                           Output: plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c, (ltrim(plt1_e_p3.c, 'A'::text))
+                           Sort Key: plt1_e_p3.a, (ltrim(plt1_e_p3.c, 'A'::text))
+                           ->  Seq Scan on public.plt1_e_p3
+                                 Output: plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c, ltrim(plt1_e_p3.c, 'A'::text)
+                                 Filter: ((plt1_e_p3.a % 25) = 0)
+(87 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE:  join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+  a  |  c   |  b  |  c   | ?column? |   c   
+-----+------+-----+------+----------+-------
+   0 | 0000 |   0 | 0000 |        0 | A0000
+  50 | 0001 |     |      |      100 | A0001
+ 100 | 0002 |     |      |      200 | A0002
+ 150 | 0003 | 150 | 0003 |      300 | A0003
+ 200 | 0004 |     |      |      400 | A0004
+ 250 | 0005 |     |      |      500 | A0005
+ 300 | 0006 | 300 | 0006 |      600 | A0006
+ 350 | 0007 |     |      |      700 | A0007
+ 400 | 0008 |     |      |      800 | A0008
+ 450 | 0009 | 450 | 0009 |      900 | A0009
+ 500 | 0010 |     |      |     1000 | A0010
+ 550 | 0011 |     |      |     1100 | A0011
+     |      |  75 | 0001 |          | 
+     |      | 225 | 0004 |          | 
+     |      | 375 | 0007 |          | 
+     |      | 525 | 0010 |          | 
+(16 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM uplt1_e WHERE uplt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+  a  |  c   |  b  |  c   | ?column? |   c   
+-----+------+-----+------+----------+-------
+   0 | 0000 |   0 | 0000 |        0 | A0000
+  50 | 0001 |     |      |      100 | A0001
+ 100 | 0002 |     |      |      200 | A0002
+ 150 | 0003 | 150 | 0003 |      300 | A0003
+ 200 | 0004 |     |      |      400 | A0004
+ 250 | 0005 |     |      |      500 | A0005
+ 300 | 0006 | 300 | 0006 |      600 | A0006
+ 350 | 0007 |     |      |      700 | A0007
+ 400 | 0008 |     |      |      800 | A0008
+ 450 | 0009 | 450 | 0009 |      900 | A0009
+ 500 | 0010 |     |      |     1000 | A0010
+ 550 | 0011 |     |      |     1100 | A0011
+     |      |  75 | 0001 |          | 
+     |      | 225 | 0004 |          | 
+     |      | 375 | 0007 |          | 
+     |      | 525 | 0010 |          | 
+(16 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+                                      QUERY PLAN                                      
+--------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.b, t1.c
+   Sort Key: t1.a
+   ->  Append
+         ->  Merge Semi Join
+               Output: t1.a, t1.b, t1.c
+               Merge Cond: (t1.c = t1_3.c)
+               ->  Sort
+                     Output: t1.a, t1.b, t1.c
+                     Sort Key: t1.c
+                     ->  Seq Scan on public.plt1_p1 t1
+                           Output: t1.a, t1.b, t1.c
+                           Filter: ((t1.a % 25) = 0)
+               ->  Materialize
+                     Output: t1_3.c, t1_6.c
+                     ->  Merge Join
+                           Output: t1_3.c, t1_6.c
+                           Merge Cond: ((ltrim(t1_6.c, 'A'::text)) = t1_3.c)
+                           ->  Sort
+                                 Output: t1_6.c, (ltrim(t1_6.c, 'A'::text))
+                                 Sort Key: (ltrim(t1_6.c, 'A'::text))
+                                 ->  HashAggregate
+                                       Output: t1_6.c, (ltrim(t1_6.c, 'A'::text))
+                                       Group Key: ltrim(t1_6.c, 'A'::text)
+                                       ->  Seq Scan on public.plt1_e_p1 t1_6
+                                             Output: t1_6.c, ltrim(t1_6.c, 'A'::text)
+                                             Filter: ((t1_6.a % 25) = 0)
+                           ->  Sort
+                                 Output: t1_3.c
+                                 Sort Key: t1_3.c
+                                 ->  Seq Scan on public.plt2_p1 t1_3
+                                       Output: t1_3.c
+         ->  Merge Semi Join
+               Output: t1_1.a, t1_1.b, t1_1.c
+               Merge Cond: (t1_1.c = t1_4.c)
+               ->  Sort
+                     Output: t1_1.a, t1_1.b, t1_1.c
+                     Sort Key: t1_1.c
+                     ->  Seq Scan on public.plt1_p2 t1_1
+                           Output: t1_1.a, t1_1.b, t1_1.c
+                           Filter: ((t1_1.a % 25) = 0)
+               ->  Materialize
+                     Output: t1_4.c, t1_7.c
+                     ->  Merge Join
+                           Output: t1_4.c, t1_7.c
+                           Merge Cond: ((ltrim(t1_7.c, 'A'::text)) = t1_4.c)
+                           ->  Sort
+                                 Output: t1_7.c, (ltrim(t1_7.c, 'A'::text))
+                                 Sort Key: (ltrim(t1_7.c, 'A'::text))
+                                 ->  HashAggregate
+                                       Output: t1_7.c, (ltrim(t1_7.c, 'A'::text))
+                                       Group Key: ltrim(t1_7.c, 'A'::text)
+                                       ->  Seq Scan on public.plt1_e_p2 t1_7
+                                             Output: t1_7.c, ltrim(t1_7.c, 'A'::text)
+                                             Filter: ((t1_7.a % 25) = 0)
+                           ->  Sort
+                                 Output: t1_4.c
+                                 Sort Key: t1_4.c
+                                 ->  Seq Scan on public.plt2_p2 t1_4
+                                       Output: t1_4.c
+         ->  Merge Semi Join
+               Output: t1_2.a, t1_2.b, t1_2.c
+               Merge Cond: (t1_2.c = t1_5.c)
+               ->  Sort
+                     Output: t1_2.a, t1_2.b, t1_2.c
+                     Sort Key: t1_2.c
+                     ->  Seq Scan on public.plt1_p3 t1_2
+                           Output: t1_2.a, t1_2.b, t1_2.c
+                           Filter: ((t1_2.a % 25) = 0)
+               ->  Materialize
+                     Output: t1_5.c, t1_8.c
+                     ->  Merge Join
+                           Output: t1_5.c, t1_8.c
+                           Merge Cond: ((ltrim(t1_8.c, 'A'::text)) = t1_5.c)
+                           ->  Sort
+                                 Output: t1_8.c, (ltrim(t1_8.c, 'A'::text))
+                                 Sort Key: (ltrim(t1_8.c, 'A'::text))
+                                 ->  HashAggregate
+                                       Output: t1_8.c, (ltrim(t1_8.c, 'A'::text))
+                                       Group Key: ltrim(t1_8.c, 'A'::text)
+                                       ->  Seq Scan on public.plt1_e_p3 t1_8
+                                             Output: t1_8.c, ltrim(t1_8.c, 'A'::text)
+                                             Filter: ((t1_8.a % 25) = 0)
+                           ->  Sort
+                                 Output: t1_5.c
+                                 Sort Key: t1_5.c
+                                 ->  Seq Scan on public.plt2_p3 t1_5
+                                       Output: t1_5.c
+(88 rows)
+
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+  50 |  50 | 0001
+ 100 | 100 | 0002
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 250 | 250 | 0005
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 400 | 400 | 0008
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+ 550 | 550 | 0011
+(12 rows)
+
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM uplt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+  50 |  50 | 0001
+ 100 | 100 | 0002
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 250 | 250 | 0005
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 400 | 400 | 0008
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+ 550 | 550 | 0011
+(12 rows)
+
+SET enable_seqscan TO off;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c
+   Sort Key: t1.a, t2.b, ((t3.a + t3.b))
+   ->  Result
+         Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c
+         ->  Append
+               ->  Merge Left Join
+                     Output: t3.a, t3.b, t3.c, t2.b, t2.c, t1.a, t1.c
+                     Merge Cond: ((t2.b = t1.a) AND (t2.c = t1.c))
+                     ->  Sort
+                           Output: t3.a, t3.b, t3.c, t2.b, t2.c
+                           Sort Key: t2.b, t2.c
+                           ->  Merge Left Join
+                                 Output: t3.a, t3.b, t3.c, t2.b, t2.c
+                                 Merge Cond: (ltrim(t3.c, 'A'::text) = t2.c)
+                                 Join Filter: (t2.b = t3.a)
+                                 ->  Index Scan using iplt1_e_p1_c on public.plt1_e_p1 t3
+                                       Output: t3.a, t3.b, t3.c
+                                       Filter: ((t3.a % 25) = 0)
+                                 ->  Index Scan using iplt2_p1_c on public.plt2_p1 t2
+                                       Output: t2.b, t2.c
+                     ->  Sort
+                           Output: t1.a, t1.c
+                           Sort Key: t1.a, t1.c
+                           ->  Index Scan using iplt1_p1_c on public.plt1_p1 t1
+                                 Output: t1.a, t1.c
+               ->  Merge Left Join
+                     Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c, t1_1.a, t1_1.c
+                     Merge Cond: ((t2_1.c = t1_1.c) AND (t2_1.b = t1_1.a))
+                     ->  Sort
+                           Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+                           Sort Key: t2_1.c, t2_1.b
+                           ->  Merge Left Join
+                                 Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c
+                                 Merge Cond: (ltrim(t3_1.c, 'A'::text) = t2_1.c)
+                                 Join Filter: (t2_1.b = t3_1.a)
+                                 ->  Index Scan using iplt1_e_p2_c on public.plt1_e_p2 t3_1
+                                       Output: t3_1.a, t3_1.b, t3_1.c
+                                       Filter: ((t3_1.a % 25) = 0)
+                                 ->  Index Scan using iplt2_p2_c on public.plt2_p2 t2_1
+                                       Output: t2_1.b, t2_1.c
+                     ->  Sort
+                           Output: t1_1.a, t1_1.c
+                           Sort Key: t1_1.c, t1_1.a
+                           ->  Index Scan using iplt1_p2_c on public.plt1_p2 t1_1
+                                 Output: t1_1.a, t1_1.c
+               ->  Merge Left Join
+                     Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c, t1_2.a, t1_2.c
+                     Merge Cond: ((t2_2.b = t1_2.a) AND (t2_2.c = t1_2.c))
+                     ->  Sort
+                           Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+                           Sort Key: t2_2.b, t2_2.c
+                           ->  Merge Left Join
+                                 Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c
+                                 Merge Cond: (ltrim(t3_2.c, 'A'::text) = t2_2.c)
+                                 Join Filter: (t2_2.b = t3_2.a)
+                                 ->  Index Scan using iplt1_e_p3_c on public.plt1_e_p3 t3_2
+                                       Output: t3_2.a, t3_2.b, t3_2.c
+                                       Filter: ((t3_2.a % 25) = 0)
+                                 ->  Index Scan using iplt2_p3_c on public.plt2_p3 t2_2
+                                       Output: t2_2.b, t2_2.c
+                     ->  Sort
+                           Output: t1_2.a, t1_2.c
+                           Sort Key: t1_2.a, t1_2.c
+                           ->  Index Scan using iplt1_p3_c on public.plt1_p3 t1_2
+                                 Output: t1_2.a, t1_2.c
+(66 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 4) and (b 2) is considered for partition-wise join.
+NOTICE:  join between relations (b 2) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 2 4) and (b 1) is considered for partition-wise join.
+NOTICE:  join between relations (b 4) and (b 1 2) is considered for partition-wise join.
+  a  |  c   |  b  |  c   | ?column? |   c   
+-----+------+-----+------+----------+-------
+   0 | 0000 |   0 | 0000 |        0 | A0000
+ 150 | 0003 | 150 | 0003 |      300 | A0003
+ 300 | 0006 | 300 | 0006 |      600 | A0006
+ 450 | 0009 | 450 | 0009 |      900 | A0009
+     |      |     |      |      100 | A0001
+     |      |     |      |      200 | A0002
+     |      |     |      |      400 | A0004
+     |      |     |      |      500 | A0005
+     |      |     |      |      700 | A0007
+     |      |     |      |      800 | A0008
+     |      |     |      |     1000 | A0010
+     |      |     |      |     1100 | A0011
+(12 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+  a  |  c   |  b  |  c   | ?column? |   c   
+-----+------+-----+------+----------+-------
+   0 | 0000 |   0 | 0000 |        0 | A0000
+ 150 | 0003 | 150 | 0003 |      300 | A0003
+ 300 | 0006 | 300 | 0006 |      600 | A0006
+ 450 | 0009 | 450 | 0009 |      900 | A0009
+     |      |     |      |      100 | A0001
+     |      |     |      |      200 | A0002
+     |      |     |      |      400 | A0004
+     |      |     |      |      500 | A0005
+     |      |     |      |      700 | A0007
+     |      |     |      |      800 | A0008
+     |      |     |      |     1000 | A0010
+     |      |     |      |     1100 | A0011
+(12 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE:  join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+                                                  QUERY PLAN                                                   
+---------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, ((plt1_e_p1.a + plt1_e_p1.b)), plt1_e_p1.c
+   Sort Key: plt1_p1.a, plt2_p1.b, ((plt1_e_p1.a + plt1_e_p1.b))
+   ->  Result
+         Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, (plt1_e_p1.a + plt1_e_p1.b), plt1_e_p1.c
+         ->  Append
+               ->  Merge Full Join
+                     Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c
+                     Merge Cond: ((plt1_p1.a = plt1_e_p1.a) AND (plt1_p1.c = (ltrim(plt1_e_p1.c, 'A'::text))))
+                     ->  Sort
+                           Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c
+                           Sort Key: plt1_p1.a, plt1_p1.c
+                           ->  Merge Full Join
+                                 Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c
+                                 Merge Cond: ((plt1_p1.a = plt2_p1.b) AND (plt1_p1.c = plt2_p1.c))
+                                 ->  Sort
+                                       Output: plt1_p1.a, plt1_p1.c
+                                       Sort Key: plt1_p1.a, plt1_p1.c
+                                       ->  Index Scan using iplt1_p1_c on public.plt1_p1
+                                             Output: plt1_p1.a, plt1_p1.c
+                                             Filter: ((plt1_p1.a % 25) = 0)
+                                 ->  Sort
+                                       Output: plt2_p1.b, plt2_p1.c
+                                       Sort Key: plt2_p1.b, plt2_p1.c
+                                       ->  Index Scan using iplt2_p1_c on public.plt2_p1
+                                             Output: plt2_p1.b, plt2_p1.c
+                                             Filter: ((plt2_p1.b % 25) = 0)
+                     ->  Sort
+                           Output: plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c, (ltrim(plt1_e_p1.c, 'A'::text))
+                           Sort Key: plt1_e_p1.a, (ltrim(plt1_e_p1.c, 'A'::text))
+                           ->  Index Scan using iplt1_e_p1_c on public.plt1_e_p1
+                                 Output: plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c, ltrim(plt1_e_p1.c, 'A'::text)
+                                 Filter: ((plt1_e_p1.a % 25) = 0)
+               ->  Merge Full Join
+                     Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c, plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c
+                     Merge Cond: ((plt1_p2.a = plt1_e_p2.a) AND (plt1_p2.c = (ltrim(plt1_e_p2.c, 'A'::text))))
+                     ->  Sort
+                           Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c
+                           Sort Key: plt1_p2.a, plt1_p2.c
+                           ->  Merge Full Join
+                                 Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c
+                                 Merge Cond: ((plt1_p2.a = plt2_p2.b) AND (plt1_p2.c = plt2_p2.c))
+                                 ->  Sort
+                                       Output: plt1_p2.a, plt1_p2.c
+                                       Sort Key: plt1_p2.a, plt1_p2.c
+                                       ->  Index Scan using iplt1_p2_c on public.plt1_p2
+                                             Output: plt1_p2.a, plt1_p2.c
+                                             Filter: ((plt1_p2.a % 25) = 0)
+                                 ->  Sort
+                                       Output: plt2_p2.b, plt2_p2.c
+                                       Sort Key: plt2_p2.b, plt2_p2.c
+                                       ->  Index Scan using iplt2_p2_c on public.plt2_p2
+                                             Output: plt2_p2.b, plt2_p2.c
+                                             Filter: ((plt2_p2.b % 25) = 0)
+                     ->  Sort
+                           Output: plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c, (ltrim(plt1_e_p2.c, 'A'::text))
+                           Sort Key: plt1_e_p2.a, (ltrim(plt1_e_p2.c, 'A'::text))
+                           ->  Index Scan using iplt1_e_p2_c on public.plt1_e_p2
+                                 Output: plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c, ltrim(plt1_e_p2.c, 'A'::text)
+                                 Filter: ((plt1_e_p2.a % 25) = 0)
+               ->  Merge Full Join
+                     Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c, plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c
+                     Merge Cond: ((plt1_p3.a = plt1_e_p3.a) AND (plt1_p3.c = (ltrim(plt1_e_p3.c, 'A'::text))))
+                     ->  Sort
+                           Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c
+                           Sort Key: plt1_p3.a, plt1_p3.c
+                           ->  Merge Full Join
+                                 Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c
+                                 Merge Cond: ((plt1_p3.a = plt2_p3.b) AND (plt1_p3.c = plt2_p3.c))
+                                 ->  Sort
+                                       Output: plt1_p3.a, plt1_p3.c
+                                       Sort Key: plt1_p3.a, plt1_p3.c
+                                       ->  Index Scan using iplt1_p3_c on public.plt1_p3
+                                             Output: plt1_p3.a, plt1_p3.c
+                                             Filter: ((plt1_p3.a % 25) = 0)
+                                 ->  Sort
+                                       Output: plt2_p3.b, plt2_p3.c
+                                       Sort Key: plt2_p3.b, plt2_p3.c
+                                       ->  Index Scan using iplt2_p3_c on public.plt2_p3
+                                             Output: plt2_p3.b, plt2_p3.c
+                                             Filter: ((plt2_p3.b % 25) = 0)
+                     ->  Sort
+                           Output: plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c, (ltrim(plt1_e_p3.c, 'A'::text))
+                           Sort Key: plt1_e_p3.a, (ltrim(plt1_e_p3.c, 'A'::text))
+                           ->  Index Scan using iplt1_e_p3_c on public.plt1_e_p3
+                                 Output: plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c, ltrim(plt1_e_p3.c, 'A'::text)
+                                 Filter: ((plt1_e_p3.a % 25) = 0)
+(87 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+NOTICE:  join between relations (b 6) and (b 7) is considered for partition-wise join.
+NOTICE:  join between relations (b 6 7) and (b 8) is considered for partition-wise join.
+  a  |  c   |  b  |  c   | ?column? |   c   
+-----+------+-----+------+----------+-------
+   0 | 0000 |   0 | 0000 |        0 | A0000
+  50 | 0001 |     |      |      100 | A0001
+ 100 | 0002 |     |      |      200 | A0002
+ 150 | 0003 | 150 | 0003 |      300 | A0003
+ 200 | 0004 |     |      |      400 | A0004
+ 250 | 0005 |     |      |      500 | A0005
+ 300 | 0006 | 300 | 0006 |      600 | A0006
+ 350 | 0007 |     |      |      700 | A0007
+ 400 | 0008 |     |      |      800 | A0008
+ 450 | 0009 | 450 | 0009 |      900 | A0009
+ 500 | 0010 |     |      |     1000 | A0010
+ 550 | 0011 |     |      |     1100 | A0011
+     |      |  75 | 0001 |          | 
+     |      | 225 | 0004 |          | 
+     |      | 375 | 0007 |          | 
+     |      | 525 | 0010 |          | 
+(16 rows)
+
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM uplt1_e WHERE uplt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+  a  |  c   |  b  |  c   | ?column? |   c   
+-----+------+-----+------+----------+-------
+   0 | 0000 |   0 | 0000 |        0 | A0000
+  50 | 0001 |     |      |      100 | A0001
+ 100 | 0002 |     |      |      200 | A0002
+ 150 | 0003 | 150 | 0003 |      300 | A0003
+ 200 | 0004 |     |      |      400 | A0004
+ 250 | 0005 |     |      |      500 | A0005
+ 300 | 0006 | 300 | 0006 |      600 | A0006
+ 350 | 0007 |     |      |      700 | A0007
+ 400 | 0008 |     |      |      800 | A0008
+ 450 | 0009 | 450 | 0009 |      900 | A0009
+ 500 | 0010 |     |      |     1000 | A0010
+ 550 | 0011 |     |      |     1100 | A0011
+     |      |  75 | 0001 |          | 
+     |      | 225 | 0004 |          | 
+     |      | 375 | 0007 |          | 
+     |      | 525 | 0010 |          | 
+(16 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, t1.b, t1.c
+   Sort Key: t1.a
+   ->  Append
+         ->  Merge Semi Join
+               Output: t1.a, t1.b, t1.c
+               Merge Cond: (t1.c = t1_3.c)
+               ->  Index Scan using iplt1_p1_c on public.plt1_p1 t1
+                     Output: t1.a, t1.b, t1.c
+                     Filter: ((t1.a % 25) = 0)
+               ->  Materialize
+                     Output: t1_3.c, t1_6.c
+                     ->  Merge Semi Join
+                           Output: t1_3.c, t1_6.c
+                           Merge Cond: (t1_3.c = ltrim(t1_6.c, 'A'::text))
+                           ->  Index Only Scan using iplt2_p1_c on public.plt2_p1 t1_3
+                                 Output: t1_3.c
+                           ->  Materialize
+                                 Output: t1_6.c
+                                 ->  Index Scan using iplt1_e_p1_c on public.plt1_e_p1 t1_6
+                                       Output: t1_6.c
+                                       Filter: ((t1_6.a % 25) = 0)
+         ->  Merge Semi Join
+               Output: t1_1.a, t1_1.b, t1_1.c
+               Merge Cond: (t1_1.c = t1_4.c)
+               ->  Index Scan using iplt1_p2_c on public.plt1_p2 t1_1
+                     Output: t1_1.a, t1_1.b, t1_1.c
+                     Filter: ((t1_1.a % 25) = 0)
+               ->  Materialize
+                     Output: t1_4.c, t1_7.c
+                     ->  Merge Semi Join
+                           Output: t1_4.c, t1_7.c
+                           Merge Cond: (t1_4.c = ltrim(t1_7.c, 'A'::text))
+                           ->  Index Only Scan using iplt2_p2_c on public.plt2_p2 t1_4
+                                 Output: t1_4.c
+                           ->  Materialize
+                                 Output: t1_7.c
+                                 ->  Index Scan using iplt1_e_p2_c on public.plt1_e_p2 t1_7
+                                       Output: t1_7.c
+                                       Filter: ((t1_7.a % 25) = 0)
+         ->  Merge Semi Join
+               Output: t1_2.a, t1_2.b, t1_2.c
+               Merge Cond: (t1_2.c = t1_5.c)
+               ->  Index Scan using iplt1_p3_c on public.plt1_p3 t1_2
+                     Output: t1_2.a, t1_2.b, t1_2.c
+                     Filter: ((t1_2.a % 25) = 0)
+               ->  Materialize
+                     Output: t1_5.c, t1_8.c
+                     ->  Merge Semi Join
+                           Output: t1_5.c, t1_8.c
+                           Merge Cond: (t1_5.c = ltrim(t1_8.c, 'A'::text))
+                           ->  Index Only Scan using iplt2_p3_c on public.plt2_p3 t1_5
+                                 Output: t1_5.c
+                           ->  Materialize
+                                 Output: t1_8.c
+                                 ->  Index Scan using iplt1_e_p3_c on public.plt1_e_p3 t1_8
+                                       Output: t1_8.c
+                                       Filter: ((t1_8.a % 25) = 0)
+(58 rows)
+
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+NOTICE:  join between relations (b 3) and (b 5) is considered for partition-wise join.
+NOTICE:  join between relations (b 1) and (b 3 5) is considered for partition-wise join.
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+  50 |  50 | 0001
+ 100 | 100 | 0002
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 250 | 250 | 0005
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 400 | 400 | 0008
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+ 550 | 550 | 0011
+(12 rows)
+
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM uplt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+  a  |  b  |  c   
+-----+-----+------
+   0 |   0 | 0000
+  50 |  50 | 0001
+ 100 | 100 | 0002
+ 150 | 150 | 0003
+ 200 | 200 | 0004
+ 250 | 250 | 0005
+ 300 | 300 | 0006
+ 350 | 350 | 0007
+ 400 | 400 | 0008
+ 450 | 450 | 0009
+ 500 | 500 | 0010
+ 550 | 550 | 0011
+(12 rows)
+
+RESET enable_hashjoin;
+RESET enable_nestloop;
+RESET enable_seqscan;
+--
+-- negative testcases
+--
+CREATE TABLE prt1_n (a int, b int, c varchar) PARTITION BY RANGE(c);
+CREATE TABLE prt1_n_p1 PARTITION OF prt1_n FOR VALUES START ('0000') END ('0250');
+CREATE TABLE prt1_n_p2 PARTITION OF prt1_n FOR VALUES START ('0250') END ('0500');
+INSERT INTO prt1_n SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 499, 2) i;
+ANALYZE prt1_n;
+ANALYZE prt1_n_p1;
+ANALYZE prt1_n_p2;
+CREATE TABLE prt2_n (a int, b int, c text) PARTITION BY LIST(c);
+CREATE TABLE prt2_n_p1 PARTITION OF prt2_n FOR VALUES IN ('0000', '0003', '0004', '0010', '0006', '0007');
+CREATE TABLE prt2_n_p2 PARTITION OF prt2_n FOR VALUES IN ('0001', '0005', '0002', '0009', '0008', '0011');
+INSERT INTO prt2_n SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt2_n;
+ANALYZE prt2_n_p1;
+ANALYZE prt2_n_p2;
+CREATE TABLE prt3_n (a int, b int, c text) PARTITION BY LIST(c);
+CREATE TABLE prt3_n_p1 PARTITION OF prt3_n FOR VALUES IN ('0000', '0004', '0006', '0007');
+CREATE TABLE prt3_n_p2 PARTITION OF prt3_n FOR VALUES IN ('0001', '0002', '0008', '0010');
+CREATE TABLE prt3_n_p3 PARTITION OF prt3_n FOR VALUES IN ('0003', '0005', '0009', '0011');
+INSERT INTO prt2_n SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt3_n;
+ANALYZE prt3_n_p1;
+ANALYZE prt3_n_p2;
+ANALYZE prt3_n_p3;
+CREATE TABLE prt4_n (a int, b int, c text) PARTITION BY RANGE(a);
+CREATE TABLE prt4_n_p1 PARTITION OF prt4_n FOR VALUES START (0) END (300);
+CREATE TABLE prt4_n_p2 PARTITION OF prt4_n FOR VALUES START (300) END (500);
+CREATE TABLE prt4_n_p3 PARTITION OF prt4_n FOR VALUES START (500) END (600);
+INSERT INTO prt4_n SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt4_n;
+ANALYZE prt4_n_p1;
+ANALYZE prt4_n_p2;
+ANALYZE prt4_n_p3;
+-- partition-wise join can not be applied if the partition ranges differ
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2 WHERE t1.a = t2.a;
+                  QUERY PLAN                  
+----------------------------------------------
+ Hash Join
+   Hash Cond: (t1.a = t2.a)
+   ->  Append
+         ->  Seq Scan on prt1 t1
+         ->  Seq Scan on prt1_p1 t1_1
+         ->  Seq Scan on prt1_p3 t1_2
+         ->  Seq Scan on prt1_p2 t1_3
+   ->  Hash
+         ->  Append
+               ->  Seq Scan on prt4_n t2
+               ->  Seq Scan on prt4_n_p1 t2_1
+               ->  Seq Scan on prt4_n_p2 t2_2
+               ->  Seq Scan on prt4_n_p3 t2_3
+(13 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 FULL JOIN prt4_n t2 ON t1.a = t2.a;
+                  QUERY PLAN                  
+----------------------------------------------
+ Hash Full Join
+   Hash Cond: (t1.a = t2.a)
+   ->  Append
+         ->  Seq Scan on prt1 t1
+         ->  Seq Scan on prt1_p1 t1_1
+         ->  Seq Scan on prt1_p3 t1_2
+         ->  Seq Scan on prt1_p2 t1_3
+   ->  Hash
+         ->  Append
+               ->  Seq Scan on prt4_n t2
+               ->  Seq Scan on prt4_n_p1 t2_1
+               ->  Seq Scan on prt4_n_p2 t2_2
+               ->  Seq Scan on prt4_n_p3 t2_3
+(13 rows)
+
+-- partition-wise 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                        
+---------------------------------------------------------
+ Nested Loop Left Join
+   ->  Append
+         ->  Seq Scan on prt1 t1
+         ->  Seq Scan on prt1_p1 t1_1
+         ->  Seq Scan on prt1_p3 t1_2
+         ->  Seq Scan on prt1_p2 t1_3
+   ->  Append
+         ->  Seq Scan on prt2 t2
+               Filter: (t1.a < b)
+         ->  Index Scan using iprt2_p1_b on prt2_p1 t2_1
+               Index Cond: (t1.a < b)
+         ->  Index Scan using iprt2_p2_b on prt2_p2 t2_2
+               Index Cond: (t1.a < b)
+         ->  Index Scan using iprt2_p3_b on prt2_p3 t2_3
+               Index Cond: (t1.a < b)
+(15 rows)
+
+-- equi-join with join condition on partial keys does not qualify for
+-- partition-wise join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t1.a % 25 = 0;
+                  QUERY PLAN                  
+----------------------------------------------
+ Hash Join
+   Hash Cond: (((t2.b + t2.a) / 2) = t1.a)
+   ->  Append
+         ->  Seq Scan on prt2_m t2
+         ->  Seq Scan on prt2_m_p1 t2_1
+         ->  Seq Scan on prt2_m_p2 t2_2
+         ->  Seq Scan on prt2_m_p3 t2_3
+   ->  Hash
+         ->  Append
+               ->  Seq Scan on prt1_m t1
+                     Filter: ((a % 25) = 0)
+               ->  Seq Scan on prt1_m_p1 t1_1
+                     Filter: ((a % 25) = 0)
+               ->  Seq Scan on prt1_m_p2 t1_2
+                     Filter: ((a % 25) = 0)
+               ->  Seq Scan on prt1_m_p3 t1_3
+                     Filter: ((a % 25) = 0)
+(17 rows)
+
+-- equi-join between out-of-order partition key columns does not qualify for
+-- partition-wise join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = t2.b WHERE t1.a % 25 = 0;
+                  QUERY PLAN                  
+----------------------------------------------
+ Hash Right Join
+   Hash Cond: (t2.b = t1.a)
+   ->  Append
+         ->  Seq Scan on prt2_m t2
+         ->  Seq Scan on prt2_m_p1 t2_1
+         ->  Seq Scan on prt2_m_p2 t2_2
+         ->  Seq Scan on prt2_m_p3 t2_3
+   ->  Hash
+         ->  Append
+               ->  Seq Scan on prt1_m t1
+                     Filter: ((a % 25) = 0)
+               ->  Seq Scan on prt1_m_p1 t1_1
+                     Filter: ((a % 25) = 0)
+               ->  Seq Scan on prt1_m_p2 t1_2
+                     Filter: ((a % 25) = 0)
+               ->  Seq Scan on prt1_m_p3 t1_3
+                     Filter: ((a % 25) = 0)
+(17 rows)
+
+-- equi-join between non-key columns does not qualify for partition-wise join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.c = t2.c WHERE t1.a % 25 = 0;
+                  QUERY PLAN                  
+----------------------------------------------
+ Hash Right Join
+   Hash Cond: ((t2.c)::text = (t1.c)::text)
+   ->  Append
+         ->  Seq Scan on prt2_m t2
+         ->  Seq Scan on prt2_m_p1 t2_1
+         ->  Seq Scan on prt2_m_p2 t2_2
+         ->  Seq Scan on prt2_m_p3 t2_3
+   ->  Hash
+         ->  Append
+               ->  Seq Scan on prt1_m t1
+                     Filter: ((a % 25) = 0)
+               ->  Seq Scan on prt1_m_p1 t1_1
+                     Filter: ((a % 25) = 0)
+               ->  Seq Scan on prt1_m_p2 t1_2
+                     Filter: ((a % 25) = 0)
+               ->  Seq Scan on prt1_m_p3 t1_3
+                     Filter: ((a % 25) = 0)
+(17 rows)
+
+-- partition-wise join can not be applied for a join between list and range
+-- partitioned table
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1, prt2_n t2 WHERE t1.c = t2.c;
+                  QUERY PLAN                  
+----------------------------------------------
+ Hash Join
+   Hash Cond: (t2.c = (t1.c)::text)
+   ->  Append
+         ->  Seq Scan on prt2_n t2
+         ->  Seq Scan on prt2_n_p1 t2_1
+         ->  Seq Scan on prt2_n_p2 t2_2
+   ->  Hash
+         ->  Append
+               ->  Seq Scan on prt1_n t1
+               ->  Seq Scan on prt1_n_p1 t1_1
+               ->  Seq Scan on prt1_n_p2 t1_2
+(11 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 LEFT JOIN prt2_n t2 ON (t1.c = t2.c);
+                  QUERY PLAN                  
+----------------------------------------------
+ Hash Right Join
+   Hash Cond: (t2.c = (t1.c)::text)
+   ->  Append
+         ->  Seq Scan on prt2_n t2
+         ->  Seq Scan on prt2_n_p1 t2_1
+         ->  Seq Scan on prt2_n_p2 t2_2
+   ->  Hash
+         ->  Append
+               ->  Seq Scan on prt1_n t1
+               ->  Seq Scan on prt1_n_p1 t1_1
+               ->  Seq Scan on prt1_n_p2 t1_2
+(11 rows)
+
+-- partition-wise join can not be applied between tables with different
+-- partition lists
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 RIGHT JOIN prt1 t2 ON (t1.c = t2.c);
+                  QUERY PLAN                  
+----------------------------------------------
+ Hash Left Join
+   Hash Cond: ((t2.c)::text = (t1.c)::text)
+   ->  Append
+         ->  Seq Scan on prt1 t2
+         ->  Seq Scan on prt1_p1 t2_1
+         ->  Seq Scan on prt1_p3 t2_2
+         ->  Seq Scan on prt1_p2 t2_3
+   ->  Hash
+         ->  Append
+               ->  Seq Scan on prt1_n t1
+               ->  Seq Scan on prt1_n_p1 t1_1
+               ->  Seq Scan on prt1_n_p2 t1_2
+(12 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 FULL JOIN prt1 t2 ON (t1.c = t2.c);
+                  QUERY PLAN                  
+----------------------------------------------
+ Hash Full Join
+   Hash Cond: ((t2.c)::text = (t1.c)::text)
+   ->  Append
+         ->  Seq Scan on prt1 t2
+         ->  Seq Scan on prt1_p1 t2_1
+         ->  Seq Scan on prt1_p3 t2_2
+         ->  Seq Scan on prt1_p2 t2_3
+   ->  Hash
+         ->  Append
+               ->  Seq Scan on prt1_n t1
+               ->  Seq Scan on prt1_n_p1 t1_1
+               ->  Seq Scan on prt1_n_p2 t1_2
+(12 rows)
+
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index f06cfa4..16e7f56 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -1,25 +1,26 @@
 SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%';
-         name         | setting 
-----------------------+---------
- enable_bitmapscan    | on
- enable_hashagg       | on
- enable_hashjoin      | on
- enable_indexonlyscan | on
- enable_indexscan     | on
- enable_material      | on
- enable_mergejoin     | on
- enable_nestloop      | on
- enable_seqscan       | on
- enable_sort          | on
- enable_tidscan       | on
-(11 rows)
+            name            | setting 
+----------------------------+---------
+ enable_bitmapscan          | on
+ enable_hashagg             | on
+ enable_hashjoin            | on
+ enable_indexonlyscan       | on
+ enable_indexscan           | on
+ enable_material            | on
+ enable_mergejoin           | on
+ enable_nestloop            | on
+ enable_partition_wise_join | on
+ enable_seqscan             | on
+ enable_sort                | on
+ enable_tidscan             | on
+(12 rows)
 
 CREATE TABLE foo2(fooid int, f2 int);
 INSERT INTO foo2 VALUES(1, 11);
 INSERT INTO foo2 VALUES(2, 22);
 INSERT INTO foo2 VALUES(1, 111);
 CREATE FUNCTION foot(int) returns setof foo2 as 'SELECT * FROM foo2 WHERE fooid = $1 ORDER BY f2;' LANGUAGE SQL;
 -- function with ORDINALITY
 select * from foot(1) with ordinality as z(a,b,ord);
  a |  b  | ord 
 ---+-----+-----
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 1cb5dfc..d62841d 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -93,21 +93,21 @@ test: alter_generic alter_operator misc psql async dbsize misc_functions
 
 # rules cannot run concurrently with any test that creates a view
 test: rules psql_crosstab amutils
 
 # run by itself so it can run parallel workers
 test: select_parallel
 
 # ----------
 # Another group of parallel tests
 # ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
+test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass partition_join
 # ----------
 # Another group of parallel tests
 # NB: temp.sql does a reconnect which transiently uses 2 connections,
 # so keep this parallel group to at most 19 tests
 # ----------
 test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
 
 # event triggers cannot run concurrently with any test that runs DDL
 test: event_trigger
 
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 8958d8c..49a27b1 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -161,10 +161,11 @@ test: truncate
 test: alter_table
 test: sequence
 test: polymorphism
 test: rowtypes
 test: returning
 test: largeobject
 test: with
 test: xml
 test: event_trigger
 test: stats
+test: partition_join
diff --git a/src/test/regress/sql/partition_join.sql b/src/test/regress/sql/partition_join.sql
new file mode 100644
index 0000000..19c7d29
--- /dev/null
+++ b/src/test/regress/sql/partition_join.sql
@@ -0,0 +1,746 @@
+--
+-- PARTITION_JOIN
+-- Test partition-wise join between partitioned tables
+--
+
+--
+-- partitioned by a single column
+--
+CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a);
+CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES START (0) END (250);
+CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES START (500) END (600);
+CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES START (250) END (500);
+INSERT INTO prt1 SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt1;
+ANALYZE prt1_p1;
+ANALYZE prt1_p2;
+ANALYZE prt1_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt1 AS SELECT * FROM prt1;
+
+CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b);
+CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES START (0) END (250);
+CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES START (250) END (500);
+CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES START (500) END (600);
+INSERT INTO prt2 SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+ANALYZE prt2;
+ANALYZE prt2_p1;
+ANALYZE prt2_p2;
+ANALYZE prt2_p3;
+CREATE TABLE uprt2 AS SELECT * FROM prt2;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1, uprt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2 t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+
+-- Cases with non-nullable expressions in subquery results;
+-- make sure these go to null as expected
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.b OR t2.phv = t2.b ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.b OR t2.phv = t2.b ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.b OR t2.phv = t2.b ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
+
+-- Join with pruned partitions from joining relations
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1, uprt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+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.a % 25 = 0 ORDER BY t1.a, t2.b;
+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.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM uprt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 RIGHT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 RIGHT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a < 450) t1 RIGHT JOIN (SELECT * FROM uprt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450 AND a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250 AND b % 25 = 0) t2 ON t1.a = t2.b ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450 AND a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250 AND b % 25 = 0) t2 ON t1.a = t2.b ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a < 450 AND a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2 WHERE b > 250 AND b % 25 = 0) t2 ON t1.a = t2.b ORDER BY t1.a, t2.b;
+
+-- Semi-join
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+
+-- lateral reference
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM uprt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM uprt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+
+--
+-- partitioned by expression
+--
+CREATE TABLE prt1_e (a int, b int, c varchar) PARTITION BY RANGE(((a + b)/2));
+CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES START (0) END (250);
+CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES START (250) END (500);
+CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES START (500) END (600);
+INSERT INTO prt1_e SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt1_e;
+ANALYZE prt1_e_p1;
+ANALYZE prt1_e_p2;
+ANALYZE prt1_e_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt1_e AS SELECT * FROM prt1_e;
+
+CREATE TABLE prt2_e (a int, b int, c varchar) PARTITION BY RANGE(((b + a)/2));
+CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES START (0) END (250);
+CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES START (250) END (500);
+CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES START (500) END (600);
+INSERT INTO prt2_e SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+ANALYZE prt2_e;
+ANALYZE prt2_e_p1;
+ANALYZE prt2_e_p2;
+ANALYZE prt2_e_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt2_e AS SELECT * FROM prt2_e;
+
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b;
+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.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_e t1, uprt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 LEFT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 LEFT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_e t1 LEFT JOIN uprt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 RIGHT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 RIGHT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_e t1 RIGHT JOIN uprt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_e WHERE prt2_e.b % 25 = 0) t2 ON ((t1.a + t1.b)/2 = (t2.b + t2.a)/2) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_e WHERE prt2_e.b % 25 = 0) t2 ON ((t1.a + t1.b)/2 = (t2.b + t2.a)/2) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1_e t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2_e t2 WHERE t2.b % 25 = 0) t2 ON ((t1.a + t1.b)/2 = (t2.b + t2.a)/2) ORDER BY t1.a, t2.b;
+
+--
+-- N-way join
+--
+EXPLAIN (VERBOSE, COSTS OFF)
+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.a % 25 = 0 ORDER BY t1.a, t2.b;
+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.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM uprt1 t1, uprt2 t2, uprt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) LEFT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, 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 (t2.b = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+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 (t2.b = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) LEFT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t3 ON (t1.a = (t3.a + t3.b)/2) ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t3 ON (t1.a = (t3.a + t3.b)/2) ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT * FROM uprt1_e WHERE uprt1_e.a % 25 = 0) t3 ON (t1.a = (t3.a + t3.b)/2) ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+-- Cases with non-nullable expressions in subquery results;
+-- make sure these go to null as expected
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.a % 25 = 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;
+SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.a % 25 = 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;
+SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM uprt1_e WHERE uprt1_e.a % 25 = 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;
+
+-- Semi-join
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.b % 25 = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.b % 25 = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1, uprt1_e t2 WHERE t1.b % 25 = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.a % 25 = 0 ORDER BY t1.a;
+
+EXPLAIN (VERBOSE, 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM uprt1_e t1 WHERE t1.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+
+-- test merge joins with and without using indexes
+SET enable_hashjoin TO off;
+SET enable_nestloop TO off;
+
+CREATE INDEX iprt1_a on prt1(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 iprt2_b on prt2(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 iprt1_e_ab2 on prt1_e(((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));
+
+ANALYZE prt1;
+ANALYZE prt1_p1;
+ANALYZE prt1_p2;
+ANALYZE prt1_p3;
+ANALYZE prt2;
+ANALYZE prt2_p1;
+ANALYZE prt2_p2;
+ANALYZE prt2_p3;
+ANALYZE prt1_e;
+ANALYZE prt1_e_p1;
+ANALYZE prt1_e_p2;
+ANALYZE prt1_e_p3;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM uprt1_e t1 WHERE t1.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+SET enable_seqscan TO off;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM uprt1_e t1 WHERE t1.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+
+EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+-- lateral references and parameterized paths
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM uprt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM prt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+SELECT * FROM uprt1 t1 LEFT JOIN LATERAL
+			  (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss
+			  ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a;
+
+RESET enable_hashjoin;
+RESET enable_nestloop;
+RESET enable_seqscan;
+
+--
+-- partitioned by multiple columns
+--
+CREATE TABLE prt1_m (a int, b int, c varchar) PARTITION BY RANGE(a, ((a + b)/2));
+CREATE TABLE prt1_m_p1 PARTITION OF prt1_m FOR VALUES START (0, 0) END (250, 250);
+CREATE TABLE prt1_m_p2 PARTITION OF prt1_m FOR VALUES START (250, 250) END (500, 500);
+CREATE TABLE prt1_m_p3 PARTITION OF prt1_m FOR VALUES START (500, 500) END (600, 600);
+INSERT INTO prt1_m SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt1_m;
+ANALYZE prt1_m_p1;
+ANALYZE prt1_m_p2;
+ANALYZE prt1_m_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt1_m AS SELECT * FROM prt1_m;
+
+CREATE TABLE prt2_m (a int, b int, c varchar) PARTITION BY RANGE(((b + a)/2), b);
+CREATE TABLE prt2_m_p1 PARTITION OF prt2_m FOR VALUES START (0, 0) END (250, 250);
+CREATE TABLE prt2_m_p2 PARTITION OF prt2_m FOR VALUES START (250, 250) END (500, 500);
+CREATE TABLE prt2_m_p3 PARTITION OF prt2_m FOR VALUES START (500, 500) END (600, 600);
+INSERT INTO prt2_m SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+ANALYZE prt2_m;
+ANALYZE prt2_m_p1;
+ANALYZE prt2_m_p2;
+ANALYZE prt2_m_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt2_m AS SELECT * FROM prt2_m;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_m t1, uprt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_m t1 LEFT JOIN uprt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 RIGHT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 RIGHT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_m t1 RIGHT JOIN uprt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.b % 25 = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.b % 25 = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1_m t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2_m t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b;
+
+--
+-- multi-leveled partitions
+--
+CREATE TABLE prt1_l (a int, b int, c varchar) PARTITION BY RANGE(a);
+CREATE TABLE prt1_l_p1 PARTITION OF prt1_l FOR VALUES START (0) END (250) PARTITION BY RANGE (b);
+CREATE TABLE prt1_l_p1_p1 PARTITION OF prt1_l_p1 FOR VALUES START (0) END (100);
+CREATE TABLE prt1_l_p1_p2 PARTITION OF prt1_l_p1 FOR VALUES START (100) END (250);
+CREATE TABLE prt1_l_p2 PARTITION OF prt1_l FOR VALUES START (250) END (500) PARTITION BY RANGE (c);
+CREATE TABLE prt1_l_p2_p1 PARTITION OF prt1_l_p2 FOR VALUES START ('0250') END ('0400');
+CREATE TABLE prt1_l_p2_p2 PARTITION OF prt1_l_p2 FOR VALUES START ('0400') END ('0500');
+CREATE TABLE prt1_l_p3 PARTITION OF prt1_l FOR VALUES START (500) END (600) PARTITION BY RANGE ((b + a));
+CREATE TABLE prt1_l_p3_p1 PARTITION OF prt1_l_p3 FOR VALUES START (1000) END (1100);
+CREATE TABLE prt1_l_p3_p2 PARTITION OF prt1_l_p3 FOR VALUES START (1100) END (1200);
+INSERT INTO prt1_l SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt1_l;
+ANALYZE prt1_l_p1;
+ANALYZE prt1_l_p1_p1;
+ANALYZE prt1_l_p1_p2;
+ANALYZE prt1_l_p2;
+ANALYZE prt1_l_p2_p1;
+ANALYZE prt1_l_p2_p2;
+ANALYZE prt1_l_p3;
+ANALYZE prt1_l_p3_p1;
+ANALYZE prt1_l_p3_p2;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt1_l AS SELECT * FROM prt1_l;
+
+CREATE TABLE prt2_l (a int, b int, c varchar) PARTITION BY RANGE(b);
+CREATE TABLE prt2_l_p1 PARTITION OF prt2_l FOR VALUES START (0) END (250) PARTITION BY RANGE (a);
+CREATE TABLE prt2_l_p1_p1 PARTITION OF prt2_l_p1 FOR VALUES START (0) END (100);
+CREATE TABLE prt2_l_p1_p2 PARTITION OF prt2_l_p1 FOR VALUES START (100) END (250);
+CREATE TABLE prt2_l_p2 PARTITION OF prt2_l FOR VALUES START (250) END (500) PARTITION BY RANGE (c);
+CREATE TABLE prt2_l_p2_p1 PARTITION OF prt2_l_p2 FOR VALUES START ('0250') END ('0400');
+CREATE TABLE prt2_l_p2_p2 PARTITION OF prt2_l_p2 FOR VALUES START ('0400') END ('0500');
+CREATE TABLE prt2_l_p3 PARTITION OF prt2_l FOR VALUES START (500) END (600) PARTITION BY RANGE ((a + b));
+CREATE TABLE prt2_l_p3_p1 PARTITION OF prt2_l_p3 FOR VALUES START (1000) END (1100);
+CREATE TABLE prt2_l_p3_p2 PARTITION OF prt2_l_p3 FOR VALUES START (1100) END (1200);
+INSERT INTO prt2_l SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i;
+ANALYZE prt2_l;
+ANALYZE prt2_l_p1;
+ANALYZE prt2_l_p1_p1;
+ANALYZE prt2_l_p1_p2;
+ANALYZE prt2_l_p2;
+ANALYZE prt2_l_p2_p1;
+ANALYZE prt2_l_p2_p2;
+ANALYZE prt2_l_p3;
+ANALYZE prt2_l_p3_p1;
+ANALYZE prt2_l_p3_p2;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uprt2_l AS SELECT * FROM prt2_l;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_l t1, uprt2_l t2 WHERE t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_l t1 LEFT JOIN uprt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_l t1 RIGHT JOIN uprt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1_l t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2_l t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b) ORDER BY t1.a, t2.b;
+
+--
+-- 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;
+ANALYZE plt1_p1;
+ANALYZE plt1_p2;
+ANALYZE plt1_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uplt1 AS SELECT * FROM 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;
+ANALYZE plt2_p1;
+ANALYZE plt2_p2;
+ANALYZE plt2_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uplt2 AS SELECT * FROM plt2;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.a = t2.a AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.a = t2.a AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1 t1, uplt2 t2 WHERE t1.c = t2.c AND t1.a = t2.a AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.a AND t1.c = t2.c WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.a AND t1.c = t2.c WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.a AND t1.c = t2.c WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b;
+
+-- Cases with non-nullable expressions in subquery results;
+-- make sure these go to null as expected
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM (SELECT 50 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM (SELECT 50 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM (SELECT 50 phv, * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, sum(t1.phv), avg(t2.b), t2.c, avg(t2.phv) FROM (SELECT 25 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, sum(t1.phv), avg(t2.b), t2.c, avg(t2.phv) FROM (SELECT 25 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, sum(t1.phv), avg(t2.b), t2.c, avg(t2.phv) FROM (SELECT 25 phv, * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+
+-- Join with pruned partitions from joining relations
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.c NOT IN ('0001', '0005', '0002', '0009') AND t2.c NOT IN ('0000', '0003', '0004', '0010') GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.c NOT IN ('0001', '0005', '0002', '0009') AND t2.c NOT IN ('0000', '0003', '0004', '0010') GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM uplt1 t1, uplt2 t2 WHERE t1.c = t2.c AND t1.c NOT IN ('0001', '0005', '0002', '0009') AND t2.c NOT IN ('0000', '0003', '0004', '0010') GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 LEFT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 LEFT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 LEFT JOIN (SELECT * FROM uplt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 RIGHT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 RIGHT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 RIGHT JOIN (SELECT * FROM uplt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 FULL JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 FULL JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 FULL JOIN (SELECT * FROM uplt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c;
+
+-- Semi-join
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a;
+
+--
+-- 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;
+ANALYZE plt1_e_p1;
+ANALYZE plt1_e_p2;
+ANALYZE plt1_e_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uplt1_e AS SELECT * FROM plt1_e;
+
+CREATE TABLE plt2_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A'));
+CREATE TABLE plt2_e_p1 PARTITION OF plt2_e FOR VALUES IN ('0000', '0003', '0004', '0010');
+CREATE TABLE plt2_e_p2 PARTITION OF plt2_e FOR VALUES IN ('0001', '0005', '0002', '0009');
+CREATE TABLE plt2_e_p3 PARTITION OF plt2_e FOR VALUES IN ('0006', '0007', '0008', '0011');
+INSERT INTO plt2_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i;
+ANALYZE plt2_e;
+ANALYZE plt2_e_p1;
+ANALYZE plt2_e_p2;
+ANALYZE plt2_e_p3;
+-- TODO: This table is created only for testing the results. Remove once
+-- results are tested.
+CREATE TABLE uplt2_e AS SELECT * FROM plt2_e;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1, plt2_e t2 WHERE t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1, plt2_e t2 WHERE t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1_e t1, uplt2_e t2 WHERE t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') AND t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 LEFT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 LEFT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1_e t1 LEFT JOIN uplt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 RIGHT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 RIGHT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1_e t1 RIGHT JOIN uplt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2_e WHERE plt2_e.b % 25 = 0) t2 ON (t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A')) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2_e WHERE plt2_e.b % 25 = 0) t2 ON (t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A')) ORDER BY t1.a, t2.b;
+SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uplt1_e t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2_e t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A')) ORDER BY t1.a, t2.b;
+
+--
+-- N-way join
+--
+EXPLAIN (VERBOSE, 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.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.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 uplt1 t1, uplt2 t2, uplt1_e t3 WHERE 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;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN uplt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM uplt1_e WHERE uplt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+-- Semi-join
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1, plt1_e t2 WHERE t1.c = ltrim(t2.c, 'A')) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1, plt1_e t2 WHERE t1.c = ltrim(t2.c, 'A')) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1, uplt1_e t2 WHERE t1.c = ltrim(t2.c, 'A')) AND t1.a % 25 = 0 ORDER BY t1.a;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM uplt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+
+-- test merge join with and without index scan
+CREATE INDEX iplt1_c on plt1(c);
+CREATE INDEX iplt1_p1_c on plt1_p1(c);
+CREATE INDEX iplt1_p2_c on plt1_p2(c);
+CREATE INDEX iplt1_p3_c on plt1_p3(c);
+CREATE INDEX iplt2_c on plt2(c);
+CREATE INDEX iplt2_p1_c on plt2_p1(c);
+CREATE INDEX iplt2_p2_c on plt2_p2(c);
+CREATE INDEX iplt2_p3_c on plt2_p3(c);
+CREATE INDEX iplt1_e_c on plt1_e(ltrim(c, 'A'));
+CREATE INDEX iplt1_e_p1_c on plt1_e_p1(ltrim(c, 'A'));
+CREATE INDEX iplt1_e_p2_c on plt1_e_p2(ltrim(c, 'A'));
+CREATE INDEX iplt1_e_p3_c on plt1_e_p3(ltrim(c, 'A'));
+
+ANALYZE plt1;
+ANALYZE plt1_p1;
+ANALYZE plt1_p2;
+ANALYZE plt1_p3;
+ANALYZE plt2;
+ANALYZE plt2_p1;
+ANALYZE plt2_p2;
+ANALYZE plt2_p3;
+ANALYZE plt1_e;
+ANALYZE plt1_e_p1;
+ANALYZE plt1_e_p2;
+ANALYZE plt1_e_p3;
+
+SET enable_hashjoin TO off;
+SET enable_nestloop TO off;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM uplt1_e WHERE uplt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM uplt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+
+SET enable_seqscan TO off;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM uplt1_e WHERE uplt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM uplt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a;
+
+RESET enable_hashjoin;
+RESET enable_nestloop;
+RESET enable_seqscan;
+
+--
+-- negative testcases
+--
+
+CREATE TABLE prt1_n (a int, b int, c varchar) PARTITION BY RANGE(c);
+CREATE TABLE prt1_n_p1 PARTITION OF prt1_n FOR VALUES START ('0000') END ('0250');
+CREATE TABLE prt1_n_p2 PARTITION OF prt1_n FOR VALUES START ('0250') END ('0500');
+INSERT INTO prt1_n SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 499, 2) i;
+ANALYZE prt1_n;
+ANALYZE prt1_n_p1;
+ANALYZE prt1_n_p2;
+
+CREATE TABLE prt2_n (a int, b int, c text) PARTITION BY LIST(c);
+CREATE TABLE prt2_n_p1 PARTITION OF prt2_n FOR VALUES IN ('0000', '0003', '0004', '0010', '0006', '0007');
+CREATE TABLE prt2_n_p2 PARTITION OF prt2_n FOR VALUES IN ('0001', '0005', '0002', '0009', '0008', '0011');
+INSERT INTO prt2_n SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt2_n;
+ANALYZE prt2_n_p1;
+ANALYZE prt2_n_p2;
+
+CREATE TABLE prt3_n (a int, b int, c text) PARTITION BY LIST(c);
+CREATE TABLE prt3_n_p1 PARTITION OF prt3_n FOR VALUES IN ('0000', '0004', '0006', '0007');
+CREATE TABLE prt3_n_p2 PARTITION OF prt3_n FOR VALUES IN ('0001', '0002', '0008', '0010');
+CREATE TABLE prt3_n_p3 PARTITION OF prt3_n FOR VALUES IN ('0003', '0005', '0009', '0011');
+INSERT INTO prt2_n SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt3_n;
+ANALYZE prt3_n_p1;
+ANALYZE prt3_n_p2;
+ANALYZE prt3_n_p3;
+
+CREATE TABLE prt4_n (a int, b int, c text) PARTITION BY RANGE(a);
+CREATE TABLE prt4_n_p1 PARTITION OF prt4_n FOR VALUES START (0) END (300);
+CREATE TABLE prt4_n_p2 PARTITION OF prt4_n FOR VALUES START (300) END (500);
+CREATE TABLE prt4_n_p3 PARTITION OF prt4_n FOR VALUES START (500) END (600);
+INSERT INTO prt4_n SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i;
+ANALYZE prt4_n;
+ANALYZE prt4_n_p1;
+ANALYZE prt4_n_p2;
+ANALYZE prt4_n_p3;
+
+-- partition-wise join can not be applied if the partition ranges differ
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2 WHERE t1.a = t2.a;
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 FULL JOIN prt4_n t2 ON t1.a = t2.a;
+-- partition-wise 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);
+-- equi-join with join condition on partial keys does not qualify for
+-- partition-wise join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t1.a % 25 = 0;
+-- equi-join between out-of-order partition key columns does not qualify for
+-- partition-wise join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = t2.b WHERE t1.a % 25 = 0;
+-- equi-join between non-key columns does not qualify for partition-wise join
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.c = t2.c WHERE t1.a % 25 = 0;
+-- partition-wise join can not be applied for a join between list and range
+-- partitioned table
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1, prt2_n t2 WHERE t1.c = t2.c;
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 LEFT JOIN prt2_n t2 ON (t1.c = t2.c);
+-- partition-wise join can not be applied between tables with different
+-- partition lists
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 RIGHT JOIN prt1 t2 ON (t1.c = t2.c);
+EXPLAIN (COSTS OFF)
+SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 FULL JOIN prt1 t2 ON (t1.c = t2.c);
