commit 31193a9040801965409ff608e222fa5b899d9721
Author: Ronan Dunklau <ronan.dunklau@aiven.io>
Date:   Tue Jul 20 11:03:27 2021 +0200

    Tag and remove resjunk added for SortGroupClause.
    
    We can provide better plans if we remove the resjunk columns from
    the final target. For example, index scan may not need to fetch /
    compute the key column.
    
    To achieve that without intruding everywhere, we tag the resjunk
    TargetEntries with why they were added, and keep them in the PlannerInfo
    processed_tlist until it's time to compute the plan's finaltarget.
    
    This allows us to properly differentiate sort_input_target and the
    finaltarget, only adding the needed column for nodes which actually need
    them to perform the sort (explicit sorts, aggregates, gathermerge...)
    
    One drawback is that this forces us to add many projection plans on top
    of the sort node to finally get the final target.
    
    This can be alleviated in a later patch by teaching indivual
    non-projection-capable (for example, sort) to perform a limited form of
    projection using the JunkFilter infrastructure.

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index e81b990092..e89e2ac120 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -2323,8 +2323,7 @@ static void
 show_sort_keys(SortState *sortstate, List *ancestors, ExplainState *es)
 {
 	Sort	   *plan = (Sort *) sortstate->ss.ps.plan;
-
-	show_sort_group_keys((PlanState *) sortstate, "Sort Key",
+	show_sort_group_keys((PlanState *) outerPlanState(sortstate), "Sort Key",
 						 plan->numCols, 0, plan->sortColIdx,
 						 plan->sortOperators, plan->collations,
 						 plan->nullsFirst,
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 01c110cd2f..87274193be 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -238,7 +238,7 @@ TargetEntry *
 makeTargetEntry(Expr *expr,
 				AttrNumber resno,
 				char *resname,
-				bool resjunk)
+				uint16 resjunk)
 {
 	TargetEntry *tle = makeNode(TargetEntry);
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index c13da7a879..2722f9e35b 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -2119,6 +2119,11 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
 								   best_path->path.parent->relids : NULL);
 
 	copy_generic_path_info(&plan->plan, (Path *) best_path);
+	/*
+	 * XXX: This should probably be passed as an arg to make_sort_from_pathkeys
+	 * ?
+	 */
+	plan->plan.targetlist = make_tlist_from_pathtarget(best_path->path.pathtarget);
 
 	return plan;
 }
@@ -4233,13 +4238,13 @@ create_nestloop_plan(PlannerInfo *root,
 	Relids		saveOuterRels = root->curOuterRels;
 
 	/* NestLoop can project, so no need to be picky about child tlists */
-	outer_plan = create_plan_recurse(root, best_path->outerjoinpath, 0);
+	outer_plan = create_plan_recurse(root, best_path->outerjoinpath, CP_EXACT_TLIST);
 
 	/* For a nestloop, include outer relids in curOuterRels for inner side */
 	root->curOuterRels = bms_union(root->curOuterRels,
 								   best_path->outerjoinpath->parent->relids);
 
-	inner_plan = create_plan_recurse(root, best_path->innerjoinpath, 0);
+	inner_plan = create_plan_recurse(root, best_path->innerjoinpath, CP_EXACT_TLIST);
 
 	/* Restore curOuterRels */
 	bms_free(root->curOuterRels);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 1868c4eff4..bf0ccf155c 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -195,19 +195,23 @@ static RelOptInfo *create_ordered_paths(PlannerInfo *root,
 										bool target_parallel_safe,
 										double limit_tuples);
 static PathTarget *make_group_input_target(PlannerInfo *root,
-										   PathTarget *final_target);
+										   PathTarget *final_target,
+										   List *sortGroupTargetEntries);
 static PathTarget *make_partial_grouping_target(PlannerInfo *root,
 												PathTarget *grouping_target,
+												PathTarget *input_target,
 												Node *havingQual);
 static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
 static List *select_active_windows(PlannerInfo *root, WindowFuncLists *wflists);
 static PathTarget *make_window_input_target(PlannerInfo *root,
 											PathTarget *final_target,
+											List *sortGroupTargetEntries,
 											List *activeWindows);
 static List *make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc,
 									  List *tlist);
 static PathTarget *make_sort_input_target(PlannerInfo *root,
 										  PathTarget *final_target,
+										  List *sortGroupTargetEntries,
 										  bool *have_postponed_srfs);
 static void adjust_paths_for_srfs(PlannerInfo *root, RelOptInfo *rel,
 								  List *targets, List *targets_contain_srfs);
