>From d38013a7e9d49dd7b1e9c6c2b22c9906945e7010 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Thu, 25 Aug 2016 11:24:49 -0700
Subject: [PATCH 6/6] Remove unused code related to targetlist SRFs.

---
 src/backend/catalog/index.c               |    3 +-
 src/backend/commands/copy.c               |    2 +-
 src/backend/commands/prepare.c            |    3 +-
 src/backend/commands/tablecmds.c          |    3 +-
 src/backend/commands/typecmds.c           |    2 +-
 src/backend/executor/execAmi.c            |   42 +-
 src/backend/executor/execQual.c           | 1174 ++++-------------------------
 src/backend/executor/execScan.c           |   30 +-
 src/backend/executor/execUtils.c          |    6 -
 src/backend/executor/nodeAgg.c            |   52 +-
 src/backend/executor/nodeBitmapHeapscan.c |    2 -
 src/backend/executor/nodeCtescan.c        |    2 -
 src/backend/executor/nodeCustom.c         |    2 -
 src/backend/executor/nodeForeignscan.c    |    2 -
 src/backend/executor/nodeFunctionscan.c   |   16 +-
 src/backend/executor/nodeGather.c         |   25 +-
 src/backend/executor/nodeGroup.c          |   42 +-
 src/backend/executor/nodeHash.c           |    2 +-
 src/backend/executor/nodeHashjoin.c       |   52 +-
 src/backend/executor/nodeIndexonlyscan.c  |    2 -
 src/backend/executor/nodeIndexscan.c      |   11 +-
 src/backend/executor/nodeLimit.c          |   19 +-
 src/backend/executor/nodeMergejoin.c      |   59 +-
 src/backend/executor/nodeModifyTable.c    |    4 +-
 src/backend/executor/nodeNestloop.c       |   41 +-
 src/backend/executor/nodeResult.c         |   33 +-
 src/backend/executor/nodeSamplescan.c     |    8 +-
 src/backend/executor/nodeSeqscan.c        |    2 -
 src/backend/executor/nodeSubplan.c        |   31 +-
 src/backend/executor/nodeSubqueryscan.c   |    2 -
 src/backend/executor/nodeTidscan.c        |    8 +-
 src/backend/executor/nodeValuesscan.c     |    5 +-
 src/backend/executor/nodeWindowAgg.c      |   58 +-
 src/backend/executor/nodeWorktablescan.c  |    2 -
 src/backend/optimizer/plan/planner.c      |  119 +--
 src/backend/optimizer/util/clauses.c      |   46 +-
 src/backend/optimizer/util/predtest.c     |    2 +-
 src/backend/utils/adt/domains.c           |    2 +-
 src/backend/utils/adt/xml.c               |    4 +-
 src/include/executor/executor.h           |   13 +-
 src/include/nodes/execnodes.h             |   16 +-
 src/include/optimizer/clauses.h           |    1 -
 src/pl/plpgsql/src/pl_exec.c              |    5 +-
 43 files changed, 284 insertions(+), 1671 deletions(-)

diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index b0b43cf..fd82855 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1788,8 +1788,7 @@ FormIndexDatum(IndexInfo *indexInfo,
 				elog(ERROR, "wrong number of index expressions");
 			iDatum = ExecEvalExprSwitchContext((ExprState *) lfirst(indexpr_item),
 											   GetPerTupleExprContext(estate),
-											   &isNull,
-											   NULL);
+											   &isNull);
 			indexpr_item = lnext(indexpr_item);
 		}
 		values[i] = iDatum;
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index f45b330..28466ac 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -3172,7 +3172,7 @@ NextCopyFrom(CopyState cstate, ExprContext *econtext,
 		Assert(CurrentMemoryContext == econtext->ecxt_per_tuple_memory);
 
 		values[defmap[i]] = ExecEvalExpr(defexprs[i], econtext,
-										 &nulls[defmap[i]], NULL);
+										 &nulls[defmap[i]]);
 	}
 
 	return true;
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index cec37ce..451c8d5 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -404,8 +404,7 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
 		prm->pflags = PARAM_FLAG_CONST;
 		prm->value = ExecEvalExprSwitchContext(n,
 											   GetPerTupleExprContext(estate),
-											   &prm->isnull,
-											   NULL);
+											   &prm->isnull);
 
 		i++;
 	}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..92e468d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4151,8 +4151,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 
 					values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate,
 														  econtext,
-													 &isnull[ex->attnum - 1],
-														  NULL);
+													 &isnull[ex->attnum - 1]);
 				}
 
 				/*
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index ce04211..755af68 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2741,7 +2741,7 @@ validateDomainConstraint(Oid domainoid, char *ccbin)
 
 				conResult = ExecEvalExprSwitchContext(exprstate,
 													  econtext,
-													  &isNull, NULL);
+													  &isNull);
 
 				if (!isNull && !DatumGetBool(conResult))
 				{
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index ea2f09e..959d27a 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -58,7 +58,6 @@
 #include "utils/syscache.h"
 
 
-static bool TargetListSupportsBackwardScan(List *targetlist);
 static bool IndexSupportsBackwardScan(Oid indexid);
 
 
@@ -119,7 +118,7 @@ ExecReScan(PlanState *node)
 			UpdateChangedParamSet(node->righttree, node->chgParam);
 	}
 
-	/* Shut down any SRFs in the plan node's targetlist */
+	/* Call expression callbacks */
 	if (node->ps_ExprContext)
 		ReScanExprContext(node->ps_ExprContext);
 
@@ -455,8 +454,7 @@ ExecSupportsBackwardScan(Plan *node)
 	{
 		case T_Result:
 			if (outerPlan(node) != NULL)
-				return ExecSupportsBackwardScan(outerPlan(node)) &&
-					TargetListSupportsBackwardScan(node->targetlist);
+				return ExecSupportsBackwardScan(outerPlan(node));
 			else
 				return false;
 
@@ -473,12 +471,6 @@ ExecSupportsBackwardScan(Plan *node)
 				return true;
 			}
 
-		case T_SeqScan:
-		case T_TidScan:
-		case T_ValuesScan:
-		case T_CteScan:
-			return TargetListSupportsBackwardScan(node->targetlist);
-
 		case T_SampleScan:
 			/* Simplify life for tablesample methods by disallowing this */
 			return false;
@@ -487,35 +479,33 @@ ExecSupportsBackwardScan(Plan *node)
 			return false;
 
 		case T_IndexScan:
-			return IndexSupportsBackwardScan(((IndexScan *) node)->indexid) &&
-				TargetListSupportsBackwardScan(node->targetlist);
+			return IndexSupportsBackwardScan(((IndexScan *) node)->indexid);
 
 		case T_IndexOnlyScan:
-			return IndexSupportsBackwardScan(((IndexOnlyScan *) node)->indexid) &&
-				TargetListSupportsBackwardScan(node->targetlist);
+			return IndexSupportsBackwardScan(((IndexOnlyScan *) node)->indexid);
 
 		case T_SubqueryScan:
-			return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan) &&
-				TargetListSupportsBackwardScan(node->targetlist);
+			return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan);
 
 		case T_CustomScan:
 			{
 				uint32		flags = ((CustomScan *) node)->flags;
 
-				if ((flags & CUSTOMPATH_SUPPORT_BACKWARD_SCAN) &&
-					TargetListSupportsBackwardScan(node->targetlist))
+				if (flags & CUSTOMPATH_SUPPORT_BACKWARD_SCAN)
 					return true;
 			}
 			return false;
 
+		case T_SeqScan:
+		case T_TidScan:
+		case T_ValuesScan:
+		case T_CteScan:
 		case T_Material:
 		case T_Sort:
-			/* these don't evaluate tlist */
 			return true;
 
 		case T_LockRows:
 		case T_Limit:
-			/* these don't evaluate tlist */
 			return ExecSupportsBackwardScan(outerPlan(node));
 
 		default:
@@ -524,18 +514,6 @@ ExecSupportsBackwardScan(Plan *node)
 }
 
 /*
- * If the tlist contains set-returning functions, we can't support backward
- * scan, because the TupFromTlist code is direction-ignorant.
- */
-static bool
-TargetListSupportsBackwardScan(List *targetlist)
-{
-	if (expression_returns_set((Node *) targetlist))
-		return false;
-	return true;
-}
-
-/*
  * An IndexScan or IndexOnlyScan node supports backward scan only if the
  * index's AM does.
  */
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d9e2797..1bdc0ac 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -64,127 +64,115 @@
 /* static function decls */
 static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
 				 ExprContext *econtext,
-				 bool *isNull, ExprDoneCond *isDone);
+				 bool *isNull);
 static bool isAssignmentIndirectionExpr(ExprState *exprstate);
 static Datum ExecEvalAggref(AggrefExprState *aggref,
 			   ExprContext *econtext,
-			   bool *isNull, ExprDoneCond *isDone);
+			   bool *isNull);
 static Datum ExecEvalWindowFunc(WindowFuncExprState *wfunc,
 				   ExprContext *econtext,
-				   bool *isNull, ExprDoneCond *isDone);
+				   bool *isNull);
 static Datum ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
-				  bool *isNull, ExprDoneCond *isDone);
+				  bool *isNull);
 static Datum ExecEvalScalarVarFast(ExprState *exprstate, ExprContext *econtext,
-					  bool *isNull, ExprDoneCond *isDone);
+					  bool *isNull);
 static Datum ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate,
 					ExprContext *econtext,
-					bool *isNull, ExprDoneCond *isDone);
+					bool *isNull);
 static Datum ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate,
 					 ExprContext *econtext,
-					 bool *isNull, ExprDoneCond *isDone);
+					 bool *isNull);
 static Datum ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate,
 					 ExprContext *econtext,
-					 bool *isNull, ExprDoneCond *isDone);
+					 bool *isNull);
 static Datum ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
-			  bool *isNull, ExprDoneCond *isDone);
+			  bool *isNull);
 static Datum ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext,
-				  bool *isNull, ExprDoneCond *isDone);
+				  bool *isNull);
 static Datum ExecEvalParamExtern(ExprState *exprstate, ExprContext *econtext,
-					bool *isNull, ExprDoneCond *isDone);
-static void ShutdownFuncExpr(Datum arg);
-static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod,
-				   TupleDesc *cache_field, ExprContext *econtext);
+					bool *isNull);
 static void ShutdownTupleDescRef(Datum arg);
-static void ExecPrepareTuplestoreResult(FuncExprState *fcache,
-							ExprContext *econtext,
-							Tuplestorestate *resultStore,
-							TupleDesc resultDesc);
-static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc);
-static Datum ExecMakeFunctionResult(FuncExprState *fcache,
-					   ExprContext *econtext,
-					   bool *isNull,
-					   ExprDoneCond *isDone);
 static Datum ExecMakeFunctionResultNoSets(FuncExprState *fcache,
 							 ExprContext *econtext,
-							 bool *isNull, ExprDoneCond *isDone);
+							 bool *isNull);
 static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext,
-			 bool *isNull, ExprDoneCond *isDone);
+			 bool *isNull);
 static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext,
-			 bool *isNull, ExprDoneCond *isDone);
+			 bool *isNull);
 static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext,
-				 bool *isNull, ExprDoneCond *isDone);
+				 bool *isNull);
 static Datum ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
 					  ExprContext *econtext,
-					  bool *isNull, ExprDoneCond *isDone);
+					  bool *isNull);
 static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
-			bool *isNull, ExprDoneCond *isDone);
+			bool *isNull);
 static Datum ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext,
-		   bool *isNull, ExprDoneCond *isDone);
+		   bool *isNull);
 static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
-			bool *isNull, ExprDoneCond *isDone);
+			bool *isNull);
 static Datum ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
 					   ExprContext *econtext,
-					   bool *isNull, ExprDoneCond *isDone);
+					   bool *isNull);
 static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
-			 bool *isNull, ExprDoneCond *isDone);
+			 bool *isNull);
 static Datum ExecEvalCaseTestExpr(ExprState *exprstate,
 					 ExprContext *econtext,
-					 bool *isNull, ExprDoneCond *isDone);
+					 bool *isNull);
 static Datum ExecEvalArray(ArrayExprState *astate,
 			  ExprContext *econtext,
-			  bool *isNull, ExprDoneCond *isDone);
+			  bool *isNull);
 static Datum ExecEvalRow(RowExprState *rstate,
 			ExprContext *econtext,
-			bool *isNull, ExprDoneCond *isDone);
+			bool *isNull);
 static Datum ExecEvalRowCompare(RowCompareExprState *rstate,
 				   ExprContext *econtext,
-				   bool *isNull, ExprDoneCond *isDone);
+				   bool *isNull);
 static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr,
 				 ExprContext *econtext,
-				 bool *isNull, ExprDoneCond *isDone);
+				 bool *isNull);
 static Datum ExecEvalMinMax(MinMaxExprState *minmaxExpr,
 			   ExprContext *econtext,
-			   bool *isNull, ExprDoneCond *isDone);
+			   bool *isNull);
 static Datum ExecEvalSQLValueFunction(ExprState *svfExpr,
 						 ExprContext *econtext,
-						 bool *isNull, ExprDoneCond *isDone);
+						 bool *isNull);
 static Datum ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
-			bool *isNull, ExprDoneCond *isDone);
+			bool *isNull);
 static Datum ExecEvalNullIf(FuncExprState *nullIfExpr,
 			   ExprContext *econtext,
-			   bool *isNull, ExprDoneCond *isDone);
+			   bool *isNull);
 static Datum ExecEvalNullTest(NullTestState *nstate,
 				 ExprContext *econtext,
-				 bool *isNull, ExprDoneCond *isDone);
+				 bool *isNull);
 static Datum ExecEvalBooleanTest(GenericExprState *bstate,
 					ExprContext *econtext,
-					bool *isNull, ExprDoneCond *isDone);
+					bool *isNull);
 static Datum ExecEvalCoerceToDomain(CoerceToDomainState *cstate,
 					   ExprContext *econtext,
-					   bool *isNull, ExprDoneCond *isDone);
+					   bool *isNull);
 static Datum ExecEvalCoerceToDomainValue(ExprState *exprstate,
 							ExprContext *econtext,
-							bool *isNull, ExprDoneCond *isDone);
+							bool *isNull);
 static Datum ExecEvalFieldSelect(FieldSelectState *fstate,
 					ExprContext *econtext,
-					bool *isNull, ExprDoneCond *isDone);
+					bool *isNull);
 static Datum ExecEvalFieldStore(FieldStoreState *fstate,
 				   ExprContext *econtext,
-				   bool *isNull, ExprDoneCond *isDone);
+				   bool *isNull);
 static Datum ExecEvalRelabelType(GenericExprState *exprstate,
 					ExprContext *econtext,
-					bool *isNull, ExprDoneCond *isDone);
+					bool *isNull);
 static Datum ExecEvalCoerceViaIO(CoerceViaIOState *iostate,
 					ExprContext *econtext,
-					bool *isNull, ExprDoneCond *isDone);
+					bool *isNull);
 static Datum ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
 						ExprContext *econtext,
-						bool *isNull, ExprDoneCond *isDone);
+						bool *isNull);
 static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
-					  bool *isNull, ExprDoneCond *isDone);
+					  bool *isNull);
 static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
 						 ExprContext *econtext,
-						 bool *isNull, ExprDoneCond *isDone);
+						 bool *isNull);
 
 
 /* ----------------------------------------------------------------
@@ -195,8 +183,7 @@ static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
  * Each of the following routines having the signature
  *		Datum ExecEvalFoo(ExprState *expression,
  *						  ExprContext *econtext,
- *						  bool *isNull,
- *						  ExprDoneCond *isDone);
+ *						  bool *isNull);
  * is responsible for evaluating one type or subtype of ExprState node.
  * They are normally called via the ExecEvalExpr macro, which makes use of
  * the function pointer set up when the ExprState node was built by
@@ -220,22 +207,6 @@ static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
  *		return value: Datum value of result
  *		*isNull: set to TRUE if result is NULL (actual return value is
  *				 meaningless if so); set to FALSE if non-null result
- *		*isDone: set to indicator of set-result status
- *
- * A caller that can only accept a singleton (non-set) result should pass
- * NULL for isDone; if the expression computes a set result then an error
- * will be reported via ereport.  If the caller does pass an isDone pointer
- * then *isDone is set to one of these three states:
- *		ExprSingleResult		singleton result (not a set)
- *		ExprMultipleResult		return value is one element of a set
- *		ExprEndResult			there are no more elements in the set
- * When ExprMultipleResult is returned, the caller should invoke
- * ExecEvalExpr() repeatedly until ExprEndResult is returned.  ExprEndResult
- * is returned after the last real set element.  For convenience isNull will
- * always be set TRUE when ExprEndResult is returned, but this should not be
- * taken as indicating a NULL element of the set.  Note that these return
- * conventions allow us to distinguish among a singleton NULL, a NULL element
- * of a set, and an empty set.
  *
  * The caller should already have switched into the temporary memory
  * context econtext->ecxt_per_tuple_memory.  The convenience entry point
@@ -260,8 +231,7 @@ static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
 static Datum
 ExecEvalArrayRef(ArrayRefExprState *astate,
 				 ExprContext *econtext,
-				 bool *isNull,
-				 ExprDoneCond *isDone)
+				 bool *isNull)
 {
 	ArrayRef   *arrayRef = (ArrayRef *) astate->xprstate.expr;
 	Datum		array_source;
@@ -278,8 +248,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
 
 	array_source = ExecEvalExpr(astate->refexpr,
 								econtext,
-								isNull,
-								isDone);
+								isNull);
 
 	/*
 	 * If refexpr yields NULL, and it's a fetch, then result is NULL. In the
@@ -287,8 +256,6 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
 	 */
 	if (*isNull)
 	{
-		if (isDone && *isDone == ExprEndResult)
-			return (Datum) NULL;	/* end of set result */
 		if (!isAssignment)
 			return (Datum) NULL;
 	}
