[PATCH] [LARGE] select * from cursor foo

Started by Alex Pilosovover 24 years ago11 messages
#1Alex Pilosov
alex@pilosoft.com
1 attachment(s)

Attached patch does the above.

Notes:
1. Incompatible changes: CURSOR is now a keyword and may not be used as an
identifier (tablename, etc). Otherwise, we get shift-reduce conflicts in
grammar.

2. Major changes:

a) RangeTblEntry (RTE for short) instead of having two possibilities,
subquery and non-subquery, now has a rtetype field which can be of 3
possible states: RTE_RELATION, RTE_SUBSELECT, RTE_PORTAL). The
type-specific structures are unionized, so where you used to have
rte->relid, now you must do rte->u.rel.relid.

Proper way to check what is the RTE type is now checking for rte->rtetype
instead of checking whether rte->subquery is null.

b) Similarly, RelOptInfo now has a RelOptInfoType which is an enum with 4
states, REL_PLAIN,REL_SUBQUERY,REL_JOIN,REL_PORTAL. I did not do the
unionization of type-specific structures. Maybe I should've if I'm going
to get in a big change anyway.

c) There's a function PortalRun which fetches N records from portal and
sets atEnd/atStart values properly. It replaces code duplicated in 2
places.

How to test:

declare foo cursor for select * from pg_class;

select * from cursor foo;

Documentation updates will be forthcoming ASAP, I just wanted to get this
patch in queue before the freeze. Or at least making sure someone could
look through this patch before freeze. :)

Next patch will be one to support "SELECT * FROM func(arg1,arg2)" which
would work by creating first a special kind of portal for selection from a
function and then setting query source to be that portal.

-alex

Attachments:

cursor.fix1.difftext/plain; charset=US-ASCII; name=cursor.fix1.diffDownload
Index: src/backend/commands/command.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/commands/command.c,v
retrieving revision 1.141
retrieving revision 1.142
diff -r1.141 -r1.142
101a102
> 	int 		result;
170,211c171,174
< 	/*
< 	 * Determine which direction to go in, and check to see if we're
< 	 * already at the end of the available tuples in that direction.  If
< 	 * so, do nothing.	(This check exists because not all plan node types
< 	 * are robust about being called again if they've already returned
< 	 * NULL once.)	If it's OK to do the fetch, call the executor.  Then,
< 	 * update the atStart/atEnd state depending on the number of tuples
< 	 * that were retrieved.
< 	 */
< 	if (forward)
< 	{
< 		if (!portal->atEnd)
< 		{
< 			ExecutorRun(queryDesc, estate, EXEC_FOR, (long) count);
< 			/*
< 			 *	I use CMD_UPDATE, because no CMD_MOVE or the like
< 			 *	exists, and I would like to provide the same
< 			 *	kind of info as CMD_UPDATE
< 			 */
< 			UpdateCommandInfo(CMD_UPDATE, 0, estate->es_processed);
< 			if (estate->es_processed > 0)
< 				portal->atStart = false;		/* OK to back up now */
< 			if (count <= 0 || (int) estate->es_processed < count)
< 				portal->atEnd = true;	/* we retrieved 'em all */
< 		}
< 	}
< 	else
< 	{
< 		if (!portal->atStart)
< 		{
< 			ExecutorRun(queryDesc, estate, EXEC_BACK, (long) count);
< 			/*
< 			 *	I use CMD_UPDATE, because no CMD_MOVE or the like
< 			 *	exists, and I would like to provide the same
< 			 *	kind of info as CMD_UPDATE
< 			 */
< 			UpdateCommandInfo(CMD_UPDATE, 0, estate->es_processed);
< 			if (estate->es_processed > 0)
< 				portal->atEnd = false;	/* OK to go forward now */
< 			if (count <= 0 || (int) estate->es_processed < count)
< 				portal->atStart = true; /* we retrieved 'em all */
< 		}
---
> 	result = PortalRun( portal, forward, (long) count, queryDesc->dest, NULL );
> 
>   	if (result > 0) {
> 		UpdateCommandInfo(CMD_UPDATE, 0, result);
Index: src/backend/commands/explain.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/commands/explain.c,v
retrieving revision 1.65
retrieving revision 1.66
diff -r1.65 -r1.66
185a186,188
> 		case T_PortalScan:
> 			pname = "Portal Scan";
> 			break;
264c267,268
< 				RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
---
> 				RangeTblEntry *rte = (RangeTblEntry*)
> 							rt_fetch(((Scan *) plan)->scanrelid,
268c272
< 				Assert(rte->relname);
---
> 				Assert( rte->rtetype == RTE_RELATION );
271,272c275,276
< 								 stringStringInfo(rte->relname));
< 				if (strcmp(rte->eref->relname, rte->relname) != 0)
---
> 								 stringStringInfo(rte->u.rel.relname));
> 				if (strcmp(rte->eref->relname, rte->u.rel.relname) != 0)
358c362,364
< 		RangeTblEntry *rte = rt_fetch(subqueryscan->scan.scanrelid,
---
> 
> 		RangeTblEntry *rte = (RangeTblEntry *)
> 									rt_fetch(subqueryscan->scan.scanrelid,
359a366
> 
362,363c369,370
< 		Assert(rte->subquery != NULL);
< 		es->rtable = rte->subquery->rtable;
---
> 		Assert(rte->rtetype == RTE_SUBSELECT);
> 		es->rtable = rte->u.sub.subquery->rtable;
Index: src/backend/executor/Makefile
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/executor/Makefile,v
retrieving revision 1.16
retrieving revision 1.17
diff -r1.16 -r1.17
19c19,20
<        nodeNestloop.o nodeResult.o nodeSeqscan.o nodeSetOp.o nodeSort.o \
---
>        nodeNestloop.o nodePortalscan.o nodeResult.o nodeSeqscan.o \
>        nodeSetOp.o nodeSort.o \
Index: src/backend/executor/execAmi.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/executor/execAmi.c,v
retrieving revision 1.58
retrieving revision 1.59
diff -r1.58 -r1.59
44a45
> #include "executor/nodePortalscan.h"
187a189,192
> 		case T_PortalScan:
> 			state = ((PortalScan *) node)->scan.scanstate;
> 			break;
> 
295a301,304
> 			break;
> 
> 		case T_PortalScan:
> 			ExecPortalReScan((PortalScan *) node, exprCtxt, parent);
Index: src/backend/executor/execMain.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/executor/execMain.c,v
retrieving revision 1.145
retrieving revision 1.146
diff -r1.145 -r1.146
348,349c348,350
< 				Assert(rte->subquery != NULL);
< 				ExecCheckQueryPerms(operation, rte->subquery, scan->subplan);
---
> 				Assert(rte->rtetype == RTE_SUBSELECT);
> 				ExecCheckQueryPerms(operation, rte->u.sub.subquery, 
> 									scan->subplan);
403,404c404
< 	if (rte->subquery)
< 		return;
---
> 	if (!rte->rtetype == RTE_RELATION) return;
406c406,407
< 	relName = rte->relname;
---
>     
> 	relName = rte->u.rel.relname;
Index: src/backend/executor/execProcnode.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/executor/execProcnode.c,v
retrieving revision 1.26
retrieving revision 1.27
diff -r1.26 -r1.27
91a92
> #include "executor/nodePortalscan.h"
166a168,172
> 		case T_PortalScan:
> 			result = ExecInitPortalScan((PortalScan *) node, estate,
> 										  parent);
> 			break;
> 
292a299,302
> 		case T_PortalScan:
> 			result = ExecPortalScan((PortalScan *) node);
> 			break;
> 
384a395,397
> 		case T_PortalScan:
> 			return ExecCountSlotsPortalScan((PortalScan *) node);
> 
496a510,513
> 		case T_PortalScan:
> 			ExecEndPortalScan((PortalScan *) node);
> 			break;
> 
625a643,650
> 
> 				slot = scanstate->cstate.cs_ResultTupleSlot;
> 			}
> 			break;
> 
> 		case T_PortalScan:
> 			{
> 				CommonScanState *scanstate = ((PortalScan *) node)->scan.scanstate;
Index: src/backend/executor/nodeIndexscan.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/executor/nodeIndexscan.c,v
retrieving revision 1.62
retrieving revision 1.63
diff -r1.62 -r1.63
991c991,993
< 	reloid = rtentry->relid;
---
>     Assert(rtentry->rtetype == RTE_RELATION);
> 
> 	reloid = rtentry->u.rel.relid;
Index: src/backend/executor/nodeSeqscan.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/executor/nodeSeqscan.c,v
retrieving revision 1.31
retrieving revision 1.32
diff -r1.31 -r1.32
161a162
> 
163c164,166
< 	reloid = rtentry->relid;
---
>     Assert(rtentry->rtetype == RTE_RELATION);
> 
> 	reloid = rtentry->u.rel.relid;
Index: src/backend/executor/nodeSubqueryscan.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/executor/nodeSubqueryscan.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -r1.9 -r1.10
150c150
< 	Assert(rte->subquery != NULL);
---
>     Assert(rte->rtetype == RTE_SUBSELECT);
155c155,156
< 	sp_estate->es_range_table = rte->subquery->rtable;
---
>     Assert( PointerIsValid(rte->u.sub.subquery) );
> 	sp_estate->es_range_table = rte->u.sub.subquery->rtable;
Index: src/backend/executor/nodeTidscan.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/executor/nodeTidscan.c,v
retrieving revision 1.18
retrieving revision 1.19
diff -r1.18 -r1.19
460a461
> 
462c463
< 	reloid = rtentry->relid;
---
>     Assert(rtentry->rtetype == RTE_RELATION);
463a465
> 	reloid = rtentry->u.rel.relid;
Index: src/backend/executor/spi.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/executor/spi.c,v
retrieving revision 1.57
retrieving revision 1.58
diff -r1.57 -r1.58
1138,1141c1138,1139
<     QueryDesc	   *querydesc;
< 	EState		   *estate;
< 	MemoryContext	oldcontext;
< 	CommandDest		olddest;
---
> 	int nrows; /* how many records portal returned */
> 	
1156,1159c1154
< 	/* Switch to the portals memory context */
< 	oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
< 	querydesc  = PortalGetQueryDesc(portal);
< 	estate     = PortalGetState(portal);
---
> 	nrows = PortalRun ( portal, forward, (long) count, dest, NULL);
1161,1195c1156
< 	/* 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);
---
> 	_SPI_current->processed = nrows;
Index: src/backend/executor/nodePortalscan.c
===================================================================
RCS file: nodePortalscan.c
diff -N nodePortalscan.c
0a1,258
> /*-------------------------------------------------------------------------
>  *
>  * nodePortalscan.c
>  *	  Support routines for scanning portals (SELECT * FROM CURSOR FOO).
>  *
>  *
>  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
>  * Portions Copyright (c) 1994, Regents of the University of California
>  *
>  *
>  * IDENTIFICATION
>  *	  $Header$
>  *
>  *-------------------------------------------------------------------------
>  */
> /*
>  * INTERFACE ROUTINES
>  *		ExecPortalScan			scans a portal.
>  *		ExecPortalNext			retrieve next tuple in sequential order.
>  *		ExecInitPortalScan		creates and initializes a portalscan node.
>  *		ExecEndPortalScan			releases any storage allocated.
>  *		ExecPortalReScan			rescans the relation
>  *
>  */
> #include "postgres.h"
> 
> #include "catalog/pg_type.h"
> #include "executor/execdebug.h"
> #include "executor/execdefs.h"
> #include "executor/execdesc.h"
> #include "executor/nodePortalscan.h"
> #include "parser/parsetree.h"
> #include "tcop/pquery.h"
> 
> static TupleTableSlot *PortalNext(PortalScan *node);
> 
> /* ----------------------------------------------------------------
>  *						Scan Support
>  * ----------------------------------------------------------------
>  */
> /* ----------------------------------------------------------------
>  *		PortalNext
>  *
>  *		This is a workhorse for ExecPortalScan
>  * ----------------------------------------------------------------
>  */
> static TupleTableSlot *
> PortalNext(PortalScan *node)
> {
> 	PortalScanState *portalstate;
> 	EState	   *estate;
> 	ScanDirection direction;
> 	TupleTableSlot *slot;
> 	bool 		forward;
> 	Portal		portal;
> 	int 		ntuples;
> 
> 	/*
> 	 * get information from the estate and scan state
> 	 */
> 	estate = node->scan.plan.state;
> 	portalstate = (PortalScanState *) node->scan.scanstate;
> 	direction = estate->es_direction;
> 	forward = ScanDirectionIsForward(direction);
> 	portal = portalstate->portal;
> 
> 	/* 
> 	 * get the next tuple from Portal
> 	 */
> 
> 	ntuples = PortalRun(portal, forward, (long) 1, NULL, &slot);
> 
> 	portalstate->csstate.css_ScanTupleSlot = slot;
> 
> 	return slot;
> }
> 
> /* ----------------------------------------------------------------
>  *		ExecPortalScan(node)
>  *
>  *		Scans the portal sequentially and returns the next qualifying
>  *		tuple.
>  *		It calls the ExecScan() routine and passes it the access method
>  *		which retrieve tuples sequentially.
>  *
>  */
> 
> TupleTableSlot *
> ExecPortalScan(PortalScan *node)
> {
> 	/*
> 	 * use PortalNext as access method
> 	 */
> 	return ExecScan(&node->scan, (ExecScanAccessMtd) PortalNext);
> }
> 
> /* ----------------------------------------------------------------
>  *		ExecInitPortalScan
>  * ----------------------------------------------------------------
>  */
> bool
> ExecInitPortalScan(PortalScan *node, EState *estate, Plan *parent)
> {
> 	PortalScanState *portalstate;
> 	RangeTblEntry *rte;
> 	Portal		portal;
> 
> 	/*
> 	 * PortalScan should not have any "normal" children.
> 	 */
> 	Assert(outerPlan((Plan *) node) == NULL);
> 	Assert(innerPlan((Plan *) node) == NULL);
> 
> 	/*
> 	 * assign the node's execution state
> 	 */
> 	node->scan.plan.state = estate;
> 
> 	/*
> 	 * create new PortalScanState for node
> 	 */
> 	portalstate = makeNode(PortalScanState);
> 	node->scan.scanstate = (CommonScanState *) portalstate;
> 
> 	/*
> 	 * Miscellaneous initialization
> 	 *
> 	 * create expression context for node
> 	 */
> 	ExecAssignExprContext(estate, &portalstate->csstate.cstate);
> 
> #define SUBQUERYSCAN_NSLOTS 1
> 
> 	/*
> 	 * tuple table initialization
> 	 */
> 	ExecInitResultTupleSlot(estate, &portalstate->csstate.cstate);
> 
> 	/*
> 	 * initialize portal
> 	 *
> 	 * This should agree with ExecInitSubPlan
> 	 */
>     rte = rt_fetch( node->scan.scanrelid, estate->es_range_table);
>     Assert(rte->rtetype == RTE_PORTAL);
> 
> 	portalstate->csstate.css_ScanTupleSlot = NULL;
> 	portalstate->csstate.cstate.cs_TupFromTlist = false;
> 
> 	portal = GetPortalByName( rte->u.portal.portalname);
> 	if (!PointerIsValid(portal)) 
> 		elog(ERROR, "InitPortalScan: portal %s disappeared", 
> 						rte->u.portal.portalname);
> 
> 	portalstate->portal = portal;
> 
> 	/*
> 	 * initialize tuple type
> 	 */
> 	ExecAssignResultTypeFromTL((Plan *) node, &portalstate->csstate.cstate);
> 	ExecAssignProjectionInfo((Plan *) node, &portalstate->csstate.cstate);
> 
> 	return TRUE;
> }
> 
> int
> ExecCountSlotsPortalScan(PortalScan *node)
> {
> 	/*
> 	 * The subplan has its own tuple table and must not be counted here!
> 	 */
> 	return ExecCountSlotsNode(outerPlan(node)) +
> 	ExecCountSlotsNode(innerPlan(node)) +
> 	SUBQUERYSCAN_NSLOTS;
> }
> 
> /* ----------------------------------------------------------------
>  *		ExecEndPortalScan
>  *
>  *		frees any storage allocated through C routines.
>  * ----------------------------------------------------------------
>  */
> void
> ExecEndPortalScan(PortalScan *node)
> {
> 	PortalScanState *portalstate;
> 
> 	/*
> 	 * get information from node
> 	 */
> 	portalstate = (PortalScanState *) node->scan.scanstate;
> 
> 	/*
> 	 * Free the projection info and the scan attribute info
> 	 *
> 	 * Note: we don't ExecFreeResultType(portalstate) because the rule
> 	 * manager depends on the tupType returned by ExecMain().  So for now,
> 	 * this is freed at end-transaction time.  -cim 6/2/91
> 	 */
> 	ExecFreeProjectionInfo(&portalstate->csstate.cstate);
> 	ExecFreeExprContext(&portalstate->csstate.cstate);
> 
> 	/*
> 	 * close down portal
> 	 */
>  	/* XXX */
> 
> 	/*
> 	 * clean up portal's tuple table
> 	 */
> 	portalstate->csstate.css_ScanTupleSlot = NULL;
> 
> 	/*
> 	 * clean out the upper tuple table
> 	 */
> 	ExecClearTuple(portalstate->csstate.cstate.cs_ResultTupleSlot);
> }
> 
> /* ----------------------------------------------------------------
>  *		ExecPortalReScan
>  *
>  *		Rescans the relation.
>  * ----------------------------------------------------------------
>  */
> void
> ExecPortalReScan(PortalScan *node, ExprContext *exprCtxt, Plan *parent)
> {
> 	PortalScanState *portalstate;
> 	EState	   *estate;
> 
> 	portalstate = (PortalScanState *) node->scan.scanstate;
> 	estate = node->scan.plan.state;
> 
> 	elog(ERROR, "PortalReScan: Cannot rescan portals");
> 
> 	/*
> 	 * ExecReScan doesn't know about my subplan, so I have to do
> 	 * changed-parameter signaling myself.
> 	 */
> 
> /*
> 	if (node->scan.plan.chgParam != NULL)
> 		SetChangedParamList(node->subplan, node->scan.plan.chgParam);
> */
> 
> 	/*
> 	 * if chgParam of subnode is not null then plan will be re-scanned by
> 	 * first ExecProcNode.
> 	 */
> 
> /*
>  XXX 
> 	if (node->subplan->chgParam == NULL)
> 		ExecReScan(node->subplan, NULL, node->subplan);
> */
> 
> 	portalstate->csstate.css_ScanTupleSlot = NULL;
> }
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.155
retrieving revision 1.156
diff -r1.155 -r1.156
229a230,247
>  *		_copyPortalScan
>  * ----------------
>  */
> static PortalScan *
> _copyPortalScan(PortalScan *from)
> {
> 	PortalScan    *newnode = makeNode(PortalScan);
> 
> 	/*
> 	 * copy node superclass fields
> 	 */
> 	CopyPlanFields((Plan *) from, (Plan *) newnode);
> 	CopyScanFields((Scan *) from, (Scan *) newnode);
> 
> 	return newnode;
> }
> 
> /* ----------------
1108c1126
< 	newnode->issubquery = from->issubquery;
---
> 	newnode->reltype = from->reltype;
1490,1494d1507
< 
< 	if (from->relname)
< 		newnode->relname = pstrdup(from->relname);
< 	newnode->relid = from->relid;
< 	Node_Copy(from, newnode, subquery);
1502c1515,1529
< 
---
> 	newnode->rtetype = from->rtetype;
> 	switch (from->rtetype) {
> 		case RTE_RELATION:
> 			newnode->u.rel.relname = pstrdup(from->u.rel.relname);
> 			newnode->u.rel.relid = from->u.rel.relid;
> 			break;
> 		case RTE_SUBSELECT:
> 			Node_Copy(from, newnode, u.sub.subquery);
> 			break;
> 		case RTE_PORTAL:
> 			newnode->u.portal.portalname = pstrdup(from->u.portal.portalname);
> 			break;
> 		default: 
> 			elog(ERROR, "copyRTE: Unknown rtetype %d", from->rtetype);
> 	}
2249d2275
< 	newnode->freeze = from->freeze;
2555a2582,2584
> 			break;
> 		case T_PortalScan:
> 			retval = _copyPortalScan(from);
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.103
retrieving revision 1.104
diff -r1.103 -r1.104
1119,1120d1118
< 	if (a->freeze != b->freeze)
< 		return false;
1600,1605d1597
< 	if (!equalstr(a->relname, b->relname))
< 		return false;
< 	if (a->relid != b->relid)
< 		return false;
< 	if (!equal(a->subquery, b->subquery))
< 		return false;
1620c1612,1631
< 
---
> 	if (a->rtetype != b->rtetype)
> 		return false;
> 	switch (a->rtetype) {
> 		case RTE_RELATION:
> 			if (!equalstr(a->u.rel.relname, b->u.rel.relname))
> 				return false;
> 			if (a->u.rel.relid != b->u.rel.relid)
> 				return false;
> 			break;
> 		case RTE_SUBSELECT:
> 			if (!equal(a->u.sub.subquery, b->u.sub.subquery))
> 				return false;
> 			break;
> 		case RTE_PORTAL:
> 			if (!equalstr(a->u.portal.portalname, b->u.portal.portalname))
> 				return false;
> 			break;
> 		default: 
> 			elog(ERROR, "equalRTE: Unknown rtetype %d", a->rtetype);
> 	}
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/nodes/outfuncs.c,v
retrieving revision 1.145
retrieving revision 1.146
diff -r1.145 -r1.146
506a507,516
>  *	PortalScan is a subclass of Scan
>  */
> static void
> _outPortalScan(StringInfo str, PortalScan *node)
> {
> 	appendStringInfo(str, " PORTALSCAN ");
> 	_outPlanInfo(str, (Plan *) node);
> }
> 
> /*
965,970c975
< 	appendStringInfo(str, " RTE :relname ");
< 	_outToken(str, node->relname);
< 	appendStringInfo(str, " :relid %u ",
< 					 node->relid);
< 	appendStringInfo(str, " :subquery ");
< 	_outNode(str, node->subquery);
---
> 	appendStringInfo(str, " RTE ");
981a987,1004
> 	appendStringInfo(str, " :rtetype %u ", node->rtetype);
> 	switch (node->rtetype) {
> 		case RTE_RELATION:
> 			appendStringInfo(str, " :relname ");
> 			_outToken(str, node->u.rel.relname);
> 			appendStringInfo(str, " :relid %u ", node->u.rel.relid);
> 			break;
> 		case RTE_SUBSELECT:
> 			appendStringInfo(str, " :subquery ");
> 			_outNode(str, node->u.sub.subquery);
> 			break;
> 		case RTE_PORTAL:
> 			appendStringInfo(str, " :portalname ");
> 			_outToken(str, node->u.portal.portalname);
> 			break;
> 		default: 
> 			elog(ERROR, "outRTE: Unknown rtetype %d", node->rtetype);
> 	}
1510a1534,1536
> 				break;
> 			case T_PortalScan:
> 				_outPortalScan(str, obj);
Index: src/backend/nodes/print.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/nodes/print.c,v
retrieving revision 1.47
retrieving revision 1.48
diff -r1.47 -r1.48
134c134
< 		if (rte->relname)
---
> 		if (rte->rtetype == RTE_RELATION) {
136,137c136,138
< 				   i, rte->relname, rte->eref->relname, rte->relid);
< 		else
---
> 				   i, rte->u.rel.relname, rte->eref->relname, rte->u.rel.relid);
> 		}
> 		else if (rte->rtetype == RTE_SUBSELECT) {
139a141,145
> 		}
> 		else if (rte->rtetype == RTE_PORTAL) {
> 			printf("%d\t[portal] (%s)\t",
> 				   i, rte->eref->relname);
> 		}
302a309,310
> 		case T_PortalScan:
> 			return "PORTALSCAN";
363c371,372
< 		StrNCpy(extraInfo, rte->relname, NAMEDATALEN);
---
> 		Assert( rte->rtetype == RTE_RELATION);
> 		StrNCpy(extraInfo, rte->u.rel.relname, NAMEDATALEN);
370c379,380
< 		StrNCpy(extraInfo, rte->relname, NAMEDATALEN);
---
> 		Assert( rte->rtetype == RTE_RELATION);
> 		StrNCpy(extraInfo, rte->u.rel.relname, NAMEDATALEN);
Index: src/backend/nodes/readfuncs.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/nodes/readfuncs.c,v
retrieving revision 1.112
retrieving revision 1.113
diff -r1.112 -r1.113
1417c1417
< _readRangeTblEntry(void)
---
> _readRangeTblEntry()
1425,1435d1424
< 	token = pg_strtok(&length); /* eat :relname */
< 	token = pg_strtok(&length); /* get :relname */
< 	local_node->relname = nullable_string(token, length);
< 
< 	token = pg_strtok(&length); /* eat :relid */
< 	token = pg_strtok(&length); /* get :relid */
< 	local_node->relid = atooid(token);
< 
< 	token = pg_strtok(&length); /* eat :subquery */
< 	local_node->subquery = nodeRead(true);		/* now read it */
< 
1461a1451,1476
> 	token = pg_strtok(&length); /* eat :rtetype */
> 	token = pg_strtok(&length); /* get :rtetype */
> 	local_node->rtetype = atoui(token);
> 
> 	switch (local_node->rtetype) {
> 		case RTE_RELATION:
> 			token = pg_strtok(&length); /* eat :relname */
> 			token = pg_strtok(&length); /* get :relname */
> 			local_node->u.rel.relname = nullable_string(token, length);
> 
> 			token = pg_strtok(&length); /* eat :relid */
> 			token = pg_strtok(&length); /* get :relid */
> 			local_node->u.rel.relid = atooid(token);
> 			break;
> 		case RTE_SUBSELECT:
> 			token = pg_strtok(&length); /* eat :subquery */
> 			local_node->u.sub.subquery = nodeRead(true);	/* now read it */
> 			break;
> 		case RTE_PORTAL:
> 			token = pg_strtok(&length); /* eat :portalname */
> 			token = pg_strtok(&length); /* get :portalname */
> 			local_node->u.portal.portalname = nullable_string(token, length);
> 			break;
> 		default: 
> 			elog(ERROR, "readRTE: Unknown rtetype %d", local_node->rtetype);
> 	}
1463a1479
> 
Index: src/backend/optimizer/path/allpaths.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/path/allpaths.c,v
retrieving revision 1.78
retrieving revision 1.79
diff -r1.78 -r1.79
27a28
> #include "utils/portal.h"
103c104
< 		if (rel->issubquery)
---
> 		if (rel->reltype == REL_SUBQUERY) 
104a106
>             Assert(rte->rtetype == RTE_SUBSELECT);
108c110,143
< 		else if ((inheritlist = expand_inherted_rtentry(root, rti, true))
---
> 		else if (rel->reltype == REL_PORTAL) 
> 		{
> 			/* its a portal, considerably simpler case, as portals
> 			   may not have setops or conditions. We only need
> 		       to fill in the cost info from Portal's own Plan */
> 			Portal portal;
> 			QueryDesc *qd; 
> 
> 			Assert(rte->rtetype == RTE_PORTAL);
> 
> 			portal = GetPortalByName(rte->u.portal.portalname);
> 			if (!PointerIsValid(portal)) 
> 				elog(ERROR, "set_base_rel_pathlists: Portal %s disappeared", 
> 					rte->u.portal.portalname);
> 
> 			qd = PortalGetQueryDesc(portal);
> 
> 			/* Copy number of output rows from portal info */
> 			rel->tuples = qd->plantree->plan_rows;
> 
> 			/* Mark rel with estimated output rows, width, etc */
> 			set_baserel_size_estimates(root, rel); 
> 
> 			/* Generate appropriate path */
> 			add_path(rel, create_portalscan_path(rel, portal) );
> 
> 			/* Select cheapest path (pretty easy in this case...) */
> 			set_cheapest(rel);
> 		}
> /* XXX: maybe we can/should determine that a relation is a root of inheritance
>  *      hierarchy earlier and have REL_INH_ROOT reltype?
>  */
> 		else if (rel->reltype == REL_PLAIN)  {
> 			if ((inheritlist = expand_inherted_rtentry(root, rti, true))
119a155,157
> 		else 
> 			elog(ERROR,"set_base_rel_pathlists: unknown reltype %d", rel->reltype);
> 	}
128a167
> 	Assert( rte->rtetype == RTE_RELATION );
181c220
< 	Oid			parentOID = rte->relid;
---
> 	Oid			parentOID = rte->u.rel.relid;
184a224
> 	Assert( rte->rtetype == RTE_RELATION );
218c258,261
< 		childOID = childrte->relid;
---
> 
> 		Assert( childrte->rtetype == RTE_RELATION );
> 
> 		childOID = childrte->u.rel.relid;
279c322,323
< 	Query	   *subquery = rte->subquery;
---
> 	Query	   *subquery = rte->u.sub.subquery;
>     Assert(rte->rtetype == RTE_SUBSELECT);
Index: src/backend/optimizer/path/clausesel.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/path/clausesel.c,v
retrieving revision 1.46
retrieving revision 1.47
diff -r1.46 -r1.47
387c387
< 			if (rte->subquery)
---
> 			if (rte->rtetype == RTE_SUBSELECT)
Index: src/backend/optimizer/path/costsize.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/path/costsize.c,v
retrieving revision 1.78
retrieving revision 1.79
diff -r1.78 -r1.79
113c113
< 	Assert(!baserel->issubquery);
---
> 	Assert(baserel->reltype == REL_PLAIN);
228c228
< 	Assert(!baserel->issubquery);
---
> 	Assert(baserel->reltype == REL_PLAIN);
Index: src/backend/optimizer/plan/createplan.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/plan/createplan.c,v
retrieving revision 1.108
retrieving revision 1.109
diff -r1.108 -r1.109
45a46,47
> static PortalScan *create_portalscan_plan(Path *best_path,
> 						 List *tlist, List *scan_clauses);
71a74,75
> static PortalScan *make_portalscan(List *qptlist, List *qpqual, 
> 									Index scanrelid);
119a124
> 		case T_PortalScan:
200a206,211
> 		case T_PortalScan:
> 			plan = (Scan *) create_portalscan_plan(best_path,
> 													 tlist,
> 													 scan_clauses);
> 			break;
> 
351,352c362
< 	Assert(length(best_path->parent->relids) == 1);
< 	Assert(!best_path->parent->issubquery);
---
> 	Assert(best_path->parent->reltype == REL_PLAIN);
395,396c405
< 	Assert(length(best_path->path.parent->relids) == 1);
< 	Assert(!best_path->path.parent->issubquery);
---
> 	Assert(best_path->path.parent->reltype == REL_PLAIN);
513,514c522
< 	Assert(length(best_path->path.parent->relids) == 1);
< 	Assert(!best_path->path.parent->issubquery);
---
> 	Assert(best_path->path.parent->reltype == REL_PLAIN);
543d550
< 	Assert(length(best_path->parent->relids) == 1);
545c552
< 	Assert(best_path->parent->issubquery);
---
> 	Assert(best_path->parent->reltype == REL_SUBQUERY);
556a564,587
> /*
>  * create_portalscan_plan
>  *	 Returns a portalscan plan for the portal scanned by 'best_path'
>  *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
>  */
> static PortalScan *
> create_portalscan_plan(Path *best_path, List *tlist, List *scan_clauses)
> {
> 	PortalScan *scan_plan;
> 	Index		scan_relid;
> 
> 	/* there should be exactly one base rel involved... */
> 	/* and it must be a portal */
> 	Assert(best_path->parent->reltype == REL_PORTAL);
> 
> 	scan_relid = (Index) lfirsti(best_path->parent->relids);
> 
> 	scan_plan = make_portalscan( tlist, scan_clauses, scan_relid );
> 
> 	copy_path_costsize(&scan_plan->scan.plan, best_path);
> 
> 	return scan_plan;
> }
> 
1288a1320,1339
> 	node->scan.scanstate = (CommonScanState *) NULL;
> 
> 	return node;
> }
> 
> PortalScan *
> make_portalscan(List *qptlist,
> 				  List *qpqual,
> 				  Index scanrelid)
> {
> 	PortalScan *node = makeNode(PortalScan);
> 	Plan	   *plan = &node->scan.plan;
> 
> 	/* cost should be inserted by caller */
> 	plan->state = (EState *) NULL;
> 	plan->targetlist = qptlist;
> 	plan->qual = qpqual;
> 	plan->lefttree = NULL;
> 	plan->righttree = NULL;
> 	node->scan.scanrelid = scanrelid;
Index: src/backend/optimizer/plan/planner.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/plan/planner.c,v
retrieving revision 1.108
retrieving revision 1.109
diff -r1.108 -r1.109
271c271,272
< 		Query	   *subquery = rte->subquery;
---
> 		Query	   *subquery = rte->u.sub.subquery;
> 		
280c281,282
< 		if (subquery && is_simple_subquery(subquery) &&
---
> 		if (rte->rtetype == RTE_SUBSELECT && 
>              is_simple_subquery(subquery) &&
Index: src/backend/optimizer/plan/setrefs.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/plan/setrefs.c,v
retrieving revision 1.71
retrieving revision 1.72
diff -r1.71 -r1.72
89a90,93
> 		case T_PortalScan:
> 			fix_expr_references(plan, (Node *) plan->targetlist);
> 			fix_expr_references(plan, (Node *) plan->qual);
> 			break;
Index: src/backend/optimizer/prep/preptlist.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/prep/preptlist.c,v
retrieving revision 1.42
retrieving revision 1.43
diff -r1.42 -r1.43
61c61
< 		if (rte->subquery != NULL || rte->relid == InvalidOid)
---
> 		if (rte->rtetype == RTE_SUBSELECT)
Index: src/backend/optimizer/prep/prepunion.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/prep/prepunion.c,v
retrieving revision 1.66
retrieving revision 1.67
diff -r1.66 -r1.67
84a85
> 	RangeTblEntry *rte;
97,98c98,102
< 	leftmostQuery = rt_fetch(((RangeTblRef *) node)->rtindex,
< 							 parse->rtable)->subquery;
---
> 	rte = rt_fetch(((RangeTblRef *) node)->rtindex, parse->rtable);
> 	Assert(rte && rte->rtetype == RTE_SUBSELECT);
> 
> 	leftmostQuery = rte->u.sub.subquery;
> 
130c134
< 		Query	   *subquery = rte->subquery;
---
> 		Query	   *subquery = rte->u.sub.subquery;
133a138
> 		Assert(rte->rtetype == RTE_SUBSELECT);
558c563
< 	Oid			parentOID = rte->relid;
---
> 	Oid			parentOID = rte->u.rel.relid;
561a567
> 	Assert(rte->rtetype == RTE_RELATION);
566c572
< 	Assert(parentOID != InvalidOid && rte->subquery == NULL);
---
> 
604,605c610,613
< 		childrte->relname = get_rel_name(childOID);
< 		childrte->relid = childOID;
---
> 		Assert(rte->rtetype == RTE_RELATION);
> 		Assert(childrte->rtetype == RTE_RELATION);
> 		childrte->u.rel.relname = get_rel_name(childOID);
> 		childrte->u.rel.relid = childOID;
Index: src/backend/optimizer/util/clauses.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/util/clauses.c,v
retrieving revision 1.88
retrieving revision 1.89
diff -r1.88 -r1.89
1801,1802c1801,1802
< 			if (rte->subquery)
< 				if (walker(rte->subquery, context))
---
> 			if (rte->rtetype == RTE_SUBSELECT)
> 				if (walker( rte->u.sub.subquery, context))
2182c2182
< 			RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
---
> 			RangeTblEntry *rte = lfirst(rt);
2184c2184
< 			if (rte->subquery)
---
> 			if (rte->rtetype == RTE_SUBSELECT)
2189,2190c2189,2192
< 				CHECKFLATCOPY(newrte->subquery, rte->subquery, Query);
< 				MUTATE(newrte->subquery, newrte->subquery, Query *);
---
> 				CHECKFLATCOPY(newrte->u.sub.subquery, rte->u.sub.subquery, 
> 								Query);
> 				MUTATE(newrte->u.sub.subquery, newrte->u.sub.subquery, 
> 						Query *);
Index: src/backend/optimizer/util/pathnode.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/util/pathnode.c,v
retrieving revision 1.75
retrieving revision 1.76
diff -r1.75 -r1.76
436a437,457
>  * create_portalscan_path
>  *	  Creates a path corresponding to a sequential scan of a portal,
>  *	  returning the pathnode.
>  */
> Path *
> create_portalscan_path(RelOptInfo *rel, Portal portal)
> {
> 	Path	   *pathnode = makeNode(Path);
> 
> 	pathnode->pathtype = T_PortalScan;
> 	pathnode->parent = rel;
> 	pathnode->pathkeys = NIL;	/* for now, assume unordered result */
> 
> 	/* just copy the Portal's own cost estimates */
> 	pathnode->startup_cost = portal->queryDesc->plantree->startup_cost;
> 	pathnode->total_cost = portal->queryDesc->plantree->total_cost;
> 
> 	return pathnode;
> }
> 
> /*
Index: src/backend/optimizer/util/relnode.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/util/relnode.c,v
retrieving revision 1.33
retrieving revision 1.34
diff -r1.33 -r1.34
128c128
< 	Oid			relationObjectId;
---
> 	RangeTblEntry *rte;
138d137
< 	rel->issubquery = false;
149,150c148,149
< 	/* Check rtable to see if it's a plain relation or a subquery */
< 	relationObjectId = getrelid(relid, root->rtable);
---
>    /* Check rtable to see if what kind of a relation it is */
> 	rte = rt_fetch(relid, root->rtable);
152c151
< 	if (relationObjectId != InvalidOid)
---
> 	if (rte->rtetype == RTE_RELATION)
154,155c153,155
< 		/* Plain relation --- retrieve statistics from the system catalogs */
< 		bool	indexed;
---
> 	    /* Plain relation --- retrieve statistics from the system catalogs */
> 	    bool    indexed;
> 	    Oid     relationObjectId;
157,160c157,163
< 		get_relation_info(relationObjectId,
< 						  &indexed, &rel->pages, &rel->tuples);
< 		if (indexed)
< 			rel->indexlist = find_secondary_indexes(relationObjectId);
---
> 	    rel->reltype = REL_PLAIN;
> 	    relationObjectId = rte->u.rel.relid;
> 
> 	    get_relation_info( relationObjectId,
> 	                      &indexed, &rel->pages, &rel->tuples);
> 	    if (indexed)
> 	        rel->indexlist = find_secondary_indexes(relationObjectId);
162c165
< 	else
---
> 	else if (rte->rtetype == RTE_SUBSELECT)
164,165c167
< 		/* subquery --- mark it as such for later processing */
< 		rel->issubquery = true;
---
> 	    rel->reltype = REL_SUBQUERY;
166a169,174
> 	else if (rte->rtetype == RTE_PORTAL)
> 	{
> 	    rel->reltype = REL_PORTAL;
> 	}
> 	else
> 	    elog(ERROR, "make_base_rel: Unknown RTE node type %d", nodeTag(rte) );
287a296
> 	joinrel->reltype = REL_JOIN;
295d303
< 	joinrel->issubquery = false;
Index: src/backend/parser/analyze.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/parser/analyze.c,v
retrieving revision 1.197
retrieving revision 1.198
diff -r1.197 -r1.198
172d171
< 
2020a2020
> 	RangeTblEntry* leftmostRTE;
2096c2096,2099
< 	leftmostQuery = rt_fetch(leftmostRTI, pstate->p_rtable)->subquery;
---
> 	leftmostRTE = rt_fetch(leftmostRTI, pstate->p_rtable);
> 	Assert(node && leftmostRTE->rtetype ==  RTE_SUBSELECT);
> 	leftmostQuery = leftmostRTE->u.sub.subquery;
> 
2366c2369
< 		Query	   *selectQuery = rte->subquery;
---
> 		Query	   *selectQuery = rte->u.sub.subquery;
2369a2373
> 		Assert(rte->rtetype == RTE_SUBSELECT);
2878c2882
< 			if (rte->subquery)
---
> 			if (rte->rtetype ==  RTE_SUBSELECT)
2881c2885
< 				transformForUpdate(rte->subquery, makeList1(NULL));
---
> 				transformForUpdate( rte->u.sub.subquery, makeList1(NULL));
2883c2887
< 			else
---
> 			else if ( rte->rtetype == RTE_RELATION )
2888a2893,2900
> 			else if ( rte->rtetype == RTE_PORTAL )
> 			{ 
>                /* do nothing */
> 			}
> 			else 
> 			{
> 				elog(ERROR, "FOR UPDATE: unknown RTE type %d", rte->rtetype);
> 			}
2906c2918
< 					if (rte->subquery)
---
> 					if (rte->rtetype == RTE_SUBSELECT) 
2909c2921
< 						transformForUpdate(rte->subquery, makeList1(NULL));
---
> 						transformForUpdate( rte->u.sub.subquery, makeList1(NULL));
2911c2923
< 					else
---
> 					else if (rte->rtetype == RTE_RELATION)
2915a2928,2936
> 					} 
> 					else if (rte->rtetype == RTE_PORTAL)
> 					{ 
>                			/* do nothing */
> 					}
> 					else 
> 					{
> 						elog(ERROR, "FOR UPDATE: unknown RTE type %d", 
> 								rte->rtetype);
Index: src/backend/parser/gram.y
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.249
diff -r2.249 gram.y
3726c3726,3735
< table_ref:  relation_expr
---
> table_ref:  
> 		CURSOR relation_name
> 				{
> 					PortalRangeVar *n = makeNode(PortalRangeVar);
> 					n->portal = $2;
> 					n->name = NULL;
> 					$$ = (Node *) n;
> 				}
> 	|
> 		relation_expr
5616d5624
< 		| CURSOR						{ $$ = "cursor"; }
Index: src/backend/parser/parse_clause.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/parser/parse_clause.c,v
retrieving revision 1.82
retrieving revision 1.83
diff -r1.82 -r1.83
33a34
> #include "utils/portal.h"
82a84
>          * FuncRangeVars, PortalRangeVars,
394a397,442
>  * transformPortalRange --- transform FROM CURSOR FOO 
>  */
> static RangeTblRef *
> transformPortalRange(ParseState *pstate, PortalRangeVar *r)
> {
> 	RangeTblEntry *rte;
> 	RangeTblRef *rtr;
> 	Portal      portal;
> 	QueryDesc  *queryDesc;
> 	TupleDesc  tupleDesc;
> 	EState     *estate;
> 	char 	   *portalname;
> 
>     /* look up information for the portal */
>     portalname = r->portal;
> 	portal = GetPortalByName(portalname);
> 	if (!PortalIsValid(portal))
> 		elog(ERROR, "transformPortalRange: portal \"%s\" not found",
> 			portalname);
> 
>     queryDesc = PortalGetQueryDesc(portal);
> 	tupleDesc = PortalGetTupleDesc(portal);
>     estate = PortalGetState(portal); /* XXX: check state ? */
> 
> 	if (queryDesc->operation != CMD_SELECT)
> 		elog(ERROR, "Expected SELECT query from the portal %s", portalname);
> 
> 	/*
> 	 * OK, build an RTE for the subquery.
> 	 */
> 	rte = addRangeTableEntryForPortal(pstate, portalname, r->name);
> 
> 	/*
> 	 * We create a RangeTblRef, but we do not add it to the joinlist or
> 	 * namespace; our caller must do that if appropriate.
> 	 */
> 	rtr = makeNode(RangeTblRef);
> 	/* assume new rte is at end */
> 	rtr->rtindex = length(pstate->p_rtable);
> 	Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
> 
> 	return rtr;
> }
> 
> 
> /*
482a531,539
> 		*containedRels = makeListi1(rtr->rtindex);
> 		return (Node *) rtr;
> 	}
> 	if (IsA(n, PortalRangeVar))
> 	{
> 		/* reference to cursor */
> 		RangeTblRef *rtr;
> 
> 		rtr = transformPortalRange(pstate, (PortalRangeVar *) n);
Index: src/backend/parser/parse_func.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/parser/parse_func.c,v
retrieving revision 1.110
retrieving revision 1.111
diff -r1.110 -r1.111
389c389,396
< 			if (rte->relname == NULL)
---
> 			if (rte->rtetype == RTE_PORTAL)
> 			{ 
>               /* RTE is a portal reference, possible? not supported yet */
> 		 		elog(ERROR, "Cannot pass tuple from portal %s to function %s",
> 						 refname, funcname);
>  			}
> 
> 			if (rte->rtetype == RTE_SUBSELECT)
410c417
< 			toid = typenameTypeId(rte->relname);
---
> 			toid = typenameTypeId( rte->u.rel.relname);
Index: src/backend/parser/parse_node.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/parser/parse_node.c,v
retrieving revision 1.55
retrieving revision 1.56
diff -r1.55 -r1.56
36a37
> #include "utils/portal.h"
173c174
< 	if (rte->relid != InvalidOid)
---
> 	if (rte->rtetype == RTE_RELATION)
180c181
< 							ObjectIdGetDatum(rte->relid),
---
> 							ObjectIdGetDatum(rte->u.rel.relid),
186c187
< 				 rte->relname, attrno);
---
> 				 rte->u.rel.relname, attrno);
192c193,206
< 	else
---
>     else if (rte->rtetype == RTE_PORTAL)
> 	{   /* Portal RTE, get type info from it */
> 		TupleDesc tupleDesc = 
> 			GetPortalTupleDescByName(rte->u.portal.portalname);
> 		Form_pg_attribute att_tup;
> 
> 		Assert( attrno <= (tupleDesc->natts) );
> 
> 		att_tup = tupleDesc->attrs[attrno-1];
> 
> 		vartypeid = att_tup->atttypid;
> 		type_mod = att_tup->atttypmod;
> 	}
> 	else if (rte->rtetype == RTE_SUBSELECT)
197c211
< 		foreach(tlistitem, rte->subquery->targetList)
---
> 		foreach(tlistitem, rte->u.sub.subquery->targetList)
210a225,228
> 	}
>  	else 
> 	{
> 			elog(ERROR, "make_var: Unknown RTE type");
Index: src/backend/parser/parse_relation.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/parser/parse_relation.c,v
retrieving revision 1.56
retrieving revision 1.57
diff -r1.56 -r1.57
30a31
> #include "utils/portal.h"
321c322
< 	if (rte->relid != InvalidOid)
---
> 	if (rte->rtetype == RTE_RELATION)
329c330
< 									 ObjectIdGetDatum(rte->relid),
---
> 									 ObjectIdGetDatum(rte->u.rel.relid),
513c514,515
< 	rte->relname = relname;
---
> 	rte->rtetype = RTE_RELATION;
> 	rte->u.rel.relname = relname;
515d516
< 	rte->subquery = NULL;
525c526
< 	rte->relid = RelationGetRelid(rel);
---
> 	rte->u.rel.relid = RelationGetRelid(rel);
580c581,649
< 	return rte;
---
> 	return (RangeTblEntry *) rte;
> }
> 
> /*
>  * Add an entry for a portal reference or for a function-used-as-table
>  *  to the pstate's range table (p_rtable).
>  *
>  * This is just like addRangeTableEntry() except that it makes a portal RTE.
>  */
> RangeTblEntry *
> addRangeTableEntryForPortal(ParseState *pstate,
> 							char *portalname,
> 						    Attr *alias
> 							)
> {
> 	RangeTblEntry *rte = makeNode(RangeTblEntry);
> 	char	   *refname = alias ? alias->relname : portalname;
> 	Attr	   *eref;
> 	int			maxattrs;
> 	int			numaliases;
> 	int			varattno;
> 	TupleDesc 	tupleDesc;
> 
> 	rte->rtetype = RTE_PORTAL;
> 	rte->u.portal.portalname = portalname;
> 	rte->alias = alias;
> 
> 	eref = alias ? (Attr *) copyObject(alias) : makeAttr(refname, NULL);
> 	numaliases = length(eref->attrs);
> 
> 	/* fill in any unspecified alias columns */
> 	varattno = 0;
> 
> 	tupleDesc = GetPortalTupleDescByName( portalname );
> 	maxattrs = tupleDesc->natts;
> 	if (maxattrs < numaliases)
> 		elog(ERROR, "Portal \"%s\" has %d columns available but %d aliases specified",
> 			 refname, maxattrs, numaliases);
> 
> 	/* fill in any unspecified alias columns */
> 	for (varattno = numaliases; varattno < maxattrs; varattno++)
> 	{
> 		char	   *attrname;
> 
> 		attrname = pstrdup(NameStr(tupleDesc->attrs[varattno]->attname));
> 		eref->attrs = lappend(eref->attrs, makeString(attrname));
> 	}
> 	rte->eref = eref;
> 
> 	/*----------
> 	 * Flags:
> 	 * - this RTE should be expanded to include descendant tables,
> 	 * - this RTE is in the FROM clause,
> 	 * - this RTE should be checked for read/write access rights.
> 	 */
> 	rte->inh = false;
> 	rte->inFromCl = true;
> 	rte->checkForRead = false;
> 	rte->checkForWrite = false;
> 	rte->checkAsUser = InvalidOid;		/* not set-uid by default, either */
> 
> 	/*
> 	 * Add completed RTE to pstate's range table list, but not to join
> 	 * list nor namespace --- caller must do that if appropriate.
> 	 */
> 	if (pstate != NULL)
> 		pstate->p_rtable = lappend(pstate->p_rtable, rte);
> 
> 	return (RangeTblEntry *) rte;
602,604c671,672
< 	rte->relname = NULL;
< 	rte->relid = InvalidOid;
< 	rte->subquery = subquery;
---
> 	rte->rtetype = RTE_SUBSELECT;
> 	rte->u.sub.subquery = subquery;
657c725
< 	return rte;
---
> 	return (RangeTblEntry *) rte;
759c827,863
< 	if (rte->relname)
---
> 	if (rte->rtetype == RTE_PORTAL)
> 	{
> 		/* Portal RTE */
> 		TupleDesc 	td;
> 		int			maxattrs;
>         td = GetPortalTupleDescByName( rte->u.portal.portalname );
> 
> 		maxattrs = td->natts;
> 
> 		for (varattno = 0; varattno < maxattrs; varattno++)
> 		{
> 			Form_pg_attribute attr = td->attrs[varattno];
> 
> 			if (colnames)
> 			{
> 				char	   *label;
> 
> 				if (varattno < length(rte->eref->attrs))
> 					label = strVal(nth(varattno, rte->eref->attrs));
> 				else
> 					label = NameStr(attr->attname);
> 				*colnames = lappend(*colnames, makeString(pstrdup(label)));
> 			}
> 
> 			if (colvars)
> 			{
> 				Var		   *varnode;
> 
> 				varnode = makeVar(rtindex, attr->attnum,
> 								  attr->atttypid, attr->atttypmod,
> 								  sublevels_up);
> 
> 				*colvars = lappend(*colvars, varnode);
> 			}
> 		}
> 	}
> 	else if (rte->rtetype == RTE_RELATION)
765c869
< 		rel = heap_openr(rte->relname, AccessShareLock);
---
> 		rel = heap_openr( rte->u.rel.relname, AccessShareLock);
803c907
< 	else
---
> 	else if (rte->rtetype == RTE_SUBSELECT)
810c914,915
< 		foreach(tlistitem, rte->subquery->targetList)
---
> 		Assert( PointerIsValid(rte->u.sub.subquery) );
> 		foreach(tlistitem,rte->u.sub.subquery->targetList)
840a946,949
>     else 
>     { 
>        elog(ERROR, "expandRTE: Unknown RTE type");
>     }
916c1025
<  *		Get an attribute name from a RangeTblEntry
---
>  *		Get an attribute name from a RangeTblEntryRelation
920c1029
<  * get_attname() only works on real relations.
---
>  * get_attname() only works on real relations. ??? (below)
928a1038
>  *
950c1060
< 	if (rte->relid == InvalidOid)
---
> 	if (!rte->rtetype == RTE_RELATION)
957c1067
< 	attname = get_attname(rte->relid, attnum);
---
> 	attname = get_attname(rte->u.rel.relid, attnum);
960c1070
< 			 attnum, rte->relid);
---
> 			 attnum, rte->u.rel.relid);
1071a1182,1200
> 
> /*
>  *              getrelid
>  *
>  *              Given the range index of a relation, return the corresponding
>  *              relation OID.  Note that InvalidOid will be returned if the
>  *              RTE is for not for a relation. (SubSelect or Portal)
>  */
> 
> /* this used to be a #define */
> Oid getrelid (int rangeindex, List * rangetable) {
> 	RangeTblEntry *rte = rt_fetch(rangeindex, rangetable);
> 	if (rte->rtetype == RTE_RELATION) {
> 		return rte->u.rel.relid;
>  	} else { 
> 		return InvalidOid;
> 	}
> }
> 
Index: src/backend/rewrite/rewriteDefine.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/rewrite/rewriteDefine.c,v
retrieving revision 1.63
retrieving revision 1.64
diff -r1.63 -r1.64
449c449
< 		if (rte->subquery)
---
> 		if ( rte->rtetype == RTE_SUBSELECT )
452c452
< 			setRuleCheckAsUser(rte->subquery, userid);
---
> 			setRuleCheckAsUser( rte->u.sub.subquery, userid);
Index: src/backend/rewrite/rewriteHandler.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/rewrite/rewriteHandler.c,v
retrieving revision 1.97
retrieving revision 1.98
diff -r1.97 -r1.98
268,269c268,269
< 	RangeTblEntry *rte,
< 			   *subrte;
---
> 	RangeTblEntry *rte;
> 	RangeTblEntry *subrte;
292,294c292,305
< 	rte->relname = NULL;
< 	rte->relid = InvalidOid;
< 	rte->subquery = rule_action;
---
> 	/* We have to mutate whatever RTE kind it was into RTESubSelect  */
> 	if ( rte->rtetype == RTE_SUBSELECT ) 
> 	{
> 		/* already was SubSelect, do nothing */
> 	} else if (rte->rtetype == RTE_RELATION) {
> 		/* this may be dangerous, as we are changing the type on the fly,
>            perhaps its a better idea to copy RTE, then mangle it? */
> 
> 		rte->rtetype = RTE_SUBSELECT;
> 	} else {
> 		elog(ERROR, "ApplyRetrieveRule: unknown original RTE type");
>  	}
> 
> 	rte->u.sub.subquery = rule_action;
302c313,314
< 	Assert(subrte->relid == relation->rd_id);
---
> 	Assert( subrte->rtetype == RTE_RELATION );
> 	Assert( subrte->u.rel.relid == relation->rd_id);
358c370
< 		if (rte->subquery)
---
> 		if (rte->rtetype == RTE_SUBSELECT )
361c373
< 			markQueryForUpdate(rte->subquery, false);
---
> 			markQueryForUpdate( rte->u.sub.subquery, false);
439a452,461
> 		 * A portal RTE can't have associated rules, so there's nothing
> 		 * to do to this level of the query.
> 		 */
> 
> 		if (rte->rtetype == RTE_PORTAL)
> 		{
> 			continue;
> 		}
> 
> 		/*
444c466
< 		if (rte->subquery)
---
> 		if (rte->rtetype == RTE_SUBSELECT)
446c468
< 			rte->subquery = fireRIRrules(rte->subquery);
---
> 			rte->u.sub.subquery = fireRIRrules(rte->u.sub.subquery);
461a484,485
> 		/* by now, we should only have plain RTE to deal with */
> 		Assert(rte->rtetype == RTE_RELATION);
482c506
< 		rel = heap_openr(rte->relname, lockmode);
---
> 		rel = heap_openr(rte->u.rel.relname, lockmode);
490c514
< 		if (RelationGetRelid(rel) != rte->relid)
---
> 		if (RelationGetRelid(rel) != rte->u.rel.relid)
492c516
< 				 rte->relname, rte->relid);
---
> 				 rte->u.rel.relname, rte->u.rel.relid);
759a784
> 	Assert( rt_entry->rtetype == RTE_RELATION );
769c794
< 	rt_entry_relation = heap_openr(rt_entry->relname, RowExclusiveLock);
---
> 	rt_entry_relation = heap_openr(rt_entry->u.rel.relname, RowExclusiveLock);
777c802
< 	if (RelationGetRelid(rt_entry_relation) != rt_entry->relid)
---
> 	if (RelationGetRelid(rt_entry_relation) != rt_entry->u.rel.relid)
779c804
< 			 rt_entry->relname, rt_entry->relid);
---
> 			 rt_entry->u.rel.relname, rt_entry->u.rel.relid);
946c971
< 			if (rte->subquery)
---
> 			if (rte->rtetype == RTE_SUBSELECT)
Index: src/backend/rewrite/rewriteManip.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/rewrite/rewriteManip.c,v
retrieving revision 1.57
retrieving revision 1.58
diff -r1.57 -r1.58
563c563,564
< 	selectquery = selectrte->subquery;
---
> 	Assert( selectrte->rtetype == RTE_SUBSELECT );
> 	selectquery = selectrte->u.sub.subquery;
574c575
< 			*subquery_ptr = &(selectrte->subquery);
---
> 			*subquery_ptr = &(selectrte->u.sub.subquery);
Index: src/backend/utils/adt/ruleutils.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/utils/adt/ruleutils.c,v
retrieving revision 1.82
retrieving revision 1.83
diff -r1.82 -r1.83
703,704c703,705
< 	rte->relname = relname;
< 	rte->relid = relid;
---
> 	rte->rtetype = RTE_RELATION;
> 	rte->u.rel.relname = relname;
> 	rte->u.rel.relid = relid;
1165,1166c1166,1167
< 		Query	   *subquery = rte->subquery;
< 
---
> 		Query	   *subquery = rte->u.sub.subquery;
> 		Assert( rte->rtetype == RTE_SUBSELECT );
1244c1245
< 	RangeTblEntry *select_rte = NULL;
---
> 	RangeTblEntry *select_rte = NULL; /* rte for underlying SELECT */
1256c1257
< 		if (rte->subquery == NULL)
---
> 		if (rte->rtetype != RTE_SUBSELECT)
1266a1268
> 	Assert( rte->rtetype == RTE_RELATION );
1268c1270
< 					 quote_identifier(rte->relname));
---
> 					 quote_identifier(rte->u.rel.relname));
1304c1306
< 		get_query_def(select_rte->subquery, buf, NIL);
---
> 		get_query_def(select_rte->u.sub.subquery, buf, NIL);
1323a1326,1327
> 
> 	Assert(rte->rtetype == RTE_RELATION);
1326c1330
< 					 quote_identifier(rte->relname));
---
> 					 quote_identifier(rte->u.rel.relname));
1376a1381
> 	Assert(rte->rtetype == RTE_RELATION);
1379c1384
< 					 quote_identifier(rte->relname));
---
> 					 quote_identifier(rte->u.rel.relname));
2428c2433
< 		if (rte->relname)
---
> 		if (rte->rtetype == RTE_RELATION)
2433c2438
< 							 quote_identifier(rte->relname));
---
> 				 quote_identifier( rte->u.rel.relname));
2435c2440
< 		else
---
> 		else if (rte->rtetype == RTE_SUBSELECT)
2438d2442
< 			Assert(rte->subquery != NULL);
2440c2444
< 			get_query_def(rte->subquery, buf, context->namespaces);
---
> 			get_query_def( rte->u.sub.subquery, buf, context->namespaces);
2442c2446,2453
< 		}
---
> 		} else if (rte->rtetype == RTE_PORTAL) 
> 		{
> 			appendStringInfo(buf, " CURSOR %s",
> 							 rte->u.portal.portalname);
> 		} else 
> 			elog(ERROR, "get_from_clause_item: unknown RTE type");
> 
> 
Index: src/backend/utils/mmgr/portalmem.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/utils/mmgr/portalmem.c,v
retrieving revision 1.41
retrieving revision 1.42
diff -r1.41 -r1.42
38a39,40
> #include "executor/execdefs.h"
> #include "executor/executor.h"
152a155,174
>  * GetPortalTupleDescByName
>  *		Returns a TupleDesc for portal given a portal name.
>  *      elog(ERROR) if it doesn't exist
>  */
> TupleDesc
> GetPortalTupleDescByName(char *name)
> {
> 	Portal		portal;
> 
> 	Assert( PointerIsValid(name) );
> 	PortalHashTableLookup(name, portal);
> 
>     if ( !PointerIsValid( portal ))
> 		elog( ERROR, 
> 				"GetPortalTupleDescByName: portal %s does not exist", name);
> 
> 	return portal->attinfo;
> }
> 
> /*
227a250,344
> }
> 
> /* Run the portal's underlying query for number of records, into dest
>  * properly setting context and atStart/atEnd flags after execution.
>  *
>  * If tts!=NULL, will put result of ExecutorRun into variable pointed by TTS
>  * 
>  * Returns number of records processed.
>  */
> int
> PortalRun(Portal portal, bool forward, long count, CommandDest dest, TupleTableSlot **tts)
> {
> 	QueryDesc  *queryDesc;
> 	EState     *estate;
> 	TupleTableSlot *tts_exec;
> 	bool        faked_desc = false;
> 	bool 		did_fetch = false; /* did we actually try to run executor? */
> 	MemoryContext oldcontext;
> 
> 	if (tts) *tts=NULL;
> 
> 	/*
> 	 * switch into the portal context
> 	 */
> 	oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
> 
> 	/*
> 	 * tell the destination to prepare to receive some tuples.
> 	 *
> 	 * If passed CommandDest  is not the same as portal's CommandDest, 
>      * make a temporary QueryDesc with the new destination.
> 	 */
> 	queryDesc = PortalGetQueryDesc(portal);
> 	estate = PortalGetState(portal);
> 
> 	if (dest != queryDesc->dest)
> 	{
> 		QueryDesc  *qdesc = (QueryDesc *) palloc(sizeof(QueryDesc));
> 
> 		memcpy(qdesc, queryDesc, sizeof(QueryDesc));
> 		qdesc->dest = dest;
> 		queryDesc = qdesc;
> 		faked_desc = true;
> 	}
> 
> 	/*
> 	 * Determine which direction to go in, and check to see if we're
> 	 * already at the end of the available tuples in that direction.  If
> 	 * so, do nothing.	(This check exists because not all plan node types
> 	 * are robust about being called again if they've already returned
> 	 * NULL once.)	If it's OK to do the fetch, call the executor.  Then,
> 	 * update the atStart/atEnd state depending on the number of tuples
> 	 * that were retrieved.
> 	 */
> 	if (forward)
> 	{
> 		if (!portal->atEnd)
> 		{
> 			tts_exec=ExecutorRun(queryDesc, estate, EXEC_FOR, (long) count);
> 			if (estate->es_processed > 0)
> 				portal->atStart = false;		/* OK to back up now */
> 			if (count <= 0 || (int) estate->es_processed < count)
> 				portal->atEnd = true;	/* we retrieved 'em all */
> 			did_fetch = true;
> 		}
> 	}
> 	else
> 	{
> 		if (!portal->atStart)
> 		{
> 			tts_exec=ExecutorRun(queryDesc, estate, EXEC_BACK, (long) count);
> 			if (estate->es_processed > 0)
> 				portal->atEnd = false;	/* OK to go forward now */
> 			if (count <= 0 || (int) estate->es_processed < count)
> 				portal->atStart = true; /* we retrieved 'em all */
> 			did_fetch = true;
> 		}
> 	}
> 
> 	/*
> 	 * Clean up and switch back to old context.
> 	 */
> 	if (faked_desc)			/* MOVE */
> 		pfree(queryDesc);
> 
> 	MemoryContextSwitchTo(oldcontext);
> 
> 	/* store what we got from Executor */
> 	if (tts && did_fetch) 
> 		*tts=tts_exec;
> 
> 	/* check whether we actually retrieved records */
> 	if (did_fetch)
> 		return estate->es_processed;
> 	return 0;
Index: src/include/nodes/execnodes.h
===================================================================
RCS file: /cvs/pgsql/pgsql/src/include/nodes/execnodes.h,v
retrieving revision 1.62
retrieving revision 1.63
diff -r1.62 -r1.63
483a484,496
> /* ----------------
>  *	 PortalScanState information
>  *
>  *		PortalScanState is used for scanning a portal.
>  *
>  * ----------------
>  */
> typedef struct PortalScanState
> {
> 	CommonScanState csstate;	/* its first field is NodeTag */
> 	struct PortalData	   * portal;
> } PortalScanState;
> 
Index: src/include/nodes/nodes.h
===================================================================
RCS file: /cvs/pgsql/pgsql/src/include/nodes/nodes.h,v
retrieving revision 1.93
retrieving revision 1.94
diff -r1.93 -r1.94
51a52
> 	T_PortalScan,
122a124
> 	T_PortalScanState,
224a227,228
> /*	T_FuncRangeVar, */
> 	T_PortalRangeVar, 
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /cvs/pgsql/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.143
retrieving revision 1.144
diff -r1.143 -r1.144
17a18,19
> #include "access/tupdesc.h"
> 
891a894
> 	bool 	isFunc; 		/* select * from func(args) */
1183a1187,1208
>  * FuncRangeVar - range variable, used in FROM clauses
>  *                select x,y from foo(1) as bar(x,y)
>  */
> typedef struct FuncRangeVar
> {
> 	NodeTag		type;
>   	Node 	*func;
> 	Attr	   *name;	/* optional table alias & column aliases */
> } FuncRangeVar;
> 
> /*
>  * PortalRangeVar - range variable, used in FROM clauses
>  *                select x,y from cursor foo
>  */
> typedef struct PortalRangeVar
> {
> 	NodeTag		type;
>   	char 	*portal;
> 	Attr	*name;			/* optional table alias & column aliases */
> } PortalRangeVar;
> 
> /*
1289a1315,1320
> typedef enum RTEType {
> 	RTE_RELATION,
> 	RTE_SUBSELECT,
> 	RTE_PORTAL
> } RTEType;
> 
1293,1304c1324
< 
< 	/*
< 	 * Fields valid for a plain relation RTE (else NULL/zero):
< 	 */
< 	char	   *relname;		/* real name of the relation */
< 	Oid			relid;			/* OID of the relation */
< 
< 	/*
< 	 * Fields valid for a subquery RTE (else NULL):
< 	 */
< 	Query	   *subquery;		/* the sub-query */
< 
---
>     RTEType 	rtetype;
1314a1335,1350
> 
>     union {
> 		struct  {
> 			/* Fields for a plain relation RTE (rtetype=RTE_RELATION) */
> 			char	   *relname;		/* real name of the relation */
> 			Oid			relid;			/* OID of the relation */
> 		} rel;
>  		struct {
> 			/* Fields for a subquery RTE (rtetype=RTE_SUBSELECT) */
> 			Query	   *subquery;		/* the sub-query */
> 		} sub;
> 		struct { 
>  			/* fields for portal RTE (rtetype=RTE_PORTAL) */
>     		char 		  *portalname;    /* portal's name */
> 		} portal;
>  	} u;
Index: src/include/nodes/plannodes.h
===================================================================
RCS file: /cvs/pgsql/pgsql/src/include/nodes/plannodes.h,v
retrieving revision 1.49
retrieving revision 1.50
diff -r1.49 -r1.50
232a233,243
> /* ----------------
>  *		portal scan node
>  *
>  * PortalScan is for scanning the existing portal. Will need additional 
>  * fields, but currently, its just the Scan.
>  */
> typedef struct PortalScan
> {
> 	Scan		scan;
> } PortalScan;
> 
Index: src/include/nodes/relation.h
===================================================================
RCS file: /cvs/pgsql/pgsql/src/include/nodes/relation.h,v
retrieving revision 1.58
retrieving revision 1.59
diff -r1.58 -r1.59
131a132,138
> typedef enum RelOptInfoType {
>   REL_PLAIN, 
>   REL_SUBQUERY,
>   REL_JOIN,
>   REL_PORTAL
> } RelOptInfoType;
> 
135a143,144
> 	RelOptInfoType		reltype;
> 
151,152c160
< 	/* information about a base rel (not set for join rels!) */
< 	bool		issubquery;
---
> 	/* Following fields are only for REL_PLAIN */
155a164,165
> 
> 	/* following field is only for REL_SUBQUERY */
Index: src/include/optimizer/pathnode.h
===================================================================
RCS file: /cvs/pgsql/pgsql/src/include/optimizer/pathnode.h,v
retrieving revision 1.38
retrieving revision 1.39
diff -r1.38 -r1.39
17a18
> #include "utils/portal.h"
37a39
> extern Path *create_portalscan_path(RelOptInfo *rel, Portal portal);
Index: src/include/parser/parse_relation.h
===================================================================
RCS file: /cvs/pgsql/pgsql/src/include/parser/parse_relation.h,v
retrieving revision 1.24
retrieving revision 1.25
diff -r1.24 -r1.25
38a39,42
> extern RangeTblEntry * addRangeTableEntryForPortal(ParseState *pstate,
>                             char *portalname,
>                             Attr *alias
>                             );
Index: src/include/parser/parsetree.h
===================================================================
RCS file: /cvs/pgsql/pgsql/src/include/parser/parsetree.h,v
retrieving revision 1.13
retrieving revision 1.14
diff -r1.13 -r1.14
39,47c39,40
< /*
<  *		getrelid
<  *
<  *		Given the range index of a relation, return the corresponding
<  *		relation OID.  Note that InvalidOid will be returned if the
<  *		RTE is for a sub-select rather than a relation.
<  */
< #define getrelid(rangeindex,rangetable) \
< 	(rt_fetch(rangeindex, rangetable)->relid)
---
> /* getrelid is no longer a #define */
> extern Oid getrelid (int rangeindex, List * rangetable);
Index: src/include/utils/portal.h
===================================================================
RCS file: /cvs/pgsql/pgsql/src/include/utils/portal.h,v
retrieving revision 1.28
retrieving revision 1.29
diff -r1.28 -r1.29
65a66
> extern TupleDesc GetPortalTupleDescByName(char *name);
69c70,71
< 
---
> extern int PortalRun(Portal portal, bool forward, 
> 					long count, CommandDest dest, TupleTableSlot **tts);
Index: src/include/executor/nodePortalscan.h
===================================================================
RCS file: nodePortalscan.h
diff -N nodePortalscan.h
0a1,27
> /*-------------------------------------------------------------------------
>  *
>  * nodePortalscan.h
>  *
>  *
>  *
>  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
>  * Portions Copyright (c) 1994, Regents of the University of California
>  *
>  * $Id$
>  *
>  *-------------------------------------------------------------------------
>  */
> #ifndef NODEPORTALSCAN_H
> #define NODEPORTALSCAN_H
> 
> #include "nodes/plannodes.h"
> 
> extern TupleTableSlot *ExecPortalScan(PortalScan *node);
> extern bool ExecInitPortalScan(PortalScan *node, EState *estate, Plan *parent);
> extern int	ExecCountSlotsPortalScan(PortalScan *node);
> extern void ExecEndPortalScan(PortalScan *node);
> extern void ExecPortalReScan(PortalScan *node, ExprContext *exprCtxt, Plan *parent);
> extern void ExecPortalMarkPos(PortalScan *node);
> extern void ExecPortalRestrPos(PortalScan *node);
> 
> #endif	 /* NODESEQSCAN_H */
#2Bruce Momjian
pgman@candle.pha.pa.us
In reply to: Alex Pilosov (#1)
Re: [PATCH] [LARGE] select * from cursor foo

Your patch has been added to the PostgreSQL unapplied patches list at:

http://candle.pha.pa.us/cgi-bin/pgpatches

I will try to apply it within the next 48 hours.

Attached patch does the above.

Notes:
1. Incompatible changes: CURSOR is now a keyword and may not be used as an
identifier (tablename, etc). Otherwise, we get shift-reduce conflicts in
grammar.

2. Major changes:

a) RangeTblEntry (RTE for short) instead of having two possibilities,
subquery and non-subquery, now has a rtetype field which can be of 3
possible states: RTE_RELATION, RTE_SUBSELECT, RTE_PORTAL). The
type-specific structures are unionized, so where you used to have
rte->relid, now you must do rte->u.rel.relid.