@@ -1345,6 +1349,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 		List	   *activeWindows = NIL;
 		grouping_sets_data *gset_data = NULL;
 		standard_qp_extra qp_extra;
+		List	   *filteredFinalTlist = NIL;
+		List	   *sortGroupTargetEntries = NIL;
 
 		/* A recursive query should always have setOperations */
 		Assert(!root->hasRecursion);
@@ -1449,9 +1455,19 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 		 * because the target width estimates can use per-Var width numbers
 		 * that were obtained within query_planner().
 		 */
-		final_target = create_pathtarget(root, root->processed_tlist);
+		foreach(lc, root->processed_tlist)
+		{
+			TargetEntry *tle = castNode(TargetEntry, lfirst(lc));
+
+			if (!TargetEntryIsSortGroupClauseResjunk(tle))
+				filteredFinalTlist = lappend(filteredFinalTlist, tle);
+			else
+				sortGroupTargetEntries = lappend(sortGroupTargetEntries, tle);
+		}
+		final_target = create_pathtarget(root, filteredFinalTlist);
 		final_target_parallel_safe =
 			is_parallel_safe(root, (Node *) final_target->exprs);
+		root->processed_tlist = filteredFinalTlist;
 
 		/*
 		 * If ORDER BY was given, consider whether we should use a post-sort
@@ -1462,6 +1478,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 		{
 			sort_input_target = make_sort_input_target(root,
 													   final_target,
+													   sortGroupTargetEntries,
 													   &have_postponed_srfs);
 			sort_input_target_parallel_safe =
 				is_parallel_safe(root, (Node *) sort_input_target->exprs);
@@ -1481,6 +1498,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 		{
 			grouping_target = make_window_input_target(root,
 													   final_target,
+													   sortGroupTargetEntries,
 													   activeWindows);
 			grouping_target_parallel_safe =
 				is_parallel_safe(root, (Node *) grouping_target->exprs);
@@ -1500,7 +1518,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 						 parse->hasAggs || root->hasHavingQual);
 		if (have_grouping)
 		{
-			scanjoin_target = make_group_input_target(root, final_target);
+			scanjoin_target = make_group_input_target(root, final_target,
+													  sortGroupTargetEntries);
 			scanjoin_target_parallel_safe =
 				is_parallel_safe(root, (Node *) scanjoin_target->exprs);
 		}
@@ -4119,6 +4138,7 @@ create_one_window_path(PlannerInfo *root,
 					   List *activeWindows)
 {
 	PathTarget *window_target;
+	List *targets;
 	ListCell   *l;
 
 	/*
@@ -4137,6 +4157,7 @@ create_one_window_path(PlannerInfo *root,
 	 * for the WindowFuncs computed at each level.
 	 */
 	window_target = input_target;
