diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 49fa4156de4add4aba10cb538620f78654964844..47840d42ebc5fa08c242124af62a82df6f027d3b 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.313 2008/08/25 22:42:32 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.314 2008/10/31 21:07:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -76,7 +76,7 @@ typedef struct evalPlanQual
 static void InitPlan(QueryDesc *queryDesc, int eflags);
 static void ExecCheckPlanOutput(Relation resultRel, List *targetList);
 static void ExecEndPlan(PlanState *planstate, EState *estate);
-static TupleTableSlot *ExecutePlan(EState *estate, PlanState *planstate,
+static void ExecutePlan(EState *estate, PlanState *planstate,
 			CmdType operation,
 			long numberTuples,
 			ScanDirection direction,
@@ -220,26 +220,28 @@ ExecutorStart(QueryDesc *queryDesc, int eflags)
  *		Note: count = 0 is interpreted as no portal limit, i.e., run to
  *		completion.
  *
+ *		There is no return value, but output tuples (if any) are sent to
+ *		the destination receiver specified in the QueryDesc; and the number
+ *		of tuples processed at the top level can be found in
+ *		estate->es_processed.
+ *
  *		We provide a function hook variable that lets loadable plugins
  *		get control when ExecutorRun is called.  Such a plugin would
  *		normally call standard_ExecutorRun().
  *
  * ----------------------------------------------------------------
  */
-TupleTableSlot *
+void
 ExecutorRun(QueryDesc *queryDesc,
 			ScanDirection direction, long count)
 {
-	TupleTableSlot *result;
-
 	if (ExecutorRun_hook)
-		result = (*ExecutorRun_hook) (queryDesc, direction, count);
+		(*ExecutorRun_hook) (queryDesc, direction, count);
 	else
-		result = standard_ExecutorRun(queryDesc, direction, count);
-	return result;
+		standard_ExecutorRun(queryDesc, direction, count);
 }
 
-TupleTableSlot *
+void
 standard_ExecutorRun(QueryDesc *queryDesc,
 					 ScanDirection direction, long count)
 {
@@ -247,7 +249,6 @@ standard_ExecutorRun(QueryDesc *queryDesc,
 	CmdType		operation;
 	DestReceiver *dest;
 	bool		sendTuples;
-	TupleTableSlot *result;
 	MemoryContext oldcontext;
 
 	/* sanity checks */
@@ -283,15 +284,13 @@ standard_ExecutorRun(QueryDesc *queryDesc,
 	/*
 	 * run plan
 	 */
-	if (ScanDirectionIsNoMovement(direction))
-		result = NULL;
-	else
-		result = ExecutePlan(estate,
-							 queryDesc->planstate,
-							 operation,
-							 count,
-							 direction,
-							 dest);
+	if (!ScanDirectionIsNoMovement(direction))
+		ExecutePlan(estate,
+					queryDesc->planstate,
+					operation,
+					count,
+					direction,
+					dest);
 
 	/*
 	 * shutdown tuple receiver, if we started it
@@ -300,8 +299,6 @@ standard_ExecutorRun(QueryDesc *queryDesc,
 		(*dest->rShutdown) (dest);
 
 	MemoryContextSwitchTo(oldcontext);
-
-	return result;
 }
 
 /* ----------------------------------------------------------------
@@ -1271,19 +1268,16 @@ ExecEndPlan(PlanState *planstate, EState *estate)
 /* ----------------------------------------------------------------
  *		ExecutePlan
  *
- *		processes the query plan to retrieve 'numberTuples' tuples in the
- *		direction specified.
- *
- *		Retrieves all tuples if numberTuples is 0
+ *		Processes the query plan until we have processed 'numberTuples' tuples,
+ *		moving in the specified direction.
  *
- *		result is either a slot containing the last tuple in the case
- *		of a SELECT or NULL otherwise.
+ *		Runs to completion if numberTuples is 0
  *
  * Note: the ctid attribute is a 'junk' attribute that is removed before the
  * user can see it
  * ----------------------------------------------------------------
  */
-static TupleTableSlot *
+static void
 ExecutePlan(EState *estate,
 			PlanState *planstate,
 			CmdType operation,
@@ -1297,13 +1291,11 @@ ExecutePlan(EState *estate,
 	ItemPointer tupleid = NULL;
 	ItemPointerData tuple_ctid;
 	long		current_tuple_count;
-	TupleTableSlot *result;
 
 	/*
 	 * initialize local variables
 	 */
 	current_tuple_count = 0;
-	result = NULL;
 
 	/*
 	 * Set the direction.
@@ -1332,7 +1324,6 @@ ExecutePlan(EState *estate,
 	/*
 	 * Loop until we've processed the proper number of tuples from the plan.
 	 */
-
 	for (;;)
 	{
 		/* Reset the per-output-tuple exprcontext */
@@ -1353,13 +1344,10 @@ lnext:	;
 
 		/*
 		 * if the tuple is null, then we assume there is nothing more to
-		 * process so we just return null...
+		 * process so we just end the loop...
 		 */
 		if (TupIsNull(planSlot))
-		{
-			result = NULL;
 			break;
-		}
 		slot = planSlot;
 
 		/*
@@ -1453,7 +1441,6 @@ lnext:	;
 						default:
 							elog(ERROR, "unrecognized heap_lock_tuple status: %u",
 								 test);
-							return NULL;
 					}
 				}
 			}
@@ -1488,35 +1475,30 @@ lnext:	;
 
 		/*
 		 * now that we have a tuple, do the appropriate thing with it.. either
-		 * return it to the user, add it to a relation someplace, delete it
-		 * from a relation, or modify some of its attributes.
+		 * send it to the output destination, add it to a relation someplace,
+		 * delete it from a relation, or modify some of its attributes.
 		 */
 		switch (operation)
 		{
 			case CMD_SELECT:
 				ExecSelect(slot, dest, estate);
-				result = slot;
 				break;
 
 			case CMD_INSERT:
 				ExecInsert(slot, tupleid, planSlot, dest, estate);
-				result = NULL;
 				break;
 
 			case CMD_DELETE:
 				ExecDelete(tupleid, planSlot, dest, estate);
-				result = NULL;
 				break;
 
 			case CMD_UPDATE:
 				ExecUpdate(slot, tupleid, planSlot, dest, estate);
-				result = NULL;
 				break;
 
 			default:
 				elog(ERROR, "unrecognized operation code: %d",
 					 (int) operation);
-				result = NULL;
 				break;
 		}
 
@@ -1548,12 +1530,6 @@ lnext:	;
 			/* do nothing */
 			break;
 	}
-
-	/*
-	 * here, result is either a slot containing a tuple in the case of a
-	 * SELECT or NULL otherwise.
-	 */
-	return result;
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index a58703111cac5ba58ebd4f09560d3bdbbbe6215e..b34b90428ef8fcec066d7ca74630b0c77c7508e6 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.127 2008/10/31 19:37:56 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.128 2008/10/31 21:07:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -105,8 +105,7 @@ static execution_state *init_execution_state(List *queryTree_list,
 											 bool lazyEvalOK);
 static void init_sql_fcache(FmgrInfo *finfo, bool lazyEvalOK);
 static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache);
-static TupleTableSlot *postquel_getnext(execution_state *es,
-				 SQLFunctionCachePtr fcache);
+static bool postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache);
 static void postquel_end(execution_state *es);
 static void postquel_sub_params(SQLFunctionCachePtr fcache,
 					FunctionCallInfo fcinfo);
@@ -441,10 +440,11 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
 }
 
 /* Run one execution_state; either to completion or to first result row */
-static TupleTableSlot *
+/* Returns true if we ran to completion */
+static bool
 postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
 {
-	TupleTableSlot *result;
+	bool		result;
 
 	/* Make our snapshot the active one for any called functions */
 	PushActiveSnapshot(es->qd->snapshot);
@@ -460,14 +460,20 @@ postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
 					   false,		/* not top level */
 					   es->qd->dest,
 					   NULL);
-		result = NULL;
+		result = true;			/* never stops early */
 	}
 	else
 	{
 		/* Run regular commands to completion unless lazyEval */
 		long		count = (es->lazyEval) ? 1L : 0L;
 
-		result = ExecutorRun(es->qd, ForwardScanDirection, count);
+		ExecutorRun(es->qd, ForwardScanDirection, count);
+
+		/*
+		 * If we requested run to completion OR there was no tuple returned,
+		 * command must be complete.
+		 */
+		result = (count == 0L || es->qd->estate->es_processed == 0);
 	}
 
 	PopActiveSnapshot();
@@ -678,22 +684,22 @@ fmgr_sql(PG_FUNCTION_ARGS)
 	 */
 	while (es)
 	{
-		TupleTableSlot *slot;
+		bool	completed;
 
 		if (es->status == F_EXEC_START)
 			postquel_start(es, fcache);
 
-		slot = postquel_getnext(es, fcache);
+		completed = postquel_getnext(es, fcache);
 
 		/*
 		 * If we ran the command to completion, we can shut it down now.
 		 * Any row(s) we need to return are safely stashed in the tuplestore,
 		 * and we want to be sure that, for example, AFTER triggers get fired
 		 * before we return anything.  Also, if the function doesn't return
-		 * set, we can shut it down anyway because we don't care about
-		 * fetching any more result rows.
+		 * set, we can shut it down anyway because it must be a SELECT and
+		 * we don't care about fetching any more result rows.
 		 */
-		if (TupIsNull(slot) || !fcache->returnsSet)
+		if (completed || !fcache->returnsSet)
 			postquel_end(es);
 
 		/*
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index ac1b81cd9bede627d173b99faf75537b81a549d0..2fdaddda4dabe4c7dc1fd09834fca12b3af5e9bf 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.151 2008/10/29 00:00:39 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.152 2008/10/31 21:07:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -61,9 +61,9 @@
 
 
 /* Hook for plugins to get control in ExecutorRun() */
-typedef TupleTableSlot *(*ExecutorRun_hook_type) (QueryDesc *queryDesc,
-												  ScanDirection direction,
-												  long count);
+typedef void (*ExecutorRun_hook_type) (QueryDesc *queryDesc,
+									   ScanDirection direction,
+									   long count);
 extern PGDLLIMPORT ExecutorRun_hook_type ExecutorRun_hook;
 
 
@@ -140,10 +140,10 @@ extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot);
  * prototypes from functions in execMain.c
  */
 extern void ExecutorStart(QueryDesc *queryDesc, int eflags);
-extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc,
-			ScanDirection direction, long count);
-extern TupleTableSlot *standard_ExecutorRun(QueryDesc *queryDesc,
-			ScanDirection direction, long count);
+extern void ExecutorRun(QueryDesc *queryDesc,
+						ScanDirection direction, long count);
+extern void standard_ExecutorRun(QueryDesc *queryDesc,
+								 ScanDirection direction, long count);
 extern void ExecutorEnd(QueryDesc *queryDesc);
 extern void ExecutorRewind(QueryDesc *queryDesc);
 extern void InitResultRelInfo(ResultRelInfo *resultRelInfo,