diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c
index cd7e1d29524ed2d7fae6ecb22ac79042e7d737a7..bc5153b8005417c53434c8d16cc2af48d7ae5672 100644
--- a/src/backend/commands/command.c
+++ b/src/backend/commands/command.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.127 2001/05/09 21:10:38 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.128 2001/05/21 14:22:11 wieck Exp $
  *
  * NOTES
  *	  The PerformAddAttribute() code, like most of the relation
@@ -108,6 +108,7 @@ PerformPortalFetch(char *name,
 	QueryDesc  *queryDesc;
 	EState	   *estate;
 	MemoryContext oldcontext;
+	bool		faked_desc = false;
 
 	/*
 	 * sanity checks
@@ -143,13 +144,14 @@ PerformPortalFetch(char *name,
 	queryDesc = PortalGetQueryDesc(portal);
 	estate = PortalGetState(portal);
 
-	if (dest == None)			/* MOVE */
+	if (dest != queryDesc->dest)			/* MOVE */
 	{
 		QueryDesc  *qdesc = (QueryDesc *) palloc(sizeof(QueryDesc));
 
 		memcpy(qdesc, queryDesc, sizeof(QueryDesc));
 		qdesc->dest = dest;
 		queryDesc = qdesc;
+		faked_desc = true;
 	}
 
 	BeginCommand(name,
@@ -197,7 +199,7 @@ PerformPortalFetch(char *name,
 	/*
 	 * Clean up and switch back to old context.
 	 */
-	if (dest == None)			/* MOVE */
+	if (faked_desc)			/* MOVE */
 		pfree(queryDesc);
 
 	MemoryContextSwitchTo(oldcontext);
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 4aa8c475c30922ff7ef4c6003509102f002adeb3..d369827742741ca072c6040ca2297339882a0484 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -3,12 +3,13 @@
  * spi.c
  *				Server Programming Interface
  *
- * $Id: spi.c,v 1.53 2001/03/22 03:59:29 momjian Exp $
+ * $Id: spi.c,v 1.54 2001/05/21 14:22:17 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "executor/spi_priv.h"
 #include "access/printtup.h"
+#include "commands/command.h"
 
 uint32		SPI_processed = 0;
 Oid			SPI_lastoid = InvalidOid;
@@ -26,6 +27,9 @@ static int	_SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount);
 static int _SPI_execute_plan(_SPI_plan *plan,
 				  Datum *Values, char *Nulls, int tcount);
 
+static void _SPI_cursor_operation(Portal portal, bool forward, int count,
+					CommandDest dest);
+
 static _SPI_plan *_SPI_copy_plan(_SPI_plan *plan, int location);
 
 static int	_SPI_begin_call(bool execmem);
@@ -272,6 +276,18 @@ SPI_saveplan(void *plan)
 
 }
 
+int
+SPI_freeplan(void *plan)
+{
+	_SPI_plan  *spiplan = (_SPI_plan *)plan;
+
+	if (plan == NULL)
+		return SPI_ERROR_ARGUMENT;
+
+	MemoryContextDelete(spiplan->plancxt);
+	return 0;
+}
+
 HeapTuple
 SPI_copytuple(HeapTuple tuple)
 {
@@ -555,6 +571,181 @@ SPI_freetuple(HeapTuple tuple)
 	heap_freetuple(tuple);
 }
 
+void
+SPI_freetuptable(SPITupleTable *tuptable)
+{
+	if (tuptable != NULL)
+		MemoryContextDelete(tuptable->tuptabcxt);
+}
+
+
+
+/*
+ * SPI_cursor_open()
+ *
+ *	Open a prepared SPI plan as a portal
+ */
+Portal
+SPI_cursor_open(char *name, void *plan, Datum *Values, char *Nulls)
+{
+	static int			unnamed_portal_count = 0;
+
+	_SPI_plan		   *spiplan = (_SPI_plan *)plan;
+	List			   *qtlist = spiplan->qtlist;
+	List			   *ptlist = spiplan->ptlist;
+	Query			   *queryTree;
+	Plan			   *planTree;
+	QueryDesc		   *queryDesc;
+	EState			   *eState;
+	TupleDesc			attinfo;
+	MemoryContext		oldcontext;
+	Portal				portal;
+	char				portalname[64];
+	int					k;
+
+	/* Ensure that the plan contains only one regular SELECT query */
+	if (length(ptlist) != 1)
+		elog(ERROR, "cannot open multi-query plan as cursor");
+	queryTree = (Query *)lfirst(qtlist);
+	planTree  = (Plan *)lfirst(ptlist);
+
+	if (queryTree->commandType != CMD_SELECT)
+		elog(ERROR, "plan in SPI_cursor_open() is not a SELECT");
+	if (queryTree->isPortal)
+		elog(ERROR, "plan in SPI_cursor_open() must NOT be a DECLARE already");
+	else if (queryTree->into != NULL)
+		elog(ERROR, "plan in SPI_cursor_open() must NOT be a SELECT INTO");
+
+	/* Reset SPI result */
+	SPI_processed = 0;
+	SPI_tuptable = NULL;
+	_SPI_current->processed = 0;
+	_SPI_current->tuptable = NULL;
+
+	/* Make up a portal name if none given */
+	if (name == NULL)
+	{
+		for (;;)
+		{
+		    unnamed_portal_count++;
+			if (unnamed_portal_count < 0)
+				unnamed_portal_count = 0;
+			sprintf(portalname, "<unnamed cursor %d>", unnamed_portal_count);
+			if (GetPortalByName(portalname) == NULL)
+				break;
+		}
+
+		name = portalname;
+	}
+
+	/* Ensure the portal doesn't exist already */
+	portal = GetPortalByName(name);
+	if (portal != NULL)
+		elog(ERROR, "cursor \"%s\" already in use", name);
+
+	/* Create the portal */
+	portal = CreatePortal(name);
+	if (portal == NULL)
+		elog(ERROR, "failed to create portal \"%s\"", name);
+
+	/* Switch to portals memory and copy the parsetree and plan to there */
+	oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
+	queryTree  = copyObject(queryTree);
+	planTree   = copyObject(planTree);
+
+	/* Modify the parsetree to be a cursor */
+	queryTree->isPortal = true;
+	queryTree->into     = pstrdup(name);
+	queryTree->isBinary = false;
+	
+	/* Create the QueryDesc object and the executor state */
+	queryDesc = CreateQueryDesc(queryTree, planTree, SPI);
+	eState    = CreateExecutorState();
+
+	/* If the plan has parameters, put them into the executor state */
+	if (spiplan->nargs > 0)
+	{
+		ParamListInfo	paramLI = (ParamListInfo) palloc((spiplan->nargs + 1) *
+									sizeof(ParamListInfoData));
+		eState->es_param_list_info = paramLI;
+		for (k = 0; k < spiplan->nargs; paramLI++, k++)
+		{
+			paramLI->kind	= PARAM_NUM;
+			paramLI->id		= k + 1;
+			paramLI->isnull	= (Nulls && Nulls[k] == 'n');
+			paramLI->value	= Values[k];
+		}
+		paramLI->kind = PARAM_INVALID;
+	}
+	else
+		eState->es_param_list_info = NULL;
+
+	/* Start the executor */
+	attinfo = ExecutorStart(queryDesc, eState);
+
+	/* Put all the objects into the portal */
+	PortalSetQuery(portal, queryDesc, attinfo, eState, PortalCleanup);
+
+	/* Switch back to the callers memory context */
+	MemoryContextSwitchTo(oldcontext);
+
+	/* Return the created portal */
+	return portal;
+}
+
+
+/*
+ * SPI_cursor_find()
+ *
+ *	Find the portal of an existing open cursor
+ */
+Portal
+SPI_cursor_find(char *name)
+{
+	return GetPortalByName(name);
+}
+
+
+/*
+ * SPI_cursor_fetch()
+ *
+ *	Fetch rows in a cursor
+ */
+void
+SPI_cursor_fetch(Portal portal, bool forward, int count)
+{
+	_SPI_cursor_operation(portal, forward, count, SPI);
+}
+
+
+/*
+ * SPI_cursor_move()
+ *
+ *	Move in a cursor
+ */
+void
+SPI_cursor_move(Portal portal, bool forward, int count)
+{
+	_SPI_cursor_operation(portal, forward, count, None);
+}
+
+
+/*
+ * SPI_cursor_close()
+ *
+ *	Close a cursor
+ */
+void
+SPI_cursor_close(Portal portal)
+{
+	Portal	my_portal = portal;
+
+	if (!PortalIsValid(my_portal))
+		elog(ERROR, "invalid portal in SPI cursor operation");
+
+	PortalDrop(&my_portal);
+}
+
 /* =================== private functions =================== */
 
 /*
@@ -568,6 +759,7 @@ spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
 {
 	SPITupleTable *tuptable;
 	MemoryContext oldcxt;
+	MemoryContext tuptabcxt;
 
 	/*
 	 * When called by Executor _SPI_curid expected to be equal to
@@ -583,18 +775,31 @@ spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
 	tuptable = _SPI_current->tuptable;
 	if (tuptable == NULL)
 	{
+		tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
+												  "SPI TupTable",
+												  ALLOCSET_DEFAULT_MINSIZE,
+												  ALLOCSET_DEFAULT_INITSIZE,
+												  ALLOCSET_DEFAULT_MAXSIZE);
+		MemoryContextSwitchTo(tuptabcxt);
+
 		_SPI_current->tuptable = tuptable = (SPITupleTable *)
 			palloc(sizeof(SPITupleTable));
+		tuptable->tuptabcxt = tuptabcxt;
 		tuptable->alloced = tuptable->free = 128;
 		tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
 		tuptable->tupdesc = CreateTupleDescCopy(tupdesc);
 	}
-	else if (tuptable->free == 0)
+	else 
 	{
-		tuptable->free = 256;
-		tuptable->alloced += tuptable->free;
-		tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
-								  tuptable->alloced * sizeof(HeapTuple));
+		MemoryContextSwitchTo(tuptable->tuptabcxt);
+
+		if (tuptable->free == 0)
+		{
+			tuptable->free = 256;
+			tuptable->alloced += tuptable->free;
+			tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
+									  tuptable->alloced * sizeof(HeapTuple));
+		}
 	}
 
 	tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
@@ -876,6 +1081,86 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
 
 }
 
+/*
+ * _SPI_cursor_operation()
+ *
+ *	Do a FETCH or MOVE in a cursor
+ */
+static void
+_SPI_cursor_operation(Portal portal, bool forward, int count,
+					CommandDest dest)
+{
+    QueryDesc	   *querydesc;
+	EState		   *estate;
+	MemoryContext	oldcontext;
+	CommandDest		olddest;
+
+	/* Check that the portal is valid */
+	if (!PortalIsValid(portal))
+		elog(ERROR, "invalid portal in SPI cursor operation");
+
+	/* Push the SPI stack */
+	_SPI_begin_call(true);
+
+	/* Reset the SPI result */
+	SPI_processed = 0;
+	SPI_tuptable = NULL;
+	_SPI_current->processed = 0;
+	_SPI_current->tuptable = NULL;
+
+	/* Switch to the portals memory context */
+	oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
+	querydesc  = PortalGetQueryDesc(portal);
+	estate     = PortalGetState(portal);
+
+	/* Save the queries command destination and set it to SPI (for fetch) */
+	/* or None (for move) */
+	olddest = querydesc->dest;
+	querydesc->dest = dest;
+
+	/* Run the executor like PerformPortalFetch and remember states */
+	if (forward)
+	{
+		if (!portal->atEnd)
+		{
+			ExecutorRun(querydesc, estate, EXEC_FOR, (long)count);
+			_SPI_current->processed = estate->es_processed;
+			if (estate->es_processed > 0)
+				portal->atStart = false;
+			if (count <= 0 || (int) estate->es_processed < count)
+				portal->atEnd = true;
+		}
+	}
+	else
+	{
+		if (!portal->atStart)
+		{
+			ExecutorRun(querydesc, estate, EXEC_BACK, (long) count);
+			_SPI_current->processed = estate->es_processed;
+			if (estate->es_processed > 0)
+				portal->atEnd = false;
+			if (count <= 0 || estate->es_processed < count)
+				portal->atStart = true;
+		}
+	}
+
+	/* Restore the old command destination and switch back to callers */
+	/* memory context */
+	querydesc->dest = olddest;
+	MemoryContextSwitchTo(oldcontext);
+
+	if (dest == SPI && _SPI_checktuples())
+		elog(FATAL, "SPI_fetch: # of processed tuples check failed");
+
+	/* Put the result into place for access by caller */
+	SPI_processed = _SPI_current->processed;
+	SPI_tuptable  = _SPI_current->tuptable;
+
+	/* Pop the SPI stack */
+	_SPI_end_call(true);
+}
+
+
 static MemoryContext
 _SPI_execmem()
 {
@@ -956,14 +1241,33 @@ static _SPI_plan *
 _SPI_copy_plan(_SPI_plan *plan, int location)
 {
 	_SPI_plan  *newplan;
-	MemoryContext oldcxt = NULL;
+	MemoryContext oldcxt;
+	MemoryContext plancxt;
+	MemoryContext parentcxt = CurrentMemoryContext;
 
+	/* Determine correct parent for the plans memory context */
 	if (location == _SPI_CPLAN_PROCXT)
+		parentcxt = _SPI_current->procCxt;
+		/*
 		oldcxt = MemoryContextSwitchTo(_SPI_current->procCxt);
+		*/
 	else if (location == _SPI_CPLAN_TOPCXT)
+		parentcxt = TopMemoryContext;
+		/*
 		oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+		*/
 
+	/* Create a memory context for the plan */
+	plancxt = AllocSetContextCreate(parentcxt,
+									  "SPI Plan",
+									  ALLOCSET_DEFAULT_MINSIZE,
+									  ALLOCSET_DEFAULT_INITSIZE,
+									  ALLOCSET_DEFAULT_MAXSIZE);
+	oldcxt = MemoryContextSwitchTo(plancxt);
+
+	/* Copy the SPI plan into it's own context */
 	newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan));
+	newplan->plancxt = plancxt;
 	newplan->qtlist = (List *) copyObject(plan->qtlist);
 	newplan->ptlist = (List *) copyObject(plan->ptlist);
 	newplan->nargs = plan->nargs;
@@ -975,8 +1279,7 @@ _SPI_copy_plan(_SPI_plan *plan, int location)
 	else
 		newplan->argtypes = NULL;
 
-	if (oldcxt != NULL)
-		MemoryContextSwitchTo(oldcxt);
+	MemoryContextSwitchTo(oldcxt);
 
 	return newplan;
 }
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index e04f97e0ec8322bdd5b833995e95e9cabcb1a360..2616b4af7d44daa12f2b67d9c98cdb9b9747e318 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.79 2001/05/20 20:28:19 tgl Exp $
+ * $Id: catversion.h,v 1.80 2001/05/21 14:22:17 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200105191
+#define CATALOG_VERSION_NO	200105211
 
 #endif
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 1cb9d2a57026ab64918c16a3f0965540989ccb83..5b330d9ea309f21134fb24c1e1d8e4693de4479c 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_type.h,v 1.105 2001/05/14 20:30:21 momjian Exp $
+ * $Id: pg_type.h,v 1.106 2001/05/21 14:22:18 wieck Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -413,6 +413,11 @@ DATA(insert OID = 1700 ( numeric	   PGUID -1  -1 f b t \054 0  0 numeric_in nume
 DESCR("numeric(precision, decimal), arbitrary precision number");
 #define NUMERICOID		1700
 
+/* OID 1790 */
+DATA(insert OID = 1790 ( refcursor	   PGUID -1  -1 f b t \054 0  0 textin textout textin textout i x _null_ ));
+DESCR("reference cursor (portal name)");
+#define REFCURSOROID	1790
+
 
 /*
  * prototypes for functions in pg_type.c
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index b95eaae4e9e19e535111175fd71638dcd5e0b3ed..3a7c7807942ab4376183a35f8ad0b564986a562e 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -41,6 +41,7 @@
 
 typedef struct
 {
+	MemoryContext tuptabcxt;	/* memory context of result table */
 	uint32		alloced;		/* # of alloced vals */
 	uint32		free;			/* # of free vals */
 	TupleDesc	tupdesc;		/* tuple descriptor */
@@ -83,6 +84,7 @@ extern int	SPI_exec(char *src, int tcount);
 extern int	SPI_execp(void *plan, Datum *values, char *Nulls, int tcount);
 extern void *SPI_prepare(char *src, int nargs, Oid *argtypes);
 extern void *SPI_saveplan(void *plan);
+extern int  SPI_freeplan(void *plan);
 
 extern HeapTuple SPI_copytuple(HeapTuple tuple);
 extern HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts,
@@ -98,6 +100,14 @@ 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);
+extern Portal SPI_cursor_find(char *name);
+extern void   SPI_cursor_fetch(Portal portal, bool forward, int count);
+extern void   SPI_cursor_move(Portal portal, bool forward, int count);
+extern void   SPI_cursor_close(Portal portal);
 
 extern void AtEOXact_SPI(void);
 
diff --git a/src/include/executor/spi_priv.h b/src/include/executor/spi_priv.h
index 00b28a58037f6029cade46ba2f138c2f88459d0e..e56266340017ada91e515d200d224bc7c05a8387 100644
--- a/src/include/executor/spi_priv.h
+++ b/src/include/executor/spi_priv.h
@@ -3,7 +3,7 @@
  * spi.c
  *				Server Programming Interface private declarations
  *
- * $Header: /cvsroot/pgsql/src/include/executor/spi_priv.h,v 1.7 2000/06/28 03:33:05 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/include/executor/spi_priv.h,v 1.8 2001/05/21 14:22:18 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,6 +25,7 @@ typedef struct
 
 typedef struct
 {
+	MemoryContext plancxt;
 	List	   *qtlist;
 	List	   *ptlist;
 	int			nargs;
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index 1cdd770dfdc0de37f7227d54becd824b4ef1ef96..6a17ec867044a0c4edfee5e8a10441154fa67bae 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: portal.h,v 1.27 2001/03/22 04:01:14 momjian Exp $
+ * $Id: portal.h,v 1.28 2001/05/21 14:22:18 wieck Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -55,7 +55,7 @@ typedef struct PortalData
  * estimate of the maximum number of open portals a user would have,
  * used in initially sizing the PortalHashTable in EnablePortalManager()
  */
-#define PORTALS_PER_USER	   10
+#define PORTALS_PER_USER	   64
 
 
 extern void EnablePortalManager(void);
diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y
index b137367454058ebe2c49773858be81396b010201..9dee73acc97823464d71b0391bb7c5554aff8d67 100644
--- a/src/pl/plpgsql/src/gram.y
+++ b/src/pl/plpgsql/src/gram.y
@@ -4,7 +4,7 @@
  *						  procedural language
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.18 2001/05/18 21:16:59 wieck Exp $
+ *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.19 2001/05/21 14:22:18 wieck Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -47,6 +47,7 @@
 
 static	PLpgSQL_expr	*read_sqlstmt(int until, char *s, char *sqlstart);
 static	PLpgSQL_stmt	*make_select_stmt(void);
+static	PLpgSQL_stmt	*make_fetch_stmt(void);
 static	PLpgSQL_expr	*make_tupret_expr(PLpgSQL_row *row);
 
 %}
@@ -99,17 +100,17 @@ static	PLpgSQL_expr	*make_tupret_expr(PLpgSQL_row *row);
 %type <varname> decl_varname
 %type <str>		decl_renname
 %type <ival>	decl_const, decl_notnull, decl_atttypmod, decl_atttypmodval
-%type <expr>	decl_defval
+%type <expr>	decl_defval, decl_cursor_query
 %type <dtype>	decl_datatype, decl_dtypename
-%type <row>		decl_rowtype
+%type <row>		decl_rowtype, decl_cursor_args, decl_cursor_arglist
 %type <nsitem>	decl_aliasitem
 %type <str>		decl_stmts, decl_stmt
 
 %type <expr>	expr_until_semi, expr_until_then, expr_until_loop
 %type <expr>	opt_exitcond
 
-%type <ival>	assign_var
-%type <var>		fori_var
+%type <ival>	assign_var, cursor_variable
+%type <var>		fori_var, cursor_varptr, decl_cursor_arg
 %type <varname> fori_varname
 %type <forilow> fori_lower
 %type <rec>		fors_target
@@ -124,6 +125,7 @@ static	PLpgSQL_expr	*make_tupret_expr(PLpgSQL_row *row);
 %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
 
 %type <intlist>	raise_params
 %type <ival>	raise_level, raise_param
@@ -140,7 +142,9 @@ static	PLpgSQL_expr	*make_tupret_expr(PLpgSQL_row *row);
 %token	K_ALIAS
 %token	K_ASSIGN
 %token	K_BEGIN
+%token	K_CLOSE
 %token	K_CONSTANT
+%token	K_CURSOR
 %token	K_DEBUG
 %token	K_DECLARE
 %token	K_DEFAULT
@@ -153,15 +157,18 @@ static	PLpgSQL_expr	*make_tupret_expr(PLpgSQL_row *row);
 %token	K_EXECUTE
 %token	K_EXIT
 %token	K_FOR
+%token	K_FETCH
 %token	K_FROM
 %token	K_GET
 %token	K_IF
 %token	K_IN
 %token	K_INTO
+%token	K_IS
 %token	K_LOOP
 %token	K_NOT
 %token	K_NOTICE
 %token	K_NULL
+%token	K_OPEN
 %token	K_PERFORM
 %token	K_ROW_COUNT
 %token	K_RAISE
@@ -300,6 +307,7 @@ decl_statement	: decl_varname decl_const decl_datatype decl_notnull decl_defval
 						PLpgSQL_var		*new;
 
 						new = malloc(sizeof(PLpgSQL_var));
+						memset(new, 0, sizeof(PLpgSQL_var));
 
 						new->dtype		= PLPGSQL_DTYPE_VAR;
 						new->refname	= $1.name;
@@ -347,8 +355,152 @@ decl_statement	: decl_varname decl_const decl_datatype decl_notnull decl_defval
 					{
 						plpgsql_ns_rename($2, $4);
 					}
+				| decl_varname K_CURSOR decl_cursor_args K_IS K_SELECT decl_cursor_query
+					{
+						PLpgSQL_var *new;
+						PLpgSQL_expr *curname_def;
+						char		buf[1024];
+						char		*cp1;
+						char		*cp2;
+
+						plpgsql_ns_pop();
+
+						new = malloc(sizeof(PLpgSQL_var));
+						memset(new, 0, sizeof(PLpgSQL_var));
+
+						curname_def = malloc(sizeof(PLpgSQL_var));
+						memset(curname_def, 0, sizeof(PLpgSQL_var));
+
+						new->dtype		= PLPGSQL_DTYPE_VAR;
+						new->refname	= $1.name;
+						new->lineno		= $1.lineno;
+
+						curname_def->dtype = PLPGSQL_DTYPE_EXPR;
+						strcpy(buf, "SELECT '");
+						cp1 = new->refname;
+						cp2 = buf + strlen(buf);
+						while (*cp1 != '\0')
+						{
+							if (*cp1 == '\\' || *cp1 == '\'')
+								*cp2++ = '\\';
+							*cp2++ = *cp1++;
+						}
+						strcat(buf, "'");
+						curname_def->query = strdup(buf);
+						new->default_val = curname_def;
+
+						plpgsql_parse_word("refcursor");
+						new->datatype	= yylval.dtype;
+
+						new->cursor_explicit_expr = $6;
+						if ($3 == NULL)
+							new->cursor_explicit_argrow = -1;
+						else
+							new->cursor_explicit_argrow = $3->rowno;
+
+						plpgsql_adddatum((PLpgSQL_datum *)new);
+						plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, new->varno,
+												$1.name);
+					}
+				;
+
+decl_cursor_query :
+					{
+						PLpgSQL_expr *query;
+
+						plpgsql_ns_setlocal(false);
+						query = plpgsql_read_expression(';', ";");
+						plpgsql_ns_setlocal(true);
+						
+						$$ = query;
+					}
 				;
 