@@ -314,8 +281,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
 
 		upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate,
 													 econtext,
-													 &eisnull,
-													 NULL));
+													 &eisnull));
 		/* If any index expr yields NULL, result is NULL or error */
 		if (eisnull)
 		{
@@ -350,8 +316,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
 
 			lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate,
 														 econtext,
-														 &eisnull,
-														 NULL));
+														 &eisnull));
 			/* If any index expr yields NULL, result is NULL or error */
 			if (eisnull)
 			{
@@ -438,8 +403,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
 		 */
 		sourceData = ExecEvalExpr(astate->refassgnexpr,
 								  econtext,
-								  &eisnull,
-								  NULL);
+								  &eisnull);
 
 		econtext->caseValue_datum = save_datum;
 		econtext->caseValue_isNull = save_isNull;
@@ -542,11 +506,8 @@ isAssignmentIndirectionExpr(ExprState *exprstate)
  */
 static Datum
 ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext,
-			   bool *isNull, ExprDoneCond *isDone)
+			   bool *isNull)
 {
-	if (isDone)
-		*isDone = ExprSingleResult;
-
 	if (econtext->ecxt_aggvalues == NULL)		/* safety check */
 		elog(ERROR, "no aggregates in this expression context");
 
@@ -563,11 +524,8 @@ ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext,
  */
 static Datum
 ExecEvalWindowFunc(WindowFuncExprState *wfunc, ExprContext *econtext,
-				   bool *isNull, ExprDoneCond *isDone)
+				   bool *isNull)
 {
-	if (isDone)
-		*isDone = ExprSingleResult;
-
 	if (econtext->ecxt_aggvalues == NULL)		/* safety check */
 		elog(ERROR, "no window functions in this expression context");
 
@@ -588,15 +546,12 @@ ExecEvalWindowFunc(WindowFuncExprState *wfunc, ExprContext *econtext,
  */
 static Datum
 ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
-				  bool *isNull, ExprDoneCond *isDone)
+				  bool *isNull)
 {
 	Var		   *variable = (Var *) exprstate->expr;
 	TupleTableSlot *slot;
 	AttrNumber	attnum;
 
-	if (isDone)
-		*isDone = ExprSingleResult;
-
 	/* Get the input slot and attribute number we want */
 	switch (variable->varno)
 	{
@@ -677,15 +632,12 @@ ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
  */
 static Datum
 ExecEvalScalarVarFast(ExprState *exprstate, ExprContext *econtext,
-					  bool *isNull, ExprDoneCond *isDone)
+					  bool *isNull)
 {
 	Var		   *variable = (Var *) exprstate->expr;
 	TupleTableSlot *slot;
 	AttrNumber	attnum;
 
-	if (isDone)
-		*isDone = ExprSingleResult;
-
 	/* Get the input slot and attribute number we want */
 	switch (variable->varno)
 	{
@@ -725,7 +677,7 @@ ExecEvalScalarVarFast(ExprState *exprstate, ExprContext *econtext,
  */
 static Datum
 ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
-					bool *isNull, ExprDoneCond *isDone)
+					bool *isNull)
 {
 	Var		   *variable = (Var *) wrvstate->xprstate.expr;
 	TupleTableSlot *slot;
@@ -733,9 +685,6 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
 	MemoryContext oldcontext;
 	bool		needslow = false;
 
-	if (isDone)
-		*isDone = ExprSingleResult;
-
 	/* This was checked by ExecInitExpr */
 	Assert(variable->varattno == InvalidAttrNumber);
 
@@ -941,7 +890,7 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
 
 	/* Fetch the value */
 	return (*wrvstate->xprstate.evalfunc) ((ExprState *) wrvstate, econtext,
-										   isNull, isDone);
+										   isNull);
 }
 
 /* ----------------------------------------------------------------
@@ -952,14 +901,12 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
  */
 static Datum
 ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
-					 bool *isNull, ExprDoneCond *isDone)
+					 bool *isNull)
 {
 	Var		   *variable = (Var *) wrvstate->xprstate.expr;
 	TupleTableSlot *slot;
 	HeapTupleHeader dtuple;
 
-	if (isDone)
-		*isDone = ExprSingleResult;
 	*isNull = false;
 
 	/* Get the input slot we want */
@@ -1008,7 +955,7 @@ ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
  */
 static Datum
 ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate, ExprContext *econtext,
-					 bool *isNull, ExprDoneCond *isDone)
+					 bool *isNull)
 {
 	Var		   *variable = (Var *) wrvstate->xprstate.expr;
 	TupleTableSlot *slot;
@@ -1018,8 +965,6 @@ ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate, ExprContext *econtext,
 	HeapTupleHeader dtuple;
 	int			i;
 
-	if (isDone)
-		*isDone = ExprSingleResult;
 	*isNull = false;
 
 	/* Get the input slot we want */
@@ -1097,13 +1042,10 @@ ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate, ExprContext *econtext,
  */
 static Datum
 ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
-			  bool *isNull, ExprDoneCond *isDone)
+			  bool *isNull)
 {
 	Const	   *con = (Const *) exprstate->expr;
 
-	if (isDone)
-		*isDone = ExprSingleResult;
-
 	*isNull = con->constisnull;
 	return con->constvalue;
 }
@@ -1116,15 +1058,12 @@ ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
  */
 static Datum
 ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext,
-				  bool *isNull, ExprDoneCond *isDone)
+				  bool *isNull)
 {
 	Param	   *expression = (Param *) exprstate->expr;
 	int			thisParamId = expression->paramid;
 	ParamExecData *prm;
 
-	if (isDone)
-		*isDone = ExprSingleResult;
-
 	/*
 	 * PARAM_EXEC params (internal executor parameters) are stored in the
 	 * ecxt_param_exec_vals array, and can be accessed by array index.
@@ -1149,15 +1088,12 @@ ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext,
  */
 static Datum
 ExecEvalParamExtern(ExprState *exprstate, ExprContext *econtext,
-					bool *isNull, ExprDoneCond *isDone)
+					bool *isNull)
 {
 	Param	   *expression = (Param *) exprstate->expr;
 	int			thisParamId = expression->paramid;
 	ParamListInfo paramInfo = econtext->ecxt_param_list_info;
 
-	if (isDone)
-		*isDone = ExprSingleResult;
-
 	/*
 	 * PARAM_EXTERN parameters must be sought in ecxt_param_list_info.
 	 */
@@ -1323,7 +1259,7 @@ GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
  */
 void
 ExecInitFcache(Oid foid, Oid input_collation, FuncExprState *fcache,
-			   MemoryContext fcacheCxt, bool needDescForSets)
+			   MemoryContext fcacheCxt)
 {
 	AclResult	aclresult;
 
@@ -1356,88 +1292,9 @@ ExecInitFcache(Oid foid, Oid input_collation, FuncExprState *fcache,
 							 list_length(fcache->args),
 							 input_collation, NULL, NULL);
 
-	/* If function returns set, prepare expected tuple descriptor */
-	if (fcache->func.fn_retset && needDescForSets)
-	{
-		TypeFuncClass functypclass;
-		Oid			funcrettype;
-		TupleDesc	tupdesc;
-		MemoryContext oldcontext;
-
-		functypclass = get_expr_result_type(fcache->func.fn_expr,
-											&funcrettype,
-											&tupdesc);
-
-		/* Must save tupdesc in fcache's context */
-		oldcontext = MemoryContextSwitchTo(fcacheCxt);
-
-		if (functypclass == TYPEFUNC_COMPOSITE)
-		{
-			/* Composite data type, e.g. a table's row type */
-			Assert(tupdesc);
-			/* Must copy it out of typcache for safety */
-			fcache->funcResultDesc = CreateTupleDescCopy(tupdesc);
-			fcache->funcReturnsTuple = true;
-		}
-		else if (functypclass == TYPEFUNC_SCALAR)
-		{
-			/* Base data type, i.e. scalar */
-			tupdesc = CreateTemplateTupleDesc(1, false);
-			TupleDescInitEntry(tupdesc,
-							   (AttrNumber) 1,
-							   NULL,
-							   funcrettype,
-							   -1,
-							   0);
-			fcache->funcResultDesc = tupdesc;
-			fcache->funcReturnsTuple = false;
-		}
-		else if (functypclass == TYPEFUNC_RECORD)
-		{
-			/* This will work if function doesn't need an expectedDesc */
-			fcache->funcResultDesc = NULL;
-			fcache->funcReturnsTuple = true;
-		}
-		else
-		{
-			/* Else, we will fail if function needs an expectedDesc */
-			fcache->funcResultDesc = NULL;
-		}
-
-		MemoryContextSwitchTo(oldcontext);
-	}
-	else
-		fcache->funcResultDesc = NULL;
-
 	/* Initialize additional state */
 	fcache->funcResultStore = NULL;
 	fcache->funcResultSlot = NULL;
-	fcache->setArgsValid = false;
-	fcache->shutdown_reg = false;
-}
-
-/*
- * callback function in case a FuncExpr returning a set needs to be shut down
- * before it has been run to completion
- */
-static void
-ShutdownFuncExpr(Datum arg)
-{
-	FuncExprState *fcache = (FuncExprState *) DatumGetPointer(arg);
-
-	/* If we have a slot, make sure it's let go of any tuplestore pointer */
-	if (fcache->funcResultSlot)
-		ExecClearTuple(fcache->funcResultSlot);
-
-	/* Release any open tuplestore */
-	if (fcache->funcResultStore)
-		tuplestore_end(fcache->funcResultStore);
-	fcache->funcResultStore = NULL;
-
-	/* Clear any active set-argument state */
-	fcache->setArgsValid = false;
-
-	/* execUtils will deregister the callback... */
 	fcache->shutdown_reg = false;
 }
 
@@ -1499,500 +1356,38 @@ ShutdownTupleDescRef(Datum arg)
 /*
  * Evaluate arguments for a function.
  */
-ExprDoneCond
+void
 ExecEvalFuncArgs(FunctionCallInfo fcinfo,
 				 List *argList,
 				 ExprContext *econtext)
 {
-	ExprDoneCond argIsDone;
 	int			i;
 	ListCell   *arg;
 
-	argIsDone = ExprSingleResult;		/* default assumption */
-
 	i = 0;
 	foreach(arg, argList)
 	{
 		ExprState  *argstate = (ExprState *) lfirst(arg);
-		ExprDoneCond thisArgIsDone;
 
 		fcinfo->arg[i] = ExecEvalExpr(argstate,
 									  econtext,
-									  &fcinfo->argnull[i],
-									  &thisArgIsDone);
-
-		if (thisArgIsDone != ExprSingleResult)
-		{
-			/*
-			 * We allow only one argument to have a set value; we'd need much
-			 * more complexity to keep track of multiple set arguments (cf.
-			 * ExecTargetList) and it doesn't seem worth it.
-			 */
-			if (argIsDone != ExprSingleResult)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("functions and operators can take at most one set argument")));
-			argIsDone = thisArgIsDone;
-		}
+									  &fcinfo->argnull[i]);
 		i++;
 	}
 
 	Assert(i == fcinfo->nargs);
