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-2007, 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.65 2007/04/27 22:05:47 tgl Exp $
19 *-------------------------------------------------------------------------
26 #include "access/xact.h"
27 #include "commands/portalcmds.h"
28 #include "executor/executor.h"
29 #include "tcop/pquery.h"
30 #include "utils/memutils.h"
35 * Execute SQL DECLARE CURSOR command.
37 * The query has already been through parse analysis, rewriting, and planning.
38 * When it gets here, it looks like a SELECT PlannedStmt, except that the
39 * utilityStmt field is set.
42 PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
43 const char *queryString, bool isTopLevel)
45 DeclareCursorStmt *cstmt = (DeclareCursorStmt *) stmt->utilityStmt;
47 MemoryContext oldContext;
49 if (cstmt == NULL || !IsA(cstmt, DeclareCursorStmt))
50 elog(ERROR, "PerformCursorOpen called for non-cursor query");
53 * Disallow empty-string cursor name (conflicts with protocol-level
56 if (!cstmt->portalname || cstmt->portalname[0] == '\0')
58 (errcode(ERRCODE_INVALID_CURSOR_NAME),
59 errmsg("invalid cursor name: must not be empty")));
62 * If this is a non-holdable cursor, we require that this statement has
63 * been executed inside a transaction block (or else, it would have no
64 * user-visible effect).
66 if (!(cstmt->options & CURSOR_OPT_HOLD))
67 RequireTransactionChain(isTopLevel, "DECLARE CURSOR");
70 * Create a portal and copy the plan into its memory context.
72 portal = CreatePortal(cstmt->portalname, false, false);
74 oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
76 stmt = copyObject(stmt);
77 stmt->utilityStmt = NULL; /* make it look like plain SELECT */
79 PortalDefineQuery(portal,
82 "SELECT", /* cursor's query is always a SELECT */
87 * Also copy the outer portal's parameter list into the inner portal's
88 * memory context. We want to pass down the parameter values in case we
90 * DECLARE c CURSOR FOR SELECT ... WHERE foo = $1
91 * This will have been parsed using the outer parameter set and the
92 * parameter value needs to be preserved for use when the cursor is
96 params = copyParamList(params);
98 MemoryContextSwitchTo(oldContext);
101 * Set up options for portal.
103 * If the user didn't specify a SCROLL type, allow or disallow scrolling
104 * based on whether it would require any additional runtime overhead to do
107 portal->cursorOptions = cstmt->options;
108 if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
110 if (ExecSupportsBackwardScan(stmt->planTree))
111 portal->cursorOptions |= CURSOR_OPT_SCROLL;
113 portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
117 * Start execution, inserting parameters if any.
119 PortalStart(portal, params, ActiveSnapshot);
121 Assert(portal->strategy == PORTAL_ONE_SELECT);
124 * We're done; the query won't actually be run until PerformPortalFetch is
131 * Execute SQL FETCH or MOVE command.
133 * stmt: parsetree node for command
134 * dest: where to send results
135 * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
136 * in which to store a command completion status string.
138 * completionTag may be NULL if caller doesn't want a status string.
141 PerformPortalFetch(FetchStmt *stmt,
149 * Disallow empty-string cursor name (conflicts with protocol-level
152 if (!stmt->portalname || stmt->portalname[0] == '\0')
154 (errcode(ERRCODE_INVALID_CURSOR_NAME),
155 errmsg("invalid cursor name: must not be empty")));
157 /* get the portal from the portal name */
158 portal = GetPortalByName(stmt->portalname);
159 if (!PortalIsValid(portal))
162 (errcode(ERRCODE_UNDEFINED_CURSOR),
163 errmsg("cursor \"%s\" does not exist", stmt->portalname)));
164 return; /* keep compiler happy */
167 /* Adjust dest if needed. MOVE wants destination DestNone */
169 dest = None_Receiver;
172 nprocessed = PortalRunFetch(portal,
177 /* Return command status if wanted */
179 snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
180 stmt->ismove ? "MOVE" : "FETCH",
189 PerformPortalClose(const char *name)
193 /* NULL means CLOSE ALL */
196 PortalHashTableDeleteAll();
201 * Disallow empty-string cursor name (conflicts with protocol-level
206 (errcode(ERRCODE_INVALID_CURSOR_NAME),
207 errmsg("invalid cursor name: must not be empty")));
210 * get the portal from the portal name
212 portal = GetPortalByName(name);
213 if (!PortalIsValid(portal))
216 (errcode(ERRCODE_UNDEFINED_CURSOR),
217 errmsg("cursor \"%s\" does not exist", name)));
218 return; /* keep compiler happy */
222 * Note: PortalCleanup is called as a side-effect
224 PortalDrop(portal, false);
230 * Clean up a portal when it's dropped. This is the standard cleanup hook
234 PortalCleanup(Portal portal)
236 QueryDesc *queryDesc;
241 AssertArg(PortalIsValid(portal));
242 AssertArg(portal->cleanup == PortalCleanup);
245 * Shut down executor, if still running. We skip this during error abort,
246 * since other mechanisms will take care of releasing executor resources,
247 * and we can't be sure that ExecutorEnd itself wouldn't fail.
249 queryDesc = PortalGetQueryDesc(portal);
252 portal->queryDesc = NULL;
253 if (portal->status != PORTAL_FAILED)
255 ResourceOwner saveResourceOwner;
257 /* We must make the portal's resource owner current */
258 saveResourceOwner = CurrentResourceOwner;
261 CurrentResourceOwner = portal->resowner;
262 /* we do not need AfterTriggerEndQuery() here */
263 ExecutorEnd(queryDesc);
267 /* Ensure CurrentResourceOwner is restored on error */
268 CurrentResourceOwner = saveResourceOwner;
272 CurrentResourceOwner = saveResourceOwner;
278 * PersistHoldablePortal
280 * Prepare the specified Portal for access outside of the current
281 * transaction. When this function returns, all future accesses to the
282 * portal must be done via the Tuplestore (not by invoking the
286 PersistHoldablePortal(Portal portal)
288 QueryDesc *queryDesc = PortalGetQueryDesc(portal);
289 Portal saveActivePortal;
290 Snapshot saveActiveSnapshot;
291 ResourceOwner saveResourceOwner;
292 MemoryContext savePortalContext;
293 MemoryContext oldcxt;
296 * If we're preserving a holdable portal, we had better be inside the
297 * transaction that originally created it.
299 Assert(portal->createSubid != InvalidSubTransactionId);
300 Assert(queryDesc != NULL);
303 * Caller must have created the tuplestore already.
305 Assert(portal->holdContext != NULL);
306 Assert(portal->holdStore != NULL);
309 * Before closing down the executor, we must copy the tupdesc into
310 * long-term memory, since it was created in executor memory.
312 oldcxt = MemoryContextSwitchTo(portal->holdContext);
314 portal->tupDesc = CreateTupleDescCopy(portal->tupDesc);
316 MemoryContextSwitchTo(oldcxt);
319 * Check for improper portal use, and mark portal active.
321 if (portal->status != PORTAL_READY)
323 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
324 errmsg("portal \"%s\" cannot be run", portal->name)));
325 portal->status = PORTAL_ACTIVE;
328 * Set up global portal context pointers.
330 saveActivePortal = ActivePortal;
331 saveActiveSnapshot = ActiveSnapshot;
332 saveResourceOwner = CurrentResourceOwner;
333 savePortalContext = PortalContext;
336 ActivePortal = portal;
337 ActiveSnapshot = queryDesc->snapshot;
338 CurrentResourceOwner = portal->resowner;
339 PortalContext = PortalGetHeapMemory(portal);
341 MemoryContextSwitchTo(PortalContext);
344 * Rewind the executor: we need to store the entire result set in the
345 * tuplestore, so that subsequent backward FETCHs can be processed.
347 ExecutorRewind(queryDesc);
349 /* Change the destination to output to the tuplestore */
350 queryDesc->dest = CreateDestReceiver(DestTuplestore, portal);
352 /* Fetch the result set into the tuplestore */
353 ExecutorRun(queryDesc, ForwardScanDirection, 0L);
355 (*queryDesc->dest->rDestroy) (queryDesc->dest);
356 queryDesc->dest = NULL;
359 * Now shut down the inner executor.
361 portal->queryDesc = NULL; /* prevent double shutdown */
362 /* we do not need AfterTriggerEndQuery() here */
363 ExecutorEnd(queryDesc);
366 * Set the position in the result set: ideally, this could be
367 * implemented by just skipping straight to the tuple # that we need
368 * to be at, but the tuplestore API doesn't support that. So we start
369 * at the beginning of the tuplestore and iterate through it until we
370 * reach where we need to be. FIXME someday? (Fortunately, the
371 * typical case is that we're supposed to be at or near the start
372 * of the result set, so this isn't as bad as it sounds.)
374 MemoryContextSwitchTo(portal->holdContext);
378 /* we can handle this case even if posOverflow */
379 while (tuplestore_advance(portal->holdStore, true))
386 if (portal->posOverflow) /* oops, cannot trust portalPos */
388 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
389 errmsg("could not reposition held cursor")));
391 tuplestore_rescan(portal->holdStore);
393 for (store_pos = 0; store_pos < portal->portalPos; store_pos++)
395 if (!tuplestore_advance(portal->holdStore, true))
396 elog(ERROR, "unexpected end of tuple stream");
402 /* Uncaught error while executing portal: mark it dead */
403 portal->status = PORTAL_FAILED;
405 /* Restore global vars and propagate error */
406 ActivePortal = saveActivePortal;
407 ActiveSnapshot = saveActiveSnapshot;
408 CurrentResourceOwner = saveResourceOwner;
409 PortalContext = savePortalContext;
415 MemoryContextSwitchTo(oldcxt);
417 /* Mark portal not active */
418 portal->status = PORTAL_READY;
420 ActivePortal = saveActivePortal;
421 ActiveSnapshot = saveActiveSnapshot;
422 CurrentResourceOwner = saveResourceOwner;
423 PortalContext = savePortalContext;
426 * We can now release any subsidiary memory of the portal's heap context;
427 * we'll never use it again. The executor already dropped its context,
428 * but this will clean up anything that glommed onto the portal's heap via
431 MemoryContextDeleteChildren(PortalGetHeapMemory(portal));