+decl_cursor_args :
+					{
+						$$ = NULL;
+					}
+				| decl_cursor_openparen decl_cursor_arglist ')'
+					{
+						char **ftmp;
+						int *vtmp;
+
+						ftmp = malloc($2->nfields * sizeof(char *));
+						vtmp = malloc($2->nfields * sizeof(int));
+						memcpy(ftmp, $2->fieldnames, $2->nfields * sizeof(char *));
+						memcpy(vtmp, $2->varnos, $2->nfields * sizeof(int));
+
+						pfree((char *)($2->fieldnames));
+						pfree((char *)($2->varnos));
+
+						$2->fieldnames = ftmp;
+						$2->varnos = vtmp;
+
+						plpgsql_adddatum((PLpgSQL_datum *)$2);
+
+						$$ = $2;
+					}
+				;
+
+decl_cursor_arglist : decl_cursor_arg
+					{
+						PLpgSQL_row *new;
+
+						new = malloc(sizeof(PLpgSQL_row));
+						memset(new, 0, sizeof(PLpgSQL_row));
+
+						new->dtype = PLPGSQL_DTYPE_ROW;
+						new->refname = strdup("*internal*");
+						new->lineno = yylineno;
+						new->rowtypeclass = InvalidOid;
+						new->fieldnames = palloc(1024 * sizeof(char *));
+						new->varnos = palloc(1024 * sizeof(int));
+						new->nfields = 1;
+
+						new->fieldnames[0] = $1->refname;
+						new->varnos[0] = $1->varno;
+
+						$$ = new;
+					}
+				| decl_cursor_arglist ',' decl_cursor_arg
+					{
+						int i = $1->nfields++;
+
+						$1->fieldnames[i] = $3->refname;
+						$1->varnos[i] = $3->varno;
+					}
+				;
+
+decl_cursor_arg : decl_varname decl_datatype
+					{
+						PLpgSQL_var *new;
+
+						new = malloc(sizeof(PLpgSQL_var));
+						memset(new, 0, sizeof(PLpgSQL_var));
+
+						new->dtype		= PLPGSQL_DTYPE_VAR;
+						new->refname	= $1.name;
+						new->lineno		= $1.lineno;
+
+						new->datatype	= $2;
+						new->isconst	= false;
+						new->notnull	= false;
+
+						plpgsql_adddatum((PLpgSQL_datum *)new);
+						plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, new->varno,
+												$1.name);
+						
+						$$ = new;
+					}
+				;
+
+decl_cursor_openparen : '('
+					{
+						plpgsql_ns_push(NULL);
+					}
+				;
+				
+
 decl_aliasitem	: T_WORD
 					{
 						PLpgSQL_nsitem *nsi;
@@ -581,6 +733,12 @@ proc_stmt		: pl_block
 						{ $$ = $1; }
 				| stmt_getdiag
 						{ $$ = $1; }
+				| stmt_open
+						{ $$ = $1; }
+				| stmt_fetch
+						{ $$ = $1; }
+				| stmt_close
+						{ $$ = $1; }
 				;
 
 stmt_perform	: K_PERFORM lno expr_until_semi
@@ -836,6 +994,7 @@ fori_var		: fori_varname
 						PLpgSQL_var		*new;
 
 						new = malloc(sizeof(PLpgSQL_var));
+						memset(new, 0, sizeof(PLpgSQL_var));
 
 						new->dtype		= PLPGSQL_DTYPE_VAR;
 						new->refname	= $1.name;
@@ -1189,15 +1348,137 @@ stmt_execsql	: execsql_start lno
 
 stmt_dynexecute : K_EXECUTE lno expr_until_semi
 						{
-								PLpgSQL_stmt_dynexecute *new;
+							PLpgSQL_stmt_dynexecute *new;
 
-						new = malloc(sizeof(PLpgSQL_stmt_dynexecute));
-						new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
-						new->lineno   = $2;
-						new->query	  = $3;
+							new = malloc(sizeof(PLpgSQL_stmt_dynexecute));
+							new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
+							new->lineno   = $2;
+							new->query	  = $3;
+
+							$$ = (PLpgSQL_stmt *)new;
+						}
+				;
+
+stmt_open		: K_OPEN lno cursor_varptr
+					{
+						PLpgSQL_stmt_open *new;
+						int				  tok;
+
+						new = malloc(sizeof(PLpgSQL_stmt_open));
+						memset(new, 0, sizeof(PLpgSQL_stmt_open));
+
+						new->cmd_type = PLPGSQL_STMT_OPEN;
+						new->lineno = $2;
+						new->curvar = $3->varno;
+
+						if ($3->cursor_explicit_expr == NULL)
+						{
+						    tok = yylex();
+
+							if (tok != K_FOR)
+							{
+								plpgsql_comperrinfo();
+								elog(ERROR, "syntax error at \"%s\" - expected FOR to open a reference cursor", yytext);
+							}
+
+							tok = yylex();
+							switch (tok)
+							{
+								case K_SELECT:
+									new->query = plpgsql_read_expression(';', ";");
+									break;
+
+								case K_EXECUTE:
+									new->dynquery = plpgsql_read_expression(';', ";");
+									break;
+
+								default:
+									plpgsql_comperrinfo();
+									elog(ERROR, "syntax error at \"%s\"", yytext);
+							}
+
+						}
+						else
+						{
+							if ($3->cursor_explicit_argrow >= 0)
+							{
+								tok = yylex();
+
+								if (tok != '(')
+								{
+									plpgsql_comperrinfo();
+									elog(ERROR, "cursor %s has arguments", $3->refname);
+								}
+
+								new->argquery = read_sqlstmt(';', ";", "SELECT (");
+							}
+							else
+							{
+								tok = yylex();
+
+								if (tok == '(')
+								{
+									plpgsql_comperrinfo();
+									elog(ERROR, "cursor %s has no arguments", $3->refname);
+								}
+								
+								if (tok != ';')
+								{
+									plpgsql_comperrinfo();
+									elog(ERROR, "syntax error at \"%s\"", yytext);
+								}
+							}
+						}
+
+						$$ = (PLpgSQL_stmt *)new;
+					}
+				;
+
+stmt_fetch		: K_FETCH lno cursor_variable K_INTO
+					{
+						PLpgSQL_stmt_fetch *new;
+
+						new = (PLpgSQL_stmt_fetch *)make_fetch_stmt();
+						new->curvar = $3;
 
 						$$ = (PLpgSQL_stmt *)new;
+						$$->lineno = $2;
+					}
+				;
+
+stmt_close		: K_CLOSE lno cursor_variable ';'
+					{
+						PLpgSQL_stmt_close *new;
+
+						new = malloc(sizeof(PLpgSQL_stmt_close));
+						new->cmd_type = PLPGSQL_STMT_CLOSE;
+						new->lineno = $2;
+						new->curvar = $3;
+
+						$$ = (PLpgSQL_stmt *)new;
+					}
+				;
+
+cursor_varptr	: T_VARIABLE
+					{
+						if (yylval.var->datatype->typoid != REFCURSOROID)
+						{
+							plpgsql_comperrinfo();
+							elog(ERROR, "%s must be of type cursor or refcursor", yylval.var->refname);
 						}
+						$$ = yylval.var;
+					}
+				;
+
+cursor_variable	: T_VARIABLE
+					{
+						if (yylval.var->datatype->typoid != REFCURSOROID)
+						{
+							plpgsql_comperrinfo();
+							elog(ERROR, "%s must be of type refcursor", yylval.var->refname);
+						}
+						$$ = yylval.var->varno;
+					}
 				;
 
 execsql_start	: T_WORD
@@ -1615,6 +1896,113 @@ make_select_stmt()
 }
 
 
