From 307c78852f516042cebacaed411a0391bfeb2129 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 14 Mar 2016 16:59:59 -0400
Subject: [PATCH] Rethink representation of PathTargets.

In commit 19a541143a09c067 I did not make PathTarget a subtype of Node,
and embedded a RelOptInfo's reltarget directly into it rather than having
a separately-allocated Node.  In hindsight that was misguided
micro-optimization, enabled by the fact that at that point we didn't have
any Paths with custom PathTargets.  Now that PathTarget processing has
been fleshed out some more, it's easier to see that it's better to have
PathTarget as an indepedent Node type, even if it does cost us one more
palloc to create a RelOptInfo.  So change it while we still can.

This commit just changes the representation, without doing anything more
interesting than that.
---
 contrib/file_fdw/file_fdw.c              |  4 +-
 contrib/postgres_fdw/deparse.c           |  4 +-
 contrib/postgres_fdw/postgres_fdw.c      | 10 ++--
 doc/src/sgml/fdwhandler.sgml             |  2 +-
 src/backend/nodes/outfuncs.c             | 46 +++++++++--------
 src/backend/optimizer/path/allpaths.c    | 22 ++++----
 src/backend/optimizer/path/costsize.c    | 20 ++++----
 src/backend/optimizer/path/indxpath.c    |  8 +--
 src/backend/optimizer/plan/createplan.c  |  2 +-
 src/backend/optimizer/plan/initsplan.c   |  4 +-
 src/backend/optimizer/plan/planmain.c    |  2 +-
 src/backend/optimizer/plan/planner.c     |  4 +-
 src/backend/optimizer/util/pathnode.c    | 44 ++++++++--------
 src/backend/optimizer/util/placeholder.c | 14 ++---
 src/backend/optimizer/util/relnode.c     | 23 ++++-----
 src/backend/optimizer/util/tlist.c       |  6 +--
 src/include/nodes/nodes.h                |  1 +
 src/include/nodes/relation.h             | 65 +++++++++++++-----------
 18 files changed, 143 insertions(+), 138 deletions(-)

diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index dc035d72a21..0ac4658e84d 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -821,7 +821,7 @@ check_selective_binary_conversion(RelOptInfo *baserel,
 	}
 
 	/* Collect all the attributes needed for joins or final output. */