Proper way to check what is the RTE type is now checking for rte->rtetype
instead of checking whether rte->subquery is null.

b) Similarly, RelOptInfo now has a RelOptInfoType which is an enum with 4
states, REL_PLAIN,REL_SUBQUERY,REL_JOIN,REL_PORTAL. I did not do the
unionization of type-specific structures. Maybe I should've if I'm going
to get in a big change anyway.

c) There's a function PortalRun which fetches N records from portal and
sets atEnd/atStart values properly. It replaces code duplicated in 2
places.

How to test:

declare foo cursor for select * from pg_class;

select * from cursor foo;

Documentation updates will be forthcoming ASAP, I just wanted to get this
patch in queue before the freeze. Or at least making sure someone could
look through this patch before freeze. :)

Next patch will be one to support "SELECT * FROM func(arg1,arg2)" which
would work by creating first a special kind of portal for selection from a
function and then setting query source to be that portal.

-alex

Content-Description:

[ Attachment, skipping... ]

---------------------------(end of broadcast)---------------------------
TIP 5: Have you checked our extensive FAQ?

http://www.postgresql.org/users-lounge/docs/faq.html

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 853-3000
  +  If your life is a hard drive,     |  830 Blythe Avenue
  +  Christ can be your backup.        |  Drexel Hill, Pennsylvania 19026