+static PLpgSQL_stmt *
+make_fetch_stmt()
+{
+	int					tok;
+	PLpgSQL_row		   *row = NULL;
+	PLpgSQL_rec		   *rec = NULL;
+	PLpgSQL_stmt_fetch *fetch;
+	int					have_nexttok = 0;
+
+	tok = yylex();
+	switch (tok)
+	{
+		case T_ROW:
+			row = yylval.row;
+			break;
+
+		case T_RECORD:
+			rec = yylval.rec;
+			break;
+
+		case T_VARIABLE:
+		case T_RECFIELD:
+			{
+				PLpgSQL_var		*var;
+				PLpgSQL_recfield *recfield;
+				int				nfields = 1;
+				char			*fieldnames[1024];
+				int				varnos[1024];
+
+				switch (tok)
+				{	
+					case T_VARIABLE:
+						var = yylval.var;
+						fieldnames[0] = strdup(yytext);
+						varnos[0]	  = var->varno;
+						break;
+
+					case T_RECFIELD:
+						recfield = yylval.recfield;
+						fieldnames[0] = strdup(yytext);
+						varnos[0]	  = recfield->rfno;
+						break;
+				}
+
+				while ((tok = yylex()) == ',')
+				{
+					tok = yylex();
+					switch(tok)
+					{
+						case T_VARIABLE:
+							var = yylval.var;
+							fieldnames[nfields] = strdup(yytext);
+							varnos[nfields++]	= var->varno;
+							break;
+
+						case T_RECFIELD:
+							recfield = yylval.recfield;
+							fieldnames[0] = strdup(yytext);
+							varnos[0]	  = recfield->rfno;
+							break;
+
+						default:
+							elog(ERROR, "plpgsql: %s is not a variable or record field", yytext);
+					}
+				}
+				row = malloc(sizeof(PLpgSQL_row));
+				row->dtype = PLPGSQL_DTYPE_ROW;
+				row->refname = strdup("*internal*");
+				row->lineno = yylineno;
+				row->rowtypeclass = InvalidOid;
+				row->nfields = nfields;
+				row->fieldnames = malloc(sizeof(char *) * nfields);
+				row->varnos = malloc(sizeof(int) * nfields);
+				while (--nfields >= 0)
+				{
+					row->fieldnames[nfields] = fieldnames[nfields];
+					row->varnos[nfields] = varnos[nfields];
+				}
+
+				plpgsql_adddatum((PLpgSQL_datum *)row);
+
+				have_nexttok = 1;
+			}
+			break;
+
+		default:
+			{
+				elog(ERROR, "syntax error at '%s'", yytext);
+			}
+	}
+
+	if (!have_nexttok)
+		tok = yylex();
+
+	if (tok != ';')
+		elog(ERROR, "syntax error at '%s'", yytext);
+
+	fetch = malloc(sizeof(PLpgSQL_stmt_select));
+	memset(fetch, 0, sizeof(PLpgSQL_stmt_fetch));
+	fetch->cmd_type = PLPGSQL_STMT_FETCH;
+	fetch->rec		 = rec;
+	fetch->row		 = row;
+
+	return (PLpgSQL_stmt *)fetch;
+}
+
+
 static PLpgSQL_expr *
 make_tupret_expr(PLpgSQL_row *row)
 {
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 9cfa748241330c4016d3deadc502c29e5f47e0bd..5d93985028672e25de91171e6558fe8f54fe5cc7 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -3,7 +3,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.30 2001/04/18 20:42:56 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.31 2001/05/21 14:22:18 wieck Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -282,6 +282,7 @@ plpgsql_compile(Oid fn_oid, int functype)
 					perm_fmgr_info(typeStruct->typinput, &(var->datatype->typinput));
 					var->datatype->typelem = typeStruct->typelem;
 					var->datatype->typbyval = typeStruct->typbyval;
+					var->datatype->typlen = typeStruct->typlen;
 					var->datatype->atttypmod = -1;
 					var->isconst = true;
 					var->notnull = false;
@@ -313,6 +314,9 @@ plpgsql_compile(Oid fn_oid, int functype)
 			memset(rec, 0, sizeof(PLpgSQL_rec));
 			rec->dtype = PLPGSQL_DTYPE_REC;
 			rec->refname = strdup("new");
+			rec->tup = NULL;
+			rec->tupdesc = NULL;
+			rec->freetup = false;
 			plpgsql_adddatum((PLpgSQL_datum *) rec);
 			plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->recno, rec->refname);
 			function->new_varno = rec->recno;