+	targets = make_tlist_from_pathtarget(input_target);
 
 	foreach(l, activeWindows)
 	{
@@ -4147,7 +4168,7 @@ create_one_window_path(PlannerInfo *root,
 
 		window_pathkeys = make_pathkeys_for_window(root,
 												   wc,
-												   root->processed_tlist);
+												   targets);
 
 		is_sorted = pathkeys_count_contained_in(window_pathkeys,
 												path->pathkeys,
@@ -4701,7 +4722,7 @@ create_ordered_paths(PlannerInfo *root,
  * query_planner().
  */
 static PathTarget *
-make_group_input_target(PlannerInfo *root, PathTarget *final_target)
+make_group_input_target(PlannerInfo *root, PathTarget *final_target, List *sortGroupTargetEntries)
 {
 	Query	   *parse = root->parse;
 	PathTarget *input_target;
@@ -4743,6 +4764,17 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
 		i++;
 	}
 
+	/* Look if we have to add more entries from the resjunk columns */
+	foreach(lc, sortGroupTargetEntries)
+	{
+		TargetEntry *entry = castNode(TargetEntry, lfirst(lc));
+
+		if (entry->ressortgroupref && get_sortgroupref_clause_noerr(entry->ressortgroupref, parse->groupClause) != NULL)
+		{
+			add_column_to_pathtarget(input_target, entry->expr, entry->ressortgroupref);
+		}
+	}
+
 	/*
 	 * If there's a HAVING clause, we'll need the Vars it uses, too.
 	 */
@@ -4784,12 +4816,16 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
  * used outside of Aggrefs in the aggregation tlist and HAVING.  (Presumably,
  * these would be Vars that are grouped by or used in grouping expressions.)
  *
+ * We also need to preseve every group column not already there, similar to
+ * make_group_input_target.
+ *
  * grouping_target is the tlist to be emitted by the topmost aggregation step.
  * havingQual represents the HAVING clause.
  */
 static PathTarget *
 make_partial_grouping_target(PlannerInfo *root,
 							 PathTarget *grouping_target,
+							 PathTarget *input_target,
 							 Node *havingQual)
 {
 	Query	   *parse = root->parse;
@@ -4829,6 +4865,28 @@ make_partial_grouping_target(PlannerInfo *root,
 		i++;
 	}
 
+	/*
+	 * Now scan the input target for grouping columns. Here, we don't care
+	 * about non-grouping columns because they will already be in
+	 * grouping_target if they are needed.
+	 */
+	i = 0;
+	foreach(lc, input_target->exprs)
+	{
+		Expr	   *expr = (Expr *) lfirst(lc);
+		Index		sgref = get_pathtarget_sortgroupref(input_target, i);
+
+		if (sgref && parse->groupClause &&
+			get_sortgroupref_clause_noerr(sgref, parse->groupClause) != NULL)
+		{
+			/*
+			 * It's a grouping column, so add it to the partial_target as-is.
+			 * (This allows the upper agg step to repeat the grouping calcs.)
+			 */
+			add_column_to_pathtarget(partial_target, expr, sgref);
+		}
+	}
+
 	/*
 	 * If there's a HAVING clause, we'll need the Vars/Aggrefs it uses, too.
 	 */
@@ -5110,6 +5168,7 @@ common_prefix_cmp(const void *a, const void *b)
 static PathTarget *
 make_window_input_target(PlannerInfo *root,
 						 PathTarget *final_target,
+						 List *sortGroupTargetEntries,
 						 List *activeWindows)
 {
 	Query	   *parse = root->parse;
@@ -5192,6 +5251,21 @@ make_window_input_target(PlannerInfo *root,
 		i++;
 	}
 
+	/*
+	 * Now look into targetentries that are not really part of the final rel
+	 */
+	foreach(lc, sortGroupTargetEntries)
+	{
+		TargetEntry *entry = castNode(TargetEntry, lfirst(lc));
+		Index		sgref = entry->ressortgroupref;
+
+		if (sgref != 0 && bms_is_member(sgref, sgrefs))
+		{
+			add_column_to_pathtarget(input_target, entry->expr, sgref);
+		}
+	}
+
+
 	/*
 	 * Pull out all the Vars and Aggrefs mentioned in flattenable columns, and
 	 * add them to the input target if not already present.  (Some might be
@@ -5322,6 +5396,7 @@ make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc,
 static PathTarget *
 make_sort_input_target(PlannerInfo *root,
 					   PathTarget *final_target,
+					   List *sortGroupTargetEntries,
 					   bool *have_postponed_srfs)
 {
 	Query	   *parse = root->parse;
@@ -5427,7 +5502,8 @@ make_sort_input_target(PlannerInfo *root,
 	 */
 	if (!(postpone_srfs || have_volatile ||
 		  (have_expensive &&
-		   (parse->limitCount || root->tuple_fraction > 0))))
+		   (parse->limitCount || root->tuple_fraction > 0)) ||
+		  (list_length(sortGroupTargetEntries) > 0)))
 		return final_target;
 
 	/*
@@ -5460,6 +5536,14 @@ make_sort_input_target(PlannerInfo *root,
 		i++;
 	}
 
+	/* Now also add the missing sort columns */
+	foreach(lc, sortGroupTargetEntries)
+	{
+		TargetEntry *entry = castNode(TargetEntry, lfirst(lc));
+
+		add_column_to_pathtarget(input_target, entry->expr, entry->ressortgroupref);
+	}
+
 	/*
 	 * Pull out all the Vars, Aggrefs, and WindowFuncs mentioned in
 	 * postponable columns, and add them to the sort-input target if not
@@ -6396,6 +6480,7 @@ create_partial_grouping_paths(PlannerInfo *root,
 	 */
 	partially_grouped_rel->reltarget =
 		make_partial_grouping_target(root, grouped_rel->reltarget,
+									 input_rel->reltarget,
 									 extra->havingQual);
 
 	if (!extra->partial_costs_set)
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 9ce5f95e3b..f864364edb 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -2711,6 +2711,7 @@ create_projection_path(PlannerInfo *root,
 	return pathnode;
 }
 
+
 /*
  * apply_projection_to_path
  *	  Add a projection step, or just apply the target directly to given path.
@@ -2791,12 +2792,27 @@ apply_projection_to_path(PlannerInfo *root,
 		else
 		{
 			GatherMergePath *gmpath = (GatherMergePath *) path;
-
+			ListCell *lc;
+			List *sp_tlist = make_tlist_from_pathtarget(target);
+			List *old_tlist = make_tlist_from_pathtarget(gmpath->subpath->pathtarget);
+			PathTarget *sp_target;
+			/* In the GatherMerge path, we must be careful to not discard the
+			 * pathkeys from the subpath.
+			 * XXX: maybe this should be computed from the pathkeys directly
+			 * instead of relying on ressortgroupref ?
+			 */
+			foreach(lc, old_tlist)
+			{
+				TargetEntry * tle = lfirst_node(TargetEntry, lc);
+				if (tle->ressortgroupref && !tlist_member(tle->expr, sp_tlist))
+					sp_tlist = lappend(sp_tlist, tle);
+			}
+			sp_target = make_pathtarget_from_tlist(sp_tlist);
 			gmpath->subpath = (Path *)
 				create_projection_path(root,
 									   gmpath->subpath->parent,
 									   gmpath->subpath,
-									   target);
+									   sp_target);
 		}
 	}
 	else if (path->parallel_safe &&
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index c5194fdbbf..eb08802939 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1695,16 +1695,19 @@ build_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
 				TargetEntry *tle = (TargetEntry *) lfirst(l);
 
 				/*
-				 * A resjunk column of the subquery can be reflected as
-				 * resjunk in the physical tlist; we need not punt.
+				 * We exclude resjunk columns, as we don't want
+				 * to have to deal with them in upper nodes.
 				 */
-				var = makeVarFromTargetEntry(varno, tle);
+				if (!TargetEntryIsResjunk(tle))
+				{
+					var = makeVarFromTargetEntry(varno, tle);
 
-				tlist = lappend(tlist,
-								makeTargetEntry((Expr *) var,
-												tle->resno,
-												NULL,
-												tle->resjunk));
+					tlist = lappend(tlist,
+									makeTargetEntry((Expr *) var,
+													tle->resno,
+													NULL,
+													tle->resjunk));
+				}
 			}
 			break;
 
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
index 311579d059..178316b388 100644
--- a/src/backend/optimizer/util/tlist.c
+++ b/src/backend/optimizer/util/tlist.c
@@ -662,6 +662,37 @@ copy_pathtarget(PathTarget *src)
 	return dst;
 }
 
