*** a/postgres-master/contrib/postgres_fdw/expected/postgres_fdw.out
--- b/postgres-master/contrib/postgres_fdw/expected/postgres_fdw.out
***************
*** 8337,8344 **** ALTER TABLE fprt2_p1 SET (autovacuum_enabled = 'false');
  ALTER TABLE fprt2_p2 SET (autovacuum_enabled = 'false');
  INSERT INTO fprt2_p1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 249, 3) i;
  INSERT INTO fprt2_p2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(250, 499, 3) i;
! CREATE FOREIGN TABLE ftprt2_p1 PARTITION OF fprt2 FOR VALUES FROM (0) TO (250)
  	SERVER loopback OPTIONS (table_name 'fprt2_p1', use_remote_estimate 'true');
  CREATE FOREIGN TABLE ftprt2_p2 PARTITION OF fprt2 FOR VALUES FROM (250) TO (500)
  	SERVER loopback OPTIONS (table_name 'fprt2_p2', use_remote_estimate 'true');
  ANALYZE fprt2;
--- 8337,8345 ----
  ALTER TABLE fprt2_p2 SET (autovacuum_enabled = 'false');
  INSERT INTO fprt2_p1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 249, 3) i;
  INSERT INTO fprt2_p2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(250, 499, 3) i;
! CREATE FOREIGN TABLE ftprt2_p1 (b int, c varchar, a int)
  	SERVER loopback OPTIONS (table_name 'fprt2_p1', use_remote_estimate 'true');
+ ALTER TABLE fprt2 ATTACH PARTITION ftprt2_p1 FOR VALUES FROM (0) TO (250);
  CREATE FOREIGN TABLE ftprt2_p2 PARTITION OF fprt2 FOR VALUES FROM (250) TO (500)
  	SERVER loopback OPTIONS (table_name 'fprt2_p2', use_remote_estimate 'true');
  ANALYZE fprt2;
***************
*** 8391,8416 **** SELECT t1.a,t2.b,t2.c FROM fprt1 t1 LEFT JOIN (SELECT * FROM fprt2 WHERE a < 10)
  
  -- with whole-row reference
  EXPLAIN (COSTS OFF)
! SELECT t1,t2 FROM fprt1 t1 JOIN fprt2 t2 ON (t1.a = t2.b and t1.b = t2.a) WHERE t1.a % 25 =0 ORDER BY 1,2;
!                                    QUERY PLAN                                    
! ---------------------------------------------------------------------------------
   Sort
     Sort Key: ((t1.*)::fprt1), ((t2.*)::fprt2)
     ->  Append
           ->  Foreign Scan
!                Relations: (public.ftprt1_p1 t1) INNER JOIN (public.ftprt2_p1 t2)
           ->  Foreign Scan
!                Relations: (public.ftprt1_p2 t1) INNER JOIN (public.ftprt2_p2 t2)
  (7 rows)
  
! SELECT t1,t2 FROM fprt1 t1 JOIN fprt2 t2 ON (t1.a = t2.b and t1.b = t2.a) WHERE t1.a % 25 =0 ORDER BY 1,2;
!        t1       |       t2       
  ----------------+----------------
   (0,0,0000)     | (0,0,0000)
   (150,150,0003) | (150,150,0003)
   (250,250,0005) | (250,250,0005)
   (400,400,0008) | (400,400,0008)
! (4 rows)
  
  -- join with lateral reference
  EXPLAIN (COSTS OFF)
--- 8392,8427 ----
  
  -- with whole-row reference
  EXPLAIN (COSTS OFF)
! SELECT t1.wr, t2.wr FROM (SELECT t1 wr, a FROM fprt1 t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT t2 wr, b FROM fprt2 t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY 1,2;
!                                    QUERY PLAN                                   
! --------------------------------------------------------------------------------
   Sort
     Sort Key: ((t1.*)::fprt1), ((t2.*)::fprt2)
     ->  Append
           ->  Foreign Scan
!                Relations: (public.ftprt1_p1 t1) FULL JOIN (public.ftprt2_p1 t2)
           ->  Foreign Scan
!                Relations: (public.ftprt1_p2 t1) FULL JOIN (public.ftprt2_p2 t2)
  (7 rows)
  
! SELECT t1.wr, t2.wr FROM (SELECT t1 wr, a FROM fprt1 t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT t2 wr, b FROM fprt2 t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY 1,2;
!        wr       |       wr       
  ----------------+----------------
   (0,0,0000)     | (0,0,0000)
+  (50,50,0001)   | 
+  (100,100,0002) | 
   (150,150,0003) | (150,150,0003)
+  (200,200,0004) | 
   (250,250,0005) | (250,250,0005)
+  (300,300,0006) | 
+  (350,350,0007) | 
   (400,400,0008) | (400,400,0008)
!  (450,450,0009) | 
!                 | (75,75,0001)
!                 | (225,225,0004)
!                 | (325,325,0006)
!                 | (475,475,0009)
! (14 rows)
  
  -- join with lateral reference
  EXPLAIN (COSTS OFF)
***************
*** 8474,8479 **** SELECT t1.a, t1.phv, t2.b, t2.phv FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE
--- 8485,8520 ----
       |        | 475 | t2_phv
  (14 rows)
  
+ -- test FOR UPDATE
+ EXPLAIN (COSTS OFF)
+ SELECT t1.a, t2.b FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.a = t2.b) WHERE t1.a % 25 = 0 ORDER BY 1,2 FOR UPDATE OF t1;
+                                       QUERY PLAN                                       
+ ---------------------------------------------------------------------------------------
+  LockRows
+    ->  Sort
+          Sort Key: t1.a
+          ->  Append
+                ->  Foreign Scan
+                      Relations: (public.ftprt1_p1 t1) INNER JOIN (public.ftprt2_p1 t2)
+                      ->  Nested Loop
+                            ->  Foreign Scan on ftprt1_p1 t1
+                            ->  Foreign Scan on ftprt2_p1 t2
+                ->  Foreign Scan
+                      Relations: (public.ftprt1_p2 t1) INNER JOIN (public.ftprt2_p2 t2)
+                      ->  Nested Loop
+                            ->  Foreign Scan on ftprt1_p2 t1_1
+                            ->  Foreign Scan on ftprt2_p2 t2_1
+ (14 rows)
+ 
+ SELECT t1.a, t2.b FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.a = t2.b) WHERE t1.a % 25 = 0 ORDER BY 1,2 FOR UPDATE OF t1;
+   a  |  b  
+ -----+-----
+    0 |   0
+  150 | 150
+  250 | 250
+  400 | 400
+ (4 rows)
+ 
  RESET enable_partitionwise_join;
  -- ===================================================================
  -- test partitionwise aggregates
*** a/postgres-master/contrib/postgres_fdw/sql/postgres_fdw.sql
--- b/postgres-master/contrib/postgres_fdw/sql/postgres_fdw.sql
***************
*** 2263,2270 **** ALTER TABLE fprt2_p1 SET (autovacuum_enabled = 'false');
  ALTER TABLE fprt2_p2 SET (autovacuum_enabled = 'false');
  INSERT INTO fprt2_p1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 249, 3) i;
  INSERT INTO fprt2_p2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(250, 499, 3) i;