-	pull_varattnos((Node *) baserel->reltarget.exprs, baserel->relid,
+	pull_varattnos((Node *) baserel->reltarget->exprs, baserel->relid,
 				   &attrs_used);
 
 	/* Add all the attributes used by restriction clauses. */
@@ -953,7 +953,7 @@ estimate_size(PlannerInfo *root, RelOptInfo *baserel,
 		 */
 		int			tuple_width;
 
-		tuple_width = MAXALIGN(baserel->reltarget.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 e6d4cdbe5ba..17081e48bd1 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -728,10 +728,10 @@ build_tlist_to_deparse(RelOptInfo *foreignrel)
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
 
 	/*
-	 * We require columns specified in foreignrel->reltarget.exprs and those
+	 * 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, foreignrel->reltarget->exprs);
 	tlist = add_to_flat_tlist(tlist,
 							  pull_var_clause((Node *) fpinfo->local_conds,
 											  PVC_RECURSE_PLACEHOLDERS));
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index aa745f237e2..d4ee2a8548f 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -481,7 +481,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	 * columns used in them.  Doesn't seem worth detecting that case though.)
 	 */
 	fpinfo->attrs_used = NULL;
-	pull_varattnos((Node *) baserel->reltarget.exprs, baserel->relid,
+	pull_varattnos((Node *) baserel->reltarget->exprs, baserel->relid,
 				   &fpinfo->attrs_used);
 	foreach(lc, fpinfo->local_conds)
 	{
@@ -532,7 +532,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 
 		/* Report estimated baserel size to planner. */
 		baserel->rows = fpinfo->rows;
-		baserel->reltarget.width = fpinfo->width;
+		baserel->reltarget->width = fpinfo->width;
 	}
 	else
 	{
@@ -549,7 +549,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 		{
 			baserel->pages = 10;
 			baserel->tuples =
-				(10 * BLCKSZ) / (baserel->reltarget.width +
+				(10 * BLCKSZ) / (baserel->reltarget->width +
 								 MAXALIGN(SizeofHeapTupleHeader));
 		}
 
@@ -2164,7 +2164,7 @@ estimate_path_cost_size(PlannerInfo *root,
 		 * between foreign relations.
 		 */
 		rows = foreignrel->rows;
-		width = foreignrel->reltarget.width;
+		width = foreignrel->reltarget->width;
 
 		/* Back into an estimate of the number of retrieved rows. */
 		retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel);
@@ -3690,7 +3690,7 @@ postgresGetForeignJoinPaths(PlannerInfo *root,
 							&width, &startup_cost, &total_cost);
 	/* Now update this information in the joinrel */
 	joinrel->rows = rows;
-	joinrel->reltarget.width = width;
+	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 e53458ea43c..bbc9c03721e 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -1173,7 +1173,7 @@ GetForeignServerByName(const char *name, bool missing_ok);
      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
+     <literal>baserel-&gt;reltarget-&gt;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 eb0fc1e121b..548a3b9e57c 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1598,22 +1598,8 @@ _outPathInfo(StringInfo str, const Path *node)
 	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);
-		if (node->pathtarget->sortgrouprefs)
-		{
-			int			i;
-
-			appendStringInfoString(str, " :pathtarget->sortgrouprefs");
-			for (i = 0; i < list_length(node->pathtarget->exprs); i++)
-				appendStringInfo(str, " %u",
-								 node->pathtarget->sortgrouprefs[i]);
-		}
-		WRITE_FLOAT_FIELD(pathtarget->cost.startup, "%.2f");
-		WRITE_FLOAT_FIELD(pathtarget->cost.per_tuple, "%.2f");
-		WRITE_INT_FIELD(pathtarget->width);
-	}
+	if (node->pathtarget != node->parent->reltarget)
+		WRITE_NODE_FIELD(pathtarget);
 	appendStringInfoString(str, " :required_outer ");
 	if (node->param_info)
 		_outBitmapset(str, node->param_info->ppi_req_outer);
@@ -2094,11 +2080,7 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node)
 	WRITE_BOOL_FIELD(consider_startup);
 	WRITE_BOOL_FIELD(consider_param_startup);
 	WRITE_BOOL_FIELD(consider_parallel);
-	WRITE_NODE_FIELD(reltarget.exprs);
-	/* reltarget.sortgrouprefs is never interesting, at present anyway */
-	WRITE_FLOAT_FIELD(reltarget.cost.startup, "%.2f");
-	WRITE_FLOAT_FIELD(reltarget.cost.per_tuple, "%.2f");
-	WRITE_INT_FIELD(reltarget.width);
+	WRITE_NODE_FIELD(reltarget);
 	WRITE_NODE_FIELD(pathlist);
 	WRITE_NODE_FIELD(ppilist);
 	WRITE_NODE_FIELD(partial_pathlist);
@@ -2201,6 +2183,25 @@ _outPathKey(StringInfo str, const PathKey *node)
 	WRITE_BOOL_FIELD(pk_nulls_first);
 }
 
+static void
+_outPathTarget(StringInfo str, const PathTarget *node)
+{
+	WRITE_NODE_TYPE("PATHTARGET");
+
+	WRITE_NODE_FIELD(exprs);
+	if (node->sortgrouprefs)
+	{
+		int			i;
+
+		appendStringInfoString(str, " :sortgrouprefs");
+		for (i = 0; i < list_length(node->exprs); i++)
+			appendStringInfo(str, " %u", node->sortgrouprefs[i]);
+	}
+	WRITE_FLOAT_FIELD(cost.startup, "%.2f");
+	WRITE_FLOAT_FIELD(cost.per_tuple, "%.2f");
+	WRITE_INT_FIELD(width);
+}
+
 static void
 _outParamPathInfo(StringInfo str, const ParamPathInfo *node)
 {
@@ -3612,6 +3613,9 @@ _outNode(StringInfo str, const void *obj)
 			case T_PathKey:
 				_outPathKey(str, obj);
 				break;
+			case T_PathTarget:
+				_outPathTarget(str, obj);
+				break;
 			case T_ParamPathInfo:
 				_outParamPathInfo(str, obj);
 				break;
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 05c51ff07e0..4f60b85861d 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -936,7 +936,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 		/*
 		 * CE failed, so finish copying/modifying targetlist and join quals.
 		 *
-		 * Note: the resulting childrel->reltarget.exprs may contain arbitrary
+		 * 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
@@ -947,9 +947,9 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 			adjust_appendrel_attrs(root,
 								   (Node *) rel->joininfo,
 								   appinfo);
-		childrel->reltarget.exprs = (List *)
+		childrel->reltarget->exprs = (List *)
 			adjust_appendrel_attrs(root,
-								   (Node *) rel->reltarget.exprs,
+								   (Node *) rel->reltarget->exprs,
 								   appinfo);
 
 		/*
@@ -994,7 +994,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 		Assert(childrel->rows > 0);
 
 		parent_rows += childrel->rows;
-		parent_size += childrel->reltarget.width * childrel->rows;
+		parent_size += childrel->reltarget->width * childrel->rows;
 
 		/*
 		 * Accumulate per-column estimates too.  We need not do anything for
@@ -1004,8 +1004,8 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 		 *
 		 * By construction, child's targetlist is 1-to-1 with parent's.
 		 */
-		forboth(parentvars, rel->reltarget.exprs,
-				childvars, childrel->reltarget.exprs)
+		forboth(parentvars, rel->reltarget->exprs,
+				childvars, childrel->reltarget->exprs)
 		{
 			Var		   *parentvar = (Var *) lfirst(parentvars);
 			Node	   *childvar = (Node *) lfirst(childvars);
@@ -1040,7 +1040,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 
 		Assert(parent_rows > 0);
 		rel->rows = parent_rows;
-		rel->reltarget.width = rint(parent_size / 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);
 
@@ -1515,7 +1515,7 @@ set_dummy_rel_pathlist(RelOptInfo *rel)
 {
 	/* Set dummy size estimates --- we leave attr_widths[] as zeroes */
 	rel->rows = 0;
-	rel->reltarget.width = 0;
+	rel->reltarget->width = 0;
 
 	/* Discard any pre-existing paths; no further need for them */
 	rel->pathlist = NIL;
@@ -1771,7 +1771,7 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 		 * not reference the ordinality column, or at least not in any way
 		 * that would be interesting for sorting.
 		 */
-		foreach(lc, rel->reltarget.exprs)
+		foreach(lc, rel->reltarget->exprs)
 		{
 			Var		   *node = (Var *) lfirst(lc);
 
@@ -2717,7 +2717,7 @@ remove_unused_subquery_outputs(Query *subquery, RelOptInfo *rel)
 	 * 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);
+	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)
@@ -3028,7 +3028,7 @@ debug_print_rel(PlannerInfo *root, RelOptInfo *rel)
 
 	printf("RELOPTINFO (");
 	print_relids(rel->relids);
-	printf("): rows=%.0f width=%d\n", rel->rows, rel->reltarget.width);
+	printf("): rows=%.0f width=%d\n", rel->rows, rel->reltarget->width);
 
 	if (rel->baserestrictinfo)
 	{
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 5350329998a..943fcde3b8e 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -4218,7 +4218,7 @@ set_foreign_size_estimates(PlannerInfo *root, RelOptInfo *rel)
  * 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.
+ * 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
@@ -4239,10 +4239,10 @@ set_rel_width(PlannerInfo *root, RelOptInfo *rel)
 	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;
+	rel->reltarget->cost.startup = 0;
+	rel->reltarget->cost.per_tuple = 0;
 
-	foreach(lc, rel->reltarget.exprs)
+	foreach(lc, rel->reltarget->exprs)
 	{
 		Node	   *node = (Node *) lfirst(lc);
 
@@ -4309,7 +4309,7 @@ set_rel_width(PlannerInfo *root, RelOptInfo *rel)
 		{
 			/*
 			 * We will need to evaluate the PHV's contained expression while
-			 * scanning this rel, so be sure to include it in reltarget.cost.
+			 * scanning this rel, so be sure to include it in reltarget->cost.
 			 */
 			PlaceHolderVar *phv = (PlaceHolderVar *) node;
 			PlaceHolderInfo *phinfo = find_placeholder_info(root, phv, false);
@@ -4317,8 +4317,8 @@ set_rel_width(PlannerInfo *root, RelOptInfo *rel)
 
 			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;
+			rel->reltarget->cost.startup += cost.startup;
+			rel->reltarget->cost.per_tuple += cost.per_tuple;
 		}
 		else
 		{
@@ -4335,8 +4335,8 @@ set_rel_width(PlannerInfo *root, RelOptInfo *rel)
 			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;
+			rel->reltarget->cost.startup += cost.startup;
+			rel->reltarget->cost.per_tuple += cost.per_tuple;
 		}
 	}
 
@@ -4373,7 +4373,7 @@ set_rel_width(PlannerInfo *root, RelOptInfo *rel)
 	}
 
 	Assert(tuple_width >= 0);
-	rel->reltarget.width = tuple_width;
+	rel->reltarget->width = tuple_width;
 }
 
 /*
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index ddb4ca50c63..b48f5f28ea9 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -1550,7 +1550,7 @@ bitmap_scan_cost_est(PlannerInfo *root, RelOptInfo *rel, Path *ipath)
 	bpath.path.type = T_BitmapHeapPath;
 	bpath.path.pathtype = T_BitmapHeapScan;
 	bpath.path.parent = rel;
-	bpath.path.pathtarget = &(rel->reltarget);
+	bpath.path.pathtarget = rel->reltarget;
 	bpath.path.param_info = get_baserel_parampathinfo(root, rel,
 													  required_outer);
 	bpath.path.pathkeys = NIL;
@@ -1579,7 +1579,7 @@ bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths)
 	apath.path.type = T_BitmapAndPath;
 	apath.path.pathtype = T_BitmapAnd;
 	apath.path.parent = rel;
-	apath.path.pathtarget = &(rel->reltarget);
+	apath.path.pathtarget = rel->reltarget;
 	apath.path.param_info = NULL;		/* not used in bitmap trees */
 	apath.path.pathkeys = NIL;
 	apath.bitmapquals = paths;
@@ -1592,7 +1592,7 @@ bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths)
 	bpath.path.type = T_BitmapHeapPath;
 	bpath.path.pathtype = T_BitmapHeapScan;
 	bpath.path.parent = rel;
-	bpath.path.pathtarget = &(rel->reltarget);
+	bpath.path.pathtarget = rel->reltarget;
 	bpath.path.param_info = get_baserel_parampathinfo(root, rel,
 													  required_outer);
 	bpath.path.pathkeys = NIL;
@@ -1815,7 +1815,7 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index)
 	 * 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);