@@ -324,6 +328,9 @@ plpgsql_compile(Oid fn_oid, int functype)
 			memset(rec, 0, sizeof(PLpgSQL_rec));
 			rec->dtype = PLPGSQL_DTYPE_REC;
 			rec->refname = strdup("old");
+			rec->tup = NULL;
+			rec->tupdesc = NULL;
+			rec->freetup = false;
 			plpgsql_adddatum((PLpgSQL_datum *) rec);
 			plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->recno, rec->refname);
 			function->old_varno = rec->recno;
@@ -632,6 +639,7 @@ plpgsql_parse_word(char *word)
 		perm_fmgr_info(typeStruct->typinput, &(typ->typinput));
 		typ->typelem = typeStruct->typelem;
 		typ->typbyval = typeStruct->typbyval;
+		typ->typlen = typeStruct->typlen;
 		typ->atttypmod = -1;
 
 		plpgsql_yylval.dtype = typ;
@@ -948,6 +956,7 @@ plpgsql_parse_wordtype(char *word)
 		perm_fmgr_info(typeStruct->typinput, &(typ->typinput));
 		typ->typelem = typeStruct->typelem;
 		typ->typbyval = typeStruct->typbyval;
+		typ->typlen = typeStruct->typlen;
 		typ->atttypmod = -1;
 
 		plpgsql_yylval.dtype = typ;
@@ -1091,6 +1100,7 @@ plpgsql_parse_dblwordtype(char *string)
 	perm_fmgr_info(typeStruct->typinput, &(typ->typinput));
 	typ->typelem = typeStruct->typelem;
 	typ->typbyval = typeStruct->typbyval;
+	typ->typlen = typeStruct->typlen;
 	typ->atttypmod = attrStruct->atttypmod;
 
 	plpgsql_yylval.dtype = typ;
@@ -1230,13 +1240,14 @@ plpgsql_parse_wordrowtype(char *string)
 		perm_fmgr_info(typeStruct->typinput, &(var->datatype->typinput));
 		var->datatype->typelem = typeStruct->typelem;
 		var->datatype->typbyval = typeStruct->typbyval;
+		var->datatype->typlen = typeStruct->typlen;
 		var->datatype->atttypmod = attrStruct->atttypmod;
 		var->isconst = false;
 		var->notnull = false;
 		var->default_val = NULL;
 		var->value = (Datum) 0;
 		var->isnull = true;
-		var->shouldfree = false;
+		var->freeval = false;
 
 		ReleaseSysCache(typetup);
 		ReleaseSysCache(attrtup);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index ba3b1b495fb3bed24a5f437946ae1a66ea772bf0..1ad04739a33faf46362ef414269c374b0985c356 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3,7 +3,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.42 2001/05/08 01:00:53 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.43 2001/05/21 14:22:19 wieck Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -95,6 +95,12 @@ static int exec_stmt_fors(PLpgSQL_execstate * estate,
 			   PLpgSQL_stmt_fors * stmt);
 static int exec_stmt_select(PLpgSQL_execstate * estate,
 				 PLpgSQL_stmt_select * stmt);
+static int exec_stmt_open(PLpgSQL_execstate * estate,
+				 PLpgSQL_stmt_open * stmt);
+static int exec_stmt_fetch(PLpgSQL_execstate * estate,
+				 PLpgSQL_stmt_fetch * stmt);
+static int exec_stmt_close(PLpgSQL_execstate * estate,
+				 PLpgSQL_stmt_close * stmt);
 static int exec_stmt_exit(PLpgSQL_execstate * estate,
 			   PLpgSQL_stmt_exit * stmt);
 static int exec_stmt_return(PLpgSQL_execstate * estate,
@@ -128,7 +134,7 @@ static Datum exec_eval_expr(PLpgSQL_execstate * estate,
 			   bool *isNull,
 			   Oid *rettype);
 static int exec_run_select(PLpgSQL_execstate * estate,
-				PLpgSQL_expr * expr, int maxtuples);
+				PLpgSQL_expr * expr, int maxtuples, Portal *portalP);
 static void exec_move_row(PLpgSQL_execstate * estate,
 			  PLpgSQL_rec * rec,
 			  PLpgSQL_row * row,
@@ -232,6 +238,12 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
 					case PLPGSQL_STMT_DYNFORS:
 						stmttype = "for over execute statement";
 						break;
+					case PLPGSQL_STMT_FETCH:
+						stmttype = "fetch";
+						break;
+					case PLPGSQL_STMT_CLOSE:
+						stmttype = "close";
+						break;
 					default:
 						stmttype = "unknown";
 						break;
@@ -314,7 +326,7 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
 
 					var->value = fcinfo->arg[i];
 					var->isnull = fcinfo->argnull[i];
-					var->shouldfree = false;
+					var->freeval = false;
 				}
 				break;
 
@@ -353,7 +365,7 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
 
 					var->value = 0;
 					var->isnull = true;
-					var->shouldfree = false;
+					var->freeval = false;
 				}
 				break;
 
@@ -607,6 +619,7 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
 	rec_old = (PLpgSQL_rec *) (estate.datums[func->old_varno]);
 	var = (PLpgSQL_var *) (estate.datums[func->tg_op_varno]);
 	var->isnull = false;