! CREATE FOREIGN TABLE ftprt2_p1 PARTITION OF fprt2 FOR VALUES FROM (0) TO (250)
  	SERVER loopback OPTIONS (table_name 'fprt2_p1', use_remote_estimate 'true');
  CREATE FOREIGN TABLE ftprt2_p2 PARTITION OF fprt2 FOR VALUES FROM (250) TO (500)
  	SERVER loopback OPTIONS (table_name 'fprt2_p2', use_remote_estimate 'true');
  ANALYZE fprt2;
--- 2263,2271 ----
  ALTER TABLE fprt2_p2 SET (autovacuum_enabled = 'false');
  INSERT INTO fprt2_p1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 249, 3) i;
  INSERT INTO fprt2_p2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(250, 499, 3) i;
! CREATE FOREIGN TABLE ftprt2_p1 (b int, c varchar, a int)
  	SERVER loopback OPTIONS (table_name 'fprt2_p1', use_remote_estimate 'true');
+ ALTER TABLE fprt2 ATTACH PARTITION ftprt2_p1 FOR VALUES FROM (0) TO (250);
  CREATE FOREIGN TABLE ftprt2_p2 PARTITION OF fprt2 FOR VALUES FROM (250) TO (500)
  	SERVER loopback OPTIONS (table_name 'fprt2_p2', use_remote_estimate 'true');
  ANALYZE fprt2;
***************
*** 2283,2290 **** SELECT t1.a,t2.b,t2.c FROM fprt1 t1 LEFT JOIN (SELECT * FROM fprt2 WHERE a < 10)
  
  -- with whole-row reference
  EXPLAIN (COSTS OFF)
! SELECT t1,t2 FROM fprt1 t1 JOIN fprt2 t2 ON (t1.a = t2.b and t1.b = t2.a) WHERE t1.a % 25 =0 ORDER BY 1,2;
! SELECT t1,t2 FROM fprt1 t1 JOIN fprt2 t2 ON (t1.a = t2.b and t1.b = t2.a) WHERE t1.a % 25 =0 ORDER BY 1,2;
  
  -- join with lateral reference
  EXPLAIN (COSTS OFF)
--- 2284,2291 ----
  
  -- with whole-row reference
  EXPLAIN (COSTS OFF)
! SELECT t1.wr, t2.wr FROM (SELECT t1 wr, a FROM fprt1 t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT t2 wr, b FROM fprt2 t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY 1,2;
! SELECT t1.wr, t2.wr FROM (SELECT t1 wr, a FROM fprt1 t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT t2 wr, b FROM fprt2 t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY 1,2;
  
  -- join with lateral reference
  EXPLAIN (COSTS OFF)
***************
*** 2296,2301 **** EXPLAIN (COSTS OFF)
--- 2297,2307 ----
  SELECT t1.a, t1.phv, t2.b, t2.phv FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE a % 25 = 0) t1 FULL JOIN (SELECT 't2_phv' phv, * FROM fprt2 WHERE b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
  SELECT t1.a, t1.phv, t2.b, t2.phv FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE a % 25 = 0) t1 FULL JOIN (SELECT 't2_phv' phv, * FROM fprt2 WHERE b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b;
  
+ -- test FOR UPDATE
+ EXPLAIN (COSTS OFF)
+ SELECT t1.a, t2.b FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.a = t2.b) WHERE t1.a % 25 = 0 ORDER BY 1,2 FOR UPDATE OF t1;
+ SELECT t1.a, t2.b FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.a = t2.b) WHERE t1.a % 25 = 0 ORDER BY 1,2 FOR UPDATE OF t1;
+ 
  RESET enable_partitionwise_join;
  
  
*** a/postgres-master/src/backend/nodes/outfuncs.c
--- b/postgres-master/src/backend/nodes/outfuncs.c
***************
*** 2357,2363 **** _outRelOptInfo(StringInfo str, const RelOptInfo *node)
--- 2357,2367 ----
  	WRITE_UINT_FIELD(baserestrict_min_security);
  	WRITE_NODE_FIELD(joininfo);
  	WRITE_BOOL_FIELD(has_eclass_joins);
+ 	WRITE_OID_FIELD(top_parent_rowtype);
  	WRITE_BITMAPSET_FIELD(top_parent_relids);
+ 	WRITE_BOOL_FIELD(top_parent_parted);
+ 	WRITE_BOOL_FIELD(top_parent_topmost);
+ 	WRITE_BOOL_FIELD(need_adjust_tlist);
  	WRITE_NODE_FIELD(partitioned_child_rels);
  }
  
*** a/postgres-master/src/backend/optimizer/path/allpaths.c
--- b/postgres-master/src/backend/optimizer/path/allpaths.c
***************
*** 909,914 **** set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
--- 909,917 ----
  		did_pruning = true;
  	}
  
+ 	if (rel->reloptkind == RELOPT_BASEREL)
+ 		rel->top_parent_topmost = bms_equal(rel->relids, root->all_baserels);
+ 
  	/*
  	 * Initialize to compute size estimates for whole append relation.
  	 *
***************
*** 956,1025 **** set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
  		childrel = find_base_rel(root, childRTindex);
  		Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
  
- 		if (rel->part_scheme)
- 		{
- 			AttrNumber	attno;
- 
- 			/*
- 			 * We need attr_needed data for building targetlist of a join
- 			 * relation representing join between matching partitions for
- 			 * partitionwise join. A given attribute of a child will be needed
- 			 * in the same highest joinrel where the corresponding attribute
- 			 * of parent is needed. Hence it suffices to use the same Relids
- 			 * set for parent and child.
- 			 */
- 			for (attno = rel->min_attr; attno <= rel->max_attr; attno++)
- 			{
- 				int			index = attno - rel->min_attr;
- 				Relids		attr_needed = rel->attr_needed[index];
- 
- 				/* System attributes do not need translation. */
- 				if (attno <= 0)
- 				{
- 					Assert(rel->min_attr == childrel->min_attr);
- 					childrel->attr_needed[index] = attr_needed;
- 				}
- 				else
- 				{
- 					Var		   *var = list_nth_node(Var,
- 													appinfo->translated_vars,
- 													attno - 1);
- 					int			child_index;
- 
- 					/*
- 					 * Ignore any column dropped from the parent.
- 					 * Corresponding Var won't have any translation. It won't
- 					 * have attr_needed information, since it can not be
- 					 * referenced in the query.
- 					 */
- 					if (var == NULL)
- 					{
- 						Assert(attr_needed == NULL);
- 						continue;
- 					}
- 
- 					child_index = var->varattno - childrel->min_attr;
- 					childrel->attr_needed[child_index] = attr_needed;
- 				}
- 			}
- 		}
- 
  		/*
! 		 * Copy/Modify targetlist. Even if this child is deemed empty, we need
! 		 * its targetlist in case it falls on nullable side in a child-join
! 		 * because of partitionwise join.
  		 *
! 		 * 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,
! 								   1, &appinfo);
  
  		/*
  		 * We have to make child entries in the EquivalenceClass data
--- 959,974 ----
  		childrel = find_base_rel(root, childRTindex);
  		Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
  
  		/*
! 		 * We have to copy the parent's targetlist to the child, with
! 		 * appropriate substitution of variables.
  		 *
! 		 * Note: when considering partitionwise joining, we need the child's
! 		 * targetlist in case it falls on nullable side in a child join even
! 		 * if the child is deemed empty.
  		 */