-
-	return argIsDone;
-}
-
-/*
- *		ExecPrepareTuplestoreResult
- *
- * Subroutine for ExecMakeFunctionResult: prepare to extract rows from a
- * tuplestore function result.  We must set up a funcResultSlot (unless
- * already done in a previous call cycle) and verify that the function
- * returned the expected tuple descriptor.
- */
-static void
-ExecPrepareTuplestoreResult(FuncExprState *fcache,
-							ExprContext *econtext,
-							Tuplestorestate *resultStore,
-							TupleDesc resultDesc)
-{
-	fcache->funcResultStore = resultStore;
-
-	if (fcache->funcResultSlot == NULL)
-	{
-		/* Create a slot so we can read data out of the tuplestore */
-		TupleDesc	slotDesc;
-		MemoryContext oldcontext;
-
-		oldcontext = MemoryContextSwitchTo(fcache->func.fn_mcxt);
-
-		/*
-		 * If we were not able to determine the result rowtype from context,
-		 * and the function didn't return a tupdesc, we have to fail.
-		 */
-		if (fcache->funcResultDesc)
-			slotDesc = fcache->funcResultDesc;
-		else if (resultDesc)
-		{
-			/* don't assume resultDesc is long-lived */
-			slotDesc = CreateTupleDescCopy(resultDesc);
-		}
-		else
-		{
-			ereport(ERROR,
-					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("function returning setof record called in "
-							"context that cannot accept type record")));
-			slotDesc = NULL;	/* keep compiler quiet */
-		}
-
-		fcache->funcResultSlot = MakeSingleTupleTableSlot(slotDesc);
-		MemoryContextSwitchTo(oldcontext);
-	}
-
-	/*
-	 * If function provided a tupdesc, cross-check it.  We only really need to
-	 * do this for functions returning RECORD, but might as well do it always.
-	 */
-	if (resultDesc)
-	{
-		if (fcache->funcResultDesc)
-			tupledesc_match(fcache->funcResultDesc, resultDesc);
-
-		/*
-		 * If it is a dynamically-allocated TupleDesc, free it: it is
-		 * typically allocated in a per-query context, so we must avoid
-		 * leaking it across multiple usages.
-		 */
-		if (resultDesc->tdrefcount == -1)
-			FreeTupleDesc(resultDesc);
-	}
-
-	/* Register cleanup callback if we didn't already */
-	if (!fcache->shutdown_reg)
-	{
-		RegisterExprContextCallback(econtext,
-									ShutdownFuncExpr,
-									PointerGetDatum(fcache));
-		fcache->shutdown_reg = true;
-	}
-}
-
-/*
- * Check that function result tuple type (src_tupdesc) matches or can
- * be considered to match what the query expects (dst_tupdesc). If
- * they don't match, ereport.
- *
- * We really only care about number of attributes and data type.
- * Also, we can ignore type mismatch on columns that are dropped in the
- * destination type, so long as the physical storage matches.  This is
- * helpful in some cases involving out-of-date cached plans.
- */
-static void
-tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
-{
-	int			i;
-
-	if (dst_tupdesc->natts != src_tupdesc->natts)
-		ereport(ERROR,
-				(errcode(ERRCODE_DATATYPE_MISMATCH),
-				 errmsg("function return row and query-specified return row do not match"),
-				 errdetail_plural("Returned row contains %d attribute, but query expects %d.",
-				"Returned row contains %d attributes, but query expects %d.",
-								  src_tupdesc->natts,
-								  src_tupdesc->natts, dst_tupdesc->natts)));
-
-	for (i = 0; i < dst_tupdesc->natts; i++)
-	{
-		Form_pg_attribute dattr = dst_tupdesc->attrs[i];
-		Form_pg_attribute sattr = src_tupdesc->attrs[i];
-
-		if (IsBinaryCoercible(sattr->atttypid, dattr->atttypid))
-			continue;			/* no worries */
-		if (!dattr->attisdropped)
-			ereport(ERROR,
-					(errcode(ERRCODE_DATATYPE_MISMATCH),
-					 errmsg("function return row and query-specified return row do not match"),
-					 errdetail("Returned type %s at ordinal position %d, but query expects %s.",
-							   format_type_be(sattr->atttypid),
-							   i + 1,
-							   format_type_be(dattr->atttypid))));
-
-		if (dattr->attlen != sattr->attlen ||
-			dattr->attalign != sattr->attalign)
-			ereport(ERROR,
-					(errcode(ERRCODE_DATATYPE_MISMATCH),
-					 errmsg("function return row and query-specified return row do not match"),
-					 errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.",
-							   i + 1)));
-	}
-}
-
-/*
- *		ExecMakeFunctionResult
- *
- * Evaluate the arguments to a function and then the function itself.
- * init_fcache is presumed already run on the FuncExprState.
- *
- * This function handles the most general case, wherein the function or
- * one of its arguments can return a set.
- */
-static Datum
-ExecMakeFunctionResult(FuncExprState *fcache,
-					   ExprContext *econtext,
-					   bool *isNull,
-					   ExprDoneCond *isDone)
-{
-	List	   *arguments;
-	Datum		result;
-	FunctionCallInfo fcinfo;
-	PgStat_FunctionCallUsage fcusage;
-	ReturnSetInfo rsinfo;		/* for functions returning sets */
-	ExprDoneCond argDone;
-	bool		hasSetArg;
-	int			i;
-
-restart:
-
-	/* Guard against stack overflow due to overly complex expressions */
-	check_stack_depth();
-
-	/*
-	 * If a previous call of the function returned a set result in the form of
-	 * a tuplestore, continue reading rows from the tuplestore until it's
-	 * empty.
-	 */
-	if (fcache->funcResultStore)
-	{
-		Assert(isDone);			/* it was provided before ... */
-		if (tuplestore_gettupleslot(fcache->funcResultStore, true, false,
-									fcache->funcResultSlot))
-		{
-			*isDone = ExprMultipleResult;
-			if (fcache->funcReturnsTuple)
-			{
-				/* We must return the whole tuple as a Datum. */
-				*isNull = false;
-				return ExecFetchSlotTupleDatum(fcache->funcResultSlot);
-			}
-			else
-			{
-				/* Extract the first column and return it as a scalar. */
-				return slot_getattr(fcache->funcResultSlot, 1, isNull);
-			}
-		}
-		/* Exhausted the tuplestore, so clean up */
-		tuplestore_end(fcache->funcResultStore);
-		fcache->funcResultStore = NULL;
-		/* We are done unless there was a set-valued argument */
-		if (!fcache->setHasSetArg)
-		{
-			*isDone = ExprEndResult;
-			*isNull = true;
-			return (Datum) 0;
-		}
-		/* If there was, continue evaluating the argument values */
-		Assert(!fcache->setArgsValid);
-	}
-
-	/*
-	 * arguments is a list of expressions to evaluate before passing to the
-	 * function manager.  We skip the evaluation if it was already done in the
-	 * previous call (ie, we are continuing the evaluation of a set-valued
-	 * function).  Otherwise, collect the current argument values into fcinfo.
-	 */
-	fcinfo = &fcache->fcinfo_data;
-	arguments = fcache->args;
-	if (!fcache->setArgsValid)
-	{
-		argDone = ExecEvalFuncArgs(fcinfo, arguments, econtext);
-		if (argDone == ExprEndResult)
-		{
-			/* input is an empty set, so return an empty set. */
-			*isNull = true;
-			if (isDone)
-				*isDone = ExprEndResult;
-			else
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			return (Datum) 0;
-		}
-		hasSetArg = (argDone != ExprSingleResult);
-	}
-	else
-	{
-		/* Re-use callinfo from previous evaluation */
-		hasSetArg = fcache->setHasSetArg;
-		/* Reset flag (we may set it again below) */
-		fcache->setArgsValid = false;
-	}
-
-	/*
-	 * Now call the function, passing the evaluated parameter values.
-	 */
-	if (fcache->func.fn_retset || hasSetArg)
-	{
-		/*
-		 * We need to return a set result.  Complain if caller not ready to
-		 * accept one.
-		 */
-		if (isDone == NULL)
-			ereport(ERROR,
-					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("set-valued function called in context that cannot accept a set")));
-
-		/*
-		 * Prepare a resultinfo node for communication.  If the function
-		 * doesn't itself return set, we don't pass the resultinfo to the
-		 * function, but we need to fill it in anyway for internal use.
-		 */
-		if (fcache->func.fn_retset)
-			fcinfo->resultinfo = (Node *) &rsinfo;
-		rsinfo.type = T_ReturnSetInfo;
-		rsinfo.econtext = econtext;
-		rsinfo.expectedDesc = fcache->funcResultDesc;
-		rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
-		/* note we do not set SFRM_Materialize_Random or _Preferred */
-		rsinfo.returnMode = SFRM_ValuePerCall;
-		/* isDone is filled below */
-		rsinfo.setResult = NULL;
-		rsinfo.setDesc = NULL;
-
-		/*
-		 * This loop handles the situation where we have both a set argument
-		 * and a set-valued function.  Once we have exhausted the function's
-		 * value(s) for a particular argument value, we have to get the next
-		 * argument value and start the function over again. We might have to
-		 * do it more than once, if the function produces an empty result set
-		 * for a particular input value.
-		 */
-		for (;;)
-		{
-			/*
-			 * If function is strict, and there are any NULL arguments, skip
-			 * calling the function (at least for this set of args).
-			 */
-			bool		callit = true;
-
-			if (fcache->func.fn_strict)
-			{
-				for (i = 0; i < fcinfo->nargs; i++)
-				{
-					if (fcinfo->argnull[i])
-					{
-						callit = false;
-						break;
-					}
-				}
-			}
-
-			if (callit)
-			{
-				pgstat_init_function_usage(fcinfo, &fcusage);
-
-				fcinfo->isnull = false;
-				rsinfo.isDone = ExprSingleResult;
-				result = FunctionCallInvoke(fcinfo);
-				*isNull = fcinfo->isnull;
-				*isDone = rsinfo.isDone;
-
-				pgstat_end_function_usage(&fcusage,
-										rsinfo.isDone != ExprMultipleResult);
-			}
-			else if (fcache->func.fn_retset)
-			{
-				/* for a strict SRF, result for NULL is an empty set */
-				result = (Datum) 0;
-				*isNull = true;
-				*isDone = ExprEndResult;
-			}
-			else
-			{
-				/* for a strict non-SRF, result for NULL is a NULL */
-				result = (Datum) 0;
-				*isNull = true;
-				*isDone = ExprSingleResult;
-			}
-
-			/* Which protocol does function want to use? */
-			if (rsinfo.returnMode == SFRM_ValuePerCall)
-			{
-				if (*isDone != ExprEndResult)
-				{
-					/*
-					 * Got a result from current argument. If function itself
-					 * returns set, save the current argument values to re-use
-					 * on the next call.
-					 */
-					if (fcache->func.fn_retset &&
-						*isDone == ExprMultipleResult)
-					{
-						fcache->setHasSetArg = hasSetArg;
-						fcache->setArgsValid = true;
-						/* Register cleanup callback if we didn't already */
-						if (!fcache->shutdown_reg)
-						{
-							RegisterExprContextCallback(econtext,
-														ShutdownFuncExpr,
-													PointerGetDatum(fcache));
-							fcache->shutdown_reg = true;
-						}
-					}
-
-					/*
-					 * Make sure we say we are returning a set, even if the
-					 * function itself doesn't return sets.
-					 */
-					if (hasSetArg)
-						*isDone = ExprMultipleResult;
-					break;
-				}
-			}
-			else if (rsinfo.returnMode == SFRM_Materialize)
-			{
-				/* check we're on the same page as the function author */
-				if (rsinfo.isDone != ExprSingleResult)
-					ereport(ERROR,
-							(errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
-							 errmsg("table-function protocol for materialize mode was not followed")));
-				if (rsinfo.setResult != NULL)
-				{
-					/* prepare to return values from the tuplestore */
-					ExecPrepareTuplestoreResult(fcache, econtext,
-												rsinfo.setResult,
-												rsinfo.setDesc);
-					/* remember whether we had set arguments */
-					fcache->setHasSetArg = hasSetArg;
-					/* loop back to top to start returning from tuplestore */
-					goto restart;
-				}
-				/* if setResult was left null, treat it as empty set */
-				*isDone = ExprEndResult;
-				*isNull = true;
-				result = (Datum) 0;
-			}
-			else
-				ereport(ERROR,
-						(errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
-						 errmsg("unrecognized table-function returnMode: %d",
-								(int) rsinfo.returnMode)));
-
-			/* Else, done with this argument */
-			if (!hasSetArg)
-				break;			/* input not a set, so done */
-
-			/* Re-eval args to get the next element of the input set */
-			argDone = ExecEvalFuncArgs(fcinfo, arguments, econtext);
-
-			if (argDone != ExprMultipleResult)
-			{
-				/* End of argument set, so we're done. */
-				*isNull = true;
-				*isDone = ExprEndResult;
-				result = (Datum) 0;
-				break;
-			}
-
-			/*
-			 * If we reach here, loop around to run the function on the new
-			 * argument.
-			 */
-		}
-	}
-	else
-	{
-		/*
-		 * Non-set case: much easier.
-		 *
-		 * In common cases, this code path is unreachable because we'd have
-		 * selected ExecMakeFunctionResultNoSets instead.  However, it's
-		 * possible to get here if an argument sometimes produces set results
-		 * and sometimes scalar results.  For example, a CASE expression might
-		 * call a set-returning function in only some of its arms.
-		 */
-		if (isDone)
-			*isDone = ExprSingleResult;
-
-		/*
-		 * If function is strict, and there are any NULL arguments, skip
-		 * calling the function and return NULL.
-		 */
-		if (fcache->func.fn_strict)
-		{
-			for (i = 0; i < fcinfo->nargs; i++)
-			{
-				if (fcinfo->argnull[i])
-				{
-					*isNull = true;
-					return (Datum) 0;
-				}
-			}
-		}
-
-		pgstat_init_function_usage(fcinfo, &fcusage);
-
-		fcinfo->isnull = false;
-		result = FunctionCallInvoke(fcinfo);
-		*isNull = fcinfo->isnull;
-
-		pgstat_end_function_usage(&fcusage, true);
-	}
-
-	return result;
 }
 
 /*
  *		ExecMakeFunctionResultNoSets
  *
- * Simplified version of ExecMakeFunctionResult that can only handle
- * non-set cases.  Hand-tuned for speed.
+ * Portion of ExecMakeFunctionResult that does not need initialization.
+ * Hand-tuned for speed.
  */
 static Datum
 ExecMakeFunctionResultNoSets(FuncExprState *fcache,
 							 ExprContext *econtext,
-							 bool *isNull,
-							 ExprDoneCond *isDone)
+							 bool *isNull)
 {
 	ListCell   *arg;
 	Datum		result;
@@ -2003,9 +1398,6 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache,
 	/* Guard against stack overflow due to overly complex expressions */
 	check_stack_depth();
 
-	if (isDone)
-		*isDone = ExprSingleResult;
-
 	/* inlined, simplified version of ExecEvalFuncArgs */
 	fcinfo = &fcache->fcinfo_data;
 	i = 0;
@@ -2015,8 +1407,7 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache,
 
 		fcinfo->arg[i] = ExecEvalExpr(argstate,
 									  econtext,
-									  &fcinfo->argnull[i],
-									  NULL);
+									  &fcinfo->argnull[i]);
 		i++;
 	}
 
@@ -2064,15 +1455,14 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache,
 static Datum
 ExecEvalFunc(FuncExprState *fcache,
 			 ExprContext *econtext,
-			 bool *isNull,
-			 ExprDoneCond *isDone)
+			 bool *isNull)
 {
 	/* This is called only the first time through */
 	FuncExpr   *func = (FuncExpr *) fcache->xprstate.expr;
 
 	/* Initialize function lookup info */
 	ExecInitFcache(func->funcid, func->inputcollid, fcache,
-				   econtext->ecxt_per_query_memory, true);
+				   econtext->ecxt_per_query_memory);
 
 	if (fcache->func.fn_retset)
 	{
@@ -2081,22 +1471,9 @@ ExecEvalFunc(FuncExprState *fcache,
 				 errmsg("set-valued function called in context that cannot accept a set")));
 	}
 
-	/*
-	 * We need to invoke ExecMakeFunctionResult if either the function itself
-	 * or any of its input expressions can return a set.  Otherwise, invoke
-	 * ExecMakeFunctionResultNoSets.  In either case, change the evalfunc
-	 * pointer to go directly there on subsequent uses.
-	 */
-	if (fcache->func.fn_retset || expression_returns_set((Node *) func->args))
-	{
-		fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResult;
-		return ExecMakeFunctionResult(fcache, econtext, isNull, isDone);
-	}
-	else
-	{
-		fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets;
-		return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone);
-	}
+	/* Change the evalfunc pointer, to skip the above initialization. */
+	fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets;
+	return ExecMakeFunctionResultNoSets(fcache, econtext, isNull);
 }
 
 /* ----------------------------------------------------------------
@@ -2106,32 +1483,25 @@ ExecEvalFunc(FuncExprState *fcache,
 static Datum
 ExecEvalOper(FuncExprState *fcache,
 			 ExprContext *econtext,
-			 bool *isNull,
-			 ExprDoneCond *isDone)
+			 bool *isNull)
 {
 	/* This is called only the first time through */
 	OpExpr	   *op = (OpExpr *) fcache->xprstate.expr;
 
 	/* Initialize function lookup info */
 	ExecInitFcache(op->opfuncid, op->inputcollid, fcache,
-				   econtext->ecxt_per_query_memory, true);
+				   econtext->ecxt_per_query_memory);
 
-	/*
-	 * We need to invoke ExecMakeFunctionResult if either the function itself
-	 * or any of its input expressions can return a set.  Otherwise, invoke
-	 * ExecMakeFunctionResultNoSets.  In either case, change the evalfunc
-	 * pointer to go directly there on subsequent uses.
-	 */
-	if (fcache->func.fn_retset || expression_returns_set((Node *) op->args))
+	if (fcache->func.fn_retset)
 	{
-		fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResult;
-		return ExecMakeFunctionResult(fcache, econtext, isNull, isDone);
-	}
-	else
-	{
-		fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets;
-		return ExecMakeFunctionResultNoSets(fcache, econtext, isNull, isDone);
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-valued function called in context that cannot accept a set")));
 	}
+
+	/* Change the evalfunc pointer, to skip the above initialization. */
+	fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets;
+	return ExecMakeFunctionResultNoSets(fcache, econtext, isNull);
 }
 
 /* ----------------------------------------------------------------
@@ -2148,17 +1518,13 @@ ExecEvalOper(FuncExprState *fcache,
 static Datum
 ExecEvalDistinct(FuncExprState *fcache,
 				 ExprContext *econtext,
-				 bool *isNull,
-				 ExprDoneCond *isDone)
+				 bool *isNull)
 {
 	Datum		result;
 	FunctionCallInfo fcinfo;
-	ExprDoneCond argDone;
 
-	/* Set default values for result flags: non-null, not a set result */
+	/* Set non-null as default */
 	*isNull = false;
-	if (isDone)
-		*isDone = ExprSingleResult;
 
 	/*
 	 * Initialize function cache if first time through
@@ -2168,7 +1534,7 @@ ExecEvalDistinct(FuncExprState *fcache,
 		DistinctExpr *op = (DistinctExpr *) fcache->xprstate.expr;
 
 		ExecInitFcache(op->opfuncid, op->inputcollid, fcache,
-					   econtext->ecxt_per_query_memory, true);
+					   econtext->ecxt_per_query_memory);
 		Assert(!fcache->func.fn_retset);
 	}
 
@@ -2176,11 +1542,7 @@ ExecEvalDistinct(FuncExprState *fcache,
 	 * Evaluate arguments
 	 */
 	fcinfo = &fcache->fcinfo_data;
-	argDone = ExecEvalFuncArgs(fcinfo, fcache->args, econtext);
-	if (argDone != ExprSingleResult)
-		ereport(ERROR,
-				(errcode(ERRCODE_DATATYPE_MISMATCH),
-				 errmsg("IS DISTINCT FROM does not support set arguments")));
+	ExecEvalFuncArgs(fcinfo, fcache->args, econtext);
 	Assert(fcinfo->nargs == 2);
 
 	if (fcinfo->argnull[0] && fcinfo->argnull[1])
@@ -2216,7 +1578,7 @@ ExecEvalDistinct(FuncExprState *fcache,
 static Datum
 ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
 					  ExprContext *econtext,
-					  bool *isNull, ExprDoneCond *isDone)
+					  bool *isNull)
 {
 	ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) sstate->fxprstate.xprstate.expr;
 	bool		useOr = opexpr->useOr;
@@ -2225,7 +1587,6 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
 	Datum		result;
 	bool		resultnull;
 	FunctionCallInfo fcinfo;
-	ExprDoneCond argDone;
 	int			i;
 	int16		typlen;
 	bool		typbyval;
@@ -2234,10 +1595,8 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
 	bits8	   *bitmap;
 	int			bitmask;
 
-	/* Set default values for result flags: non-null, not a set result */
+	/* Set non-null as default */
 	*isNull = false;
-	if (isDone)
-		*isDone = ExprSingleResult;
 
 	/*
 	 * Initialize function cache if first time through
@@ -2245,7 +1604,7 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
 	if (sstate->fxprstate.func.fn_oid == InvalidOid)
 	{
 		ExecInitFcache(opexpr->opfuncid, opexpr->inputcollid, &sstate->fxprstate,
-					   econtext->ecxt_per_query_memory, true);
+					   econtext->ecxt_per_query_memory);
 		Assert(!sstate->fxprstate.func.fn_retset);
 	}
 
@@ -2253,11 +1612,7 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
 	 * Evaluate arguments
 	 */
 	fcinfo = &sstate->fxprstate.fcinfo_data;
-	argDone = ExecEvalFuncArgs(fcinfo, sstate->fxprstate.args, econtext);
-	if (argDone != ExprSingleResult)
-		ereport(ERROR,
-				(errcode(ERRCODE_DATATYPE_MISMATCH),
-			   errmsg("op ANY/ALL (array) does not support set arguments")));
+	ExecEvalFuncArgs(fcinfo, sstate->fxprstate.args, econtext);
 	Assert(fcinfo->nargs == 2);
 
 	/*
@@ -2403,15 +1758,12 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
  */
 static Datum
 ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