#3Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alex Pilosov (#1)
Re: [PATCH] [LARGE] select * from cursor foo

Alex Pilosov <alex@pilosoft.com> writes:

Attached patch does the above.

Alex, could we have this resubmitted in "diff -c" format? Plain diff
format is way too risky to apply.

regards, tom lane

#4Alex Pilosov
alex@pilosoft.com
In reply to: Tom Lane (#3)
Re: [PATCH] [LARGE] select * from cursor foo

On Mon, 17 Sep 2001, Tom Lane wrote:

Alex Pilosov <alex@pilosoft.com> writes:

Attached patch does the above.

Alex, could we have this resubmitted in "diff -c" format? Plain diff
format is way too risky to apply.

Tom,

postgresql.org cvsup repository is broken (and according to my records,
been so for last 4 days at least). Unfortunately, I can't get my changes
in correct format unless that gets fixed...So I guess that'll go in 7.3 :(

(error I'm getting is this:
Server message: Collection "pgsql" release "cvs" is not available here
which leads me to assume a misconfiguration somewhere).

That is, unless you make an exception for a few days and cvsup gets fixed
in meantime :P)

CVS repository also seems broken right now, I'm unable to log in (cvs
login: authorization failed: server cvs.postgresql.org rejected access to
/home/projects/pgsql/cvsroot for user anoncvs) in both cvs.postgresql.org
and anoncvs.postgresql.org with all possible anoncvs passwords (empty,
anoncvs, postgresql). Or had the anoncvs password been changed, or I
missed an announcement?

--
Alex Pilosov | http://www.acedsl.com/home.html
CTO - Acecape, Inc. | AceDSL:The best ADSL in the world
325 W 38 St. Suite 1005 | (Stealth Marketing Works! :)
New York, NY 10018 |

#5Alex Pilosov
alex@pilosoft.com
In reply to: Alex Pilosov (#4)
CVS/CVSup problems (was Re: [PATCH] [LARGE] )

On Thu, 20 Sep 2001, Alex Pilosov wrote:

CVS repository also seems broken right now, I'm unable to log in (cvs
login: authorization failed: server cvs.postgresql.org rejected access
to /home/projects/pgsql/cvsroot for user anoncvs) in both
cvs.postgresql.org and anoncvs.postgresql.org with all possible
anoncvs passwords (empty, anoncvs, postgresql). Or had the anoncvs
password been changed, or I missed an announcement?

Augh. A minute later, I find the announcement, but I still have problem,
after logging in, I have:

tick-bash# cvs -d :pserver:anoncvs@anoncvs.postgresql.org:/projects/cvsroot co pgsql
cvs server: Updating pgsql
cvs server: failed to create lock directory for `/projects/cvsroot/pgsql'
(/projects/cvsroot/pgsql/#cvs.lock): Permission denied
cvs server: failed to obtain dir lock in repository
`/projects/cvsroot/pgsql'
cvs [server aborted]: read lock failed - giving up

A minute later:
cannot create_adm_p /tmp/cvs-serv8577/pgsql