+	var->freeval = false;
 
 	if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
 	{
@@ -644,11 +657,13 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
 	 */
 	var = (PLpgSQL_var *) (estate.datums[func->tg_name_varno]);
 	var->isnull = false;
+	var->freeval = false;
 	var->value = DirectFunctionCall1(namein,
 						  CStringGetDatum(trigdata->tg_trigger->tgname));
 
 	var = (PLpgSQL_var *) (estate.datums[func->tg_when_varno]);
 	var->isnull = false;
+	var->freeval = false;
 	if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
 		var->value = DirectFunctionCall1(textin, CStringGetDatum("BEFORE"));
 	else if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
@@ -658,6 +673,7 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
 
 	var = (PLpgSQL_var *) (estate.datums[func->tg_level_varno]);
 	var->isnull = false;
+	var->freeval = false;
 	if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
 		var->value = DirectFunctionCall1(textin, CStringGetDatum("ROW"));
 	else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
@@ -667,6 +683,7 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
 
 	var = (PLpgSQL_var *) (estate.datums[func->tg_relid_varno]);
 	var->isnull = false;
+	var->freeval = false;
 	var->value = (Datum) (trigdata->tg_relation->rd_id);
 
 	var = (PLpgSQL_var *) (estate.datums[func->tg_relname_varno]);
@@ -676,6 +693,7 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
 
 	var = (PLpgSQL_var *) (estate.datums[func->tg_nargs_varno]);
 	var->isnull = false;
+	var->freeval = false;
 	var->value = (Datum) (trigdata->tg_trigger->tgnargs);
 
 	/*
@@ -709,7 +727,7 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
 
 					var->value = 0;
 					var->isnull = true;
-					var->shouldfree = false;
+					var->freeval = false;
 				}
 				break;
 
@@ -794,6 +812,7 @@ copy_var(PLpgSQL_var * var)
 	PLpgSQL_var *new = palloc(sizeof(PLpgSQL_var));
 
 	memcpy(new, var, sizeof(PLpgSQL_var));
+	new->freeval = false;
 
 	return new;
 }
@@ -805,6 +824,10 @@ copy_rec(PLpgSQL_rec * rec)
 	PLpgSQL_rec *new = palloc(sizeof(PLpgSQL_rec));
 
 	memcpy(new, rec, sizeof(PLpgSQL_rec));
+	new->tup = NULL;
+	new->tupdesc = NULL;
+	new->freetup = false;
+	new->freetupdesc = false;
 
 	return new;
 }
@@ -834,6 +857,12 @@ exec_stmt_block(PLpgSQL_execstate * estate, PLpgSQL_stmt_block * block)
 				{
 					PLpgSQL_var *var = (PLpgSQL_var *) (estate->datums[n]);
 
+					if (var->freeval)
+					{
+						pfree((void *)(var->value));
+						var->freeval = false;
+					}
+
 					if (!var->isconst || var->isnull)
 					{
 						if (var->default_val == NULL)
@@ -856,6 +885,13 @@ exec_stmt_block(PLpgSQL_execstate * estate, PLpgSQL_stmt_block * block)
 				{
 					PLpgSQL_rec *rec = (PLpgSQL_rec *) (estate->datums[n]);
 
+					if (rec->freetup)
+					{
+						heap_freetuple(rec->tup);
+						FreeTupleDesc(rec->tupdesc);
+						rec->freetup = false;
+					}
+
 					rec->tup = NULL;
 					rec->tupdesc = NULL;
 				}
@@ -1002,6 +1038,18 @@ exec_stmt(PLpgSQL_execstate * estate, PLpgSQL_stmt * stmt)
 			rc = exec_stmt_dynfors(estate, (PLpgSQL_stmt_dynfors *) stmt);
 			break;
 
+		case PLPGSQL_STMT_OPEN:
+			rc = exec_stmt_open(estate, (PLpgSQL_stmt_open *) stmt);
+			break;
+
+		case PLPGSQL_STMT_FETCH:
+			rc = exec_stmt_fetch(estate, (PLpgSQL_stmt_fetch *) stmt);
+			break;
+
+		case PLPGSQL_STMT_CLOSE:
+			rc = exec_stmt_close(estate, (PLpgSQL_stmt_close *) stmt);
+			break;
+
 		default:
 			error_info_stmt = save_estmt;
 			elog(ERROR, "unknown cmdtype %d in exec_stmt",
@@ -1092,6 +1140,7 @@ exec_stmt_if(PLpgSQL_execstate * estate, PLpgSQL_stmt_if * stmt)
 	bool		isnull = false;
 
 	value = exec_eval_expr(estate, stmt->cond, &isnull, &valtype);
+	SPI_freetuptable(SPI_tuptable);
 
 	if (value)
 	{
@@ -1166,6 +1215,7 @@ exec_stmt_while(PLpgSQL_execstate * estate, PLpgSQL_stmt_while * stmt)
 	for (;;)
 	{
 		value = exec_eval_expr(estate, stmt->cond, &isnull, &valtype);
+		SPI_freetuptable(SPI_tuptable);
 		if (!value)
 			break;
 
@@ -1227,6 +1277,7 @@ exec_stmt_fori(PLpgSQL_execstate * estate, PLpgSQL_stmt_fori * stmt)
 		elog(ERROR, "lower bound of FOR loop cannot be NULL");
 	var->value = value;
 	var->isnull = false;
+	SPI_freetuptable(SPI_tuptable);
 
 	/*
 	 * Get the value of the upper bound
@@ -1238,6 +1289,7 @@ exec_stmt_fori(PLpgSQL_execstate * estate, PLpgSQL_stmt_fori * stmt)
 							var->datatype->atttypmod, &isnull);
 	if (isnull)
 		elog(ERROR, "upper bound of FOR loop cannot be NULL");
+	SPI_freetuptable(SPI_tuptable);
 
 	/*
 	 * Now do the loop
@@ -1317,6 +1369,7 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
 	PLpgSQL_rec *rec = NULL;
 	PLpgSQL_row *row = NULL;
 	SPITupleTable *tuptab;
+	Portal		portal;
 	int			rc;
 	int			i;
 	int			n;
@@ -1340,12 +1393,12 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
 	}
 
 	/*
-	 * Run the query
+	 * Open the implicit cursor for the statement and fetch
+	 * the initial 10 rows.
 	 */
-	exec_run_select(estate, stmt->query, 0);
+	exec_run_select(estate, stmt->query, 0, &portal);
+	SPI_cursor_fetch(portal, true, 10);
 	n = SPI_processed;
-	tuptab = SPI_tuptable;
-	SPI_tuptable = NULL;
 
 	/*
 	 * If the query didn't return any row, set the target to NULL and
@@ -1354,6 +1407,7 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
 	if (n == 0)
 	{
 		exec_move_row(estate, rec, row, NULL, NULL);
+		SPI_cursor_close(portal);
 		return PLPGSQL_RC_OK;
 	}
 
@@ -1365,45 +1419,71 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
 	/*
 	 * Now do the loop
 	 */
-	for (i = 0; i < n; i++)
+	for (;;)
 	{
+		tuptab = SPI_tuptable;
+		SPI_tuptable = NULL;
 
-		/*
-		 * Assign the tuple to the target
-		 */
-		exec_move_row(estate, rec, row, tuptab->vals[i], tuptab->tupdesc);
+		for (i = 0; i < n; i++)
+		{
 
-		/*
-		 * Execute the statements
-		 */
-		rc = exec_stmts(estate, stmt->body);
+			/*
+			 * Assign the tuple to the target
+			 */
+			exec_move_row(estate, rec, row, tuptab->vals[i], tuptab->tupdesc);
 
-		/*
-		 * Check returncode
-		 */
-		switch (rc)
-		{
-			case PLPGSQL_RC_OK:
-				break;
+			/*
+			 * Execute the statements
+			 */
+			rc = exec_stmts(estate, stmt->body);
 
-			case PLPGSQL_RC_EXIT:
-				if (estate->exitlabel == NULL)
+			/*
+			 * Check returncode
+			 */
+			switch (rc)
+			{
+				case PLPGSQL_RC_OK:
+					break;
+
+				case PLPGSQL_RC_EXIT:
+					SPI_freetuptable(tuptab);
+					SPI_cursor_close(portal);
+
+					if (estate->exitlabel == NULL)
+						return PLPGSQL_RC_OK;
+					if (stmt->label == NULL)
+						return PLPGSQL_RC_EXIT;
+					if (strcmp(stmt->label, estate->exitlabel))
+						return PLPGSQL_RC_EXIT;
+					estate->exitlabel = NULL;
 					return PLPGSQL_RC_OK;
-				if (stmt->label == NULL)
-					return PLPGSQL_RC_EXIT;
-				if (strcmp(stmt->label, estate->exitlabel))
-					return PLPGSQL_RC_EXIT;
-				estate->exitlabel = NULL;
-				return PLPGSQL_RC_OK;
 
-			case PLPGSQL_RC_RETURN:
-				return PLPGSQL_RC_RETURN;
+				case PLPGSQL_RC_RETURN:
+					SPI_freetuptable(tuptab);
+					SPI_cursor_close(portal);
 
-			default:
-				elog(ERROR, "unknown rc %d from exec_stmts()", rc);
+					return PLPGSQL_RC_RETURN;
+
+				default:
+					elog(ERROR, "unknown rc %d from exec_stmts()", rc);
+			}
 		}
+
+		SPI_freetuptable(tuptab);
+
+		/*
+		 * Fetch the next 50 tuples
+		 */
+		SPI_cursor_fetch(portal, true, 50);
+		if ((n = SPI_processed) == 0)
+			break;
 	}
 
+	/*
+	 * Close the implicit cursor
+	 */
+	SPI_cursor_close(portal);
+
 	return PLPGSQL_RC_OK;
 }
 
@@ -1442,7 +1522,7 @@ exec_stmt_select(PLpgSQL_execstate * estate, PLpgSQL_stmt_select * stmt)
 	/*
 	 * Run the query
 	 */
-	exec_run_select(estate, stmt->query, 1);
+	exec_run_select(estate, stmt->query, 1, NULL);
 	n = SPI_processed;
 	tuptab = SPI_tuptable;
 	SPI_tuptable = NULL;
@@ -1461,8 +1541,8 @@ exec_stmt_select(PLpgSQL_execstate * estate, PLpgSQL_stmt_select * stmt)
 	 * Put the result into the target and set found to true
 	 */
 	exec_move_row(estate, rec, row, tuptab->vals[0], tuptab->tupdesc);
-
 	exec_set_found(estate, true);
+	SPI_freetuptable(tuptab);
 
 	return PLPGSQL_RC_OK;
 }
@@ -1485,6 +1565,7 @@ exec_stmt_exit(PLpgSQL_execstate * estate, PLpgSQL_stmt_exit * stmt)
 	if (stmt->cond != NULL)
 	{
 		value = exec_eval_expr(estate, stmt->cond, &isnull, &valtype);
+		SPI_freetuptable(SPI_tuptable);
 		if (!value)
 			return PLPGSQL_RC_OK;
 	}
