Index: doc/src/sgml/plpgsql.sgml
===================================================================
RCS file: /var/lib/cvs/pgsql-server/doc/src/sgml/plpgsql.sgml,v
retrieving revision 1.4
diff -c -r1.4 plpgsql.sgml
*** doc/src/sgml/plpgsql.sgml	29 Aug 2002 04:12:02 -0000	1.4
--- doc/src/sgml/plpgsql.sgml	29 Aug 2002 07:22:28 -0000
***************
*** 1794,1812 ****
  RAISE <replaceable class="parameter">level</replaceable> '<replaceable class="parameter">format</replaceable>' <optional>, <replaceable class="parameter">variable</replaceable> <optional>...</optional></optional>;
  </synopsis>
  
!     Possible levels are DEBUG (write the message into the postmaster log),
!     NOTICE (write the message into the postmaster log and forward it to
!     the client application) and EXCEPTION (raise an error,
!     aborting the transaction).
     </para>
  
     <para>
!     Inside the format string, <literal>%</literal> is replaced by the next
!     optional argument's external representation.
!     Write <literal>%%</literal> to emit a literal <literal>%</literal>.
!     Note that the optional arguments must presently
!     be simple variables, not expressions, and the format must be a simple
!     string literal.
     </para>
  
     <!--
--- 1794,1820 ----
  RAISE <replaceable class="parameter">level</replaceable> '<replaceable class="parameter">format</replaceable>' <optional>, <replaceable class="parameter">variable</replaceable> <optional>...</optional></optional>;
  </synopsis>
  
!     Possible levels are <literal>DEBUG</literal> (write the message to
!     the server log), <literal>LOG</literal> (write the message to the
!     server log with a higher priority), <literal>INFO</literal>,
!     <literal>NOTICE</literal> and <literal>WARNING</literal> (write
!     the message to the server log and send it to the client, with
!     respectively higher priorities), and <literal>EXCEPTION</literal>
!     (raise an error and abort the current transaction). Whether error
!     messages of a particular priority are reported to the client,
!     written to the server log, or both is controlled by the
!     <option>SERVER_MIN_MESSAGES</option> and
!     <option>CLIENT_MIN_MESSAGES</literal> configuration variables. See
!     the <citetitle>PostgreSQL Administrator's Guide</title> for more
!     information.
     </para>
  
     <para>
!     Inside the format string, <literal>%</literal> is replaced by the
!     next optional argument's external representation. Write
!     <literal>%%</literal> to emit a literal <literal>%</literal>. Note
!     that the optional arguments must presently be simple variables,
!     not expressions, and the format must be a simple string literal.
     </para>
  
     <!--
***************
*** 1820,1827 ****
  <programlisting>
  RAISE NOTICE ''Calling cs_create_job(%)'',v_job_id;
  </programlisting>
!     In this example, the value of v_job_id will replace the % in the
!     string.
     </para>
  
     <para>
--- 1828,1836 ----
  <programlisting>
  RAISE NOTICE ''Calling cs_create_job(%)'',v_job_id;
  </programlisting>
! 
!     In this example, the value of v_job_id will replace the
!     <literal>%</literal> in the string.
     </para>
  
     <para>
***************
*** 1852,1863 ****
      </para>
  
      <para>
!      Thus, the only thing <application>PL/pgSQL</application> currently does when it encounters
!      an abort during execution of a function or trigger
!      procedure is to write some additional NOTICE level log messages
!      telling in which function and where (line number and type of
!      statement) this happened.  The error always stops execution of
!      the function.
      </para>
    </sect2>
   </sect1>
--- 1861,1872 ----
      </para>
  
      <para>
!      Thus, the only thing <application>PL/pgSQL</application>
!      currently does when it encounters an abort during execution of a
!      function or trigger procedure is to write some additional
!      <literal>NOTICE</literal> level log messages telling in which
!      function and where (line number and type of statement) this
!      happened.  The error always stops execution of the function.
      </para>
    </sect2>
   </sect1>
Index: doc/src/sgml/runtime.sgml
===================================================================
RCS file: /var/lib/cvs/pgsql-server/doc/src/sgml/runtime.sgml,v
retrieving revision 1.127
diff -c -r1.127 runtime.sgml
*** doc/src/sgml/runtime.sgml	22 Aug 2002 04:52:17 -0000	1.127
--- doc/src/sgml/runtime.sgml	29 Aug 2002 07:22:28 -0000
***************
*** 900,908 ****
          default is <literal>NOTICE</>. Valid values are
          <literal>DEBUG5</>, <literal>DEBUG4</>, <literal>DEBUG3</>,
          <literal>DEBUG2</>, <literal>DEBUG1</>, <literal>LOG</>,
!         <literal>NOTICE</>, <literal>WARNING</>, and <literal>ERROR</>.
!         Later values send less information to the user. <literal>LOG</>
!         has a different precedence here than in
          <literal>SERVER_MIN_MESSAGES</>. Also see that section for an
          explanation of the various values.
         </para>
--- 900,908 ----
          default is <literal>NOTICE</>. Valid values are
          <literal>DEBUG5</>, <literal>DEBUG4</>, <literal>DEBUG3</>,
          <literal>DEBUG2</>, <literal>DEBUG1</>, <literal>LOG</>,