...:(

#6Thomas Lockhart
lockhart@fourpalms.org
In reply to: Alex Pilosov (#4)
Re: [PATCH] [LARGE] select * from cursor foo

postgresql.org cvsup repository is broken (and according to my records,
been so for last 4 days at least). Unfortunately, I can't get my changes
in correct format unless that gets fixed...So I guess that'll go in 7.3 :(

The beta release schedule is on hold until we can get access. I've
wasted a few days tracking down a problem which turns out to have been
fixed in cvs, but since I don't have CVSup access (just like you) I
can't get the updates. And I can't finish testing and can't finish
updating the regression suite, etc etc.

- Thomas

#7Alex Pilosov
alex@pilosoft.com
In reply to: Tom Lane (#3)
1 attachment(s)
Re: [PATCH] [LARGE] select * from cursor foo

On Mon, 17 Sep 2001, Tom Lane wrote:

Alex Pilosov <alex@pilosoft.com> writes:

Attached patch does the above.

Alex, could we have this resubmitted in "diff -c" format? Plain diff
format is way too risky to apply.

Resubmitted. In case list server chokes on the attachment's size, it is
also available on www.formenos.org/pg/cursor.fix5.diff

Thanks
-alex

Attachments:

cursor.fix5.difftext/plain; charset=US-ASCII; name=cursor.fix5.diffDownload
Index: src/backend/commands/command.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/commands/command.c,v
retrieving revision 1.142
diff --unified -r1.142 command.c
--- src/backend/commands/command.c	2001/09/07 21:57:53	1.142
+++ src/backend/commands/command.c	2001/09/21 17:32:46
@@ -101,6 +101,7 @@
 				   CommandDest dest)
 {
 	Portal		portal;
+	int 		result;
 	QueryDesc  *queryDesc;
 	EState	   *estate;
 	MemoryContext oldcontext;
@@ -169,48 +170,10 @@
 				 tag,
 				 queryDesc->dest);
 
-	/*
-	 * Determine which direction to go in, and check to see if we're
-	 * already at the end of the available tuples in that direction.  If
-	 * so, do nothing.	(This check exists because not all plan node types
-	 * are robust about being called again if they've already returned
-	 * NULL once.)	If it's OK to do the fetch, call the executor.  Then,
-	 * update the atStart/atEnd state depending on the number of tuples
-	 * that were retrieved.
-	 */
-	if (forward)
-	{
-		if (!portal->atEnd)
-		{
-			ExecutorRun(queryDesc, estate, EXEC_FOR, (long) count);
-			/*
-			 *	I use CMD_UPDATE, because no CMD_MOVE or the like
-			 *	exists, and I would like to provide the same
-			 *	kind of info as CMD_UPDATE
-			 */
-			UpdateCommandInfo(CMD_UPDATE, 0, estate->es_processed);
-			if (estate->es_processed > 0)
-				portal->atStart = false;		/* OK to back up now */
-			if (count <= 0 || (int) estate->es_processed < count)
-				portal->atEnd = true;	/* we retrieved 'em all */
-		}
-	}
-	else
-	{
-		if (!portal->atStart)
-		{
-			ExecutorRun(queryDesc, estate, EXEC_BACK, (long) count);
-			/*
-			 *	I use CMD_UPDATE, because no CMD_MOVE or the like
-			 *	exists, and I would like to provide the same
-			 *	kind of info as CMD_UPDATE
-			 */
-			UpdateCommandInfo(CMD_UPDATE, 0, estate->es_processed);
-			if (estate->es_processed > 0)
-				portal->atEnd = false;	/* OK to go forward now */
-			if (count <= 0 || (int) estate->es_processed < count)
-				portal->atStart = true; /* we retrieved 'em all */
-		}
+	result = PortalRun( portal, forward, (long) count, queryDesc->dest, NULL );
+
+  	if (result > 0) {
+		UpdateCommandInfo(CMD_UPDATE, 0, result);
 	}
 
 	/*
Index: src/backend/commands/explain.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/commands/explain.c,v
retrieving revision 1.66
diff --unified -r1.66 explain.c
--- src/backend/commands/explain.c	2001/09/18 01:59:06	1.66
+++ src/backend/commands/explain.c	2001/09/21 17:32:46
@@ -218,6 +218,9 @@
 		case T_SeqScan:
 			pname = "Seq Scan";
 			break;
+		case T_PortalScan:
+			pname = "Portal Scan";
+			break;
 		case T_IndexScan:
 			pname = "Index Scan";
 			break;
@@ -296,15 +299,16 @@
 		case T_TidScan:
 			if (((Scan *) plan)->scanrelid > 0)
 			{
-				RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
+				RangeTblEntry *rte = (RangeTblEntry*)
+							rt_fetch(((Scan *) plan)->scanrelid,
 											  es->rtable);
 
 				/* Assume it's on a real relation */
-				Assert(rte->relname);
+				Assert( rte->rtetype == RTE_RELATION );
 
 				appendStringInfo(str, " on %s",
-								 stringStringInfo(rte->relname));
-				if (strcmp(rte->eref->relname, rte->relname) != 0)
+								 stringStringInfo(rte->u.rel.relname));
+				if (strcmp(rte->eref->relname, rte->u.rel.relname) != 0)
 					appendStringInfo(str, " %s",
 								   stringStringInfo(rte->eref->relname));
 			}
@@ -401,12 +405,15 @@
 	{
 		SubqueryScan *subqueryscan = (SubqueryScan *) plan;
 		Plan	   *subnode = subqueryscan->subplan;
-		RangeTblEntry *rte = rt_fetch(subqueryscan->scan.scanrelid,
+
+		RangeTblEntry *rte = (RangeTblEntry *)
+									rt_fetch(subqueryscan->scan.scanrelid,
 									  es->rtable);
+
 		List	   *saved_rtable = es->rtable;
 
-		Assert(rte->subquery != NULL);
-		es->rtable = rte->subquery->rtable;
+		Assert(rte->rtetype == RTE_SUBSELECT);
+		es->rtable = rte->u.sub.subquery->rtable;
 
 		for (i = 0; i < indent; i++)
 			appendStringInfo(str, "  ");
Index: src/backend/executor/Makefile
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/executor/Makefile,v
retrieving revision 1.17
diff --unified -r1.17 Makefile
--- src/backend/executor/Makefile	2001/09/18 01:59:06	1.17
+++ src/backend/executor/Makefile	2001/09/21 17:32:46
@@ -16,7 +16,8 @@
        execProcnode.o execQual.o execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o nodeHash.o \
        nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
-       nodeNestloop.o nodeResult.o nodeSeqscan.o nodeSetOp.o nodeSort.o \
+       nodeNestloop.o nodePortalscan.o nodeResult.o nodeSeqscan.o \
+       nodeSetOp.o nodeSort.o \
        nodeUnique.o nodeLimit.o nodeGroup.o nodeSubplan.o \
        nodeSubqueryscan.o nodeTidscan.o spi.o
 
Index: src/backend/executor/execAmi.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/executor/execAmi.c,v
retrieving revision 1.59
diff --unified -r1.59 execAmi.c
--- src/backend/executor/execAmi.c	2001/09/18 01:59:06	1.59
+++ src/backend/executor/execAmi.c	2001/09/21 17:32:47
@@ -43,6 +43,7 @@
 #include "executor/nodeMergejoin.h"
 #include "executor/nodeNestloop.h"
 #include "executor/nodeResult.h"
+#include "executor/nodePortalscan.h"
 #include "executor/nodeSeqscan.h"
 #include "executor/nodeSetOp.h"
 #include "executor/nodeSort.h"
@@ -186,6 +187,10 @@
 			state = ((SeqScan *) node)->scanstate;
 			break;
 
+		case T_PortalScan:
+			state = ((PortalScan *) node)->scan.scanstate;
+			break;
+
 		case T_IndexScan:
 			state = ((IndexScan *) node)->scan.scanstate;
 			break;
@@ -296,6 +301,10 @@
 	{
 		case T_SeqScan:
 			ExecSeqReScan((SeqScan *) node, exprCtxt, parent);
+			break;
+
+		case T_PortalScan:
+			ExecPortalReScan((PortalScan *) node, exprCtxt, parent);
 			break;
 
 		case T_IndexScan:
Index: src/backend/executor/execMain.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/executor/execMain.c,v
retrieving revision 1.148
diff --unified -r1.148 execMain.c
--- src/backend/executor/execMain.c	2001/09/18 01:59:06	1.148
+++ src/backend/executor/execMain.c	2001/09/21 17:32:47
@@ -345,8 +345,9 @@
 
 				/* Recursively check the subquery */
 				rte = rt_fetch(scan->scan.scanrelid, rangeTable);
-				Assert(rte->subquery != NULL);
-				ExecCheckQueryPerms(operation, rte->subquery, scan->subplan);
+				Assert(rte->rtetype == RTE_SUBSELECT);
+				ExecCheckQueryPerms(operation, rte->u.sub.subquery, 
+									scan->subplan);
 				break;
 			}
 		case T_Append:
@@ -400,10 +401,10 @@
 	 * If it's a subquery RTE, ignore it --- it will be checked when
 	 * ExecCheckPlanPerms finds the SubqueryScan node for it.
 	 */
-	if (rte->subquery)
-		return;
+	if (!rte->rtetype == RTE_RELATION) return;
 
-	relName = rte->relname;
+    
+	relName = rte->u.rel.relname;
 
 	/*
 	 * userid to check as: current user unless we have a setuid
Index: src/backend/executor/execProcnode.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/executor/execProcnode.c,v
retrieving revision 1.27
diff --unified -r1.27 execProcnode.c
--- src/backend/executor/execProcnode.c	2001/09/18 01:59:06	1.27
+++ src/backend/executor/execProcnode.c	2001/09/21 17:32:47
@@ -90,6 +90,7 @@
 #include "executor/nodeMaterial.h"
 #include "executor/nodeMergejoin.h"
 #include "executor/nodeNestloop.h"
+#include "executor/nodePortalscan.h"
 #include "executor/nodeResult.h"
 #include "executor/nodeSeqscan.h"
 #include "executor/nodeSetOp.h"
@@ -168,6 +169,11 @@
 										  parent);
 			break;
 
+		case T_PortalScan:
+			result = ExecInitPortalScan((PortalScan *) node, estate,
+										  parent);
+			break;
+
 			/*
 			 * join nodes
 			 */
@@ -297,6 +303,10 @@
 			result = ExecSubqueryScan((SubqueryScan *) node);
 			break;
 
+		case T_PortalScan:
+			result = ExecPortalScan((PortalScan *) node);
+			break;
+
 			/*
 			 * join nodes
 			 */
@@ -392,6 +402,9 @@
 		case T_SubqueryScan:
 			return ExecCountSlotsSubqueryScan((SubqueryScan *) node);
 
+		case T_PortalScan:
+			return ExecCountSlotsPortalScan((PortalScan *) node);
+
 			/*
 			 * join nodes
 			 */
@@ -503,6 +516,10 @@
 			ExecEndSubqueryScan((SubqueryScan *) node);
 			break;
 
+		case T_PortalScan:
+			ExecEndPortalScan((PortalScan *) node);
+			break;
+
 			/*
 			 * join nodes
 			 */
@@ -635,6 +652,14 @@
 		case T_SubqueryScan:
 			{
 				CommonScanState *scanstate = ((SubqueryScan *) node)->scan.scanstate;
+
+				slot = scanstate->cstate.cs_ResultTupleSlot;
+			}
+			break;
+
+		case T_PortalScan:
+			{
+				CommonScanState *scanstate = ((PortalScan *) node)->scan.scanstate;
 
 				slot = scanstate->cstate.cs_ResultTupleSlot;
 			}
Index: src/backend/executor/nodeIndexscan.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/executor/nodeIndexscan.c,v
retrieving revision 1.62
diff --unified -r1.62 nodeIndexscan.c
--- src/backend/executor/nodeIndexscan.c	2001/07/15 22:48:17	1.62
+++ src/backend/executor/nodeIndexscan.c	2001/09/21 17:32:47
@@ -988,7 +988,9 @@
 	 */
 	relid = node->scan.scanrelid;
 	rtentry = rt_fetch(relid, rangeTable);
-	reloid = rtentry->relid;
+    Assert(rtentry->rtetype == RTE_RELATION);
+
+	reloid = rtentry->u.rel.relid;
 
 	ExecOpenScanR(reloid,		/* relation */
 				  0,			/* nkeys */
Index: src/backend/executor/nodeSeqscan.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/executor/nodeSeqscan.c,v
retrieving revision 1.31
diff --unified -r1.31 nodeSeqscan.c
--- src/backend/executor/nodeSeqscan.c	2001/07/15 22:48:18	1.31
+++ src/backend/executor/nodeSeqscan.c	2001/09/21 17:32:47
@@ -159,8 +159,11 @@
 	 */
 	relid = node->scanrelid;
 	rangeTable = estate->es_range_table;
+
 	rtentry = rt_fetch(relid, rangeTable);
-	reloid = rtentry->relid;
+    Assert(rtentry->rtetype == RTE_RELATION);
+
+	reloid = rtentry->u.rel.relid;
 	direction = estate->es_direction;
 
 	ExecOpenScanR(reloid,		/* relation */
Index: src/backend/executor/nodeSubqueryscan.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/executor/nodeSubqueryscan.c,v
retrieving revision 1.10
diff --unified -r1.10 nodeSubqueryscan.c
--- src/backend/executor/nodeSubqueryscan.c	2001/09/18 01:59:06	1.10
+++ src/backend/executor/nodeSubqueryscan.c	2001/09/21 17:32:47
@@ -147,12 +147,13 @@
 	 * This should agree with ExecInitSubPlan
 	 */
 	rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
-	Assert(rte->subquery != NULL);
+    Assert(rte->rtetype == RTE_SUBSELECT);
 
 	sp_estate = CreateExecutorState();
 	subquerystate->sss_SubEState = sp_estate;
 
-	sp_estate->es_range_table = rte->subquery->rtable;
+    Assert( PointerIsValid(rte->u.sub.subquery) );
+	sp_estate->es_range_table = rte->u.sub.subquery->rtable;
 	sp_estate->es_param_list_info = estate->es_param_list_info;
 	sp_estate->es_param_exec_vals = estate->es_param_exec_vals;
 	sp_estate->es_tupleTable =
Index: src/backend/executor/nodeTidscan.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/executor/nodeTidscan.c,v
retrieving revision 1.18
diff --unified -r1.18 nodeTidscan.c
--- src/backend/executor/nodeTidscan.c	2001/06/22 19:16:22	1.18
+++ src/backend/executor/nodeTidscan.c	2001/09/21 17:32:47
@@ -458,9 +458,11 @@
 	 * open the base relation
 	 */
 	relid = node->scan.scanrelid;
+
 	rtentry = rt_fetch(relid, rangeTable);
-	reloid = rtentry->relid;
+    Assert(rtentry->rtetype == RTE_RELATION);
 
+	reloid = rtentry->u.rel.relid;
 	currentRelation = heap_open(reloid, AccessShareLock);
 	scanstate->css_currentRelation = currentRelation;
 	scanstate->css_currentScanDesc = 0;
Index: src/backend/executor/spi.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/executor/spi.c,v
retrieving revision 1.57
diff --unified -r1.57 spi.c
--- src/backend/executor/spi.c	2001/08/02 18:08:43	1.57
+++ src/backend/executor/spi.c	2001/09/21 17:32:47
@@ -1135,10 +1135,8 @@
 _SPI_cursor_operation(Portal portal, bool forward, int count,
 					CommandDest dest)
 {
-    QueryDesc	   *querydesc;
-	EState		   *estate;
-	MemoryContext	oldcontext;
-	CommandDest		olddest;
+	int nrows; /* how many records portal returned */
+	
 
 	/* Check that the portal is valid */
 	if (!PortalIsValid(portal))
@@ -1153,46 +1151,9 @@
 	_SPI_current->processed = 0;
 	_SPI_current->tuptable = NULL;
 
-	/* Switch to the portals memory context */
-	oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
-	querydesc  = PortalGetQueryDesc(portal);
-	estate     = PortalGetState(portal);
+	nrows = PortalRun ( portal, forward, (long) count, dest, NULL);
 
-	/* 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);
+	_SPI_current->processed = nrows;
 
 	if (dest == SPI && _SPI_checktuples())
 		elog(FATAL, "SPI_fetch: # of processed tuples check failed");
Index: src/backend/executor/nodePortalscan.c
===================================================================
RCS file: nodePortalscan.c
diff -N nodePortalscan.c
--- /dev/null	Fri Sep 21 14:39:51 2001
+++ nodePortalscan.c	Fri Sep 21 13:38:15 2001
@@ -0,0 +1,258 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodePortalscan.c
+ *	  Support routines for scanning portals (SELECT * FROM CURSOR FOO).
+ *
+ *
+ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *		ExecPortalScan			scans a portal.
+ *		ExecPortalNext			retrieve next tuple in sequential order.
+ *		ExecInitPortalScan		creates and initializes a portalscan node.
+ *		ExecEndPortalScan			releases any storage allocated.
+ *		ExecPortalReScan			rescans the relation
+ *
+ */
+#include "postgres.h"
+
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/execdefs.h"
+#include "executor/execdesc.h"
+#include "executor/nodePortalscan.h"
+#include "parser/parsetree.h"
+#include "tcop/pquery.h"
+
+static TupleTableSlot *PortalNext(PortalScan *node);
+
+/* ----------------------------------------------------------------
+ *						Scan Support
+ * ----------------------------------------------------------------
+ */
+/* ----------------------------------------------------------------
+ *		PortalNext
+ *
+ *		This is a workhorse for ExecPortalScan
+ * ----------------------------------------------------------------
+ */
+static TupleTableSlot *
+PortalNext(PortalScan *node)
+{
+	PortalScanState *portalstate;
+	EState	   *estate;
+	ScanDirection direction;
+	TupleTableSlot *slot;
+	bool 		forward;
+	Portal		portal;
+	int 		ntuples;
+
+	/*
+	 * get information from the estate and scan state
+	 */
+	estate = node->scan.plan.state;
+	portalstate = (PortalScanState *) node->scan.scanstate;
+	direction = estate->es_direction;
+	forward = ScanDirectionIsForward(direction);
+	portal = portalstate->portal;
+
+	/* 
+	 * get the next tuple from Portal
+	 */
+
+	ntuples = PortalRun(portal, forward, (long) 1, NULL, &slot);
+
+	portalstate->csstate.css_ScanTupleSlot = slot;
+
+	return slot;
+}
+
+/* ----------------------------------------------------------------
+ *		ExecPortalScan(node)
+ *
+ *		Scans the portal sequentially and returns the next qualifying
+ *		tuple.
+ *		It calls the ExecScan() routine and passes it the access method
+ *		which retrieve tuples sequentially.
+ *
+ */
+
+TupleTableSlot *
+ExecPortalScan(PortalScan *node)
+{
+	/*
+	 * use PortalNext as access method
+	 */
+	return ExecScan(&node->scan, (ExecScanAccessMtd) PortalNext);
+}
+
+/* ----------------------------------------------------------------
+ *		ExecInitPortalScan
+ * ----------------------------------------------------------------
+ */
+bool
+ExecInitPortalScan(PortalScan *node, EState *estate, Plan *parent)
+{
+	PortalScanState *portalstate;
+	RangeTblEntry *rte;
+	Portal		portal;
+
+	/*
+	 * PortalScan should not have any "normal" children.
+	 */
+	Assert(outerPlan((Plan *) node) == NULL);
+	Assert(innerPlan((Plan *) node) == NULL);
+
+	/*
+	 * assign the node's execution state
+	 */
+	node->scan.plan.state = estate;
+
+	/*
+	 * create new PortalScanState for node
+	 */
+	portalstate = makeNode(PortalScanState);
+	node->scan.scanstate = (CommonScanState *) portalstate;
+
+	/*
+	 * Miscellaneous initialization
+	 *
+	 * create expression context for node
+	 */
+	ExecAssignExprContext(estate, &portalstate->csstate.cstate);
+
+#define SUBQUERYSCAN_NSLOTS 1
+
+	/*
+	 * tuple table initialization
+	 */
+	ExecInitResultTupleSlot(estate, &portalstate->csstate.cstate);
+
+	/*
+	 * initialize portal
+	 *
+	 * This should agree with ExecInitSubPlan
+	 */
+    rte = rt_fetch( node->scan.scanrelid, estate->es_range_table);
+    Assert(rte->rtetype == RTE_PORTAL);
+
+	portalstate->csstate.css_ScanTupleSlot = NULL;
+	portalstate->csstate.cstate.cs_TupFromTlist = false;
+
+	portal = GetPortalByName( rte->u.portal.portalname);
+	if (!PointerIsValid(portal)) 
+		elog(ERROR, "InitPortalScan: portal %s disappeared", 
+						rte->u.portal.portalname);
+
+	portalstate->portal = portal;
+
+	/*
+	 * initialize tuple type
+	 */
+	ExecAssignResultTypeFromTL((Plan *) node, &portalstate->csstate.cstate);
+	ExecAssignProjectionInfo((Plan *) node, &portalstate->csstate.cstate);
+
+	return TRUE;
+}
+
+int
+ExecCountSlotsPortalScan(PortalScan *node)
+{
+	/*
+	 * The subplan has its own tuple table and must not be counted here!
+	 */
+	return ExecCountSlotsNode(outerPlan(node)) +
+	ExecCountSlotsNode(innerPlan(node)) +
+	SUBQUERYSCAN_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ *		ExecEndPortalScan
+ *
+ *		frees any storage allocated through C routines.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndPortalScan(PortalScan *node)
+{
+	PortalScanState *portalstate;
+
+	/*
+	 * get information from node
+	 */
+	portalstate = (PortalScanState *) node->scan.scanstate;
+
+	/*
+	 * Free the projection info and the scan attribute info
+	 *
+	 * Note: we don't ExecFreeResultType(portalstate) because the rule
+	 * manager depends on the tupType returned by ExecMain().  So for now,
+	 * this is freed at end-transaction time.  -cim 6/2/91
+	 */
+	ExecFreeProjectionInfo(&portalstate->csstate.cstate);
+	ExecFreeExprContext(&portalstate->csstate.cstate);
+
+	/*
+	 * close down portal
+	 */
+ 	/* XXX */
+
+	/*
+	 * clean up portal's tuple table
+	 */
+	portalstate->csstate.css_ScanTupleSlot = NULL;
+
+	/*
+	 * clean out the upper tuple table
+	 */
+	ExecClearTuple(portalstate->csstate.cstate.cs_ResultTupleSlot);
+}
+
+/* ----------------------------------------------------------------
+ *		ExecPortalReScan
+ *
+ *		Rescans the relation.
+ * ----------------------------------------------------------------
+ */
+void
+ExecPortalReScan(PortalScan *node, ExprContext *exprCtxt, Plan *parent)
+{
+	PortalScanState *portalstate;
+	EState	   *estate;
+
+	portalstate = (PortalScanState *) node->scan.scanstate;
+	estate = node->scan.plan.state;
+
+	elog(ERROR, "PortalReScan: Cannot rescan portals");
+
+	/*
+	 * ExecReScan doesn't know about my subplan, so I have to do
+	 * changed-parameter signaling myself.
+	 */
+
+/*
+	if (node->scan.plan.chgParam != NULL)
+		SetChangedParamList(node->subplan, node->scan.plan.chgParam);
+*/
+
+	/*
+	 * if chgParam of subnode is not null then plan will be re-scanned by
+	 * first ExecProcNode.
+	 */
+
+/*
+ XXX 
+	if (node->subplan->chgParam == NULL)
+		ExecReScan(node->subplan, NULL, node->subplan);
+*/
+
+	portalstate->csstate.css_ScanTupleSlot = NULL;
+}
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.156
diff --unified -r1.156 copyfuncs.c
--- src/backend/nodes/copyfuncs.c	2001/09/18 01:59:06	1.156
+++ src/backend/nodes/copyfuncs.c	2001/09/21 17:32:47
@@ -227,6 +227,24 @@
 }
 
 /* ----------------
+ *		_copyPortalScan
+ * ----------------
+ */
+static PortalScan *
+_copyPortalScan(PortalScan *from)
+{
+	PortalScan    *newnode = makeNode(PortalScan);
+
+	/*
+	 * copy node superclass fields
+	 */
+	CopyPlanFields((Plan *) from, (Plan *) newnode);
+	CopyScanFields((Scan *) from, (Scan *) newnode);
+
+	return newnode;
+}
+
+/* ----------------
  *		_copyIndexScan
  * ----------------
  */
@@ -1105,7 +1123,7 @@
 	Node_Copy(from, newnode, cheapest_total_path);
 	newnode->pruneable = from->pruneable;
 
-	newnode->issubquery = from->issubquery;
+	newnode->reltype = from->reltype;
 	Node_Copy(from, newnode, indexlist);
 	newnode->pages = from->pages;
 	newnode->tuples = from->tuples;
@@ -1487,11 +1505,6 @@
 _copyRangeTblEntry(RangeTblEntry *from)
 {
 	RangeTblEntry *newnode = makeNode(RangeTblEntry);
-
-	if (from->relname)
-		newnode->relname = pstrdup(from->relname);
-	newnode->relid = from->relid;
-	Node_Copy(from, newnode, subquery);
 	Node_Copy(from, newnode, alias);
 	Node_Copy(from, newnode, eref);
 	newnode->inh = from->inh;
@@ -1499,7 +1512,21 @@
 	newnode->checkForRead = from->checkForRead;
 	newnode->checkForWrite = from->checkForWrite;
 	newnode->checkAsUser = from->checkAsUser;
-
+	newnode->rtetype = from->rtetype;
+	switch (from->rtetype) {
+		case RTE_RELATION:
+			newnode->u.rel.relname = pstrdup(from->u.rel.relname);
+			newnode->u.rel.relid = from->u.rel.relid;
+			break;
+		case RTE_SUBSELECT:
+			Node_Copy(from, newnode, u.sub.subquery);
+			break;
+		case RTE_PORTAL:
+			newnode->u.portal.portalname = pstrdup(from->u.portal.portalname);
+			break;
+		default: 
+			elog(ERROR, "copyRTE: Unknown rtetype %d", from->rtetype);
+	}
 	return newnode;
 }
 
@@ -2246,7 +2273,6 @@
 	newnode->vacuum = from->vacuum;
 	newnode->full = from->full;
 	newnode->analyze = from->analyze;
-	newnode->freeze = from->freeze;
 	newnode->verbose = from->verbose;
 	if (from->vacrel)
 		newnode->vacrel = pstrdup(from->vacrel);
@@ -2554,6 +2580,9 @@
 			break;
 		case T_SeqScan:
 			retval = _copySeqScan(from);
+			break;
+		case T_PortalScan:
+			retval = _copyPortalScan(from);
 			break;
 		case T_IndexScan:
 			retval = _copyIndexScan(from);
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.104
diff --unified -r1.104 equalfuncs.c
--- src/backend/nodes/equalfuncs.c	2001/09/18 01:59:06	1.104
+++ src/backend/nodes/equalfuncs.c	2001/09/21 17:32:47
@@ -1116,8 +1116,6 @@
 		return false;
 	if (a->analyze != b->analyze)
 		return false;
-	if (a->freeze != b->freeze)
-		return false;
 	if (a->verbose != b->verbose)
 		return false;
 	if (!equalstr(a->vacrel, b->vacrel))
@@ -1599,12 +1597,6 @@
 static bool
 _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
 {
-	if (!equalstr(a->relname, b->relname))
-		return false;
-	if (a->relid != b->relid)
-		return false;
-	if (!equal(a->subquery, b->subquery))
-		return false;
 	if (!equal(a->alias, b->alias))
 		return false;
 	if (!equal(a->eref, b->eref))
@@ -1619,7 +1611,26 @@
 		return false;
 	if (a->checkAsUser != b->checkAsUser)
 		return false;
-
+	if (a->rtetype != b->rtetype)
+		return false;
+	switch (a->rtetype) {
+		case RTE_RELATION:
+			if (!equalstr(a->u.rel.relname, b->u.rel.relname))
+				return false;
+			if (a->u.rel.relid != b->u.rel.relid)
+				return false;
+			break;
+		case RTE_SUBSELECT:
+			if (!equal(a->u.sub.subquery, b->u.sub.subquery))
+				return false;
+			break;
+		case RTE_PORTAL:
+			if (!equalstr(a->u.portal.portalname, b->u.portal.portalname))
+				return false;
+			break;
+		default: 
+			elog(ERROR, "equalRTE: Unknown rtetype %d", a->rtetype);
+	}
 	return true;
 }
 
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/nodes/outfuncs.c,v
retrieving revision 1.145
diff --unified -r1.145 outfuncs.c
--- src/backend/nodes/outfuncs.c	2001/08/21 16:36:02	1.145
+++ src/backend/nodes/outfuncs.c	2001/09/21 17:32:47
@@ -504,6 +504,16 @@
 }
 
 /*
+ *	PortalScan is a subclass of Scan
+ */
+static void
+_outPortalScan(StringInfo str, PortalScan *node)
+{
+	appendStringInfo(str, " PORTALSCAN ");
+	_outPlanInfo(str, (Plan *) node);
+}
+
+/*
  *	IndexScan is a subclass of Scan
  */
 static void
@@ -962,12 +972,7 @@
 static void
 _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
 {
-	appendStringInfo(str, " RTE :relname ");
-	_outToken(str, node->relname);
-	appendStringInfo(str, " :relid %u ",
-					 node->relid);
-	appendStringInfo(str, " :subquery ");
-	_outNode(str, node->subquery);
+	appendStringInfo(str, " RTE ");
 	appendStringInfo(str, " :alias ");
 	_outNode(str, node->alias);
 	appendStringInfo(str, " :eref ");
@@ -979,6 +984,24 @@
 					 booltostr(node->checkForRead),
 					 booltostr(node->checkForWrite),
 					 node->checkAsUser);
+	appendStringInfo(str, " :rtetype %u ", node->rtetype);
+	switch (node->rtetype) {
+		case RTE_RELATION:
+			appendStringInfo(str, " :relname ");
+			_outToken(str, node->u.rel.relname);
+			appendStringInfo(str, " :relid %u ", node->u.rel.relid);
+			break;
+		case RTE_SUBSELECT:
+			appendStringInfo(str, " :subquery ");
+			_outNode(str, node->u.sub.subquery);
+			break;
+		case RTE_PORTAL:
+			appendStringInfo(str, " :portalname ");
+			_outToken(str, node->u.portal.portalname);
+			break;
+		default: 
+			elog(ERROR, "outRTE: Unknown rtetype %d", node->rtetype);
+	}
 }
 
 /*
@@ -1508,6 +1531,9 @@
 				break;
 			case T_SeqScan:
 				_outSeqScan(str, obj);
+				break;
+			case T_PortalScan:
+				_outPortalScan(str, obj);
 				break;
 			case T_IndexScan:
 				_outIndexScan(str, obj);
Index: src/backend/nodes/print.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/nodes/print.c,v
retrieving revision 1.47
diff --unified -r1.47 print.c
--- src/backend/nodes/print.c	2001/03/22 03:59:32	1.47
+++ src/backend/nodes/print.c	2001/09/21 17:32:47
@@ -131,12 +131,18 @@
 	{
 		RangeTblEntry *rte = lfirst(l);
 
-		if (rte->relname)
+		if (rte->rtetype == RTE_RELATION) {
 			printf("%d\t%s (%s)\t%u",
-				   i, rte->relname, rte->eref->relname, rte->relid);
-		else
+				   i, rte->u.rel.relname, rte->eref->relname, rte->u.rel.relid);
+		}
+		else if (rte->rtetype == RTE_SUBSELECT) {
 			printf("%d\t[subquery] (%s)\t",
 				   i, rte->eref->relname);
+		}
+		else if (rte->rtetype == RTE_PORTAL) {
+			printf("%d\t[portal] (%s)\t",
+				   i, rte->eref->relname);
+		}
 		printf("\t%s\t%s\n",
 			   (rte->inh ? "inh" : ""),
 			   (rte->inFromCl ? "inFromCl" : ""));
@@ -300,6 +306,8 @@
 			return "SCAN";
 		case T_SeqScan:
 			return "SEQSCAN";
+		case T_PortalScan:
+			return "PORTALSCAN";
 		case T_IndexScan:
 			return "INDEXSCAN";
 		case T_TidScan:
@@ -360,14 +368,16 @@
 		RangeTblEntry *rte;
 
 		rte = rt_fetch(((Scan *) p)->scanrelid, parsetree->rtable);
-		StrNCpy(extraInfo, rte->relname, NAMEDATALEN);
+		Assert( rte->rtetype == RTE_RELATION);
+		StrNCpy(extraInfo, rte->u.rel.relname, NAMEDATALEN);
 	}
 	else if (IsA(p, IndexScan))
 	{
 		RangeTblEntry *rte;
 
 		rte = rt_fetch(((IndexScan *) p)->scan.scanrelid, parsetree->rtable);
-		StrNCpy(extraInfo, rte->relname, NAMEDATALEN);
+		Assert( rte->rtetype == RTE_RELATION);
+		StrNCpy(extraInfo, rte->u.rel.relname, NAMEDATALEN);
 	}
 	else
 		extraInfo[0] = '\0';
Index: src/backend/nodes/readfuncs.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/nodes/readfuncs.c,v
retrieving revision 1.112
diff --unified -r1.112 readfuncs.c
--- src/backend/nodes/readfuncs.c	2001/07/03 16:52:48	1.112
+++ src/backend/nodes/readfuncs.c	2001/09/21 17:32:47
@@ -1414,7 +1414,7 @@
  * ----------------
  */
 static RangeTblEntry *
-_readRangeTblEntry(void)
+_readRangeTblEntry()
 {
 	RangeTblEntry *local_node;
 	char	   *token;
@@ -1422,17 +1422,6 @@
 
 	local_node = makeNode(RangeTblEntry);
 
-	token = pg_strtok(&length); /* eat :relname */
-	token = pg_strtok(&length); /* get :relname */
-	local_node->relname = nullable_string(token, length);
-
-	token = pg_strtok(&length); /* eat :relid */
-	token = pg_strtok(&length); /* get :relid */
-	local_node->relid = atooid(token);
-
-	token = pg_strtok(&length); /* eat :subquery */
-	local_node->subquery = nodeRead(true);		/* now read it */
-
 	token = pg_strtok(&length); /* eat :alias */
 	local_node->alias = nodeRead(true); /* now read it */
 
@@ -1459,8 +1448,35 @@
 	token = pg_strtok(&length); /* get :checkAsUser */
 	local_node->checkAsUser = atooid(token);
 
+	token = pg_strtok(&length); /* eat :rtetype */
+	token = pg_strtok(&length); /* get :rtetype */
+	local_node->rtetype = atoui(token);
+
+	switch (local_node->rtetype) {
+		case RTE_RELATION:
+			token = pg_strtok(&length); /* eat :relname */
+			token = pg_strtok(&length); /* get :relname */
+			local_node->u.rel.relname = nullable_string(token, length);
+
+			token = pg_strtok(&length); /* eat :relid */
+			token = pg_strtok(&length); /* get :relid */
+			local_node->u.rel.relid = atooid(token);
+			break;
+		case RTE_SUBSELECT:
+			token = pg_strtok(&length); /* eat :subquery */
+			local_node->u.sub.subquery = nodeRead(true);	/* now read it */
+			break;
+		case RTE_PORTAL:
+			token = pg_strtok(&length); /* eat :portalname */
+			token = pg_strtok(&length); /* get :portalname */
+			local_node->u.portal.portalname = nullable_string(token, length);
+			break;
+		default: 
+			elog(ERROR, "readRTE: Unknown rtetype %d", local_node->rtetype);
+	}
 	return local_node;
 }
+
 
 /* ----------------
  *		_readPath
Index: src/backend/optimizer/path/allpaths.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/path/allpaths.c,v
retrieving revision 1.78
diff --unified -r1.78 allpaths.c
--- src/backend/optimizer/path/allpaths.c	2001/07/31 17:56:30	1.78
+++ src/backend/optimizer/path/allpaths.c	2001/09/21 17:32:47
@@ -25,6 +25,7 @@
 #include "optimizer/prep.h"
 #include "parser/parsetree.h"
 #include "rewrite/rewriteManip.h"
+#include "utils/portal.h"
 
 
 bool		enable_geqo = true;
@@ -100,12 +101,46 @@
 		rti = lfirsti(rel->relids);
 		rte = rt_fetch(rti, root->rtable);
 
-		if (rel->issubquery)
+		if (rel->reltype == REL_SUBQUERY) 
 		{
+            Assert(rte->rtetype == RTE_SUBSELECT);
 			/* Subquery --- generate a separate plan for it */
 			set_subquery_pathlist(root, rel, rti, rte);
 		}
-		else if ((inheritlist = expand_inherted_rtentry(root, rti, true))
+		else if (rel->reltype == REL_PORTAL) 
+		{
+			/* its a portal, considerably simpler case, as portals
+			   may not have setops or conditions. We only need
+		       to fill in the cost info from Portal's own Plan */
+			Portal portal;
+			QueryDesc *qd; 
+
+			Assert(rte->rtetype == RTE_PORTAL);
+
+			portal = GetPortalByName(rte->u.portal.portalname);
+			if (!PointerIsValid(portal)) 
+				elog(ERROR, "set_base_rel_pathlists: Portal %s disappeared", 
+					rte->u.portal.portalname);
+
+			qd = PortalGetQueryDesc(portal);
+
+			/* Copy number of output rows from portal info */
+			rel->tuples = qd->plantree->plan_rows;
+
+			/* Mark rel with estimated output rows, width, etc */
+			set_baserel_size_estimates(root, rel); 
+
+			/* Generate appropriate path */
+			add_path(rel, create_portalscan_path(rel, portal) );
+
+			/* Select cheapest path (pretty easy in this case...) */
+			set_cheapest(rel);
+		}
+/* XXX: maybe we can/should determine that a relation is a root of inheritance
+ *      hierarchy earlier and have REL_INH_ROOT reltype?
+ */
+		else if (rel->reltype == REL_PLAIN)  {
+			if ((inheritlist = expand_inherted_rtentry(root, rti, true))
 				 != NIL)
 		{
 			/* Relation is root of an inheritance tree, process specially */
@@ -117,6 +152,9 @@
 			set_plain_rel_pathlist(root, rel, rte);
 		}
 	}
+		else 
+			elog(ERROR,"set_base_rel_pathlists: unknown reltype %d", rel->reltype);
+	}
 }
 
 /*
@@ -126,6 +164,7 @@
 static void
 set_plain_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte)
 {
+	Assert( rte->rtetype == RTE_RELATION );
 	/* Mark rel with estimated output rows, width, etc */
 	set_baserel_size_estimates(root, rel);
 
@@ -178,10 +217,11 @@
 						   List *inheritlist)
 {
 	int			parentRTindex = rti;
-	Oid			parentOID = rte->relid;
+	Oid			parentOID = rte->u.rel.relid;
 	List	   *subpaths = NIL;
 	List	   *il;
 
+	Assert( rte->rtetype == RTE_RELATION );
 	/*
 	 * XXX for now, can't handle inherited expansion of FOR UPDATE; can we
 	 * do better?
@@ -215,7 +255,10 @@
 		RelOptInfo *childrel;
 
 		childrte = rt_fetch(childRTindex, root->rtable);
-		childOID = childrte->relid;
+
+		Assert( childrte->rtetype == RTE_RELATION );
+
+		childOID = childrte->u.rel.relid;
 
 		/*
 		 * Make a RelOptInfo for the child so we can do planning.  Do NOT
@@ -276,7 +319,8 @@
 set_subquery_pathlist(Query *root, RelOptInfo *rel,
 					  Index rti, RangeTblEntry *rte)
 {
-	Query	   *subquery = rte->subquery;
+	Query	   *subquery = rte->u.sub.subquery;
+    Assert(rte->rtetype == RTE_SUBSELECT);
 
 	/*
 	 * If there are any restriction clauses that have been attached to the
Index: src/backend/optimizer/path/clausesel.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/path/clausesel.c,v
retrieving revision 1.46
diff --unified -r1.46 clausesel.c
--- src/backend/optimizer/path/clausesel.c	2001/06/25 21:11:43	1.46
+++ src/backend/optimizer/path/clausesel.c	2001/09/21 17:32:47
@@ -384,7 +384,7 @@
 		{
 			RangeTblEntry *rte = rt_fetch(var->varno, root->rtable);
 
-			if (rte->subquery)
+			if (rte->rtetype == RTE_SUBSELECT)
 			{
 
 				/*
Index: src/backend/optimizer/path/costsize.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/path/costsize.c,v
retrieving revision 1.78
diff --unified -r1.78 costsize.c
--- src/backend/optimizer/path/costsize.c	2001/08/21 16:36:02	1.78
+++ src/backend/optimizer/path/costsize.c	2001/09/21 17:32:47
@@ -110,7 +110,7 @@
 
 	/* Should only be applied to base relations */
 	Assert(length(baserel->relids) == 1);
-	Assert(!baserel->issubquery);
+	Assert(baserel->reltype == REL_PLAIN);
 
 	if (!enable_seqscan)
 		startup_cost += disable_cost;
@@ -225,7 +225,7 @@
 	/* Should only be applied to base relations */
 	Assert(IsA(baserel, RelOptInfo) &&IsA(index, IndexOptInfo));
 	Assert(length(baserel->relids) == 1);
-	Assert(!baserel->issubquery);
+	Assert(baserel->reltype == REL_PLAIN);
 
 	if (!enable_indexscan && !is_injoin)
 		startup_cost += disable_cost;
Index: src/backend/optimizer/plan/createplan.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/plan/createplan.c,v
retrieving revision 1.109
diff --unified -r1.109 createplan.c
--- src/backend/optimizer/plan/createplan.c	2001/09/21 04:06:04	1.109
+++ src/backend/optimizer/plan/createplan.c	2001/09/21 17:32:47
@@ -43,6 +43,8 @@
 					List *scan_clauses);
 static SubqueryScan *create_subqueryscan_plan(Path *best_path,
 						 List *tlist, List *scan_clauses);
+static PortalScan *create_portalscan_plan(Path *best_path,
+						 List *tlist, List *scan_clauses);
 static NestLoop *create_nestloop_plan(NestPath *best_path, List *tlist,
 					 List *joinclauses, List *otherclauses,
 					 Plan *outer_plan, List *outer_tlist,
@@ -69,6 +71,8 @@
 static void copy_path_costsize(Plan *dest, Path *src);
 static void copy_plan_costsize(Plan *dest, Plan *src);
 static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
+static PortalScan *make_portalscan(List *qptlist, List *qpqual, 
+									Index scanrelid);
 static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
 			   List *indxid, List *indxqual,
 			   List *indxqualorig,
@@ -117,6 +121,7 @@
 		case T_SeqScan:
 		case T_TidScan:
 		case T_SubqueryScan:
+		case T_PortalScan:
 			plan = (Plan *) create_scan_plan(root, best_path);
 			break;
 		case T_HashJoin:
@@ -198,6 +203,12 @@
 													 scan_clauses);
 			break;
 
+		case T_PortalScan:
+			plan = (Scan *) create_portalscan_plan(best_path,
+													 tlist,
+													 scan_clauses);
+			break;
+
 		default:
 			elog(ERROR, "create_scan_plan: unknown node type: %d",
 				 best_path->pathtype);
@@ -348,8 +359,7 @@
 	Index		scan_relid;
 
 	/* there should be exactly one base rel involved... */
-	Assert(length(best_path->parent->relids) == 1);
-	Assert(!best_path->parent->issubquery);
+	Assert(best_path->parent->reltype == REL_PLAIN);
 
 	scan_relid = (Index) lfirsti(best_path->parent->relids);
 
@@ -392,8 +402,7 @@
 	IndexScan  *scan_plan;
 
 	/* there should be exactly one base rel involved... */
-	Assert(length(best_path->path.parent->relids) == 1);
-	Assert(!best_path->path.parent->issubquery);
+	Assert(best_path->path.parent->reltype == REL_PLAIN);
 
 	baserelid = lfirsti(best_path->path.parent->relids);
 
@@ -510,8 +519,7 @@
 	Index		scan_relid;
 
 	/* there should be exactly one base rel involved... */
-	Assert(length(best_path->path.parent->relids) == 1);
-	Assert(!best_path->path.parent->issubquery);
+	Assert(best_path->path.parent->reltype == REL_PLAIN);
 
 	scan_relid = (Index) lfirsti(best_path->path.parent->relids);
 
@@ -540,9 +548,8 @@
 	Index		scan_relid;
 
 	/* there should be exactly one base rel involved... */
-	Assert(length(best_path->parent->relids) == 1);
 	/* and it must be a subquery */
-	Assert(best_path->parent->issubquery);
+	Assert(best_path->parent->reltype == REL_SUBQUERY);
 
 	scan_relid = (Index) lfirsti(best_path->parent->relids);
 
@@ -554,6 +561,30 @@
 	return scan_plan;
 }
 
