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-2008, PostgreSQL Global Development Group
13 * Portions Copyright (c) 1994, Regents of the University of California
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"
31 #include "utils/snapmgr.h"
36 * Execute SQL DECLARE CURSOR command.
38 * The query has already been through parse analysis, rewriting, and planning.
39 * When it gets here, it looks like a SELECT PlannedStmt, except that the
40 * utilityStmt field is set.
43 PerformCursorOpen(PlannedStmt
*stmt
, ParamListInfo params
,
44 const char *queryString
, bool isTopLevel
)
46 DeclareCursorStmt
*cstmt
= (DeclareCursorStmt
*) stmt
->utilityStmt
;
48 MemoryContext oldContext
;
50 if (cstmt
== NULL
|| !IsA(cstmt
, DeclareCursorStmt
))
51 elog(ERROR
, "PerformCursorOpen called for non-cursor query");
54 * Disallow empty-string cursor name (conflicts with protocol-level
57 if (!cstmt
->portalname
|| cstmt
->portalname
[0] == '\0')
59 (errcode(ERRCODE_INVALID_CURSOR_NAME
),
60 errmsg("invalid cursor name: must not be empty")));
63 * If this is a non-holdable cursor, we require that this statement has
64 * been executed inside a transaction block (or else, it would have no
65 * user-visible effect).
67 if (!(cstmt
->options
& CURSOR_OPT_HOLD
))
68 RequireTransactionChain(isTopLevel
, "DECLARE CURSOR");
71 * Create a portal and copy the plan and queryString into its memory.
73 portal
= CreatePortal(cstmt
->portalname
, false, false);
75 oldContext
= MemoryContextSwitchTo(PortalGetHeapMemory(portal
));
77 stmt
= copyObject(stmt
);
78 stmt
->utilityStmt
= NULL
; /* make it look like plain SELECT */
80 queryString
= pstrdup(queryString
);
82 PortalDefineQuery(portal
,
85 "SELECT", /* cursor's query is always a SELECT */
90 * Also copy the outer portal's parameter list into the inner portal's
91 * memory context. We want to pass down the parameter values in case we
93 * DECLARE c CURSOR FOR SELECT ... WHERE foo = $1
94 * This will have been parsed using the outer parameter set and the
95 * parameter value needs to be preserved for use when the cursor is
99 params
= copyParamList(params
);
101 MemoryContextSwitchTo(oldContext
);
104 * Set up options for portal.
106 * If the user didn't specify a SCROLL type, allow or disallow scrolling
107 * based on whether it would require any additional runtime overhead to do
108 * so. Also, we disallow scrolling for FOR UPDATE cursors.
110 portal
->cursorOptions
= cstmt
->options
;
111 if (!(portal
->cursorOptions
& (CURSOR_OPT_SCROLL
| CURSOR_OPT_NO_SCROLL
)))
113 if (stmt
->rowMarks
== NIL
&&
114 ExecSupportsBackwardScan(stmt
->planTree
))
115 portal
->cursorOptions
|= CURSOR_OPT_SCROLL
;
117 portal
->cursorOptions
|= CURSOR_OPT_NO_SCROLL
;
121 * Start execution, inserting parameters if any.
123 PortalStart(portal
, params
, GetActiveSnapshot());
125 Assert(portal
->strategy
== PORTAL_ONE_SELECT
);
128 * We're done; the query won't actually be run until PerformPortalFetch is
135 * Execute SQL FETCH or MOVE command.
137 * stmt: parsetree node for command
138 * dest: where to send results
139 * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
140 * in which to store a command completion status string.
142 * completionTag may be NULL if caller doesn't want a status string.
145 PerformPortalFetch(FetchStmt
*stmt
,
153 * Disallow empty-string cursor name (conflicts with protocol-level
156 if (!stmt
->portalname
|| stmt
->portalname
[0] == '\0')
158 (errcode(ERRCODE_INVALID_CURSOR_NAME
),
159 errmsg("invalid cursor name: must not be empty")));
161 /* get the portal from the portal name */
162 portal
= GetPortalByName(stmt
->portalname
);
163 if (!PortalIsValid(portal
))
166 (errcode(ERRCODE_UNDEFINED_CURSOR
),
167 errmsg("cursor \"%s\" does not exist", stmt
->portalname
)));
168 return; /* keep compiler happy */
171 /* Adjust dest if needed. MOVE wants destination DestNone */
173 dest
= None_Receiver
;
176 nprocessed
= PortalRunFetch(portal
,
181 /* Return command status if wanted */
183 snprintf(completionTag
, COMPLETION_TAG_BUFSIZE
, "%s %ld",
184 stmt
->ismove
? "MOVE" : "FETCH",
193 PerformPortalClose(const char *name
)
197 /* NULL means CLOSE ALL */
200 PortalHashTableDeleteAll();
205 * Disallow empty-string cursor name (conflicts with protocol-level
210 (errcode(ERRCODE_INVALID_CURSOR_NAME
),
211 errmsg("invalid cursor name: must not be empty")));
214 * get the portal from the portal name
216 portal
= GetPortalByName(name
);
217 if (!PortalIsValid(portal
))
220 (errcode(ERRCODE_UNDEFINED_CURSOR
),
221 errmsg("cursor \"%s\" does not exist", name
)));
222 return; /* keep compiler happy */
226 * Note: PortalCleanup is called as a side-effect
228 PortalDrop(portal
, false);
234 * Clean up a portal when it's dropped. This is the standard cleanup hook
238 PortalCleanup(Portal portal
)
240 QueryDesc
*queryDesc
;
245 AssertArg(PortalIsValid(portal
));
246 AssertArg(portal
->cleanup
== PortalCleanup
);
249 * Shut down executor, if still running. We skip this during error abort,
250 * since other mechanisms will take care of releasing executor resources,
251 * and we can't be sure that ExecutorEnd itself wouldn't fail.
253 queryDesc
= PortalGetQueryDesc(portal
);
256 portal
->queryDesc
= NULL
;
257 if (portal
->status
!= PORTAL_FAILED
)
259 ResourceOwner saveResourceOwner
;
261 /* We must make the portal's resource owner current */
262 saveResourceOwner
= CurrentResourceOwner
;
265 CurrentResourceOwner
= portal
->resowner
;
266 /* we do not need AfterTriggerEndQuery() here */
267 ExecutorEnd(queryDesc
);
268 FreeQueryDesc(queryDesc
);
272 /* Ensure CurrentResourceOwner is restored on error */
273 CurrentResourceOwner
= saveResourceOwner
;
277 CurrentResourceOwner
= saveResourceOwner
;
283 * PersistHoldablePortal
285 * Prepare the specified Portal for access outside of the current
286 * transaction. When this function returns, all future accesses to the
287 * portal must be done via the Tuplestore (not by invoking the
291 PersistHoldablePortal(Portal portal
)
293 QueryDesc
*queryDesc
= PortalGetQueryDesc(portal
);
294 Portal saveActivePortal
;
295 ResourceOwner saveResourceOwner
;
296 MemoryContext savePortalContext
;
297 MemoryContext oldcxt
;
300 * If we're preserving a holdable portal, we had better be inside the
301 * transaction that originally created it.
303 Assert(portal
->createSubid
!= InvalidSubTransactionId
);
304 Assert(queryDesc
!= NULL
);
307 * Caller must have created the tuplestore already.
309 Assert(portal
->holdContext
!= NULL
);
310 Assert(portal
->holdStore
!= NULL
);
313 * Before closing down the executor, we must copy the tupdesc into
314 * long-term memory, since it was created in executor memory.
316 oldcxt
= MemoryContextSwitchTo(portal
->holdContext
);
318 portal
->tupDesc
= CreateTupleDescCopy(portal
->tupDesc
);
320 MemoryContextSwitchTo(oldcxt
);
323 * Check for improper portal use, and mark portal active.
325 if (portal
->status
!= PORTAL_READY
)
327 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
328 errmsg("portal \"%s\" cannot be run", portal
->name
)));
329 portal
->status
= PORTAL_ACTIVE
;
332 * Set up global portal context pointers.
334 saveActivePortal
= ActivePortal
;
335 saveResourceOwner
= CurrentResourceOwner
;
336 savePortalContext
= PortalContext
;
339 ActivePortal
= portal
;
340 CurrentResourceOwner
= portal
->resowner
;
341 PortalContext
= PortalGetHeapMemory(portal
);
343 MemoryContextSwitchTo(PortalContext
);
345 PushActiveSnapshot(queryDesc
->snapshot
);
348 * Rewind the executor: we need to store the entire result set in the
349 * tuplestore, so that subsequent backward FETCHs can be processed.
351 ExecutorRewind(queryDesc
);
353 /* Change the destination to output to the tuplestore */
354 queryDesc
->dest
= CreateDestReceiver(DestTuplestore
, portal
);
356 /* Fetch the result set into the tuplestore */
357 ExecutorRun(queryDesc
, ForwardScanDirection
, 0L);
359 (*queryDesc
->dest
->rDestroy
) (queryDesc
->dest
);
360 queryDesc
->dest
= NULL
;
363 * Now shut down the inner executor.
365 portal
->queryDesc
= NULL
; /* prevent double shutdown */
366 /* we do not need AfterTriggerEndQuery() here */
367 ExecutorEnd(queryDesc
);
368 FreeQueryDesc(queryDesc
);
371 * Set the position in the result set: ideally, this could be
372 * implemented by just skipping straight to the tuple # that we need
373 * to be at, but the tuplestore API doesn't support that. So we start
374 * at the beginning of the tuplestore and iterate through it until we
375 * reach where we need to be. FIXME someday? (Fortunately, the
376 * typical case is that we're supposed to be at or near the start of
377 * the result set, so this isn't as bad as it sounds.)
379 MemoryContextSwitchTo(portal
->holdContext
);
383 /* we can handle this case even if posOverflow */
384 while (tuplestore_advance(portal
->holdStore
, true))
391 if (portal
->posOverflow
) /* oops, cannot trust portalPos */
393 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
394 errmsg("could not reposition held cursor")));
396 tuplestore_rescan(portal
->holdStore
);
398 for (store_pos
= 0; store_pos
< portal
->portalPos
; store_pos
++)
400 if (!tuplestore_advance(portal
->holdStore
, true))
401 elog(ERROR
, "unexpected end of tuple stream");
407 /* Uncaught error while executing portal: mark it dead */
408 portal
->status
= PORTAL_FAILED
;
410 /* Restore global vars and propagate error */
411 ActivePortal
= saveActivePortal
;
412 CurrentResourceOwner
= saveResourceOwner
;
413 PortalContext
= savePortalContext
;
419 MemoryContextSwitchTo(oldcxt
);
421 /* Mark portal not active */
422 portal
->status
= PORTAL_READY
;
424 ActivePortal
= saveActivePortal
;
425 CurrentResourceOwner
= saveResourceOwner
;
426 PortalContext
= savePortalContext
;
431 * We can now release any subsidiary memory of the portal's heap context;
432 * we'll never use it again. The executor already dropped its context,
433 * but this will clean up anything that glommed onto the portal's heap via
436 MemoryContextDeleteChildren(PortalGetHeapMemory(portal
));