!         <literal>LOG</>, <literal>NOTICE</>, <literal>WARNING</>, and
!         <literal>ERROR</>. Later values send less information to the
!         user. <literal>LOG</> has a different precedence here than in
          <literal>SERVER_MIN_MESSAGES</>. Also see that section for an
          explanation of the various values.
         </para>
***************
*** 921,927 ****
          built (see the configure option
          <literal>--enable-cassert</literal>). Note that
          <literal>DEBUG_ASSERTIONS</literal> defaults to on if
!         <productname>PostgreSQL</productname> has been built this way.
         </para>
        </listitem>
       </varlistentry>
--- 921,928 ----
          built (see the configure option
          <literal>--enable-cassert</literal>). Note that
          <literal>DEBUG_ASSERTIONS</literal> defaults to on if
!         <productname>PostgreSQL</productname> has been built with
!         assertions enabled.
         </para>
        </listitem>
       </varlistentry>
Index: src/backend/executor/nodeFunctionscan.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/backend/executor/nodeFunctionscan.c,v
retrieving revision 1.6
diff -c -r1.6 nodeFunctionscan.c
*** src/backend/executor/nodeFunctionscan.c	29 Aug 2002 00:17:04 -0000	1.6
--- src/backend/executor/nodeFunctionscan.c	29 Aug 2002 07:22:28 -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,257 ----
  	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, but it does *not* include functions that
+  * return a single composite type (i.e. a tuple), but not a set.
+  */
+ 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)
   *
***************
*** 417,425 ****
  					 bool *isNull,
  					 ExprDoneCond *isDone)
  {
- 	HeapTuple		tuple;
  	Datum			retDatum;
- 	char			nullflag;
  	TupleDesc		tupdesc = scanstate->tupdesc;
  	bool			returnsTuple = scanstate->returnsTuple;
  	Node		   *expr = scanstate->funcexpr;
--- 542,548 ----
***************
*** 463,468 ****
--- 586,594 ----
  			 * Must be a base data type, i.e. scalar
  			 * turn it into a tuple
  			 */
+ 			char nullflag;
+ 			HeapTuple tuple;
+ 
  			nullflag = *isNull ? 'n' : ' ';
  			tuple = heap_formtuple(tupdesc, &retDatum, &nullflag);
  
***************
*** 484,492 ****
  get_functionmode(Node *expr)
  {
  	/*
! 	 * for the moment, hardwire this
  	 */
! 	return PM_REPEATEDCALL;
  }
  
  static bool
--- 610,618 ----
  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	29 Aug 2002 07:22:28 -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/fmgr.h
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/include/fmgr.h,v
retrieving revision 1.22
diff -c -r1.22 fmgr.h
*** src/include/fmgr.h	20 Jun 2002 20:29:42 -0000	1.22
--- src/include/fmgr.h	29 Aug 2002 07:22:28 -0000
***************
*** 45,52 ****
  								 * count */
  	bool		fn_strict;		/* function is "strict" (NULL in => NULL
  								 * out) */
! 	bool		fn_retset;		/* function returns a set (over multiple
! 								 * calls) */
  	void	   *fn_extra;		/* extra space for use by handler */
  	MemoryContext fn_mcxt;		/* memory context to store fn_extra in */
  } FmgrInfo;
--- 45,51 ----
  								 * count */
  	bool		fn_strict;		/* function is "strict" (NULL in => NULL
  								 * out) */
! 	bool		fn_retset;		/* function returns a set */
  	void	   *fn_extra;		/* extra space for use by handler */
  	MemoryContext fn_mcxt;		/* memory context to store fn_extra in */
  } FmgrInfo;
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	29 Aug 2002 07:22:28 -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.72
diff -c -r1.72 execnodes.h
*** src/include/nodes/execnodes.h	29 Aug 2002 00:17:06 -0000	1.72
--- src/include/nodes/execnodes.h	29 Aug 2002 07:22:28 -0000
***************
*** 509,515 ****
   *		Function nodes are used to scan the results of a
   *		function appearing in FROM (typically a function returning set).
   *
!  *		functionmode		function operating mode
   *		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 FM_MATERIALIZE, in which the
!  *							executor repeatedly calls the SRF, building
!  *							up a tuplestore of results. More function
!  *							modes may be added in the future.
   *		tupdesc				function's return tuple description
   *		tuplestorestate		private state of tuplestore.c
   *		funcexpr			function expression being evaluated
***************
*** 520,528 ****
   */
  typedef enum FunctionMode
  {
! 	PM_REPEATEDCALL,
! 	PM_MATERIALIZE,
! 	PM_QUERY
  } FunctionMode;
  
  typedef struct FunctionScanState
--- 524,530 ----
   */
  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	29 Aug 2002 07:22:28 -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	29 Aug 2002 07:22:28 -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.35
diff -c -r1.35 gram.y
*** src/pl/plpgsql/src/gram.y	28 Aug 2002 20:46:24 -0000	1.35
--- src/pl/plpgsql/src/gram.y	29 Aug 2002 07:22:28 -0000
***************
*** 48,54 ****
  static	PLpgSQL_stmt	*make_select_stmt(void);
  static	PLpgSQL_stmt	*make_fetch_stmt(void);
  static	PLpgSQL_expr	*make_tupret_expr(PLpgSQL_row *row);