! 		childrel->top_parent_topmost = rel->top_parent_topmost;
! 		build_childrel_tlist(root, rel, childrel, 1, &appinfo);
  
  		/*
  		 * We have to make child entries in the EquivalenceClass data
***************
*** 1181,1186 **** set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
--- 1130,1143 ----
  								   1, &appinfo);
  
  		/*
+ 		 * Note: we could compute appropriate attr_needed data for the child's
+ 		 * variables, by transforming the parent's attr_needed through the
+ 		 * translated_vars mapping.  However, currently there's no need
+ 		 * because attr_needed is only examined for base relations not
+ 		 * otherrels.  So we just leave the child's attr_needed empty.
+ 		 */
+ 
+ 		/*
  		 * If parallelism is allowable for this query in general, see whether
  		 * it's allowable for this childrel in particular.  But if we've
  		 * already decided the appendrel is not parallel-safe as a whole,
*** a/postgres-master/src/backend/optimizer/plan/createplan.c
--- b/postgres-master/src/backend/optimizer/plan/createplan.c
***************
*** 29,34 ****
--- 29,35 ----
  #include "nodes/nodeFuncs.h"
  #include "optimizer/clauses.h"
  #include "optimizer/cost.h"
+ #include "optimizer/pathnode.h"
  #include "optimizer/paths.h"
  #include "optimizer/placeholder.h"
  #include "optimizer/plancat.h"
***************
*** 162,167 **** static void copy_generic_path_info(Plan *dest, Path *src);
--- 163,169 ----
  static void copy_plan_costsize(Plan *dest, Plan *src);
  static void label_sort_with_costsize(PlannerInfo *root, Sort *plan,
  						 double limit_tuples);
+ static void adjust_subplan_tlist(PlannerInfo *root, RelOptInfo *rel, Plan *subplan);
  static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
  static SampleScan *make_samplescan(List *qptlist, List *qpqual, Index scanrelid,
  				TableSampleClause *tsc);
***************
*** 1065,1070 **** create_append_plan(PlannerInfo *root, AppendPath *best_path)
--- 1067,1076 ----
  		/* Must insist that all children return the same tlist */
  		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
  
+ 		/* Adjust the plan's targetlist if necessary */
+ 		if (subpath->parent->need_adjust_tlist)
+ 			adjust_subplan_tlist(root, subpath->parent, subplan);
+ 
  		subplans = lappend(subplans, subplan);
  	}
  
***************
*** 1176,1181 **** create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
--- 1182,1191 ----
  		/* Must insist that all children return the same tlist */
  		subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
  
+ 		/* Adjust the plan's targetlist if necessary */
+ 		if (subpath->parent->need_adjust_tlist)
+ 			adjust_subplan_tlist(root, subpath->parent, subplan);
+ 
  		/* Compute sort column info, and adjust subplan's tlist as needed */
  		subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
  											 subpath->parent->relids,
***************
*** 4968,4973 **** bitmap_subplan_mark_shared(Plan *plan)
--- 4978,5045 ----
  		elog(ERROR, "unrecognized node type: %d", nodeTag(plan));
  }
  
+ /*
+  * adjust_subplan_tlist
+  *	  If the child plan's targetlist contains a whole-row Var as an entry of
+  *	  that list, add a ConvertRowtypeExpr above the Var to convert the tuple
+  *	  layout of the Var back to the top parent's rowtype.
+  */
+ static void
+ adjust_subplan_tlist(PlannerInfo *root, RelOptInfo *rel, Plan *subplan)
+ {
+ 	List	   *newtlist;
+ 	ListCell   *l;
+ 
+ 	/* The top parent appendrel should be partitioned */
+ 	Assert(rel->top_parent_parted);
+ 	/* But it should not be the topmost scan/join relation */
+ 	Assert(!rel->top_parent_topmost);
+ 
+ 	/* The given rel should represent a scan/join relation */
+ 	Assert(rel->reloptkind == RELOPT_OTHER_MEMBER_REL ||
+ 		   rel->reloptkind == RELOPT_OTHER_JOINREL);
+ 
+ 	/*
+ 	 * If the child plan is an Append or MergeAppend, the targetlists of its
+ 	 * subplans would have already been adjusted before we get here, so need
+ 	 * to work here.
+ 	 */
+ 	if (IsA(subplan, Append) || IsA(subplan, MergeAppend))
+ 		return;
+ 
+ 	/* The child plan should be a scan or join */
+ 	Assert(is_projection_capable_plan(subplan));
+ 
+ 	newtlist = NIL;
+ 	foreach(l, subplan->targetlist)
+ 	{
+ 		TargetEntry *tle = (TargetEntry *) lfirst(l);
+ 		Var		   *var = (Var *) tle->expr;
+ 
+ 		if (IsA(var, Var) && var->varattno == 0)
+ 		{
+ 			RelOptInfo *partrel = find_base_rel(root, var->varno);
+ 			ConvertRowtypeExpr *cre = makeNode(ConvertRowtypeExpr);
+ 
+ 			/* The rel should be a partition */
+ 			Assert(partrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
+ 
+ 			/* The top parent rel should be a partitioned table */
+ 			Assert(OidIsValid(partrel->top_parent_rowtype));
+ 
+ 			cre->arg = (Expr *) var;
+ 			cre->resulttype = partrel->top_parent_rowtype;
+ 			cre->convertformat = COERCE_IMPLICIT_CAST;
+ 			cre->location = -1;
+ 
+ 			tle = flatCopyTargetEntry(tle);
+ 			tle->expr = (Expr *) cre;
+ 		}
+ 		newtlist = lappend(newtlist, tle);
+ 	}
+ 	subplan->targetlist = newtlist;
+ }
+ 
  /*****************************************************************************
   *
   *	PLAN NODE BUILDING ROUTINES
*** a/postgres-master/src/backend/optimizer/plan/setrefs.c
--- b/postgres-master/src/backend/optimizer/plan/setrefs.c
***************
*** 41,49 **** typedef struct
  	int			num_vars;		/* number of plain Var tlist entries */
  	bool		has_ph_vars;	/* are there PlaceHolderVar entries? */
  	bool		has_non_vars;	/* are there other entries? */
- 	bool		has_conv_whole_rows;	/* are there ConvertRowtypeExpr
- 										 * entries encapsulating a whole-row
- 										 * Var? */
  	tlist_vinfo vars[FLEXIBLE_ARRAY_MEMBER];	/* has num_vars entries */
  } indexed_tlist;
  
--- 41,46 ----
***************
*** 143,149 **** static List *set_returning_clause_references(PlannerInfo *root,
  								int rtoffset);
  static bool extract_query_dependencies_walker(Node *node,
  								  PlannerInfo *context);