+/*
+ * Check wether a tlist is a subset of another tlist.
+ * That subset can be in a different order, or not.
+ */
+bool
+pathtarget_is_subset(PathTarget *target1, PathTarget *target2)
+{
+	ListCell   *lc1;
+
+	foreach(lc1, target1->exprs)
+	{
+		Expr *expr1 = (Expr*) lfirst(lc1);
+		ListCell   *lc2;
+		bool            found = false;
+
+		foreach(lc2, target2->exprs)
+		{
+			Expr *expr2 = (Expr*) lfirst(lc2);
+
+			if (equal(expr1, expr2))
+			{
+				found = true;
+				break;
+			}
+		}
+		if (!found)
+			return false;
+	}
+	return true;
+}
+
 /*
  * create_empty_pathtarget
  *	  Create an empty (zero columns, zero cost) PathTarget.
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 71c360bea5..18b422e22c 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -2096,7 +2096,7 @@ findTargetlistEntrySQL99(ParseState *pstate, Node *node, List **tlist,
 	 * will not be projected into the final tuple.
 	 */
 	target_result = transformTargetEntry(pstate, node, expr, exprKind,
-										 NULL, true);
+										 NULL, TARGET_ENTRY_SORTGROUP_CLAUSE);
 
 	*tlist = lappend(*tlist, target_result);
 
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 6e8fbc4780..08fbc07f53 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -79,7 +79,7 @@ transformTargetEntry(ParseState *pstate,
 					 Node *expr,
 					 ParseExprKind exprKind,
 					 char *colname,
-					 bool resjunk)
+					 uint16 resjunk)
 {
 	/* Transform the node if caller didn't do it already */
 	if (expr == NULL)
@@ -1964,3 +1964,34 @@ FigureColnameInternal(Node *node, char **name)
 
 	return strength;
 }