! static void check_assignable(PLpgSQL_datum *datum);
  
  %}
  
--- 48,54 ----
  static	PLpgSQL_stmt	*make_select_stmt(void);
  static	PLpgSQL_stmt	*make_fetch_stmt(void);
  static	PLpgSQL_expr	*make_tupret_expr(PLpgSQL_row *row);
! static	void check_assignable(PLpgSQL_datum *datum);
  
  %}
  
***************
*** 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
***************
*** 177,182 ****
--- 178,184 ----
  %token	K_RENAME
  %token	K_RESULT_OID
  %token	K_RETURN
+ %token	K_RETURN_NEXT
  %token	K_REVERSE
  %token	K_SELECT
  %token	K_THEN
***************
*** 516,525 ****
  
  						plpgsql_convert_ident(yytext, &name, 1);
  						if (name[0] != '$')
! 						{
! 							plpgsql_error_lineno = yylineno;
! 							elog(ERROR, "can only alias positional parameters");
! 						}
  						plpgsql_ns_setlocal(false);
  						nsi = plpgsql_ns_lookup(name, NULL);
  						if (nsi == NULL)
--- 518,525 ----
  
  						plpgsql_convert_ident(yytext, &name, 1);
  						if (name[0] != '$')
! 							yyerror("can only alias positional parameters");
! 
  						plpgsql_ns_setlocal(false);
  						nsi = plpgsql_ns_lookup(name, NULL);
  						if (nsi == NULL)
***************
*** 609,622 ****
  						switch (tok)
  						{
  							case 0:
! 								plpgsql_error_lineno = lno;
! 								elog(ERROR, "unexpected end of file");
  							case K_NULL:
  								if (yylex() != ';')
! 								{
! 									plpgsql_error_lineno = lno;
! 									elog(ERROR, "expected ; after NULL");
! 								}
  								free(expr);
  								plpgsql_dstring_free(&ds);
  
--- 609,619 ----
  						switch (tok)
  						{
  							case 0:
! 								yyerror("unexpected end of file");
  							case K_NULL:
  								if (yylex() != ';')
! 									yyerror("expected ; after NULL");
! 
  								free(expr);
  								plpgsql_dstring_free(&ds);
  
***************
*** 628,637 ****
  								while ((tok = yylex()) != ';')
  								{
  									if (tok == 0)
! 									{
! 										plpgsql_error_lineno = lno;
! 										elog(ERROR, "unterminated default value");
! 									}
  									if (plpgsql_SpaceScanned)
  										plpgsql_dstring_append(&ds, " ");
  									plpgsql_dstring_append(&ds, yytext);
--- 625,632 ----
  								while ((tok = yylex()) != ';')
  								{
  									if (tok == 0)
! 										yyerror("unterminated default value");
! 
  									if (plpgsql_SpaceScanned)
  										plpgsql_dstring_append(&ds, " ");
  									plpgsql_dstring_append(&ds, yytext);
***************
*** 663,669 ****
  
  proc_stmts		: proc_stmts proc_stmt
  						{
! 								if ($1->stmts_used == $1->stmts_alloc) {
  									$1->stmts_alloc *= 2;
  									$1->stmts = realloc($1->stmts, sizeof(PLpgSQL_stmt *) * $1->stmts_alloc);
  								}
--- 658,665 ----
  
  proc_stmts		: proc_stmts proc_stmt
  						{
! 								if ($1->stmts_used == $1->stmts_alloc)
! 								{
  									$1->stmts_alloc *= 2;
  									$1->stmts = realloc($1->stmts, sizeof(PLpgSQL_stmt *) * $1->stmts_alloc);
  								}
***************
*** 708,713 ****
--- 704,711 ----
  						{ $$ = $1; }
  				| stmt_return
  						{ $$ = $1; }
+ 				| stmt_return_next
+ 						{ $$ = $1; }
  				| stmt_raise
  						{ $$ = $1; }
  				| stmt_execsql
***************
*** 1121,1165 ****
  stmt_return		: K_RETURN lno
  					{
  						PLpgSQL_stmt_return *new;
- 						PLpgSQL_expr	*expr = NULL;
- 						int				tok;
  
  						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;
! 							switch (tok = yylex())
  							{
  								case K_NULL:
! 									expr = NULL;
  									break;
  
  								case T_ROW:
! 									expr = make_tupret_expr(yylval.row);
  									break;
  
  								case T_RECORD:
  									new->retrecno = yylval.rec->recno;
! 									expr = NULL;
  									break;
  
  								default:
! 									yyerror("return type mismatch in function returning table row");
  									break;
  							}
  							if (yylex() != ';')
  								yyerror("expected ';'");
- 						} else {
- 							new->retistuple = false;
- 							expr = plpgsql_read_expression(';', ";");
  						}
  
  						new->cmd_type = PLPGSQL_STMT_RETURN;
  						new->lineno   = $2;
! 						new->expr	  = expr;
  
  						$$ = (PLpgSQL_stmt *)new;
  					}
--- 1119,1191 ----
  stmt_return		: K_RETURN lno
  					{
  						PLpgSQL_stmt_return *new;
  
  						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->retrecno	= -1;
! 							switch (yylex())
  							{
  								case K_NULL:
! 									new->expr = NULL;
  									break;
  
  								case T_ROW:
! 									new->expr = make_tupret_expr(yylval.row);
  									break;
  
  								case T_RECORD:
  									new->retrecno = yylval.rec->recno;
! 									new->expr = NULL;
  									break;
  
  								default:
! 									yyerror("return type mismatch in function returning tuple");
  									break;
  							}
  							if (yylex() != ';')
  								yyerror("expected ';'");
  						}
+ 						else
+ 							new->expr = plpgsql_read_expression(';', ";");
  
  						new->cmd_type = PLPGSQL_STMT_RETURN;
  						new->lineno   = $2;
! 
! 						$$ = (PLpgSQL_stmt *)new;
! 					}
! 				;
! 
! /* FIXME: this syntax needs work, RETURN NEXT breaks stmt_return */
! stmt_return_next: K_RETURN_NEXT lno
! 					{
! 						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;
! 
! 						if (plpgsql_curr_compile->fn_retistuple)
! 						{
! 							int tok = yylex();
! 
! 							if (tok == T_RECORD)
! 								new->rec = yylval.rec;
! 							else if (tok == T_ROW)
! 								new->row = yylval.row;
! 							else
! 								yyerror("Incorrect argument to RETURN NEXT");
! 
! 							if (yylex() != ';')
! 								yyerror("Expected ';'");
! 						}
! 						else
! 							new->expr = plpgsql_read_expression(';', ";");
  
  						$$ = (PLpgSQL_stmt *)new;
  					}
***************
*** 1226,1232 ****
  					}
  				| K_DEBUG
  					{
! 						$$ = DEBUG5;
  					}
  				;
  
--- 1252,1258 ----
  					}
  				| K_DEBUG
  					{
! 						$$ = DEBUG1;
  					}
  				;
  
***************
*** 1377,1386 ****
  								cp += strlen(cp) - 1;
  
  								if (*cp != ')')
! 								{
! 									plpgsql_error_lineno = yylineno;
! 									elog(ERROR, "missing )");
! 								}
  								*cp = '\0';
  							}
  							else
--- 1403,1409 ----
  								cp += strlen(cp) - 1;
  
  								if (*cp != ')')
! 									yyerror("missing )");
  								*cp = '\0';
  							}
  							else
