diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index f13316b..cf12710 100644
*** a/contrib/file_fdw/file_fdw.c
--- b/contrib/file_fdw/file_fdw.c
*************** check_selective_binary_conversion(RelOpt
*** 806,812 ****
  	}
  
  	/* Collect all the attributes needed for joins or final output. */
! 	pull_varattnos((Node *) baserel->reltargetlist, baserel->relid,
  				   &attrs_used);
  
  	/* Add all the attributes used by restriction clauses. */
--- 806,812 ----
  	}
  
  	/* Collect all the attributes needed for joins or final output. */
! 	pull_varattnos((Node *) baserel->reltarget.exprs, baserel->relid,
  				   &attrs_used);
  
  	/* Add all the attributes used by restriction clauses. */
*************** estimate_size(PlannerInfo *root, RelOptI
*** 938,944 ****
  		 */
  		int			tuple_width;
  
! 		tuple_width = MAXALIGN(baserel->width) +
  			MAXALIGN(SizeofHeapTupleHeader);
  		ntuples = clamp_row_est((double) stat_buf.st_size /
  								(double) tuple_width);
--- 938,944 ----
  		 */
  		int			tuple_width;
  
! 		tuple_width = MAXALIGN(baserel->reltarget.width) +
  			MAXALIGN(SizeofHeapTupleHeader);
  		ntuples = clamp_row_est((double) stat_buf.st_size /
  								(double) tuple_width);
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index cb41886..ef8eab6 100644
*** a/contrib/postgres_fdw/deparse.c
--- b/contrib/postgres_fdw/deparse.c
*************** build_tlist_to_deparse(RelOptInfo *forei
*** 728,737 ****
  	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
  
  	/*
! 	 * We require columns specified in foreignrel->reltargetlist and those
  	 * required for evaluating the local conditions.
  	 */
! 	tlist = add_to_flat_tlist(tlist, foreignrel->reltargetlist);
  	tlist = add_to_flat_tlist(tlist,
  							  pull_var_clause((Node *) fpinfo->local_conds,
  											  PVC_REJECT_AGGREGATES,
--- 728,737 ----
  	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
  
  	/*
! 	 * We require columns specified in foreignrel->reltarget.exprs and those
  	 * required for evaluating the local conditions.
  	 */
! 	tlist = add_to_flat_tlist(tlist, foreignrel->reltarget.exprs);
  	tlist = add_to_flat_tlist(tlist,
  							  pull_var_clause((Node *) fpinfo->local_conds,
  											  PVC_REJECT_AGGREGATES,
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index ffe6388..465f43c 100644
*** a/contrib/postgres_fdw/postgres_fdw.c
--- b/contrib/postgres_fdw/postgres_fdw.c
*************** postgresGetForeignRelSize(PlannerInfo *r
*** 479,485 ****
  	 * columns used in them.  Doesn't seem worth detecting that case though.)
  	 */
  	fpinfo->attrs_used = NULL;
! 	pull_varattnos((Node *) baserel->reltargetlist, baserel->relid,
  				   &fpinfo->attrs_used);
  	foreach(lc, fpinfo->local_conds)
  	{
--- 479,485 ----
  	 * columns used in them.  Doesn't seem worth detecting that case though.)
  	 */
  	fpinfo->attrs_used = NULL;
! 	pull_varattnos((Node *) baserel->reltarget.exprs, baserel->relid,
  				   &fpinfo->attrs_used);
  	foreach(lc, fpinfo->local_conds)
  	{
*************** postgresGetForeignRelSize(PlannerInfo *r
*** 522,528 ****
  
  		/* Report estimated baserel size to planner. */
  		baserel->rows = fpinfo->rows;
! 		baserel->width = fpinfo->width;
  	}
  	else
  	{
--- 522,528 ----
  
  		/* Report estimated baserel size to planner. */
  		baserel->rows = fpinfo->rows;
! 		baserel->reltarget.width = fpinfo->width;
  	}
  	else
  	{
*************** postgresGetForeignRelSize(PlannerInfo *r
*** 539,545 ****
  		{
  			baserel->pages = 10;
  			baserel->tuples =
! 				(10 * BLCKSZ) / (baserel->width + MAXALIGN(SizeofHeapTupleHeader));
  		}
  
  		/* Estimate baserel size as best we can with local statistics. */
--- 539,546 ----
  		{
  			baserel->pages = 10;
  			baserel->tuples =
! 				(10 * BLCKSZ) / (baserel->reltarget.width +
! 								 MAXALIGN(SizeofHeapTupleHeader));
  		}
  
  		/* Estimate baserel size as best we can with local statistics. */
*************** estimate_path_cost_size(PlannerInfo *roo
*** 2176,2182 ****
  		 * between foreign relations.
  		 */
  		rows = foreignrel->rows;
! 		width = foreignrel->width;
  
  		/* Back into an estimate of the number of retrieved rows. */
  		retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
--- 2177,2183 ----
  		 * between foreign relations.
  		 */
  		rows = foreignrel->rows;
! 		width = foreignrel->reltarget.width;
  
  		/* Back into an estimate of the number of retrieved rows. */
  		retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
*************** postgresGetForeignJoinPaths(PlannerInfo 
*** 3646,3652 ****
  							&width, &startup_cost, &total_cost);
  	/* Now update this information in the joinrel */
  	joinrel->rows = rows;
! 	joinrel->width = width;
  	fpinfo->rows = rows;
  	fpinfo->width = width;
  	fpinfo->startup_cost = startup_cost;
--- 3647,3653 ----
  							&width, &startup_cost, &total_cost);
  	/* Now update this information in the joinrel */
  	joinrel->rows = rows;
! 	joinrel->reltarget.width = width;
  	fpinfo->rows = rows;
  	fpinfo->width = width;
  	fpinfo->startup_cost = startup_cost;
diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index ffbd1e3..c24ddfc 100644
*** a/doc/src/sgml/fdwhandler.sgml
--- b/doc/src/sgml/fdwhandler.sgml
*************** GetForeignServerByName(const char *name,
*** 1159,1165 ****
       it contains restriction quals (<literal>WHERE</> clauses) that should be
       used to filter the rows to be fetched.  (The FDW itself is not required
       to enforce these quals, as the core executor can check them instead.)
!      <literal>baserel-&gt;reltargetlist</> can be used to determine which
       columns need to be fetched; but note that it only lists columns that
       have to be emitted by the <structname>ForeignScan</> plan node, not
       columns that are used in qual evaluation but not output by the query.
--- 1159,1165 ----
       it contains restriction quals (<literal>WHERE</> clauses) that should be
       used to filter the rows to be fetched.  (The FDW itself is not required
       to enforce these quals, as the core executor can check them instead.)
!      <literal>baserel-&gt;reltarget.exprs</> can be used to determine which
       columns need to be fetched; but note that it only lists columns that
       have to be emitted by the <structname>ForeignScan</> plan node, not
       columns that are used in qual evaluation but not output by the query.
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 28d983c..85acce8 100644
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
*************** _outOnConflictExpr(StringInfo str, const
*** 1589,1594 ****
--- 1589,1595 ----
   *
   * Note we do NOT print the parent, else we'd be in infinite recursion.
   * We can print the parent's relids for identification purposes, though.
+  * We print the pathtarget only if it's not the default one for the rel.
   * We also do not print the whole of param_info, since it's printed by
   * _outRelOptInfo; it's sufficient and less cluttering to print just the
   * required outer relids.
*************** _outPathInfo(StringInfo str, const Path 
*** 1598,1607 ****
  {
  	WRITE_ENUM_FIELD(pathtype, NodeTag);
  	appendStringInfoString(str, " :parent_relids ");
! 	if (node->parent)
! 		_outBitmapset(str, node->parent->relids);
! 	else
! 		_outBitmapset(str, NULL);
  	appendStringInfoString(str, " :required_outer ");
  	if (node->param_info)
  		_outBitmapset(str, node->param_info->ppi_req_outer);
--- 1599,1612 ----
  {
  	WRITE_ENUM_FIELD(pathtype, NodeTag);
  	appendStringInfoString(str, " :parent_relids ");
! 	_outBitmapset(str, node->parent->relids);
! 	if (node->pathtarget != &(node->parent->reltarget))
! 	{
! 		WRITE_NODE_FIELD(pathtarget->exprs);
! 		WRITE_FLOAT_FIELD(pathtarget->cost.startup, "%.2f");
! 		WRITE_FLOAT_FIELD(pathtarget->cost.per_tuple, "%.2f");
! 		WRITE_INT_FIELD(pathtarget->width);
! 	}
  	appendStringInfoString(str, " :required_outer ");
  	if (node->param_info)
  		_outBitmapset(str, node->param_info->ppi_req_outer);
*************** _outRelOptInfo(StringInfo str, const Rel
*** 1901,1911 ****
  	WRITE_ENUM_FIELD(reloptkind, RelOptKind);
  	WRITE_BITMAPSET_FIELD(relids);
  	WRITE_FLOAT_FIELD(rows, "%.0f");
- 	WRITE_INT_FIELD(width);
  	WRITE_BOOL_FIELD(consider_startup);
  	WRITE_BOOL_FIELD(consider_param_startup);
  	WRITE_BOOL_FIELD(consider_parallel);
! 	WRITE_NODE_FIELD(reltargetlist);
  	WRITE_NODE_FIELD(pathlist);
  	WRITE_NODE_FIELD(ppilist);
  	WRITE_NODE_FIELD(partial_pathlist);
--- 1906,1918 ----
  	WRITE_ENUM_FIELD(reloptkind, RelOptKind);
  	WRITE_BITMAPSET_FIELD(relids);
  	WRITE_FLOAT_FIELD(rows, "%.0f");
  	WRITE_BOOL_FIELD(consider_startup);
  	WRITE_BOOL_FIELD(consider_param_startup);
  	WRITE_BOOL_FIELD(consider_parallel);
! 	WRITE_NODE_FIELD(reltarget.exprs);
! 	WRITE_FLOAT_FIELD(reltarget.cost.startup, "%.2f");
! 	WRITE_FLOAT_FIELD(reltarget.cost.per_tuple, "%.2f");
! 	WRITE_INT_FIELD(reltarget.width);
  	WRITE_NODE_FIELD(pathlist);
  	WRITE_NODE_FIELD(ppilist);
  	WRITE_NODE_FIELD(partial_pathlist);
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 380b15e..bcb668f 100644
*** a/src/backend/optimizer/path/allpaths.c
--- b/src/backend/optimizer/path/allpaths.c
*************** set_append_rel_size(PlannerInfo *root, R
*** 919,937 ****
  		/*
  		 * CE failed, so finish copying/modifying targetlist and join quals.
  		 *
! 		 * Note: the resulting childrel->reltargetlist may contain arbitrary
! 		 * expressions, which otherwise would not occur in a reltargetlist.
  		 * Code that might be looking at an appendrel child must cope with
! 		 * such.  (Normally, a reltargetlist would only include Vars and
! 		 * PlaceHolderVars.)
  		 */
  		childrel->joininfo = (List *)
  			adjust_appendrel_attrs(root,
  								   (Node *) rel->joininfo,
  								   appinfo);
! 		childrel->reltargetlist = (List *)
  			adjust_appendrel_attrs(root,
! 								   (Node *) rel->reltargetlist,
  								   appinfo);
  
  		/*
--- 919,938 ----
  		/*
  		 * CE failed, so finish copying/modifying targetlist and join quals.
  		 *
! 		 * Note: 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->joininfo = (List *)
  			adjust_appendrel_attrs(root,
  								   (Node *) rel->joininfo,
  								   appinfo);
! 		childrel->reltarget.exprs = (List *)
  			adjust_appendrel_attrs(root,
! 								   (Node *) rel->reltarget.exprs,
  								   appinfo);
  
  		/*
*************** set_append_rel_size(PlannerInfo *root, R
*** 976,982 ****
  		Assert(childrel->rows > 0);
  
  		parent_rows += childrel->rows;
! 		parent_size += childrel->width * childrel->rows;
  
  		/*
  		 * Accumulate per-column estimates too.  We need not do anything for
--- 977,983 ----
  		Assert(childrel->rows > 0);
  
  		parent_rows += childrel->rows;
! 		parent_size += childrel->reltarget.width * childrel->rows;
  
  		/*
  		 * Accumulate per-column estimates too.  We need not do anything for
*************** set_append_rel_size(PlannerInfo *root, R
*** 984,993 ****
  		 * Var, or we didn't record a width estimate for it, we have to fall
  		 * back on a datatype-based estimate.
  		 *
! 		 * By construction, child's reltargetlist is 1-to-1 with parent's.
  		 */
! 		forboth(parentvars, rel->reltargetlist,
! 				childvars, childrel->reltargetlist)
  		{
  			Var		   *parentvar = (Var *) lfirst(parentvars);
  			Node	   *childvar = (Node *) lfirst(childvars);
--- 985,994 ----
  		 * Var, or we didn't record a width estimate for it, we have to fall
  		 * back on a datatype-based estimate.
  		 *
! 		 * By construction, child's targetlist is 1-to-1 with parent's.
  		 */
! 		forboth(parentvars, rel->reltarget.exprs,
! 				childvars, childrel->reltarget.exprs)
  		{
  			Var		   *parentvar = (Var *) lfirst(parentvars);
  			Node	   *childvar = (Node *) lfirst(childvars);
*************** set_append_rel_size(PlannerInfo *root, R
*** 1022,1028 ****
  
  		Assert(parent_rows > 0);
  		rel->rows = parent_rows;
! 		rel->width = rint(parent_size / parent_rows);
  		for (i = 0; i < nattrs; i++)
  			rel->attr_widths[i] = rint(parent_attrsizes[i] / parent_rows);
  
--- 1023,1029 ----
  
  		Assert(parent_rows > 0);
  		rel->rows = parent_rows;
! 		rel->reltarget.width = rint(parent_size / parent_rows);
  		for (i = 0; i < nattrs; i++)
  			rel->attr_widths[i] = rint(parent_attrsizes[i] / parent_rows);
  
*************** set_dummy_rel_pathlist(RelOptInfo *rel)
*** 1495,1501 ****
  {
  	/* Set dummy size estimates --- we leave attr_widths[] as zeroes */
  	rel->rows = 0;
! 	rel->width = 0;
  
  	/* Discard any pre-existing paths; no further need for them */
  	rel->pathlist = NIL;
--- 1496,1502 ----
  {
  	/* Set dummy size estimates --- we leave attr_widths[] as zeroes */
  	rel->rows = 0;
! 	rel->reltarget.width = 0;
  
  	/* Discard any pre-existing paths; no further need for them */
  	rel->pathlist = NIL;
*************** set_function_pathlist(PlannerInfo *root,
*** 1728,1738 ****
  		ListCell   *lc;
  
  		/*
! 		 * Is there a Var for it in reltargetlist?	If not, the query did not
! 		 * reference the ordinality column, or at least not in any way that
! 		 * would be interesting for sorting.
  		 */
! 		foreach(lc, rel->reltargetlist)
  		{
  			Var		   *node = (Var *) lfirst(lc);
  
--- 1729,1739 ----
  		ListCell   *lc;
  
  		/*
! 		 * Is there a Var for it in rel's targetlist?  If not, the query did
! 		 * not reference the ordinality column, or at least not in any way
! 		 * that would be interesting for sorting.
  		 */
! 		foreach(lc, rel->reltarget.exprs)
  		{
  			Var		   *node = (Var *) lfirst(lc);
  
*************** remove_unused_subquery_outputs(Query *su
*** 2676,2686 ****
  	 * query.
  	 *
  	 * Add all the attributes needed for joins or final output.  Note: we must
! 	 * look at reltargetlist, not the attr_needed data, because attr_needed
  	 * isn't computed for inheritance child rels, cf set_append_rel_size().
  	 * (XXX might be worth changing that sometime.)
  	 */
! 	pull_varattnos((Node *) rel->reltargetlist, rel->relid, &attrs_used);
  
  	/* Add all the attributes used by un-pushed-down restriction clauses. */
  	foreach(lc, rel->baserestrictinfo)
--- 2677,2687 ----
  	 * query.
  	 *
  	 * Add all the attributes needed for joins or final output.  Note: we must
! 	 * look at rel's targetlist, not the attr_needed data, because attr_needed
  	 * isn't computed for inheritance child rels, cf set_append_rel_size().
  	 * (XXX might be worth changing that sometime.)
  	 */
! 	pull_varattnos((Node *) rel->reltarget.exprs, rel->relid, &attrs_used);
  
  	/* Add all the attributes used by un-pushed-down restriction clauses. */
  	foreach(lc, rel->baserestrictinfo)
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 5fc80e7..5fc2f9c 100644
*** a/src/backend/optimizer/path/costsize.c
--- b/src/backend/optimizer/path/costsize.c
*************** clamp_row_est(double nrows)
*** 182,189 ****
   *
   * 'baserel' is the relation to be scanned
   * 'param_info' is the ParamPathInfo if this is a parameterized path, else NULL
-  * 'nworkers' are the number of workers among which the work will be
-  *			distributed if the scan is parallel scan
   */
  void
  cost_seqscan(Path *path, PlannerInfo *root,
--- 182,187 ----
*************** cost_seqscan(Path *path, PlannerInfo *ro
*** 225,230 ****
--- 223,231 ----
  	startup_cost += qpqual_cost.startup;
  	cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
  	cpu_run_cost = cpu_per_tuple * baserel->tuples;
+ 	/* tlist eval costs are paid per output row, not per tuple scanned */
+ 	startup_cost += path->pathtarget->cost.startup;
+ 	cpu_run_cost += path->pathtarget->cost.per_tuple * path->rows;
  
  	/* Adjust costing for parallelism, if used. */
  	if (path->parallel_degree > 0)
*************** cost_samplescan(Path *path, PlannerInfo 
*** 335,340 ****
--- 336,344 ----
  	startup_cost += qpqual_cost.startup;
  	cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
  	run_cost += cpu_per_tuple * baserel->tuples;
+ 	/* tlist eval costs are paid per output row, not per tuple scanned */
+ 	startup_cost += path->pathtarget->cost.startup;
+ 	run_cost += path->pathtarget->cost.per_tuple * path->rows;
  
  	path->startup_cost = startup_cost;
  	path->total_cost = startup_cost + run_cost;
*************** cost_index(IndexPath *path, PlannerInfo 
*** 601,606 ****
--- 605,614 ----
  
  	run_cost += cpu_per_tuple * tuples_fetched;
  
+ 	/* tlist eval costs are paid per output row, not per tuple scanned */
+ 	startup_cost += path->path.pathtarget->cost.startup;
+ 	run_cost += path->path.pathtarget->cost.per_tuple * path->path.rows;
+ 
  	path->path.startup_cost = startup_cost;
  	path->path.total_cost = startup_cost + run_cost;
  }
*************** cost_bitmap_heap_scan(Path *path, Planne
*** 910,915 ****
--- 918,927 ----
  
  	run_cost += cpu_per_tuple * tuples_fetched;
  
+ 	/* tlist eval costs are paid per output row, not per tuple scanned */
+ 	startup_cost += path->pathtarget->cost.startup;
+ 	run_cost += path->pathtarget->cost.per_tuple * path->rows;
+ 
  	path->startup_cost = startup_cost;
  	path->total_cost = startup_cost + run_cost;
  }
*************** cost_tidscan(Path *path, PlannerInfo *ro
*** 1141,1146 ****
--- 1153,1162 ----
  		tid_qual_cost.per_tuple;
  	run_cost += cpu_per_tuple * ntuples;
  
+ 	/* tlist eval costs are paid per output row, not per tuple scanned */
+ 	startup_cost += path->pathtarget->cost.startup;
+ 	run_cost += path->pathtarget->cost.per_tuple * path->rows;
+ 
  	path->startup_cost = startup_cost;
  	path->total_cost = startup_cost + run_cost;
  }
*************** cost_subqueryscan(Path *path, PlannerInf
*** 1185,1190 ****
--- 1201,1210 ----
  	cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
  	run_cost = cpu_per_tuple * baserel->tuples;
  
+ 	/* tlist eval costs are paid per output row, not per tuple scanned */
+ 	startup_cost += path->pathtarget->cost.startup;
+ 	run_cost += path->pathtarget->cost.per_tuple * path->rows;
+ 
  	path->startup_cost += startup_cost;
  	path->total_cost += startup_cost + run_cost;
  }
*************** cost_functionscan(Path *path, PlannerInf
*** 1242,1247 ****
--- 1262,1271 ----
  	cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
  	run_cost += cpu_per_tuple * baserel->tuples;
  
+ 	/* tlist eval costs are paid per output row, not per tuple scanned */
+ 	startup_cost += path->pathtarget->cost.startup;
+ 	run_cost += path->pathtarget->cost.per_tuple * path->rows;
+ 
  	path->startup_cost = startup_cost;
  	path->total_cost = startup_cost + run_cost;
  }
*************** cost_valuesscan(Path *path, PlannerInfo 
*** 1285,1290 ****
--- 1309,1318 ----
  	cpu_per_tuple += cpu_tuple_cost + qpqual_cost.per_tuple;
  	run_cost += cpu_per_tuple * baserel->tuples;
  
+ 	/* tlist eval costs are paid per output row, not per tuple scanned */
+ 	startup_cost += path->pathtarget->cost.startup;
+ 	run_cost += path->pathtarget->cost.per_tuple * path->rows;
+ 
  	path->startup_cost = startup_cost;
  	path->total_cost = startup_cost + run_cost;
  }
*************** cost_ctescan(Path *path, PlannerInfo *ro
*** 1328,1333 ****
--- 1356,1365 ----
  	cpu_per_tuple += cpu_tuple_cost + qpqual_cost.per_tuple;
  	run_cost += cpu_per_tuple * baserel->tuples;
  
+ 	/* tlist eval costs are paid per output row, not per tuple scanned */
+ 	startup_cost += path->pathtarget->cost.startup;
+ 	run_cost += path->pathtarget->cost.per_tuple * path->rows;
+ 
  	path->startup_cost = startup_cost;
  	path->total_cost = startup_cost + run_cost;
  }
*************** final_cost_nestloop(PlannerInfo *root, N
*** 2080,2085 ****
--- 2112,2121 ----
  	cpu_per_tuple = cpu_tuple_cost + restrict_qual_cost.per_tuple;
  	run_cost += cpu_per_tuple * ntuples;
  
+ 	/* tlist eval costs are paid per output row, not per tuple scanned */
+ 	startup_cost += path->path.pathtarget->cost.startup;
+ 	run_cost += path->path.pathtarget->cost.per_tuple * path->path.rows;
+ 
  	path->path.startup_cost = startup_cost;
  	path->path.total_cost = startup_cost + run_cost;
  }
*************** initial_cost_mergejoin(PlannerInfo *root
*** 2250,2256 ****
  				  outersortkeys,
  				  outer_path->total_cost,
  				  outer_path_rows,
! 				  outer_path->parent->width,
  				  0.0,
  				  work_mem,
  				  -1.0);
--- 2286,2292 ----
  				  outersortkeys,
  				  outer_path->total_cost,
  				  outer_path_rows,
! 				  outer_path->pathtarget->width,
  				  0.0,
  				  work_mem,
  				  -1.0);
*************** initial_cost_mergejoin(PlannerInfo *root
*** 2276,2282 ****
  				  innersortkeys,
  				  inner_path->total_cost,
  				  inner_path_rows,
! 				  inner_path->parent->width,
  				  0.0,
  				  work_mem,
  				  -1.0);
--- 2312,2318 ----
  				  innersortkeys,
  				  inner_path->total_cost,
  				  inner_path_rows,
! 				  inner_path->pathtarget->width,
  				  0.0,
  				  work_mem,
  				  -1.0);
*************** final_cost_mergejoin(PlannerInfo *root, 
*** 2500,2506 ****
  	 * off.
  	 */
  	else if (enable_material && innersortkeys != NIL &&
! 			 relation_byte_size(inner_path_rows, inner_path->parent->width) >
  			 (work_mem * 1024L))
  		path->materialize_inner = true;
  	else
--- 2536,2543 ----
  	 * off.
  	 */
  	else if (enable_material && innersortkeys != NIL &&
! 			 relation_byte_size(inner_path_rows,
! 								inner_path->pathtarget->width) >
  			 (work_mem * 1024L))
  		path->materialize_inner = true;
  	else
*************** final_cost_mergejoin(PlannerInfo *root, 
*** 2539,2544 ****
--- 2576,2585 ----
  	cpu_per_tuple = cpu_tuple_cost + qp_qual_cost.per_tuple;
  	run_cost += cpu_per_tuple * mergejointuples;
  
+ 	/* tlist eval costs are paid per output row, not per tuple scanned */
+ 	startup_cost += path->jpath.path.pathtarget->cost.startup;
+ 	run_cost += path->jpath.path.pathtarget->cost.per_tuple * path->jpath.path.rows;
+ 
  	path->jpath.path.startup_cost = startup_cost;
  	path->jpath.path.total_cost = startup_cost + run_cost;
  }
*************** initial_cost_hashjoin(PlannerInfo *root,
*** 2671,2677 ****
  	 * optimization in the cost estimate, but for now, we don't.
  	 */
  	ExecChooseHashTableSize(inner_path_rows,
! 							inner_path->parent->width,
  							true,		/* useskew */
  							&numbuckets,
  							&numbatches,
--- 2712,2718 ----
  	 * optimization in the cost estimate, but for now, we don't.
  	 */
  	ExecChooseHashTableSize(inner_path_rows,
! 							inner_path->pathtarget->width,
  							true,		/* useskew */
  							&numbuckets,
  							&numbatches,
*************** initial_cost_hashjoin(PlannerInfo *root,
*** 2687,2695 ****
  	if (numbatches > 1)
  	{
  		double		outerpages = page_size(outer_path_rows,
! 										   outer_path->parent->width);
  		double		innerpages = page_size(inner_path_rows,
! 										   inner_path->parent->width);
  
  		startup_cost += seq_page_cost * innerpages;
  		run_cost += seq_page_cost * (innerpages + 2 * outerpages);
--- 2728,2736 ----
  	if (numbatches > 1)
  	{
  		double		outerpages = page_size(outer_path_rows,
! 										   outer_path->pathtarget->width);
  		double		innerpages = page_size(inner_path_rows,
! 										   inner_path->pathtarget->width);
  
  		startup_cost += seq_page_cost * innerpages;
  		run_cost += seq_page_cost * (innerpages + 2 * outerpages);
*************** final_cost_hashjoin(PlannerInfo *root, H
*** 2919,2924 ****
--- 2960,2969 ----
  	cpu_per_tuple = cpu_tuple_cost + qp_qual_cost.per_tuple;
  	run_cost += cpu_per_tuple * hashjointuples;
  
+ 	/* tlist eval costs are paid per output row, not per tuple scanned */
+ 	startup_cost += path->jpath.path.pathtarget->cost.startup;
+ 	run_cost += path->jpath.path.pathtarget->cost.per_tuple * path->jpath.path.rows;
+ 
  	path->jpath.path.startup_cost = startup_cost;
  	path->jpath.path.total_cost = startup_cost + run_cost;
  }
*************** cost_rescan(PlannerInfo *root, Path *pat
*** 3063,3069 ****
  				 */
  				Cost		run_cost = cpu_tuple_cost * path->rows;
  				double		nbytes = relation_byte_size(path->rows,
! 														path->parent->width);
  				long		work_mem_bytes = work_mem * 1024L;
  
  				if (nbytes > work_mem_bytes)
--- 3108,3114 ----
  				 */
  				Cost		run_cost = cpu_tuple_cost * path->rows;
  				double		nbytes = relation_byte_size(path->rows,
! 													path->pathtarget->width);
  				long		work_mem_bytes = work_mem * 1024L;
  
  				if (nbytes > work_mem_bytes)
*************** cost_rescan(PlannerInfo *root, Path *pat
*** 3090,3096 ****
  				 */
  				Cost		run_cost = cpu_operator_cost * path->rows;
  				double		nbytes = relation_byte_size(path->rows,
! 														path->parent->width);
  				long		work_mem_bytes = work_mem * 1024L;
  
  				if (nbytes > work_mem_bytes)
--- 3135,3141 ----
  				 */
  				Cost		run_cost = cpu_operator_cost * path->rows;
  				double		nbytes = relation_byte_size(path->rows,
! 													path->pathtarget->width);
  				long		work_mem_bytes = work_mem * 1024L;
  
  				if (nbytes > work_mem_bytes)
*************** cost_qual_eval_walker(Node *node, cost_q
*** 3356,3361 ****
--- 3401,3420 ----
  		return cost_qual_eval_walker((Node *) linitial(asplan->subplans),
  									 context);
  	}
+ 	else if (IsA(node, PlaceHolderVar))
+ 	{
+ 		/*
+ 		 * A PlaceHolderVar should be given cost zero when considering general
+ 		 * expression evaluation costs.  The expense of doing the contained
+ 		 * expression is charged as part of the tlist eval costs of the scan
+ 		 * or join where the PHV is first computed (see set_rel_width and
+ 		 * add_placeholders_to_joinrel).  If we charged it again here, we'd be
+ 		 * double-counting the cost for each level of plan that the PHV
+ 		 * bubbles up through.  Hence, return without recursing into the
+ 		 * phexpr.
+ 		 */
+ 		return false;
+ 	}
  
  	/* recurse into children */
  	return expression_tree_walker(node, cost_qual_eval_walker,
*************** get_parameterized_baserel_size(PlannerIn
*** 3751,3757 ****
   * anyway we must keep the rowcount estimate the same for all paths for the
   * joinrel.)
   *
!  * We set only the rows field here.  The width field was already set by
   * build_joinrel_tlist, and baserestrictcost is not used for join rels.
   */
  void
--- 3810,3816 ----
   * anyway we must keep the rowcount estimate the same for all paths for the
   * joinrel.)
   *
!  * We set only the rows field here.  The reltarget field was already set by
   * build_joinrel_tlist, and baserestrictcost is not used for join rels.
   */
  void
*************** set_foreign_size_estimates(PlannerInfo *
*** 4156,4161 ****
--- 4215,4222 ----
   * that have to be calculated at this relation.  This is the amount of data
   * we'd need to pass upwards in case of a sort, hash, etc.
   *
+  * This function also sets reltarget.cost, so it's a bit misnamed now.
+  *
   * NB: this works best on plain relations because it prefers to look at
   * real Vars.  For subqueries, set_subquery_size_estimates will already have
   * copied up whatever per-column estimates were made within the subquery,
*************** set_rel_width(PlannerInfo *root, RelOptI
*** 4174,4185 ****
  	bool		have_wholerow_var = false;
  	ListCell   *lc;
  
! 	foreach(lc, rel->reltargetlist)
  	{
  		Node	   *node = (Node *) lfirst(lc);
  
  		/*
! 		 * Ordinarily, a Var in a rel's reltargetlist must belong to that rel;
  		 * but there are corner cases involving LATERAL references where that
  		 * isn't so.  If the Var has the wrong varno, fall through to the
  		 * generic case (it doesn't seem worth the trouble to be any smarter).
--- 4235,4250 ----
  	bool		have_wholerow_var = false;
  	ListCell   *lc;
  
! 	/* Vars are assumed to have cost zero, but other exprs do not */
! 	rel->reltarget.cost.startup = 0;
! 	rel->reltarget.cost.per_tuple = 0;
! 
! 	foreach(lc, rel->reltarget.exprs)
  	{
  		Node	   *node = (Node *) lfirst(lc);
  
  		/*
! 		 * Ordinarily, a Var in a rel's targetlist must belong to that rel;
  		 * but there are corner cases involving LATERAL references where that
  		 * isn't so.  If the Var has the wrong varno, fall through to the
  		 * generic case (it doesn't seem worth the trouble to be any smarter).
*************** set_rel_width(PlannerInfo *root, RelOptI
*** 4239,4248 ****
--- 4304,4321 ----
  		}
  		else if (IsA(node, PlaceHolderVar))
  		{
+ 			/*
+ 			 * We will need to evaluate the PHV's contained expression while
+ 			 * scanning this rel, so be sure to include it in reltarget.cost.
+ 			 */
  			PlaceHolderVar *phv = (PlaceHolderVar *) node;
  			PlaceHolderInfo *phinfo = find_placeholder_info(root, phv, false);
+ 			QualCost	cost;
  
  			tuple_width += phinfo->ph_width;
+ 			cost_qual_eval_node(&cost, (Node *) phv->phexpr, root);
+ 			rel->reltarget.cost.startup += cost.startup;
+ 			rel->reltarget.cost.per_tuple += cost.per_tuple;
  		}
  		else
  		{
*************** set_rel_width(PlannerInfo *root, RelOptI
*** 4252,4261 ****
--- 4325,4339 ----
  			 * can using the expression type information.
  			 */
  			int32		item_width;
+ 			QualCost	cost;
  
  			item_width = get_typavgwidth(exprType(node), exprTypmod(node));
  			Assert(item_width > 0);
  			tuple_width += item_width;
+ 			/* Not entirely clear if we need to account for cost, but do so */
+ 			cost_qual_eval_node(&cost, node, root);
+ 			rel->reltarget.cost.startup += cost.startup;
+ 			rel->reltarget.cost.per_tuple += cost.per_tuple;
  		}
  	}
  
*************** set_rel_width(PlannerInfo *root, RelOptI
*** 4292,4298 ****
  	}
  
  	Assert(tuple_width >= 0);
! 	rel->width = tuple_width;
  }
  
  /*
--- 4370,4376 ----
  	}
  
  	Assert(tuple_width >= 0);
! 	rel->reltarget.width = tuple_width;
  }
  
  /*
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index de49c12..ddb4ca5 100644
*** a/src/backend/optimizer/path/indxpath.c
--- b/src/backend/optimizer/path/indxpath.c
*************** bitmap_scan_cost_est(PlannerInfo *root, 
*** 1550,1555 ****
--- 1550,1556 ----
  	bpath.path.type = T_BitmapHeapPath;
  	bpath.path.pathtype = T_BitmapHeapScan;
  	bpath.path.parent = rel;
+ 	bpath.path.pathtarget = &(rel->reltarget);
  	bpath.path.param_info = get_baserel_parampathinfo(root, rel,
  													  required_outer);
  	bpath.path.pathkeys = NIL;
*************** bitmap_and_cost_est(PlannerInfo *root, R
*** 1578,1583 ****
--- 1579,1585 ----
  	apath.path.type = T_BitmapAndPath;
  	apath.path.pathtype = T_BitmapAnd;
  	apath.path.parent = rel;
+ 	apath.path.pathtarget = &(rel->reltarget);
  	apath.path.param_info = NULL;		/* not used in bitmap trees */
  	apath.path.pathkeys = NIL;
  	apath.bitmapquals = paths;
*************** bitmap_and_cost_est(PlannerInfo *root, R
*** 1590,1595 ****
--- 1592,1598 ----
  	bpath.path.type = T_BitmapHeapPath;
  	bpath.path.pathtype = T_BitmapHeapScan;
  	bpath.path.parent = rel;
+ 	bpath.path.pathtarget = &(rel->reltarget);
  	bpath.path.param_info = get_baserel_parampathinfo(root, rel,
  													  required_outer);
  	bpath.path.pathkeys = NIL;
*************** check_index_only(RelOptInfo *rel, IndexO
*** 1809,1818 ****
  
  	/*
  	 * Add all the attributes needed for joins or final output.  Note: we must
! 	 * look at reltargetlist, not the attr_needed data, because attr_needed
  	 * isn't computed for inheritance child rels.
  	 */
! 	pull_varattnos((Node *) rel->reltargetlist, rel->relid, &attrs_used);
  
  	/* Add all the attributes used by restriction clauses. */
  	foreach(lc, rel->baserestrictinfo)
--- 1812,1821 ----
  
  	/*
  	 * Add all the attributes needed for joins or final output.  Note: we must
! 	 * look at rel's targetlist, not the attr_needed data, because attr_needed
  	 * isn't computed for inheritance child rels.
  	 */
! 	pull_varattnos((Node *) rel->reltarget.exprs, rel->relid, &attrs_used);
  
  	/* Add all the attributes used by restriction clauses. */
  	foreach(lc, rel->baserestrictinfo)
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 6e0db08..198b06b 100644
*** a/src/backend/optimizer/plan/createplan.c
--- b/src/backend/optimizer/plan/createplan.c
*************** build_path_tlist(PlannerInfo *root, Path
*** 476,482 ****
  	int			resno = 1;
  	ListCell   *v;
  
! 	foreach(v, rel->reltargetlist)
  	{
  		/* Do we really need to copy here?	Not sure */
  		Node	   *node = (Node *) copyObject(lfirst(v));
--- 476,482 ----
  	int			resno = 1;
  	ListCell   *v;
  
! 	foreach(v, rel->reltarget.exprs)
  	{
  		/* Do we really need to copy here?	Not sure */
  		Node	   *node = (Node *) copyObject(lfirst(v));
*************** create_result_plan(PlannerInfo *root, Re
*** 875,883 ****
  	List	   *tlist;
  	List	   *quals;
  
! 	/* The tlist will be installed later, since we have no RelOptInfo */
! 	Assert(best_path->path.parent == NULL);
! 	tlist = NIL;
  
  	/* best_path->quals is just bare clauses */
  
--- 875,882 ----
  	List	   *tlist;
  	List	   *quals;
  
! 	/* This is a bit useless currently, because rel will have empty tlist */
! 	tlist = build_path_tlist(root, &best_path->path);
  
  	/* best_path->quals is just bare clauses */
  
*************** create_foreignscan_plan(PlannerInfo *roo
*** 2183,2189 ****
  	/*
  	 * If rel is a base relation, detect whether any system columns are
  	 * requested from the rel.  (If rel is a join relation, rel->relid will be
! 	 * 0, but there can be no Var with relid 0 in the reltargetlist or the
  	 * restriction clauses, so we skip this in that case.  Note that any such
  	 * columns in base relations that were joined are assumed to be contained
  	 * in fdw_scan_tlist.)  This is a bit of a kluge and might go away someday,
--- 2182,2188 ----
  	/*
  	 * If rel is a base relation, detect whether any system columns are
  	 * requested from the rel.  (If rel is a join relation, rel->relid will be
! 	 * 0, but there can be no Var with relid 0 in the rel's targetlist or the
  	 * restriction clauses, so we skip this in that case.  Note that any such
  	 * columns in base relations that were joined are assumed to be contained
  	 * in fdw_scan_tlist.)  This is a bit of a kluge and might go away someday,
*************** create_foreignscan_plan(PlannerInfo *roo
*** 2198,2207 ****
  
  		/*
  		 * First, examine all the attributes needed for joins or final output.
! 		 * Note: we must look at reltargetlist, not the attr_needed data,
  		 * because attr_needed isn't computed for inheritance child rels.
  		 */
! 		pull_varattnos((Node *) rel->reltargetlist, scan_relid, &attrs_used);
  
  		/* Add all the attributes used by restriction clauses. */
  		foreach(lc, rel->baserestrictinfo)
--- 2197,2206 ----
  
  		/*
  		 * First, examine all the attributes needed for joins or final output.
! 		 * Note: we must look at rel's targetlist, not the attr_needed data,
  		 * because attr_needed isn't computed for inheritance child rels.
  		 */
! 		pull_varattnos((Node *) rel->reltarget.exprs, scan_relid, &attrs_used);
  
  		/* Add all the attributes used by restriction clauses. */
  		foreach(lc, rel->baserestrictinfo)
*************** copy_generic_path_info(Plan *dest, Path 
*** 3455,3461 ****
  		dest->startup_cost = src->startup_cost;
  		dest->total_cost = src->total_cost;
  		dest->plan_rows = src->rows;
! 		dest->plan_width = src->parent->width;
  		dest->parallel_aware = src->parallel_aware;
  	}
  	else
--- 3454,3460 ----
  		dest->startup_cost = src->startup_cost;
  		dest->total_cost = src->total_cost;
  		dest->plan_rows = src->rows;
! 		dest->plan_width = src->pathtarget->width;
  		dest->parallel_aware = src->parallel_aware;
  	}
  	else
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 4a906a8..37fb586 100644
*** a/src/backend/optimizer/plan/initsplan.c
--- b/src/backend/optimizer/plan/initsplan.c
*************** add_vars_to_targetlist(PlannerInfo *root
*** 211,220 ****
  			attno -= rel->min_attr;
  			if (rel->attr_needed[attno] == NULL)
  			{
! 				/* Variable not yet requested, so add to reltargetlist */
  				/* XXX is copyObject necessary here? */
! 				rel->reltargetlist = lappend(rel->reltargetlist,
! 											 copyObject(var));
  			}
  			rel->attr_needed[attno] = bms_add_members(rel->attr_needed[attno],
  													  where_needed);
--- 211,221 ----
  			attno -= rel->min_attr;
  			if (rel->attr_needed[attno] == NULL)
  			{
! 				/* Variable not yet requested, so add to rel's targetlist */
  				/* XXX is copyObject necessary here? */
! 				rel->reltarget.exprs = lappend(rel->reltarget.exprs,
! 											   copyObject(var));
! 				/* reltarget cost and width will be computed later */
  			}
  			rel->attr_needed[attno] = bms_add_members(rel->attr_needed[attno],
  													  where_needed);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index f77c804..65b99e2 100644
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
*************** static List *reorder_grouping_sets(List 
*** 98,111 ****
  static void standard_qp_callback(PlannerInfo *root, void *extra);
  static bool choose_hashed_grouping(PlannerInfo *root,
  					   double tuple_fraction, double limit_tuples,
! 					   double path_rows, int path_width,
  					   Path *cheapest_path, Path *sorted_path,
  					   double dNumGroups, AggClauseCosts *agg_costs);
  static bool choose_hashed_distinct(PlannerInfo *root,
  					   double tuple_fraction, double limit_tuples,
! 					   double path_rows, int path_width,
  					   Cost cheapest_startup_cost, Cost cheapest_total_cost,
  					   Cost sorted_startup_cost, Cost sorted_total_cost,
  					   List *sorted_pathkeys,
  					   double dNumDistinctRows);
  static List *make_subplanTargetList(PlannerInfo *root, List *tlist,
--- 98,113 ----
  static void standard_qp_callback(PlannerInfo *root, void *extra);
  static bool choose_hashed_grouping(PlannerInfo *root,
  					   double tuple_fraction, double limit_tuples,
! 					   double path_rows,
  					   Path *cheapest_path, Path *sorted_path,
  					   double dNumGroups, AggClauseCosts *agg_costs);
  static bool choose_hashed_distinct(PlannerInfo *root,
  					   double tuple_fraction, double limit_tuples,
! 					   double path_rows,
  					   Cost cheapest_startup_cost, Cost cheapest_total_cost,
+ 					   int cheapest_path_width,
  					   Cost sorted_startup_cost, Cost sorted_total_cost,
+ 					   int sorted_path_width,
  					   List *sorted_pathkeys,
  					   double dNumDistinctRows);
  static List *make_subplanTargetList(PlannerInfo *root, List *tlist,
*************** grouping_planner(PlannerInfo *root, doub
*** 1467,1473 ****
  		AggClauseCosts agg_costs;
  		int			numGroupCols;
  		double		path_rows;
- 		int			path_width;
  		bool		use_hashed_grouping = false;
  		WindowFuncLists *wflists = NULL;
  		List	   *activeWindows = NIL;
--- 1469,1474 ----
*************** grouping_planner(PlannerInfo *root, doub
*** 1672,1683 ****
  								  standard_qp_callback, &qp_extra);
  
  		/*
! 		 * Extract rowcount and width estimates for use below.  If final_rel
! 		 * has been proven dummy, its rows estimate will be zero; clamp it to
! 		 * one to avoid zero-divide in subsequent calculations.
  		 */
  		path_rows = clamp_row_est(final_rel->rows);
- 		path_width = final_rel->width;
  
  		/*
  		 * If there's grouping going on, estimate the number of result groups.
--- 1673,1683 ----
  								  standard_qp_callback, &qp_extra);
  
  		/*
! 		 * Extract rowcount estimate for use below.  If final_rel has been
! 		 * proven dummy, its rows estimate will be zero; clamp it to one to
! 		 * avoid zero-divide in subsequent calculations.
  		 */
  		path_rows = clamp_row_est(final_rel->rows);
  
  		/*
  		 * If there's grouping going on, estimate the number of result groups.
*************** grouping_planner(PlannerInfo *root, doub
*** 1849,1855 ****
  				/* Figure cost for sorting */
  				cost_sort(&sort_path, root, root->query_pathkeys,
  						  cheapest_path->total_cost,
! 						  path_rows, path_width,
  						  0.0, work_mem, root->limit_tuples);
  			}
  
--- 1849,1855 ----
  				/* Figure cost for sorting */
  				cost_sort(&sort_path, root, root->query_pathkeys,
  						  cheapest_path->total_cost,
! 						  path_rows, cheapest_path->pathtarget->width,
  						  0.0, work_mem, root->limit_tuples);
  			}
  
*************** grouping_planner(PlannerInfo *root, doub
*** 1881,1887 ****
  				use_hashed_grouping =
  					choose_hashed_grouping(root,
  										   tuple_fraction, limit_tuples,
! 										   path_rows, path_width,
  										   cheapest_path, sorted_path,
  										   dNumGroups, &agg_costs);
  			}
--- 1881,1887 ----
  				use_hashed_grouping =
  					choose_hashed_grouping(root,
  										   tuple_fraction, limit_tuples,
! 										   path_rows,
  										   cheapest_path, sorted_path,
  										   dNumGroups, &agg_costs);
  			}
*************** grouping_planner(PlannerInfo *root, doub
*** 1900,1910 ****
  			use_hashed_distinct =
  				choose_hashed_distinct(root,
  									   tuple_fraction, limit_tuples,
! 									   path_rows, path_width,
  									   cheapest_path->startup_cost,
  									   cheapest_path->total_cost,
  									   sorted_path->startup_cost,
  									   sorted_path->total_cost,
  									   sorted_path->pathkeys,
  									   dNumGroups);
  			tested_hashed_distinct = true;
--- 1900,1912 ----
  			use_hashed_distinct =
  				choose_hashed_distinct(root,
  									   tuple_fraction, limit_tuples,
! 									   path_rows,
  									   cheapest_path->startup_cost,
  									   cheapest_path->total_cost,
+ 									   cheapest_path->pathtarget->width,
  									   sorted_path->startup_cost,
  									   sorted_path->total_cost,
+ 									   sorted_path->pathtarget->width,
  									   sorted_path->pathkeys,
  									   dNumGroups);
  			tested_hashed_distinct = true;
*************** grouping_planner(PlannerInfo *root, doub
*** 2343,2353 ****
  				choose_hashed_distinct(root,
  									   tuple_fraction, limit_tuples,
  									   result_plan->plan_rows,
- 									   result_plan->plan_width,
  									   result_plan->startup_cost,
  									   result_plan->total_cost,
  									   result_plan->startup_cost,
  									   result_plan->total_cost,
  									   current_pathkeys,
  									   dNumDistinctRows);
  		}
--- 2345,2356 ----
  				choose_hashed_distinct(root,
  									   tuple_fraction, limit_tuples,
  									   result_plan->plan_rows,
  									   result_plan->startup_cost,
  									   result_plan->total_cost,
+ 									   result_plan->plan_width,
  									   result_plan->startup_cost,
  									   result_plan->total_cost,
+ 									   result_plan->plan_width,
  									   current_pathkeys,
  									   dNumDistinctRows);
  		}
*************** build_grouping_chain(PlannerInfo *root,
*** 2678,2687 ****
   * any logic that uses plan_rows to, eg, estimate qual evaluation costs.)
   *
   * Note: during initial stages of planning, we mostly consider plan nodes with
!  * "flat" tlists, containing just Vars.  So their evaluation cost is zero
!  * according to the model used by cost_qual_eval() (or if you prefer, the cost
!  * is factored into cpu_tuple_cost).  Thus we can avoid accounting for tlist
!  * cost throughout query_planner() and subroutines.  But once we apply a
   * tlist that might contain actual operators, sub-selects, etc, we'd better
   * account for its cost.  Any set-returning functions in the tlist must also
   * affect the estimated rowcount.
--- 2681,2693 ----
   * any logic that uses plan_rows to, eg, estimate qual evaluation costs.)
   *
   * Note: during initial stages of planning, we mostly consider plan nodes with
!  * "flat" tlists, containing just Vars and PlaceHolderVars.  The evaluation
!  * cost of Vars is zero according to the model used by cost_qual_eval() (or if
!  * you prefer, the cost is factored into cpu_tuple_cost).  The evaluation cost
!  * of a PHV's expression is charged as part of the scan cost of whichever plan
!  * node first computes it, and then subsequent references to the PHV can be
!  * taken as having cost zero.  Thus we can avoid worrying about tlist cost
!  * as such throughout query_planner() and subroutines.  But once we apply a
   * tlist that might contain actual operators, sub-selects, etc, we'd better
   * account for its cost.  Any set-returning functions in the tlist must also
   * affect the estimated rowcount.
*************** standard_qp_callback(PlannerInfo *root, 
*** 3840,3846 ****
  static bool
  choose_hashed_grouping(PlannerInfo *root,
  					   double tuple_fraction, double limit_tuples,
! 					   double path_rows, int path_width,
  					   Path *cheapest_path, Path *sorted_path,
  					   double dNumGroups, AggClauseCosts *agg_costs)
  {
--- 3846,3852 ----
  static bool
  choose_hashed_grouping(PlannerInfo *root,
  					   double tuple_fraction, double limit_tuples,
! 					   double path_rows,
  					   Path *cheapest_path, Path *sorted_path,
  					   double dNumGroups, AggClauseCosts *agg_costs)
  {
*************** choose_hashed_grouping(PlannerInfo *root
*** 3853,3858 ****
--- 3859,3865 ----
  	List	   *current_pathkeys;
  	Path		hashed_p;
  	Path		sorted_p;
+ 	int			sorted_p_width;
  
  	/*
  	 * Executor doesn't support hashed aggregation with DISTINCT or ORDER BY
*************** choose_hashed_grouping(PlannerInfo *root
*** 3890,3896 ****
  	 */
  
  	/* Estimate per-hash-entry space at tuple width... */
! 	hashentrysize = MAXALIGN(path_width) + MAXALIGN(SizeofMinimalTupleHeader);
  	/* plus space for pass-by-ref transition values... */
  	hashentrysize += agg_costs->transitionSpace;
  	/* plus the per-hash-entry overhead */
--- 3897,3904 ----
  	 */
  
  	/* Estimate per-hash-entry space at tuple width... */
! 	hashentrysize = MAXALIGN(cheapest_path->pathtarget->width) +
! 		MAXALIGN(SizeofMinimalTupleHeader);
  	/* plus space for pass-by-ref transition values... */
  	hashentrysize += agg_costs->transitionSpace;
  	/* plus the per-hash-entry overhead */
*************** choose_hashed_grouping(PlannerInfo *root
*** 3935,3959 ****
  	/* Result of hashed agg is always unsorted */
  	if (target_pathkeys)
  		cost_sort(&hashed_p, root, target_pathkeys, hashed_p.total_cost,
! 				  dNumGroups, path_width,
  				  0.0, work_mem, limit_tuples);
  
  	if (sorted_path)
  	{
  		sorted_p.startup_cost = sorted_path->startup_cost;
  		sorted_p.total_cost = sorted_path->total_cost;
  		current_pathkeys = sorted_path->pathkeys;
  	}
  	else
  	{
  		sorted_p.startup_cost = cheapest_path->startup_cost;
  		sorted_p.total_cost = cheapest_path->total_cost;
  		current_pathkeys = cheapest_path->pathkeys;
  	}
  	if (!pathkeys_contained_in(root->group_pathkeys, current_pathkeys))
  	{
  		cost_sort(&sorted_p, root, root->group_pathkeys, sorted_p.total_cost,
! 				  path_rows, path_width,
  				  0.0, work_mem, -1.0);
  		current_pathkeys = root->group_pathkeys;
  	}
--- 3943,3969 ----
  	/* Result of hashed agg is always unsorted */
  	if (target_pathkeys)
  		cost_sort(&hashed_p, root, target_pathkeys, hashed_p.total_cost,
! 				  dNumGroups, cheapest_path->pathtarget->width,
  				  0.0, work_mem, limit_tuples);
  
  	if (sorted_path)
  	{
  		sorted_p.startup_cost = sorted_path->startup_cost;
  		sorted_p.total_cost = sorted_path->total_cost;
+ 		sorted_p_width = sorted_path->pathtarget->width;
  		current_pathkeys = sorted_path->pathkeys;
  	}
  	else
  	{
  		sorted_p.startup_cost = cheapest_path->startup_cost;
  		sorted_p.total_cost = cheapest_path->total_cost;
+ 		sorted_p_width = cheapest_path->pathtarget->width;
  		current_pathkeys = cheapest_path->pathkeys;
  	}
  	if (!pathkeys_contained_in(root->group_pathkeys, current_pathkeys))
  	{
  		cost_sort(&sorted_p, root, root->group_pathkeys, sorted_p.total_cost,
! 				  path_rows, sorted_p_width,
  				  0.0, work_mem, -1.0);
  		current_pathkeys = root->group_pathkeys;
  	}
*************** choose_hashed_grouping(PlannerInfo *root
*** 3971,3977 ****
  	if (target_pathkeys &&
  		!pathkeys_contained_in(target_pathkeys, current_pathkeys))
  		cost_sort(&sorted_p, root, target_pathkeys, sorted_p.total_cost,
! 				  dNumGroups, path_width,
  				  0.0, work_mem, limit_tuples);
  
  	/*
--- 3981,3987 ----
  	if (target_pathkeys &&
  		!pathkeys_contained_in(target_pathkeys, current_pathkeys))
  		cost_sort(&sorted_p, root, target_pathkeys, sorted_p.total_cost,
! 				  dNumGroups, sorted_p_width,
  				  0.0, work_mem, limit_tuples);
  
  	/*
*************** choose_hashed_grouping(PlannerInfo *root
*** 4008,4016 ****
  static bool
  choose_hashed_distinct(PlannerInfo *root,
  					   double tuple_fraction, double limit_tuples,
! 					   double path_rows, int path_width,
  					   Cost cheapest_startup_cost, Cost cheapest_total_cost,
  					   Cost sorted_startup_cost, Cost sorted_total_cost,
  					   List *sorted_pathkeys,
  					   double dNumDistinctRows)
  {
--- 4018,4028 ----
  static bool
  choose_hashed_distinct(PlannerInfo *root,
  					   double tuple_fraction, double limit_tuples,
! 					   double path_rows,
  					   Cost cheapest_startup_cost, Cost cheapest_total_cost,
+ 					   int cheapest_path_width,
  					   Cost sorted_startup_cost, Cost sorted_total_cost,
+ 					   int sorted_path_width,
  					   List *sorted_pathkeys,
  					   double dNumDistinctRows)
  {
*************** choose_hashed_distinct(PlannerInfo *root
*** 4058,4064 ****
  	 */
  
  	/* Estimate per-hash-entry space at tuple width... */
! 	hashentrysize = MAXALIGN(path_width) + MAXALIGN(SizeofMinimalTupleHeader);
  	/* plus the per-hash-entry overhead */
  	hashentrysize += hash_agg_entry_size(0);
  
--- 4070,4077 ----
  	 */
  
  	/* Estimate per-hash-entry space at tuple width... */
! 	hashentrysize = MAXALIGN(cheapest_path_width) +
! 		MAXALIGN(SizeofMinimalTupleHeader);
  	/* plus the per-hash-entry overhead */
  	hashentrysize += hash_agg_entry_size(0);
  
*************** choose_hashed_distinct(PlannerInfo *root
*** 4089,4095 ****
  	 */
  	if (parse->sortClause)
  		cost_sort(&hashed_p, root, root->sort_pathkeys, hashed_p.total_cost,
! 				  dNumDistinctRows, path_width,
  				  0.0, work_mem, limit_tuples);
  
  	/*
--- 4102,4108 ----
  	 */
  	if (parse->sortClause)
  		cost_sort(&hashed_p, root, root->sort_pathkeys, hashed_p.total_cost,
! 				  dNumDistinctRows, cheapest_path_width,
  				  0.0, work_mem, limit_tuples);
  
  	/*
*************** choose_hashed_distinct(PlannerInfo *root
*** 4113,4119 ****
  		else
  			current_pathkeys = root->sort_pathkeys;
  		cost_sort(&sorted_p, root, current_pathkeys, sorted_p.total_cost,
! 				  path_rows, path_width,
  				  0.0, work_mem, -1.0);
  	}
  	cost_group(&sorted_p, root, numDistinctCols, dNumDistinctRows,
--- 4126,4132 ----
  		else
  			current_pathkeys = root->sort_pathkeys;
  		cost_sort(&sorted_p, root, current_pathkeys, sorted_p.total_cost,
! 				  path_rows, sorted_path_width,
  				  0.0, work_mem, -1.0);
  	}
  	cost_group(&sorted_p, root, numDistinctCols, dNumDistinctRows,
*************** choose_hashed_distinct(PlannerInfo *root
*** 4122,4128 ****
  	if (parse->sortClause &&
  		!pathkeys_contained_in(root->sort_pathkeys, current_pathkeys))
  		cost_sort(&sorted_p, root, root->sort_pathkeys, sorted_p.total_cost,
! 				  dNumDistinctRows, path_width,
  				  0.0, work_mem, limit_tuples);
  
  	/*
--- 4135,4141 ----
  	if (parse->sortClause &&
  		!pathkeys_contained_in(root->sort_pathkeys, current_pathkeys))
  		cost_sort(&sorted_p, root, root->sort_pathkeys, sorted_p.total_cost,
! 				  dNumDistinctRows, sorted_path_width,
  				  0.0, work_mem, limit_tuples);
  
  	/*
*************** plan_cluster_use_sort(Oid tableOid, Oid 
*** 4896,4902 ****
  	 * set_baserel_size_estimates, just do a quick hack for rows and width.
  	 */
  	rel->rows = rel->tuples;
! 	rel->width = get_relation_data_width(tableOid, NULL);
  
  	root->total_table_pages = rel->pages;
  
--- 4909,4915 ----
  	 * set_baserel_size_estimates, just do a quick hack for rows and width.
  	 */
  	rel->rows = rel->tuples;
! 	rel->reltarget.width = get_relation_data_width(tableOid, NULL);
  
  	root->total_table_pages = rel->pages;
  
*************** plan_cluster_use_sort(Oid tableOid, Oid 
*** 4912,4918 ****
  	/* Estimate the cost of seq scan + sort */
  	seqScanPath = create_seqscan_path(root, rel, NULL, 0);
  	cost_sort(&seqScanAndSortPath, root, NIL,
! 			  seqScanPath->total_cost, rel->tuples, rel->width,
  			  comparisonCost, maintenance_work_mem, -1.0);
  
  	/* Estimate the cost of index scan */
--- 4925,4931 ----
  	/* Estimate the cost of seq scan + sort */
  	seqScanPath = create_seqscan_path(root, rel, NULL, 0);
  	cost_sort(&seqScanAndSortPath, root, NIL,
! 			  seqScanPath->total_cost, rel->tuples, rel->reltarget.width,
  			  comparisonCost, maintenance_work_mem, -1.0);
  
  	/* Estimate the cost of index scan */
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index dff115e..6ac25dc 100644
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
*************** eval_const_expressions_mutator(Node *nod
*** 3489,3495 ****
  				 * can optimize field selection from a RowExpr construct.
  				 *
  				 * However, replacing a whole-row Var in this way has a
! 				 * pitfall: if we've already built the reltargetlist for the
  				 * source relation, then the whole-row Var is scheduled to be
  				 * produced by the relation scan, but the simple Var probably
  				 * isn't, which will lead to a failure in setrefs.c.  This is
--- 3489,3495 ----
  				 * can optimize field selection from a RowExpr construct.
  				 *
  				 * However, replacing a whole-row Var in this way has a
! 				 * pitfall: if we've already built the rel targetlist for the
  				 * source relation, then the whole-row Var is scheduled to be
  				 * produced by the relation scan, but the simple Var probably
  				 * isn't, which will lead to a failure in setrefs.c.  This is
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 1097a18..9417587 100644
*** a/src/backend/optimizer/util/pathnode.c
--- b/src/backend/optimizer/util/pathnode.c
*************** create_seqscan_path(PlannerInfo *root, R
*** 929,934 ****
--- 929,935 ----
  
  	pathnode->pathtype = T_SeqScan;
  	pathnode->parent = rel;
+ 	pathnode->pathtarget = &(rel->reltarget);
  	pathnode->param_info = get_baserel_parampathinfo(root, rel,
  													 required_outer);
  	pathnode->parallel_aware = parallel_degree > 0 ? true : false;
*************** create_samplescan_path(PlannerInfo *root
*** 952,957 ****
--- 953,959 ----
  
  	pathnode->pathtype = T_SampleScan;
  	pathnode->parent = rel;
+ 	pathnode->pathtarget = &(rel->reltarget);
  	pathnode->param_info = get_baserel_parampathinfo(root, rel,
  													 required_outer);
  	pathnode->parallel_aware = false;
*************** create_index_path(PlannerInfo *root,
*** 1008,1013 ****
--- 1010,1016 ----
  
  	pathnode->path.pathtype = indexonly ? T_IndexOnlyScan : T_IndexScan;
  	pathnode->path.parent = rel;
+ 	pathnode->path.pathtarget = &(rel->reltarget);
  	pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
  														  required_outer);
  	pathnode->path.parallel_aware = false;
*************** create_bitmap_heap_path(PlannerInfo *roo
*** 1056,1061 ****
--- 1059,1065 ----
  
  	pathnode->path.pathtype = T_BitmapHeapScan;
  	pathnode->path.parent = rel;
+ 	pathnode->path.pathtarget = &(rel->reltarget);
  	pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
  														  required_outer);
  	pathnode->path.parallel_aware = false;
*************** create_bitmap_and_path(PlannerInfo *root
*** 1085,1090 ****
--- 1089,1095 ----
  
  	pathnode->path.pathtype = T_BitmapAnd;
  	pathnode->path.parent = rel;
+ 	pathnode->path.pathtarget = &(rel->reltarget);
  	pathnode->path.param_info = NULL;	/* not used in bitmap trees */
  
  	/*
*************** create_bitmap_or_path(PlannerInfo *root,
*** 1120,1125 ****
--- 1125,1131 ----
  
  	pathnode->path.pathtype = T_BitmapOr;
  	pathnode->path.parent = rel;
+ 	pathnode->path.pathtarget = &(rel->reltarget);
  	pathnode->path.param_info = NULL;	/* not used in bitmap trees */
  
  	/*
*************** create_tidscan_path(PlannerInfo *root, R
*** 1154,1159 ****
--- 1160,1166 ----
  
  	pathnode->path.pathtype = T_TidScan;
  	pathnode->path.parent = rel;
+ 	pathnode->path.pathtarget = &(rel->reltarget);
  	pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
  														  required_outer);
  	pathnode->path.parallel_aware = false;
*************** create_append_path(RelOptInfo *rel, List
*** 1185,1190 ****
--- 1192,1198 ----
  
  	pathnode->path.pathtype = T_Append;
  	pathnode->path.parent = rel;
+ 	pathnode->path.pathtarget = &(rel->reltarget);
  	pathnode->path.param_info = get_appendrel_parampathinfo(rel,
  															required_outer);
  	pathnode->path.parallel_aware = false;
*************** create_merge_append_path(PlannerInfo *ro
*** 1243,1248 ****
--- 1251,1257 ----
  
  	pathnode->path.pathtype = T_MergeAppend;
  	pathnode->path.parent = rel;
+ 	pathnode->path.pathtarget = &(rel->reltarget);
  	pathnode->path.param_info = get_appendrel_parampathinfo(rel,
  															required_outer);
  	pathnode->path.parallel_aware = false;
*************** create_merge_append_path(PlannerInfo *ro
*** 1290,1296 ****
  					  pathkeys,
  					  subpath->total_cost,
  					  subpath->parent->tuples,
! 					  subpath->parent->width,
  					  0.0,
  					  work_mem,
  					  pathnode->limit_tuples);
--- 1299,1305 ----
  					  pathkeys,
  					  subpath->total_cost,
  					  subpath->parent->tuples,
! 					  subpath->pathtarget->width,
  					  0.0,
  					  work_mem,
  					  pathnode->limit_tuples);
*************** create_result_path(RelOptInfo *rel, List
*** 1322,1328 ****
  	ResultPath *pathnode = makeNode(ResultPath);
  
  	pathnode->path.pathtype = T_Result;
! 	pathnode->path.parent = NULL;
  	pathnode->path.param_info = NULL;	/* there are no other rels... */
  	pathnode->path.parallel_aware = false;
  	pathnode->path.parallel_safe = rel->consider_parallel;
--- 1331,1338 ----
  	ResultPath *pathnode = makeNode(ResultPath);
  
  	pathnode->path.pathtype = T_Result;
! 	pathnode->path.parent = rel;
! 	pathnode->path.pathtarget = &(rel->reltarget);
  	pathnode->path.param_info = NULL;	/* there are no other rels... */
  	pathnode->path.parallel_aware = false;
  	pathnode->path.parallel_safe = rel->consider_parallel;
*************** create_result_path(RelOptInfo *rel, List
*** 1339,1345 ****
  	 * In theory we should include the qual eval cost as well, but at present
  	 * that doesn't accomplish much except duplicate work that will be done
  	 * again in make_result; since this is only used for degenerate cases,
! 	 * nothing interesting will be done with the path cost values...
  	 */
  
  	return pathnode;
--- 1349,1358 ----
  	 * In theory we should include the qual eval cost as well, but at present
  	 * that doesn't accomplish much except duplicate work that will be done
  	 * again in make_result; since this is only used for degenerate cases,
! 	 * nothing interesting will be done with the path cost values.
! 	 *
! 	 * (Likewise, we don't worry about pathtarget->cost since that tlist will
! 	 * be empty at this point.)
  	 */
  
  	return pathnode;
*************** create_material_path(RelOptInfo *rel, Pa
*** 1359,1364 ****
--- 1372,1378 ----
  
  	pathnode->path.pathtype = T_Material;
  	pathnode->path.parent = rel;
+ 	pathnode->path.pathtarget = &(rel->reltarget);
  	pathnode->path.param_info = subpath->param_info;
  	pathnode->path.parallel_aware = false;
  	pathnode->path.parallel_safe = subpath->parallel_safe;
*************** create_material_path(RelOptInfo *rel, Pa
*** 1371,1377 ****
  				  subpath->startup_cost,
  				  subpath->total_cost,
  				  subpath->rows,
! 				  rel->width);
  
  	return pathnode;
  }
--- 1385,1391 ----
  				  subpath->startup_cost,
  				  subpath->total_cost,
  				  subpath->rows,
! 				  subpath->pathtarget->width);
  
  	return pathnode;
  }
*************** create_unique_path(PlannerInfo *root, Re
*** 1422,1427 ****
--- 1436,1442 ----
  
  	pathnode->path.pathtype = T_Unique;
  	pathnode->path.parent = rel;
+ 	pathnode->path.pathtarget = &(rel->reltarget);
  	pathnode->path.param_info = subpath->param_info;
  	pathnode->path.parallel_aware = false;
  	pathnode->path.parallel_safe = subpath->parallel_safe;
*************** create_unique_path(PlannerInfo *root, Re
*** 1516,1522 ****
  		cost_sort(&sort_path, root, NIL,
  				  subpath->total_cost,
  				  rel->rows,
! 				  rel->width,
  				  0.0,
  				  work_mem,
  				  -1.0);
--- 1531,1537 ----
  		cost_sort(&sort_path, root, NIL,
  				  subpath->total_cost,
  				  rel->rows,
! 				  subpath->pathtarget->width,
  				  0.0,
  				  work_mem,
  				  -1.0);
*************** create_unique_path(PlannerInfo *root, Re
*** 1536,1542 ****
  		 * Estimate the overhead per hashtable entry at 64 bytes (same as in
  		 * planner.c).
  		 */
! 		int			hashentrysize = rel->width + 64;
  
  		if (hashentrysize * pathnode->path.rows > work_mem * 1024L)
  		{
--- 1551,1557 ----
  		 * Estimate the overhead per hashtable entry at 64 bytes (same as in
  		 * planner.c).
  		 */
! 		int			hashentrysize = subpath->pathtarget->width + 64;
  
  		if (hashentrysize * pathnode->path.rows > work_mem * 1024L)
  		{
*************** create_gather_path(PlannerInfo *root, Re
*** 1607,1612 ****
--- 1622,1628 ----
  
  	pathnode->path.pathtype = T_Gather;
  	pathnode->path.parent = rel;
+ 	pathnode->path.pathtarget = &(rel->reltarget);
  	pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
  														  required_outer);
  	pathnode->path.parallel_aware = false;
*************** create_subqueryscan_path(PlannerInfo *ro
*** 1672,1677 ****
--- 1688,1694 ----
  
  	pathnode->pathtype = T_SubqueryScan;
  	pathnode->parent = rel;
+ 	pathnode->pathtarget = &(rel->reltarget);
  	pathnode->param_info = get_baserel_parampathinfo(root, rel,
  													 required_outer);
  	pathnode->parallel_aware = false;
*************** create_functionscan_path(PlannerInfo *ro
*** 1697,1702 ****
--- 1714,1720 ----
  
  	pathnode->pathtype = T_FunctionScan;
  	pathnode->parent = rel;
+ 	pathnode->pathtarget = &(rel->reltarget);
  	pathnode->param_info = get_baserel_parampathinfo(root, rel,
  													 required_outer);
  	pathnode->parallel_aware = false;
*************** create_valuesscan_path(PlannerInfo *root
*** 1722,1727 ****
--- 1740,1746 ----
  
  	pathnode->pathtype = T_ValuesScan;
  	pathnode->parent = rel;
+ 	pathnode->pathtarget = &(rel->reltarget);
  	pathnode->param_info = get_baserel_parampathinfo(root, rel,
  													 required_outer);
  	pathnode->parallel_aware = false;
*************** create_ctescan_path(PlannerInfo *root, R
*** 1746,1751 ****
--- 1765,1771 ----
  
  	pathnode->pathtype = T_CteScan;
  	pathnode->parent = rel;
+ 	pathnode->pathtarget = &(rel->reltarget);
  	pathnode->param_info = get_baserel_parampathinfo(root, rel,
  													 required_outer);
  	pathnode->parallel_aware = false;
*************** create_worktablescan_path(PlannerInfo *r
*** 1771,1776 ****
--- 1791,1797 ----
  
  	pathnode->pathtype = T_WorkTableScan;
  	pathnode->parent = rel;
+ 	pathnode->pathtarget = &(rel->reltarget);
  	pathnode->param_info = get_baserel_parampathinfo(root, rel,
  													 required_outer);
  	pathnode->parallel_aware = false;
*************** create_foreignscan_path(PlannerInfo *roo
*** 1806,1811 ****
--- 1827,1833 ----
  
  	pathnode->path.pathtype = T_ForeignScan;
  	pathnode->path.parent = rel;
+ 	pathnode->path.pathtarget = &(rel->reltarget);
  	pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
  														  required_outer);
  	pathnode->path.parallel_aware = false;
*************** create_nestloop_path(PlannerInfo *root,
*** 1938,1943 ****
--- 1960,1966 ----
  
  	pathnode->path.pathtype = T_NestLoop;
  	pathnode->path.parent = joinrel;
+ 	pathnode->path.pathtarget = &(joinrel->reltarget);
  	pathnode->path.param_info =
  		get_joinrel_parampathinfo(root,
  								  joinrel,
*************** create_mergejoin_path(PlannerInfo *root,
*** 2000,2005 ****
--- 2023,2029 ----
  
  	pathnode->jpath.path.pathtype = T_MergeJoin;
  	pathnode->jpath.path.parent = joinrel;
+ 	pathnode->jpath.path.pathtarget = &(joinrel->reltarget);
  	pathnode->jpath.path.param_info =
  		get_joinrel_parampathinfo(root,
  								  joinrel,
*************** create_hashjoin_path(PlannerInfo *root,
*** 2060,2065 ****
--- 2084,2090 ----
  
  	pathnode->jpath.path.pathtype = T_HashJoin;
  	pathnode->jpath.path.parent = joinrel;
+ 	pathnode->jpath.path.pathtarget = &(joinrel->reltarget);
  	pathnode->jpath.path.param_info =
  		get_joinrel_parampathinfo(root,
  								  joinrel,
diff --git a/src/backend/optimizer/util/placeholder.c b/src/backend/optimizer/util/placeholder.c
index d954265..4af06ab 100644
*** a/src/backend/optimizer/util/placeholder.c
--- b/src/backend/optimizer/util/placeholder.c
***************
*** 16,21 ****
--- 16,22 ----
  #include "postgres.h"
  
  #include "nodes/nodeFuncs.h"
+ #include "optimizer/cost.h"
  #include "optimizer/pathnode.h"
  #include "optimizer/placeholder.h"
  #include "optimizer/planmain.h"
*************** add_placeholders_to_base_rels(PlannerInf
*** 388,395 ****
  		{
  			RelOptInfo *rel = find_base_rel(root, varno);
  
! 			rel->reltargetlist = lappend(rel->reltargetlist,
! 										 copyObject(phinfo->ph_var));
  		}
  	}
  }
--- 389,397 ----
  		{
  			RelOptInfo *rel = find_base_rel(root, varno);
  
! 			rel->reltarget.exprs = lappend(rel->reltarget.exprs,
! 										   copyObject(phinfo->ph_var));
! 			/* reltarget's cost and width fields will be updated later */
  		}
  	}
  }
*************** add_placeholders_to_base_rels(PlannerInf
*** 402,412 ****
   *
   * A join rel should emit a PlaceHolderVar if (a) the PHV is needed above
   * this join level and (b) the PHV can be computed at or below this level.
-  * At this time we do not need to distinguish whether the PHV will be
-  * computed here or copied up from below.
   */
  void
! add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel)
  {
  	Relids		relids = joinrel->relids;
  	ListCell   *lc;
--- 404,413 ----
   *
   * A join rel should emit a PlaceHolderVar if (a) the PHV is needed above
   * this join level and (b) the PHV can be computed at or below this level.
   */
  void
! add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
! 							RelOptInfo *outer_rel, RelOptInfo *inner_rel)
  {
  	Relids		relids = joinrel->relids;
  	ListCell   *lc;
*************** add_placeholders_to_joinrel(PlannerInfo 
*** 422,430 ****
  			if (bms_is_subset(phinfo->ph_eval_at, relids))
  			{
  				/* Yup, add it to the output */
! 				joinrel->reltargetlist = lappend(joinrel->reltargetlist,
! 												 phinfo->ph_var);
! 				joinrel->width += phinfo->ph_width;
  				/* Adjust joinrel's direct_lateral_relids as needed */
  				joinrel->direct_lateral_relids =
  					bms_add_members(joinrel->direct_lateral_relids,
--- 423,454 ----
  			if (bms_is_subset(phinfo->ph_eval_at, relids))
  			{
  				/* Yup, add it to the output */
! 				joinrel->reltarget.exprs = lappend(joinrel->reltarget.exprs,
! 												   phinfo->ph_var);
! 				joinrel->reltarget.width += phinfo->ph_width;
! 
! 				/*
! 				 * Charge the cost of evaluating the contained expression if
! 				 * the PHV can be computed here but not in either input.  This
! 				 * is a bit bogus because we make the decision based on the
! 				 * first pair of possible input relations considered for the
! 				 * joinrel.  With other pairs, it might be possible to compute
! 				 * the PHV in one input or the other, and then we'd be double
! 				 * charging the PHV's cost for some join paths.  For now, live
! 				 * with that; but we might want to improve it later by
! 				 * refiguring the reltarget costs for each pair of inputs.
! 				 */
! 				if (!bms_is_subset(phinfo->ph_eval_at, outer_rel->relids) &&
! 					!bms_is_subset(phinfo->ph_eval_at, inner_rel->relids))
! 				{
! 					QualCost	cost;
! 
! 					cost_qual_eval_node(&cost, (Node *) phinfo->ph_var->phexpr,
! 										root);
! 					joinrel->reltarget.cost.startup += cost.startup;
! 					joinrel->reltarget.cost.per_tuple += cost.per_tuple;
! 				}
! 
  				/* Adjust joinrel's direct_lateral_relids as needed */
  				joinrel->direct_lateral_relids =
  					bms_add_members(joinrel->direct_lateral_relids,
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 420692f..e658f27 100644
*** a/src/backend/optimizer/util/relnode.c
--- b/src/backend/optimizer/util/relnode.c
*************** build_simple_rel(PlannerInfo *root, int 
*** 102,113 ****
  	rel->reloptkind = reloptkind;
  	rel->relids = bms_make_singleton(relid);
  	rel->rows = 0;
- 	rel->width = 0;
  	/* cheap startup cost is interesting iff not all tuples to be retrieved */
  	rel->consider_startup = (root->tuple_fraction > 0);
  	rel->consider_param_startup = false;		/* might get changed later */
  	rel->consider_parallel = false;		/* might get changed later */
! 	rel->reltargetlist = NIL;
  	rel->pathlist = NIL;
  	rel->ppilist = NIL;
  	rel->partial_pathlist = NIL;
--- 102,115 ----
  	rel->reloptkind = reloptkind;
  	rel->relids = bms_make_singleton(relid);
  	rel->rows = 0;
  	/* cheap startup cost is interesting iff not all tuples to be retrieved */
  	rel->consider_startup = (root->tuple_fraction > 0);
  	rel->consider_param_startup = false;		/* might get changed later */
  	rel->consider_parallel = false;		/* might get changed later */
! 	rel->reltarget.exprs = NIL;
! 	rel->reltarget.cost.startup = 0;
! 	rel->reltarget.cost.per_tuple = 0;
! 	rel->reltarget.width = 0;
  	rel->pathlist = NIL;
  	rel->ppilist = NIL;
  	rel->partial_pathlist = NIL;
*************** build_join_rel(PlannerInfo *root,
*** 387,398 ****
  	joinrel->reloptkind = RELOPT_JOINREL;
  	joinrel->relids = bms_copy(joinrelids);
  	joinrel->rows = 0;
- 	joinrel->width = 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->reltargetlist = NIL;
  	joinrel->pathlist = NIL;
  	joinrel->ppilist = NIL;
  	joinrel->partial_pathlist = NIL;
--- 389,402 ----
  	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.exprs = NIL;
! 	joinrel->reltarget.cost.startup = 0;
! 	joinrel->reltarget.cost.per_tuple = 0;
! 	joinrel->reltarget.width = 0;
  	joinrel->pathlist = NIL;
  	joinrel->ppilist = NIL;
  	joinrel->partial_pathlist = NIL;
*************** build_join_rel(PlannerInfo *root,
*** 459,465 ****
  	 */
  	build_joinrel_tlist(root, joinrel, outer_rel);
  	build_joinrel_tlist(root, joinrel, inner_rel);
! 	add_placeholders_to_joinrel(root, joinrel);
  
  	/*
  	 * add_placeholders_to_joinrel also took care of adding the ph_lateral
--- 463,469 ----
  	 */
  	build_joinrel_tlist(root, joinrel, outer_rel);
  	build_joinrel_tlist(root, joinrel, inner_rel);
! 	add_placeholders_to_joinrel(root, joinrel, outer_rel, inner_rel);
  
  	/*
  	 * add_placeholders_to_joinrel also took care of adding the ph_lateral
*************** build_joinrel_tlist(PlannerInfo *root, R
*** 609,615 ****
  	Relids		relids = joinrel->relids;
  	ListCell   *vars;
  
! 	foreach(vars, input_rel->reltargetlist)
  	{
  		Var		   *var = (Var *) lfirst(vars);
  		RelOptInfo *baserel;
--- 613,619 ----
  	Relids		relids = joinrel->relids;
  	ListCell   *vars;
  
! 	foreach(vars, input_rel->reltarget.exprs)
  	{
  		Var		   *var = (Var *) lfirst(vars);
  		RelOptInfo *baserel;
*************** build_joinrel_tlist(PlannerInfo *root, R
*** 628,634 ****
  		 * rels, which will never be seen here.)
  		 */
  		if (!IsA(var, Var))
! 			elog(ERROR, "unexpected node type in reltargetlist: %d",
  				 (int) nodeTag(var));
  
  		/* Get the Var's original base rel */
--- 632,638 ----
  		 * 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 */
*************** build_joinrel_tlist(PlannerInfo *root, R
*** 639,646 ****
  		if (bms_nonempty_difference(baserel->attr_needed[ndx], relids))
  		{
  			/* Yup, add it to the output */
! 			joinrel->reltargetlist = lappend(joinrel->reltargetlist, var);
! 			joinrel->width += baserel->attr_widths[ndx];
  		}
  	}
  }
--- 643,651 ----
  		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];
  		}
  	}
  }
*************** build_empty_join_rel(PlannerInfo *root)
*** 826,832 ****
  	joinrel->reloptkind = RELOPT_JOINREL;
  	joinrel->relids = NULL;		/* empty set */
  	joinrel->rows = 1;			/* we produce one row for such cases */
- 	joinrel->width = 0;			/* it contains no Vars */
  	joinrel->rtekind = RTE_JOIN;
  
  	root->join_rel_list = lappend(root->join_rel_list, joinrel);
--- 831,836 ----
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 96198ae..af8cb6b 100644
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
*************** typedef struct AggClauseCosts
*** 61,66 ****
--- 61,85 ----
  	Size		transitionSpace;	/* space for pass-by-ref transition data */
  } AggClauseCosts;
  
+ /*
+  * This struct contains what we need to know during planning about the
+  * targetlist (output columns) that a Path will compute.  Each RelOptInfo
+  * includes a default PathTarget, which its individual Paths may merely point
+  * to.  However, in some cases a Path may compute outputs different from other
+  * Paths, and in that case we make a custom PathTarget struct for it.  For
+  * example, an indexscan might return index expressions that would otherwise
+  * need to be explicitly calculated.
+  *
+  * Note that PathTarget.exprs is just a list of expressions; they do not have
+  * TargetEntry nodes on top, though those will appear in the finished Plan.
+  */
+ typedef struct PathTarget
+ {
+ 	List	   *exprs;			/* list of expressions to be computed */
+ 	QualCost	cost;			/* cost of evaluating the above */
+ 	int			width;			/* estimated avg width of result tuples */
+ } PathTarget;
+ 
  
  /*----------
   * PlannerGlobal
*************** typedef struct PlannerInfo
*** 334,350 ****
   *				if there is just one, a join relation if more than one
   *		rows - estimated number of tuples in the relation after restriction
   *			   clauses have been applied (ie, output rows of a plan for it)
-  *		width - avg. number of bytes per tuple in the relation after the
-  *				appropriate projections have been done (ie, output width)
   *		consider_startup - true if there is any value in keeping plain paths for
   *						   this rel on the basis of having cheap startup cost
   *		consider_param_startup - the same for parameterized paths
!  *		reltargetlist - List of Var and PlaceHolderVar nodes for the values
!  *						we need to output from this relation.
!  *						List is in no particular order, but all rels of an
!  *						appendrel set must use corresponding orders.
!  *						NOTE: in an appendrel child relation, may contain
!  *						arbitrary expressions pulled up from a subquery!
   *		pathlist - List of Path nodes, one for each potentially useful
   *				   method of generating the relation
   *		ppilist - ParamPathInfo nodes for parameterized Paths, if any
--- 353,368 ----
   *				if there is just one, a join relation if more than one
   *		rows - estimated number of tuples in the relation after restriction
   *			   clauses have been applied (ie, output rows of a plan for it)
   *		consider_startup - true if there is any value in keeping plain paths for
   *						   this rel on the basis of having cheap startup cost
   *		consider_param_startup - the same for parameterized paths
!  *		reltarget - Default Path output tlist for this rel; normally contains
!  *					Var and PlaceHolderVar nodes for the values we need to
!  *					output from this relation.
!  *					List is in no particular order, but all rels of an
!  *					appendrel set must use corresponding orders.
!  *					NOTE: in an appendrel child relation, may contain
!  *					arbitrary expressions pulled up from a subquery!
   *		pathlist - List of Path nodes, one for each potentially useful
   *				   method of generating the relation
   *		ppilist - ParamPathInfo nodes for parameterized Paths, if any
*************** typedef struct RelOptInfo
*** 451,465 ****
  
  	/* size estimates generated by planner */
  	double		rows;			/* estimated number of result tuples */
- 	int			width;			/* estimated avg width of result tuples */
  
  	/* per-relation planner control flags */
  	bool		consider_startup;		/* keep cheap-startup-cost paths? */
  	bool		consider_param_startup; /* ditto, for parameterized paths? */
  	bool		consider_parallel;		/* consider parallel paths? */
  
  	/* materialization information */
- 	List	   *reltargetlist;	/* Vars to be output by scan of relation */
  	List	   *pathlist;		/* Path structures */
  	List	   *ppilist;		/* ParamPathInfos used in pathlist */
  	List	   *partial_pathlist;	/* partial Paths */
--- 469,484 ----
  
  	/* size estimates generated by planner */
  	double		rows;			/* estimated number of result tuples */
  
  	/* per-relation planner control flags */
  	bool		consider_startup;		/* keep cheap-startup-cost paths? */
  	bool		consider_param_startup; /* ditto, for parameterized paths? */
  	bool		consider_parallel;		/* consider parallel paths? */
  
+ 	/* default result targetlist for Paths scanning this relation */
+ 	PathTarget	reltarget;		/* list of Vars/Exprs, cost, width */
+ 
  	/* materialization information */
  	List	   *pathlist;		/* Path structures */
  	List	   *ppilist;		/* ParamPathInfos used in pathlist */
  	List	   *partial_pathlist;	/* partial Paths */
*************** typedef struct ParamPathInfo
*** 744,749 ****
--- 763,773 ----
   * the same Path type for multiple Plan types when there is no need to
   * distinguish the Plan type during path processing.
   *
+  * "parent" identifies the relation this Path scans, and "pathtarget"
+  * describes the precise set of output columns the Path would compute.
+  * In simple cases all Paths for a given rel share the same targetlist,
+  * which we represent by having path->pathtarget point to parent->reltarget.
+  *
   * "param_info", if not NULL, links to a ParamPathInfo that identifies outer
   * relation(s) that provide parameter values to each scan of this path.
   * That means this path can only be joined to those rels by means of nestloop
*************** typedef struct Path
*** 765,771 ****
--- 789,798 ----
  	NodeTag		pathtype;		/* tag identifying scan/join method */
  
  	RelOptInfo *parent;			/* the relation this path can build */
+ 	PathTarget *pathtarget;		/* list of Vars/Exprs, cost, width */
+ 
  	ParamPathInfo *param_info;	/* parameterization info, or NULL if none */
+ 
  	bool		parallel_aware; /* engage parallel-aware logic? */
  	bool		parallel_safe;	/* OK to use as part of parallel plan? */
  	int			parallel_degree; /* desired parallel degree; 0 = not parallel */
diff --git a/src/include/optimizer/placeholder.h b/src/include/optimizer/placeholder.h
index 59c87ae..54d0216 100644
*** a/src/include/optimizer/placeholder.h
--- b/src/include/optimizer/placeholder.h
*************** extern void update_placeholder_eval_leve
*** 26,32 ****
  							   SpecialJoinInfo *new_sjinfo);
  extern void fix_placeholder_input_needed_levels(PlannerInfo *root);
  extern void add_placeholders_to_base_rels(PlannerInfo *root);
! extern void add_placeholders_to_joinrel(PlannerInfo *root,
! 							RelOptInfo *joinrel);
  
  #endif   /* PLACEHOLDER_H */
--- 26,32 ----
  							   SpecialJoinInfo *new_sjinfo);
  extern void fix_placeholder_input_needed_levels(PlannerInfo *root);
  extern void add_placeholders_to_base_rels(PlannerInfo *root);
! extern void add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
! 							RelOptInfo *outer_rel, RelOptInfo *inner_rel);
  
  #endif   /* PLACEHOLDER_H */
diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out
index e402834..59d7877 100644
*** a/src/test/regress/expected/join.out
--- b/src/test/regress/expected/join.out
*************** select v.* from
*** 4682,4705 ****
    lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy);
          vx         |        vy         
  -------------------+-------------------
!                123 |                  
!                456 |                  
!                123 |  4567890123456789
!   4567890123456789 | -4567890123456789
                 123 |  4567890123456789
    4567890123456789 |  4567890123456789
-                123 |  4567890123456789
-   4567890123456789 |               123
    4567890123456789 |               123
                 123 |  4567890123456789
    4567890123456789 |               123
-                123 |               456
    4567890123456789 |  4567890123456789
-   4567890123456789 | -4567890123456789
    4567890123456789 |  4567890123456789
    4567890123456789 |  4567890123456789
    4567890123456789 |  4567890123456789
!   4567890123456789 |               123
    4567890123456789 |                  
   -4567890123456789 |                  
  (20 rows)
--- 4682,4705 ----
    lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy);
          vx         |        vy         
  -------------------+-------------------
!   4567890123456789 |               123
!                123 |               456
!   4567890123456789 |               123
                 123 |  4567890123456789
    4567890123456789 |  4567890123456789
    4567890123456789 |               123
                 123 |  4567890123456789
    4567890123456789 |               123
    4567890123456789 |  4567890123456789
    4567890123456789 |  4567890123456789
+                123 |  4567890123456789
    4567890123456789 |  4567890123456789
    4567890123456789 |  4567890123456789
!   4567890123456789 | -4567890123456789
!                123 |  4567890123456789
!   4567890123456789 | -4567890123456789
!                123 |                  
!                456 |                  
    4567890123456789 |                  
   -4567890123456789 |                  
  (20 rows)
*************** select v.* from
*** 4713,4736 ****
    lateral (select x.q1,y.q1 from dual union all select x.q2,y.q2 from dual) v(vx,vy);
          vx         |        vy         
  -------------------+-------------------
!                123 |                  
!                456 |                  
!                123 |  4567890123456789
!   4567890123456789 | -4567890123456789
                 123 |  4567890123456789
    4567890123456789 |  4567890123456789
-                123 |  4567890123456789
-   4567890123456789 |               123
    4567890123456789 |               123
                 123 |  4567890123456789
    4567890123456789 |               123
-                123 |               456
    4567890123456789 |  4567890123456789
-   4567890123456789 | -4567890123456789
    4567890123456789 |  4567890123456789
    4567890123456789 |  4567890123456789
    4567890123456789 |  4567890123456789
!   4567890123456789 |               123
    4567890123456789 |                  
   -4567890123456789 |                  
  (20 rows)
--- 4713,4736 ----
    lateral (select x.q1,y.q1 from dual union all select x.q2,y.q2 from dual) v(vx,vy);
          vx         |        vy         
  -------------------+-------------------
!   4567890123456789 |               123
!                123 |               456
!   4567890123456789 |               123
                 123 |  4567890123456789
    4567890123456789 |  4567890123456789
    4567890123456789 |               123
                 123 |  4567890123456789
    4567890123456789 |               123
    4567890123456789 |  4567890123456789
    4567890123456789 |  4567890123456789
+                123 |  4567890123456789
    4567890123456789 |  4567890123456789
    4567890123456789 |  4567890123456789
!   4567890123456789 | -4567890123456789
!                123 |  4567890123456789
!   4567890123456789 | -4567890123456789
!                123 |                  
!                456 |                  
    4567890123456789 |                  
   -4567890123456789 |                  
  (20 rows)