- static bool is_converted_whole_row_reference(Node *node);
  
  /*****************************************************************************
   *
--- 140,145 ----
***************
*** 1997,2003 **** build_tlist_index(List *tlist)
  	itlist->tlist = tlist;
  	itlist->has_ph_vars = false;
  	itlist->has_non_vars = false;
- 	itlist->has_conv_whole_rows = false;
  
  	/* Find the Vars and fill in the index array */
  	vinfo = itlist->vars;
--- 1993,1998 ----
***************
*** 2016,2023 **** build_tlist_index(List *tlist)
  		}
  		else if (tle->expr && IsA(tle->expr, PlaceHolderVar))
  			itlist->has_ph_vars = true;
- 		else if (is_converted_whole_row_reference((Node *) tle->expr))
- 			itlist->has_conv_whole_rows = true;
  		else
  			itlist->has_non_vars = true;
  	}
--- 2011,2016 ----
***************
*** 2033,2042 **** build_tlist_index(List *tlist)
   * This is like build_tlist_index, but we only index tlist entries that
   * are Vars belonging to some rel other than the one specified.  We will set
   * has_ph_vars (allowing PlaceHolderVars to be matched), but not has_non_vars
!  * (so nothing other than Vars and PlaceHolderVars can be matched). In case of
!  * DML, where this function will be used, returning lists from child relations
!  * will be appended similar to a simple append relation. That does not require
!  * fixing ConvertRowtypeExpr references. So, those are not considered here.
   */
  static indexed_tlist *
  build_tlist_index_other_vars(List *tlist, Index ignore_rel)
--- 2026,2032 ----
   * This is like build_tlist_index, but we only index tlist entries that
   * are Vars belonging to some rel other than the one specified.  We will set
   * has_ph_vars (allowing PlaceHolderVars to be matched), but not has_non_vars
!  * (so nothing other than Vars and PlaceHolderVars can be matched).
   */
  static indexed_tlist *
  build_tlist_index_other_vars(List *tlist, Index ignore_rel)
***************
*** 2053,2059 **** build_tlist_index_other_vars(List *tlist, Index ignore_rel)
  	itlist->tlist = tlist;
  	itlist->has_ph_vars = false;
  	itlist->has_non_vars = false;
- 	itlist->has_conv_whole_rows = false;
  
  	/* Find the desired Vars and fill in the index array */
  	vinfo = itlist->vars;
--- 2043,2048 ----
***************
*** 2256,2262 **** static Node *
  fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
  {
  	Var		   *newvar;
- 	bool		converted_whole_row;
  
  	if (node == NULL)
  		return NULL;
--- 2245,2250 ----
***************
*** 2326,2337 **** fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
  	}
  	if (IsA(node, Param))
  		return fix_param_node(context->root, (Param *) node);
- 
  	/* Try matching more complex expressions too, if tlists have any */