+	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 913ac844f09..e37bdfd2cfd 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -3279,7 +3279,7 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
 		 * 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);
+		pull_varattnos((Node *) rel->reltarget->exprs, scan_relid, &attrs_used);
 
 		/* Add all the attributes used by restriction clauses. */
 		foreach(lc, rel->baserestrictinfo)
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 64bc8a8e597..9999eea4991 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -215,8 +215,8 @@ add_vars_to_targetlist(PlannerInfo *root, List *vars,
 			{
 				/* Variable not yet requested, so add to rel's targetlist */
 				/* XXX is copyObject necessary here? */
-				rel->reltarget.exprs = lappend(rel->reltarget.exprs,
-											   copyObject(var));
+				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],
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 443e64e9691..88d7ea45479 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -83,7 +83,7 @@ query_planner(PlannerInfo *root, List *tlist,
 		/* The only path for it is a trivial Result path */
 		add_path(final_rel, (Path *)
 				 create_result_path(root, final_rel,
-									&(final_rel->reltarget),
+									final_rel->reltarget,
 									(List *) parse->jointree->quals));
 
 		/* Select cheapest path (pretty easy in this case...) */
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 8afac0bda73..f3a0a44fd88 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -4534,7 +4534,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid)
 	 * 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);
+	rel->reltarget->width = get_relation_data_width(tableOid, NULL);
 
 	root->total_table_pages = rel->pages;
 