+/*
+ * create_portalscan_plan
+ *	 Returns a portalscan plan for the portal scanned by 'best_path'
+ *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
+ */
+static PortalScan *
+create_portalscan_plan(Path *best_path, List *tlist, List *scan_clauses)
+{
+	PortalScan *scan_plan;
+	Index		scan_relid;
+
+	/* there should be exactly one base rel involved... */
+	/* and it must be a portal */
+	Assert(best_path->parent->reltype == REL_PORTAL);
+
+	scan_relid = (Index) lfirsti(best_path->parent->relids);
+
+	scan_plan = make_portalscan( tlist, scan_clauses, scan_relid );
+
+	copy_path_costsize(&scan_plan->scan.plan, best_path);
+
+	return scan_plan;
+}
+
 /*****************************************************************************
  *
  *	JOIN METHODS
@@ -1286,6 +1317,26 @@
 	plan->righttree = NULL;
 	node->scan.scanrelid = scanrelid;
 	node->subplan = subplan;
+	node->scan.scanstate = (CommonScanState *) NULL;
+
+	return node;
+}
+
+PortalScan *
+make_portalscan(List *qptlist,
+				  List *qpqual,
+				  Index scanrelid)
+{
+	PortalScan *node = makeNode(PortalScan);
+	Plan	   *plan = &node->scan.plan;
+
+	/* cost should be inserted by caller */
+	plan->state = (EState *) NULL;
+	plan->targetlist = qptlist;
+	plan->qual = qpqual;
+	plan->lefttree = NULL;
+	plan->righttree = NULL;
+	node->scan.scanrelid = scanrelid;
 	node->scan.scanstate = (CommonScanState *) NULL;
 
 	return node;
Index: src/backend/optimizer/plan/planner.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/plan/planner.c,v
retrieving revision 1.108
diff --unified -r1.108 planner.c
--- src/backend/optimizer/plan/planner.c	2001/06/05 05:26:04	1.108
+++ src/backend/optimizer/plan/planner.c	2001/09/21 17:32:47
@@ -268,7 +268,8 @@
 	{
 		int			varno = ((RangeTblRef *) jtnode)->rtindex;
 		RangeTblEntry *rte = rt_fetch(varno, parse->rtable);
-		Query	   *subquery = rte->subquery;
+		Query	   *subquery = rte->u.sub.subquery;
+		
 
 		/*
 		 * Is this a subquery RTE, and if so, is the subquery simple
@@ -277,7 +278,8 @@
 		 * Note: even if the subquery itself is simple enough, we can't
 		 * pull it up if there is a reference to its whole tuple result.
 		 */
-		if (subquery && is_simple_subquery(subquery) &&
+		if (rte->rtetype == RTE_SUBSELECT && 
+             is_simple_subquery(subquery) &&
 			!contain_whole_tuple_var((Node *) parse, varno, 0))
 		{
 			int			rtoffset;
Index: src/backend/optimizer/plan/setrefs.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/plan/setrefs.c,v
retrieving revision 1.71
diff --unified -r1.71 setrefs.c
--- src/backend/optimizer/plan/setrefs.c	2001/03/22 03:59:37	1.71
+++ src/backend/optimizer/plan/setrefs.c	2001/09/21 17:32:47
@@ -87,6 +87,10 @@
 	 */
 	switch (nodeTag(plan))
 	{
+		case T_PortalScan:
+			fix_expr_references(plan, (Node *) plan->targetlist);
+			fix_expr_references(plan, (Node *) plan->qual);
+			break;
 		case T_SeqScan:
 			fix_expr_references(plan, (Node *) plan->targetlist);
 			fix_expr_references(plan, (Node *) plan->qual);
Index: src/backend/optimizer/prep/preptlist.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/prep/preptlist.c,v
retrieving revision 1.43
diff --unified -r1.43 preptlist.c
--- src/backend/optimizer/prep/preptlist.c	2001/09/06 02:07:42	1.43
+++ src/backend/optimizer/prep/preptlist.c	2001/09/21 17:32:47
@@ -58,7 +58,7 @@
 	{
 		RangeTblEntry *rte = rt_fetch(result_relation, range_table);
 
-		if (rte->subquery != NULL || rte->relid == InvalidOid)
+		if (rte->rtetype == RTE_SUBSELECT)
 			elog(ERROR, "preprocess_targetlist: subquery cannot be result relation");
 	}
 
Index: src/backend/optimizer/prep/prepunion.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/prep/prepunion.c,v
retrieving revision 1.66
diff --unified -r1.66 prepunion.c
--- src/backend/optimizer/prep/prepunion.c	2001/08/14 17:12:57	1.66
+++ src/backend/optimizer/prep/prepunion.c	2001/09/21 17:32:47
@@ -82,6 +82,7 @@
 {
 	SetOperationStmt *topop = (SetOperationStmt *) parse->setOperations;
 	Node	   *node;
+	RangeTblEntry *rte;
 	Query	   *leftmostQuery;
 
 	Assert(topop && IsA(topop, SetOperationStmt));
@@ -94,8 +95,11 @@
 	while (node && IsA(node, SetOperationStmt))
 		node = ((SetOperationStmt *) node)->larg;
 	Assert(node && IsA(node, RangeTblRef));
-	leftmostQuery = rt_fetch(((RangeTblRef *) node)->rtindex,
-							 parse->rtable)->subquery;
+	rte = rt_fetch(((RangeTblRef *) node)->rtindex, parse->rtable);
+	Assert(rte && rte->rtetype == RTE_SUBSELECT);
+
+	leftmostQuery = rte->u.sub.subquery;
+
 	Assert(leftmostQuery != NULL);
 
 	/*
@@ -127,10 +131,11 @@
 	{
 		RangeTblRef *rtr = (RangeTblRef *) setOp;
 		RangeTblEntry *rte = rt_fetch(rtr->rtindex, parse->rtable);
-		Query	   *subquery = rte->subquery;
+		Query	   *subquery = rte->u.sub.subquery;
 		Plan	   *subplan,
 				   *plan;
 
+		Assert(rte->rtetype == RTE_SUBSELECT);
 		Assert(subquery != NULL);
 
 		/*
@@ -555,15 +560,16 @@
 expand_inherted_rtentry(Query *parse, Index rti, bool dup_parent)
 {
 	RangeTblEntry *rte = rt_fetch(rti, parse->rtable);
-	Oid			parentOID = rte->relid;
+	Oid			parentOID = rte->u.rel.relid;
 	List	   *inhOIDs;
 	List	   *inhRTIs;
 	List	   *l;
+	Assert(rte->rtetype == RTE_RELATION);
 
 	/* Does RT entry allow inheritance? */
 	if (!rte->inh)
 		return NIL;
-	Assert(parentOID != InvalidOid && rte->subquery == NULL);
+
 	/* Always clear the parent's inh flag, see above comments */
 	rte->inh = false;
 	/* Fast path for common case of childless table */
@@ -601,8 +607,10 @@
 		 * this point.
 		 */
 		childrte = copyObject(rte);
-		childrte->relname = get_rel_name(childOID);
-		childrte->relid = childOID;
+		Assert(rte->rtetype == RTE_RELATION);
+		Assert(childrte->rtetype == RTE_RELATION);
+		childrte->u.rel.relname = get_rel_name(childOID);
+		childrte->u.rel.relid = childOID;
 		parse->rtable = lappend(parse->rtable, childrte);
 		childRTindex = length(parse->rtable);
 
Index: src/backend/optimizer/util/clauses.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/util/clauses.c,v
retrieving revision 1.88
diff --unified -r1.88 clauses.c
--- src/backend/optimizer/util/clauses.c	2001/07/31 20:16:33	1.88
+++ src/backend/optimizer/util/clauses.c	2001/09/21 17:32:47
@@ -1798,8 +1798,8 @@
 		{
 			RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
 
-			if (rte->subquery)
-				if (walker(rte->subquery, context))
+			if (rte->rtetype == RTE_SUBSELECT)
+				if (walker( rte->u.sub.subquery, context))
 					return true;
 		}
 	}
@@ -2179,15 +2179,17 @@
 
 		foreach(rt, query->rtable)
 		{
-			RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
+			RangeTblEntry *rte = lfirst(rt);
 
-			if (rte->subquery)
+			if (rte->rtetype == RTE_SUBSELECT)
 			{
 				RangeTblEntry *newrte;
 
 				FLATCOPY(newrte, rte, RangeTblEntry);
-				CHECKFLATCOPY(newrte->subquery, rte->subquery, Query);
-				MUTATE(newrte->subquery, newrte->subquery, Query *);
+				CHECKFLATCOPY(newrte->u.sub.subquery, rte->u.sub.subquery, 
+								Query);
+				MUTATE(newrte->u.sub.subquery, newrte->u.sub.subquery, 
+						Query *);
 				rte = newrte;
 			}
 			newrt = lappend(newrt, rte);
Index: src/backend/optimizer/util/pathnode.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/util/pathnode.c,v
retrieving revision 1.75
diff --unified -r1.75 pathnode.c
--- src/backend/optimizer/util/pathnode.c	2001/07/16 05:06:58	1.75
+++ src/backend/optimizer/util/pathnode.c	2001/09/21 17:32:47
@@ -434,6 +434,27 @@
 }
 
 /*
+ * create_portalscan_path
+ *	  Creates a path corresponding to a sequential scan of a portal,
+ *	  returning the pathnode.
+ */
+Path *
+create_portalscan_path(RelOptInfo *rel, Portal portal)
+{
+	Path	   *pathnode = makeNode(Path);
+
+	pathnode->pathtype = T_PortalScan;
+	pathnode->parent = rel;
+	pathnode->pathkeys = NIL;	/* for now, assume unordered result */
+
+	/* just copy the Portal's own cost estimates */
+	pathnode->startup_cost = portal->queryDesc->plantree->startup_cost;
+	pathnode->total_cost = portal->queryDesc->plantree->total_cost;
+
+	return pathnode;
+}
+
+/*
  * create_subqueryscan_path
  *	  Creates a path corresponding to a sequential scan of a subquery,
  *	  returning the pathnode.
Index: src/backend/optimizer/util/relnode.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/optimizer/util/relnode.c,v
retrieving revision 1.33
diff --unified -r1.33 relnode.c
--- src/backend/optimizer/util/relnode.c	2001/05/20 20:28:19	1.33
+++ src/backend/optimizer/util/relnode.c	2001/09/21 17:32:47
@@ -125,7 +125,7 @@
 make_base_rel(Query *root, int relid)
 {
 	RelOptInfo *rel = makeNode(RelOptInfo);
-	Oid			relationObjectId;
+	RangeTblEntry *rte;
 
 	rel->relids = makeListi1(relid);
 	rel->rows = 0;
@@ -135,7 +135,6 @@
 	rel->cheapest_startup_path = NULL;
 	rel->cheapest_total_path = NULL;
 	rel->pruneable = true;
-	rel->issubquery = false;
 	rel->indexlist = NIL;
 	rel->pages = 0;
 	rel->tuples = 0;
@@ -146,24 +145,33 @@
 	rel->joininfo = NIL;
 	rel->innerjoin = NIL;
 
-	/* Check rtable to see if it's a plain relation or a subquery */
-	relationObjectId = getrelid(relid, root->rtable);
+   /* Check rtable to see if what kind of a relation it is */
+	rte = rt_fetch(relid, root->rtable);
 
-	if (relationObjectId != InvalidOid)
+	if (rte->rtetype == RTE_RELATION)
 	{
-		/* Plain relation --- retrieve statistics from the system catalogs */
-		bool	indexed;
+	    /* Plain relation --- retrieve statistics from the system catalogs */
+	    bool    indexed;
+	    Oid     relationObjectId;
 
-		get_relation_info(relationObjectId,
-						  &indexed, &rel->pages, &rel->tuples);
-		if (indexed)
-			rel->indexlist = find_secondary_indexes(relationObjectId);
+	    rel->reltype = REL_PLAIN;
+	    relationObjectId = rte->u.rel.relid;
+
+	    get_relation_info( relationObjectId,
+	                      &indexed, &rel->pages, &rel->tuples);
+	    if (indexed)
+	        rel->indexlist = find_secondary_indexes(relationObjectId);
 	}
-	else
+	else if (rte->rtetype == RTE_SUBSELECT)
 	{
-		/* subquery --- mark it as such for later processing */
-		rel->issubquery = true;
+	    rel->reltype = REL_SUBQUERY;
 	}
+	else if (rte->rtetype == RTE_PORTAL)
+	{
+	    rel->reltype = REL_PORTAL;
+	}
+	else
+	    elog(ERROR, "make_base_rel: Unknown RTE node type %d", nodeTag(rte) );
 
 	return rel;
 }
@@ -285,6 +293,7 @@
 	 */
 	joinrel = makeNode(RelOptInfo);
 	joinrel->relids = joinrelids;
+	joinrel->reltype = REL_JOIN;
 	joinrel->rows = 0;
 	joinrel->width = 0;
 	joinrel->targetlist = NIL;
@@ -292,7 +301,6 @@
 	joinrel->cheapest_startup_path = NULL;
 	joinrel->cheapest_total_path = NULL;
 	joinrel->pruneable = true;
-	joinrel->issubquery = false;
 	joinrel->indexlist = NIL;
 	joinrel->pages = 0;
 	joinrel->tuples = 0;
Index: src/backend/parser/analyze.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/parser/analyze.c,v
retrieving revision 1.198
diff --unified -r1.198 analyze.c
--- src/backend/parser/analyze.c	2001/09/07 21:57:53	1.198
+++ src/backend/parser/analyze.c	2001/09/21 17:32:47
@@ -169,7 +169,6 @@
 				{
 					List	   *aliaslist = n->aliases;
 					List	   *targetList;
-
 					foreach(targetList, n->query->targetList)
 					{
 						TargetEntry *te = (TargetEntry *) lfirst(targetList);
@@ -2018,6 +2017,7 @@
 	Query	   *qry = makeNode(Query);
 	SelectStmt *leftmostSelect;
 	int			leftmostRTI;
+	RangeTblEntry* leftmostRTE;
 	Query	   *leftmostQuery;
 	SetOperationStmt *sostmt;
 	char	   *into;
@@ -2093,7 +2093,10 @@
 		node = ((SetOperationStmt *) node)->larg;
 	Assert(node && IsA(node, RangeTblRef));
 	leftmostRTI = ((RangeTblRef *) node)->rtindex;
-	leftmostQuery = rt_fetch(leftmostRTI, pstate->p_rtable)->subquery;
+	leftmostRTE = rt_fetch(leftmostRTI, pstate->p_rtable);
+	Assert(node && leftmostRTE->rtetype ==  RTE_SUBSELECT);
+	leftmostQuery = leftmostRTE->u.sub.subquery;
+
 	Assert(leftmostQuery != NULL);
 
 	/*
@@ -2363,10 +2366,11 @@
 	{
 		RangeTblRef *rtr = (RangeTblRef *) node;
 		RangeTblEntry *rte = rt_fetch(rtr->rtindex, pstate->p_rtable);
-		Query	   *selectQuery = rte->subquery;
+		Query	   *selectQuery = rte->u.sub.subquery;
 		List	   *result = NIL;
 		List	   *tl;
 
+		Assert(rte->rtetype == RTE_SUBSELECT);
 		Assert(selectQuery != NULL);
 		/* Get types of non-junk columns */
 		foreach(tl, selectQuery->targetList)
@@ -2875,17 +2879,25 @@
 			RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
 
 			++i;
-			if (rte->subquery)
+			if (rte->rtetype ==  RTE_SUBSELECT)
 			{
 				/* FOR UPDATE of subquery is propagated to subquery's rels */
-				transformForUpdate(rte->subquery, makeList1(NULL));
+				transformForUpdate( rte->u.sub.subquery, makeList1(NULL));
 			}
-			else
+			else if ( rte->rtetype == RTE_RELATION )
 			{
 				if (!intMember(i, rowMarks))	/* avoid duplicates */
 					rowMarks = lappendi(rowMarks, i);
 				rte->checkForWrite = true;
 			}
+			else if ( rte->rtetype == RTE_PORTAL )
+			{ 
+               /* do nothing */
+			}
+			else 
+			{
+				elog(ERROR, "FOR UPDATE: unknown RTE type %d", rte->rtetype);
+			}
 		}
 	}
 	else
@@ -2903,16 +2915,25 @@
 				++i;
 				if (strcmp(rte->eref->relname, relname) == 0)
 				{
-					if (rte->subquery)
+					if (rte->rtetype == RTE_SUBSELECT) 
 					{
 						/* propagate to subquery */
-						transformForUpdate(rte->subquery, makeList1(NULL));
+						transformForUpdate( rte->u.sub.subquery, makeList1(NULL));
 					}
-					else
+					else if (rte->rtetype == RTE_RELATION)
 					{
 						if (!intMember(i, rowMarks))	/* avoid duplicates */
 							rowMarks = lappendi(rowMarks, i);
 						rte->checkForWrite = true;
+					} 
+					else if (rte->rtetype == RTE_PORTAL)
+					{ 
+               			/* do nothing */
+					}
+					else 
+					{
+						elog(ERROR, "FOR UPDATE: unknown RTE type %d", 
+								rte->rtetype);
 					}
 					break;
 				}
Index: src/backend/parser/gram.y
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.252
diff --unified -r2.252 gram.y
--- src/backend/parser/gram.y	2001/09/20 14:20:27	2.252
+++ src/backend/parser/gram.y	2001/09/21 17:32:47
@@ -3732,8 +3732,17 @@
  * and joined_table := '(' joined_table ')'.  So, we must have the
  * redundant-looking productions here instead.
  */
-table_ref:  relation_expr
+table_ref:  
+		CURSOR relation_name
 				{
+					PortalRangeVar *n = makeNode(PortalRangeVar);
+					n->portal = $2;
+					n->name = NULL;
+					$$ = (Node *) n;
+				}
+	|
+		relation_expr
+				{
 					$$ = (Node *) $1;
 				}
 		| relation_expr alias_clause
@@ -5600,7 +5609,6 @@
 		| CREATE						{ $$ = "create"; }
 		| CREATEDB						{ $$ = "createdb"; }
 		| CREATEUSER					{ $$ = "createuser"; }
-		| CURSOR						{ $$ = "cursor"; }
 		| CYCLE							{ $$ = "cycle"; }
 		| DATABASE						{ $$ = "database"; }
 		| DECLARE						{ $$ = "declare"; }
Index: src/backend/parser/parse_clause.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/parser/parse_clause.c,v
retrieving revision 1.82
diff --unified -r1.82 parse_clause.c
--- src/backend/parser/parse_clause.c	2001/08/09 18:28:17	1.82
+++ src/backend/parser/parse_clause.c	2001/09/21 17:32:47
@@ -31,6 +31,7 @@
 #include "parser/parse_type.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/portal.h"
 
 
 #define ORDER_CLAUSE 0
@@ -80,6 +81,7 @@
 
 	/*
 	 * The grammar will have produced a list of RangeVars,
+         * FuncRangeVars, PortalRangeVars,
 	 * RangeSubselects, and/or JoinExprs. Transform each one (possibly
 	 * adding entries to the rtable), check for duplicate refnames, and
 	 * then add it to the joinlist and namespace.
@@ -392,6 +394,52 @@
 
 
 /*
+ * transformPortalRange --- transform FROM CURSOR FOO 
+ */
+static RangeTblRef *
+transformPortalRange(ParseState *pstate, PortalRangeVar *r)
+{
+	RangeTblEntry *rte;
+	RangeTblRef *rtr;
+	Portal      portal;
+	QueryDesc  *queryDesc;
+	TupleDesc  tupleDesc;
+	EState     *estate;
+	char 	   *portalname;
+
+    /* look up information for the portal */
+    portalname = r->portal;
+	portal = GetPortalByName(portalname);
+	if (!PortalIsValid(portal))
+		elog(ERROR, "transformPortalRange: portal \"%s\" not found",
+			portalname);
+
+    queryDesc = PortalGetQueryDesc(portal);
+	tupleDesc = PortalGetTupleDesc(portal);
+    estate = PortalGetState(portal); /* XXX: check state ? */
+
+	if (queryDesc->operation != CMD_SELECT)
+		elog(ERROR, "Expected SELECT query from the portal %s", portalname);
+
+	/*
+	 * OK, build an RTE for the subquery.
+	 */
+	rte = addRangeTableEntryForPortal(pstate, portalname, r->name);
+
+	/*
+	 * We create a RangeTblRef, but we do not add it to the joinlist or
+	 * namespace; our caller must do that if appropriate.
+	 */
+	rtr = makeNode(RangeTblRef);
+	/* assume new rte is at end */
+	rtr->rtindex = length(pstate->p_rtable);
+	Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
+
+	return rtr;
+}
+
+
+/*
  * transformRangeSubselect --- transform a sub-SELECT appearing in FROM
  */
 static RangeTblRef *
@@ -480,6 +528,15 @@
 		RangeTblRef *rtr;
 
 		rtr = transformTableEntry(pstate, (RangeVar *) n);
+		*containedRels = makeListi1(rtr->rtindex);
+		return (Node *) rtr;
+	}
+	if (IsA(n, PortalRangeVar))
+	{
+		/* reference to cursor */
+		RangeTblRef *rtr;
+
+		rtr = transformPortalRange(pstate, (PortalRangeVar *) n);
 		*containedRels = makeListi1(rtr->rtindex);
 		return (Node *) rtr;
 	}
Index: src/backend/parser/parse_func.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/parser/parse_func.c,v
retrieving revision 1.110
diff --unified -r1.110 parse_func.c
--- src/backend/parser/parse_func.c	2001/08/09 18:28:18	1.110
+++ src/backend/parser/parse_func.c	2001/09/21 17:32:54
@@ -386,7 +386,14 @@
 			 * signal that the runtime representation will be a pointer
 			 * not an Oid.
 			 */
-			if (rte->relname == NULL)
+			if (rte->rtetype == RTE_PORTAL)
+			{ 
+              /* RTE is a portal reference, possible? not supported yet */
+		 		elog(ERROR, "Cannot pass tuple from portal %s to function %s",
+						 refname, funcname);
+ 			}
+
+			if (rte->rtetype == RTE_SUBSELECT)
 			{
 				/*
 				 * RTE is a subselect; must fail for lack of a specific type
@@ -407,7 +414,7 @@
 				}
 			}
 
-			toid = typenameTypeId(rte->relname);
+			toid = typenameTypeId( rte->u.rel.relname);
 
 			/* replace it in the arg list */
 			lfirst(i) = makeVar(vnum,
Index: src/backend/parser/parse_node.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/parser/parse_node.c,v
retrieving revision 1.55
diff --unified -r1.55 parse_node.c
--- src/backend/parser/parse_node.c	2001/08/09 18:28:18	1.55
+++ src/backend/parser/parse_node.c	2001/09/21 17:32:54
@@ -34,6 +34,7 @@
 #include "utils/varbit.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
+#include "utils/portal.h"
 
 static bool fitsInFloat(Value *value);
 
@@ -170,31 +171,44 @@
 
 	vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
 
-	if (rte->relid != InvalidOid)
+	if (rte->rtetype == RTE_RELATION)
 	{
 		/* Plain relation RTE --- get the attribute's type info */
 		HeapTuple	tp;
 		Form_pg_attribute att_tup;
 
 		tp = SearchSysCache(ATTNUM,
-							ObjectIdGetDatum(rte->relid),
+							ObjectIdGetDatum(rte->u.rel.relid),
 							Int16GetDatum(attrno),
 							0, 0);
 		/* this shouldn't happen... */
 		if (!HeapTupleIsValid(tp))
 			elog(ERROR, "Relation %s does not have attribute %d",
-				 rte->relname, attrno);
+				 rte->u.rel.relname, attrno);
 		att_tup = (Form_pg_attribute) GETSTRUCT(tp);
 		vartypeid = att_tup->atttypid;
 		type_mod = att_tup->atttypmod;
 		ReleaseSysCache(tp);
 	}
-	else
+    else if (rte->rtetype == RTE_PORTAL)
+	{   /* Portal RTE, get type info from it */
+		TupleDesc tupleDesc = 
+			GetPortalTupleDescByName(rte->u.portal.portalname);
+		Form_pg_attribute att_tup;
+
+		Assert( attrno <= (tupleDesc->natts) );
+
+		att_tup = tupleDesc->attrs[attrno-1];
+
+		vartypeid = att_tup->atttypid;
+		type_mod = att_tup->atttypmod;
+	}
+	else if (rte->rtetype == RTE_SUBSELECT)
 	{
 		/* Subselect RTE --- get type info from subselect's tlist */
 		List	   *tlistitem;
 
-		foreach(tlistitem, rte->subquery->targetList)
+		foreach(tlistitem, rte->u.sub.subquery->targetList)
 		{
 			TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
 
@@ -208,6 +222,10 @@
 		if (tlistitem == NIL)
 			elog(ERROR, "Subquery %s does not have attribute %d",
 				 rte->eref->relname, attrno);
+	}
+ 	else 
+	{
+			elog(ERROR, "make_var: Unknown RTE type");
 	}
 
 	return makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);
Index: src/backend/parser/parse_relation.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/parser/parse_relation.c,v
retrieving revision 1.56
diff --unified -r1.56 parse_relation.c
--- src/backend/parser/parse_relation.c	2001/08/10 18:57:37	1.56
+++ src/backend/parser/parse_relation.c	2001/09/21 17:32:54
@@ -28,6 +28,7 @@
 #include "rewrite/rewriteManip.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
+#include "utils/portal.h"
 #include "utils/syscache.h"
 
 
@@ -318,7 +319,7 @@
 	 * If the RTE represents a table (not a sub-select), consider system
 	 * column names.
 	 */
-	if (rte->relid != InvalidOid)
+	if (rte->rtetype == RTE_RELATION)
 	{
 		/* quick check to see if name could be a system column */
 		attnum = specialAttNum(colname);
@@ -326,7 +327,7 @@
 		{
 			/* now check to see if column actually is defined */
 			if (SearchSysCacheExists(ATTNUM,
-									 ObjectIdGetDatum(rte->relid),
+									 ObjectIdGetDatum(rte->u.rel.relid),
 									 Int16GetDatum(attnum),
 									 0, 0))
 			{
@@ -510,9 +511,9 @@
 	int			numaliases;
 	int			varattno;
 
-	rte->relname = relname;
+	rte->rtetype = RTE_RELATION;
+	rte->u.rel.relname = relname;
 	rte->alias = alias;
-	rte->subquery = NULL;
 
 	/*
 	 * Get the rel's OID.  This access also ensures that we have an
@@ -522,7 +523,7 @@
 	 */
 	lockmode = isForUpdate(pstate, relname) ? RowShareLock : AccessShareLock;
 	rel = heap_openr(relname, lockmode);
-	rte->relid = RelationGetRelid(rel);
+	rte->u.rel.relid = RelationGetRelid(rel);
 
 	eref = alias ? (Attr *) copyObject(alias) : makeAttr(refname, NULL);
 	numaliases = length(eref->attrs);
@@ -577,7 +578,75 @@
 	if (pstate != NULL)
 		pstate->p_rtable = lappend(pstate->p_rtable, rte);
 
-	return rte;
+	return (RangeTblEntry *) rte;
+}
+
+/*
+ * Add an entry for a portal reference or for a function-used-as-table
+ *  to the pstate's range table (p_rtable).
+ *
+ * This is just like addRangeTableEntry() except that it makes a portal RTE.
+ */
+RangeTblEntry *
+addRangeTableEntryForPortal(ParseState *pstate,
+							char *portalname,
+						    Attr *alias
+							)
+{
+	RangeTblEntry *rte = makeNode(RangeTblEntry);
+	char	   *refname = alias ? alias->relname : portalname;
+	Attr	   *eref;
+	int			maxattrs;
+	int			numaliases;
+	int			varattno;
+	TupleDesc 	tupleDesc;
+
+	rte->rtetype = RTE_PORTAL;
+	rte->u.portal.portalname = portalname;
+	rte->alias = alias;
+
+	eref = alias ? (Attr *) copyObject(alias) : makeAttr(refname, NULL);
+	numaliases = length(eref->attrs);
+
+	/* fill in any unspecified alias columns */
+	varattno = 0;
+
+	tupleDesc = GetPortalTupleDescByName( portalname );
+	maxattrs = tupleDesc->natts;
+	if (maxattrs < numaliases)
+		elog(ERROR, "Portal \"%s\" has %d columns available but %d aliases specified",
+			 refname, maxattrs, numaliases);
+
+	/* fill in any unspecified alias columns */
+	for (varattno = numaliases; varattno < maxattrs; varattno++)
+	{
+		char	   *attrname;
+
+		attrname = pstrdup(NameStr(tupleDesc->attrs[varattno]->attname));
+		eref->attrs = lappend(eref->attrs, makeString(attrname));
+	}
+	rte->eref = eref;
+
+	/*----------
+	 * Flags:
+	 * - this RTE should be expanded to include descendant tables,
+	 * - this RTE is in the FROM clause,
+	 * - this RTE should be checked for read/write access rights.
+	 */
+	rte->inh = false;
+	rte->inFromCl = true;
+	rte->checkForRead = false;
+	rte->checkForWrite = false;
+	rte->checkAsUser = InvalidOid;		/* not set-uid by default, either */
+
+	/*
+	 * Add completed RTE to pstate's range table list, but not to join
+	 * list nor namespace --- caller must do that if appropriate.
+	 */
+	if (pstate != NULL)
+		pstate->p_rtable = lappend(pstate->p_rtable, rte);
+
+	return (RangeTblEntry *) rte;
 }
 
 /*
@@ -599,9 +668,8 @@
 	int			varattno;
 	List	   *tlistitem;
 
-	rte->relname = NULL;
-	rte->relid = InvalidOid;
-	rte->subquery = subquery;
+	rte->rtetype = RTE_SUBSELECT;
+	rte->u.sub.subquery = subquery;
 	rte->alias = alias;
 
 	eref = copyObject(alias);
@@ -654,7 +722,7 @@
 	if (pstate != NULL)
 		pstate->p_rtable = lappend(pstate->p_rtable, rte);
 
-	return rte;
+	return (RangeTblEntry *) rte;
 }
 
 /*
@@ -755,14 +823,50 @@
 
 	/* Need the RT index of the entry for creating Vars */
 	rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
+
+	if (rte->rtetype == RTE_PORTAL)
+	{
+		/* Portal RTE */
+		TupleDesc 	td;
+		int			maxattrs;
+        td = GetPortalTupleDescByName( rte->u.portal.portalname );
+
+		maxattrs = td->natts;
+
+		for (varattno = 0; varattno < maxattrs; varattno++)
+		{
+			Form_pg_attribute attr = td->attrs[varattno];
+
+			if (colnames)
+			{
+				char	   *label;
+
+				if (varattno < length(rte->eref->attrs))
+					label = strVal(nth(varattno, rte->eref->attrs));
+				else
+					label = NameStr(attr->attname);
+				*colnames = lappend(*colnames, makeString(pstrdup(label)));
+			}
+
+			if (colvars)
+			{
+				Var		   *varnode;
+
+				varnode = makeVar(rtindex, attr->attnum,
+								  attr->atttypid, attr->atttypmod,
+								  sublevels_up);
 
-	if (rte->relname)
+				*colvars = lappend(*colvars, varnode);
+			}
+		}
+	}
+	else if (rte->rtetype == RTE_RELATION)
 	{
 		/* Ordinary relation RTE */
 		Relation	rel;
 		int			maxattrs;
 
-		rel = heap_openr(rte->relname, AccessShareLock);
+		rel = heap_openr( rte->u.rel.relname, AccessShareLock);
 
 		maxattrs = RelationGetNumberOfAttributes(rel);
 
@@ -800,14 +904,15 @@
 
 		heap_close(rel, AccessShareLock);
 	}
-	else
+	else if (rte->rtetype == RTE_SUBSELECT)
 	{
 		/* Subquery RTE */
 		List	   *aliasp = rte->eref->attrs;
 		List	   *tlistitem;
 
 		varattno = 0;
-		foreach(tlistitem, rte->subquery->targetList)
+		Assert( PointerIsValid(rte->u.sub.subquery) );
+		foreach(tlistitem,rte->u.sub.subquery->targetList)
 		{
 			TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
 
@@ -838,6 +943,10 @@
 			}
 		}
 	}
+    else 
+    { 
+       elog(ERROR, "expandRTE: Unknown RTE type");
+    }
 }
 
 /*
@@ -913,11 +1022,11 @@
 
 /* ----------
  * get_rte_attribute_name
- *		Get an attribute name from a RangeTblEntry
+ *		Get an attribute name from a RangeTblEntryRelation
  *
  * This is unlike get_attname() because we use aliases if available.
  * In particular, it will work on an RTE for a subselect, whereas
- * get_attname() only works on real relations.
+ * get_attname() only works on real relations. ??? (below)
  *
  * "*" is returned if the given attnum is InvalidAttrNumber --- this case
  * occurs when a Var represents a whole tuple of a relation.
@@ -926,6 +1035,7 @@
  * not guaranteed unique, and may not even have scope across the whole
  * query.  Cleanest fix would be to add refname/attname to Var nodes and
  * just print those, rather than indulging in this hack.
+ *
  * ----------
  */
 char *
@@ -947,17 +1057,17 @@
 	 * if alias name list is too short (which probably can't happen
 	 * anymore).  Neither of these cases is valid for a subselect RTE.
 	 */
-	if (rte->relid == InvalidOid)
+	if (!rte->rtetype == RTE_RELATION)
 		elog(ERROR, "Invalid attnum %d for rangetable entry %s",
 			 attnum, rte->eref->relname);
 
 	/*
 	 * Use the real name of the table's column
 	 */
-	attname = get_attname(rte->relid, attnum);
+	attname = get_attname(rte->u.rel.relid, attnum);
 	if (attname == NULL)
 		elog(ERROR, "cache lookup of attribute %d in relation %u failed",
-			 attnum, rte->relid);
+			 attnum, rte->u.rel.relid);
 	return attname;
 }
 
@@ -1069,3 +1179,22 @@
 			 pstate->parentParseState != NULL ? " in subquery" : "",
 			 refname);
 }