-			bool *isNull, ExprDoneCond *isDone)
+			bool *isNull)
 {
 	ExprState  *clause = linitial(notclause->args);
 	Datum		expr_value;
 
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	expr_value = ExecEvalExpr(clause, econtext, isNull, NULL);
+	expr_value = ExecEvalExpr(clause, econtext, isNull);
 
 	/*
 	 * if the expression evaluates to null, then we just cascade the null back
@@ -2433,15 +1785,12 @@ ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
  */
 static Datum
 ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext,
-		   bool *isNull, ExprDoneCond *isDone)
+		   bool *isNull)
 {
 	List	   *clauses = orExpr->args;
 	ListCell   *clause;
 	bool		AnyNull;
 
-	if (isDone)
-		*isDone = ExprSingleResult;
-
 	AnyNull = false;
 
 	/*
@@ -2462,7 +1811,7 @@ ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext,
 		ExprState  *clausestate = (ExprState *) lfirst(clause);
 		Datum		clause_value;
 
-		clause_value = ExecEvalExpr(clausestate, econtext, isNull, NULL);
+		clause_value = ExecEvalExpr(clausestate, econtext, isNull);
 
 		/*
 		 * if we have a non-null true result, then return it.
@@ -2484,15 +1833,12 @@ ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext,
  */
 static Datum
 ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
-			bool *isNull, ExprDoneCond *isDone)
+			bool *isNull)
 {
 	List	   *clauses = andExpr->args;
 	ListCell   *clause;
 	bool		AnyNull;
 
-	if (isDone)
-		*isDone = ExprSingleResult;
-
 	AnyNull = false;
 
 	/*
@@ -2509,7 +1855,7 @@ ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
 		ExprState  *clausestate = (ExprState *) lfirst(clause);
 		Datum		clause_value;
 
-		clause_value = ExecEvalExpr(clausestate, econtext, isNull, NULL);
+		clause_value = ExecEvalExpr(clausestate, econtext, isNull);
 
 		/*
 		 * if we have a non-null false result, then return it.
@@ -2535,7 +1881,7 @@ ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
 static Datum
 ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
 					   ExprContext *econtext,
-					   bool *isNull, ExprDoneCond *isDone)
+					   bool *isNull)
 {
 	ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) cstate->xprstate.expr;
 	HeapTuple	result;
@@ -2543,7 +1889,7 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
 	HeapTupleHeader tuple;
 	HeapTupleData tmptup;
 
-	tupDatum = ExecEvalExpr(cstate->arg, econtext, isNull, isDone);
+	tupDatum = ExecEvalExpr(cstate->arg, econtext, isNull);
 
 	/* this test covers the isDone exception too: */
 	if (*isNull)
@@ -2619,16 +1965,13 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
  */
 static Datum
 ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
-			 bool *isNull, ExprDoneCond *isDone)
+			 bool *isNull)
 {
 	List	   *clauses = caseExpr->args;
 	ListCell   *clause;
 	Datum		save_datum;
 	bool		save_isNull;
 
-	if (isDone)
-		*isDone = ExprSingleResult;
-
 	/*
 	 * If there's a test expression, we have to evaluate it and save the value
 	 * where the CaseTestExpr placeholders can find it.  We must save and
@@ -2652,8 +1995,7 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
 
 		econtext->caseValue_datum = ExecEvalExpr(caseExpr->arg,
 												 econtext,
-												 &arg_isNull,
-												 NULL);
+												 &arg_isNull);
 		econtext->caseValue_isNull = arg_isNull;
 	}
 
@@ -2670,8 +2012,7 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
 
 		clause_value = ExecEvalExpr(wclause->expr,
 									econtext,
-									&clause_isNull,
-									NULL);
+									&clause_isNull);
 
 		/*
 		 * if we have a true test, then we return the result, since the case
@@ -2684,8 +2025,7 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
 			econtext->caseValue_isNull = save_isNull;
 			return ExecEvalExpr(wclause->result,
 								econtext,
-								isNull,
-								isDone);
+								isNull);
 		}
 	}
 
@@ -2696,8 +2036,7 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
 	{
 		return ExecEvalExpr(caseExpr->defresult,
 							econtext,
-							isNull,
-							isDone);
+							isNull);
 	}
 
 	*isNull = true;
@@ -2712,10 +2051,8 @@ ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
 static Datum
 ExecEvalCaseTestExpr(ExprState *exprstate,
 					 ExprContext *econtext,
-					 bool *isNull, ExprDoneCond *isDone)
+					 bool *isNull)
 {
-	if (isDone)
-		*isDone = ExprSingleResult;
 	*isNull = econtext->caseValue_isNull;
 	return econtext->caseValue_datum;
 }
@@ -2732,17 +2069,13 @@ ExecEvalCaseTestExpr(ExprState *exprstate,
 static Datum
 ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
 						 ExprContext *econtext,
-						 bool *isNull,
-						 ExprDoneCond *isDone)
+						 bool *isNull)
 {
 	int			result = 0;
 	int			attnum = 0;
 	Bitmapset  *grouped_cols = gstate->aggstate->grouped_cols;
 	ListCell   *lc;
 
-	if (isDone)
-		*isDone = ExprSingleResult;
-
 	*isNull = false;
 
 	foreach(lc, (gstate->clauses))
@@ -2764,7 +2097,7 @@ ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
  */
 static Datum
 ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
-			  bool *isNull, ExprDoneCond *isDone)
+			  bool *isNull)
 {
 	ArrayExpr  *arrayExpr = (ArrayExpr *) astate->xprstate.expr;
 	ArrayType  *result;
@@ -2774,10 +2107,8 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
 	int			dims[MAXDIM];
 	int			lbs[MAXDIM];
 
-	/* Set default values for result flags: non-null, not a set result */
+	/* Set default values for result flags: non-null */
 	*isNull = false;
-	if (isDone)
-		*isDone = ExprSingleResult;
 
 	if (!arrayExpr->multidims)
 	{
@@ -2802,7 +2133,7 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
 		{
 			ExprState  *e = (ExprState *) lfirst(element);
 
-			dvalues[i] = ExecEvalExpr(e, econtext, &dnulls[i], NULL);
+			dvalues[i] = ExecEvalExpr(e, econtext, &dnulls[i]);
 			i++;
 		}
 
@@ -2852,7 +2183,7 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
 			ArrayType  *array;
 			int			this_ndims;
 
-			arraydatum = ExecEvalExpr(e, econtext, &eisnull, NULL);
+			arraydatum = ExecEvalExpr(e, econtext, &eisnull);
 			/* temporarily ignore null subarrays */
 			if (eisnull)
 			{
@@ -2991,7 +2322,7 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
 static Datum
 ExecEvalRow(RowExprState *rstate,
 			ExprContext *econtext,
-			bool *isNull, ExprDoneCond *isDone)
+			bool *isNull)
 {
 	HeapTuple	tuple;
 	Datum	   *values;
@@ -3000,10 +2331,8 @@ ExecEvalRow(RowExprState *rstate,
 	ListCell   *arg;
 	int			i;
 
-	/* Set default values for result flags: non-null, not a set result */
+	/* Set default values for result flag: non-null */
 	*isNull = false;
-	if (isDone)
-		*isDone = ExprSingleResult;
 
 	/* Allocate workspace */
 	natts = rstate->tupdesc->natts;
@@ -3019,7 +2348,7 @@ ExecEvalRow(RowExprState *rstate,
 	{
 		ExprState  *e = (ExprState *) lfirst(arg);
 
-		values[i] = ExecEvalExpr(e, econtext, &isnull[i], NULL);
+		values[i] = ExecEvalExpr(e, econtext, &isnull[i]);
 		i++;
 	}
 
@@ -3038,7 +2367,7 @@ ExecEvalRow(RowExprState *rstate,
 static Datum
 ExecEvalRowCompare(RowCompareExprState *rstate,
 				   ExprContext *econtext,
-				   bool *isNull, ExprDoneCond *isDone)
+				   bool *isNull)
 {
 	bool		result;
 	RowCompareType rctype = ((RowCompareExpr *) rstate->xprstate.expr)->rctype;
@@ -3047,8 +2376,6 @@ ExecEvalRowCompare(RowCompareExprState *rstate,
 	ListCell   *r;
 	int			i;
 
-	if (isDone)
-		*isDone = ExprSingleResult;
 	*isNull = true;				/* until we get a result */
 
 	i = 0;
@@ -3062,9 +2389,9 @@ ExecEvalRowCompare(RowCompareExprState *rstate,
 								 rstate->collations[i],
 								 NULL, NULL);
 		locfcinfo.arg[0] = ExecEvalExpr(le, econtext,
-										&locfcinfo.argnull[0], NULL);
+										&locfcinfo.argnull[0]);
 		locfcinfo.arg[1] = ExecEvalExpr(re, econtext,
-										&locfcinfo.argnull[1], NULL);
+										&locfcinfo.argnull[1]);
 		if (rstate->funcs[i].fn_strict &&
 			(locfcinfo.argnull[0] || locfcinfo.argnull[1]))
 			return (Datum) 0;	/* force NULL result */
@@ -3108,20 +2435,17 @@ ExecEvalRowCompare(RowCompareExprState *rstate,
  */
 static Datum
 ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext,
-				 bool *isNull, ExprDoneCond *isDone)
+				 bool *isNull)
 {
 	ListCell   *arg;
 
-	if (isDone)
-		*isDone = ExprSingleResult;
-
 	/* Simply loop through until something NOT NULL is found */
 	foreach(arg, coalesceExpr->args)
 	{
 		ExprState  *e = (ExprState *) lfirst(arg);
 		Datum		value;
 
-		value = ExecEvalExpr(e, econtext, isNull, NULL);
+		value = ExecEvalExpr(e, econtext, isNull);
 		if (!*isNull)
 			return value;
 	}
@@ -3137,7 +2461,7 @@ ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext,
  */
 static Datum
 ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext,
-			   bool *isNull, ExprDoneCond *isDone)
+			   bool *isNull)
 {
 	Datum		result = (Datum) 0;
 	MinMaxExpr *minmax = (MinMaxExpr *) minmaxExpr->xprstate.expr;
@@ -3146,8 +2470,6 @@ ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext,
 	FunctionCallInfoData locfcinfo;
 	ListCell   *arg;
 
-	if (isDone)
-		*isDone = ExprSingleResult;
 	*isNull = true;				/* until we get a result */
 
 	InitFunctionCallInfoData(locfcinfo, &minmaxExpr->cfunc, 2,
@@ -3162,7 +2484,7 @@ ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext,
 		bool		valueIsNull;
 		int32		cmpresult;
 
-		value = ExecEvalExpr(e, econtext, &valueIsNull, NULL);
+		value = ExecEvalExpr(e, econtext, &valueIsNull);
 		if (valueIsNull)
 			continue;			/* ignore NULL inputs */
 
@@ -3198,14 +2520,12 @@ ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext,
 static Datum
 ExecEvalSQLValueFunction(ExprState *svfExpr,
 						 ExprContext *econtext,
-						 bool *isNull, ExprDoneCond *isDone)
+						 bool *isNull)
 {
 	Datum		result = (Datum) 0;
 	SQLValueFunction *svf = (SQLValueFunction *) svfExpr->expr;
 	FunctionCallInfoData fcinfo;
 
-	if (isDone)
-		*isDone = ExprSingleResult;
 	*isNull = false;
 
 	/*
@@ -3266,7 +2586,7 @@ ExecEvalSQLValueFunction(ExprState *svfExpr,
  */
 static Datum
 ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
-			bool *isNull, ExprDoneCond *isDone)
+			bool *isNull)
 {
 	XmlExpr    *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
 	Datum		value;
@@ -3274,8 +2594,6 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
 	ListCell   *arg;
 	ListCell   *narg;
 
-	if (isDone)
-		*isDone = ExprSingleResult;
 	*isNull = true;				/* until we get a result */
 
 	switch (xexpr->op)
@@ -3288,7 +2606,7 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
 				{
 					ExprState  *e = (ExprState *) lfirst(arg);
 
-					value = ExecEvalExpr(e, econtext, &isnull, NULL);
+					value = ExecEvalExpr(e, econtext, &isnull);
 					if (!isnull)
 						values = lappend(values, DatumGetPointer(value));
 				}
@@ -3313,7 +2631,7 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
 					ExprState  *e = (ExprState *) lfirst(arg);
 					char	   *argname = strVal(lfirst(narg));
 
-					value = ExecEvalExpr(e, econtext, &isnull, NULL);
+					value = ExecEvalExpr(e, econtext, &isnull);
 					if (!isnull)
 					{
 						appendStringInfo(&buf, "<%s>%s</%s>",
@@ -3356,13 +2674,13 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
 				Assert(list_length(xmlExpr->args) == 2);
 
 				e = (ExprState *) linitial(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull, NULL);
+				value = ExecEvalExpr(e, econtext, &isnull);
 				if (isnull)
 					return (Datum) 0;
 				data = DatumGetTextP(value);
 
 				e = (ExprState *) lsecond(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull, NULL);
+				value = ExecEvalExpr(e, econtext, &isnull);
 				if (isnull)		/* probably can't happen */
 					return (Datum) 0;
 				preserve_whitespace = DatumGetBool(value);
@@ -3386,7 +2704,7 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
 				if (xmlExpr->args)
 				{
 					e = (ExprState *) linitial(xmlExpr->args);
-					value = ExecEvalExpr(e, econtext, &isnull, NULL);
+					value = ExecEvalExpr(e, econtext, &isnull);
 					if (isnull)
 						arg = NULL;
 					else
@@ -3413,20 +2731,20 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
 				Assert(list_length(xmlExpr->args) == 3);
 
 				e = (ExprState *) linitial(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull, NULL);
+				value = ExecEvalExpr(e, econtext, &isnull);
 				if (isnull)
 					return (Datum) 0;
 				data = DatumGetXmlP(value);
 
 				e = (ExprState *) lsecond(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull, NULL);
+				value = ExecEvalExpr(e, econtext, &isnull);
 				if (isnull)
 					version = NULL;
 				else
 					version = DatumGetTextP(value);
 
 				e = (ExprState *) lthird(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull, NULL);
+				value = ExecEvalExpr(e, econtext, &isnull);
 				standalone = DatumGetInt32(value);
 
 				*isNull = false;
@@ -3445,7 +2763,7 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
 				Assert(list_length(xmlExpr->args) == 1);
 
 				e = (ExprState *) linitial(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull, NULL);
+				value = ExecEvalExpr(e, econtext, &isnull);
 				if (isnull)
 					return (Datum) 0;
 
@@ -3463,7 +2781,7 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
 				Assert(list_length(xmlExpr->args) == 1);
 
 				e = (ExprState *) linitial(xmlExpr->args);
-				value = ExecEvalExpr(e, econtext, &isnull, NULL);
+				value = ExecEvalExpr(e, econtext, &isnull);
 				if (isnull)
 					return (Datum) 0;
 				else
@@ -3490,14 +2808,10 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
 static Datum
 ExecEvalNullIf(FuncExprState *nullIfExpr,
 			   ExprContext *econtext,
-			   bool *isNull, ExprDoneCond *isDone)
+			   bool *isNull)
 {
 	Datum		result;
 	FunctionCallInfo fcinfo;
-	ExprDoneCond argDone;
-
-	if (isDone)
-		*isDone = ExprSingleResult;
 
 	/*
 	 * Initialize function cache if first time through
@@ -3507,7 +2821,7 @@ ExecEvalNullIf(FuncExprState *nullIfExpr,
 		NullIfExpr *op = (NullIfExpr *) nullIfExpr->xprstate.expr;
 
 		ExecInitFcache(op->opfuncid, op->inputcollid, nullIfExpr,
-					   econtext->ecxt_per_query_memory, true);
+					   econtext->ecxt_per_query_memory);
 		Assert(!nullIfExpr->func.fn_retset);
 	}
 
@@ -3515,11 +2829,7 @@ ExecEvalNullIf(FuncExprState *nullIfExpr,
 	 * Evaluate arguments
 	 */
 	fcinfo = &nullIfExpr->fcinfo_data;
-	argDone = ExecEvalFuncArgs(fcinfo, nullIfExpr->args, econtext);
-	if (argDone != ExprSingleResult)
-		ereport(ERROR,
-				(errcode(ERRCODE_DATATYPE_MISMATCH),
-				 errmsg("NULLIF does not support set arguments")));
+	ExecEvalFuncArgs(fcinfo, nullIfExpr->args, econtext);
 	Assert(fcinfo->nargs == 2);
 
 	/* if either argument is NULL they can't be equal */
@@ -3549,16 +2859,12 @@ ExecEvalNullIf(FuncExprState *nullIfExpr,
 static Datum
 ExecEvalNullTest(NullTestState *nstate,
 				 ExprContext *econtext,
-				 bool *isNull,
-				 ExprDoneCond *isDone)
+				 bool *isNull)
 {
 	NullTest   *ntest = (NullTest *) nstate->xprstate.expr;
 	Datum		result;
 
-	result = ExecEvalExpr(nstate->arg, econtext, isNull, isDone);
-
-	if (isDone && *isDone == ExprEndResult)
-		return result;			/* nothing to check */
+	result = ExecEvalExpr(nstate->arg, econtext, isNull);
 
 	if (ntest->argisrow && !(*isNull))
 	{
@@ -3658,16 +2964,12 @@ ExecEvalNullTest(NullTestState *nstate,
 static Datum
 ExecEvalBooleanTest(GenericExprState *bstate,
 					ExprContext *econtext,
-					bool *isNull,
-					ExprDoneCond *isDone)
+					bool *isNull)
 {
 	BooleanTest *btest = (BooleanTest *) bstate->xprstate.expr;
 	Datum		result;
 
-	result = ExecEvalExpr(bstate->arg, econtext, isNull, isDone);
-
-	if (isDone && *isDone == ExprEndResult)
-		return result;			/* nothing to check */
+	result = ExecEvalExpr(bstate->arg, econtext, isNull);
 
 	switch (btest->booltesttype)
 	{
@@ -3743,16 +3045,13 @@ ExecEvalBooleanTest(GenericExprState *bstate,
  */
 static Datum
 ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext,
-					   bool *isNull, ExprDoneCond *isDone)
+					   bool *isNull)
 {
 	CoerceToDomain *ctest = (CoerceToDomain *) cstate->xprstate.expr;
 	Datum		result;
 	ListCell   *l;
 
-	result = ExecEvalExpr(cstate->arg, econtext, isNull, isDone);
-
-	if (isDone && *isDone == ExprEndResult)
-		return result;			/* nothing to check */
+	result = ExecEvalExpr(cstate->arg, econtext, isNull);
 
 	/* Make sure we have up-to-date constraints */
 	UpdateDomainConstraintRef(cstate->constraint_ref);
@@ -3790,8 +3089,8 @@ ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext,
 					econtext->domainValue_datum = result;
 					econtext->domainValue_isNull = *isNull;
 
-					conResult = ExecEvalExpr(con->check_expr,
-											 econtext, &conIsNull, NULL);
+					conResult = ExecEvalExpr(con->check_expr, econtext,
+											 &conIsNull);
 
 					if (!conIsNull &&
 						!DatumGetBool(conResult))
@@ -3826,10 +3125,8 @@ ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext,
 static Datum
 ExecEvalCoerceToDomainValue(ExprState *exprstate,
 							ExprContext *econtext,
-							bool *isNull, ExprDoneCond *isDone)
+							bool *isNull)
 {
-	if (isDone)
-		*isDone = ExprSingleResult;
 	*isNull = econtext->domainValue_isNull;
 	return econtext->domainValue_datum;
 }
@@ -3843,8 +3140,7 @@ ExecEvalCoerceToDomainValue(ExprState *exprstate,
 static Datum
 ExecEvalFieldSelect(FieldSelectState *fstate,
 					ExprContext *econtext,
-					bool *isNull,
-					ExprDoneCond *isDone)
+					bool *isNull)
 {
 	FieldSelect *fselect = (FieldSelect *) fstate->xprstate.expr;
 	AttrNumber	fieldnum = fselect->fieldnum;
@@ -3857,7 +3153,7 @@ ExecEvalFieldSelect(FieldSelectState *fstate,
 	Form_pg_attribute attr;
 	HeapTupleData tmptup;
 
-	tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone);
+	tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull);
 
 	/* this test covers the isDone exception too: */
 	if (*isNull)
@@ -3922,8 +3218,7 @@ ExecEvalFieldSelect(FieldSelectState *fstate,
 static Datum
 ExecEvalFieldStore(FieldStoreState *fstate,
 				   ExprContext *econtext,
-				   bool *isNull,
-				   ExprDoneCond *isDone)
+				   bool *isNull)
 {
 	FieldStore *fstore = (FieldStore *) fstate->xprstate.expr;
 	HeapTuple	tuple;
@@ -3936,10 +3231,7 @@ ExecEvalFieldStore(FieldStoreState *fstate,
 	ListCell   *l1,
 			   *l2;
 
-	tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone);
-
-	if (isDone && *isDone == ExprEndResult)
-		return tupDatum;
+	tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull);
 
 	/* Lookup tupdesc if first time through or after rescan */
 	tupDesc = get_cached_rowtype(fstore->resulttype, -1,
@@ -3999,8 +3291,7 @@ ExecEvalFieldStore(FieldStoreState *fstate,
 
 		values[fieldnum - 1] = ExecEvalExpr(newval,
 											econtext,
-											&isnull[fieldnum - 1],
-											NULL);
+											&isnull[fieldnum - 1]);
 	}
 
 	econtext->caseValue_datum = save_datum;
@@ -4023,9 +3314,9 @@ ExecEvalFieldStore(FieldStoreState *fstate,
 static Datum
 ExecEvalRelabelType(GenericExprState *exprstate,
 					ExprContext *econtext,
-					bool *isNull, ExprDoneCond *isDone)
+					bool *isNull)
 {
-	return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone);
+	return ExecEvalExpr(exprstate->arg, econtext, isNull);
 }
 
 /* ----------------------------------------------------------------
@@ -4037,16 +3328,13 @@ ExecEvalRelabelType(GenericExprState *exprstate,
 static Datum
 ExecEvalCoerceViaIO(CoerceViaIOState *iostate,
 					ExprContext *econtext,
-					bool *isNull, ExprDoneCond *isDone)
+					bool *isNull)
 {
 	Datum		result;
 	Datum		inputval;
 	char	   *string;
 
-	inputval = ExecEvalExpr(iostate->arg, econtext, isNull, isDone);
-
-	if (isDone && *isDone == ExprEndResult)
-		return inputval;		/* nothing to do */
+	inputval = ExecEvalExpr(iostate->arg, econtext, isNull);
 
 	if (*isNull)
 		string = NULL;			/* output functions are not called on nulls */
@@ -4071,16 +3359,14 @@ ExecEvalCoerceViaIO(CoerceViaIOState *iostate,
 static Datum
 ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
 						ExprContext *econtext,
-						bool *isNull, ExprDoneCond *isDone)
+						bool *isNull)
 {
 	ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) astate->xprstate.expr;
 	Datum		result;
 	FunctionCallInfoData locfcinfo;
 
-	result = ExecEvalExpr(astate->arg, econtext, isNull, isDone);
+	result = ExecEvalExpr(astate->arg, econtext, isNull);
 
-	if (isDone && *isDone == ExprEndResult)
-		return result;			/* nothing to do */
 	if (*isNull)
 		return result;			/* nothing to do */
 
@@ -4148,7 +3434,7 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
  */
 static Datum
 ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
-					  bool *isNull, ExprDoneCond *isDone)
+					  bool *isNull)
 {
 	ereport(ERROR,
 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -4165,14 +3451,13 @@ ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
 Datum
 ExecEvalExprSwitchContext(ExprState *expression,
 						  ExprContext *econtext,
-						  bool *isNull,
-						  ExprDoneCond *isDone)
+						  bool *isNull)
 {
 	Datum		retDatum;
 	MemoryContext oldContext;
 
 	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-	retDatum = ExecEvalExpr(expression, econtext, isNull, isDone);
+	retDatum = ExecEvalExpr(expression, econtext, isNull);
 	MemoryContextSwitchTo(oldContext);
 	return retDatum;
 }
@@ -5032,7 +4317,7 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
 		Datum		expr_value;
 		bool		isNull;
 
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
+		expr_value = ExecEvalExpr(clause, econtext, &isNull);
 
 		if (isNull)
 		{
@@ -5090,17 +4375,9 @@ ExecCleanTargetListLength(List *targetlist)
 /*
  * ExecTargetList
  *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
+ *		expression context.
  *
  * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
  *
  * Since fields of the result tuple might be multiply referenced in higher
  * plan nodes, we have to force any read/write expanded values to read-only
@@ -5109,19 +4386,16 @@ ExecCleanTargetListLength(List *targetlist)
  * actually-multiply-referenced Vars and insert an expression node that
  * would do that only where really required.
  */
-static bool
+static void
 ExecTargetList(List *targetlist,
 			   TupleDesc tupdesc,
 			   ExprContext *econtext,
 			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
+			   bool *isnull)
 {
 	Form_pg_attribute *att = tupdesc->attrs;
 	MemoryContext oldContext;
 	ListCell   *tl;
-	bool		haveDoneSets;
 
 	/*
 	 * Run in short-lived per-tuple context while computing expressions.
@@ -5131,8 +4405,6 @@ ExecTargetList(List *targetlist,
 	/*
 	 * evaluate all the expressions in the target list
 	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
 	foreach(tl, targetlist)
 	{
 		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
@@ -5141,117 +4413,15 @@ ExecTargetList(List *targetlist,
 
 		values[resind] = ExecEvalExpr(gstate->arg,
 									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
+									  &isnull[resind]);
 
 		values[resind] = MakeExpandedObjectReadOnly(values[resind],
 													isnull[resind],
 													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					if (itemIsDone[resind] == ExprEndResult)
-					{
-						/*
-						 * Oh dear, this item is returning an empty set. Guess
-						 * we can't make a tuple after all.
-						 */
-						*isDone = ExprEndResult;
-						break;
-					}
-				}
-			}
-
-			/*
-			 * If we cannot make a tuple because some sets are empty, we still
-			 * have to cycle the nonempty sets to completion, else resources
-			 * will not be released from subplans etc.
-			 *
-			 * XXX is that still necessary?
-			 */
-			if (*isDone == ExprEndResult)
-			{
-				foreach(tl, targetlist)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
 	}
 
 	/* Report success */
 	MemoryContextSwitchTo(oldContext);
-
-	return true;
 }
 
 /*
@@ -5268,7 +4438,7 @@ ExecTargetList(List *targetlist,
  *		result slot.
  */
 TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+ExecProject(ProjectionInfo *projInfo)
 {
 	TupleTableSlot *slot;
 	ExprContext *econtext;
@@ -5285,10 +4455,6 @@ ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
 	slot = projInfo->pi_slot;
 	econtext = projInfo->pi_exprContext;
 
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
 	/*
 	 * Clear any former contents of the result slot.  This makes it safe for
 	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
@@ -5356,21 +4522,15 @@ ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
 	}
 
 	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
+	 * If there are any generic expressions, evaluate them.
 	 */
 	if (projInfo->pi_targetlist)
 	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
+		ExecTargetList(projInfo->pi_targetlist,
+					   slot->tts_tupleDescriptor,
+					   econtext,
+					   slot->tts_values,
+					   slot->tts_isnull);
 	}
 
 	/*
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..eb224b4 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -125,8 +125,6 @@ ExecScan(ScanState *node,
 	ExprContext *econtext;
 	List	   *qual;
 	ProjectionInfo *projInfo;
-	ExprDoneCond isDone;
-	TupleTableSlot *resultSlot;
 
 	/*
 	 * Fetch data from node
@@ -146,21 +144,6 @@ ExecScan(ScanState *node,
 	}
 
 	/*
-	 * Check to see if we're still projecting out tuples from a previous scan
-	 * tuple (because there is a function-returning-set in the projection
-	 * expressions).  If so, try to project another one.
-	 */
-	if (node->ps.ps_TupFromTlist)
-	{
-		Assert(projInfo);		/* can't get here if not projecting */
-		resultSlot = ExecProject(projInfo, &isDone);
-		if (isDone == ExprMultipleResult)
-			return resultSlot;
-		/* Done with that source tuple... */
-		node->ps.ps_TupFromTlist = false;
-	}
-
-	/*
 	 * Reset per-tuple memory context to free any expression evaluation
 	 * storage allocated in the previous tuple cycle.  Note this can't happen
 	 * until we're done projecting out tuples from a scan tuple.
@@ -214,15 +197,9 @@ ExecScan(ScanState *node,
 			{
 				/*
 				 * Form a projection tuple, store it in the result tuple slot
-				 * and return it --- unless we find we can project no tuples
-				 * from this scan tuple, in which case continue scan.
+				 * and return it.
 				 */
-				resultSlot = ExecProject(projInfo, &isDone);
-				if (isDone != ExprEndResult)
-				{
-					node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
-					return resultSlot;
-				}
+				return ExecProject(projInfo);
 			}
 			else
 			{
@@ -352,9 +329,6 @@ ExecScanReScan(ScanState *node)
 {
 	EState	   *estate = node->ps.state;
 
-	/* Stop projecting any tuples from SRFs in the targetlist */
-	node->ps.ps_TupFromTlist = false;
-
 	/* Rescan EvalPlanQual tuple if we're inside an EvalPlanQual recheck */
 	if (estate->es_epqScanDone != NULL)
 	{
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index e937cf8..ded073a 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -592,12 +592,6 @@ ExecBuildProjectionInfo(List *targetList,
 	projInfo->pi_numSimpleVars = numSimpleVars;
 	projInfo->pi_directMap = directMap;
 
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
 	return projInfo;
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index ce2fc28..f2ba170 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -859,13 +859,13 @@ advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup)
 			bool		isnull;
 
 			res = ExecEvalExprSwitchContext(filter, aggstate->tmpcontext,
-											&isnull, NULL);
+											&isnull);
 			if (isnull || !DatumGetBool(res))
 				continue;
 		}
 
 		/* Evaluate the current input expressions for this aggregate */
-		slot = ExecProject(pertrans->evalproj, NULL);
+		slot = ExecProject(pertrans->evalproj);
 
 		if (pertrans->numSortCols > 0)
 		{
@@ -951,7 +951,7 @@ combine_aggregates(AggState *aggstate, AggStatePerGroup pergroup)
 		FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
 
 		/* Evaluate the current input expressions for this aggregate */
-		slot = ExecProject(pertrans->evalproj, NULL);
+		slot = ExecProject(pertrans->evalproj);
 		Assert(slot->tts_nvalid >= 1);
 
 		/*
@@ -1325,8 +1325,7 @@ finalize_aggregate(AggState *aggstate,
 
 		fcinfo.arg[i] = ExecEvalExpr(expr,
 									 aggstate->ss.ps.ps_ExprContext,
-									 &fcinfo.argnull[i],
-									 NULL);
+									 &fcinfo.argnull[i]);
 		anynull |= fcinfo.argnull[i];
 		i++;
 	}
@@ -1566,7 +1565,7 @@ finalize_aggregates(AggState *aggstate,
 /*
  * Project the result of a group (whose aggs have already been calculated by
  * finalize_aggregates). Returns the result slot, or NULL if no row is
- * projected (suppressed by qual or by an empty SRF).
+ * projected (suppressed by qual).
  */
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
@@ -1579,20 +1578,10 @@ project_aggregates(AggState *aggstate)
 	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
 	{
 		/*
-		 * Form and return or store a projection tuple using the aggregate
-		 * results and the representative input tuple.
+		 * Form and return projection tuple using the aggregate results and
+		 * the representative input tuple.
 		 */
-		ExprDoneCond isDone;
-		TupleTableSlot *result;
-
-		result = ExecProject(aggstate->ss.ps.ps_ProjInfo, &isDone);
-
-		if (isDone != ExprEndResult)
-		{
-			aggstate->ss.ps.ps_TupFromTlist =
-				(isDone == ExprMultipleResult);
-			return result;
-		}
+		return ExecProject(aggstate->ss.ps.ps_ProjInfo);
 	}
 	else
 		InstrCountFiltered1(aggstate, 1);
@@ -1802,27 +1791,6 @@ ExecAgg(AggState *node)
 {
 	TupleTableSlot *result;
 
-	/*
-	 * Check to see if we're still projecting out tuples from a previous agg
-	 * tuple (because there is a function-returning-set in the projection
-	 * expressions).  If so, try to project another one.
-	 */
-	if (node->ss.ps.ps_TupFromTlist)
-	{
-		ExprDoneCond isDone;
-
-		result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
-		if (isDone == ExprMultipleResult)
-			return result;
-		/* Done with that source tuple... */
-		node->ss.ps.ps_TupFromTlist = false;
-	}
-
-	/*
-	 * (We must do the ps_TupFromTlist check first, because in some cases
-	 * agg_done gets set before we emit the final aggregate tuple, and we have
-	 * to finish running SRFs for it.)
-	 */
 	if (!node->agg_done)
 	{
 		/* Dispatch based on strategy */
@@ -2443,8 +2411,6 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	ExecAssignResultTypeFromTL(&aggstate->ss.ps);
 	ExecAssignProjectionInfo(&aggstate->ss.ps, NULL);
 
-	aggstate->ss.ps.ps_TupFromTlist = false;
-
 	/*
 	 * get the count of aggregates in targetlist and quals
 	 */
@@ -3411,8 +3377,6 @@ ExecReScanAgg(AggState *node)
 
 	node->agg_done = false;
 
-	node->ss.ps.ps_TupFromTlist = false;
-
 	if (aggnode->aggstrategy == AGG_HASHED)
 	{
 		/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..16381f6 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -575,8 +575,6 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &scanstate->ss.ps);
 
-	scanstate->ss.ps.ps_TupFromTlist = false;
-
 	/*
 	 * initialize child expressions
 	 */
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..1acb166 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -265,8 +265,6 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	ExecAssignResultTypeFromTL(&scanstate->ss.ps);
 	ExecAssignScanProjectionInfo(&scanstate->ss);
 
-	scanstate->ss.ps.ps_TupFromTlist = false;
-
 	return scanstate;
 }
 
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..b465252 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -48,8 +48,6 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	/* create expression context for node */
 	ExecAssignExprContext(estate, &css->ss.ps);
 
-	css->ss.ps.ps_TupFromTlist = false;
-
 	/* initialize child expressions */
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..3762843 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -152,8 +152,6 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &scanstate->ss.ps);
 
-	scanstate->ss.ps.ps_TupFromTlist = false;
-
 	/*
 	 * initialize child expressions
 	 */
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index 1da4fde..725a5fe 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -282,8 +282,6 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &scanstate->ss.ps);
 
-	scanstate->ss.ps.ps_TupFromTlist = false;
-
 	/*
 	 * tuple table initialization
 	 */
@@ -647,7 +645,6 @@ ExecBeginFunctionResult(FunctionScanState *node,
 		IsA(funcexpr->expr, FuncExpr))
 	{
 		FuncExprState *fcache = (FuncExprState *) funcexpr;
-		ExprDoneCond argDone;
 
 		/*
 		 * This path is similar to ExecMakeFunctionResult.
@@ -662,7 +659,7 @@ ExecBeginFunctionResult(FunctionScanState *node,
 			FuncExpr   *func = (FuncExpr *) fcache->xprstate.expr;
 
 			ExecInitFcache(func->funcid, func->inputcollid, fcache,
-						   econtext->ecxt_per_query_memory, false);
+						   econtext->ecxt_per_query_memory);
 		}
 		returnsSet = fcache->func.fn_retset;
 		InitFunctionCallInfoData(perfunc->fcinfo, &(fcache->func),
@@ -681,15 +678,9 @@ ExecBeginFunctionResult(FunctionScanState *node,
 		 * and can be reset each time the node is re-scanned.
 		 */
 		oldcontext = MemoryContextSwitchTo(node->argcontext);
-		argDone = ExecEvalFuncArgs(&perfunc->fcinfo, fcache->args, econtext);
+		ExecEvalFuncArgs(&perfunc->fcinfo, fcache->args, econtext);
 		MemoryContextSwitchTo(oldcontext);
 
-		/* We don't allow sets in the arguments of the table function */
-		if (argDone != ExprSingleResult)
-			ereport(ERROR,
-					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("set-valued function called in context that cannot accept a set")));
-
 		/*
 		 * If function is strict, and there are any NULL arguments, skip
 		 * calling the function and act like it returned NULL (or an empty
@@ -742,8 +733,7 @@ ExecBeginFunctionResult(FunctionScanState *node,
 	else
 	{
 		perfunc->rsinfo.isDone = ExprSingleResult;
-		result = ExecEvalExpr(funcexpr, econtext,
-							  &perfunc->fcinfo.isnull, NULL);
+		result = ExecEvalExpr(funcexpr, econtext, &perfunc->fcinfo.isnull);
 
 		/* done after this, will use SFRM_ValuePerCall branch below */
 	}
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 438d1b2..51754c8 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -99,8 +99,6 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	outerNode = outerPlan(node);
 	outerPlanState(gatherstate) = ExecInitNode(outerNode, estate, eflags);
 
-	gatherstate->ps.ps_TupFromTlist = false;
-
 	/*
 	 * Initialize result tuple type and projection info.
 	 */
@@ -131,8 +129,6 @@ ExecGather(GatherState *node)
 	TupleTableSlot *fslot = node->funnel_slot;
 	int			i;
 	TupleTableSlot *slot;
-	TupleTableSlot *resultSlot;
-	ExprDoneCond isDone;
 	ExprContext *econtext;
 
 	/*
@@ -198,20 +194,6 @@ ExecGather(GatherState *node)
 	}
 
 	/*
-	 * Check to see if we're still projecting out tuples from a previous scan
-	 * tuple (because there is a function-returning-set in the projection
-	 * expressions).  If so, try to project another one.
-	 */
-	if (node->ps.ps_TupFromTlist)
-	{
-		resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone);
-		if (isDone == ExprMultipleResult)
-			return resultSlot;
-		/* Done with that source tuple... */
-		node->ps.ps_TupFromTlist = false;
-	}
-
-	/*
 	 * Reset per-tuple memory context to free any expression evaluation
 	 * storage allocated in the previous tuple cycle.  Note we can't do this
 	 * until we're done projecting.  This will also clear any previous tuple
@@ -239,13 +221,8 @@ ExecGather(GatherState *node)
 		 * back around for another tuple
 		 */
 		econtext->ecxt_outertuple = slot;
-		resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone);
 
-		if (isDone != ExprEndResult)
-		{
-			node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
-			return resultSlot;
-		}
+		return ExecProject(node->ps.ps_ProjInfo);
 	}
 
 	return slot;
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..2f55c70 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -50,23 +50,6 @@ ExecGroup(GroupState *node)
 	grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx;
 
 	/*
-	 * Check to see if we're still projecting out tuples from a previous group
-	 * tuple (because there is a function-returning-set in the projection
-	 * expressions).  If so, try to project another one.
-	 */
-	if (node->ss.ps.ps_TupFromTlist)
-	{
-		TupleTableSlot *result;
-		ExprDoneCond isDone;
-
-		result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
-		if (isDone == ExprMultipleResult)
-			return result;
-		/* Done with that source tuple... */
-		node->ss.ps.ps_TupFromTlist = false;
-	}
-
-	/*
 	 * The ScanTupleSlot holds the (copied) first tuple of each group.
 	 */
 	firsttupleslot = node->ss.ss_ScanTupleSlot;
@@ -107,16 +90,7 @@ ExecGroup(GroupState *node)
 			/*
 			 * Form and return a projection tuple using the first input tuple.
 			 */
-			TupleTableSlot *result;
-			ExprDoneCond isDone;
-
-			result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
-
-			if (isDone != ExprEndResult)
-			{
-				node->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
-				return result;
-			}
+			return ExecProject(node->ss.ps.ps_ProjInfo);
 		}
 		else
 			InstrCountFiltered1(node, 1);
