Index: src/backend/executor/nodeFunctionscan.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/backend/executor/nodeFunctionscan.c,v
retrieving revision 1.5
diff -c -r1.5 nodeFunctionscan.c
*** src/backend/executor/nodeFunctionscan.c	5 Aug 2002 02:30:50 -0000	1.5
--- src/backend/executor/nodeFunctionscan.c	19 Aug 2002 19:50:50 -0000
***************
*** 24,29 ****
--- 24,31 ----
  
  #include "miscadmin.h"
  #include "access/heapam.h"
+ #include "catalog/pg_language.h"
+ #include "catalog/pg_proc.h"
  #include "catalog/pg_type.h"
  #include "executor/execdebug.h"
  #include "executor/execdefs.h"
***************
*** 34,39 ****
--- 36,42 ----
  #include "parser/parse_type.h"
  #include "storage/lmgr.h"
  #include "tcop/pquery.h"
+ #include "utils/builtins.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"
  #include "utils/tuplestore.h"
***************
*** 42,47 ****
--- 45,53 ----
  static TupleTableSlot *function_getonetuple(FunctionScanState *scanstate,
  											bool *isNull,
  											ExprDoneCond *isDone);
+ static Tuplestorestate *function_getalltuples(FunctionScanState *scanstate);
+ static bool is_plpgsql_srf(FunctionScanState *scanstate);
+ static Tuplestorestate *function_call_plpgsql_srf(FunctionScanState *scanstate);
  static FunctionMode get_functionmode(Node *expr);
  static bool tupledesc_mismatch(TupleDesc tupdesc1, TupleDesc tupdesc2);
  