@@ -4550,7 +4550,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid)
 	/* 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,
+			  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/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 6e7980041f7..8f089c59884 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -929,7 +929,7 @@ create_seqscan_path(PlannerInfo *root, RelOptInfo *rel,
 
 	pathnode->pathtype = T_SeqScan;
 	pathnode->parent = rel;
-	pathnode->pathtarget = &(rel->reltarget);
+	pathnode->pathtarget = rel->reltarget;
 	pathnode->param_info = get_baserel_parampathinfo(root, rel,
 													 required_outer);
 	pathnode->parallel_aware = parallel_degree > 0 ? true : false;
@@ -953,7 +953,7 @@ create_samplescan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer
 
 	pathnode->pathtype = T_SampleScan;
 	pathnode->parent = rel;
-	pathnode->pathtarget = &(rel->reltarget);
+	pathnode->pathtarget = rel->reltarget;
 	pathnode->param_info = get_baserel_parampathinfo(root, rel,
 													 required_outer);
 	pathnode->parallel_aware = false;
@@ -1010,7 +1010,7 @@ create_index_path(PlannerInfo *root,
 
 	pathnode->path.pathtype = indexonly ? T_IndexOnlyScan : T_IndexScan;
 	pathnode->path.parent = rel;
-	pathnode->path.pathtarget = &(rel->reltarget);
+	pathnode->path.pathtarget = rel->reltarget;
 	pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
 														  required_outer);
 	pathnode->path.parallel_aware = false;
@@ -1059,7 +1059,7 @@ create_bitmap_heap_path(PlannerInfo *root,
 
 	pathnode->path.pathtype = T_BitmapHeapScan;
 	pathnode->path.parent = rel;
-	pathnode->path.pathtarget = &(rel->reltarget);
+	pathnode->path.pathtarget = rel->reltarget;
 	pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
 														  required_outer);
 	pathnode->path.parallel_aware = false;
@@ -1089,7 +1089,7 @@ create_bitmap_and_path(PlannerInfo *root,
 
 	pathnode->path.pathtype = T_BitmapAnd;
 	pathnode->path.parent = rel;
-	pathnode->path.pathtarget = &(rel->reltarget);
+	pathnode->path.pathtarget = rel->reltarget;
 	pathnode->path.param_info = NULL;	/* not used in bitmap trees */
 
 	/*
@@ -1125,7 +1125,7 @@ create_bitmap_or_path(PlannerInfo *root,
 
 	pathnode->path.pathtype = T_BitmapOr;
 	pathnode->path.parent = rel;
-	pathnode->path.pathtarget = &(rel->reltarget);
+	pathnode->path.pathtarget = rel->reltarget;
 	pathnode->path.param_info = NULL;	/* not used in bitmap trees */
 
 	/*
@@ -1160,7 +1160,7 @@ create_tidscan_path(PlannerInfo *root, RelOptInfo *rel, List *tidquals,
 
 	pathnode->path.pathtype = T_TidScan;
 	pathnode->path.parent = rel;
-	pathnode->path.pathtarget = &(rel->reltarget);
+	pathnode->path.pathtarget = rel->reltarget;
 	pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
 														  required_outer);
 	pathnode->path.parallel_aware = false;
@@ -1192,7 +1192,7 @@ create_append_path(RelOptInfo *rel, List *subpaths, Relids required_outer,
 
 	pathnode->path.pathtype = T_Append;
 	pathnode->path.parent = rel;
-	pathnode->path.pathtarget = &(rel->reltarget);
+	pathnode->path.pathtarget = rel->reltarget;
 	pathnode->path.param_info = get_appendrel_parampathinfo(rel,
 															required_outer);
 	pathnode->path.parallel_aware = false;
@@ -1251,7 +1251,7 @@ create_merge_append_path(PlannerInfo *root,
 
 	pathnode->path.pathtype = T_MergeAppend;
 	pathnode->path.parent = rel;
-	pathnode->path.pathtarget = &(rel->reltarget);
+	pathnode->path.pathtarget = rel->reltarget;
 	pathnode->path.param_info = get_appendrel_parampathinfo(rel,
 															required_outer);
 	pathnode->path.parallel_aware = false;
@@ -1375,7 +1375,7 @@ create_material_path(RelOptInfo *rel, Path *subpath)
 
 	pathnode->path.pathtype = T_Material;
 	pathnode->path.parent = rel;
-	pathnode->path.pathtarget = &(rel->reltarget);
+	pathnode->path.pathtarget = rel->reltarget;
 	pathnode->path.param_info = subpath->param_info;
 	pathnode->path.parallel_aware = false;
 	pathnode->path.parallel_safe = rel->consider_parallel &&
@@ -1440,7 +1440,7 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
 
 	pathnode->path.pathtype = T_Unique;
 	pathnode->path.parent = rel;
-	pathnode->path.pathtarget = &(rel->reltarget);
+	pathnode->path.pathtarget = rel->reltarget;
 	pathnode->path.param_info = subpath->param_info;
 	pathnode->path.parallel_aware = false;
 	pathnode->path.parallel_safe = rel->consider_parallel &&
@@ -1656,7 +1656,7 @@ create_gather_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
 
 	pathnode->path.pathtype = T_Gather;
 	pathnode->path.parent = rel;
-	pathnode->path.pathtarget = &(rel->reltarget);
+	pathnode->path.pathtarget = rel->reltarget;
 	pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
 														  required_outer);
 	pathnode->path.parallel_aware = false;
@@ -1692,7 +1692,7 @@ create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
 
 	pathnode->path.pathtype = T_SubqueryScan;
 	pathnode->path.parent = rel;
-	pathnode->path.pathtarget = &(rel->reltarget);
+	pathnode->path.pathtarget = rel->reltarget;
 	pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
 														  required_outer);
 	pathnode->path.parallel_aware = false;
@@ -1720,7 +1720,7 @@ create_functionscan_path(PlannerInfo *root, RelOptInfo *rel,
 
 	pathnode->pathtype = T_FunctionScan;
 	pathnode->parent = rel;
-	pathnode->pathtarget = &(rel->reltarget);
+	pathnode->pathtarget = rel->reltarget;
 	pathnode->param_info = get_baserel_parampathinfo(root, rel,
 													 required_outer);
 	pathnode->parallel_aware = false;
@@ -1746,7 +1746,7 @@ create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel,
 
 	pathnode->pathtype = T_ValuesScan;
 	pathnode->parent = rel;
-	pathnode->pathtarget = &(rel->reltarget);
+	pathnode->pathtarget = rel->reltarget;
 	pathnode->param_info = get_baserel_parampathinfo(root, rel,
 													 required_outer);
 	pathnode->parallel_aware = false;
@@ -1771,7 +1771,7 @@ create_ctescan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer)
 
 	pathnode->pathtype = T_CteScan;
 	pathnode->parent = rel;
-	pathnode->pathtarget = &(rel->reltarget);
+	pathnode->pathtarget = rel->reltarget;
 	pathnode->param_info = get_baserel_parampathinfo(root, rel,
 													 required_outer);
 	pathnode->parallel_aware = false;
@@ -1797,7 +1797,7 @@ create_worktablescan_path(PlannerInfo *root, RelOptInfo *rel,
 
 	pathnode->pathtype = T_WorkTableScan;
 	pathnode->parent = rel;
-	pathnode->pathtarget = &(rel->reltarget);
+	pathnode->pathtarget = rel->reltarget;
 	pathnode->param_info = get_baserel_parampathinfo(root, rel,
 													 required_outer);
 	pathnode->parallel_aware = false;
@@ -1833,7 +1833,7 @@ create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
 
 	pathnode->path.pathtype = T_ForeignScan;
 	pathnode->path.parent = rel;
-	pathnode->path.pathtarget = &(rel->reltarget);
+	pathnode->path.pathtarget = rel->reltarget;
 	pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
 														  required_outer);
 	pathnode->path.parallel_aware = false;
@@ -1966,7 +1966,7 @@ create_nestloop_path(PlannerInfo *root,
 
 	pathnode->path.pathtype = T_NestLoop;
 	pathnode->path.parent = joinrel;
-	pathnode->path.pathtarget = &(joinrel->reltarget);
+	pathnode->path.pathtarget = joinrel->reltarget;
 	pathnode->path.param_info =
 		get_joinrel_parampathinfo(root,
 								  joinrel,
@@ -2029,7 +2029,7 @@ create_mergejoin_path(PlannerInfo *root,
 
 	pathnode->jpath.path.pathtype = T_MergeJoin;
 	pathnode->jpath.path.parent = joinrel;
-	pathnode->jpath.path.pathtarget = &(joinrel->reltarget);
+	pathnode->jpath.path.pathtarget = joinrel->reltarget;
 	pathnode->jpath.path.param_info =
 		get_joinrel_parampathinfo(root,
 								  joinrel,
@@ -2091,7 +2091,7 @@ create_hashjoin_path(PlannerInfo *root,
 
 	pathnode->jpath.path.pathtype = T_HashJoin;
 	pathnode->jpath.path.parent = joinrel;
-	pathnode->jpath.path.pathtarget = &(joinrel->reltarget);
+	pathnode->jpath.path.pathtarget = joinrel->reltarget;
 	pathnode->jpath.path.param_info =
 		get_joinrel_parampathinfo(root,
 								  joinrel,
@@ -2876,7 +2876,7 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel,
 	pathnode->path.pathtype = T_ModifyTable;
 	pathnode->path.parent = rel;
 	/* pathtarget is not interesting, just make it minimally valid */