***************
*** 1433,1442 ****
  cursor_varptr	: T_VARIABLE
  					{
  						if (yylval.variable->dtype != PLPGSQL_DTYPE_VAR)
! 						{
! 							plpgsql_error_lineno = yylineno;
! 							elog(ERROR, "cursor variable must be a simple variable");
! 						}
  						if (((PLpgSQL_var *) yylval.variable)->datatype->typoid != REFCURSOROID)
  						{
  							plpgsql_error_lineno = yylineno;
--- 1456,1463 ----
  cursor_varptr	: T_VARIABLE
  					{
  						if (yylval.variable->dtype != PLPGSQL_DTYPE_VAR)
! 							yyerror("cursor variable must be a simple variable");
! 
  						if (((PLpgSQL_var *) yylval.variable)->datatype->typoid != REFCURSOROID)
  						{
  							plpgsql_error_lineno = yylineno;
***************
*** 1450,1459 ****
  cursor_variable	: T_VARIABLE
  					{
  						if (yylval.variable->dtype != PLPGSQL_DTYPE_VAR)
! 						{
! 							plpgsql_error_lineno = yylineno;
! 							elog(ERROR, "cursor variable must be a simple variable");
! 						}
  						if (((PLpgSQL_var *) yylval.variable)->datatype->typoid != REFCURSOROID)
  						{
  							plpgsql_error_lineno = yylineno;
--- 1471,1478 ----
  cursor_variable	: T_VARIABLE
  					{
  						if (yylval.variable->dtype != PLPGSQL_DTYPE_VAR)
! 							yyerror("cursor variable must be a simple variable");
! 
  						if (((PLpgSQL_var *) yylval.variable)->datatype->typoid != REFCURSOROID)
  						{
  							plpgsql_error_lineno = yylineno;
***************
*** 1906,1923 ****
  			break;
  
  		default:
! 			plpgsql_error_lineno = yylineno;
! 			elog(ERROR, "syntax error at '%s'", yytext);
  	}
  
  	if (!have_nexttok)
  		tok = yylex();
  
  	if (tok != ';')
! 	{
! 		plpgsql_error_lineno = yylineno;
! 		elog(ERROR, "syntax error at '%s'", yytext);
! 	}
  
  	fetch = malloc(sizeof(PLpgSQL_stmt_select));
  	memset(fetch, 0, sizeof(PLpgSQL_stmt_fetch));
--- 1925,1938 ----
  			break;
  
  		default:
! 			yyerror("syntax error");
  	}
  
  	if (!have_nexttok)
  		tok = yylex();
  
  	if (tok != ';')
! 		yyerror("syntax error");
  
  	fetch = malloc(sizeof(PLpgSQL_stmt_select));
  	memset(fetch, 0, sizeof(PLpgSQL_stmt_fetch));
***************
*** 1976,1986 ****
  			/* always assignable? */
  			break;
  		case PLPGSQL_DTYPE_TRIGARG:
! 			plpgsql_error_lineno = yylineno;
! 			elog(ERROR, "cannot assign to tg_argv");
  			break;
  		default:
! 			elog(ERROR, "check_assignable: unexpected datum type");
  			break;
  	}
  }
--- 1991,2000 ----
  			/* always assignable? */
  			break;
  		case PLPGSQL_DTYPE_TRIGARG:
! 			yyerror("cannot assign to tg_argv");
  			break;
  		default:
! 			yyerror("check_assignable: unexpected datum type");
  			break;
  	}
  }
Index: src/pl/plpgsql/src/pl_comp.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/pl/plpgsql/src/pl_comp.c,v
retrieving revision 1.48
diff -c -r1.48 pl_comp.c
*** src/pl/plpgsql/src/pl_comp.c	28 Aug 2002 20:46:24 -0000	1.48
--- src/pl/plpgsql/src/pl_comp.c	29 Aug 2002 07:22:28 -0000
***************
*** 37,44 ****
  
  #include "plpgsql.h"
  
- #include <unistd.h>
- #include <fcntl.h>
  #include <ctype.h>
  #include <setjmp.h>
  
--- 37,42 ----
***************
*** 52,60 ****
  #include "catalog/pg_class.h"
  #include "catalog/pg_proc.h"
  #include "catalog/pg_type.h"
- #include "commands/trigger.h"
- #include "executor/spi.h"
- #include "fmgr.h"
  #include "nodes/makefuncs.h"
  #include "parser/gramparse.h"
  #include "parser/parse_type.h"
--- 50,55 ----
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.59
diff -c -r1.59 pl_exec.c
*** src/pl/plpgsql/src/pl_exec.c	29 Aug 2002 04:12:03 -0000	1.59
--- src/pl/plpgsql/src/pl_exec.c	29 Aug 2002 07:22:28 -0000
***************
*** 35,46 ****
   *
   **********************************************************************/
  
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <string.h>
  #include <ctype.h>
  #include <setjmp.h>
  
--- 35,40 ----
***************
*** 50,59 ****
  #include "access/heapam.h"
  #include "catalog/pg_proc.h"
  #include "catalog/pg_type.h"
- #include "commands/trigger.h"
- #include "executor/spi.h"
  #include "executor/spi_priv.h"
! #include "fmgr.h"
  #include "optimizer/clauses.h"
  #include "parser/parse_expr.h"
  #include "tcop/tcopprot.h"
--- 44,51 ----
  #include "access/heapam.h"
  #include "catalog/pg_proc.h"
  #include "catalog/pg_type.h"
  #include "executor/spi_priv.h"
! #include "funcapi.h"
  #include "optimizer/clauses.h"
  #include "parser/parse_expr.h"
  #include "tcop/tcopprot.h"
***************
*** 105,110 ****
--- 97,104 ----
  			   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 ****
--- 144,153 ----
  				int32 reqtypmod,
  				bool *isnull);
  static void exec_set_found(PLpgSQL_execstate * estate, bool state);
+ static void exec_init_tuple_store(PLpgSQL_execstate *estate);
+ static void exec_set_ret_tupdesc(PLpgSQL_execstate *estate, List *labels);
+ static int exec_finish_set_func(PLpgSQL_execstate *estate,
+ 								PLpgSQL_stmt_return *stmt);
  
  
  /* ----------
***************
*** 332,342 ****
  	 * 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;
  
! 	if (!estate.retisnull)
  	{
  		if (estate.retistuple)
  		{
--- 330,340 ----
  	 * 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;
  
! 	if (!(estate.retisnull || estate.retisset))
  	{
  		if (estate.retistuple)
  		{
***************
*** 908,913 ****
--- 906,915 ----
  			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;
***************
*** 1302,1314 ****
  	 */
  	if (stmt->rec != NULL)
  		rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]);
  	else