@@ -1523,7 +1604,7 @@ exec_stmt_return(PLpgSQL_execstate * estate, PLpgSQL_stmt_return * stmt)
 		}
 		else
 		{
-			exec_run_select(estate, stmt->expr, 1);
+			exec_run_select(estate, stmt->expr, 1, NULL);
 			estate->retval = (Datum) SPI_copytuple(SPI_tuptable->vals[0]);
 			estate->rettupdesc = SPI_tuptable->tupdesc;
 			estate->retisnull = false;
@@ -1643,6 +1724,7 @@ exec_stmt_raise(PLpgSQL_execstate * estate, PLpgSQL_stmt_raise * stmt)
 							(estate->datums[stmt->params[pidx]]);
 						value = (int) exec_eval_expr(estate, trigarg->argnum,
 												   &valisnull, &valtype);
+						SPI_freetuptable(SPI_tuptable);
 						if (valisnull)
 							extval = "<INDEX_IS_NULL>";
 						else
@@ -1835,6 +1917,7 @@ exec_stmt_execsql(PLpgSQL_execstate * estate,
 				trigarg = (PLpgSQL_trigarg *) (estate->datums[expr->params[i]]);
 				tgargno = (int) exec_eval_expr(estate, trigarg->argnum,
 											   &isnull, &tgargoid);
+				SPI_freetuptable(SPI_tuptable);
 				if (isnull || tgargno < 0 || tgargno >= estate->trig_nargs)
 				{
 					values[i] = 0;
@@ -1922,8 +2005,7 @@ exec_stmt_dynexecute(PLpgSQL_execstate * estate,
 								   ObjectIdGetDatum(typeStruct->typelem),
 											 Int32GetDatum(-1)));
 
-	if (!typeStruct->typbyval)
-		pfree((void *) query);
+	SPI_freetuptable(SPI_tuptable);
 
 	ReleaseSysCache(typetup);
 
@@ -1995,6 +2077,8 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
 	HeapTuple	typetup;
 	Form_pg_type typeStruct;
 	FmgrInfo	finfo_output;
+	void		*plan;
+	Portal		portal;
 
 	/*
 	 * Initialize the global found variable to false
@@ -2038,21 +2122,28 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
 								   ObjectIdGetDatum(typeStruct->typelem),
 											 Int32GetDatum(-1)));
 
-	if (!typeStruct->typbyval)
-		pfree((void *) query);
+	SPI_freetuptable(SPI_tuptable);
 
 	ReleaseSysCache(typetup);
 
 	/*
-	 * Run the query
+	 * Prepare a plan and open an implicit cursor for the query
 	 */
-	if (SPI_exec(querystr, 0) != SPI_OK_SELECT)
-		elog(ERROR, "FOR ... EXECUTE query '%s' was not SELECT", querystr);
+	plan = SPI_prepare(querystr, 0, NULL);
+	if (plan == NULL)
+		elog(ERROR, "SPI_prepare() failed for dynamic query \"%s\"", querystr);
+	portal = SPI_cursor_open(NULL, plan, NULL, NULL);
+	if (portal == NULL)
+		elog(ERROR, "failed to open implicit cursor for dynamic query \"%s\"",
+					querystr);
 	pfree(querystr);
+	SPI_freeplan(plan);
 
+	/*
+	 * Fetch the initial 10 tuples
+	 */
+	SPI_cursor_fetch(portal, true, 10);
 	n = SPI_processed;
-	tuptab = SPI_tuptable;
-	SPI_tuptable = NULL;
 
 	/*
 	 * If the query didn't return any row, set the target to NULL and
@@ -2061,6 +2152,7 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
 	if (n == 0)
 	{
 		exec_move_row(estate, rec, row, NULL, NULL);
+		SPI_cursor_close(portal);
 		return PLPGSQL_RC_OK;
 	}
 
@@ -2072,44 +2164,431 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
 	/*
 	 * Now do the loop
 	 */
-	for (i = 0; i < n; i++)
+	for (;;)
 	{
+		tuptab = SPI_tuptable;
+		SPI_tuptable = NULL;
+
+		for (i = 0; i < n; i++)
+		{
+
+			/*
+			 * Assign the tuple to the target
+			 */
+			exec_move_row(estate, rec, row, tuptab->vals[i], tuptab->tupdesc);
+
+			/*
+			 * Execute the statements
+			 */
+			rc = exec_stmts(estate, stmt->body);
+
+			/*
+			 * Check returncode
+			 */
+			switch (rc)
+			{
+				case PLPGSQL_RC_OK:
+					break;
+
+				case PLPGSQL_RC_EXIT:
+					SPI_freetuptable(tuptab);
+					SPI_cursor_close(portal);
+
+					if (estate->exitlabel == NULL)
+						return PLPGSQL_RC_OK;
+					if (stmt->label == NULL)
+						return PLPGSQL_RC_EXIT;
+					if (strcmp(stmt->label, estate->exitlabel))
+						return PLPGSQL_RC_EXIT;
+					estate->exitlabel = NULL;
+					return PLPGSQL_RC_OK;
+
+				case PLPGSQL_RC_RETURN:
+					SPI_freetuptable(tuptab);
+					SPI_cursor_close(portal);
+
+					return PLPGSQL_RC_RETURN;
+
+				default:
+					elog(ERROR, "unknown rc %d from exec_stmts()", rc);
+			}
+		}
+
+		SPI_freetuptable(tuptab);
 
 		/*
-		 * Assign the tuple to the target
+		 * Fetch the next 50 tuples
 		 */
-		exec_move_row(estate, rec, row, tuptab->vals[i], tuptab->tupdesc);
+		SPI_cursor_fetch(portal, true, 50);
+		if ((n = SPI_processed) == 0)
+			break;
+	}
 
-		/*
-		 * Execute the statements
+	/*
+	 * Close the cursor
+	 */
+	SPI_cursor_close(portal);
+
+	return PLPGSQL_RC_OK;
+}
+
+
+/* ----------
+ * exec_stmt_open			Execute an OPEN cursor statement
+ * ----------
+ */
+static int
+exec_stmt_open(PLpgSQL_execstate * estate, PLpgSQL_stmt_open * stmt)
+{
+	PLpgSQL_var *curvar = NULL;
+	char		*curname = NULL;
+	PLpgSQL_expr *query = NULL;
+	Portal		portal;
+
+	PLpgSQL_var *var;
+	PLpgSQL_rec *rec;
+	PLpgSQL_recfield *recfield;
+	PLpgSQL_trigarg *trigarg;
+	int			tgargno;
+	Oid			tgargoid;
+	int			i;
+	Datum	   *values;
+	char	   *nulls;
+	int			fno;
+	bool		isnull;
+
+
+	/* ----------
+	 * Get the cursor variable and if it has an assigned name, check
+	 * that it's not in use currently.
+	 * ----------
+	 */
+	curvar = (PLpgSQL_var *) (estate->datums[stmt->curvar]);
+	if (!curvar->isnull)
+	{
+		curname = DatumGetCString(DirectFunctionCall1(textout, curvar->value));
+		if (SPI_cursor_find(curname) != NULL)
+			elog(ERROR, "cursor \"%s\" already in use", curname);
+	}
+
+	/* ----------
+	 * Process the OPEN according to it's type.
+	 * ----------
+	 */
+	if (stmt->query != NULL)
+	{
+		/* ----------
+		 * This is an OPEN refcursor FOR SELECT ...
+		 *
+		 * We just make sure the query is planned. The real work is
+		 * done downstairs.
+		 * ----------
 		 */
-		rc = exec_stmts(estate, stmt->body);
+		query = stmt->query; 
+		if (query->plan == NULL)
+			exec_prepare_plan(estate, query);
+	}
+	else if (stmt->dynquery != NULL)
+	{
+		/* ----------
+		 * This is an OPEN refcursor FOR EXECUTE ...
+		 * ----------
+		 */
+		Datum		queryD;
+		Oid			restype;
+		char	   *querystr;
+		HeapTuple	typetup;
+		Form_pg_type typeStruct;
+		FmgrInfo	finfo_output;
+		void		*curplan = NULL;
+
+		/* ----------
+		 * We evaluate the string expression after the
+		 * EXECUTE keyword. It's result is the querystring we have
+		 * to execute.
+		 * ----------
+		 */
+		queryD = exec_eval_expr(estate, stmt->dynquery, &isnull, &restype);
+		if (isnull)
+			elog(ERROR, "cannot EXECUTE NULL query");
 
-		/*
-		 * Check returncode
+		/* ----------
+		 * Get the C-String representation.
+		 * ----------
 		 */
-		switch (rc)
+		typetup = SearchSysCache(TYPEOID,
+								 ObjectIdGetDatum(restype),
+								 0, 0, 0);
+		if (!HeapTupleIsValid(typetup))
+			elog(ERROR, "cache lookup for type %u failed (1)", restype);
+		typeStruct = (Form_pg_type) GETSTRUCT(typetup);
+
+		fmgr_info(typeStruct->typoutput, &finfo_output);
+		querystr = DatumGetCString(FunctionCall3(&finfo_output,
+												 queryD,
+												 ObjectIdGetDatum(typeStruct->typelem),
+												 Int32GetDatum(-1)));
+
+		SPI_freetuptable(SPI_tuptable);
+		ReleaseSysCache(typetup);
+
+		/* ----------
+		 * Now we prepare a query plan for it and open a cursor
+		 * ----------
+		 */
+		curplan = SPI_prepare(querystr, 0, NULL);
+		portal = SPI_cursor_open(curname, curplan, NULL, NULL);
+		if (portal == NULL)
+			elog(ERROR, "Failed to open cursor");
+		pfree(querystr);
+
+		/* ----------
+		 * Store the eventually assigned cursor name in the cursor variable
+		 * ----------
+		 */
+		if (curvar->freeval)
+			pfree((void *)(curvar->value));
+
+		curvar->value = DirectFunctionCall1(textin, CStringGetDatum(portal->name));
+		curvar->isnull = false;
+		curvar->freeval = true;
+
+		return PLPGSQL_RC_OK;
+	}
+	else
+	{
+		/* ----------
+		 * This is an OPEN cursor
+		 * ----------
+		 */
+		if (stmt->argquery != NULL)
 		{
-			case PLPGSQL_RC_OK:
+			/* ----------
+			 * Er - OPEN CURSOR (args). We fake a SELECT ... INTO ...
+			 * statement to evaluate the args and put 'em into the
+			 * internal row.
+			 * ----------
+			 */
+			PLpgSQL_stmt_select	set_args;
+
+			memset(&set_args, 0, sizeof(set_args));
+			set_args.cmd_type	= PLPGSQL_STMT_SELECT;
+			set_args.lineno		= stmt->lineno;
+			set_args.row		= (PLpgSQL_row *) 
+								  (estate->datums[curvar->cursor_explicit_argrow]);
+			set_args.query		= stmt->argquery;
+
+			if (exec_stmt_select(estate, &set_args) != PLPGSQL_RC_OK)
+				elog(ERROR, "open cursor failed during argument processing");
+		}
+
+		query = curvar->cursor_explicit_expr; 
+		if (query->plan == NULL)
+			exec_prepare_plan(estate, query);
+	}
+
+	/* ----------
+	 * Here we go if we have a saved plan where we have to put
+	 * values into, either from an explicit cursor or from a
+	 * refcursor opened with OPEN ... FOR SELECT ...;
+	 * ----------
+	 */
+	values = palloc(sizeof(Datum) * (query->nparams + 1));
+	nulls = palloc(query->nparams + 1);
+
+	for (i = 0; i < query->nparams; i++)
+	{
+		switch (estate->datums[query->params[i]]->dtype)
+		{
+			case PLPGSQL_DTYPE_VAR:
+				var = (PLpgSQL_var *) (estate->datums[query->params[i]]);
+				values[i] = var->value;
+				if (var->isnull)
+					nulls[i] = 'n';
+				else
+					nulls[i] = ' ';
 				break;
 
-			case PLPGSQL_RC_EXIT:
-				if (estate->exitlabel == NULL)
-					return PLPGSQL_RC_OK;
-				if (stmt->label == NULL)
-					return PLPGSQL_RC_EXIT;
-				if (strcmp(stmt->label, estate->exitlabel))
-					return PLPGSQL_RC_EXIT;
-				estate->exitlabel = NULL;
-				return PLPGSQL_RC_OK;
+			case PLPGSQL_DTYPE_RECFIELD:
+				recfield = (PLpgSQL_recfield *) (estate->datums[query->params[i]]);
+				rec = (PLpgSQL_rec *) (estate->datums[recfield->recno]);
 
-			case PLPGSQL_RC_RETURN:
-				return PLPGSQL_RC_RETURN;
+				if (!HeapTupleIsValid(rec->tup))
+					elog(ERROR, "record %s is unassigned yet", rec->refname);
+				fno = SPI_fnumber(rec->tupdesc, recfield->fieldname);
+				if (fno == SPI_ERROR_NOATTRIBUTE)
+					elog(ERROR, "record %s has no field %s", rec->refname, recfield->fieldname);
+
+				if (query->plan_argtypes[i] != SPI_gettypeid(rec->tupdesc, fno))
+					elog(ERROR, "type of %s.%s doesn't match that when preparing the plan", rec->refname, recfield->fieldname);
+
+				values[i] = SPI_getbinval(rec->tup, rec->tupdesc, fno, &isnull);
+				if (isnull)
+					nulls[i] = 'n';
+				else
+					nulls[i] = ' ';
+				break;
+
+			case PLPGSQL_DTYPE_TRIGARG:
+				trigarg = (PLpgSQL_trigarg *) (estate->datums[query->params[i]]);
+				tgargno = (int) exec_eval_expr(estate, trigarg->argnum,
+											   &isnull, &tgargoid);
+				SPI_freetuptable(SPI_tuptable);
+				if (isnull || tgargno < 0 || tgargno >= estate->trig_nargs)
+				{
+					values[i] = 0;
+					nulls[i] = 'n';
+				}
+				else
+				{
+					values[i] = estate->trig_argv[tgargno];
+					nulls[i] = ' ';
+				}
+				break;
 
 			default:
-				elog(ERROR, "unknown rc %d from exec_stmts()", rc);
+				elog(ERROR, "unknown parameter dtype %d in exec_stmt_open()",
+					 estate->datums[query->params[i]]->dtype);
 		}
 	}
+	nulls[i] = '\0';
+
+	/* ----------
+	 * Open the cursor
+	 * ----------
+	 */
+	portal = SPI_cursor_open(curname, query->plan, values, nulls);
+	if (portal == NULL)
+		elog(ERROR, "Failed to open cursor");
+
+	pfree(values);
+	pfree(nulls);
+
+	/* ----------
+	 * Store the eventually assigned portal name in the cursor variable
+	 * ----------
+	 */
+	if (curvar->freeval)
+		pfree((void *)(curvar->value));
+
+	curvar->value = DirectFunctionCall1(textin, CStringGetDatum(portal->name));
+	curvar->isnull = false;
+	curvar->freeval = true;
+
+	return PLPGSQL_RC_OK;
+}
+
+
+/* ----------
+ * exec_stmt_fetch			Fetch from a cursor into a target
+ * ----------
+ */
+static int
+exec_stmt_fetch(PLpgSQL_execstate * estate, PLpgSQL_stmt_fetch * stmt)
+{
+	PLpgSQL_var *curvar = NULL;
+	PLpgSQL_rec *rec = NULL;
+	PLpgSQL_row *row = NULL;
+	Portal		portal;
+	char		*curname;
+	int			n;
+
+	/* ----------
+	 * Get the portal of the cursor by name
+	 * ----------
+	 */
+	curvar = (PLpgSQL_var *) (estate->datums[stmt->curvar]);
+	if (curvar->isnull)
+		elog(ERROR, "cursor variable \"%s\" is NULL", curvar->refname);
+	curname = DatumGetCString(DirectFunctionCall1(textout, curvar->value));
+
+	portal = SPI_cursor_find(curname);
+	if (portal == NULL)
+		elog(ERROR, "cursor \"%s\" is invalid", curname);
+	pfree(curname);
+
+	/* ----------
+	 * Initialize the global found variable to false
+	 * ----------
+	 */
+	exec_set_found(estate, false);
+
+	/* ----------
+	 * Determine if we fetch into a record or a row
+	 * ----------
+	 */
+	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_select()");
+	}
+
+	/* ----------
+	 * Fetch 1 tuple from the cursor
+	 * ----------
+	 */
+	SPI_cursor_fetch(portal, true, 1);
+	n = SPI_processed;
+
+	/* ----------
+	 * If the FETCH didn't return a row, set the target
+	 * to NULL and return with FOUND = false.
+	 * ----------
+	 */
+	if (n == 0)
+	{
+		exec_move_row(estate, rec, row, NULL, NULL);
+		return PLPGSQL_RC_OK;
+	}
+
+	/* ----------
+	 * Put the result into the target and set found to true
+	 * ----------
+	 */
+	exec_move_row(estate, rec, row, SPI_tuptable->vals[0], 
+				SPI_tuptable->tupdesc);
+	exec_set_found(estate, true);
+
+	SPI_freetuptable(SPI_tuptable);
+
+	return PLPGSQL_RC_OK;
+}
+
+
+/* ----------
+ * exec_stmt_close			Close a cursor
+ * ----------
+ */
+static int
+exec_stmt_close(PLpgSQL_execstate * estate, PLpgSQL_stmt_close * stmt)
+{
+	PLpgSQL_var *curvar = NULL;
+	Portal		portal;
+	char		*curname;
+
+	/* ----------
+	 * Get the portal of the cursor by name
+	 * ----------
+	 */
+	curvar = (PLpgSQL_var *) (estate->datums[stmt->curvar]);
+	if (curvar->isnull)
+		elog(ERROR, "cursor variable \"%s\" is NULL", curvar->refname);
+	curname = DatumGetCString(DirectFunctionCall1(textout, curvar->value));
+
+	portal = SPI_cursor_find(curname);
+	if (portal == NULL)
+		elog(ERROR, "cursor \"%s\" is invalid", curname);
+	pfree(curname);
+
+	/* ----------
+	 * And close it.
+	 * ----------
+	 */
+	SPI_cursor_close(portal);
 
 	return PLPGSQL_RC_OK;
 }