@@ -170,16 +144,7 @@ ExecGroup(GroupState *node)
 			/*
 			 * Form and return a projection tuple using the first input tuple.
 			 */
-			TupleTableSlot *result;
-			ExprDoneCond isDone;
-
-			result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
-
-			if (isDone != ExprEndResult)
-			{
-				node->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
-				return result;
-			}
+			return ExecProject(node->ss.ps.ps_ProjInfo);
 		}
 		else
 			InstrCountFiltered1(node, 1);
@@ -246,8 +211,6 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	ExecAssignResultTypeFromTL(&grpstate->ss.ps);
 	ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
 
-	grpstate->ss.ps.ps_TupFromTlist = false;
-
 	/*
 	 * Precompute fmgr lookup data for inner loop
 	 */
@@ -283,7 +246,6 @@ ExecReScanGroup(GroupState *node)
 	PlanState  *outerPlan = outerPlanState(node);
 
 	node->grp_done = FALSE;
-	node->ss.ps.ps_TupFromTlist = false;
 	/* must clear first tuple */
 	ExecClearTuple(node->ss.ss_ScanTupleSlot);
 
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..e008a51 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -963,7 +963,7 @@ ExecHashGetHashValue(HashJoinTable hashtable,
 		/*
 		 * Get the join attribute value of the tuple
 		 */
-		keyval = ExecEvalExpr(keyexpr, econtext, &isNull, NULL);
+		keyval = ExecEvalExpr(keyexpr, econtext, &isNull);
 
 		/*
 		 * If the attribute is NULL, and the join operator is strict, then
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..45c7be2 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -66,7 +66,6 @@ ExecHashJoin(HashJoinState *node)
 	List	   *joinqual;
 	List	   *otherqual;
 	ExprContext *econtext;
-	ExprDoneCond isDone;
 	HashJoinTable hashtable;
 	TupleTableSlot *outerTupleSlot;
 	uint32		hashvalue;
@@ -83,22 +82,6 @@ ExecHashJoin(HashJoinState *node)
 	econtext = node->js.ps.ps_ExprContext;
 
 	/*
-	 * Check to see if we're still projecting out tuples from a previous join
-	 * tuple (because there is a function-returning-set in the projection
-	 * expressions).  If so, try to project another one.
-	 */
-	if (node->js.ps.ps_TupFromTlist)
-	{
-		TupleTableSlot *result;
-
-		result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
-		if (isDone == ExprMultipleResult)
-			return result;
-		/* Done with that source tuple... */
-		node->js.ps.ps_TupFromTlist = false;
-	}
-
-	/*
 	 * Reset per-tuple memory context to free any expression evaluation
 	 * storage allocated in the previous tuple cycle.  Note this can't happen
 	 * until we're done projecting out tuples from a join tuple.
@@ -315,16 +298,7 @@ ExecHashJoin(HashJoinState *node)
 					if (otherqual == NIL ||
 						ExecQual(otherqual, econtext, false))
 					{
-						TupleTableSlot *result;
-
-						result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
-
-						if (isDone != ExprEndResult)
-						{
-							node->js.ps.ps_TupFromTlist =
-								(isDone == ExprMultipleResult);
-							return result;
-						}
+						return ExecProject(node->js.ps.ps_ProjInfo);
 					}
 					else
 						InstrCountFiltered2(node, 1);
@@ -354,16 +328,7 @@ ExecHashJoin(HashJoinState *node)
 					if (otherqual == NIL ||
 						ExecQual(otherqual, econtext, false))
 					{
-						TupleTableSlot *result;
-
-						result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
-
-						if (isDone != ExprEndResult)
-						{
-							node->js.ps.ps_TupFromTlist =
-								(isDone == ExprMultipleResult);
-							return result;
-						}
+						return ExecProject(node->js.ps.ps_ProjInfo);
 					}
 					else
 						InstrCountFiltered2(node, 1);
@@ -393,16 +358,7 @@ ExecHashJoin(HashJoinState *node)
 				if (otherqual == NIL ||
 					ExecQual(otherqual, econtext, false))
 				{
-					TupleTableSlot *result;
-
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
-
-					if (isDone != ExprEndResult)
-					{
-						node->js.ps.ps_TupFromTlist =
-							(isDone == ExprMultipleResult);
-						return result;
-					}
+					return ExecProject(node->js.ps.ps_ProjInfo);
 				}
 				else
 					InstrCountFiltered2(node, 1);
@@ -586,7 +542,6 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	/* child Hash node needs to evaluate inner hash keys, too */
 	((HashState *) innerPlanState(hjstate))->hashkeys = rclauses;
 
-	hjstate->js.ps.ps_TupFromTlist = false;
 	hjstate->hj_JoinState = HJ_BUILD_HASHTABLE;
 	hjstate->hj_MatchedOuter = false;
 	hjstate->hj_OuterNotEmpty = false;
@@ -1000,7 +955,6 @@ ExecReScanHashJoin(HashJoinState *node)
 	node->hj_CurSkewBucketNo = INVALID_SKEW_BUCKET_NO;
 	node->hj_CurTuple = NULL;
 
-	node->js.ps.ps_TupFromTlist = false;
 	node->hj_MatchedOuter = false;
 	node->hj_FirstOuterTupleSlot = NULL;
 
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..edd45661 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -412,8 +412,6 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &indexstate->ss.ps);
 