+
+/*
+ * Check wether a tlist is a subset of another tlist.
+ * That subset can be in a different order, or not.
+ */
+bool
+tlist_is_subset(List *tlist1, List *tlist2)
+{
+	ListCell   *lc1;
+
+	foreach(lc1, tlist1)
+	{
+		TargetEntry *tle1 = lfirst_node(TargetEntry, lc1);
+		ListCell   *lc2;
+		bool		found = false;
+
+		foreach(lc2, tlist2)
+		{
+			TargetEntry *tle2 = lfirst_node(TargetEntry, lc2);
+
+			if (equal(tle1->expr, tle2->expr))
+			{
+				found = true;
+				break;
+			}
+		}
+		if (!found)
+			return false;
+	}
+	return true;
+}
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index 48a7ebfe45..8b32b00914 100644
--- a/src/include/nodes/makefuncs.h
+++ b/src/include/nodes/makefuncs.h
@@ -42,7 +42,7 @@ extern Var *makeWholeRowVar(RangeTblEntry *rte,
 extern TargetEntry *makeTargetEntry(Expr *expr,
 									AttrNumber resno,
 									char *resname,
-									bool resjunk);
+									uint16 resjunk);
 
 extern TargetEntry *flatCopyTargetEntry(TargetEntry *src_tle);
 
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 996c3e4016..3ad4b8a946 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1441,13 +1441,18 @@ typedef struct InferenceElem
  * simple reference to a column of a base table (or view).  If it is not
  * a simple reference, these fields are zeroes.
  *
- * If resjunk is true then the column is a working column (such as a sort key)
- * that should be removed from the final output of the query.  Resjunk columns
- * must have resnos that cannot duplicate any regular column's resno.  Also
- * note that there are places that assume resjunk columns come after non-junk
+ * If resjunk is different from 0 then the column is a working column
+ * (such as a sort key) that should be removed from the final output of the query.
+ * Resjunk columns must have resnos that cannot duplicate any regular column's resno.
+ * When different from zero the value is a flag indicating the resjunk's source
+ * Also note that there are places that assume resjunk columns come after non-junk
  * columns.
  *--------------------
  */