@@ -2131,6 +2610,8 @@ exec_assign_expr(PLpgSQL_execstate * estate, PLpgSQL_datum * target,
 	value = exec_eval_expr(estate, expr, &isnull, &valtype);
 	if (target != NULL)
 		exec_assign_value(estate, target, value, valtype, &isnull);
+
+	SPI_freetuptable(SPI_tuptable);
 }
 
 
@@ -2156,6 +2637,7 @@ exec_assign_value(PLpgSQL_execstate * estate,
 	Oid			atttype;
 	int32		atttypmod;
 	HeapTuple	typetup;
+	HeapTuple	newtup;
 	Form_pg_type typeStruct;
 	FmgrInfo	finfo_input;
 
@@ -2164,19 +2646,39 @@ exec_assign_value(PLpgSQL_execstate * estate,
 		case PLPGSQL_DTYPE_VAR:
 
 			/*
-			 * Target field is a variable - that's easy
+			 * Target field is a variable
 			 */
 			var = (PLpgSQL_var *) target;
+
+			if (var->freeval)
+			{
+				pfree((void *)(var->value));
+				var->freeval = false;
+			}
+
 			newvalue = exec_cast_value(value, valtype, var->datatype->typoid,
 									   &(var->datatype->typinput),
 									   var->datatype->typelem,
 									   var->datatype->atttypmod,
 									   isNull);
 
+			if (!var->datatype->typbyval && newvalue == value && !*isNull)
+			{
+				int		len;
+				if (var->datatype->typlen < 0)
+					len = VARSIZE(newvalue);
+				else
+					len = var->datatype->typlen;
+				var->value = (Datum)palloc(len);
+				memcpy((void *)(var->value), (void *)newvalue, len);
+				var->freeval = true;
+			}
+			else
+				var->value = newvalue;
+
 			if (*isNull && var->notnull)
 				elog(ERROR, "NULL assignment to variable '%s' declared NOT NULL", var->refname);
 
-			var->value = newvalue;
 			var->isnull = *isNull;
 			break;
 
@@ -2263,7 +2765,14 @@ exec_assign_value(PLpgSQL_execstate * estate,
 			 * replaces the old one in the record.
 			 */
 			nulls[i] = '\0';
-			rec->tup = heap_formtuple(rec->tupdesc, values, nulls);
+			newtup = heap_formtuple(rec->tupdesc, values, nulls);
+
+			if (rec->freetup)
+				heap_freetuple(rec->tup);
+
+			rec->tup = newtup;
+			rec->freetup = true;
+
 			pfree(values);
 			pfree(nulls);
 
@@ -2289,6 +2798,9 @@ exec_eval_expr(PLpgSQL_execstate * estate,
 {
 	int			rc;
 
+	SPI_tuptable = NULL;
+	SPI_processed = 0;
+
 	/*
 	 * If not already done create a plan for this expression
 	 */
@@ -2302,7 +2814,7 @@ exec_eval_expr(PLpgSQL_execstate * estate,
 	if (expr->plan_simple_expr != NULL)
 		return exec_eval_simple_expr(estate, expr, isNull, rettype);
 
-	rc = exec_run_select(estate, expr, 2);
+	rc = exec_run_select(estate, expr, 2, NULL);
 	if (rc != SPI_OK_SELECT)
 		elog(ERROR, "query \"%s\" didn't return data", expr->query);
 
@@ -2321,7 +2833,8 @@ exec_eval_expr(PLpgSQL_execstate * estate,
 	if (SPI_processed > 1)
 		elog(ERROR, "query \"%s\" returned more than one row", expr->query);
 	if (SPI_tuptable->tupdesc->natts != 1)
-		elog(ERROR, "query \"%s\" returned more than one column", expr->query);
+		elog(ERROR, "query \"%s\" returned %d columns", expr->query,
+				SPI_tuptable->tupdesc->natts);
 
 	/*
 	 * Return the result and its type
@@ -2337,7 +2850,7 @@ exec_eval_expr(PLpgSQL_execstate * estate,
  */
 static int
 exec_run_select(PLpgSQL_execstate * estate,
-				PLpgSQL_expr * expr, int maxtuples)
+				PLpgSQL_expr * expr, int maxtuples, Portal *portalP)
 {
 	PLpgSQL_var *var;
 	PLpgSQL_rec *rec;
@@ -2401,6 +2914,7 @@ exec_run_select(PLpgSQL_execstate * estate,
 				trigarg = (PLpgSQL_trigarg *) (estate->datums[expr->params[i]]);
 				tgargno = (int) exec_eval_expr(estate, trigarg->argnum,
 											   &isnull, &tgargoid);
+				SPI_freetuptable(SPI_tuptable);
 				if (isnull || tgargno < 0 || tgargno >= estate->trig_nargs)
 				{
 					values[i] = 0;
@@ -2423,6 +2937,16 @@ exec_run_select(PLpgSQL_execstate * estate,
 	/*
 	 * Execute the query
 	 */
+	if (portalP != NULL)
+	{
+		*portalP = SPI_cursor_open(NULL, expr->plan, values, nulls);
+		if (*portalP == NULL)
+			elog(ERROR, "failed to open implicit cursor for \"%s\"",
+						expr->query);
+		pfree(values);
+		pfree(nulls);
+		return SPI_OK_CURSOR;
+	}
 	rc = SPI_execp(expr->plan, values, nulls, maxtuples);
 	if (rc != SPI_OK_SELECT)
 		elog(ERROR, "query \"%s\" isn't a SELECT", expr->query);
@@ -2510,6 +3034,7 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate,
 				trigarg = (PLpgSQL_trigarg *) (estate->datums[expr->params[i]]);
 				tgargno = (int) exec_eval_expr(estate, trigarg->argnum,
 											   &isnull, &tgargoid);
+				SPI_freetuptable(SPI_tuptable);
 				if (isnull || tgargno < 0 || tgargno >= estate->trig_nargs)
 				{
 					paramLI->value = 0;
@@ -2581,10 +3106,23 @@ exec_move_row(PLpgSQL_execstate * estate,
 	 */
 	if (rec != NULL)
 	{
+		if (rec->freetup)
+		{
+			heap_freetuple(rec->tup);
+			rec->freetup = false;
+		}
+		if (rec->freetupdesc)
+		{
+			FreeTupleDesc(rec->tupdesc);
+			rec->freetupdesc = false;
+		}
+
 		if (HeapTupleIsValid(tup))
 		{
-			rec->tup = tup;
-			rec->tupdesc = tupdesc;
+			rec->tup = heap_copytuple(tup);
+			rec->tupdesc = CreateTupleDescCopy(tupdesc);
+			rec->freetup = true;
+			rec->freetupdesc = true;
 		}
 		else
 		{
@@ -2766,6 +3304,12 @@ exec_simple_check_plan(PLpgSQL_expr * expr)
 	TargetEntry *tle;
 
 	expr->plan_simple_expr = NULL;
+	/*
+	 * Disabled for now until we can execute simple expressions
+	 * without collecting all the memory allocations until procedure
+	 * returns. 05/17/2001 Jan
+	 */
+    return;
 
 	/*
 	 * 1. We can only evaluate queries that resulted in one single
@@ -2833,3 +3377,5 @@ exec_set_found(PLpgSQL_execstate * estate, bool state)
 	var->value = (Datum) state;
 	var->isnull = false;
 }
+
+
diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c
index 13fd96737670ea7e91c314fe961b3c3835ebec59..a657512fda15e3b281e4f729f6456e04ecfb7d65 100644
--- a/src/pl/plpgsql/src/pl_funcs.c
+++ b/src/pl/plpgsql/src/pl_funcs.c
@@ -3,7 +3,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.12 2001/03/22 06:16:21 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.13 2001/05/21 14:22:19 wieck Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -387,6 +387,9 @@ static void dump_execsql(PLpgSQL_stmt_execsql * stmt);
 static void dump_dynexecute(PLpgSQL_stmt_dynexecute * stmt);
 static void dump_dynfors(PLpgSQL_stmt_dynfors * stmt);
 static void dump_getdiag(PLpgSQL_stmt_getdiag * stmt);
+static void dump_open(PLpgSQL_stmt_open * stmt);
+static void dump_fetch(PLpgSQL_stmt_fetch * stmt);
+static void dump_close(PLpgSQL_stmt_close * stmt);
 static void dump_expr(PLpgSQL_expr * expr);
 
 
@@ -450,6 +453,15 @@ dump_stmt(PLpgSQL_stmt * stmt)
 		case PLPGSQL_STMT_GETDIAG:
 			dump_getdiag((PLpgSQL_stmt_getdiag *) stmt);
 			break;
+		case PLPGSQL_STMT_OPEN:
+			dump_open((PLpgSQL_stmt_open *) stmt);
+			break;
+		case PLPGSQL_STMT_FETCH:
+			dump_fetch((PLpgSQL_stmt_fetch *) stmt);
+			break;
+		case PLPGSQL_STMT_CLOSE:
+			dump_close((PLpgSQL_stmt_close *) stmt);
+			break;
 		default:
 			elog(ERROR, "plpgsql_dump: unknown cmd_type %d\n", stmt->cmd_type);
 			break;
@@ -619,6 +631,66 @@ dump_select(PLpgSQL_stmt_select * stmt)
 
 }
 
+static void
+dump_open(PLpgSQL_stmt_open * stmt)
+{
+	dump_ind();
+	printf("OPEN curvar=%d\n", stmt->curvar);
+
+	dump_indent += 2;
+	if (stmt->argquery != NULL)
+	{
+		dump_ind();
+		printf("  arguments = '");
+		dump_expr(stmt->argquery);
+		printf("'\n");
+	}
+	if (stmt->query != NULL)
+	{
+		dump_ind();
+		printf("  query = '");
+		dump_expr(stmt->query);
+		printf("'\n");
+	}
+	if (stmt->dynquery != NULL)
+	{
+		dump_ind();
+		printf("  execute = '");
+		dump_expr(stmt->dynquery);
+		printf("'\n");
+	}
+	dump_indent -= 2;
+
+}
+
+static void
+dump_fetch(PLpgSQL_stmt_fetch * stmt)
+{
+	dump_ind();
+	printf("FETCH curvar=%d\n", stmt->curvar);
+
+	dump_indent += 2;
+	if (stmt->rec != NULL)
+	{
+		dump_ind();
+		printf("    target = %d %s\n", stmt->rec->recno, stmt->rec->refname);
+	}
+	if (stmt->row != NULL)
+	{
+		dump_ind();
+		printf("    target = %d %s\n", stmt->row->rowno, stmt->row->refname);
+	}
+	dump_indent -= 2;
+
+}
+
+static void
+dump_close(PLpgSQL_stmt_close * stmt)
+{
+	dump_ind();
+	printf("CLOSE curvar=%d\n", stmt->curvar);
+}
+
 static void
 dump_exit(PLpgSQL_stmt_exit * stmt)
 {
@@ -777,6 +849,25 @@ plpgsql_dumptree(PLpgSQL_function * func)
 						   var->refname, var->datatype->typname,
 						   var->datatype->typoid,
 						   var->datatype->atttypmod);
+					if (var->isconst)
+						printf("                                  CONSTANT\n");
+					if (var->notnull)
+						printf("                                  NOT NULL\n");
+					if (var->default_val != NULL)
+					{
+						printf("                                  DEFAULT ");
+						dump_expr(var->default_val);
+						printf("\n");
+					}
+					if (var->cursor_explicit_expr != NULL)
+					{
+						if (var->cursor_explicit_argrow >= 0)
+							printf("                                  CURSOR argument row %d\n", var->cursor_explicit_argrow);
+
+						printf("                                  CURSOR IS ");
+						dump_expr(var->cursor_explicit_expr);
+						printf("\n");
+					}
 				}
 				break;
 			case PLPGSQL_DTYPE_ROW:
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index f364b39e12c06c95d559b5a319757932bdfe1411..7089144988b98fe6d7bc9fad692169e6cf3370ef 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -3,7 +3,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.13 2001/03/22 04:01:42 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.14 2001/05/21 14:22:19 wieck Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -94,7 +94,10 @@ enum
 	PLPGSQL_STMT_EXECSQL,
 	PLPGSQL_STMT_DYNEXECUTE,
 	PLPGSQL_STMT_DYNFORS,
-	PLPGSQL_STMT_GETDIAG
+	PLPGSQL_STMT_GETDIAG,
+	PLPGSQL_STMT_OPEN,
+	PLPGSQL_STMT_FETCH,
+	PLPGSQL_STMT_CLOSE
 };
 
 
@@ -139,6 +142,7 @@ typedef struct
 	Oid			typoid;
 	FmgrInfo	typinput;
 	Oid			typelem;
+	int16		typlen;
 	bool		typbyval;
 	int32		atttypmod;
 }			PLpgSQL_type;
@@ -176,10 +180,12 @@ typedef struct
 	int			isconst;
 	int			notnull;
 	PLpgSQL_expr *default_val;
+	PLpgSQL_expr *cursor_explicit_expr;
+	int			cursor_explicit_argrow;
 
 	Datum		value;
 	bool		isnull;
-	int			shouldfree;
+	bool		freeval;
 }			PLpgSQL_var;
 
 
@@ -206,6 +212,8 @@ typedef struct
 
 	HeapTuple	tup;
 	TupleDesc	tupdesc;
+	bool		freetup;
+	bool		freetupdesc;
 }			PLpgSQL_rec;
 
 
@@ -369,6 +377,36 @@ typedef struct
 }			PLpgSQL_stmt_select;
 
 
+typedef struct
+{								/* OPEN a curvar					*/
+	int			cmd_type;
+	int			lineno;
+	int			curvar;
+	PLpgSQL_row	*returntype;
+	PLpgSQL_expr *argquery;
+	PLpgSQL_expr *query;
+	PLpgSQL_expr *dynquery;
+}			PLpgSQL_stmt_open;
+
+
+typedef struct
+{								/* FETCH curvar INTO statement		*/
+	int			cmd_type;
+	int			lineno;
+	PLpgSQL_rec *rec;
+	PLpgSQL_row *row;
+	int			curvar;
+}			PLpgSQL_stmt_fetch;
+
+
+typedef struct
+{								/* CLOSE curvar						*/
+	int			cmd_type;
+	int			lineno;
+	int			curvar;
+}			PLpgSQL_stmt_close;
+
+
 typedef struct
 {								/* EXIT statement			*/
 	int			cmd_type;
diff --git a/src/pl/plpgsql/src/scan.l b/src/pl/plpgsql/src/scan.l
index 65c5c75faaa2a992e7a1bcb86a4bb2d2eb556387..08f9fb9d06f6be7d6c19bbdafba4367e9d784590 100644
--- a/src/pl/plpgsql/src/scan.l
+++ b/src/pl/plpgsql/src/scan.l
@@ -4,7 +4,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.11 2001/05/18 21:16:59 wieck Exp $
+ *    $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.12 2001/05/21 14:22:19 wieck Exp $
  *
  *    This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -93,7 +93,9 @@ alias			{ return K_ALIAS;			}
 begin			{ return K_BEGIN;			}
 bpchar			{ return T_BPCHAR;			}
 char			{ return T_CHAR;			}
+close			{ return K_CLOSE;			}
 constant		{ return K_CONSTANT;		}
+cursor			{ return K_CURSOR;			}
 debug			{ return K_DEBUG;			}
 declare			{ return K_DECLARE;			}
 default			{ return K_DEFAULT;			}
@@ -104,16 +106,19 @@ end				{ return K_END;				}
 exception		{ return K_EXCEPTION;		}
 execute			{ return K_EXECUTE;			}
 exit			{ return K_EXIT;			}
+fetch			{ return K_FETCH;			}
 for				{ return K_FOR;				}
 from			{ return K_FROM;			}
 get				{ return K_GET;				}
 if				{ return K_IF;				}
 in				{ return K_IN;				}
 into			{ return K_INTO;			}
+is				{ return K_IS;				}
 loop			{ return K_LOOP;			}
 not				{ return K_NOT;				}
 notice			{ return K_NOTICE;			}
 null			{ return K_NULL;			}
+open			{ return K_OPEN;			}
 perform			{ return K_PERFORM;			}
 raise			{ return K_RAISE;			}
 record			{ return K_RECORD;			}