-	indexstate->ss.ps.ps_TupFromTlist = false;
-
 	/*
 	 * initialize child expressions
 	 *
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..d1b1c23 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -336,8 +336,7 @@ EvalOrderByExpressions(IndexScanState *node, ExprContext *econtext)
 
 		node->iss_OrderByValues[i] = ExecEvalExpr(orderby,
 												  econtext,
-												  &node->iss_OrderByNulls[i],
-												  NULL);
+												  &node->iss_OrderByNulls[i]);
 		i++;
 	}
 
@@ -590,8 +589,7 @@ ExecIndexEvalRuntimeKeys(ExprContext *econtext,
 		 */
 		scanvalue = ExecEvalExpr(key_expr,
 								 econtext,
-								 &isNull,
-								 NULL);
+								 &isNull);
 		if (isNull)
 		{
 			scan_key->sk_argument = scanvalue;
@@ -648,8 +646,7 @@ ExecIndexEvalArrayKeys(ExprContext *econtext,
 		 */
 		arraydatum = ExecEvalExpr(array_expr,
 								  econtext,
-								  &isNull,
-								  NULL);
+								  &isNull);
 		if (isNull)
 		{
 			result = false;
@@ -837,8 +834,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &indexstate->ss.ps);
 
-	indexstate->ss.ps.ps_TupFromTlist = false;
-
 	/*
 	 * initialize child expressions
 	 *
diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c
index faf32e1..0ef9ea5 100644
--- a/src/backend/executor/nodeLimit.c
+++ b/src/backend/executor/nodeLimit.c
@@ -239,8 +239,7 @@ recompute_limits(LimitState *node)
 	{
 		val = ExecEvalExprSwitchContext(node->limitOffset,
 										econtext,
-										&isNull,
-										NULL);
+										&isNull);
 		/* Interpret NULL offset as no offset */
 		if (isNull)
 			node->offset = 0;
@@ -263,8 +262,7 @@ recompute_limits(LimitState *node)
 	{
 		val = ExecEvalExprSwitchContext(node->limitCount,
 										econtext,
-										&isNull,
-										NULL);
+										&isNull);
 		/* Interpret NULL count as no count (LIMIT ALL) */
 		if (isNull)
 		{
@@ -346,18 +344,11 @@ pass_down_bound(LimitState *node, PlanState *child_node)
 	else if (IsA(child_node, ResultState))
 	{
 		/*
-		 * An extra consideration here is that if the Result is projecting a
-		 * targetlist that contains any SRFs, we can't assume that every input
-		 * tuple generates an output tuple, so a Sort underneath might need to
-		 * return more than N tuples to satisfy LIMIT N. So we cannot use
-		 * bounded sort.
-		 *
 		 * If Result supported qual checking, we'd have to punt on seeing a
-		 * qual, too.  Note that having a resconstantqual is not a
-		 * showstopper: if that fails we're not getting any rows at all.
+		 * qual.  Note that having a resconstantqual is not a showstopper: if
+		 * that fails we're not getting any rows at all.
 		 */
-		if (outerPlanState(child_node) &&
-			!expression_returns_set((Node *) child_node->plan->targetlist))
+		if (outerPlanState(child_node))
 			pass_down_bound(node, outerPlanState(child_node));
 	}
 }
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..340a2a9 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -313,7 +313,7 @@ MJEvalOuterValues(MergeJoinState *mergestate)
 		MergeJoinClause clause = &mergestate->mj_Clauses[i];
 
 		clause->ldatum = ExecEvalExpr(clause->lexpr, econtext,
-									  &clause->lisnull, NULL);
+									  &clause->lisnull);
 		if (clause->lisnull)
 		{
 			/* match is impossible; can we end the join early? */
@@ -360,7 +360,7 @@ MJEvalInnerValues(MergeJoinState *mergestate, TupleTableSlot *innerslot)
 		MergeJoinClause clause = &mergestate->mj_Clauses[i];
 
 		clause->rdatum = ExecEvalExpr(clause->rexpr, econtext,
-									  &clause->risnull, NULL);
+									  &clause->risnull);
 		if (clause->risnull)
 		{
 			/* match is impossible; can we end the join early? */
@@ -465,19 +465,10 @@ MJFillOuter(MergeJoinState *node)
 		 * qualification succeeded.  now form the desired projection tuple and
 		 * return the slot containing it.
 		 */
-		TupleTableSlot *result;
-		ExprDoneCond isDone;
 
 		MJ_printf("ExecMergeJoin: returning outer fill tuple\n");
 
-		result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
-
-		if (isDone != ExprEndResult)
-		{
-			node->js.ps.ps_TupFromTlist =
-				(isDone == ExprMultipleResult);
-			return result;
-		}
+		return ExecProject(node->js.ps.ps_ProjInfo);
 	}
 	else
 		InstrCountFiltered2(node, 1);
@@ -506,19 +497,9 @@ MJFillInner(MergeJoinState *node)
 		 * qualification succeeded.  now form the desired projection tuple and
 		 * return the slot containing it.
 		 */
-		TupleTableSlot *result;
-		ExprDoneCond isDone;
-
 		MJ_printf("ExecMergeJoin: returning inner fill tuple\n");
 
-		result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
-
-		if (isDone != ExprEndResult)
-		{
-			node->js.ps.ps_TupFromTlist =
-				(isDone == ExprMultipleResult);
-			return result;
-		}
+		return ExecProject(node->js.ps.ps_ProjInfo);
 	}
 	else
 		InstrCountFiltered2(node, 1);
@@ -642,23 +623,6 @@ ExecMergeJoin(MergeJoinState *node)
 	doFillInner = node->mj_FillInner;
 
 	/*
-	 * Check to see if we're still projecting out tuples from a previous join
-	 * tuple (because there is a function-returning-set in the projection
-	 * expressions).  If so, try to project another one.
-	 */
-	if (node->js.ps.ps_TupFromTlist)
-	{
-		TupleTableSlot *result;
-		ExprDoneCond isDone;
-
-		result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
-		if (isDone == ExprMultipleResult)
-			return result;
-		/* Done with that source tuple... */
-		node->js.ps.ps_TupFromTlist = false;
-	}
-
-	/*
 	 * Reset per-tuple memory context to free any expression evaluation
 	 * storage allocated in the previous tuple cycle.  Note this can't happen
 	 * until we're done projecting out tuples from a join tuple.
@@ -856,20 +820,9 @@ ExecMergeJoin(MergeJoinState *node)
 						 * qualification succeeded.  now form the desired
 						 * projection tuple and return the slot containing it.
 						 */
-						TupleTableSlot *result;
-						ExprDoneCond isDone;
-
 						MJ_printf("ExecMergeJoin: returning tuple\n");
 
-						result = ExecProject(node->js.ps.ps_ProjInfo,
-											 &isDone);
-
-						if (isDone != ExprEndResult)
-						{
-							node->js.ps.ps_TupFromTlist =
-								(isDone == ExprMultipleResult);
-							return result;
-						}
+						return ExecProject(node->js.ps.ps_ProjInfo);
 					}
 					else
 						InstrCountFiltered2(node, 1);
@@ -1629,7 +1582,6 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	 * initialize join state
 	 */
 	mergestate->mj_JoinState = EXEC_MJ_INITIALIZE_OUTER;
-	mergestate->js.ps.ps_TupFromTlist = false;
 	mergestate->mj_MatchedOuter = false;
 	mergestate->mj_MatchedInner = false;
 	mergestate->mj_OuterTupleSlot = NULL;
@@ -1684,7 +1636,6 @@ ExecReScanMergeJoin(MergeJoinState *node)
 	ExecClearTuple(node->mj_MarkedTupleSlot);
 
 	node->mj_JoinState = EXEC_MJ_INITIALIZE_OUTER;
-	node->js.ps.ps_TupFromTlist = false;
 	node->mj_MatchedOuter = false;
 	node->mj_MatchedInner = false;
 	node->mj_OuterTupleSlot = NULL;
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..0e6187b 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -175,7 +175,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo,
 	econtext->ecxt_outertuple = planSlot;
 
 	/* Compute the RETURNING expressions */
-	return ExecProject(projectReturning, NULL);
+	return ExecProject(projectReturning);
 }
 
 /*
@@ -1216,7 +1216,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	}
 
 	/* Project the new tuple version */
-	ExecProject(resultRelInfo->ri_onConflictSetProj, NULL);
+	ExecProject(resultRelInfo->ri_onConflictSetProj);
 
 	/*
 	 * Note that it is possible that the target tuple has been modified in
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..5d30e75 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -82,23 +82,6 @@ ExecNestLoop(NestLoopState *node)
 	econtext = node->js.ps.ps_ExprContext;
 
 	/*
-	 * Check to see if we're still projecting out tuples from a previous join
-	 * tuple (because there is a function-returning-set in the projection
-	 * expressions).  If so, try to project another one.
-	 */
-	if (node->js.ps.ps_TupFromTlist)
-	{
-		TupleTableSlot *result;
-		ExprDoneCond isDone;
-
-		result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
-		if (isDone == ExprMultipleResult)
-			return result;
-		/* Done with that source tuple... */
-		node->js.ps.ps_TupFromTlist = false;
-	}
-
-	/*
 	 * Reset per-tuple memory context to free any expression evaluation
 	 * storage allocated in the previous tuple cycle.  Note this can't happen
 	 * until we're done projecting out tuples from a join tuple.
@@ -201,19 +184,10 @@ ExecNestLoop(NestLoopState *node)
 					 * the slot containing the result tuple using
 					 * ExecProject().
 					 */
