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-2002, PostgreSQL Global Development Group
13 * Portions Copyright (c) 1994, Regents of the University of California
17 * $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.18 2003/07/28 00:09:14 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 strange.
69 rewritten = QueryRewrite((Query *) stmt->query);
70 if (length(rewritten) != 1 || !IsA(lfirst(rewritten), Query))
71 elog(ERROR, "unexpected rewrite result");
72 query = (Query *) lfirst(rewritten);
73 if (query->commandType != CMD_SELECT)
74 elog(ERROR, "unexpected rewrite result");
78 (errcode(ERRCODE_SYNTAX_ERROR),
79 errmsg("DECLARE CURSOR may not specify INTO")));
80 if (query->rowMarks != NIL)
82 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
83 errmsg("DECLARE CURSOR ... FOR UPDATE is not supported"),
84 errdetail("Cursors must be READ ONLY.")));
86 plan = planner(query, true, stmt->options);
89 * Create a portal and copy the query and plan into its memory context.
90 * (If a duplicate cursor name already exists, warn and drop it.)
92 portal = CreatePortal(stmt->portalname, true, false);
94 oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
96 query = copyObject(query);
97 plan = copyObject(plan);
99 PortalDefineQuery(portal,
100 NULL, /* unfortunately don't have sourceText */
101 "SELECT", /* cursor's query is always a SELECT */
104 PortalGetHeapMemory(portal));
106 MemoryContextSwitchTo(oldContext);
109 * Set up options for portal.
111 * If the user didn't specify a SCROLL type, allow or disallow
112 * scrolling based on whether it would require any additional
113 * runtime overhead to do so.
115 portal->cursorOptions = stmt->options;
116 if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
118 if (ExecSupportsBackwardScan(plan))
119 portal->cursorOptions |= CURSOR_OPT_SCROLL;
121 portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
125 * Start execution --- never any params for a cursor.
127 PortalStart(portal, NULL);
129 Assert(portal->strategy == PORTAL_ONE_SELECT);
132 * We're done; the query won't actually be run until PerformPortalFetch
139 * Execute SQL FETCH or MOVE command.
141 * stmt: parsetree node for command
142 * dest: where to send results
143 * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
144 * in which to store a command completion status string.
146 * completionTag may be NULL if caller doesn't want a status string.
149 PerformPortalFetch(FetchStmt *stmt,
157 * Disallow empty-string cursor name (conflicts with protocol-level
160 if (!stmt->portalname || stmt->portalname[0] == '\0')
162 (errcode(ERRCODE_INVALID_CURSOR_NAME),
163 errmsg("invalid cursor name: must not be empty")));
165 /* get the portal from the portal name */
166 portal = GetPortalByName(stmt->portalname);
167 if (!PortalIsValid(portal))
169 /* FIXME: shouldn't this be an ERROR? */
171 (errcode(ERRCODE_UNDEFINED_CURSOR),
172 errmsg("portal \"%s\" does not exist", stmt->portalname),
173 errfunction("PerformPortalFetch"))); /* for ecpg */
175 strcpy(completionTag, stmt->ismove ? "MOVE 0" : "FETCH 0");
179 /* Adjust dest if needed. MOVE wants destination None */
181 dest = None_Receiver;
184 nprocessed = PortalRunFetch(portal,
189 /* Return command status if wanted */
191 snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
192 stmt->ismove ? "MOVE" : "FETCH",
201 PerformPortalClose(const char *name)
206 * Disallow empty-string cursor name (conflicts with protocol-level
209 if (!name || name[0] == '\0')
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("portal \"%s\" does not exist", name),
223 errfunction("PerformPortalClose"))); /* for ecpg */
228 * Note: PortalCleanup is called as a side-effect
230 PortalDrop(portal, false);
236 * Clean up a portal when it's dropped. This is the standard cleanup hook
240 PortalCleanup(Portal portal, bool isError)
242 QueryDesc *queryDesc;
247 AssertArg(PortalIsValid(portal));
248 AssertArg(portal->cleanup == PortalCleanup);
251 * Shut down executor, if still running. We skip this during error
252 * abort, since other mechanisms will take care of releasing executor
253 * resources, and we can't be sure that ExecutorEnd itself wouldn't fail.
255 queryDesc = PortalGetQueryDesc(portal);
258 portal->queryDesc = NULL;
260 ExecutorEnd(queryDesc);
265 * PersistHoldablePortal
267 * Prepare the specified Portal for access outside of the current
268 * transaction. When this function returns, all future accesses to the
269 * portal must be done via the Tuplestore (not by invoking the
273 PersistHoldablePortal(Portal portal)
275 QueryDesc *queryDesc = PortalGetQueryDesc(portal);
276 MemoryContext savePortalContext;
277 MemoryContext saveQueryContext;
278 MemoryContext oldcxt;
281 * If we're preserving a holdable portal, we had better be
282 * inside the transaction that originally created it.
284 Assert(portal->createXact == GetCurrentTransactionId());
285 Assert(queryDesc != NULL);
286 Assert(portal->portalReady);
287 Assert(!portal->portalDone);
290 * Caller must have created the tuplestore already.
292 Assert(portal->holdContext != NULL);
293 Assert(portal->holdStore != NULL);
296 * Before closing down the executor, we must copy the tupdesc into
297 * long-term memory, since it was created in executor memory.
299 oldcxt = MemoryContextSwitchTo(portal->holdContext);
301 portal->tupDesc = CreateTupleDescCopy(portal->tupDesc);
303 MemoryContextSwitchTo(oldcxt);
306 * Check for improper portal use, and mark portal active.
308 if (portal->portalActive)
310 (errcode(ERRCODE_OBJECT_IN_USE),
311 errmsg("portal \"%s\" already active", portal->name)));
312 portal->portalActive = true;
315 * Set global portal context pointers.
317 savePortalContext = PortalContext;
318 PortalContext = PortalGetHeapMemory(portal);
319 saveQueryContext = QueryContext;
320 QueryContext = portal->queryContext;
322 MemoryContextSwitchTo(PortalContext);
325 * Rewind the executor: we need to store the entire result set in
326 * the tuplestore, so that subsequent backward FETCHs can be
329 ExecutorRewind(queryDesc);
331 /* Change the destination to output to the tuplestore */
332 queryDesc->dest = CreateDestReceiver(Tuplestore, portal);
334 /* Fetch the result set into the tuplestore */
335 ExecutorRun(queryDesc, ForwardScanDirection, 0L);
337 (*queryDesc->dest->destroy) (queryDesc->dest);
338 queryDesc->dest = NULL;
341 * Now shut down the inner executor.
343 portal->queryDesc = NULL; /* prevent double shutdown */
344 ExecutorEnd(queryDesc);
346 /* Mark portal not active */
347 portal->portalActive = false;
349 PortalContext = savePortalContext;
350 QueryContext = saveQueryContext;
353 * Reset the position in the result set: ideally, this could be
354 * implemented by just skipping straight to the tuple # that we need
355 * to be at, but the tuplestore API doesn't support that. So we
356 * start at the beginning of the tuplestore and iterate through it
357 * until we reach where we need to be. FIXME someday?
359 MemoryContextSwitchTo(portal->holdContext);
365 if (portal->posOverflow) /* oops, cannot trust portalPos */
367 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
368 errmsg("could not reposition held cursor")));
370 tuplestore_rescan(portal->holdStore);
372 for (store_pos = 0; store_pos < portal->portalPos; store_pos++)
377 tup = tuplestore_gettuple(portal->holdStore, true,
381 elog(ERROR, "unexpected end of tuple stream");
388 MemoryContextSwitchTo(oldcxt);
391 * We can now release any subsidiary memory of the portal's heap
392 * context; we'll never use it again. The executor already dropped
393 * its context, but this will clean up anything that glommed onto
394 * the portal's heap via PortalContext.
396 MemoryContextDeleteChildren(PortalGetHeapMemory(portal));