***************
*** 59,65 ****
  FunctionNext(FunctionScan *node)
  {
  	TupleTableSlot	   *slot;
- 	EState			   *estate;
  	ScanDirection		direction;
  	Tuplestorestate	   *tuplestorestate;
  	FunctionScanState  *scanstate;
--- 65,70 ----
***************
*** 70,77 ****
  	 * get information from the estate and scan state
  	 */
  	scanstate = (FunctionScanState *) node->scan.scanstate;
! 	estate = node->scan.plan.state;
! 	direction = estate->es_direction;
  
  	tuplestorestate = scanstate->tuplestorestate;
  
--- 75,81 ----
  	 * get information from the estate and scan state
  	 */
  	scanstate = (FunctionScanState *) node->scan.scanstate;
! 	direction = node->scan.plan.state->es_direction;
  
  	tuplestorestate = scanstate->tuplestorestate;
  
***************
*** 82,119 ****
  	if (tuplestorestate == NULL)
  	{
  		/*
! 		 * Initialize tuplestore module.
  		 */
! 		tuplestorestate = tuplestore_begin_heap(true,	/* randomAccess */
! 												SortMem);
  
  		scanstate->tuplestorestate = (void *) tuplestorestate;
- 
- 		/*
- 		 * Compute all the function tuples and pass to tuplestore.
- 		 */
- 		for (;;)
- 		{
- 			bool				isNull;
- 			ExprDoneCond		isDone;
- 
- 			isNull = false;
- 			isDone = ExprSingleResult;
- 			slot = function_getonetuple(scanstate, &isNull, &isDone);
- 			if (TupIsNull(slot))
- 				break;
- 
- 			tuplestore_puttuple(tuplestorestate, (void *) slot->val);
- 			ExecClearTuple(slot);
- 
- 			if (isDone != ExprMultipleResult)
- 				break;
- 		}
- 
- 		/*
- 		 * Complete the store.
- 		 */
- 		tuplestore_donestoring(tuplestorestate);
  	}
  
  	/*
--- 86,99 ----
  	if (tuplestorestate == NULL)
  	{
  		/*
! 		 * PL/PgSQL set-returning functions need to be handled specially
  		 */
! 		if (is_plpgsql_srf(scanstate))
! 			tuplestorestate = function_call_plpgsql_srf(scanstate);
! 		else
! 			tuplestorestate = function_getalltuples(scanstate);
  
  		scanstate->tuplestorestate = (void *) tuplestorestate;
  	}
  
  	/*
***************
*** 127,132 ****
--- 107,256 ----
  	return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
  }
  
+ static Tuplestorestate *
+ function_getalltuples(FunctionScanState *scanstate)
+ {
+ 	Tuplestorestate	*tuplestorestate;
+ 	TupleTableSlot	*slot;
+ 
+ 	/*
+ 	 * Initialize tuplestore module.
+ 	 */
+ 	tuplestorestate = tuplestore_begin_heap(true,	/* randomAccess */
+ 											SortMem);
+ 
+ 	/*
+ 	 * Compute all the function tuples and pass to tuplestore.
+ 	 */
+ 	for (;;)
+ 	{
+ 		bool				isNull;
+ 		ExprDoneCond		isDone;
+ 
+ 		isNull = false;
+ 		isDone = ExprSingleResult;
+ 		slot = function_getonetuple(scanstate, &isNull, &isDone);
+ 		if (TupIsNull(slot))
+ 			break;
+ 
+ 		tuplestore_puttuple(tuplestorestate, (void *) slot->val);
+ 		ExecClearTuple(slot);
+ 
+ 		if (isDone != ExprMultipleResult)
+ 			break;
+ 	}
+ 
+ 	/*
+ 	 * Complete the store.
+ 	 */
+ 	tuplestore_donestoring(tuplestorestate);
+ 
+ 	return tuplestorestate;
+ }
+ 
+ /*
+  *		is_plpgsql_srf(scanstate)
+  *
+  * If the function is a set-returning function defined in PL/PgSQL,
+  * return true. Note that this includes both scalar and composite (a.k.a
+  * tuple-returning) SRFs.
+  */
+ static bool
+ is_plpgsql_srf(FunctionScanState *scanstate)
+ {
+ 	Node		*node = scanstate->funcexpr;
+ 	Expr		*expr;
+ 	Func		*func;
+ 	HeapTuple	 tuple;
+ 	Oid			 lanoid;
+ 	NameData	 lanname;
+ 	bool		 retset;
+ 	bool		 matched;
+ 
+ 	if (nodeTag(node) != T_Expr)
+ 		elog(ERROR, "Unexpected node type in function scan: %d",
+ 			 nodeTag(node));
+ 
+ 	expr = (Expr *) node;
+ 
+ 	if (expr->opType != FUNC_EXPR)
+ 		elog(ERROR, "Unexpected expr type in function scan: %d",
+ 			 expr->opType);
+ 
+ 	func = (Func *) expr->oper;
+ 
+ 	tuple = SearchSysCache(PROCOID,
+ 						   ObjectIdGetDatum(func->funcid),
+ 						   0, 0, 0);
+ 
+ 	if (!HeapTupleIsValid(tuple))
+ 		elog(ERROR, "Cache lookup for procedure %u failed",
+ 			 func->funcid);
+ 
+ 	lanoid = ((Form_pg_proc) GETSTRUCT(tuple))->prolang;
+ 	retset = ((Form_pg_proc) GETSTRUCT(tuple))->proretset;
+ 	ReleaseSysCache(tuple);
+ 
+ 	/* Not a set-returning function, we're done */
+ 	if (!retset)
+ 		return false;
+ 
+ 	/* Short-cut: if the OID matches a well-known language, we're done */
+ 	if (lanoid == INTERNALlanguageId || lanoid == ClanguageId ||
+ 		lanoid == SQLlanguageId)
+ 		return false;
+ 
+ 	tuple = SearchSysCache(LANGOID,
+ 						   ObjectIdGetDatum(lanoid),
+ 						   0, 0, 0);
+ 
+ 	if (!HeapTupleIsValid(tuple))
+ 		elog(ERROR, "Cache lookup for language %u failed",
+ 			 lanoid);
+ 
+ 	lanname = ((Form_pg_language) GETSTRUCT(tuple))->lanname;
+ 
+ 	matched = (namestrcmp(&lanname, "plpgsql") == 0);
+ 
+ 	ReleaseSysCache(tuple);
+ 
+ 	return matched;
+ }
+ 
+ /*
+  *		function_call_plpgsql_srf(scanstate)
+  *
+  * The execution of PL/PgSQL set-returning functions is similar to
+  * the execution of other set-returning functions: the first call
+  * to FunctionNext() creates a tuplestore of results, subsequent calls
+  * to FunctionNext() fetch data from the store. The difference with SRFs
+  * in PL/PgSQL is that the tuplestore is created within the PL/PgSQL
+  * code itself: inside the PL/PgSQL function, the application developer
+  * can store multiple tuples using RETURN NEXT. Thus, we simple execute
+  * the function once, and assume it returns a pointer to the tuplestore
+  * containing the results.
+  */
+ 
+ static Tuplestorestate *
+ function_call_plpgsql_srf(FunctionScanState *scanstate)
+ {
+ 	ExprContext		*econtext = scanstate->csstate.cstate.cs_ExprContext;
+ 	bool			 isNull;
+ 	ExprDoneCond	 isDone;
+ 	Datum			 retval;
+ 
+ 	/*
+ 	 * Execute the PL/PgSQL function, returns a pointer to
+ 	 * tuplestore allocated in the TransactionCommandContext.
+ 	 */
+ 	retval = ExecEvalExprSwitchContext(scanstate->funcexpr,
+ 									   econtext,
+ 									   &isNull,
+ 									   &isDone);
+ 
+ 	return (Tuplestorestate *) DatumGetPointer(retval);
+ }
+ 
  /* ----------------------------------------------------------------
   *		ExecFunctionScan(node)
   *
***************
*** 491,499 ****
  get_functionmode(Node *expr)
  {
  	/*
! 	 * for the moment, hardwire this
  	 */