! 	{
! 		if (stmt->row != NULL)
! 			row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]);
! 		else
! 			elog(ERROR, "unsupported target in exec_stmt_fors()");
! 	}
  
  	/*
  	 * Open the implicit cursor for the statement and fetch the initial 10
--- 1304,1313 ----
  	 */
  	if (stmt->rec != NULL)
  		rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]);
+ 	else if (stmt->row != NULL)
+ 		row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]);
  	else
! 		elog(ERROR, "unsupported target in exec_stmt_fors()");
  
  	/*
  	 * Open the implicit cursor for the statement and fetch the initial 10
***************
*** 1499,1504 ****
--- 1498,1510 ----
  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 */
***************
*** 1539,1544 ****
--- 1545,1674 ----
  	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 || estate->rettupdesc == NULL)
+ 		elog(ERROR, "Table functions must return at least 1 tuple");
+ 
+ 	tuplestore_donestoring(estate->tuple_store);
+ 
+ 	return PLPGSQL_RC_RETURN;
+ }
+ 
+ /*
+  * 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 if (stmt->row)
+ 	{
+ 		PLpgSQL_var *var;
+ 		TupleDesc tupdesc;
+ 		Datum	*dvalues;
+ 		char	*nulls;
+ 		int		 natts;
+ 		int		 i;
+ 
+ 		if (!estate->rettupdesc)
+ 			exec_set_ret_tupdesc(estate, NIL);
+ 
+ 		tupdesc = estate->rettupdesc;
+ 		natts	= tupdesc->natts;
+ 		dvalues	= (Datum *) palloc(natts * sizeof(*dvalues));
+ 		nulls	= (char *) palloc(natts);
+ 
+ 		MemSet(dvalues, 0, natts * sizeof(*dvalues));
+ 		MemSet(nulls, 'n', natts);
+ 
+ 		for (i = 0; i < stmt->row->nfields; i++)
+ 		{
+ 			var = (PLpgSQL_var *) (estate->datums[stmt->row->varnos[i]]);
+ 			dvalues[i] = var->value;
+ 			if (!var->isnull)
+ 				nulls[i] = ' ';
+ 		}
+ 
+ 		tuple = heap_formtuple(tupdesc, dvalues, nulls);
+ 	}
+ 	else if (stmt->expr)
+ 	{
+ 		Datum	retval;
+ 		bool	isNull;
+ 		char	nullflag;
+ 
+ 		if (!estate->rettupdesc)
+ 			exec_set_ret_tupdesc(estate, makeList1(makeString("unused")));
+ 
+ 		retval = exec_eval_expr(estate, stmt->expr, &isNull, &(estate->rettype));
+ 
+ 		nullflag = isNull ? 'n' : ' ';
+ 
+ 		tuple = heap_formtuple(estate->rettupdesc, &retval, &nullflag);
+ 
+ 		exec_eval_cleanup(estate);
+ 	}
+ 	else
+ 		elog(ERROR, "Blank RETURN NEXT not allowed");
+ 
+ 	if (HeapTupleIsValid(tuple))
+ 	{
+ 		MemoryContext oldcxt;
+ 
+ 		oldcxt = MemoryContextSwitchTo(TopTransactionContext);
+ 		tuplestore_puttuple(estate->tuple_store, tuple);
+ 		MemoryContextSwitchTo(oldcxt);
+ 	}
+ 
+ 	estate->retisnull = false;
+ 
+ 	return PLPGSQL_RC_OK;
+ }
+ 
+ static void
+ exec_set_ret_tupdesc(PLpgSQL_execstate *estate, List *labels)
+ {
+ 	estate->rettype		= get_func_rettype(estate->func_oid);
+ 	estate->rettupdesc	= TypeGetTupleDesc(estate->rettype, labels);
+ 
+ 	if (!estate->rettupdesc)
+ 		elog(ERROR, "Could not produce descriptor for rowtype.");
+ }
+ 
+ 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
***************
*** 1700,1706 ****
  
  
  /* ----------
!  * Initialize an empty estate
   * ----------
   */
  static void