-					TupleTableSlot *result;
-					ExprDoneCond isDone;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
-
-					if (isDone != ExprEndResult)
-					{
-						node->js.ps.ps_TupFromTlist =
-							(isDone == ExprMultipleResult);
-						return result;
-					}
+					return ExecProject(node->js.ps.ps_ProjInfo);
 				}
 				else
 					InstrCountFiltered2(node, 1);
@@ -259,19 +233,10 @@ ExecNestLoop(NestLoopState *node)
 				 * qualification was satisfied so we project and return the
 				 * slot containing the result tuple using ExecProject().
 				 */
-				TupleTableSlot *result;
-				ExprDoneCond isDone;
 
 				ENL1_printf("qualification succeeded, projecting tuple");
 
-				result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
-
-				if (isDone != ExprEndResult)
-				{
-					node->js.ps.ps_TupFromTlist =
-						(isDone == ExprMultipleResult);
-					return result;
-				}
+				return ExecProject(node->js.ps.ps_ProjInfo);
 			}
 			else
 				InstrCountFiltered2(node, 1);
@@ -377,7 +342,6 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	/*
 	 * finally, wipe the current outer tuple clean.
 	 */
-	nlstate->js.ps.ps_TupFromTlist = false;
 	nlstate->nl_NeedNewOuter = true;
 	nlstate->nl_MatchedOuter = false;
 
@@ -441,7 +405,6 @@ ExecReScanNestLoop(NestLoopState *node)
 	 * outer Vars are used as run-time keys...
 	 */
 
-	node->js.ps.ps_TupFromTlist = false;
 	node->nl_NeedNewOuter = true;
 	node->nl_MatchedOuter = false;
 }
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..3901351 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -67,10 +67,8 @@ TupleTableSlot *
 ExecResult(ResultState *node)
 {
 	TupleTableSlot *outerTupleSlot;
-	TupleTableSlot *resultSlot;
 	PlanState  *outerPlan;
 	ExprContext *econtext;
-	ExprDoneCond isDone;
 
 	econtext = node->ps.ps_ExprContext;
 
@@ -92,20 +90,6 @@ ExecResult(ResultState *node)
 	}
 
 	/*
-	 * Check to see if we're still projecting out tuples from a previous scan
-	 * tuple (because there is a function-returning-set in the projection
-	 * expressions).  If so, try to project another one.
-	 */
-	if (node->ps.ps_TupFromTlist)
-	{
-		resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone);
-		if (isDone == ExprMultipleResult)
-			return resultSlot;
-		/* Done with that source tuple... */
-		node->ps.ps_TupFromTlist = false;
-	}
-
-	/*
 	 * Reset per-tuple memory context to free any expression evaluation
 	 * storage allocated in the previous tuple cycle.  Note this can't happen
 	 * until we're done projecting out tuples from a scan tuple.
@@ -147,18 +131,8 @@ ExecResult(ResultState *node)
 			node->rs_done = true;
 		}
 
-		/*
-		 * form the result tuple using ExecProject(), and return it --- unless
-		 * the projection produces an empty set, in which case we must loop
-		 * back to see if there are more outerPlan tuples.
-		 */
-		resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone);
-
-		if (isDone != ExprEndResult)
-		{
-			node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
-			return resultSlot;
-		}
+		/* form the result tuple using ExecProject(), and return it */
+		return ExecProject(node->ps.ps_ProjInfo);
 	}
 
 	return NULL;
@@ -228,8 +202,6 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &resstate->ps);
 
-	resstate->ps.ps_TupFromTlist = false;
-
 	/*
 	 * tuple table initialization
 	 */
@@ -295,7 +267,6 @@ void
 ExecReScanResult(ResultState *node)
 {
 	node->rs_done = false;
-	node->ps.ps_TupFromTlist = false;
 	node->rs_checkqual = (node->resconstantqual == NULL) ? false : true;
 
 	/*
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..64396e1 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -188,8 +188,6 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	 */
 	InitScanRelation(scanstate, estate, eflags);
 
-	scanstate->ss.ps.ps_TupFromTlist = false;
-
 	/*
 	 * Initialize result tuple type and projection info.
 	 */
@@ -299,8 +297,7 @@ tablesample_init(SampleScanState *scanstate)
 
 		params[i] = ExecEvalExprSwitchContext(argstate,
 											  econtext,
-											  &isnull,
-											  NULL);
+											  &isnull);
 		if (isnull)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_TABLESAMPLE_ARGUMENT),
@@ -312,8 +309,7 @@ tablesample_init(SampleScanState *scanstate)
 	{
 		datum = ExecEvalExprSwitchContext(scanstate->repeatable,
 										  econtext,
-										  &isnull,
-										  NULL);
+										  &isnull);
 		if (isnull)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_TABLESAMPLE_REPEAT),
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..477dc42 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -206,8 +206,6 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	 */
 	InitScanRelation(scanstate, estate, eflags);
 
-	scanstate->ss.ps.ps_TupFromTlist = false;
-
 	/*
 	 * Initialize result tuple type and projection info.
 	 */
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..5800ca8 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -41,12 +41,10 @@
 
 static Datum ExecSubPlan(SubPlanState *node,
 			ExprContext *econtext,
-			bool *isNull,
-			ExprDoneCond *isDone);
+			bool *isNull);
 static Datum ExecAlternativeSubPlan(AlternativeSubPlanState *node,
 					   ExprContext *econtext,
-					   bool *isNull,
-					   ExprDoneCond *isDone);
+					   bool *isNull);
 static Datum ExecHashSubPlan(SubPlanState *node,
 				ExprContext *econtext,
 				bool *isNull);
@@ -69,15 +67,12 @@ static bool slotNoNulls(TupleTableSlot *slot);
 static Datum
 ExecSubPlan(SubPlanState *node,
 			ExprContext *econtext,
-			bool *isNull,
-			ExprDoneCond *isDone)
+			bool *isNull)
 {
 	SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
 
 	/* Set default values for result flags: non-null, not a set result */
 	*isNull = false;
-	if (isDone)
-		*isDone = ExprSingleResult;
 
 	/* Sanity checks */
 	if (subplan->subLinkType == CTE_SUBLINK)
@@ -128,7 +123,7 @@ ExecHashSubPlan(SubPlanState *node,
 	 * have to set the econtext to use (hack alert!).
 	 */
 	node->projLeft->pi_exprContext = econtext;
-	slot = ExecProject(node->projLeft, NULL);
+	slot = ExecProject(node->projLeft);
 
 	/*
 	 * Note: because we are typically called in a per-tuple context, we have
@@ -285,8 +280,7 @@ ExecScanSubPlan(SubPlanState *node,
 
 		prm->value = ExecEvalExprSwitchContext((ExprState *) lfirst(pvar),
 											   econtext,
-											   &(prm->isnull),
-											   NULL);
+											   &(prm->isnull));
 		planstate->chgParam = bms_add_member(planstate->chgParam, paramid);
 	}
 
@@ -403,7 +397,7 @@ ExecScanSubPlan(SubPlanState *node,
 		}
 
 		rowresult = ExecEvalExprSwitchContext(node->testexpr, econtext,
-											  &rownull, NULL);
+											  &rownull);
 
 		if (subLinkType == ANY_SUBLINK)
 		{
@@ -570,7 +564,7 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
 										  &(prmdata->isnull));
 			col++;
 		}
-		slot = ExecProject(node->projRight, NULL);
+		slot = ExecProject(node->projRight);
 
 		/*
 		 * If result contains any nulls, store separately or not at all.
@@ -987,8 +981,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 
 		prm->value = ExecEvalExprSwitchContext((ExprState *) lfirst(pvar),
 											   econtext,
-											   &(prm->isnull),
-											   NULL);
+											   &(prm->isnull));
 		planstate->chgParam = bms_add_member(planstate->chgParam, paramid);
 	}
 
@@ -1224,8 +1217,7 @@ ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent)
 static Datum
 ExecAlternativeSubPlan(AlternativeSubPlanState *node,
 					   ExprContext *econtext,
-					   bool *isNull,
-					   ExprDoneCond *isDone)
+					   bool *isNull)
 {
 	/* Just pass control to the active subplan */
 	SubPlanState *activesp = (SubPlanState *) list_nth(node->subplans,
@@ -1233,8 +1225,5 @@ ExecAlternativeSubPlan(AlternativeSubPlanState *node,
 
 	Assert(IsA(activesp, SubPlanState));
 
-	return ExecSubPlan(activesp,
-					   econtext,
-					   isNull,
-					   isDone);
+	return ExecSubPlan(activesp, econtext, isNull);
 }
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..4de7024 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -138,8 +138,6 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	 */
 	subquerystate->subplan = ExecInitNode(node->subplan, estate, eflags);
 
-	subquerystate->ss.ps.ps_TupFromTlist = false;
-
 	/*
 	 * Initialize scan tuple type (needed by ExecAssignScanProjectionInfo)
 	 */
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e1c736c 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -104,8 +104,7 @@ TidListCreate(TidScanState *tidstate)
 			itemptr = (ItemPointer)
 				DatumGetPointer(ExecEvalExprSwitchContext(exstate,
 														  econtext,
-														  &isNull,
-														  NULL));
+														  &isNull));
 			if (!isNull &&
 				ItemPointerIsValid(itemptr) &&
 				ItemPointerGetBlockNumber(itemptr) < nblocks)
@@ -133,8 +132,7 @@ TidListCreate(TidScanState *tidstate)
 			exstate = (ExprState *) lsecond(saexstate->fxprstate.args);
 			arraydatum = ExecEvalExprSwitchContext(exstate,
 												   econtext,
-												   &isNull,
-												   NULL);
+												   &isNull);
 			if (isNull)
 				continue;
 			itemarray = DatumGetArrayTypeP(arraydatum);
@@ -469,8 +467,6 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	 */
 	ExecAssignExprContext(estate, &tidstate->ss.ps);
 
-	tidstate->ss.ps.ps_TupFromTlist = false;
-
 	/*
 	 * initialize child expressions
 	 */
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..18c8ae9 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -140,8 +140,7 @@ ValuesNext(ValuesScanState *node)
 
 			values[resind] = ExecEvalExpr(estate,
 										  econtext,
-										  &isnull[resind],
-										  NULL);
+										  &isnull[resind]);
 
 			/*
 			 * We must force any R/W expanded datums to read-only state, in
@@ -272,8 +271,6 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 		scanstate->exprlists[i++] = (List *) lfirst(vtl);
 	}
 
-	scanstate->ss.ps.ps_TupFromTlist = false;
-
 	/*
 	 * Initialize result tuple type and projection info.
 	 */
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..42550c9 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -256,7 +256,7 @@ advance_windowaggregate(WindowAggState *winstate,
 	if (filter)
 	{
 		bool		isnull;
-		Datum		res = ExecEvalExpr(filter, econtext, &isnull, NULL);
+		Datum		res = ExecEvalExpr(filter, econtext, &isnull);
 
 		if (isnull || !DatumGetBool(res))
 		{
@@ -272,7 +272,7 @@ advance_windowaggregate(WindowAggState *winstate,
 		ExprState  *argstate = (ExprState *) lfirst(arg);
 
 		fcinfo->arg[i] = ExecEvalExpr(argstate, econtext,
-									  &fcinfo->argnull[i], NULL);
+									  &fcinfo->argnull[i]);
 		i++;
 	}
 
@@ -418,7 +418,7 @@ advance_windowaggregate_base(WindowAggState *winstate,
 	if (filter)
 	{
 		bool		isnull;
-		Datum		res = ExecEvalExpr(filter, econtext, &isnull, NULL);
+		Datum		res = ExecEvalExpr(filter, econtext, &isnull);
 
 		if (isnull || !DatumGetBool(res))
 		{
@@ -434,7 +434,7 @@ advance_windowaggregate_base(WindowAggState *winstate,
 		ExprState  *argstate = (ExprState *) lfirst(arg);
 
 		fcinfo->arg[i] = ExecEvalExpr(argstate, econtext,
-									  &fcinfo->argnull[i], NULL);
+									  &fcinfo->argnull[i]);
 		i++;
 	}
 
@@ -1551,15 +1551,12 @@ update_frametailpos(WindowObject winobj, TupleTableSlot *slot)
  *	ExecWindowAgg receives tuples from its outer subplan and
  *	stores them into a tuplestore, then processes window functions.
  *	This node doesn't reduce nor qualify any row so the number of
- *	returned rows is exactly the same as its outer subplan's result
- *	(ignoring the case of SRFs in the targetlist, that is).
+ *	returned rows is exactly the same as its outer subplan's result.
  * -----------------
  */
 TupleTableSlot *
 ExecWindowAgg(WindowAggState *winstate)
 {
-	TupleTableSlot *result;
-	ExprDoneCond isDone;
 	ExprContext *econtext;
 	int			i;
 	int			numfuncs;
@@ -1568,23 +1565,6 @@ ExecWindowAgg(WindowAggState *winstate)
 		return NULL;
 
 	/*
-	 * Check to see if we're still projecting out tuples from a previous
-	 * output tuple (because there is a function-returning-set in the
-	 * projection expressions).  If so, try to project another one.
-	 */
-	if (winstate->ss.ps.ps_TupFromTlist)
-	{
-		TupleTableSlot *result;
-		ExprDoneCond isDone;
-
-		result = ExecProject(winstate->ss.ps.ps_ProjInfo, &isDone);
-		if (isDone == ExprMultipleResult)
-			return result;
-		/* Done with that source tuple... */
-		winstate->ss.ps.ps_TupFromTlist = false;
-	}
-
-	/*
 	 * Compute frame offset values, if any, during first call.
 	 */
 	if (winstate->all_first)
@@ -1601,8 +1581,7 @@ ExecWindowAgg(WindowAggState *winstate)
 			Assert(winstate->startOffset != NULL);
 			value = ExecEvalExprSwitchContext(winstate->startOffset,
 											  econtext,
-											  &isnull,
-											  NULL);
+											  &isnull);
 			if (isnull)
 				ereport(ERROR,
 						(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
@@ -1627,8 +1606,7 @@ ExecWindowAgg(WindowAggState *winstate)
 			Assert(winstate->endOffset != NULL);
 			value = ExecEvalExprSwitchContext(winstate->endOffset,
 											  econtext,
-											  &isnull,
-											  NULL);
+											  &isnull);
 			if (isnull)
 				ereport(ERROR,
 						(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
@@ -1651,7 +1629,6 @@ ExecWindowAgg(WindowAggState *winstate)
 		winstate->all_first = false;
 	}
 
-restart:
 	if (winstate->buffer == NULL)
 	{
 		/* Initialize for first partition and set current row = 0 */
@@ -1743,17 +1720,8 @@ restart:
 	 * evaluated with respect to that row.
 	 */
 	econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
-	result = ExecProject(winstate->ss.ps.ps_ProjInfo, &isDone);
 
-	if (isDone == ExprEndResult)
-	{
-		/* SRF in tlist returned no rows, so advance to next input tuple */
-		goto restart;
-	}
-
-	winstate->ss.ps.ps_TupFromTlist =
-		(isDone == ExprMultipleResult);
-	return result;
+	return ExecProject(winstate->ss.ps.ps_ProjInfo);
 }
 
 /* -----------------
@@ -1867,8 +1835,6 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	ExecAssignResultTypeFromTL(&winstate->ss.ps);
 	ExecAssignProjectionInfo(&winstate->ss.ps, NULL);
 
-	winstate->ss.ps.ps_TupFromTlist = false;
-
 	/* Set up data for comparing tuples */
 	if (node->partNumCols > 0)
 		winstate->partEqfunctions = execTuplesMatchPrepare(node->partNumCols,
@@ -2061,8 +2027,6 @@ ExecReScanWindowAgg(WindowAggState *node)
 	ExprContext *econtext = node->ss.ps.ps_ExprContext;
 
 	node->all_done = false;
-
-	node->ss.ps.ps_TupFromTlist = false;
 	node->all_first = true;
 
 	/* release tuplestore et al */
@@ -2685,7 +2649,7 @@ WinGetFuncArgInPartition(WindowObject winobj, int argno,
 		}
 		econtext->ecxt_outertuple = slot;
 		return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
-							econtext, isnull, NULL);
+							econtext, isnull);
 	}
 }
 
@@ -2784,7 +2748,7 @@ WinGetFuncArgInFrame(WindowObject winobj, int argno,
 		}
 		econtext->ecxt_outertuple = slot;
 		return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
-							econtext, isnull, NULL);
+							econtext, isnull);
 	}
 }
 
@@ -2814,5 +2778,5 @@ WinGetFuncArgCurrent(WindowObject winobj, int argno, bool *isnull)
 
 	econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
 	return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
-						econtext, isnull, NULL);
+						econtext, isnull);
 }
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..dbb8ea3 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -174,8 +174,6 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	 */
 	ExecAssignResultTypeFromTL(&scanstate->ss.ps);
 