! 	return PM_REPEATEDCALL;
  }
  
  static bool
--- 615,623 ----
  get_functionmode(Node *expr)
  {
  	/*
! 	 * for the moment, hardwire this: only one FM at present
  	 */
! 	return FM_MATERIALIZE;
  }
  
  static bool
Index: src/backend/executor/spi.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/backend/executor/spi.c,v
retrieving revision 1.72
diff -c -r1.72 spi.c
*** src/backend/executor/spi.c	20 Jul 2002 05:16:58 -0000	1.72
--- src/backend/executor/spi.c	19 Aug 2002 19:50:50 -0000
***************
*** 663,689 ****
  	return pointer;
  }
  
- void *
- SPI_repalloc(void *pointer, Size size)
- {
- 	/* No longer need to worry which context chunk was in... */
- 	return repalloc(pointer, size);
- }
- 
- void
- SPI_pfree(void *pointer)
- {
- 	/* No longer need to worry which context chunk was in... */
- 	pfree(pointer);
- }
- 
- void
- SPI_freetuple(HeapTuple tuple)
- {
- 	/* No longer need to worry which context tuple was in... */
- 	heap_freetuple(tuple);
- }
- 
  void
  SPI_freetuptable(SPITupleTable *tuptable)
  {
--- 663,668 ----
Index: src/include/executor/spi.h
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/include/executor/spi.h,v
retrieving revision 1.33
diff -c -r1.33 spi.h
*** src/include/executor/spi.h	8 Nov 2001 20:37:52 -0000	1.33
--- src/include/executor/spi.h	19 Aug 2002 19:50:51 -0000
***************
*** 101,110 ****
  extern Oid	SPI_gettypeid(TupleDesc tupdesc, int fnumber);
  extern char *SPI_getrelname(Relation rel);
  extern void *SPI_palloc(Size size);
- extern void *SPI_repalloc(void *pointer, Size size);
- extern void SPI_pfree(void *pointer);
- extern void SPI_freetuple(HeapTuple pointer);
  extern void SPI_freetuptable(SPITupleTable *tuptable);
  
  extern Portal SPI_cursor_open(char *name, void *plan,
  				Datum *Values, char *Nulls);
--- 101,112 ----
  extern Oid	SPI_gettypeid(TupleDesc tupdesc, int fnumber);
  extern char *SPI_getrelname(Relation rel);
  extern void *SPI_palloc(Size size);
  extern void SPI_freetuptable(SPITupleTable *tuptable);
+ 
+ /* No longer need to worry which context chunk was in... */
+ #define SPI_pfree(pointer) pfree((pointer))
+ #define SPI_repalloc(pointer, size) repalloc((pointer), (size))
+ #define SPI_freetuple(pointer) heap_freetuple((pointer))
  
  extern Portal SPI_cursor_open(char *name, void *plan,
  				Datum *Values, char *Nulls);
Index: src/include/nodes/execnodes.h
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/include/nodes/execnodes.h,v
retrieving revision 1.71
diff -c -r1.71 execnodes.h
*** src/include/nodes/execnodes.h	4 Aug 2002 19:48:10 -0000	1.71
--- src/include/nodes/execnodes.h	19 Aug 2002 19:50:51 -0000
***************
*** 509,518 ****
   *		Function nodes are used to scan the results of a
   *		function appearing in FROM (typically a function returning set).
   *
!  *		functionmode		function operating mode:
!  *							- repeated call
!  *							- materialize
!  *							- return query
   *		tupdesc				function's return tuple description
   *		tuplestorestate		private state of tuplestore.c
   *		funcexpr			function expression being evaluated
--- 509,519 ----
   *		Function nodes are used to scan the results of a
   *		function appearing in FROM (typically a function returning set).
   *
!  *		functionmode		function operating mode. At the moment, the
!  *						    only mode is 'materialize', in which the
!  *						    executor repeatedly calls the SRF, building
!  *						    up a tuplestore of results. More function
!  *						    modes will be added in the future.
   *		tupdesc				function's return tuple description
   *		tuplestorestate		private state of tuplestore.c
   *		funcexpr			function expression being evaluated
***************
*** 524,532 ****
   */
  typedef enum FunctionMode
  {
! 	PM_REPEATEDCALL,
! 	PM_MATERIALIZE,
! 	PM_QUERY
  } FunctionMode;
  
  typedef struct FunctionScanState
--- 525,531 ----
   */
  typedef enum FunctionMode
  {
! 	FM_MATERIALIZE
  } FunctionMode;
  
  typedef struct FunctionScanState
Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.55
diff -c -r1.55 libpq-int.h
*** src/interfaces/libpq/libpq-int.h	18 Aug 2002 03:47:08 -0000	1.55
--- src/interfaces/libpq/libpq-int.h	19 Aug 2002 19:50:51 -0000
***************
*** 363,369 ****
  #define DefaultPassword		  ""
  
  /*
!  * this is so that we can check is a connection is non-blocking internally
   * without the overhead of a function call
   */
  #define pqIsnonblocking(conn)	((conn)->nonblocking)
--- 363,369 ----
  #define DefaultPassword		  ""
  
  /*
!  * This is so that we can check if a connection is non-blocking internally
   * without the overhead of a function call
   */
  #define pqIsnonblocking(conn)	((conn)->nonblocking)
Index: src/pl/plpgsql/src/Makefile
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/pl/plpgsql/src/Makefile,v
retrieving revision 1.20
diff -c -r1.20 Makefile
*** src/pl/plpgsql/src/Makefile	16 Nov 2001 16:32:33 -0000	1.20
--- src/pl/plpgsql/src/Makefile	19 Aug 2002 19:50:51 -0000
***************
*** 78,84 ****
  
  $(srcdir)/pl_scan.c: scan.l
  ifdef FLEX
! 	$(FLEX) -i $(FLEXFLAGS) -Pplpgsql_base_yy -o'$@' $<
  else
  	@$(missing) flex $< $@
  endif
--- 78,84 ----
  
  $(srcdir)/pl_scan.c: scan.l
  ifdef FLEX
! 	$(FLEX) $(FLEXFLAGS) -Pplpgsql_base_yy -o'$@' $<
  else
  	@$(missing) flex $< $@
  endif
Index: src/pl/plpgsql/src/gram.y
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/pl/plpgsql/src/gram.y,v
retrieving revision 1.34
diff -c -r1.34 gram.y
*** src/pl/plpgsql/src/gram.y	8 Aug 2002 01:36:04 -0000	1.34
--- src/pl/plpgsql/src/gram.y	19 Aug 2002 19:50:51 -0000
***************
*** 121,128 ****
  %type <stmts>	proc_sect, proc_stmts, stmt_else, loop_body
  %type <stmt>	proc_stmt, pl_block
  %type <stmt>	stmt_assign, stmt_if, stmt_loop, stmt_while, stmt_exit
! %type <stmt>	stmt_return, stmt_raise, stmt_execsql, stmt_fori
! %type <stmt>	stmt_fors, stmt_select, stmt_perform
  %type <stmt>	stmt_dynexecute, stmt_dynfors, stmt_getdiag
  %type <stmt>	stmt_open, stmt_fetch, stmt_close
  
--- 121,128 ----
  %type <stmts>	proc_sect, proc_stmts, stmt_else, loop_body
  %type <stmt>	proc_stmt, pl_block
  %type <stmt>	stmt_assign, stmt_if, stmt_loop, stmt_while, stmt_exit
! %type <stmt>	stmt_return, stmt_return_next, stmt_raise, stmt_execsql
! %type <stmt>	stmt_fori, stmt_fors, stmt_select, stmt_perform
  %type <stmt>	stmt_dynexecute, stmt_dynfors, stmt_getdiag
  %type <stmt>	stmt_open, stmt_fetch, stmt_close
  
***************
*** 166,171 ****
--- 166,172 ----
  %token	K_IS
  %token	K_LOG
  %token	K_LOOP
+ %token	K_NEXT
  %token	K_NOT
  %token	K_NOTICE
  %token	K_NULL
***************
*** 708,713 ****
--- 709,716 ----
  						{ $$ = $1; }
  				| stmt_return
  						{ $$ = $1; }
+ 				| stmt_return_next
+ 						{ $$ = $1; }
  				| stmt_raise
  						{ $$ = $1; }
  				| stmt_execsql
***************
*** 1127,1133 ****
  						new = malloc(sizeof(PLpgSQL_stmt_return));
  						memset(new, 0, sizeof(PLpgSQL_stmt_return));
  
! 						if (plpgsql_curr_compile->fn_retistuple)
  						{
  							new->retistuple = true;
  							new->retrecno	= -1;
--- 1130,1137 ----
  						new = malloc(sizeof(PLpgSQL_stmt_return));
  						memset(new, 0, sizeof(PLpgSQL_stmt_return));
  
! 						if (plpgsql_curr_compile->fn_retistuple &&
! 							!plpgsql_curr_compile->fn_retset)
  						{
  							new->retistuple = true;
  							new->retrecno	= -1;
***************
*** 1147,1153 ****
  									break;
  
  								default:
! 									yyerror("return type mismatch in function returning table row");
  									break;
  							}
  							if (yylex() != ';')
--- 1151,1157 ----
  									break;
  
  								default:
! 									yyerror("return type mismatch in function returning tuple");
  									break;
  							}
  							if (yylex() != ';')
***************
*** 1160,1165 ****
--- 1164,1193 ----
  						new->cmd_type = PLPGSQL_STMT_RETURN;
  						new->lineno   = $2;
  						new->expr	  = expr;
+ 
+ 						$$ = (PLpgSQL_stmt *)new;
+ 					}
+ 				;
+ 
+ /* FIXME: this syntax needs work, RETURN NEXT breaks stmt_return */
+ stmt_return_next: K_NEXT lno fors_target ';'
+ 					{
+ 						PLpgSQL_stmt_return_next *new;
+ 
+ 						new = malloc(sizeof(PLpgSQL_stmt_return_next));
+ 						memset(new, 0, sizeof(PLpgSQL_stmt_return_next));
+ 
+ 						new->cmd_type	= PLPGSQL_STMT_RETURN_NEXT;
+ 						new->lineno		= $2;
+ 						switch ($3->dtype)
+ 						{
+ 							case PLPGSQL_DTYPE_REC:
+ 								new->rec = $3;
+ 								break;
+ 							case PLPGSQL_DTYPE_ROW:
+ 								new->row = (PLpgSQL_row *) $3;
+ 								break;
+ 						}
  
  						$$ = (PLpgSQL_stmt *)new;
  					}