--- 1830,1836 ----
  
  
  /* ----------
!  * Initialize a mostly empty execution state
   * ----------
   */
  static void
***************
*** 1715,1720 ****
--- 1845,1853 ----
  	estate->retisset = func->fn_retset;
  	estate->exitlabel = NULL;
  
+ 	estate->func_oid = func->fn_oid;
+ 	estate->tuple_store = NULL;
+ 
  	estate->trig_nargs = 0;
  	estate->trig_argv = NULL;
  
***************
*** 2099,2111 ****
  	 */
  	if (stmt->rec != NULL)
  		rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]);
  	else
! 	{
! 		if (stmt->row != NULL)
! 			row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]);
! 		else
! 			elog(ERROR, "unsupported target in exec_stmt_fors()");
! 	}
  
  	/*
  	 * Evaluate the string expression after the EXECUTE keyword. It's
--- 2232,2241 ----
  	 */
  	if (stmt->rec != NULL)
  		rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]);
+ 	else if (stmt->row != NULL)
+ 		row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]);
  	else
! 		elog(ERROR, "unsupported target in exec_stmt_dynfors()");
  
  	/*
  	 * Evaluate the string expression after the EXECUTE keyword. It's
Index: src/pl/plpgsql/src/pl_funcs.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/pl/plpgsql/src/pl_funcs.c,v
retrieving revision 1.19
diff -c -r1.19 pl_funcs.c
*** src/pl/plpgsql/src/pl_funcs.c	8 Aug 2002 01:36:05 -0000	1.19
--- src/pl/plpgsql/src/pl_funcs.c	29 Aug 2002 07:22:28 -0000
***************
*** 35,46 ****
   *
   **********************************************************************/
  
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <string.h>
  #include <ctype.h>
  
  #include "plpgsql.h"
--- 35,40 ----
***************
*** 274,282 ****
  				return ns->items[i];
  		}
  		if (ns_localmode)
- 		{
  			return NULL;		/* name not found in current namespace */
- 		}
  	}
  
  	return NULL;
--- 268,274 ----
Index: src/pl/plpgsql/src/pl_handler.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/pl/plpgsql/src/pl_handler.c,v
retrieving revision 1.11
diff -c -r1.11 pl_handler.c
*** src/pl/plpgsql/src/pl_handler.c	15 Jun 2002 19:54:24 -0000	1.11
--- src/pl/plpgsql/src/pl_handler.c	29 Aug 2002 07:22:28 -0000
***************
*** 35,47 ****
   *
   **********************************************************************/
  
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <string.h>
- 
  #include "plpgsql.h"
  #include "pl.tab.h"
  
