1 /*-------------------------------------------------------------------------
4 * Utility commands affecting portals (that is, SQL cursor commands)
6 * Note: see also tcop/pquery.c, which implements portal operations for
7 * the FE/BE protocol. This module uses pquery.c for some operations.
8 * And both modules depend on utils/mmgr/portalmem.c, which controls
9 * storage management for portals (but doesn't run any queries in them).
12 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
13 * Portions Copyright (c) 1994, Regents of the University of California
17 * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.30 2004/07/31 00:45:31 tgl Exp $
19 *-------------------------------------------------------------------------
26 #include "commands/portalcmds.h"
27 #include "executor/executor.h"
28 #include "optimizer/planner.h"
29 #include "rewrite/rewriteHandler.h"
30 #include "tcop/pquery.h"
31 #include "utils/memutils.h"
36 * Execute SQL DECLARE CURSOR command.
39 PerformCursorOpen(DeclareCursorStmt *stmt)
45 MemoryContext oldContext;
48 * Disallow empty-string cursor name (conflicts with protocol-level
51 if (!stmt->portalname || stmt->portalname[0] == '\0')
53 (errcode(ERRCODE_INVALID_CURSOR_NAME),
54 errmsg("invalid cursor name: must not be empty")));
57 * If this is a non-holdable cursor, we require that this statement
58 * has been executed inside a transaction block (or else, it would
59 * have no user-visible effect).
61 if (!(stmt->options & CURSOR_OPT_HOLD))
62 RequireTransactionChain((void *) stmt, "DECLARE CURSOR");
65 * The query has been through parse analysis, but not rewriting or
66 * planning as yet. Note that the grammar ensured we have a SELECT
67 * query, so we are not expecting rule rewriting to do anything
70 rewritten = QueryRewrite((Query *) stmt->query);
71 if (list_length(rewritten) != 1 || !IsA(linitial(rewritten), Query))
72 elog(ERROR, "unexpected rewrite result");
73 query = (Query *) linitial(rewritten);
74 if (query->commandType != CMD_SELECT)
75 elog(ERROR, "unexpected rewrite result");
79 (errcode(ERRCODE_SYNTAX_ERROR),
80 errmsg("DECLARE CURSOR may not specify INTO")));
81 if (query->rowMarks != NIL)
83 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
84 errmsg("DECLARE CURSOR ... FOR UPDATE is not supported"),
85 errdetail("Cursors must be READ ONLY.")));
87 plan = planner(query, true, stmt->options, NULL);
90 * Create a portal and copy the query and plan into its memory
93 portal = CreatePortal(stmt->portalname, false, false);
95 oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
97 query = copyObject(query);
98 plan = copyObject(plan);
100 PortalDefineQuery(portal,
101 NULL, /* unfortunately don't have sourceText */
102 "SELECT", /* cursor's query is always a SELECT */
105 PortalGetHeapMemory(portal));
107 MemoryContextSwitchTo(oldContext);
110 * Set up options for portal.
112 * If the user didn't specify a SCROLL type, allow or disallow scrolling
113 * based on whether it would require any additional runtime overhead
116 portal->cursorOptions = stmt->options;
117 if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
119 if (ExecSupportsBackwardScan(plan))
120 portal->cursorOptions |= CURSOR_OPT_SCROLL;
122 portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
126 * Start execution --- never any params for a cursor.
128 PortalStart(portal, NULL);
130 Assert(portal->strategy == PORTAL_ONE_SELECT);
133 * We're done; the query won't actually be run until
134 * PerformPortalFetch is called.
140 * Execute SQL FETCH or MOVE command.
142 * stmt: parsetree node for command
143 * dest: where to send results
144 * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
145 * in which to store a command completion status string.
147 * completionTag may be NULL if caller doesn't want a status string.
150 PerformPortalFetch(FetchStmt *stmt,
158 * Disallow empty-string cursor name (conflicts with protocol-level
161 if (!stmt->portalname || stmt->portalname[0] == '\0')
163 (errcode(ERRCODE_INVALID_CURSOR_NAME),
164 errmsg("invalid cursor name: must not be empty")));
166 /* get the portal from the portal name */
167 portal = GetPortalByName(stmt->portalname);
168 if (!PortalIsValid(portal))
171 (errcode(ERRCODE_UNDEFINED_CURSOR),
172 errmsg("cursor \"%s\" does not exist", stmt->portalname)));
173 return; /* keep compiler happy */
176 /* Adjust dest if needed. MOVE wants destination None */
178 dest = None_Receiver;
181 nprocessed = PortalRunFetch(portal,
186 /* Return command status if wanted */
188 snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
189 stmt->ismove ? "MOVE" : "FETCH",
198 PerformPortalClose(const char *name)
203 * Disallow empty-string cursor name (conflicts with protocol-level
206 if (!name || name[0] == '\0')
208 (errcode(ERRCODE_INVALID_CURSOR_NAME),
209 errmsg("invalid cursor name: must not be empty")));
212 * get the portal from the portal name
214 portal = GetPortalByName(name);
215 if (!PortalIsValid(portal))
218 (errcode(ERRCODE_UNDEFINED_CURSOR),
219 errmsg("cursor \"%s\" does not exist", name)));
220 return; /* keep compiler happy */
224 * Note: PortalCleanup is called as a side-effect
226 PortalDrop(portal, false);
232 * Clean up a portal when it's dropped. This is the standard cleanup hook
236 PortalCleanup(Portal portal)
238 QueryDesc *queryDesc;
243 AssertArg(PortalIsValid(portal));
244 AssertArg(portal->cleanup == PortalCleanup);
247 * Shut down executor, if still running. We skip this during error
248 * abort, since other mechanisms will take care of releasing executor
249 * resources, and we can't be sure that ExecutorEnd itself wouldn't
252 queryDesc = PortalGetQueryDesc(portal);
255 portal->queryDesc = NULL;
256 if (portal->status != PORTAL_FAILED)
258 ResourceOwner saveResourceOwner;
260 /* We must make the portal's resource owner current */
261 saveResourceOwner = CurrentResourceOwner;
264 CurrentResourceOwner = portal->resowner;
265 ExecutorEnd(queryDesc);
269 /* Ensure CurrentResourceOwner is restored on error */
270 CurrentResourceOwner = saveResourceOwner;
274 CurrentResourceOwner = saveResourceOwner;
280 * PersistHoldablePortal
282 * Prepare the specified Portal for access outside of the current
283 * transaction. When this function returns, all future accesses to the
284 * portal must be done via the Tuplestore (not by invoking the
288 PersistHoldablePortal(Portal portal)
290 QueryDesc *queryDesc = PortalGetQueryDesc(portal);
291 Portal saveActivePortal;
292 ResourceOwner saveResourceOwner;
293 MemoryContext savePortalContext;
294 MemoryContext saveQueryContext;
295 MemoryContext oldcxt;
298 * If we're preserving a holdable portal, we had better be inside the
299 * transaction that originally created it.
301 Assert(portal->createXact == GetCurrentTransactionId());
302 Assert(queryDesc != NULL);
305 * Caller must have created the tuplestore already.
307 Assert(portal->holdContext != NULL);
308 Assert(portal->holdStore != NULL);
311 * Before closing down the executor, we must copy the tupdesc into
312 * long-term memory, since it was created in executor memory.
314 oldcxt = MemoryContextSwitchTo(portal->holdContext);
316 portal->tupDesc = CreateTupleDescCopy(portal->tupDesc);
318 MemoryContextSwitchTo(oldcxt);
321 * Check for improper portal use, and mark portal active.
323 if (portal->status != PORTAL_READY)
325 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
326 errmsg("portal \"%s\" cannot be run", portal->name)));
327 portal->status = PORTAL_ACTIVE;
330 * Set up global portal context pointers.
332 saveActivePortal = ActivePortal;
333 saveResourceOwner = CurrentResourceOwner;
334 savePortalContext = PortalContext;
335 saveQueryContext = QueryContext;
338 ActivePortal = portal;
339 CurrentResourceOwner = portal->resowner;
340 PortalContext = PortalGetHeapMemory(portal);
341 QueryContext = portal->queryContext;
343 MemoryContextSwitchTo(PortalContext);
346 * Rewind the executor: we need to store the entire result set in the
347 * tuplestore, so that subsequent backward FETCHs can be processed.
349 ExecutorRewind(queryDesc);
351 /* Change the destination to output to the tuplestore */
352 queryDesc->dest = CreateDestReceiver(Tuplestore, portal);
354 /* Fetch the result set into the tuplestore */
355 ExecutorRun(queryDesc, ForwardScanDirection, 0L);
357 (*queryDesc->dest->rDestroy) (queryDesc->dest);
358 queryDesc->dest = NULL;
361 * Now shut down the inner executor.
363 portal->queryDesc = NULL; /* prevent double shutdown */
364 ExecutorEnd(queryDesc);
367 * Reset the position in the result set: ideally, this could be
368 * implemented by just skipping straight to the tuple # that we need
369 * to be at, but the tuplestore API doesn't support that. So we start
370 * at the beginning of the tuplestore and iterate through it until we
371 * reach where we need to be. FIXME someday?
373 MemoryContextSwitchTo(portal->holdContext);
379 if (portal->posOverflow) /* oops, cannot trust portalPos */
381 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
382 errmsg("could not reposition held cursor")));
384 tuplestore_rescan(portal->holdStore);
386 for (store_pos = 0; store_pos < portal->portalPos; store_pos++)
391 tup = tuplestore_gettuple(portal->holdStore, true,
395 elog(ERROR, "unexpected end of tuple stream");
404 /* Uncaught error while executing portal: mark it dead */
405 portal->status = PORTAL_FAILED;
407 /* Restore global vars and propagate error */
408 ActivePortal = saveActivePortal;
409 CurrentResourceOwner = saveResourceOwner;
410 PortalContext = savePortalContext;
411 QueryContext = saveQueryContext;
417 MemoryContextSwitchTo(oldcxt);
419 /* Mark portal not active */
420 portal->status = PORTAL_READY;
422 ActivePortal = saveActivePortal;
423 CurrentResourceOwner = saveResourceOwner;
424 PortalContext = savePortalContext;
425 QueryContext = saveQueryContext;
428 * We can now release any subsidiary memory of the portal's heap
429 * context; we'll never use it again. The executor already dropped
430 * its context, but this will clean up anything that glommed onto the
431 * portal's heap via PortalContext.
433 MemoryContextDeleteChildren(PortalGetHeapMemory(portal));