Index: src/pl/plpgsql/src/pl_exec.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/pl/plpgsql/src/pl_exec.c,v
retrieving revision 1.56
diff -c -r1.56 pl_exec.c
*** src/pl/plpgsql/src/pl_exec.c	24 Jun 2002 23:12:06 -0000	1.56
--- src/pl/plpgsql/src/pl_exec.c	19 Aug 2002 19:50:51 -0000
***************
*** 105,110 ****
--- 105,112 ----
  			   PLpgSQL_stmt_exit * stmt);
  static int exec_stmt_return(PLpgSQL_execstate * estate,
  				 PLpgSQL_stmt_return * stmt);
+ static int exec_stmt_return_next(PLpgSQL_execstate * estate,
+ 				 PLpgSQL_stmt_return_next * stmt);
  static int exec_stmt_raise(PLpgSQL_execstate * estate,
  				PLpgSQL_stmt_raise * stmt);
  static int exec_stmt_execsql(PLpgSQL_execstate * estate,
***************
*** 150,155 ****
--- 152,160 ----
  				int32 reqtypmod,
  				bool *isnull);
  static void exec_set_found(PLpgSQL_execstate * estate, bool state);
+ static void exec_init_tuple_store(PLpgSQL_execstate *estate);
+ static int exec_finish_set_func(PLpgSQL_execstate *estate,
+ 								PLpgSQL_stmt_return *stmt);
  
  
  /* ----------
***************
*** 315,321 ****
  	 * Set the magic variable FOUND to false
  	 */
  	exec_set_found(&estate, false);
