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-2005, 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.42 2005/06/03 23:05:28 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, ParamListInfo params)
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 * Because the planner is not cool about not scribbling on its input,
66 * we make a preliminary copy of the source querytree. This prevents
67 * problems in the case that the DECLARE CURSOR is in a portal and is
68 * executed repeatedly. XXX the planner really shouldn't modify its
69 * input ... FIXME someday.
71 query = copyObject(stmt->query);
74 * The query has been through parse analysis, but not rewriting or
75 * planning as yet. Note that the grammar ensured we have a SELECT
76 * query, so we are not expecting rule rewriting to do anything
79 AcquireRewriteLocks(query);
80 rewritten = QueryRewrite(query);
81 if (list_length(rewritten) != 1 || !IsA(linitial(rewritten), Query))
82 elog(ERROR, "unexpected rewrite result");
83 query = (Query *) linitial(rewritten);
84 if (query->commandType != CMD_SELECT)
85 elog(ERROR, "unexpected rewrite result");
89 (errcode(ERRCODE_SYNTAX_ERROR),
90 errmsg("DECLARE CURSOR may not specify INTO")));
91 if (query->rowMarks != NIL)
93 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
94 errmsg("DECLARE CURSOR ... FOR UPDATE/SHARE is not supported"),
95 errdetail("Cursors must be READ ONLY.")));
97 plan = planner(query, true, stmt->options, NULL);
100 * Create a portal and copy the query and plan into its memory
103 portal = CreatePortal(stmt->portalname, false, false);
105 oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
107 query = copyObject(query);
108 plan = copyObject(plan);
110 PortalDefineQuery(portal,
111 NULL, /* unfortunately don't have sourceText */
112 "SELECT", /* cursor's query is always a SELECT */
115 PortalGetHeapMemory(portal));
118 * Also copy the outer portal's parameter list into the inner portal's
119 * memory context. We want to pass down the parameter values in case
120 * we had a command like DECLARE c CURSOR FOR SELECT ... WHERE foo =
121 * $1 This will have been parsed using the outer parameter set and the
122 * parameter value needs to be preserved for use when the cursor is
125 params = copyParamList(params);
127 MemoryContextSwitchTo(oldContext);
130 * Set up options for portal.
132 * If the user didn't specify a SCROLL type, allow or disallow scrolling
133 * based on whether it would require any additional runtime overhead
136 portal->cursorOptions = stmt->options;
137 if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
139 if (ExecSupportsBackwardScan(plan))
140 portal->cursorOptions |= CURSOR_OPT_SCROLL;
142 portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
146 * Start execution, inserting parameters if any.
148 PortalStart(portal, params, ActiveSnapshot);
150 Assert(portal->strategy == PORTAL_ONE_SELECT);
153 * We're done; the query won't actually be run until
154 * PerformPortalFetch is called.
160 * Execute SQL FETCH or MOVE command.
162 * stmt: parsetree node for command
163 * dest: where to send results
164 * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
165 * in which to store a command completion status string.
167 * completionTag may be NULL if caller doesn't want a status string.
170 PerformPortalFetch(FetchStmt *stmt,
178 * Disallow empty-string cursor name (conflicts with protocol-level
181 if (!stmt->portalname || stmt->portalname[0] == '\0')
183 (errcode(ERRCODE_INVALID_CURSOR_NAME),
184 errmsg("invalid cursor name: must not be empty")));
186 /* get the portal from the portal name */
187 portal = GetPortalByName(stmt->portalname);
188 if (!PortalIsValid(portal))
191 (errcode(ERRCODE_UNDEFINED_CURSOR),
192 errmsg("cursor \"%s\" does not exist", stmt->portalname)));
193 return; /* keep compiler happy */
196 /* Adjust dest if needed. MOVE wants destination None */
198 dest = None_Receiver;
201 nprocessed = PortalRunFetch(portal,
206 /* Return command status if wanted */
208 snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
209 stmt->ismove ? "MOVE" : "FETCH",
218 PerformPortalClose(const char *name)
223 * Disallow empty-string cursor name (conflicts with protocol-level
226 if (!name || name[0] == '\0')
228 (errcode(ERRCODE_INVALID_CURSOR_NAME),
229 errmsg("invalid cursor name: must not be empty")));
232 * get the portal from the portal name
234 portal = GetPortalByName(name);
235 if (!PortalIsValid(portal))
238 (errcode(ERRCODE_UNDEFINED_CURSOR),
239 errmsg("cursor \"%s\" does not exist", name)));
240 return; /* keep compiler happy */
244 * Note: PortalCleanup is called as a side-effect
246 PortalDrop(portal, false);
252 * Clean up a portal when it's dropped. This is the standard cleanup hook
256 PortalCleanup(Portal portal)
258 QueryDesc *queryDesc;
263 AssertArg(PortalIsValid(portal));
264 AssertArg(portal->cleanup == PortalCleanup);
267 * Shut down executor, if still running. We skip this during error
268 * abort, since other mechanisms will take care of releasing executor
269 * resources, and we can't be sure that ExecutorEnd itself wouldn't
272 queryDesc = PortalGetQueryDesc(portal);
275 portal->queryDesc = NULL;
276 if (portal->status != PORTAL_FAILED)
278 ResourceOwner saveResourceOwner;
280 /* We must make the portal's resource owner current */
281 saveResourceOwner = CurrentResourceOwner;
284 CurrentResourceOwner = portal->resowner;
285 /* we do not need AfterTriggerEndQuery() here */
286 ExecutorEnd(queryDesc);
290 /* Ensure CurrentResourceOwner is restored on error */
291 CurrentResourceOwner = saveResourceOwner;
295 CurrentResourceOwner = saveResourceOwner;
301 * PersistHoldablePortal
303 * Prepare the specified Portal for access outside of the current
304 * transaction. When this function returns, all future accesses to the
305 * portal must be done via the Tuplestore (not by invoking the
309 PersistHoldablePortal(Portal portal)
311 QueryDesc *queryDesc = PortalGetQueryDesc(portal);
312 Portal saveActivePortal;
313 Snapshot saveActiveSnapshot;
314 ResourceOwner saveResourceOwner;
315 MemoryContext savePortalContext;
316 MemoryContext saveQueryContext;
317 MemoryContext oldcxt;
320 * If we're preserving a holdable portal, we had better be inside the
321 * transaction that originally created it.
323 Assert(portal->createSubid != InvalidSubTransactionId);
324 Assert(queryDesc != NULL);
327 * Caller must have created the tuplestore already.
329 Assert(portal->holdContext != NULL);
330 Assert(portal->holdStore != NULL);
333 * Before closing down the executor, we must copy the tupdesc into
334 * long-term memory, since it was created in executor memory.
336 oldcxt = MemoryContextSwitchTo(portal->holdContext);
338 portal->tupDesc = CreateTupleDescCopy(portal->tupDesc);
340 MemoryContextSwitchTo(oldcxt);
343 * Check for improper portal use, and mark portal active.
345 if (portal->status != PORTAL_READY)
347 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
348 errmsg("portal \"%s\" cannot be run", portal->name)));
349 portal->status = PORTAL_ACTIVE;
352 * Set up global portal context pointers.
354 saveActivePortal = ActivePortal;
355 saveActiveSnapshot = ActiveSnapshot;
356 saveResourceOwner = CurrentResourceOwner;
357 savePortalContext = PortalContext;
358 saveQueryContext = QueryContext;
361 ActivePortal = portal;
362 ActiveSnapshot = queryDesc->snapshot;
363 CurrentResourceOwner = portal->resowner;
364 PortalContext = PortalGetHeapMemory(portal);
365 QueryContext = portal->queryContext;
367 MemoryContextSwitchTo(PortalContext);
370 * Rewind the executor: we need to store the entire result set in
371 * the tuplestore, so that subsequent backward FETCHs can be
374 ExecutorRewind(queryDesc);
376 /* Change the destination to output to the tuplestore */
377 queryDesc->dest = CreateDestReceiver(Tuplestore, portal);
379 /* Fetch the result set into the tuplestore */
380 ExecutorRun(queryDesc, ForwardScanDirection, 0L);
382 (*queryDesc->dest->rDestroy) (queryDesc->dest);
383 queryDesc->dest = NULL;
386 * Now shut down the inner executor.
388 portal->queryDesc = NULL; /* prevent double shutdown */
389 /* we do not need AfterTriggerEndQuery() here */
390 ExecutorEnd(queryDesc);
393 * Reset the position in the result set: ideally, this could be
394 * implemented by just skipping straight to the tuple # that we
395 * need to be at, but the tuplestore API doesn't support that. So
396 * we start at the beginning of the tuplestore and iterate through
397 * it until we reach where we need to be. FIXME someday?
399 MemoryContextSwitchTo(portal->holdContext);
405 if (portal->posOverflow) /* oops, cannot trust portalPos */
407 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
408 errmsg("could not reposition held cursor")));
410 tuplestore_rescan(portal->holdStore);
412 for (store_pos = 0; store_pos < portal->portalPos; store_pos++)
417 tup = tuplestore_gettuple(portal->holdStore, true,
421 elog(ERROR, "unexpected end of tuple stream");
430 /* Uncaught error while executing portal: mark it dead */
431 portal->status = PORTAL_FAILED;
433 /* Restore global vars and propagate error */
434 ActivePortal = saveActivePortal;
435 ActiveSnapshot = saveActiveSnapshot;
436 CurrentResourceOwner = saveResourceOwner;
437 PortalContext = savePortalContext;
438 QueryContext = saveQueryContext;
444 MemoryContextSwitchTo(oldcxt);
446 /* Mark portal not active */
447 portal->status = PORTAL_READY;
449 ActivePortal = saveActivePortal;
450 ActiveSnapshot = saveActiveSnapshot;
451 CurrentResourceOwner = saveResourceOwner;
452 PortalContext = savePortalContext;
453 QueryContext = saveQueryContext;
456 * We can now release any subsidiary memory of the portal's heap
457 * context; we'll never use it again. The executor already dropped
458 * its context, but this will clean up anything that glommed onto the
459 * portal's heap via PortalContext.
461 MemoryContextDeleteChildren(PortalGetHeapMemory(portal));