cursor.fix5.diff

text/plain

Filename: cursor.fix5.diff
Type: text/plain
Part: 0
Message: Re: [PATCH] [LARGE] select * from cursor foo

Patch

Format: unified
File+
nodePortalscan.c 258 0
nodePortalscan.h 27 0
src/backend/commands/command.c 5 42
src/backend/commands/explain.c 14 7
src/backend/executor/execAmi.c 9 0
src/backend/executor/execMain.c 6 5
src/backend/executor/execProcnode.c 25 0
src/backend/executor/Makefile 2 1
src/backend/executor/nodeIndexscan.c 3 1
src/backend/executor/nodeSeqscan.c 4 1
src/backend/executor/nodeSubqueryscan.c 3 2
src/backend/executor/nodeTidscan.c 3 1
src/backend/executor/spi.c 4 43
src/backend/nodes/copyfuncs.c 37 8
src/backend/nodes/equalfuncs.c 20 9
src/backend/nodes/outfuncs.c 32 6
src/backend/nodes/print.c 15 5
src/backend/nodes/readfuncs.c 28 12
src/backend/optimizer/path/allpaths.c 49 5
src/backend/optimizer/path/clausesel.c 1 1
src/backend/optimizer/path/costsize.c 2 2
src/backend/optimizer/plan/createplan.c 59 8
src/backend/optimizer/plan/planner.c 4 2
src/backend/optimizer/plan/setrefs.c 4 0
src/backend/optimizer/prep/preptlist.c 1 1
src/backend/optimizer/prep/prepunion.c 15 7
src/backend/optimizer/util/clauses.c 8 6
src/backend/optimizer/util/pathnode.c 21 0
src/backend/optimizer/util/relnode.c 23 15
src/backend/parser/analyze.c 30 9
src/backend/parser/gram.y 10 2
src/backend/parser/parse_clause.c 57 0
src/backend/parser/parse_func.c 9 2
src/backend/parser/parse_node.c 23 5
src/backend/parser/parse_relation.c 148 19
src/backend/rewrite/rewriteDefine.c 2 2
src/backend/rewrite/rewriteHandler.c 42 17
src/backend/rewrite/rewriteManip.c 3 2
src/backend/utils/adt/ruleutils.c 27 16
src/backend/utils/mmgr/portalmem.c 117 0
src/include/nodes/execnodes.h 13 0
src/include/nodes/nodes.h 4 0
src/include/nodes/parsenodes.h 48 12
src/include/nodes/plannodes.h 11 0
src/include/nodes/relation.h 12 2
src/include/optimizer/pathnode.h 2 0
src/include/parser/parse_relation.h 4 0
src/include/parser/parsetree.h 2 9
src/include/utils/portal.h 3 1
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 */