-	pathnode->path.pathtarget = &(rel->reltarget);
+	pathnode->path.pathtarget = rel->reltarget;
 	/* For now, assume we are above any joins, so no parameterization */
 	pathnode->path.param_info = NULL;
 	pathnode->path.parallel_aware = false;
diff --git a/src/backend/optimizer/util/placeholder.c b/src/backend/optimizer/util/placeholder.c
index 9e1c0e53333..b210914b853 100644
--- a/src/backend/optimizer/util/placeholder.c
+++ b/src/backend/optimizer/util/placeholder.c
@@ -391,8 +391,8 @@ add_placeholders_to_base_rels(PlannerInfo *root)
 		{
 			RelOptInfo *rel = find_base_rel(root, varno);
 
-			rel->reltarget.exprs = lappend(rel->reltarget.exprs,
-										   copyObject(phinfo->ph_var));
+			rel->reltarget->exprs = lappend(rel->reltarget->exprs,
+											copyObject(phinfo->ph_var));
 			/* reltarget's cost and width fields will be updated later */
 		}
 	}
@@ -425,9 +425,9 @@ add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
 			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;
+				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
@@ -447,8 +447,8 @@ add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
 
 					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;
+					joinrel->reltarget->cost.startup += cost.startup;
+					joinrel->reltarget->cost.per_tuple += cost.per_tuple;
 				}
 
 				/* Adjust joinrel's direct_lateral_relids as needed */
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 763d39d1429..20e4bf7d125 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -24,6 +24,7 @@
 #include "optimizer/placeholder.h"
 #include "optimizer/plancat.h"
 #include "optimizer/restrictinfo.h"