+
+/*
+ *              getrelid
+ *
+ *              Given the range index of a relation, return the corresponding
+ *              relation OID.  Note that InvalidOid will be returned if the
+ *              RTE is for not for a relation. (SubSelect or Portal)
+ */
+
+/* this used to be a #define */
+Oid getrelid (int rangeindex, List * rangetable) {
+	RangeTblEntry *rte = rt_fetch(rangeindex, rangetable);
+	if (rte->rtetype == RTE_RELATION) {
+		return rte->u.rel.relid;
+ 	} else { 
+		return InvalidOid;
+	}
+}
+
Index: src/backend/rewrite/rewriteDefine.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/rewrite/rewriteDefine.c,v
retrieving revision 1.63
diff --unified -r1.63 rewriteDefine.c
--- src/backend/rewrite/rewriteDefine.c	2001/08/12 21:35:18	1.63
+++ src/backend/rewrite/rewriteDefine.c	2001/09/21 17:32:54
@@ -446,10 +446,10 @@
 	{
 		RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
 
-		if (rte->subquery)
+		if ( rte->rtetype == RTE_SUBSELECT )
 		{
 			/* Recurse into subquery in FROM */
-			setRuleCheckAsUser(rte->subquery, userid);
+			setRuleCheckAsUser( rte->u.sub.subquery, userid);
 		}
 		else
 			rte->checkAsUser = userid;
Index: src/backend/rewrite/rewriteHandler.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/rewrite/rewriteHandler.c,v
retrieving revision 1.97
diff --unified -r1.97 rewriteHandler.c
--- src/backend/rewrite/rewriteHandler.c	2001/07/09 23:50:32	1.97
+++ src/backend/rewrite/rewriteHandler.c	2001/09/21 17:32:55
@@ -265,8 +265,8 @@
 				  bool relIsUsed)
 {
 	Query	   *rule_action;
-	RangeTblEntry *rte,
-			   *subrte;
+	RangeTblEntry *rte;
+	RangeTblEntry *subrte;
 
 	if (length(rule->actions) != 1)
 		elog(ERROR, "ApplyRetrieveRule: expected just one rule action");
@@ -289,9 +289,20 @@
 	 */
 	rte = rt_fetch(rt_index, parsetree->rtable);
 
-	rte->relname = NULL;
-	rte->relid = InvalidOid;
-	rte->subquery = rule_action;
+	/* We have to mutate whatever RTE kind it was into RTESubSelect  */
+	if ( rte->rtetype == RTE_SUBSELECT ) 
+	{
+		/* already was SubSelect, do nothing */
+	} else if (rte->rtetype == RTE_RELATION) {
+		/* this may be dangerous, as we are changing the type on the fly,
+           perhaps its a better idea to copy RTE, then mangle it? */
+
+		rte->rtetype = RTE_SUBSELECT;
+	} else {
+		elog(ERROR, "ApplyRetrieveRule: unknown original RTE type");
+ 	}
+
+	rte->u.sub.subquery = rule_action;
 	rte->inh = false;			/* must not be set for a subquery */
 
 	/*
@@ -299,7 +310,8 @@
 	 * The checks will actually be done against the *OLD* entry therein.
 	 */
 	subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable);
-	Assert(subrte->relid == relation->rd_id);
+	Assert( subrte->rtetype == RTE_RELATION );
+	Assert( subrte->u.rel.relid == relation->rd_id);
 	subrte->checkForRead = rte->checkForRead;
 	subrte->checkForWrite = rte->checkForWrite;
 	subrte->checkAsUser = rte->checkAsUser;
@@ -355,10 +367,10 @@
 			(rti == PRS2_OLD_VARNO || rti == PRS2_NEW_VARNO))
 			continue;
 
-		if (rte->subquery)
+		if (rte->rtetype == RTE_SUBSELECT )
 		{
 			/* FOR UPDATE of subquery is propagated to subquery's rels */
-			markQueryForUpdate(rte->subquery, false);
+			markQueryForUpdate( rte->u.sub.subquery, false);
 		}
 		else
 		{
@@ -437,13 +449,23 @@
 		rte = rt_fetch(rt_index, parsetree->rtable);
 
 		/*
+		 * A portal RTE can't have associated rules, so there's nothing
+		 * to do to this level of the query.
+		 */
+
+		if (rte->rtetype == RTE_PORTAL)
+		{
+			continue;
+		}
+
+		/*
 		 * A subquery RTE can't have associated rules, so there's nothing
 		 * to do to this level of the query, but we must recurse into the
 		 * subquery to expand any rule references in it.
 		 */
-		if (rte->subquery)
+		if (rte->rtetype == RTE_SUBSELECT)
 		{
-			rte->subquery = fireRIRrules(rte->subquery);
+			rte->u.sub.subquery = fireRIRrules(rte->u.sub.subquery);
 			continue;
 		}
 
@@ -459,6 +481,8 @@
 		if (!relIsUsed && rt_index != parsetree->resultRelation)
 			continue;
 
+		/* by now, we should only have plain RTE to deal with */
+		Assert(rte->rtetype == RTE_RELATION);
 		/*
 		 * This may well be the first access to the relation during the
 		 * current statement (it will be, if this Query was extracted from
@@ -479,7 +503,7 @@
 		else
 			lockmode = AccessShareLock;
 
-		rel = heap_openr(rte->relname, lockmode);
+		rel = heap_openr(rte->u.rel.relname, lockmode);
 
 		/*
 		 * Check to see if relation's OID matches the RTE.  If not, the RTE
@@ -487,9 +511,9 @@
 		 * Eventually we might want to reparse the referencing rule, but
 		 * for now all we can do is punt.
 		 */
-		if (RelationGetRelid(rel) != rte->relid)
+		if (RelationGetRelid(rel) != rte->u.rel.relid)
 			elog(ERROR, "Relation \"%s\" with OID %u no longer exists",
-				 rte->relname, rte->relid);
+				 rte->u.rel.relname, rte->u.rel.relid);
 
 		/*
 		 * Collect the RIR rules that we must apply
@@ -757,6 +781,7 @@
 	result_relation = parsetree->resultRelation;
 	Assert(result_relation != 0);
 	rt_entry = rt_fetch(result_relation, parsetree->rtable);
+	Assert( rt_entry->rtetype == RTE_RELATION );
 
 	/*
 	 * This may well be the first access to the result relation during the
@@ -766,7 +791,7 @@
 	 * release it until end of transaction.  This protects the rewriter
 	 * and planner against schema changes mid-query.
 	 */
-	rt_entry_relation = heap_openr(rt_entry->relname, RowExclusiveLock);
+	rt_entry_relation = heap_openr(rt_entry->u.rel.relname, RowExclusiveLock);
 
 	/*
 	 * Check to see if relation's OID matches the RTE.  If not, the RTE
@@ -774,9 +799,9 @@
 	 * Eventually we might want to reparse the referencing rule, but
 	 * for now all we can do is punt.
 	 */
-	if (RelationGetRelid(rt_entry_relation) != rt_entry->relid)
+	if (RelationGetRelid(rt_entry_relation) != rt_entry->u.rel.relid)
 		elog(ERROR, "Relation \"%s\" with OID %u no longer exists",
-			 rt_entry->relname, rt_entry->relid);
+			 rt_entry->u.rel.relname, rt_entry->u.rel.relid);
 
 	/*
 	 * Collect and apply the appropriate rules.
@@ -943,7 +968,7 @@
 			RangeTblEntry *rte = rt_fetch(query->resultRelation,
 										  query->rtable);
 
-			if (rte->subquery)
+			if (rte->rtetype == RTE_SUBSELECT)
 			{
 				switch (query->commandType)
 				{
Index: src/backend/rewrite/rewriteManip.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/rewrite/rewriteManip.c,v
retrieving revision 1.58
diff --unified -r1.58 rewriteManip.c
--- src/backend/rewrite/rewriteManip.c	2001/09/07 20:52:31	1.58
+++ src/backend/rewrite/rewriteManip.c	2001/09/21 17:32:55
@@ -560,7 +560,8 @@
 	rtr = (RangeTblRef *) lfirst(parsetree->jointree->fromlist);
 	Assert(IsA(rtr, RangeTblRef));
 	selectrte = rt_fetch(rtr->rtindex, parsetree->rtable);
-	selectquery = selectrte->subquery;
+	Assert( selectrte->rtetype == RTE_SUBSELECT );
+	selectquery = selectrte->u.sub.subquery;
 	if (!(selectquery && IsA(selectquery, Query) &&
 		  selectquery->commandType == CMD_SELECT))
 		elog(ERROR, "getInsertSelectQuery: expected to find SELECT subquery");
@@ -571,7 +572,7 @@
 			"*NEW*") == 0)
 	{
 		if (subquery_ptr)
-			*subquery_ptr = &(selectrte->subquery);
+			*subquery_ptr = &(selectrte->u.sub.subquery);
 		return selectquery;
 	}
 	elog(ERROR, "getInsertSelectQuery: can't find rule placeholders");
Index: src/backend/utils/adt/ruleutils.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/utils/adt/ruleutils.c,v
retrieving revision 1.82
diff --unified -r1.82 ruleutils.c
--- src/backend/utils/adt/ruleutils.c	2001/08/12 21:35:19	1.82
+++ src/backend/utils/adt/ruleutils.c	2001/09/21 17:32:55
@@ -700,8 +700,9 @@
 
 	/* Build a minimal RTE for the rel */
 	rte = makeNode(RangeTblEntry);
-	rte->relname = relname;
-	rte->relid = relid;
+	rte->rtetype = RTE_RELATION;
+	rte->u.rel.relname = relname;
+	rte->u.rel.relid = relid;
 	rte->eref = makeNode(Attr);
 	rte->eref->relname = relname;
 	rte->inh = false;
@@ -1162,8 +1163,8 @@
 	{
 		RangeTblRef *rtr = (RangeTblRef *) setOp;
 		RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
-		Query	   *subquery = rte->subquery;
-
+		Query	   *subquery = rte->u.sub.subquery;
+		Assert( rte->rtetype == RTE_SUBSELECT );
 		Assert(subquery != NULL);
 		get_query_def(subquery, buf, context->namespaces);
 	}
@@ -1241,7 +1242,7 @@
 get_insert_query_def(Query *query, deparse_context *context)
 {
 	StringInfo	buf = context->buf;
-	RangeTblEntry *select_rte = NULL;
+	RangeTblEntry *select_rte = NULL; /* rte for underlying SELECT */
 	RangeTblEntry *rte;
 	char	   *sep;
 	List	   *l;
@@ -1253,7 +1254,7 @@
 	foreach(l, query->rtable)
 	{
 		rte = (RangeTblEntry *) lfirst(l);
-		if (rte->subquery == NULL)
+		if (rte->rtetype != RTE_SUBSELECT)
 			continue;
 		if (select_rte)
 			elog(ERROR, "get_insert_query_def: too many RTEs in INSERT!");
@@ -1264,8 +1265,9 @@
 	 * Start the query with INSERT INTO relname
 	 */
 	rte = rt_fetch(query->resultRelation, query->rtable);
+	Assert( rte->rtetype == RTE_RELATION );
 	appendStringInfo(buf, "INSERT INTO %s",
-					 quote_identifier(rte->relname));
+					 quote_identifier(rte->u.rel.relname));
 
 	/* Add the insert-column-names list */
 	sep = " (";
@@ -1301,7 +1303,7 @@
 		appendStringInfoChar(buf, ')');
 	}
 	else
-		get_query_def(select_rte->subquery, buf, NIL);
+		get_query_def(select_rte->u.sub.subquery, buf, NIL);
 }
 
 
@@ -1321,9 +1323,11 @@
 	 * Start the query with UPDATE relname SET
 	 */
 	rte = rt_fetch(query->resultRelation, query->rtable);
+
+	Assert(rte->rtetype == RTE_RELATION);
 	appendStringInfo(buf, "UPDATE %s%s SET ",
 					 only_marker(rte),
-					 quote_identifier(rte->relname));
+					 quote_identifier(rte->u.rel.relname));
 
 	/* Add the comma separated list of 'attname = value' */
 	sep = "";
@@ -1374,9 +1378,10 @@
 	 * Start the query with DELETE FROM relname
 	 */
 	rte = rt_fetch(query->resultRelation, query->rtable);
+	Assert(rte->rtetype == RTE_RELATION);
 	appendStringInfo(buf, "DELETE FROM %s%s",
 					 only_marker(rte),
-					 quote_identifier(rte->relname));
+					 quote_identifier(rte->u.rel.relname));
 
 	/* Add a WHERE clause if given */
 	if (query->jointree->quals != NULL)
@@ -2425,21 +2430,27 @@
 		int			varno = ((RangeTblRef *) jtnode)->rtindex;
 		RangeTblEntry *rte = rt_fetch(varno, query->rtable);
 
-		if (rte->relname)
+		if (rte->rtetype == RTE_RELATION)
 		{
 			/* Normal relation RTE */
 			appendStringInfo(buf, "%s%s",
 							 only_marker(rte),
-							 quote_identifier(rte->relname));
+				 quote_identifier( rte->u.rel.relname));
 		}