--- 35,40 ----
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	29 Aug 2002 07:22:28 -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,
***************
*** 420,430 ****
  {								/* RETURN statement			*/
  	int			cmd_type;
  	int			lineno;
- 	bool		retistuple;
  	PLpgSQL_expr *expr;
  	int			retrecno;
  }	PLpgSQL_stmt_return;
  
  
  typedef struct
  {								/* RAISE statement			*/
--- 423,440 ----
  {								/* RETURN statement			*/
  	int			cmd_type;
  	int			lineno;
  	PLpgSQL_expr *expr;
  	int			retrecno;
  }	PLpgSQL_stmt_return;
  
+ typedef struct
+ {								/* RETURN NEXT statement */
+ 	int			cmd_type;
+ 	int			lineno;
+ 	PLpgSQL_rec *rec;
+ 	PLpgSQL_row *row;
+ 	PLpgSQL_expr *expr;
+ }	PLpgSQL_stmt_return_next;
  
  typedef struct
  {								/* RAISE statement			*/
***************
*** 499,504 ****
--- 509,517 ----
  	TupleDesc	rettupdesc;
  	bool		retisset;
  	char	   *exitlabel;
+ 
+ 	Oid			func_oid;
+ 	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	29 Aug 2002 07:22:28 -0000
***************
*** 46,51 ****
--- 46,53 ----
  static int	scanner_typereported;
  static int	pushback_token;
  static bool have_pushback_token;
+ static int	lookahead_token;	
+ static bool have_lookahead_token;
  
  int	plpgsql_SpaceScanned = 0;
  
***************
*** 134,139 ****
--- 136,142 ----
  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;			}
***************
*** 255,272 ****
  }
  
  /*
!  * This is the yylex routine called from outside.  It exists to provide
!  * a token pushback facility.
   */
  int
  plpgsql_yylex(void)
  {
  	if (have_pushback_token)
  	{
  		have_pushback_token = false;
! 		return pushback_token;
  	}
! 	return yylex();
  }
  
  /*
--- 258,302 ----
  }
  
  /*
!  * This is the yylex routine called from outside. It exists to provide
!  * a pushback facility, as well as to allow us to parse syntax that
!  * requires more than one token of lookahead.
   */
  int
  plpgsql_yylex(void)
  {
+ 	int cur_token;
+ 
  	if (have_pushback_token)
  	{
  		have_pushback_token = false;
! 		cur_token = pushback_token;
! 	}
! 	else if (have_lookahead_token)
! 	{
! 		have_lookahead_token = false;
! 		cur_token = lookahead_token;
! 	}
! 	else
! 		cur_token = yylex();
! 
! 	/* Do we need to look ahead for a possible multiword token? */
! 	switch (cur_token)
! 	{
! 		/* RETURN NEXT must be reduced to a single token */
! 		case K_RETURN:
! 			lookahead_token = yylex();
! 			if (lookahead_token == K_NEXT)
! 				cur_token = K_RETURN_NEXT;
! 			else
! 				have_lookahead_token = true;
! 			break;
! 
! 		default:
! 			break;
  	}
! 
! 	return cur_token;
  }
  
  /*
***************
*** 312,315 ****
--- 342,346 ----
      scanner_typereported = 0;
  
  	have_pushback_token = false;
+ 	have_lookahead_token = false;
  }
Index: src/test/regress/expected/plpgsql.out
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/test/regress/expected/plpgsql.out,v
retrieving revision 1.9
diff -c -r1.9 plpgsql.out
*** src/test/regress/expected/plpgsql.out	22 Aug 2002 23:31:48 -0000	1.9
--- src/test/regress/expected/plpgsql.out	29 Aug 2002 07:22:28 -0000
***************
*** 162,168 ****
  create function tg_pslot_biu() returns trigger as '
  declare
      pfrec	record;
!     rename new to ps;
  begin
      select into pfrec * from PField where name = ps.pfname;
      if not found then
--- 162,168 ----
  create function tg_pslot_biu() returns trigger as '
  declare
      pfrec	record;
! 	rename new to ps;
  begin
      select into pfrec * from PField where name = ps.pfname;
      if not found then
***************
*** 1535,1540 ****
--- 1535,1548 ----
  insert into IFace values ('IF', 'orion', 'ethernet_interface_name_too_long', '');
  ERROR:  IFace slotname "IF.orion.ethernet_interface_name_too_long" too long (20 char max)
  --
+ -- The following tests are unrelated to the scenario outlined above;
+ -- they merely excercise a specific part of PL/PgSQL
+ --
+ --
+ -- The following tests are unrelated to the scenario outlined above;
+ -- they merely excercise a specific part of PL/PgSQL
+ --
+ --
  -- Test recursion, per bug report 7-Sep-01
  --
  CREATE FUNCTION recursion_test(int,int) RETURNS text AS '
***************
*** 1557,1563 ****
  -- Test the FOUND magic variable
  --
  CREATE TABLE found_test_tbl (a int);
! create function test_found ()
    returns boolean as '
    declare
    begin
--- 1565,1571 ----
  -- Test the FOUND magic variable
  --
  CREATE TABLE found_test_tbl (a int);
! create function test_found()
    returns boolean as '
    declare
    begin
***************
*** 1608,1611 ****
--- 1616,1689 ----
     5
     6
  (6 rows)
+ 
+ --
+ -- Test table functions for PL/pgSQL
+ --
+ create function test_table_func_rec() returns setof found_test_tbl as
+ '
+ DECLARE
+ 	rec RECORD;
+ BEGIN
+ 	FOR rec IN select * from found_test_tbl LOOP
+ 		RETURN NEXT rec;
+ 	END LOOP;
+ 	RETURN;
+ END;' language 'plpgsql';
+ select * from test_table_func_rec();
+   a  
+ -----
+    2
+  100
+    3
+    4
+    5
+    6
+ (6 rows)
+ 
+ create function test_table_func_row() returns setof found_test_tbl as
+ '
+ DECLARE
+ 	row found_test_tbl%ROWTYPE;
+ BEGIN
+ 	FOR row IN select * from found_test_tbl LOOP
+ 		RETURN NEXT row;
+ 	END LOOP;
+ 	RETURN;
+ END;' language 'plpgsql';
+ select * from test_table_func_row();
+   a  
+ -----
+    2
+  100
+    3
+    4
+    5
+    6
+ (6 rows)
+ 
+ create function test_ret_set_scalar() returns setof int as
+ '
+ DECLARE
+ 	i int;
+ BEGIN
+ 	FOR i IN 1 .. 10 LOOP
+ 		RETURN NEXT i + 1;
+ 	END LOOP;
+ 	RETURN;
+ END;' language 'plpgsql';
+ select * from test_ret_set_scalar();
+  test_ret_set_scalar 
+ ---------------------
+                    2
+                    3
+                    4
+                    5
+                    6
+                    7
+                    8
+                    9
+                   10
+                   11
+ (10 rows)
  
Index: src/test/regress/sql/plpgsql.sql
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/test/regress/sql/plpgsql.sql,v
retrieving revision 1.8
diff -c -r1.8 plpgsql.sql
*** src/test/regress/sql/plpgsql.sql	22 Aug 2002 23:31:48 -0000	1.8
--- src/test/regress/sql/plpgsql.sql	29 Aug 2002 07:22:28 -0000
***************
*** 211,217 ****
  create function tg_pslot_biu() returns trigger as '
  declare
      pfrec	record;
!     rename new to ps;
  begin
      select into pfrec * from PField where name = ps.pfname;
      if not found then
--- 211,217 ----
  create function tg_pslot_biu() returns trigger as '
  declare
      pfrec	record;
! 	rename new to ps;
  begin
      select into pfrec * from PField where name = ps.pfname;
      if not found then
***************
*** 1420,1425 ****
--- 1420,1435 ----
  insert into IFace values ('IF', 'orion', 'ethernet_interface_name_too_long', '');
  
  --
+ -- The following tests are unrelated to the scenario outlined above;
+ -- they merely excercise a specific part of PL/PgSQL
+ --
+ 
+ --
+ -- The following tests are unrelated to the scenario outlined above;
+ -- they merely excercise a specific part of PL/PgSQL
+ --
+ 
+ --
  -- Test recursion, per bug report 7-Sep-01
  --
  CREATE FUNCTION recursion_test(int,int) RETURNS text AS '
***************
*** 1440,1446 ****
  --
  CREATE TABLE found_test_tbl (a int);
  
! create function test_found ()
    returns boolean as '
    declare
    begin
--- 1450,1456 ----
  --
  CREATE TABLE found_test_tbl (a int);
  
! create function test_found()
    returns boolean as '
    declare
    begin
***************
*** 1478,1480 ****
--- 1488,1532 ----
  
  select test_found();
  select * from found_test_tbl;
+ 
+ --
+ -- Test table functions for PL/pgSQL
+ --
+ create function test_table_func_rec() returns setof found_test_tbl as
+ '
+ DECLARE
+ 	rec RECORD;
+ BEGIN
+ 	FOR rec IN select * from found_test_tbl LOOP
+ 		RETURN NEXT rec;
+ 	END LOOP;
+ 	RETURN;
+ END;' language 'plpgsql';
+ 
+ select * from test_table_func_rec();
+ 
+ create function test_table_func_row() returns setof found_test_tbl as
+ '
+ DECLARE
+ 	row found_test_tbl%ROWTYPE;
+ BEGIN
+ 	FOR row IN select * from found_test_tbl LOOP
+ 		RETURN NEXT row;
+ 	END LOOP;
+ 	RETURN;
+ END;' language 'plpgsql';
+ 
+ select * from test_table_func_row();
+ 
+ create function test_ret_set_scalar() returns setof int as
+ '
+ DECLARE
+ 	i int;
+ BEGIN
+ 	FOR i IN 1 .. 10 LOOP
+ 		RETURN NEXT i + 1;
+ 	END LOOP;
+ 	RETURN;
+ END;' language 'plpgsql';
+ 
+ select * from test_ret_set_scalar();
