1 /*-------------------------------------------------------------------------
6 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.7 2002/12/30 15:31:47 momjian Exp $
13 *-------------------------------------------------------------------------
20 #include "commands/portalcmds.h"
21 #include "executor/executor.h"
27 * Clean up a portal when it's dropped. Since this mainly exists to run
28 * ExecutorEnd(), it should not be set as the cleanup hook until we have
29 * called ExecutorStart() on the portal's query.
32 PortalCleanup(Portal portal)
37 AssertArg(PortalIsValid(portal));
38 AssertArg(portal->cleanup == PortalCleanup);
41 * tell the executor to shutdown the query
43 ExecutorEnd(PortalGetQueryDesc(portal));
46 * This should be unnecessary since the querydesc should be in the
47 * portal's memory context, but do it anyway for symmetry.
49 FreeQueryDesc(PortalGetQueryDesc(portal));
56 * name: name of portal
57 * forward: forward or backward fetch?
58 * count: # of tuples to fetch
59 * dest: where to send results
60 * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
61 * in which to store a command completion status string.
63 * completionTag may be NULL if caller doesn't want a status string.
66 PerformPortalFetch(char *name,
75 MemoryContext oldcontext;
76 ScanDirection direction;
77 bool temp_desc = false;
79 /* initialize completion status in case of early exit */
81 strcpy(completionTag, (dest == None) ? "MOVE 0" : "FETCH 0");
88 elog(WARNING, "PerformPortalFetch: missing portal name");
93 * get the portal from the portal name
95 portal = GetPortalByName(name);
96 if (!PortalIsValid(portal))
98 elog(WARNING, "PerformPortalFetch: portal \"%s\" not found",
103 /* If zero count, handle specially */
108 /* Are we sitting on a row? */
109 oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
110 queryDesc = PortalGetQueryDesc(portal);
111 estate = queryDesc->estate;
112 if (portal->atStart == false && portal->atEnd == false)
114 MemoryContextSwitchTo(oldcontext);
118 /* MOVE 0 returns 0/1 based on if FETCH 0 would return a row */
119 if (completionTag && on_row)
120 strcpy(completionTag, "MOVE 1");
125 /* If we are not on a row, FETCH 0 returns nothing */
129 /* Since we are sitting on a row, return the row */
130 /* Back up so we can reread the row */
131 PerformPortalFetch(name, false /* backward */, 1,
132 None, /* throw away output */
133 NULL /* do not modify the command tag */);
135 /* Set up to fetch one row */
141 /* Internally, zero count processes all portal rows */
142 if (count == LONG_MAX)
146 * switch into the portal context
148 oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
150 queryDesc = PortalGetQueryDesc(portal);
151 estate = queryDesc->estate;
154 * If the requested destination is not the same as the query's
155 * original destination, make a temporary QueryDesc with the proper
156 * destination. This supports MOVE, for example, which will pass in
159 * EXCEPTION: if the query's original dest is RemoteInternal (ie, it's a
160 * binary cursor) and the request is Remote, we do NOT override the
161 * original dest. This is necessary since a FETCH command will pass
162 * dest = Remote, not knowing whether the cursor is binary or not.
164 if (dest != queryDesc->dest &&
165 !(queryDesc->dest == RemoteInternal && dest == Remote))
167 QueryDesc *qdesc = (QueryDesc *) palloc(sizeof(QueryDesc));
169 memcpy(qdesc, queryDesc, sizeof(QueryDesc));
176 * Determine which direction to go in, and check to see if we're
177 * already at the end of the available tuples in that direction. If
178 * so, set the direction to NoMovement to avoid trying to fetch any
179 * tuples. (This check exists because not all plan node types are
180 * robust about being called again if they've already returned NULL
181 * once.) Then call the executor (we must not skip this, because the
182 * destination needs to see a setup and shutdown even if no tuples are
183 * available). Finally, update the atStart/atEnd state depending on
184 * the number of tuples that were retrieved.
189 direction = NoMovementScanDirection;
191 direction = ForwardScanDirection;
193 ExecutorRun(queryDesc, direction, (long) count);
195 if (estate->es_processed > 0)
196 portal->atStart = false; /* OK to back up now */
197 if (count <= 0 || (int) estate->es_processed < count)
198 portal->atEnd = true; /* we retrieved 'em all */
203 direction = NoMovementScanDirection;
205 direction = BackwardScanDirection;
207 ExecutorRun(queryDesc, direction, (long) count);
209 if (estate->es_processed > 0)
210 portal->atEnd = false; /* OK to go forward now */
211 if (count <= 0 || (int) estate->es_processed < count)
212 portal->atStart = true; /* we retrieved 'em all */
215 /* Return command status if wanted */
217 snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %u",
218 (dest == None) ? "MOVE" : "FETCH",
219 estate->es_processed);
222 * Clean up and switch back to old context.
227 MemoryContextSwitchTo(oldcontext);
234 PerformPortalClose(char *name, CommandDest dest)
243 elog(WARNING, "PerformPortalClose: missing portal name");
248 * get the portal from the portal name
250 portal = GetPortalByName(name);
251 if (!PortalIsValid(portal))
253 elog(WARNING, "PerformPortalClose: portal \"%s\" not found",
259 * Note: PortalCleanup is called as a side-effect