-		else
+		else if (rte->rtetype == RTE_SUBSELECT)
 		{
 			/* Subquery RTE */
-			Assert(rte->subquery != NULL);
 			appendStringInfoChar(buf, '(');
-			get_query_def(rte->subquery, buf, context->namespaces);
+			get_query_def( rte->u.sub.subquery, buf, context->namespaces);
 			appendStringInfoChar(buf, ')');
-		}
+		} else if (rte->rtetype == RTE_PORTAL) 
+		{
+			appendStringInfo(buf, " CURSOR %s",
+							 rte->u.portal.portalname);
+		} else 
+			elog(ERROR, "get_from_clause_item: unknown RTE type");
+
+
 		if (rte->alias != NULL)
 		{
 			appendStringInfo(buf, " %s",
Index: src/backend/utils/mmgr/portalmem.c
===================================================================
RCS file: /cvs/pgsql/pgsql/src/backend/utils/mmgr/portalmem.c,v
retrieving revision 1.41
diff --unified -r1.41 portalmem.c
--- src/backend/utils/mmgr/portalmem.c	2001/03/22 04:00:08	1.41
+++ src/backend/utils/mmgr/portalmem.c	2001/09/21 17:32:55
@@ -36,6 +36,8 @@
 #include "lib/hasht.h"
 #include "utils/memutils.h"
 #include "utils/portal.h"
+#include "executor/execdefs.h"
+#include "executor/executor.h"
 
 /* ----------------
  *		Global state
@@ -150,6 +152,26 @@
 }
 
 /*
+ * GetPortalTupleDescByName
+ *		Returns a TupleDesc for portal given a portal name.
+ *      elog(ERROR) if it doesn't exist
+ */
+TupleDesc
+GetPortalTupleDescByName(char *name)
+{
+	Portal		portal;
+
+	Assert( PointerIsValid(name) );
+	PortalHashTableLookup(name, portal);
+
+    if ( !PointerIsValid( portal ))
+		elog( ERROR, 
+				"GetPortalTupleDescByName: portal %s does not exist", name);
+
+	return portal->attinfo;
+}
+
+/*
  * PortalSetQuery
  *		Attaches a "query" to portal.
  *
@@ -225,6 +247,101 @@
 	PortalHashTableInsert(portal);
 
 	return portal;
+}
+
+/* Run the portal's underlying query for number of records, into dest
+ * properly setting context and atStart/atEnd flags after execution.
+ *
+ * If tts!=NULL, will put result of ExecutorRun into variable pointed by TTS
+ * 
+ * Returns number of records processed.
+ */
+int
+PortalRun(Portal portal, bool forward, long count, CommandDest dest, TupleTableSlot **tts)
+{
+	QueryDesc  *queryDesc;
+	EState     *estate;
+	TupleTableSlot *tts_exec;
+	bool        faked_desc = false;
+	bool 		did_fetch = false; /* did we actually try to run executor? */
+	MemoryContext oldcontext;
+
+	if (tts) *tts=NULL;
+
+	/*
+	 * switch into the portal context
+	 */
+	oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
+
+	/*
+	 * tell the destination to prepare to receive some tuples.
+	 *
+	 * If passed CommandDest  is not the same as portal's CommandDest, 
+     * make a temporary QueryDesc with the new destination.
+	 */
+	queryDesc = PortalGetQueryDesc(portal);
+	estate = PortalGetState(portal);
+
+	if (dest != queryDesc->dest)
+	{
+		QueryDesc  *qdesc = (QueryDesc *) palloc(sizeof(QueryDesc));
+
+		memcpy(qdesc, queryDesc, sizeof(QueryDesc));
+		qdesc->dest = dest;
+		queryDesc = qdesc;
+		faked_desc = true;
+	}
+
+	/*
+	 * Determine which direction to go in, and check to see if we're
+	 * already at the end of the available tuples in that direction.  If
+	 * so, do nothing.	(This check exists because not all plan node types
+	 * are robust about being called again if they've already returned
+	 * NULL once.)	If it's OK to do the fetch, call the executor.  Then,
+	 * update the atStart/atEnd state depending on the number of tuples
+	 * that were retrieved.
+	 */
+	if (forward)
+	{
+		if (!portal->atEnd)
+		{
+			tts_exec=ExecutorRun(queryDesc, estate, EXEC_FOR, (long) count);
+			if (estate->es_processed > 0)
+				portal->atStart = false;		/* OK to back up now */
+			if (count <= 0 || (int) estate->es_processed < count)
+				portal->atEnd = true;	/* we retrieved 'em all */
+			did_fetch = true;
+		}
+	}
+	else
+	{
+		if (!portal->atStart)
+		{
+			tts_exec=ExecutorRun(queryDesc, estate, EXEC_BACK, (long) count);
+			if (estate->es_processed > 0)
+				portal->atEnd = false;	/* OK to go forward now */
+			if (count <= 0 || (int) estate->es_processed < count)
+				portal->atStart = true; /* we retrieved 'em all */
+			did_fetch = true;
+		}
+	}
+
+	/*
+	 * Clean up and switch back to old context.
+	 */
+	if (faked_desc)			/* MOVE */
+		pfree(queryDesc);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/* store what we got from Executor */
+	if (tts && did_fetch) 
+		*tts=tts_exec;
+
+	/* check whether we actually retrieved records */
+	if (did_fetch)
+		return estate->es_processed;
+	return 0;
 }
 
 /*
Index: src/include/nodes/execnodes.h
===================================================================
RCS file: /cvs/pgsql/pgsql/src/include/nodes/execnodes.h,v
retrieving revision 1.62
diff --unified -r1.62 execnodes.h
--- src/include/nodes/execnodes.h	2001/07/16 05:07:00	1.62
+++ src/include/nodes/execnodes.h	2001/09/21 17:32:55
@@ -481,6 +481,19 @@
 	EState	   *sss_SubEState;
 } SubqueryScanState;
 
+/* ----------------
+ *	 PortalScanState information
+ *
+ *		PortalScanState is used for scanning a portal.
+ *
+ * ----------------
+ */
+typedef struct PortalScanState
+{
+	CommonScanState csstate;	/* its first field is NodeTag */
+	struct PortalData	   * portal;
+} PortalScanState;
+
 /* ----------------------------------------------------------------
  *				 Join State Information
  * ----------------------------------------------------------------
Index: src/include/nodes/nodes.h
===================================================================
RCS file: /cvs/pgsql/pgsql/src/include/nodes/nodes.h,v
retrieving revision 1.93
diff --unified -r1.93 nodes.h
--- src/include/nodes/nodes.h	2001/07/16 19:12:58	1.93
+++ src/include/nodes/nodes.h	2001/09/21 17:32:55
@@ -49,6 +49,7 @@
 	T_SubPlan,
 	T_TidScan,
 	T_SubqueryScan,
+	T_PortalScan,
 
 	/*
 	 * TAGS FOR PRIMITIVE NODES (primnodes.h)
@@ -120,6 +121,7 @@
 	T_SubqueryScanState,
 	T_SetOpState,
 	T_LimitState,
+	T_PortalScanState,
 
 	/*
 	 * TAGS FOR MEMORY NODES (memnodes.h)
@@ -222,6 +224,8 @@
 	T_CaseWhen,
 	T_FkConstraint,
 	T_PrivGrantee,
+/*	T_FuncRangeVar, */
+	T_PortalRangeVar, 
 
 	/*
 	 * TAGS FOR FUNCTION-CALL CONTEXT AND RESULTINFO NODES (see fmgr.h)
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /cvs/pgsql/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.144
diff --unified -r1.144 parsenodes.h
--- src/include/nodes/parsenodes.h	2001/09/18 01:59:06	1.144
+++ src/include/nodes/parsenodes.h	2001/09/21 17:32:55
@@ -15,7 +15,9 @@
 #define PARSENODES_H
 
 #include "nodes/primnodes.h"
+#include "access/tupdesc.h"
 
+
 /*****************************************************************************
  *	Query Tree
  *****************************************************************************/
@@ -890,6 +892,7 @@
 	Node	   *limitOffset;	/* # of result tuples to skip */
 	Node	   *limitCount;		/* # of result tuples to return */
 	List	   *forUpdate;		/* FOR UPDATE clause */
+	bool 	isFunc; 		/* select * from func(args) */
 
 	/*
 	 * These fields are used only in upper-level SelectStmts.
@@ -1182,6 +1185,28 @@
 } SortGroupBy;
 
 /*
+ * FuncRangeVar - range variable, used in FROM clauses
+ *                select x,y from foo(1) as bar(x,y)
+ */
+typedef struct FuncRangeVar
+{
+	NodeTag		type;
+  	Node 	*func;
+	Attr	   *name;	/* optional table alias & column aliases */
+} FuncRangeVar;
+
+/*
+ * PortalRangeVar - range variable, used in FROM clauses
+ *                select x,y from cursor foo
+ */
+typedef struct PortalRangeVar
+{
+	NodeTag		type;
+  	char 	*portal;
+	Attr	*name;			/* optional table alias & column aliases */
+} PortalRangeVar;
+
+/*
  * RangeVar - range variable, used in FROM clauses
  */
 typedef struct RangeVar
@@ -1288,22 +1313,17 @@
  *	  (This allows rules to act as setuid gateways.)
  *--------------------
  */
+typedef enum RTEType {
+	RTE_RELATION,
+	RTE_SUBSELECT,
+	RTE_PORTAL
+} RTEType;
+
 typedef struct RangeTblEntry
 {
 	NodeTag		type;
-
-	/*
-	 * Fields valid for a plain relation RTE (else NULL/zero):
-	 */
-	char	   *relname;		/* real name of the relation */
-	Oid			relid;			/* OID of the relation */
-
+    RTEType 	rtetype;
 	/*
-	 * Fields valid for a subquery RTE (else NULL):
-	 */
-	Query	   *subquery;		/* the sub-query */
-
-	/*
 	 * Fields valid in all RTEs:
 	 */
 	Attr	   *alias;			/* user-written alias clause, if any */
@@ -1313,6 +1333,22 @@
 	bool		checkForRead;	/* check rel for read access */
 	bool		checkForWrite;	/* check rel for write access */
 	Oid			checkAsUser;	/* if not zero, check access as this user */
+
+    union {
+		struct  {
+			/* Fields for a plain relation RTE (rtetype=RTE_RELATION) */
+			char	   *relname;		/* real name of the relation */
+			Oid			relid;			/* OID of the relation */
+		} rel;
+ 		struct {
+			/* Fields for a subquery RTE (rtetype=RTE_SUBSELECT) */
+			Query	   *subquery;		/* the sub-query */
+		} sub;
+		struct { 
+ 			/* fields for portal RTE (rtetype=RTE_PORTAL) */
+    		char 		  *portalname;    /* portal's name */
+		} portal;
+ 	} u;
 } RangeTblEntry;
 
 /*
Index: src/include/nodes/plannodes.h
===================================================================
RCS file: /cvs/pgsql/pgsql/src/include/nodes/plannodes.h,v
retrieving revision 1.50
diff --unified -r1.50 plannodes.h
--- src/include/nodes/plannodes.h	2001/09/18 01:59:07	1.50
+++ src/include/nodes/plannodes.h	2001/09/21 17:32:55
@@ -242,6 +242,17 @@
 	Plan	   *subplan;
 } SubqueryScan;
 
+/* ----------------
+ *		portal scan node
+ *
+ * PortalScan is for scanning the existing portal. Will need additional 
+ * fields, but currently, its just the Scan.
+ */
+typedef struct PortalScan
+{
+	Scan		scan;
+} PortalScan;
+
 /*
  * ==========
  * Join nodes
Index: src/include/nodes/relation.h
===================================================================
RCS file: /cvs/pgsql/pgsql/src/include/nodes/relation.h,v
retrieving revision 1.58
diff --unified -r1.58 relation.h
--- src/include/nodes/relation.h	2001/08/21 16:36:06	1.58
+++ src/include/nodes/relation.h	2001/09/21 17:32:55
@@ -129,10 +129,19 @@
  *----------
  */
 
+typedef enum RelOptInfoType {
+  REL_PLAIN, 
+  REL_SUBQUERY,
+  REL_JOIN,
+  REL_PORTAL
+} RelOptInfoType;
+
 typedef struct RelOptInfo
 {
 	NodeTag		type;
 
+	RelOptInfoType		reltype;
+
 	/* all relations included in this RelOptInfo */
 	Relids		relids;			/* integer list of base relids (RT
 								 * indexes) */
@@ -148,11 +157,12 @@
 	struct Path *cheapest_total_path;
 	bool		pruneable;
 
-	/* information about a base rel (not set for join rels!) */
-	bool		issubquery;
+	/* Following fields are only for REL_PLAIN */
 	List	   *indexlist;
 	long		pages;
 	double		tuples;
+
+	/* following field is only for REL_SUBQUERY */
 	struct Plan *subplan;
 
 	/* used by various scans and joins: */
Index: src/include/optimizer/pathnode.h
===================================================================
RCS file: /cvs/pgsql/pgsql/src/include/optimizer/pathnode.h,v
retrieving revision 1.38
diff --unified -r1.38 pathnode.h
--- src/include/optimizer/pathnode.h	2001/06/05 05:26:05	1.38
+++ src/include/optimizer/pathnode.h	2001/09/21 17:32:55
@@ -15,6 +15,7 @@
 #define PATHNODE_H
 
 #include "nodes/relation.h"
+#include "utils/portal.h"
 
 /*
  * prototypes for pathnode.c
@@ -35,6 +36,7 @@
 extern TidPath *create_tidscan_path(Query *root, RelOptInfo *rel,
 									List *tideval);
 extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths);
+extern Path *create_portalscan_path(RelOptInfo *rel, Portal portal);
 extern Path *create_subqueryscan_path(RelOptInfo *rel);
 
 extern NestPath *create_nestloop_path(Query *root,
Index: src/include/parser/parse_relation.h
===================================================================
RCS file: /cvs/pgsql/pgsql/src/include/parser/parse_relation.h,v
retrieving revision 1.24
diff --unified -r1.24 parse_relation.h
--- src/include/parser/parse_relation.h	2001/08/10 18:57:41	1.24
+++ src/include/parser/parse_relation.h	2001/09/21 17:32:55
@@ -36,6 +36,10 @@
 							  Query *subquery,
 							  Attr *alias,
 							  bool inFromCl);
+extern RangeTblEntry * addRangeTableEntryForPortal(ParseState *pstate,
+                            char *portalname,
+                            Attr *alias
+                            );
 extern void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
 			  bool addToJoinList, bool addToNameSpace);
 extern RangeTblEntry *addImplicitRTE(ParseState *pstate, char *relname);
Index: src/include/parser/parsetree.h
===================================================================
RCS file: /cvs/pgsql/pgsql/src/include/parser/parsetree.h,v
retrieving revision 1.13
diff --unified -r1.13 parsetree.h
--- src/include/parser/parsetree.h	2001/01/24 19:43:27	1.13
+++ src/include/parser/parsetree.h	2001/09/21 17:32:55
@@ -36,15 +36,8 @@
 #define rt_store(rangetable_index, rangetable, rt) \
 	set_nth(rangetable, (rangetable_index)-1, rt)
 
-/*
- *		getrelid
- *
- *		Given the range index of a relation, return the corresponding
- *		relation OID.  Note that InvalidOid will be returned if the
- *		RTE is for a sub-select rather than a relation.
- */
-#define getrelid(rangeindex,rangetable) \
-	(rt_fetch(rangeindex, rangetable)->relid)
+/* getrelid is no longer a #define */
+extern Oid getrelid (int rangeindex, List * rangetable);
 
 /*
  * Given an RTE and an attribute number, return the appropriate
Index: src/include/utils/portal.h
===================================================================
RCS file: /cvs/pgsql/pgsql/src/include/utils/portal.h,v
retrieving revision 1.28
diff --unified -r1.28 portal.h
--- src/include/utils/portal.h	2001/05/21 14:22:18	1.28
+++ src/include/utils/portal.h	2001/09/21 17:32:55
@@ -63,9 +63,11 @@
 extern Portal CreatePortal(char *name);
 extern void PortalDrop(Portal *portalP);
 extern Portal GetPortalByName(char *name);
+extern TupleDesc GetPortalTupleDescByName(char *name);
 extern void PortalSetQuery(Portal portal, QueryDesc *queryDesc,
 			   TupleDesc attinfo, EState *state,
 			   void (*cleanup) (Portal portal));
-
+extern int PortalRun(Portal portal, bool forward, 
+					long count, CommandDest dest, TupleTableSlot **tts);
 
 #endif	 /* PORTAL_H */
Index: src/include/executor/nodePortalscan.h
===================================================================
RCS file: nodePortalscan.h
diff -N nodePortalscan.h
--- /dev/null	Fri Sep 21 14:39:51 2001
+++ nodePortalscan.h	Fri Sep 21 13:38:30 2001
@@ -0,0 +1,27 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodePortalscan.h
+ *
+ *
+ *
+ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODEPORTALSCAN_H
+#define NODEPORTALSCAN_H
+
+#include "nodes/plannodes.h"
+
+extern TupleTableSlot *ExecPortalScan(PortalScan *node);
+extern bool ExecInitPortalScan(PortalScan *node, EState *estate, Plan *parent);
+extern int	ExecCountSlotsPortalScan(PortalScan *node);
+extern void ExecEndPortalScan(PortalScan *node);
+extern void ExecPortalReScan(PortalScan *node, ExprContext *exprCtxt, Plan *parent);
+extern void ExecPortalMarkPos(PortalScan *node);
+extern void ExecPortalRestrPos(PortalScan *node);
+
+#endif	 /* NODESEQSCAN_H */
#8Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alex Pilosov (#7)
Re: [PATCH] [LARGE] select * from cursor foo

Alex Pilosov <alex@pilosoft.com> writes:

Alex, could we have this resubmitted in "diff -c" format? Plain diff
format is way too risky to apply.

Resubmitted. In case list server chokes on the attachment's size, it is
also available on www.formenos.org/pg/cursor.fix5.diff

I've looked this over, and I think it's not mature enough to apply at
this late stage of the 7.2 cycle; we'd better hold it over for more work
during 7.3. Major problems:

1. Insufficient defense against queries that outlive the cursors they
select from. For example, I could create a view that selects from a
cursor. Yes, you check to see if the cursor name still exists ... but
what if that name now refers to a new cursor that delivers a completely
different set of columns? Instant coredump.

2. I don't understand the semantics of queries that read cursors
that've already had some rows fetched from them. Should we reset the
cursor to the start, so that all its data is implicitly available?
That seems strange, but if we don't do it, I think the behavior will be
quite unpredictable, since some join types are going to result in
resetting and re-reading the cursor multiple times anyway. (You've
punted on this issue by not implementing ExecPortalReScan, but that's
not acceptable for a production feature.)

3. What does it mean to SELECT FOR UPDATE from a cursor? I don't think
ignoring the FOR UPDATE spec is acceptable; maybe we just have to raise
an error.

4. Complete lack of documentation, including lack of attention to
updating the code's internal documentation. (For instance, you seem
to have changed some of the conventions described in nodes/relation.h,
but you didn't fix those comments.)

The work you have done so far on changing RTE etc looks good ... but
I don't think the patch is ready to go. Nor am I comfortable with
applying it now on the assumption that the problems can be fixed during
beta.

I realize you originally sent this in a month ago, and perhaps you would
have had time to respond to these concerns if people had reviewed the
patch promptly. For myself, I can only apologize for not getting to it
sooner. I've had a few distractions over the past month :-(

regards, tom lane

#9Alex Pilosov
alex@pilosoft.com
In reply to: Tom Lane (#8)
Re: [PATCH] [LARGE] select * from cursor foo

On Fri, 21 Sep 2001, Tom Lane wrote:

I've looked this over, and I think it's not mature enough to apply at
this late stage of the 7.2 cycle; we'd better hold it over for more work
during 7.3. Major problems:

1. Insufficient defense against queries that outlive the cursors they
select from. For example, I could create a view that selects from a
cursor. Yes, you check to see if the cursor name still exists ... but
what if that name now refers to a new cursor that delivers a completely
different set of columns? Instant coredump.

Good point. I'll work on it.

2. I don't understand the semantics of queries that read cursors
that've already had some rows fetched from them. Should we reset the
cursor to the start, so that all its data is implicitly available?
That seems strange, but if we don't do it, I think the behavior will be
quite unpredictable, since some join types are going to result in
resetting and re-reading the cursor multiple times anyway. (You've
punted on this issue by not implementing ExecPortalReScan, but that's
not acceptable for a production feature.)

Yeah, I couldn't figure out which option is worse, which is why I didn't
implement it. I think that rewinding the cursor on each query is better,
but I wanted to get comments first.

3. What does it mean to SELECT FOR UPDATE from a cursor? I don't think
ignoring the FOR UPDATE spec is acceptable; maybe we just have to raise
an error.

OK, giving an error makes sense.

4. Complete lack of documentation, including lack of attention to
updating the code's internal documentation. (For instance, you seem
to have changed some of the conventions described in nodes/relation.h,
but you didn't fix those comments.)

OK.

The work you have done so far on changing RTE etc looks good ... but
I don't think the patch is ready to go. Nor am I comfortable with
applying it now on the assumption that the problems can be fixed during
beta.

If you want to consider this argument: It won't break anything that's not
using the feature. It is needed (its not a 'fringe change' to benefit few)
(well, at least I think so :).

It also is a base for my code to do 'select * from func(args)', which is
definitely needed and base of many flames against postgres not having
'real' stored procedures (ones that return sets). I was hoping to get the
rest of it in 7.2 so these flames can be put to rest.

Changes to core code are obvious, and all documentation can be taken care
of during beta.

But I understand your apprehension...

I realize you originally sent this in a month ago, and perhaps you would
have had time to respond to these concerns if people had reviewed the
patch promptly. For myself, I can only apologize for not getting to it
sooner. I've had a few distractions over the past month :-(

Can't blame you, completely understandable with GB situation...

#10Bruce Momjian
pgman@candle.pha.pa.us
In reply to: Alex Pilosov (#9)
Re: [PATCH] [LARGE] select * from cursor foo

Alex, can I have an updated version of this patch for application to
7.3?

---------------------------------------------------------------------------

Alex Pilosov wrote:

On Fri, 21 Sep 2001, Tom Lane wrote:

I've looked this over, and I think it's not mature enough to apply at
this late stage of the 7.2 cycle; we'd better hold it over for more work
during 7.3. Major problems:

1. Insufficient defense against queries that outlive the cursors they
select from. For example, I could create a view that selects from a
cursor. Yes, you check to see if the cursor name still exists ... but
what if that name now refers to a new cursor that delivers a completely
different set of columns? Instant coredump.

Good point. I'll work on it.

2. I don't understand the semantics of queries that read cursors
that've already had some rows fetched from them. Should we reset the
cursor to the start, so that all its data is implicitly available?
That seems strange, but if we don't do it, I think the behavior will be
quite unpredictable, since some join types are going to result in
resetting and re-reading the cursor multiple times anyway. (You've
punted on this issue by not implementing ExecPortalReScan, but that's
not acceptable for a production feature.)

Yeah, I couldn't figure out which option is worse, which is why I didn't
implement it. I think that rewinding the cursor on each query is better,
but I wanted to get comments first.

3. What does it mean to SELECT FOR UPDATE from a cursor? I don't think
ignoring the FOR UPDATE spec is acceptable; maybe we just have to raise
an error.

OK, giving an error makes sense.

4. Complete lack of documentation, including lack of attention to
updating the code's internal documentation. (For instance, you seem
to have changed some of the conventions described in nodes/relation.h,
but you didn't fix those comments.)

OK.

The work you have done so far on changing RTE etc looks good ... but
I don't think the patch is ready to go. Nor am I comfortable with
applying it now on the assumption that the problems can be fixed during
beta.

If you want to consider this argument: It won't break anything that's not
using the feature. It is needed (its not a 'fringe change' to benefit few)
(well, at least I think so :).

It also is a base for my code to do 'select * from func(args)', which is
definitely needed and base of many flames against postgres not having
'real' stored procedures (ones that return sets). I was hoping to get the
rest of it in 7.2 so these flames can be put to rest.

Changes to core code are obvious, and all documentation can be taken care
of during beta.

But I understand your apprehension...

I realize you originally sent this in a month ago, and perhaps you would
have had time to respond to these concerns if people had reviewed the
patch promptly. For myself, I can only apologize for not getting to it
sooner. I've had a few distractions over the past month :-(

Can't blame you, completely understandable with GB situation...

---------------------------(end of broadcast)---------------------------
TIP 2: you can get off all lists at once with the unregister command
(send "unregister YourEmailAddressHere" to majordomo@postgresql.org)

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 853-3000
  +  If your life is a hard drive,     |  830 Blythe Avenue
  +  Christ can be your backup.        |  Drexel Hill, Pennsylvania 19026
#11Bruce Momjian
pgman@candle.pha.pa.us
In reply to: Alex Pilosov (#7)
Re: [PATCH] [LARGE] select * from cursor foo

Sorry, this needs more work. Please continue discussion on hackers.

---------------------------------------------------------------------------

Alex Pilosov wrote:

On Mon, 17 Sep 2001, Tom Lane wrote:

Alex Pilosov <alex@pilosoft.com> writes:

Attached patch does the above.

Alex, could we have this resubmitted in "diff -c" format? Plain diff
format is way too risky to apply.

Resubmitted. In case list server chokes on the attachment's size, it is
also available on www.formenos.org/pg/cursor.fix5.diff

Thanks
-alex

Content-Description:

[ Attachment, skipping... ]

---------------------------(end of broadcast)---------------------------
TIP 5: Have you checked our extensive FAQ?

http://www.postgresql.org/users-lounge/docs/faq.html

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 853-3000
  +  If your life is a hard drive,     |  830 Blythe Avenue
  +  Christ can be your backup.        |  Drexel Hill, Pennsylvania 19026