diff options
Diffstat (limited to 'src/backend/commands/portalcmds.c')
-rw-r--r-- | src/backend/commands/portalcmds.c | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c new file mode 100644 index 00000000000..6f690c0927c --- /dev/null +++ b/src/backend/commands/portalcmds.c @@ -0,0 +1,234 @@ +/*------------------------------------------------------------------------- + * + * portalcmds.c + * portal support code + * + * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $ + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "commands/portalcmds.h" +#include "executor/executor.h" + + +/* + * PortalCleanup + */ +void +PortalCleanup(Portal portal) +{ + MemoryContext oldcontext; + + /* + * sanity checks + */ + AssertArg(PortalIsValid(portal)); + AssertArg(portal->cleanup == PortalCleanup); + + /* + * set proper portal-executor context before calling ExecMain. + */ + oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); + + /* + * tell the executor to shutdown the query + */ + ExecutorEnd(PortalGetQueryDesc(portal), PortalGetState(portal)); + + /* + * switch back to previous context + */ + MemoryContextSwitchTo(oldcontext); +} + + +/* + * PerformPortalFetch + * + * name: name of portal + * forward: forward or backward fetch? + * count: # of tuples to fetch (0 implies all) + * dest: where to send results + * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE + * in which to store a command completion status string. + * + * completionTag may be NULL if caller doesn't want a status string. + */ +void +PerformPortalFetch(char *name, + bool forward, + int count, + CommandDest dest, + char *completionTag) +{ + Portal portal; + QueryDesc *queryDesc; + EState *estate; + MemoryContext oldcontext; + ScanDirection direction; + CommandId savedId; + bool temp_desc = false; + + /* initialize completion status in case of early exit */ + if (completionTag) + strcpy(completionTag, (dest == None) ? "MOVE 0" : "FETCH 0"); + + /* + * sanity checks + */ + if (name == NULL) + { + elog(WARNING, "PerformPortalFetch: missing portal name"); + return; + } + + /* + * get the portal from the portal name + */ + portal = GetPortalByName(name); + if (!PortalIsValid(portal)) + { + elog(WARNING, "PerformPortalFetch: portal \"%s\" not found", + name); + return; + } + + /* + * switch into the portal context + */ + oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); + + queryDesc = PortalGetQueryDesc(portal); + estate = PortalGetState(portal); + + /* + * If the requested destination is not the same as the query's + * original destination, make a temporary QueryDesc with the proper + * destination. This supports MOVE, for example, which will pass in + * dest = None. + * + * EXCEPTION: if the query's original dest is RemoteInternal (ie, it's a + * binary cursor) and the request is Remote, we do NOT override the + * original dest. This is necessary since a FETCH command will pass + * dest = Remote, not knowing whether the cursor is binary or not. + */ + if (dest != queryDesc->dest && + !(queryDesc->dest == RemoteInternal && dest == Remote)) + { + QueryDesc *qdesc = (QueryDesc *) palloc(sizeof(QueryDesc)); + + memcpy(qdesc, queryDesc, sizeof(QueryDesc)); + qdesc->dest = dest; + queryDesc = qdesc; + temp_desc = true; + } + + /* + * Restore the scanCommandId that was current when the cursor was + * opened. This ensures that we see the same tuples throughout the + * execution of the cursor. + */ + savedId = GetScanCommandId(); + SetScanCommandId(PortalGetCommandId(portal)); + + /* + * 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, set the direction to NoMovement to avoid trying to fetch any + * tuples. (This check exists because not all plan node types + * are robust about being called again if they've already returned + * NULL once.) Then call the executor (we must not skip this, because + * the destination needs to see a setup and shutdown even if no tuples + * are available). Finally, update the atStart/atEnd state depending + * on the number of tuples that were retrieved. + */ + if (forward) + { + if (portal->atEnd) + direction = NoMovementScanDirection; + else + direction = ForwardScanDirection; + + ExecutorRun(queryDesc, estate, direction, (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 */ + } + else + { + if (portal->atStart) + direction = NoMovementScanDirection; + else + direction = BackwardScanDirection; + + ExecutorRun(queryDesc, estate, direction, (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 */ + } + + /* Return command status if wanted */ + if (completionTag) + snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %u", + (dest == None) ? "MOVE" : "FETCH", + estate->es_processed); + + /* + * Restore outer command ID. + */ + SetScanCommandId(savedId); + + /* + * Clean up and switch back to old context. + */ + if (temp_desc) + pfree(queryDesc); + + MemoryContextSwitchTo(oldcontext); +} + +/* + * PerformPortalClose + */ +void +PerformPortalClose(char *name, CommandDest dest) +{ + Portal portal; + + /* + * sanity checks + */ + if (name == NULL) + { + elog(WARNING, "PerformPortalClose: missing portal name"); + return; + } + + /* + * get the portal from the portal name + */ + portal = GetPortalByName(name); + if (!PortalIsValid(portal)) + { + elog(WARNING, "PerformPortalClose: portal \"%s\" not found", + name); + return; + } + + /* + * Note: PortalCleanup is called as a side-effect + */ + PortalDrop(portal); +} |