-	scanstate->ss.ps.ps_TupFromTlist = false;
-
 	return scanstate;
 }
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 986c92b..73862a2 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -151,8 +151,7 @@ static PathTarget *make_window_input_target(PlannerInfo *root,
 static List *make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc,
 						 List *tlist);
 static PathTarget *make_sort_input_target(PlannerInfo *root,
-					   PathTarget *final_target,
-					   bool *have_postponed_srfs);
+					   PathTarget *final_target);
 
 
 /*****************************************************************************
@@ -1443,8 +1442,6 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
 	int64		offset_est = 0;
 	int64		count_est = 0;
 	double		limit_tuples = -1.0;
-	bool		have_postponed_srfs = false;
-	double		tlist_rows;
 	PathTarget *final_target;
 	RelOptInfo *current_rel;
 	RelOptInfo *final_rel;
@@ -1710,10 +1707,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
 		 * Figure out whether there's a hard limit on the number of rows that
 		 * query_planner's result subplan needs to return.  Even if we know a
 		 * hard limit overall, it doesn't apply if the query has any
-		 * grouping/aggregation operations.  (XXX it also doesn't apply if the
-		 * tlist contains any SRFs; but checking for that here seems more
-		 * costly than it's worth, since root->limit_tuples is only used for
-		 * cost estimates, and only in a small number of cases.)
+		 * grouping/aggregation operations.
 		 */
 		if (parse->groupClause ||
 			parse->groupingSets ||
@@ -1757,8 +1751,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
 		 */
 		if (parse->sortClause)
 			sort_input_target = make_sort_input_target(root,
-													   final_target,
-													   &have_postponed_srfs);
+													   final_target);
 		else
 			sort_input_target = final_target;
 
@@ -1915,50 +1908,17 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
 	/*
 	 * If ORDER BY was given, consider ways to implement that, and generate a
 	 * new upperrel containing only paths that emit the correct ordering and
-	 * project the correct final_target.  We can apply the original
-	 * limit_tuples limit in sort costing here, but only if there are no
-	 * postponed SRFs.
+	 * project the correct final_target.
 	 */
 	if (parse->sortClause)
 	{
 		current_rel = create_ordered_paths(root,
 										   current_rel,
 										   final_target,
-										   have_postponed_srfs ? -1.0 :
 										   limit_tuples);
 	}
 
 	/*
-	 * If there are set-returning functions in the tlist, scale up the output
-	 * rowcounts of all surviving Paths to account for that.  Note that if any
-	 * SRFs appear in sorting or grouping columns, we'll have underestimated
-	 * the numbers of rows passing through earlier steps; but that's such a
-	 * weird usage that it doesn't seem worth greatly complicating matters to
-	 * account for it.
-	 */
-	tlist_rows = tlist_returns_set_rows(tlist);
-	if (tlist_rows > 1)
-	{
-		foreach(lc, current_rel->pathlist)
-		{
-			Path	   *path = (Path *) lfirst(lc);
-
-			/*
-			 * We assume that execution costs of the tlist as such were
-			 * already accounted for.  However, it still seems appropriate to
-			 * charge something more for the executor's general costs of
-			 * processing the added tuples.  The cost is probably less than
-			 * cpu_tuple_cost, though, so we arbitrarily use half of that.
-			 */
-			path->total_cost += path->rows * (tlist_rows - 1) *
-				cpu_tuple_cost / 2;
-
-			path->rows *= tlist_rows;
-		}
-		/* No need to run set_cheapest; we're keeping all paths anyway. */
-	}
-
-	/*
 	 * Now we are prepared to build the final-output upperrel.
 	 */
 	final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);
@@ -4907,20 +4867,12 @@ make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc,
  *
  * Our current policy is to postpone volatile expressions till after the sort
  * unconditionally (assuming that that's possible, ie they are in plain tlist
- * columns and not ORDER BY/GROUP BY/DISTINCT columns).  We also prefer to
- * postpone set-returning expressions, because running them beforehand would
- * bloat the sort dataset, and because it might cause unexpected output order
- * if the sort isn't stable.  However there's a constraint on that: all SRFs
- * in the tlist should be evaluated at the same plan step, so that they can
- * run in sync in ExecTargetList.  So if any SRFs are in sort columns, we
- * mustn't postpone any SRFs.  (Note that in principle that policy should
- * probably get applied to the group/window input targetlists too, but we
- * have not done that historically.)  Lastly, expensive expressions are
- * postponed if there is a LIMIT, or if root->tuple_fraction shows that
- * partial evaluation of the query is possible (if neither is true, we expect
- * to have to evaluate the expressions for every row anyway), or if there are
- * any volatile or set-returning expressions (since once we've put in a
- * projection at all, it won't cost any more to postpone more stuff).
+ * columns and not ORDER BY/GROUP BY/DISTINCT columns).  Also, expensive
+ * expressions are postponed if there is a LIMIT, or if root->tuple_fraction
+ * shows that partial evaluation of the query is possible (if neither is true,
+ * we expect to have to evaluate the expressions for every row anyway), or if
+ * there are any volatile or set-returning expressions (since once we've put
+ * in a projection at all, it won't cost any more to postpone more stuff).
  *
  * Another issue that could potentially be considered here is that
  * evaluating tlist expressions could result in data that's either wider
@@ -4944,30 +4896,21 @@ make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc,
  * computed earlier.
  *
  * 'final_target' is the query's final target list (in PathTarget form)
- * 'have_postponed_srfs' is an output argument, see below
  *
  * The result is the PathTarget to be computed by the plan node immediately
  * below the Sort step (and the Distinct step, if any).  This will be
  * exactly final_target if we decide a projection step wouldn't be helpful.
- *
- * In addition, *have_postponed_srfs is set to TRUE if we choose to postpone
- * any set-returning functions to after the Sort.
  */
 static PathTarget *
 make_sort_input_target(PlannerInfo *root,
-					   PathTarget *final_target,
-					   bool *have_postponed_srfs)
+					   PathTarget *final_target)
 {
 	Query	   *parse = root->parse;
 	PathTarget *input_target;
 	int			ncols;
-	bool	   *col_is_srf;
 	bool	   *postpone_col;
-	bool		have_srf;
 	bool		have_volatile;
 	bool		have_expensive;
-	bool		have_srf_sortcols;
-	bool		postpone_srfs;
 	List	   *postponable_cols;
 	List	   *postponable_vars;
 	int			i;
@@ -4976,13 +4919,10 @@ make_sort_input_target(PlannerInfo *root,
 	/* Shouldn't get here unless query has ORDER BY */
 	Assert(parse->sortClause);
 
-	*have_postponed_srfs = false;		/* default result */
-
 	/* Inspect tlist and collect per-column information */
 	ncols = list_length(final_target->exprs);
-	col_is_srf = (bool *) palloc0(ncols * sizeof(bool));
 	postpone_col = (bool *) palloc0(ncols * sizeof(bool));
-	have_srf = have_volatile = have_expensive = have_srf_sortcols = false;
+	have_volatile = have_expensive = false;
 
 	i = 0;
 	foreach(lc, final_target->exprs)
@@ -5000,16 +4940,9 @@ make_sort_input_target(PlannerInfo *root,
 		if (get_pathtarget_sortgroupref(final_target, i) == 0)
 		{
 			/*
-			 * Check for SRF or volatile functions.  Check the SRF case first
-			 * because we must know whether we have any postponed SRFs.
+			 * Check for volatile functions.
 			 */
-			if (expression_returns_set((Node *) expr))
-			{
-				/* We'll decide below whether these are postponable */
-				col_is_srf[i] = true;
-				have_srf = true;
-			}
-			else if (contain_volatile_functions((Node *) expr))
+			if (contain_volatile_functions((Node *) expr))
 			{
 				/* Unconditionally postpone */
 				postpone_col[i] = true;
@@ -5038,39 +4971,19 @@ make_sort_input_target(PlannerInfo *root,
 				}
 			}
 		}
-		else
-		{
-			/* For sortgroupref cols, just check if any contain SRFs */
-			if (!have_srf_sortcols &&
-				expression_returns_set((Node *) expr))
-				have_srf_sortcols = true;
-		}
 
 		i++;
 	}
 
 	/*
-	 * We can postpone SRFs if we have some but none are in sortgroupref cols.
-	 */
-	postpone_srfs = (have_srf && !have_srf_sortcols);
-
-	/*
 	 * If we don't need a post-sort projection, just return final_target.
 	 */
-	if (!(postpone_srfs || have_volatile ||
+	if (!(have_volatile ||
 		  (have_expensive &&
 		   (parse->limitCount || root->tuple_fraction > 0))))
 		return final_target;
 
 	/*
-	 * Report whether the post-sort projection will contain set-returning
-	 * functions.  This is important because it affects whether the Sort can
-	 * rely on the query's LIMIT (if any) to bound the number of rows it needs
-	 * to return.
-	 */
-	*have_postponed_srfs = postpone_srfs;
-
-	/*
 	 * Construct the sort-input target, taking all non-postponable columns and
 	 * then adding Vars, PlaceHolderVars, Aggrefs, and WindowFuncs found in
 	 * the postponable ones.
@@ -5083,7 +4996,7 @@ make_sort_input_target(PlannerInfo *root,
 	{
 		Expr	   *expr = (Expr *) lfirst(lc);
 
-		if (postpone_col[i] || (postpone_srfs && col_is_srf[i]))
+		if (postpone_col[i])
 			postponable_cols = lappend(postponable_cols, expr);
 		else
 			add_column_to_pathtarget(input_target, expr,
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 9c502bd..8f7a8bd 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -807,11 +807,13 @@ find_window_functions_walker(Node *node, WindowFuncLists *lists)
  *	  Estimate the number of rows returned by a set-returning expression.
  *	  The result is 1 if there are no set-returning functions.
  *
- * We use the product of the rowcount estimates of all the functions in
- * the given tree (this corresponds to the behavior of ExecMakeFunctionResult
- * for nested set-returning functions).
+ * We use the product of the rowcount estimates of all the functions in the
+ * given tree (this corresponds to the behavior of ExecMakeFunctionResult for
+ * nested set-returning functions).
  *
  * Note: keep this in sync with expression_returns_set() in nodes/nodeFuncs.c.
+ *
+ * FIXME: This possibly be simplified now that targetlist SRFs are gone.
  */
 double
 expression_returns_set_rows(Node *clause)
@@ -881,40 +883,6 @@ expression_returns_set_rows_walker(Node *node, double *count)
 								  (void *) count);
 }
 
-/*
- * tlist_returns_set_rows
- *	  Estimate the number of rows returned by a set-returning targetlist.
- *	  The result is 1 if there are no set-returning functions.
- *
- * Here, the result is the largest rowcount estimate of any of the tlist's
- * expressions, not the product as you would get from naively applying
- * expression_returns_set_rows() to the whole tlist.  The behavior actually
- * implemented by ExecTargetList produces a number of rows equal to the least
- * common multiple of the expression rowcounts, so that the product would be
- * a worst-case estimate that is typically not realistic.  Taking the max as
- * we do here is a best-case estimate that might not be realistic either,
- * but it's probably closer for typical usages.  We don't try to compute the
- * actual LCM because we're working with very approximate estimates, so their
- * LCM would be unduly noisy.
- */
-double
-tlist_returns_set_rows(List *tlist)
-{
-	double		result = 1;
-	ListCell   *lc;
-
-	foreach(lc, tlist)
-	{
-		TargetEntry *tle = (TargetEntry *) lfirst(lc);
-		double		colresult;
-
-		colresult = expression_returns_set_rows((Node *) tle->expr);
-		if (result < colresult)
-			result = colresult;
-	}
-	return result;
-}
-
 
 /*****************************************************************************
  *		Subplan clause manipulation
@@ -4899,7 +4867,7 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
 
 	/*
 	 * Forget it if the function is not SQL-language or has other showstopper
-	 * properties.  (The nargs check is just paranoia.)
+	 * properties.  (The nargs and retset checks are just paranoia.)
 	 */
 	if (funcform->prolang != SQLlanguageId ||
 		funcform->prosecdef ||
@@ -5287,7 +5255,7 @@ evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,
 	 */
 	const_val = ExecEvalExprSwitchContext(exprstate,
 										  GetPerTupleExprContext(estate),
-										  &const_is_null, NULL);
+										  &const_is_null);
 
 	/* Get info needed about result datatype */
 	get_typlenbyval(result_type, &resultTypLen, &resultTypByVal);
diff --git a/src/backend/optimizer/util/predtest.c b/src/backend/optimizer/util/predtest.c
index 2c2efb1..0c59fe8 100644
--- a/src/backend/optimizer/util/predtest.c
+++ b/src/backend/optimizer/util/predtest.c
@@ -1596,7 +1596,7 @@ operator_predicate_proof(Expr *predicate, Node *clause, bool refute_it)
 	/* And execute it. */
 	test_result = ExecEvalExprSwitchContext(test_exprstate,
 											GetPerTupleExprContext(estate),
-											&isNull, NULL);
+											&isNull);
 
 	/* Get back to outer memory context */
 	MemoryContextSwitchTo(oldcontext);
diff --git a/src/backend/utils/adt/domains.c b/src/backend/utils/adt/domains.c
index 19ee4ce..c568c6c 100644
--- a/src/backend/utils/adt/domains.c
+++ b/src/backend/utils/adt/domains.c
@@ -164,7 +164,7 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
 
 					conResult = ExecEvalExprSwitchContext(con->check_expr,
 														  econtext,
-														  &conIsNull, NULL);
+														  &conIsNull);
 
 					if (!conIsNull &&
 						!DatumGetBool(conResult))
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 7ed5bcb..65bf6ad 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -603,7 +603,7 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
 		bool		isnull;
 		char	   *str;
 
-		value = ExecEvalExpr(e, econtext, &isnull, NULL);
+		value = ExecEvalExpr(e, econtext, &isnull);
 		if (isnull)
 			str = NULL;
 		else
@@ -620,7 +620,7 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
 		bool		isnull;
 		char	   *str;
 
-		value = ExecEvalExpr(e, econtext, &isnull, NULL);
+		value = ExecEvalExpr(e, econtext, &isnull);
 		/* here we can just forget NULL elements immediately */
 		if (!isnull)
 		{
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 7f11285..79a31a9 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -69,8 +69,8 @@
  * now it's just a macro invoking the function pointed to by an ExprState
  * node.  Beware of double evaluation of the ExprState argument!
  */
-#define ExecEvalExpr(expr, econtext, isNull, isDone) \
-	((*(expr)->evalfunc) (expr, econtext, isNull, isDone))
+#define ExecEvalExpr(expr, econtext, isNull) \
+	((*(expr)->evalfunc) (expr, econtext, isNull))
 
 
 /* Hook for plugins to get control in ExecutorStart() */
@@ -235,18 +235,17 @@ extern Datum GetAttributeByNum(HeapTupleHeader tuple, AttrNumber attrno,
 extern Datum GetAttributeByName(HeapTupleHeader tuple, const char *attname,
 				   bool *isNull);
 extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econtext,
-						  bool *isNull, ExprDoneCond *isDone);
-extern ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo,
+						  bool *isNull);
+extern void ExecEvalFuncArgs(FunctionCallInfo fcinfo,
 				 List *argList, ExprContext *econtext);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern void ExecInitFcache(Oid foid, Oid input_collation, FuncExprState *fcache,
-			MemoryContext fcacheCxt, bool needDescForSets);
+			MemoryContext fcacheCxt);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
 extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo);
 
 /*
  * prototypes from functions in execScan.c
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..043f969 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -243,7 +243,6 @@ typedef struct ProjectionInfo
 	List	   *pi_targetlist;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
 	bool		pi_directMap;
 	int			pi_numSimpleVars;
 	int		   *pi_varSlotOffsets;
@@ -569,8 +568,7 @@ typedef struct ExprState ExprState;
 
 typedef Datum (*ExprStateEvalFunc) (ExprState *expression,
 												ExprContext *econtext,
-												bool *isNull,
-												ExprDoneCond *isDone);
+												bool *isNull);
 
 struct ExprState
 {
@@ -692,21 +690,13 @@ typedef struct FuncExprState
 	TupleTableSlot *funcResultSlot;
 
 	/*
-	 * In some cases we need to compute a tuple descriptor for the function's
-	 * output.  If so, it's stored here.
-	 */
-	TupleDesc	funcResultDesc;
-	bool		funcReturnsTuple;		/* valid when funcResultDesc isn't
-										 * NULL */
-
-	/*
 	 * setArgsValid is true when we are evaluating a set-returning function
 	 * that uses value-per-call mode and we are in the middle of a call
 	 * series; we want to pass the same argument values to the function again
 	 * (and again, until it returns ExprEndResult).  This indicates that
 	 * fcinfo_data already contains valid argument data.
 	 */
-	bool		setArgsValid;
+	bool		setArgsValid2;
 
 	/*
 	 * Flag to remember whether we found a set-valued argument to the
@@ -1057,8 +1047,6 @@ typedef struct PlanState
 	TupleTableSlot *ps_ResultTupleSlot; /* slot for my result tuples */
 	ExprContext *ps_ExprContext;	/* node's expression-evaluation context */
 	ProjectionInfo *ps_ProjInfo;	/* info for doing tuple projection */
-	bool		ps_TupFromTlist;/* state flag for processing set-valued
-								 * functions in targetlist */
 } PlanState;
 
 /* ----------------
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index 7fb5005..e2d44ce 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -54,7 +54,6 @@ extern bool contain_window_function(Node *clause);
 extern WindowFuncLists *find_window_functions(Node *clause, Index maxWinRef);
 
 extern double expression_returns_set_rows(Node *clause);
-extern double tlist_returns_set_rows(List *tlist);
 
 extern bool contain_subplans(Node *clause);
 
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index f9b3b22..d8905c9 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -5644,8 +5644,7 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
 	 */
 	*result = ExecEvalExpr(expr->expr_simple_state,
 						   econtext,
-						   isNull,
-						   NULL);
+						   isNull);
 
 	/* Assorted cleanup */
 	expr->expr_simple_in_use = false;
@@ -6312,7 +6311,7 @@ exec_cast_value(PLpgSQL_execstate *estate,
 			cast_entry->cast_in_use = true;
 
 			value = ExecEvalExpr(cast_entry->cast_exprstate, econtext,
-								 isnull, NULL);
+								 isnull);
 
 			cast_entry->cast_in_use = false;
 
-- 
2.9.3

