diff options
Diffstat (limited to 'src/backend/commands/portalcmds.c')
-rw-r--r-- | src/backend/commands/portalcmds.c | 208 |
1 files changed, 167 insertions, 41 deletions
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c index 5881fe6c582..0621cdd5903 100644 --- a/src/backend/commands/portalcmds.c +++ b/src/backend/commands/portalcmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.8 2003/01/08 00:22:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.9 2003/03/10 03:53:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,39 +19,88 @@ #include "commands/portalcmds.h" #include "executor/executor.h" +#include "optimizer/planner.h" +#include "rewrite/rewriteHandler.h" + + +static Portal PreparePortal(char *portalName); /* - * PortalCleanup - * - * Clean up a portal when it's dropped. Since this mainly exists to run - * ExecutorEnd(), it should not be set as the cleanup hook until we have - * called ExecutorStart() on the portal's query. + * PerformCursorOpen + * Execute SQL DECLARE CURSOR command. */ void -PortalCleanup(Portal portal) +PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest) { + List *rewritten; + Query *query; + Plan *plan; + Portal portal; + MemoryContext oldContext; + char *cursorName; + QueryDesc *queryDesc; + + /* Check for invalid context (must be in transaction block) */ + RequireTransactionChain((void *) stmt, "DECLARE CURSOR"); + /* - * sanity checks + * The query has been through parse analysis, but not rewriting or + * planning as yet. Note that the grammar ensured we have a SELECT + * query, so we are not expecting rule rewriting to do anything strange. */ - AssertArg(PortalIsValid(portal)); - AssertArg(portal->cleanup == PortalCleanup); + rewritten = QueryRewrite((Query *) stmt->query); + if (length(rewritten) != 1 || !IsA(lfirst(rewritten), Query)) + elog(ERROR, "PerformCursorOpen: unexpected rewrite result"); + query = (Query *) lfirst(rewritten); + if (query->commandType != CMD_SELECT) + elog(ERROR, "PerformCursorOpen: unexpected rewrite result"); + + if (query->into) + elog(ERROR, "DECLARE CURSOR may not specify INTO"); + if (query->rowMarks != NIL) + elog(ERROR, "DECLARE/UPDATE is not supported" + "\n\tCursors must be READ ONLY"); + + plan = planner(query, true, stmt->options); + + /* If binary cursor, switch to alternate output format */ + if ((stmt->options & CURSOR_OPT_BINARY) && dest == Remote) + dest = RemoteInternal; /* - * tell the executor to shutdown the query + * Create a portal and copy the query and plan into its memory context. */ - ExecutorEnd(PortalGetQueryDesc(portal)); + portal = PreparePortal(stmt->portalname); + + oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); + query = copyObject(query); + plan = copyObject(plan); /* - * This should be unnecessary since the querydesc should be in the - * portal's memory context, but do it anyway for symmetry. + * Create the QueryDesc object in the portal context, too. */ - FreeQueryDesc(PortalGetQueryDesc(portal)); -} + cursorName = pstrdup(stmt->portalname); + queryDesc = CreateQueryDesc(query, plan, dest, cursorName, NULL, false); + + /* + * call ExecStart to prepare the plan for execution + */ + ExecutorStart(queryDesc); + /* Arrange to shut down the executor if portal is dropped */ + PortalSetQuery(portal, queryDesc, PortalCleanup); + + /* + * We're done; the query won't actually be run until PerformPortalFetch + * is called. + */ + MemoryContextSwitchTo(oldContext); +} /* * PerformPortalFetch + * Execute SQL FETCH or MOVE command. * * name: name of portal * forward: forward or backward fetch? @@ -70,28 +119,20 @@ PerformPortalFetch(char *name, char *completionTag) { Portal portal; - QueryDesc *queryDesc; - EState *estate; - MemoryContext oldcontext; - ScanDirection direction; - bool temp_desc = false; + long nprocessed; /* initialize completion status in case of early exit */ if (completionTag) strcpy(completionTag, (dest == None) ? "MOVE 0" : "FETCH 0"); - /* - * sanity checks - */ + /* sanity checks */ if (name == NULL) { elog(WARNING, "PerformPortalFetch: missing portal name"); return; } - /* - * get the portal from the portal name - */ + /* get the portal from the portal name */ portal = GetPortalByName(name); if (!PortalIsValid(portal)) { @@ -100,6 +141,31 @@ PerformPortalFetch(char *name, return; } + /* Do it */ + nprocessed = DoPortalFetch(portal, forward, count, dest); + + /* Return command status if wanted */ + if (completionTag) + snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld", + (dest == None) ? "MOVE" : "FETCH", + nprocessed); +} + +/* + * DoPortalFetch + * Guts of PerformPortalFetch --- shared with SPI cursor operations + * + * Returns number of rows processed. + */ +long +DoPortalFetch(Portal portal, bool forward, long count, CommandDest dest) +{ + QueryDesc *queryDesc; + EState *estate; + MemoryContext oldcontext; + ScanDirection direction; + bool temp_desc = false; + /* * Zero count means to re-fetch the current row, if any (per SQL92) */ @@ -113,9 +179,7 @@ PerformPortalFetch(char *name, if (dest == None) { /* MOVE 0 returns 0/1 based on if FETCH 0 would return a row */ - if (completionTag && on_row) - strcpy(completionTag, "MOVE 1"); - return; + return on_row ? 1L : 0L; } else { @@ -128,9 +192,9 @@ PerformPortalFetch(char *name, */ if (on_row) { - PerformPortalFetch(name, false /* backward */, 1L, - None, /* throw away output */ - NULL /* do not modify the command tag */); + DoPortalFetch(portal, + false /* backward */, 1L, + None /* throw away output */); /* Set up to fetch one row forward */ count = 1; forward = true; @@ -202,6 +266,10 @@ PerformPortalFetch(char *name, } else { + if (!portal->backwardOK) + elog(ERROR, "Cursor cannot scan backwards" + "\n\tDeclare it with SCROLL option to enable backward scan"); + if (portal->atStart || count == 0) direction = NoMovementScanDirection; else @@ -222,12 +290,6 @@ PerformPortalFetch(char *name, } } - /* Return command status if wanted */ - if (completionTag) - snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %u", - (dest == None) ? "MOVE" : "FETCH", - estate->es_processed); - /* * Clean up and switch back to old context. */ @@ -235,18 +297,21 @@ PerformPortalFetch(char *name, pfree(queryDesc); MemoryContextSwitchTo(oldcontext); + + return estate->es_processed; } /* * PerformPortalClose + * Close a cursor. */ void -PerformPortalClose(char *name, CommandDest dest) +PerformPortalClose(char *name) { Portal portal; /* - * sanity checks + * sanity checks ... why is this case allowed by the grammar, anyway? */ if (name == NULL) { @@ -270,3 +335,64 @@ PerformPortalClose(char *name, CommandDest dest) */ PortalDrop(portal); } + + +/* + * PreparePortal + */ +static Portal +PreparePortal(char *portalName) +{ + Portal portal; + + /* + * Check for already-in-use portal name. + */ + portal = GetPortalByName(portalName); + if (PortalIsValid(portal)) + { + /* + * XXX Should we raise an error rather than closing the old + * portal? + */ + elog(WARNING, "Closing pre-existing portal \"%s\"", + portalName); + PortalDrop(portal); + } + + /* + * Create the new portal. + */ + portal = CreatePortal(portalName); + + return portal; +} + + +/* + * PortalCleanup + * + * Clean up a portal when it's dropped. Since this mainly exists to run + * ExecutorEnd(), it should not be set as the cleanup hook until we have + * called ExecutorStart() on the portal's query. + */ +void +PortalCleanup(Portal portal) +{ + /* + * sanity checks + */ + AssertArg(PortalIsValid(portal)); + AssertArg(portal->cleanup == PortalCleanup); + + /* + * tell the executor to shutdown the query + */ + ExecutorEnd(PortalGetQueryDesc(portal)); + + /* + * This should be unnecessary since the querydesc should be in the + * portal's memory context, but do it anyway for symmetry. + */ + FreeQueryDesc(PortalGetQueryDesc(portal)); +} |