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 */