+#include "optimizer/tlist.h"
 #include "utils/hsearch.h"
 
 
@@ -106,11 +107,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
 	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.sortgrouprefs = NULL;
-	rel->reltarget.cost.startup = 0;
-	rel->reltarget.cost.per_tuple = 0;
-	rel->reltarget.width = 0;
+	rel->reltarget = create_empty_pathtarget();
 	rel->pathlist = NIL;
 	rel->ppilist = NIL;
 	rel->partial_pathlist = NIL;
@@ -393,11 +390,7 @@ build_join_rel(PlannerInfo *root,
 	joinrel->consider_startup = (root->tuple_fraction > 0);
 	joinrel->consider_param_startup = false;
 	joinrel->consider_parallel = false;
-	joinrel->reltarget.exprs = NIL;
-	joinrel->reltarget.sortgrouprefs = NULL;
-	joinrel->reltarget.cost.startup = 0;
-	joinrel->reltarget.cost.per_tuple = 0;
-	joinrel->reltarget.width = 0;
+	joinrel->reltarget = create_empty_pathtarget();
 	joinrel->pathlist = NIL;
 	joinrel->ppilist = NIL;
 	joinrel->partial_pathlist = NIL;
@@ -613,7 +606,7 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
 	Relids		relids = joinrel->relids;
 	ListCell   *vars;
 
-	foreach(vars, input_rel->reltarget.exprs)
+	foreach(vars, input_rel->reltarget->exprs)
 	{
 		Var		   *var = (Var *) lfirst(vars);
 		RelOptInfo *baserel;
@@ -643,9 +636,9 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *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 */
-			joinrel->reltarget.width += baserel->attr_widths[ndx];
+			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];
 		}
 	}
 }