+
+#define TARGET_ENTRY_REGULAR 			0x0000
+#define TARGET_ENTRY_SORTGROUP_CLAUSE 	0x0002
+
 typedef struct TargetEntry
 {
 	Expr		xpr;
@@ -1458,10 +1463,24 @@ typedef struct TargetEntry
 									 * clause */
 	Oid			resorigtbl;		/* OID of column's source table */
 	AttrNumber	resorigcol;		/* column's number in source table */
-	bool		resjunk;		/* set to true to eliminate the attribute from
-								 * final target list */
+	uint16         resjunk;		/* Flag indicating the resjunk source, 0 if not a resjunk */
 } TargetEntry;
 
+#define TargetEntryIsResjunk(tle) \
+( \
+  (tle)->resjunk != TARGET_ENTRY_REGULAR \
+)
+
+#define TargetEntryIsSortGroupClauseResjunk(tle) \
+( \
+  ((tle)->resjunk & TARGET_ENTRY_SORTGROUP_CLAUSE) != 0 \
+)
+
+#define TargetEntrySetSortGroupClauseResjunk(tle) \
+( \
+  (tle)->resjunk |= TARGET_ENTRY_SORTGROUP_CLAUSE \
+)
+
 
 /* ----------------------------------------------------------------
  *					node types for join trees
diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index d62a09665a..42f27a4a7d 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.h
@@ -24,6 +24,7 @@ extern List *add_to_flat_tlist(List *tlist, List *exprs);
 extern List *get_tlist_exprs(List *tlist, bool includeJunk);
 
 extern bool tlist_same_exprs(List *tlist1, List *tlist2);
+extern bool tlist_is_subset(List* tlist1, List *tlist2);
 
 extern bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
 extern bool tlist_same_collations(List *tlist, List *colCollations, bool junkOK);
@@ -39,6 +40,7 @@ extern bool grouping_is_hashable(List *groupClause);
 extern PathTarget *make_pathtarget_from_tlist(List *tlist);
 extern List *make_tlist_from_pathtarget(PathTarget *target);
 extern PathTarget *copy_pathtarget(PathTarget *src);
+extern bool pathtarget_is_subset(PathTarget *target1, PathTarget *target2);
 extern PathTarget *create_empty_pathtarget(void);
 extern void add_column_to_pathtarget(PathTarget *target,
 									 Expr *expr, Index sortgroupref);
diff --git a/src/include/parser/parse_target.h b/src/include/parser/parse_target.h
index 1a7b1a9277..fa3047032f 100644
--- a/src/include/parser/parse_target.h
+++ b/src/include/parser/parse_target.h
@@ -25,7 +25,7 @@ extern void resolveTargetListUnknowns(ParseState *pstate, List *targetlist);
 extern void markTargetListOrigins(ParseState *pstate, List *targetlist);
 extern TargetEntry *transformTargetEntry(ParseState *pstate,
 										 Node *node, Node *expr, ParseExprKind exprKind,
-										 char *colname, bool resjunk);
+										 char *colname, uint16 resjunk);
 extern Expr *transformAssignedExpr(ParseState *pstate, Expr *expr,
 								   ParseExprKind exprKind,
 								   const char *colname,
diff --git a/src/include/parser/parsetree.h b/src/include/parser/parsetree.h
index 6e1058f1c2..ffe5f34e08 100644
--- a/src/include/parser/parsetree.h
+++ b/src/include/parser/parsetree.h
@@ -50,6 +50,7 @@ extern bool get_rte_attribute_is_dropped(RangeTblEntry *rte,
  */
 
 extern TargetEntry *get_tle_by_resno(List *tlist, AttrNumber resno);
+extern bool tlist_is_subset(List* tlist1, List *tlist2);
 
 /* ----------------
  *		FOR UPDATE/SHARE info