! 	converted_whole_row = is_converted_whole_row_reference(node);
! 	if (context->outer_itlist &&
! 		(context->outer_itlist->has_non_vars ||
! 		 (context->outer_itlist->has_conv_whole_rows && converted_whole_row)))
  	{
  		newvar = search_indexed_tlist_for_non_var((Expr *) node,
  												  context->outer_itlist,
--- 2314,2321 ----
  	}
  	if (IsA(node, Param))
  		return fix_param_node(context->root, (Param *) node);
  	/* Try matching more complex expressions too, if tlists have any */
! 	if (context->outer_itlist && context->outer_itlist->has_non_vars)
  	{
  		newvar = search_indexed_tlist_for_non_var((Expr *) node,
  												  context->outer_itlist,
***************
*** 2339,2347 **** fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
  		if (newvar)
  			return (Node *) newvar;
  	}
! 	if (context->inner_itlist &&
! 		(context->inner_itlist->has_non_vars ||
! 		 (context->inner_itlist->has_conv_whole_rows && converted_whole_row)))
  	{
  		newvar = search_indexed_tlist_for_non_var((Expr *) node,
  												  context->inner_itlist,
--- 2323,2329 ----
  		if (newvar)
  			return (Node *) newvar;
  	}
! 	if (context->inner_itlist && context->inner_itlist->has_non_vars)
  	{
  		newvar = search_indexed_tlist_for_non_var((Expr *) node,
  												  context->inner_itlist,
***************
*** 2461,2469 **** fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
  		/* If no match, just fall through to process it normally */
  	}
  	/* Try matching more complex expressions too, if tlist has any */
! 	if (context->subplan_itlist->has_non_vars ||
! 		(context->subplan_itlist->has_conv_whole_rows &&
! 		 is_converted_whole_row_reference(node)))
  	{
  		newvar = search_indexed_tlist_for_non_var((Expr *) node,
  												  context->subplan_itlist,
--- 2443,2449 ----
  		/* If no match, just fall through to process it normally */
  	}
  	/* Try matching more complex expressions too, if tlist has any */
! 	if (context->subplan_itlist->has_non_vars)
  	{
  		newvar = search_indexed_tlist_for_non_var((Expr *) node,
  												  context->subplan_itlist,
***************
*** 2670,2702 **** extract_query_dependencies_walker(Node *node, PlannerInfo *context)
  	return expression_tree_walker(node, extract_query_dependencies_walker,
  								  (void *) context);
  }
- 
- /*
-  * is_converted_whole_row_reference
-  *		If the given node is a ConvertRowtypeExpr encapsulating a whole-row
-  *		reference as implicit cast, return true. Otherwise return false.
-  */
- static bool
- is_converted_whole_row_reference(Node *node)
- {
- 	ConvertRowtypeExpr *convexpr;
- 
- 	if (!node || !IsA(node, ConvertRowtypeExpr))
- 		return false;
- 
- 	/* Traverse nested ConvertRowtypeExpr's. */
- 	convexpr = castNode(ConvertRowtypeExpr, node);
- 	while (convexpr->convertformat == COERCE_IMPLICIT_CAST &&
- 		   IsA(convexpr->arg, ConvertRowtypeExpr))
- 		convexpr = castNode(ConvertRowtypeExpr, convexpr->arg);
- 
- 	if (IsA(convexpr->arg, Var))
- 	{
- 		Var		   *var = castNode(Var, convexpr->arg);
- 
- 		if (var->varattno == 0)
- 			return true;
- 	}
- 
- 	return false;
- }
--- 2650,2652 ----
*** a/postgres-master/src/backend/optimizer/util/placeholder.c
--- b/postgres-master/src/backend/optimizer/util/placeholder.c
***************
*** 20,26 ****
  #include "optimizer/pathnode.h"
  #include "optimizer/placeholder.h"
  #include "optimizer/planmain.h"
- #include "optimizer/prep.h"
  #include "optimizer/var.h"
  #include "utils/lsyscache.h"
  
--- 20,25 ----
***************
*** 415,424 **** add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
  	Relids		relids = joinrel->relids;
  	ListCell   *lc;
  
- 	/* This function is called only on the parent relations. */
- 	Assert(!IS_OTHER_REL(joinrel) && !IS_OTHER_REL(outer_rel) &&
- 		   !IS_OTHER_REL(inner_rel));
- 
  	foreach(lc, root->placeholder_list)
  	{
  		PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
--- 414,419 ----
***************
*** 464,519 **** add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
  		}
  	}
  }
- 
- /*
-  * add_placeholders_to_child_joinrel
-  *		Translate the PHVs in parent's targetlist and add them to the child's
-  *		targetlist. Also adjust the cost
-  */
- void
- add_placeholders_to_child_joinrel(PlannerInfo *root, RelOptInfo *childrel,
- 								  RelOptInfo *parentrel)
- {
- 	ListCell   *lc;
- 	AppendRelInfo **appinfos;
- 	int			nappinfos;
- 
- 	Assert(IS_JOIN_REL(childrel) && IS_JOIN_REL(parentrel));
- 	Assert(IS_OTHER_REL(childrel));
- 
- 	/* Nothing to do if no PHVs. */
- 	if (root->placeholder_list == NIL)
- 		return;
- 
- 	appinfos = find_appinfos_by_relids(root, childrel->relids, &nappinfos);
- 	foreach(lc, parentrel->reltarget->exprs)
- 	{
- 		PlaceHolderVar *phv = lfirst(lc);
- 
- 		if (IsA(phv, PlaceHolderVar))
- 		{
- 			/*
- 			 * In case the placeholder Var refers to any of the parent
- 			 * relations, translate it to refer to the corresponding child.
- 			 */
- 			if (bms_overlap(phv->phrels, parentrel->relids) &&
- 				childrel->reloptkind == RELOPT_OTHER_JOINREL)
- 			{
- 				phv = (PlaceHolderVar *) adjust_appendrel_attrs(root,
- 																(Node *) phv,
- 																nappinfos,
- 																appinfos);
- 			}
- 
- 			childrel->reltarget->exprs = lappend(childrel->reltarget->exprs,
- 												 phv);
- 		}
- 	}
- 
- 	/* Adjust the cost and width of child targetlist. */
- 	childrel->reltarget->cost.startup = parentrel->reltarget->cost.startup;
- 	childrel->reltarget->cost.per_tuple = parentrel->reltarget->cost.per_tuple;
- 	childrel->reltarget->width = parentrel->reltarget->width;
- 
- 	pfree(appinfos);
- }
--- 459,461 ----
*** a/postgres-master/src/backend/optimizer/util/relnode.c
--- b/postgres-master/src/backend/optimizer/util/relnode.c
***************
*** 16,21 ****
--- 16,22 ----
  
  #include <limits.h>
  
+ #include "catalog/pg_class.h"
  #include "miscadmin.h"
  #include "optimizer/clauses.h"
  #include "optimizer/cost.h"
***************
*** 28,33 ****
--- 29,35 ----
  #include "optimizer/tlist.h"
  #include "partitioning/partbounds.h"
  #include "utils/hsearch.h"
+ #include "utils/lsyscache.h"
  
  
  typedef struct JoinHashEntry
***************
*** 57,62 **** static void add_join_rel(PlannerInfo *root, RelOptInfo *joinrel);
--- 59,69 ----
  static void build_joinrel_partition_info(RelOptInfo *joinrel,
  							 RelOptInfo *outer_rel, RelOptInfo *inner_rel,
  							 List *restrictlist, JoinType jointype);
+ static void build_child_join_reltarget(PlannerInfo *root,
+ 						   RelOptInfo *parentrel,
+ 						   RelOptInfo *childrel,
+ 						   int nappinfos,
+ 						   AppendRelInfo **appinfos);
  
  
  /*
***************
*** 188,193 **** build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
--- 195,205 ----
  	rel->baserestrict_min_security = UINT_MAX;
  	rel->joininfo = NIL;
  	rel->has_eclass_joins = false;
+ 	rel->top_parent_rowtype = InvalidOid;
+ 	rel->top_parent_relids = NULL;
+ 	rel->top_parent_parted = false;
+ 	rel->top_parent_topmost = false;
+ 	rel->need_adjust_tlist = false;
  	rel->part_scheme = NULL;
  	rel->nparts = 0;
  	rel->boundinfo = NULL;
***************
*** 201,207 **** build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
  	 * Pass top parent's relids down the inheritance hierarchy. If the parent
  	 * has top_parent_relids set, it's a direct or an indirect child of the
  	 * top parent indicated by top_parent_relids. By extension this child is
! 	 * also an indirect child of that parent.
  	 */
  	if (parent)
  	{
--- 213,221 ----
  	 * Pass top parent's relids down the inheritance hierarchy. If the parent
  	 * has top_parent_relids set, it's a direct or an indirect child of the
  	 * top parent indicated by top_parent_relids. By extension this child is
! 	 * also an indirect child of that parent.  Likeweise for the flag to
! 	 * indicate whether that parent is a partitioned table and the OID of that
! 	 * parent's rowtype.
  	 */
  	if (parent)
  	{
***************
*** 209,218 **** build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
--- 223,243 ----
  			rel->top_parent_relids = parent->top_parent_relids;
  		else
  			rel->top_parent_relids = bms_copy(parent->relids);
+ 
+ 		rel->top_parent_parted = parent->top_parent_parted;
+ 		rel->top_parent_rowtype = parent->top_parent_rowtype;
  	}
  	else
+ 	{
  		rel->top_parent_relids = NULL;
  
+ 		if (rte->relkind == RELKIND_PARTITIONED_TABLE)
+ 		{
+ 			rel->top_parent_parted = true;
+ 			rel->top_parent_rowtype = get_rel_type_id(rte->relid);
+ 		}
+ 	}
+ 
  	/* Check type of rtable entry */
  	switch (rte->rtekind)
  	{
***************
*** 602,608 **** build_join_rel(PlannerInfo *root,
--- 627,637 ----
  	joinrel->baserestrict_min_security = UINT_MAX;
  	joinrel->joininfo = NIL;
  	joinrel->has_eclass_joins = false;
+ 	joinrel->top_parent_rowtype = InvalidOid;
  	joinrel->top_parent_relids = NULL;
+ 	joinrel->top_parent_parted = false;
+ 	joinrel->top_parent_topmost = false;
+ 	joinrel->need_adjust_tlist = false;
  	joinrel->part_scheme = NULL;
  	joinrel->nparts = 0;
  	joinrel->boundinfo = NULL;
***************
*** 659,664 **** build_join_rel(PlannerInfo *root,
--- 688,699 ----
  	/* Store the partition information. */
  	build_joinrel_partition_info(joinrel, outer_rel, inner_rel, restrictlist,
  								 sjinfo->jointype);
+ 	if (IS_PARTITIONED_REL(joinrel))
+ 	{
+ 		joinrel->top_parent_parted = true;
+ 		joinrel->top_parent_topmost = bms_equal(joinrel->relids,
+ 												root->all_baserels);
+ 	}
  
  	/*
  	 * Set estimates of the joinrel's size.
***************
*** 773,779 **** build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
--- 808,818 ----
  	joinrel->baserestrictcost.per_tuple = 0;
  	joinrel->joininfo = NIL;
  	joinrel->has_eclass_joins = false;
+ 	joinrel->top_parent_rowtype = InvalidOid;
  	joinrel->top_parent_relids = NULL;
+ 	joinrel->top_parent_parted = parent_joinrel->top_parent_parted;
+ 	joinrel->top_parent_topmost = parent_joinrel->top_parent_topmost;
+ 	joinrel->need_adjust_tlist = false;
  	joinrel->part_scheme = NULL;
  	joinrel->nparts = 0;
  	joinrel->boundinfo = NULL;
***************
*** 785,802 **** build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
  
  	joinrel->top_parent_relids = bms_union(outer_rel->top_parent_relids,
  										   inner_rel->top_parent_relids);
  
  	/* Compute information relevant to foreign relations. */
  	set_foreign_rel_properties(joinrel, outer_rel, inner_rel);
  
! 	/* Build targetlist */
! 	build_joinrel_tlist(root, joinrel, outer_rel);
! 	build_joinrel_tlist(root, joinrel, inner_rel);
! 	/* Add placeholder variables. */
! 	add_placeholders_to_child_joinrel(root, joinrel, parent_joinrel);
  
  	/* Construct joininfo list. */
- 	appinfos = find_appinfos_by_relids(root, joinrel->relids, &nappinfos);
  	joinrel->joininfo = (List *) adjust_appendrel_attrs(root,
  														(Node *) parent_joinrel->joininfo,
  														nappinfos,
--- 824,841 ----
  
  	joinrel->top_parent_relids = bms_union(outer_rel->top_parent_relids,
  										   inner_rel->top_parent_relids);
+ 	Assert(joinrel->top_parent_parted);
  
  	/* Compute information relevant to foreign relations. */
  	set_foreign_rel_properties(joinrel, outer_rel, inner_rel);
  
! 	appinfos = find_appinfos_by_relids(root, joinrel->relids, &nappinfos);
! 
! 	/* Set up reltarget struct */
! 	build_child_join_reltarget(root, parent_joinrel, joinrel,
! 							   nappinfos, appinfos);
  
  	/* Construct joininfo list. */
  	joinrel->joininfo = (List *) adjust_appendrel_attrs(root,
  														(Node *) parent_joinrel->joininfo,
  														nappinfos,
***************
*** 895,909 **** static void
  build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
  					RelOptInfo *input_rel)
  {
! 	Relids		relids;
  	ListCell   *vars;
  
- 	/* attrs_needed refers to parent relids and not those of a child. */
- 	if (joinrel->top_parent_relids)
- 		relids = joinrel->top_parent_relids;
- 	else
- 		relids = joinrel->relids;
- 
  	foreach(vars, input_rel->reltarget->exprs)
  	{
  		Var		   *var = (Var *) lfirst(vars);
--- 934,942 ----
  build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
  					RelOptInfo *input_rel)
  {
! 	Relids		relids = joinrel->relids;
  	ListCell   *vars;
  
  	foreach(vars, input_rel->reltarget->exprs)
  	{
  		Var		   *var = (Var *) lfirst(vars);
***************
*** 919,972 **** build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
  
  		/*
  		 * Otherwise, anything in a baserel or joinrel targetlist ought to be
! 		 * a Var. Children of a partitioned table may have ConvertRowtypeExpr
! 		 * translating whole-row Var of a child to that of the parent.
! 		 * Children of an inherited table or subquery child rels can not
! 		 * directly participate in a join, so other kinds of nodes here.
  		 */
! 		if (IsA(var, Var))
! 		{
! 			baserel = find_base_rel(root, var->varno);
! 			ndx = var->varattno - baserel->min_attr;
! 		}
! 		else if (IsA(var, ConvertRowtypeExpr))
! 		{
! 			ConvertRowtypeExpr *child_expr = (ConvertRowtypeExpr *) var;
! 			Var		   *childvar = (Var *) child_expr->arg;
! 
! 			/*
! 			 * Child's whole-row references are converted to look like those
! 			 * of parent using ConvertRowtypeExpr. There can be as many
! 			 * ConvertRowtypeExpr decorations as the depth of partition tree.
! 			 * The argument to the deepest ConvertRowtypeExpr is expected to
! 			 * be a whole-row reference of the child.
! 			 */
! 			while (IsA(childvar, ConvertRowtypeExpr))
! 			{
! 				child_expr = (ConvertRowtypeExpr *) childvar;
! 				childvar = (Var *) child_expr->arg;
! 			}
! 			Assert(IsA(childvar, Var) &&childvar->varattno == 0);
! 
! 			baserel = find_base_rel(root, childvar->varno);
! 			ndx = 0 - baserel->min_attr;
! 		}
! 		else
  			elog(ERROR, "unexpected node type in rel targetlist: %d",
  				 (int) nodeTag(var));
  
  
! 		/* Is the target expression still needed above this joinrel? */
  		if (bms_nonempty_difference(baserel->attr_needed[ndx], relids))
  		{
  			/* Yup, add it to the output */
  			joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs, var);
! 
! 			/*
! 			 * Vars have cost zero, so no need to adjust reltarget->cost. Even
! 			 * if it's a ConvertRowtypeExpr, it will be computed only for the
! 			 * base relation, costing nothing for a join.
! 			 */
  			joinrel->reltarget->width += baserel->attr_widths[ndx];
  		}
  	}
--- 952,974 ----
  
  		/*
  		 * Otherwise, anything in a baserel or joinrel targetlist ought to be
! 		 * a Var.  (More general cases can only appear in appendrel child
! 		 * rels, which will never be seen here.)
  		 */
! 		if (!IsA(var, Var))
  			elog(ERROR, "unexpected node type in rel targetlist: %d",
  				 (int) nodeTag(var));
  
+ 		/* Get the Var's original base rel */
+ 		baserel = find_base_rel(root, var->varno);
  
! 		/* Is it still needed above this joinrel? */
! 		ndx = var->varattno - baserel->min_attr;
  		if (bms_nonempty_difference(baserel->attr_needed[ndx], relids))
  		{
  			/* Yup, add it to the output */
  			joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs, var);
! 			/* Vars have cost zero, so no need to adjust reltarget->cost */
  			joinrel->reltarget->width += baserel->attr_widths[ndx];
  		}
  	}
***************
*** 1215,1220 **** fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
--- 1217,1224 ----
  	upperrel->cheapest_unique_path = NULL;
  	upperrel->cheapest_parameterized_paths = NIL;
  
+ 	upperrel->need_adjust_tlist = false;
+ 
  	root->upper_rels[kind] = lappend(root->upper_rels[kind], upperrel);
  
  	return upperrel;
***************
*** 1768,1770 **** build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
--- 1772,1884 ----
  		joinrel->nullable_partexprs[cnt] = nullable_partexpr;
  	}
  }
+ 
+ /*
+  * build_childrel_tlist
+  *	  Build the child's targetlist from the parent's with
+  *	  adjust_appendrel_attrs
+  *
+  * When considering partitionwise joining, we need special handling: if the
+  * entry in the parent's targetlist is a whole-row Var for the parent rel,
+  * create a new whole-row Var for the child rel and add it to the child's
+  * targetlist.  This allows us to handle the child's targetlist in the same
+  * way as the parent's during planning.  The tuple layout of the child Var
+  * will be converted back to the parent's rowtype at create_plan time.
+  */
+ void
+ build_childrel_tlist(PlannerInfo *root,
+ 					 RelOptInfo *parentrel,
+ 					 RelOptInfo *childrel,
+ 					 int nappinfos,
+ 					 AppendRelInfo **appinfos)
+ {
+ 	ListCell   *lc;
+ 
+ 	/*
+ 	 * If enable_partitionwise_join=off, if the parent is not partitioned, or
+ 	 * if the parent is the topmost scan/join relation, we don't consider
+ 	 * partitionwise joining, so no need for special handling; just apply
+ 	 * adjust_appendrel_attrs to the parent's targetlist to get the child's.
+ 	 *
+ 	 * 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.
+ 	 */
+ 	if (!enable_partitionwise_join ||
+ 		!childrel->top_parent_parted ||
+ 		childrel->top_parent_topmost)
+ 	{
+ 		childrel->reltarget->exprs = (List *)
+ 			adjust_appendrel_attrs(root,
+ 								   (Node *) parentrel->reltarget->exprs,
+ 								   nappinfos, appinfos);
+ 		return;
+ 	}
+ 
+ 	foreach(lc, parentrel->reltarget->exprs)
+ 	{
+ 		Node	   *node = (Node *) lfirst(lc);
+ 
+ 		/* Should be a simple Var or PlaceHolderVar ... */
+ 		Assert(IsA(node, Var) || IsA(node, PlaceHolderVar));
+ 
+ 		if (IsA(node, Var) && ((Var *) node)->varattno == 0)
+ 		{
+ 			Var		   *var = (Var *) copyObject(node);
+ 			AppendRelInfo *appinfo = NULL;
+ 			int			cnt;
+ 
+ 			/* Find the AppendRelInfo associated with the parent */
+ 			for (cnt = 0; cnt < nappinfos; cnt++)
+ 			{
+ 				if (var->varno == appinfos[cnt]->parent_relid)
+ 				{
+ 					appinfo = appinfos[cnt];
+ 					break;
+ 				}
+ 			}
+ 
+ 			/* Should have found the entry ... */
+ 			Assert(appinfo);
+ 
+ 			var->varno = appinfo->child_relid;
+ 			var->varnoold = appinfo->child_relid;
+ 			var->vartype = appinfo->child_reltype;
+ 
+ 			childrel->reltarget->exprs =
+ 				lappend(childrel->reltarget->exprs, var);
+ 			childrel->need_adjust_tlist = true;
+ 		}
+ 		else
+ 		{
+ 			childrel->reltarget->exprs =
+ 				lappend(childrel->reltarget->exprs,
+ 						adjust_appendrel_attrs(root, node,
+ 											   nappinfos,
+ 											   appinfos));
+ 		}
+ 	}
+ }
+ 
+ /*
+  * build_child_join_reltarget
+  *	  Set up a child-join relation's reltarget from a parent-join relation.
+  */
+ static void
+ build_child_join_reltarget(PlannerInfo *root,
+ 						   RelOptInfo *parentrel,
+ 						   RelOptInfo *childrel,
+ 						   int nappinfos,
+ 						   AppendRelInfo **appinfos)
+ {
+ 	/* Build the targetlist */
+ 	build_childrel_tlist(root, parentrel, childrel, nappinfos, appinfos);
+ 
+ 	/* Set the cost and width fields */
+ 	childrel->reltarget->cost.startup = parentrel->reltarget->cost.startup;
+ 	childrel->reltarget->cost.per_tuple = parentrel->reltarget->cost.per_tuple;
+ 	childrel->reltarget->width = parentrel->reltarget->width;
+ }
*** a/postgres-master/src/include/nodes/relation.h
--- b/postgres-master/src/include/nodes/relation.h
***************
*** 687,694 **** typedef struct RelOptInfo
  								 * involving this rel */
  	bool		has_eclass_joins;	/* T means joininfo is incomplete */
  
! 	/* used by "other" relations */
! 	Relids		top_parent_relids;	/* Relids of topmost parents */
  
  	/* used for partitioned relations */
  	PartitionScheme part_scheme;	/* Partitioning scheme. */
--- 687,701 ----
  								 * involving this rel */
  	bool		has_eclass_joins;	/* T means joininfo is incomplete */
  
! 	/* used by partitionwise joins: */
! 	Oid			top_parent_rowtype; /* OID of topmost parent's rowtype */
! 	Relids		top_parent_relids;	/* Relids of topmost parent(s) */
! 	bool		top_parent_parted;	/* T means the topmost parent(s) rel is
! 									 * partitioned */
! 	bool		top_parent_topmost;	/* T means the topmost parent(s) rel is
! 									 * topmost scan/join one */
! 	bool		need_adjust_tlist;	/* T means reltarget->exprs has child
! 									 * whole-row Var(s) */
  
  	/* used for partitioned relations */
  	PartitionScheme part_scheme;	/* Partitioning scheme. */
*** a/postgres-master/src/include/optimizer/pathnode.h
--- b/postgres-master/src/include/optimizer/pathnode.h
***************
*** 297,301 **** extern RelOptInfo *build_child_join_rel(PlannerInfo *root,
--- 297,306 ----
  					 RelOptInfo *outer_rel, RelOptInfo *inner_rel,
  					 RelOptInfo *parent_joinrel, List *restrictlist,
  					 SpecialJoinInfo *sjinfo, JoinType jointype);
+ extern void build_childrel_tlist(PlannerInfo *root,
+ 					 RelOptInfo *parentrel,
+ 					 RelOptInfo *childrel,
+ 					 int nappinfos,
+ 					 AppendRelInfo **appinfos);
  
  #endif							/* PATHNODE_H */
*** a/postgres-master/src/test/regress/expected/partition_join.out
--- b/postgres-master/src/test/regress/expected/partition_join.out
***************
*** 476,481 **** SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL
--- 476,554 ----
   550 |     | 
  (12 rows)
  
+ -- Check whole-row-Var handling in situations where the partitioned rel is not
+ -- the topmost scan/join relation
+ CREATE TABLE nonprt (a int, x text default 'testing');
+ INSERT INTO nonprt (a) SELECT i FROM generate_series(0, 599) i WHERE i % 6 = 0;
+ ANALYZE nonprt;
+ EXPLAIN (COSTS OFF)
+ SELECT t1, t3 FROM prt1 t1, nonprt t3 WHERE t1.a = t3.a AND t1.b = 0 ORDER BY t1.a;
+                     QUERY PLAN                    
+ --------------------------------------------------
+  Sort
+    Sort Key: t1.a
+    ->  Hash Join
+          Hash Cond: (t3.a = t1.a)
+          ->  Seq Scan on nonprt t3
+          ->  Hash
+                ->  Append
+                      ->  Seq Scan on prt1_p1 t1
+                            Filter: (b = 0)
+                      ->  Seq Scan on prt1_p2 t1_1
+                            Filter: (b = 0)
+                      ->  Seq Scan on prt1_p3 t1_2
+                            Filter: (b = 0)
+ (13 rows)
+ 
+ SELECT t1, t3 FROM prt1 t1, nonprt t3 WHERE t1.a = t3.a AND t1.b = 0 ORDER BY t1.a;
+       t1      |      t3       
+ --------------+---------------
+  (0,0,0000)   | (0,testing)
+  (150,0,0150) | (150,testing)
+  (300,0,0300) | (300,testing)
+  (450,0,0450) | (450,testing)
+ (4 rows)
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT t1, t2, t3 FROM prt1 t1, prt2 t2, nonprt t3 WHERE t1.a = t2.b AND t1.a = t3.a AND t1.b = 0 ORDER BY t1.a;
+                           QUERY PLAN                          
+ --------------------------------------------------------------
+  Sort
+    Sort Key: t1.a
+    ->  Hash Join
+          Hash Cond: (t3.a = t1.a)
+          ->  Seq Scan on nonprt t3
+          ->  Hash
+                ->  Append
+                      ->  Hash Join
+                            Hash Cond: (t2.b = t1.a)
+                            ->  Seq Scan on prt2_p1 t2
+                            ->  Hash
+                                  ->  Seq Scan on prt1_p1 t1
+                                        Filter: (b = 0)
+                      ->  Hash Join
+                            Hash Cond: (t2_1.b = t1_1.a)
+                            ->  Seq Scan on prt2_p2 t2_1
+                            ->  Hash
+                                  ->  Seq Scan on prt1_p2 t1_1
+                                        Filter: (b = 0)
+                      ->  Hash Join
+                            Hash Cond: (t2_2.b = t1_2.a)
+                            ->  Seq Scan on prt2_p3 t2_2
+                            ->  Hash
+                                  ->  Seq Scan on prt1_p3 t1_2
+                                        Filter: (b = 0)
+ (25 rows)
+ 
+ SELECT t1, t2, t3 FROM prt1 t1, prt2 t2, nonprt t3 WHERE t1.a = t2.b AND t1.a = t3.a AND t1.b = 0 ORDER BY t1.a;
+       t1      |      t2      |      t3       
+ --------------+--------------+---------------
+  (0,0,0000)   | (0,0,0000)   | (0,testing)
+  (150,0,0150) | (0,150,0150) | (150,testing)
+  (300,0,0300) | (0,300,0300) | (300,testing)
+  (450,0,0450) | (0,450,0450) | (450,testing)
+ (4 rows)
+ 
  --
  -- partitioned by expression
  --
***************
*** 1042,1047 **** SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT *
--- 1115,1163 ----
   400 |    
  (9 rows)
  
+ -- merge join when expression with whole-row reference needs to be sorted
+ EXPLAIN (COSTS OFF)
+ SELECT t1.a, t2.b FROM prt1 t1, prt2 t2 WHERE t1::text = t2::text AND t1.a = t2.b ORDER BY t1.a;
+                                     QUERY PLAN                                     
+ -----------------------------------------------------------------------------------
+  Merge Append
+    Sort Key: t1.a
+    ->  Merge Join
+          Merge Cond: ((t1.a = t2.b) AND (((t1.*)::text) = ((t2.*)::text)))
+          ->  Sort
+                Sort Key: t1.a, ((t1.*)::text)
+                ->  Seq Scan on prt1_p1 t1
+          ->  Sort
+                Sort Key: t2.b, ((t2.*)::text)
+                ->  Seq Scan on prt2_p1 t2
+    ->  Merge Join
+          Merge Cond: ((t1_1.a = t2_1.b) AND (((t1_1.*)::text) = ((t2_1.*)::text)))
+          ->  Sort
+                Sort Key: t1_1.a, ((t1_1.*)::text)
+                ->  Seq Scan on prt1_p2 t1_1
+          ->  Sort
+                Sort Key: t2_1.b, ((t2_1.*)::text)
+                ->  Seq Scan on prt2_p2 t2_1
+    ->  Merge Join
+          Merge Cond: ((t1_2.a = t2_2.b) AND (((t1_2.*)::text) = ((t2_2.*)::text)))
+          ->  Sort
+                Sort Key: t1_2.a, ((t1_2.*)::text)
+                ->  Seq Scan on prt1_p3 t1_2
+          ->  Sort
+                Sort Key: t2_2.b, ((t2_2.*)::text)
+                ->  Seq Scan on prt2_p3 t2_2
+ (26 rows)
+ 
+ SELECT t1.a, t2.b FROM prt1 t1, prt2 t2 WHERE t1::text = t2::text AND t1.a = t2.b ORDER BY t1.a;
+  a  | b  
+ ----+----
+   0 |  0
+   6 |  6
+  12 | 12
+  18 | 18
+  24 | 24
+ (5 rows)
+ 
  RESET enable_hashjoin;
  RESET enable_nestloop;
  --
*** a/postgres-master/src/test/regress/sql/partition_join.sql
--- b/postgres-master/src/test/regress/sql/partition_join.sql
***************
*** 89,94 **** SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL
--- 89,108 ----
  			  (SELECT t2.a AS t2a, t3.a AS t3a, t2.b t2b, t2.c t2c, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss
  			  ON t1.c = ss.t2c WHERE (t1.b + coalesce(ss.t2b, 0)) = 0 ORDER BY t1.a;
  
+ -- Check whole-row-Var handling in situations where the partitioned rel is not
+ -- the topmost scan/join relation
+ CREATE TABLE nonprt (a int, x text default 'testing');
+ INSERT INTO nonprt (a) SELECT i FROM generate_series(0, 599) i WHERE i % 6 = 0;
+ ANALYZE nonprt;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT t1, t3 FROM prt1 t1, nonprt t3 WHERE t1.a = t3.a AND t1.b = 0 ORDER BY t1.a;
+ SELECT t1, t3 FROM prt1 t1, nonprt t3 WHERE t1.a = t3.a AND t1.b = 0 ORDER BY t1.a;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT t1, t2, t3 FROM prt1 t1, prt2 t2, nonprt t3 WHERE t1.a = t2.b AND t1.a = t3.a AND t1.b = 0 ORDER BY t1.a;
+ SELECT t1, t2, t3 FROM prt1 t1, prt2 t2, nonprt t3 WHERE t1.a = t2.b AND t1.a = t3.a AND t1.b = 0 ORDER BY t1.a;
+ 
  --
  -- partitioned by expression
  --
***************
*** 160,165 **** EXPLAIN (COSTS OFF)
--- 174,184 ----
  SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;
  SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;
  
+ -- merge join when expression with whole-row reference needs to be sorted
+ EXPLAIN (COSTS OFF)
+ SELECT t1.a, t2.b FROM prt1 t1, prt2 t2 WHERE t1::text = t2::text AND t1.a = t2.b ORDER BY t1.a;
+ SELECT t1.a, t2.b FROM prt1 t1, prt2 t2 WHERE t1::text = t2::text AND t1.a = t2.b ORDER BY t1.a;
+ 
  RESET enable_hashjoin;
  RESET enable_nestloop;
  