@@ -832,6 +825,7 @@ build_empty_join_rel(PlannerInfo *root)
 	joinrel->relids = NULL;		/* empty set */
 	joinrel->rows = 1;			/* we produce one row for such cases */
 	joinrel->rtekind = RTE_JOIN;
+	joinrel->reltarget = create_empty_pathtarget();
 
 	root->join_rel_list = lappend(root->join_rel_list, joinrel);
 
@@ -882,6 +876,7 @@ fetch_upper_rel(PlannerInfo *root, UpperRelationKind kind, Relids relids)
 	upperrel->consider_startup = (root->tuple_fraction > 0);
 	upperrel->consider_param_startup = false;
 	upperrel->consider_parallel = false;		/* might get changed later */
+	upperrel->reltarget = create_empty_pathtarget();
 	upperrel->pathlist = NIL;
 	upperrel->cheapest_startup_path = NULL;
 	upperrel->cheapest_total_path = NULL;
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
index 9f85dee3872..b297d87e7ec 100644
--- a/src/backend/optimizer/util/tlist.c
+++ b/src/backend/optimizer/util/tlist.c
@@ -546,7 +546,7 @@ grouping_is_hashable(List *groupClause)
 PathTarget *
 make_pathtarget_from_tlist(List *tlist)
 {
-	PathTarget *target = (PathTarget *) palloc0(sizeof(PathTarget));
+	PathTarget *target = makeNode(PathTarget);
 	int			i;
 	ListCell   *lc;
 
@@ -606,7 +606,7 @@ make_tlist_from_pathtarget(PathTarget *target)
 PathTarget *
 copy_pathtarget(PathTarget *src)
 {
-	PathTarget *dst = (PathTarget *) palloc(sizeof(PathTarget));
+	PathTarget *dst = makeNode(PathTarget);
 
 	/* Copy scalar fields */
 	memcpy(dst, src, sizeof(PathTarget));
@@ -631,7 +631,7 @@ PathTarget *
 create_empty_pathtarget(void)
 {
 	/* This is easy, but we don't want callers to hard-wire this ... */
-	return (PathTarget *) palloc0(sizeof(PathTarget));
+	return makeNode(PathTarget);
 }
 
 /*
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index fad9988119f..42c958258b6 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -259,6 +259,7 @@ typedef enum NodeTag
 	T_EquivalenceClass,
 	T_EquivalenceMember,
 	T_PathKey,
+	T_PathTarget,
 	T_RestrictInfo,
 	T_PlaceHolderVar,
 	T_SpecialJoinInfo,
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index bdea72c3f47..b48a6183dc2 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -61,34 +61,6 @@ typedef struct AggClauseCosts
 	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.
- *
- * exprs contains bare expressions; they do not have TargetEntry nodes on top,
- * though those will appear in finished Plans.
- *
- * sortgrouprefs[] is an array of the same length as exprs, containing the
- * corresponding sort/group refnos, or zeroes for expressions not referenced
- * by sort/group clauses.  If sortgrouprefs is NULL (which it always is in
- * RelOptInfo.reltarget structs; only upper-level Paths contain this info), we
- * have not identified sort/group columns in this tlist.  This allows us to
- * deal with sort/group refnos when needed with less expense than including
- * TargetEntry nodes in the exprs list.
- */
-typedef struct PathTarget
-{
-	List	   *exprs;			/* list of expressions to be computed */
-	Index	   *sortgrouprefs;	/* corresponding sort/group refnos, or 0 */
-	QualCost	cost;			/* cost of evaluating the expressions */
-	int			width;			/* estimated avg width of result tuples */
-} PathTarget;
-
 /*
  * This enum identifies the different types of "upper" (post-scan/join)
  * relations that we might deal with during planning.
@@ -514,7 +486,7 @@ typedef struct RelOptInfo
 	bool		consider_parallel;		/* consider parallel paths? */
 
 	/* default result targetlist for Paths scanning this relation */
-	PathTarget	reltarget;		/* list of Vars/Exprs, cost, width */
+	struct PathTarget *reltarget;		/* list of Vars/Exprs, cost, width */
 
 	/* materialization information */
 	List	   *pathlist;		/* Path structures */
@@ -765,6 +737,39 @@ typedef struct PathKey
 } PathKey;
 
 
+/*
+ * PathTarget
+ *
+ * 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 simply
+ * reference.  However, in some cases a Path may compute outputs different
+ * from other Paths, and in that case we make a custom PathTarget for it.
+ * For example, an indexscan might return index expressions that would
+ * otherwise need to be explicitly calculated.  (Note also that "upper"
+ * relations generally don't have useful default PathTargets.)
+ *
+ * exprs contains bare expressions; they do not have TargetEntry nodes on top,
+ * though those will appear in finished Plans.
+ *
+ * sortgrouprefs[] is an array of the same length as exprs, containing the
+ * corresponding sort/group refnos, or zeroes for expressions not referenced
+ * by sort/group clauses.  If sortgrouprefs is NULL (which it generally is in
+ * RelOptInfo.reltarget targets; only upper-level Paths contain this info),
+ * we have not identified sort/group columns in this tlist.  This allows us to
+ * deal with sort/group refnos when needed with less expense than including
+ * TargetEntry nodes in the exprs list.
+ */
+typedef struct PathTarget
+{
+	NodeTag		type;
+	List	   *exprs;			/* list of expressions to be computed */
+	Index	   *sortgrouprefs;	/* corresponding sort/group refnos, or 0 */
+	QualCost	cost;			/* cost of evaluating the expressions */
+	int			width;			/* estimated avg width of result tuples */
+} PathTarget;
+
+
 /*
  * ParamPathInfo
  *
@@ -802,7 +807,7 @@ typedef struct ParamPathInfo
  * "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.
+ * which we represent by having path->pathtarget equal 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.
-- 
GitLab