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-2009, 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.79 2009/06/11 14:48:56 momjian Exp $
19 *-------------------------------------------------------------------------
26 #include "access/xact.h"
27 #include "commands/portalcmds.h"
28 #include "executor/executor.h"
29 #include "executor/tstoreReceiver.h"
30 #include "tcop/pquery.h"
31 #include "utils/memutils.h"
32 #include "utils/snapmgr.h"
37 * Execute SQL DECLARE CURSOR command.
39 * The query has already been through parse analysis, rewriting, and planning.
40 * When it gets here, it looks like a SELECT PlannedStmt, except that the
41 * utilityStmt field is set.
44 PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
45 const char *queryString, bool isTopLevel)
47 DeclareCursorStmt *cstmt = (DeclareCursorStmt *) stmt->utilityStmt;
49 MemoryContext oldContext;
51 if (cstmt == NULL || !IsA(cstmt, DeclareCursorStmt))
52 elog(ERROR, "PerformCursorOpen called for non-cursor query");
55 * Disallow empty-string cursor name (conflicts with protocol-level
58 if (!cstmt->portalname || cstmt->portalname[0] == '\0')
60 (errcode(ERRCODE_INVALID_CURSOR_NAME),
61 errmsg("invalid cursor name: must not be empty")));
64 * If this is a non-holdable cursor, we require that this statement has
65 * been executed inside a transaction block (or else, it would have no
66 * user-visible effect).
68 if (!(cstmt->options & CURSOR_OPT_HOLD))
69 RequireTransactionChain(isTopLevel, "DECLARE CURSOR");
72 * Create a portal and copy the plan and queryString into its memory.
74 portal = CreatePortal(cstmt->portalname, false, false);
76 oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
78 stmt = copyObject(stmt);
79 stmt->utilityStmt = NULL; /* make it look like plain SELECT */
81 queryString = pstrdup(queryString);
83 PortalDefineQuery(portal,
86 "SELECT", /* cursor's query is always a SELECT */
91 * Also copy the outer portal's parameter list into the inner portal's
92 * memory context. We want to pass down the parameter values in case we
94 * DECLARE c CURSOR FOR SELECT ... WHERE foo = $1
95 * This will have been parsed using the outer parameter set and the
96 * parameter value needs to be preserved for use when the cursor is
100 params = copyParamList(params);
102 MemoryContextSwitchTo(oldContext);
105 * Set up options for portal.
107 * If the user didn't specify a SCROLL type, allow or disallow scrolling
108 * based on whether it would require any additional runtime overhead to do
109 * so. Also, we disallow scrolling for FOR UPDATE cursors.
111 portal->cursorOptions = cstmt->options;
112 if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
114 if (stmt->rowMarks == NIL &&
115 ExecSupportsBackwardScan(stmt->planTree))
116 portal->cursorOptions |= CURSOR_OPT_SCROLL;
118 portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
122 * Start execution, inserting parameters if any.
124 PortalStart(portal, params, GetActiveSnapshot());
126 Assert(portal->strategy == PORTAL_ONE_SELECT);
129 * We're done; the query won't actually be run until PerformPortalFetch is
136 * Execute SQL FETCH or MOVE command.
138 * stmt: parsetree node for command
139 * dest: where to send results
140 * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
141 * in which to store a command completion status string.
143 * completionTag may be NULL if caller doesn't want a status string.
146 PerformPortalFetch(FetchStmt *stmt,
154 * Disallow empty-string cursor name (conflicts with protocol-level
157 if (!stmt->portalname || stmt->portalname[0] == '\0')
159 (errcode(ERRCODE_INVALID_CURSOR_NAME),
160 errmsg("invalid cursor name: must not be empty")));
162 /* get the portal from the portal name */
163 portal = GetPortalByName(stmt->portalname);
164 if (!PortalIsValid(portal))
167 (errcode(ERRCODE_UNDEFINED_CURSOR),
168 errmsg("cursor \"%s\" does not exist", stmt->portalname)));
169 return; /* keep compiler happy */
172 /* Adjust dest if needed. MOVE wants destination DestNone */
174 dest = None_Receiver;
177 nprocessed = PortalRunFetch(portal,
182 /* Return command status if wanted */
184 snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
185 stmt->ismove ? "MOVE" : "FETCH",
194 PerformPortalClose(const char *name)
198 /* NULL means CLOSE ALL */
201 PortalHashTableDeleteAll();
206 * Disallow empty-string cursor name (conflicts with protocol-level
211 (errcode(ERRCODE_INVALID_CURSOR_NAME),
212 errmsg("invalid cursor name: must not be empty")));
215 * get the portal from the portal name
217 portal = GetPortalByName(name);
218 if (!PortalIsValid(portal))
221 (errcode(ERRCODE_UNDEFINED_CURSOR),
222 errmsg("cursor \"%s\" does not exist", name)));
223 return; /* keep compiler happy */
227 * Note: PortalCleanup is called as a side-effect
229 PortalDrop(portal, false);
235 * Clean up a portal when it's dropped. This is the standard cleanup hook
239 PortalCleanup(Portal portal)
241 QueryDesc *queryDesc;
246 AssertArg(PortalIsValid(portal));
247 AssertArg(portal->cleanup == PortalCleanup);
250 * Shut down executor, if still running. We skip this during error abort,
251 * since other mechanisms will take care of releasing executor resources,
252 * and we can't be sure that ExecutorEnd itself wouldn't fail.
254 queryDesc = PortalGetQueryDesc(portal);
257 portal->queryDesc = NULL;
258 if (portal->status != PORTAL_FAILED)
260 ResourceOwner saveResourceOwner;
262 /* We must make the portal's resource owner current */
263 saveResourceOwner = CurrentResourceOwner;
266 CurrentResourceOwner = portal->resowner;
267 /* we do not need AfterTriggerEndQuery() here */
268 ExecutorEnd(queryDesc);
269 FreeQueryDesc(queryDesc);
273 /* Ensure CurrentResourceOwner is restored on error */
274 CurrentResourceOwner = saveResourceOwner;
278 CurrentResourceOwner = saveResourceOwner;
284 * PersistHoldablePortal
286 * Prepare the specified Portal for access outside of the current
287 * transaction. When this function returns, all future accesses to the
288 * portal must be done via the Tuplestore (not by invoking the
292 PersistHoldablePortal(Portal portal)
294 QueryDesc *queryDesc = PortalGetQueryDesc(portal);
295 Portal saveActivePortal;
296 ResourceOwner saveResourceOwner;
297 MemoryContext savePortalContext;
298 MemoryContext oldcxt;
301 * If we're preserving a holdable portal, we had better be inside the
302 * transaction that originally created it.
304 Assert(portal->createSubid != InvalidSubTransactionId);
305 Assert(queryDesc != NULL);
308 * Caller must have created the tuplestore already.
310 Assert(portal->holdContext != NULL);
311 Assert(portal->holdStore != NULL);
314 * Before closing down the executor, we must copy the tupdesc into
315 * long-term memory, since it was created in executor memory.
317 oldcxt = MemoryContextSwitchTo(portal->holdContext);
319 portal->tupDesc = CreateTupleDescCopy(portal->tupDesc);
321 MemoryContextSwitchTo(oldcxt);
324 * Check for improper portal use, and mark portal active.
326 if (portal->status != PORTAL_READY)
328 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
329 errmsg("portal \"%s\" cannot be run", portal->name)));
330 portal->status = PORTAL_ACTIVE;
333 * Set up global portal context pointers.
335 saveActivePortal = ActivePortal;
336 saveResourceOwner = CurrentResourceOwner;
337 savePortalContext = PortalContext;
340 ActivePortal = portal;
341 CurrentResourceOwner = portal->resowner;
342 PortalContext = PortalGetHeapMemory(portal);
344 MemoryContextSwitchTo(PortalContext);
346 PushActiveSnapshot(queryDesc->snapshot);
349 * Rewind the executor: we need to store the entire result set in the
350 * tuplestore, so that subsequent backward FETCHs can be processed.
352 ExecutorRewind(queryDesc);
355 * Change the destination to output to the tuplestore. Note we tell
356 * the tuplestore receiver to detoast all data passed through it.
358 queryDesc->dest = CreateDestReceiver(DestTuplestore);
359 SetTuplestoreDestReceiverParams(queryDesc->dest,
364 /* Fetch the result set into the tuplestore */
365 ExecutorRun(queryDesc, ForwardScanDirection, 0L);
367 (*queryDesc->dest->rDestroy) (queryDesc->dest);
368 queryDesc->dest = NULL;
371 * Now shut down the inner executor.
373 portal->queryDesc = NULL; /* prevent double shutdown */
374 /* we do not need AfterTriggerEndQuery() here */
375 ExecutorEnd(queryDesc);
376 FreeQueryDesc(queryDesc);
379 * Set the position in the result set: ideally, this could be
380 * implemented by just skipping straight to the tuple # that we need
381 * to be at, but the tuplestore API doesn't support that. So we start
382 * at the beginning of the tuplestore and iterate through it until we
383 * reach where we need to be. FIXME someday? (Fortunately, the
384 * typical case is that we're supposed to be at or near the start of
385 * the result set, so this isn't as bad as it sounds.)
387 MemoryContextSwitchTo(portal->holdContext);
391 /* we can handle this case even if posOverflow */
392 while (tuplestore_advance(portal->holdStore, true))
399 if (portal->posOverflow) /* oops, cannot trust portalPos */
401 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
402 errmsg("could not reposition held cursor")));
404 tuplestore_rescan(portal->holdStore);
406 for (store_pos = 0; store_pos < portal->portalPos; store_pos++)
408 if (!tuplestore_advance(portal->holdStore, true))
409 elog(ERROR, "unexpected end of tuple stream");
415 /* Uncaught error while executing portal: mark it dead */
416 portal->status = PORTAL_FAILED;
418 /* Restore global vars and propagate error */
419 ActivePortal = saveActivePortal;
420 CurrentResourceOwner = saveResourceOwner;
421 PortalContext = savePortalContext;
427 MemoryContextSwitchTo(oldcxt);
429 /* Mark portal not active */
430 portal->status = PORTAL_READY;
432 ActivePortal = saveActivePortal;
433 CurrentResourceOwner = saveResourceOwner;
434 PortalContext = savePortalContext;
439 * We can now release any subsidiary memory of the portal's heap context;
440 * we'll never use it again. The executor already dropped its context,
441 * but this will clean up anything that glommed onto the portal's heap via
444 MemoryContextDeleteChildren(PortalGetHeapMemory(portal));