! 
  	/*
  	 * Now call the toplevel block of statements
  	 */
--- 320,326 ----
  	 * Set the magic variable FOUND to false
  	 */
  	exec_set_found(&estate, false);
! 	
  	/*
  	 * Now call the toplevel block of statements
  	 */
***************
*** 332,338 ****
  	 * We got a return value - process it
  	 */
  	error_info_stmt = NULL;
! 	error_info_text = "while casting return value to functions return type";
  
  	fcinfo->isnull = estate.retisnull;
  
--- 337,343 ----
  	 * We got a return value - process it
  	 */
  	error_info_stmt = NULL;
! 	error_info_text = "while casting return value to function's return type";
  
  	fcinfo->isnull = estate.retisnull;
  
***************
*** 912,917 ****
--- 917,926 ----
  			rc = exec_stmt_return(estate, (PLpgSQL_stmt_return *) stmt);
  			break;
  
+ 		case PLPGSQL_STMT_RETURN_NEXT:
+ 			rc = exec_stmt_return_next(estate, (PLpgSQL_stmt_return_next *) stmt);
+ 			break;
+ 
  		case PLPGSQL_STMT_RAISE:
  			rc = exec_stmt_raise(estate, (PLpgSQL_stmt_raise *) stmt);
  			break;
***************
*** 1500,1505 ****
--- 1509,1521 ----
  static int
  exec_stmt_return(PLpgSQL_execstate * estate, PLpgSQL_stmt_return * stmt)
  {
+ 	/*
+ 	 * If processing a set-returning PL/PgSQL function, the final RETURN
+ 	 * indicates that the function is finished producing tuples.
+ 	 */
+ 	if (estate->retisset)
+ 		return exec_finish_set_func(estate, stmt);
+ 
  	if (estate->retistuple)
  	{
  		/* initialize for null result tuple */
***************
*** 1540,1545 ****
--- 1556,1632 ----
  	return PLPGSQL_RC_RETURN;
  }
  
+ static int
+ exec_finish_set_func(PLpgSQL_execstate *estate,
+ 					 PLpgSQL_stmt_return *stmt)
+ {
+ 	/* We should probably allow this */
+ 	if (estate->tuple_store == NULL)
+ 		elog(ERROR, "Table functions must return at least 1 tuple");
+ 
+ 	tuplestore_donestoring(estate->tuple_store);
+ 
+ 	return PLPGSQL_RC_RETURN;
+ }
+ 
+ /*
+  * TODO:
+  * - if the function returns a set of scalars, form a tuple and insert
+  *   it into the tuplestore (using heap_formtuple() )
+  * - what other return types should we allow? i.e. record, row, etc.
+  */
+ /*
+  * Notes:
+  *  - the tuple store must be created in a sufficiently long-lived
+  *    memory context, as the same store must be used within the executor
+  *    after the PL/PgSQL call returns. At present, the code uses
+  *    TopTransactionContext.
+  */
+ static int
+ exec_stmt_return_next(PLpgSQL_execstate *estate,
+ 					  PLpgSQL_stmt_return_next *stmt)
+ {
+ 	HeapTuple tuple;
+ 
+ 	if (!estate->retisset)
+ 		elog(ERROR, "Cannot use RETURN NEXT in a non-table function");
+ 
+ 	if (estate->tuple_store == NULL)
+ 		exec_init_tuple_store(estate);
+ 
+ 	if (stmt->rec)
+ 	{
+ 		PLpgSQL_rec *rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]);
+ 		tuple = rec->tup;
+ 		estate->rettupdesc = rec->tupdesc;
+ 	}
+ 	else
+ 		elog(ERROR, "Blank RETURN NEXT not allowed");
+ 
+ 	if (HeapTupleIsValid(tuple))
+ 	{
+ 		MemoryContext oldcxt;
+ 
+ 		oldcxt = MemoryContextSwitchTo(TopTransactionContext);
+ 		tuplestore_puttuple(estate->tuple_store, tuple);
+ 		MemoryContextSwitchTo(oldcxt);
+ 	}
+ 
+ 	return PLPGSQL_RC_OK;
+ }
+ 
+ static void
+ exec_init_tuple_store(PLpgSQL_execstate *estate)
+ {
+ 	MemoryContext oldcxt;
+ 
+ 	oldcxt = MemoryContextSwitchTo(TopTransactionContext);
+ 	estate->tuple_store = tuplestore_begin_heap(true, SortMem);
+ 	MemoryContextSwitchTo(oldcxt);
+ 
+ 	estate->retval = PointerGetDatum(estate->tuple_store);
+ }
+ 
  
  /* ----------
   * exec_stmt_raise			Build a message and throw it with
***************
*** 1716,1721 ****
--- 1803,1810 ----
  	estate->retisset = func->fn_retset;
  	estate->exitlabel = NULL;
  
+ 	estate->tuple_store = NULL;
+ 
  	estate->trig_nargs = 0;
  	estate->trig_argv = NULL;
  
***************
*** 2102,2108 ****
  		if (stmt->row != NULL)
  			row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]);
  		else
! 			elog(ERROR, "unsupported target in exec_stmt_fors()");
  	}
  
  	/*
--- 2191,2197 ----
  		if (stmt->row != NULL)
  			row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]);
  		else
! 			elog(ERROR, "unsupported target in exec_stmt_dynfors()");
  	}
  
  	/*
Index: src/pl/plpgsql/src/plpgsql.h
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/pl/plpgsql/src/plpgsql.h,v
retrieving revision 1.25
diff -c -r1.25 plpgsql.h
*** src/pl/plpgsql/src/plpgsql.h	8 Aug 2002 01:36:05 -0000	1.25
--- src/pl/plpgsql/src/plpgsql.h	19 Aug 2002 19:50:51 -0000
***************
*** 40,47 ****
--- 40,49 ----
  #include "postgres.h"
  
  #include "fmgr.h"
+ #include "miscadmin.h"
  #include "executor/spi.h"
  #include "commands/trigger.h"
+ #include "utils/tuplestore.h"
  
  /**********************************************************************
   * Definitions
***************
*** 90,95 ****
--- 92,98 ----
  	PLPGSQL_STMT_SELECT,
  	PLPGSQL_STMT_EXIT,
  	PLPGSQL_STMT_RETURN,
+ 	PLPGSQL_STMT_RETURN_NEXT,
  	PLPGSQL_STMT_RAISE,
  	PLPGSQL_STMT_EXECSQL,
  	PLPGSQL_STMT_DYNEXECUTE,
***************
*** 425,430 ****
--- 428,440 ----
  	int			retrecno;
  }	PLpgSQL_stmt_return;
  
+ typedef struct
+ {								/* RETURN NEXT statement */
+ 	int			cmd_type;
+ 	int			lineno;
+ 	PLpgSQL_rec *rec;
+ 	PLpgSQL_row *row;
+ }	PLpgSQL_stmt_return_next;
  
  typedef struct
  {								/* RAISE statement			*/
***************
*** 499,504 ****
--- 509,516 ----
  	TupleDesc	rettupdesc;
  	bool		retisset;
  	char	   *exitlabel;
+ 
+ 	Tuplestorestate *tuple_store;		/* SRFs accumulate results here */
  
  	int			trig_nargs;
  	Datum	   *trig_argv;
Index: src/pl/plpgsql/src/scan.l
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/pl/plpgsql/src/scan.l,v
retrieving revision 1.21
diff -c -r1.21 scan.l
*** src/pl/plpgsql/src/scan.l	8 Aug 2002 01:36:05 -0000	1.21
--- src/pl/plpgsql/src/scan.l	19 Aug 2002 19:50:51 -0000
***************
*** 134,139 ****
--- 134,140 ----
  is				{ return K_IS;				}
  log				{ return K_LOG;				}
  loop			{ return K_LOOP;			}
+ next			{ return K_NEXT;			}
  not				{ return K_NOT;				}
  notice			{ return K_NOTICE;			}
  null			{ return K_NULL;			}
