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-2024, PostgreSQL Global Development Group
13 * Portions Copyright (c) 1994, Regents of the University of California
17 * src/backend/commands/portalcmds.c
19 *-------------------------------------------------------------------------
26 #include "access/xact.h"
27 #include "commands/portalcmds.h"
28 #include "executor/executor.h"
29 #include "executor/tstoreReceiver.h"
30 #include "miscadmin.h"
31 #include "nodes/queryjumble.h"
32 #include "parser/analyze.h"
33 #include "rewrite/rewriteHandler.h"
34 #include "tcop/pquery.h"
35 #include "tcop/tcopprot.h"
36 #include "utils/memutils.h"
37 #include "utils/snapmgr.h"
42 * Execute SQL DECLARE CURSOR command.
45 PerformCursorOpen(ParseState
*pstate
, DeclareCursorStmt
*cstmt
, ParamListInfo params
,
48 Query
*query
= castNode(Query
, cstmt
->query
);
49 JumbleState
*jstate
= NULL
;
53 MemoryContext oldContext
;
57 * Disallow empty-string cursor name (conflicts with protocol-level
60 if (!cstmt
->portalname
|| cstmt
->portalname
[0] == '\0')
62 (errcode(ERRCODE_INVALID_CURSOR_NAME
),
63 errmsg("invalid cursor name: must not be empty")));
66 * If this is a non-holdable cursor, we require that this statement has
67 * been executed inside a transaction block (or else, it would have no
68 * user-visible effect).
70 if (!(cstmt
->options
& CURSOR_OPT_HOLD
))
71 RequireTransactionBlock(isTopLevel
, "DECLARE CURSOR");
72 else if (InSecurityRestrictedOperation())
74 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
75 errmsg("cannot create a cursor WITH HOLD within security-restricted operation")));
77 /* Query contained by DeclareCursor needs to be jumbled if requested */
78 if (IsQueryIdEnabled())
79 jstate
= JumbleQuery(query
);
81 if (post_parse_analyze_hook
)
82 (*post_parse_analyze_hook
) (pstate
, query
, jstate
);
85 * Parse analysis was done already, but we still have to run the rule
86 * rewriter. We do not do AcquireRewriteLocks: we assume the query either
87 * came straight from the parser, or suitable locks were acquired by
90 rewritten
= QueryRewrite(query
);
92 /* SELECT should never rewrite to more or less than one query */
93 if (list_length(rewritten
) != 1)
94 elog(ERROR
, "non-SELECT statement in DECLARE CURSOR");
96 query
= linitial_node(Query
, rewritten
);
98 if (query
->commandType
!= CMD_SELECT
)
99 elog(ERROR
, "non-SELECT statement in DECLARE CURSOR");
101 /* Plan the query, applying the specified options */
102 plan
= pg_plan_query(query
, pstate
->p_sourcetext
, cstmt
->options
, params
);
105 * Create a portal and copy the plan and query string into its memory.
107 portal
= CreatePortal(cstmt
->portalname
, false, false);
109 oldContext
= MemoryContextSwitchTo(portal
->portalContext
);
111 plan
= copyObject(plan
);
113 queryString
= pstrdup(pstate
->p_sourcetext
);
115 PortalDefineQuery(portal
,
118 CMDTAG_SELECT
, /* cursor's query is always a SELECT */
123 * Also copy the outer portal's parameter list into the inner portal's
124 * memory context. We want to pass down the parameter values in case we
126 * DECLARE c CURSOR FOR SELECT ... WHERE foo = $1
127 * This will have been parsed using the outer parameter set and the
128 * parameter value needs to be preserved for use when the cursor is
132 params
= copyParamList(params
);
134 MemoryContextSwitchTo(oldContext
);
137 * Set up options for portal.
139 * If the user didn't specify a SCROLL type, allow or disallow scrolling
140 * based on whether it would require any additional runtime overhead to do
141 * so. Also, we disallow scrolling for FOR UPDATE cursors.
143 portal
->cursorOptions
= cstmt
->options
;
144 if (!(portal
->cursorOptions
& (CURSOR_OPT_SCROLL
| CURSOR_OPT_NO_SCROLL
)))
146 if (plan
->rowMarks
== NIL
&&
147 ExecSupportsBackwardScan(plan
->planTree
))
148 portal
->cursorOptions
|= CURSOR_OPT_SCROLL
;
150 portal
->cursorOptions
|= CURSOR_OPT_NO_SCROLL
;
154 * Start execution, inserting parameters if any.
156 PortalStart(portal
, params
, 0, GetActiveSnapshot());
158 Assert(portal
->strategy
== PORTAL_ONE_SELECT
);
161 * We're done; the query won't actually be run until PerformPortalFetch is
168 * Execute SQL FETCH or MOVE command.
170 * stmt: parsetree node for command
171 * dest: where to send results
172 * qc: where to store a command completion status data.
174 * qc may be NULL if caller doesn't want status data.
177 PerformPortalFetch(FetchStmt
*stmt
,
185 * Disallow empty-string cursor name (conflicts with protocol-level
188 if (!stmt
->portalname
|| stmt
->portalname
[0] == '\0')
190 (errcode(ERRCODE_INVALID_CURSOR_NAME
),
191 errmsg("invalid cursor name: must not be empty")));
193 /* get the portal from the portal name */
194 portal
= GetPortalByName(stmt
->portalname
);
195 if (!PortalIsValid(portal
))
198 (errcode(ERRCODE_UNDEFINED_CURSOR
),
199 errmsg("cursor \"%s\" does not exist", stmt
->portalname
)));
200 return; /* keep compiler happy */
203 /* Adjust dest if needed. MOVE wants destination DestNone */
205 dest
= None_Receiver
;
208 nprocessed
= PortalRunFetch(portal
,
213 /* Return command status if wanted */
215 SetQueryCompletion(qc
, stmt
->ismove
? CMDTAG_MOVE
: CMDTAG_FETCH
,
224 PerformPortalClose(const char *name
)
228 /* NULL means CLOSE ALL */
231 PortalHashTableDeleteAll();
236 * Disallow empty-string cursor name (conflicts with protocol-level
241 (errcode(ERRCODE_INVALID_CURSOR_NAME
),
242 errmsg("invalid cursor name: must not be empty")));
245 * get the portal from the portal name
247 portal
= GetPortalByName(name
);
248 if (!PortalIsValid(portal
))
251 (errcode(ERRCODE_UNDEFINED_CURSOR
),
252 errmsg("cursor \"%s\" does not exist", name
)));
253 return; /* keep compiler happy */
257 * Note: PortalCleanup is called as a side-effect, if not already done.
259 PortalDrop(portal
, false);
265 * Clean up a portal when it's dropped. This is the standard cleanup hook
268 * Note: if portal->status is PORTAL_FAILED, we are probably being called
269 * during error abort, and must be careful to avoid doing anything that
270 * is likely to fail again.
273 PortalCleanup(Portal portal
)
275 QueryDesc
*queryDesc
;
280 Assert(PortalIsValid(portal
));
281 Assert(portal
->cleanup
== PortalCleanup
);
284 * Shut down executor, if still running. We skip this during error abort,
285 * since other mechanisms will take care of releasing executor resources,
286 * and we can't be sure that ExecutorEnd itself wouldn't fail.
288 queryDesc
= portal
->queryDesc
;
292 * Reset the queryDesc before anything else. This prevents us from
293 * trying to shut down the executor twice, in case of an error below.
294 * The transaction abort mechanisms will take care of resource cleanup
297 portal
->queryDesc
= NULL
;
299 if (portal
->status
!= PORTAL_FAILED
)
301 ResourceOwner saveResourceOwner
;
303 /* We must make the portal's resource owner current */
304 saveResourceOwner
= CurrentResourceOwner
;
305 if (portal
->resowner
)
306 CurrentResourceOwner
= portal
->resowner
;
308 ExecutorFinish(queryDesc
);
309 ExecutorEnd(queryDesc
);
310 FreeQueryDesc(queryDesc
);
312 CurrentResourceOwner
= saveResourceOwner
;
318 * PersistHoldablePortal
320 * Prepare the specified Portal for access outside of the current
321 * transaction. When this function returns, all future accesses to the
322 * portal must be done via the Tuplestore (not by invoking the
326 PersistHoldablePortal(Portal portal
)
328 QueryDesc
*queryDesc
= portal
->queryDesc
;
329 Portal saveActivePortal
;
330 ResourceOwner saveResourceOwner
;
331 MemoryContext savePortalContext
;
332 MemoryContext oldcxt
;
335 * If we're preserving a holdable portal, we had better be inside the
336 * transaction that originally created it.
338 Assert(portal
->createSubid
!= InvalidSubTransactionId
);
339 Assert(queryDesc
!= NULL
);
342 * Caller must have created the tuplestore already ... but not a snapshot.
344 Assert(portal
->holdContext
!= NULL
);
345 Assert(portal
->holdStore
!= NULL
);
346 Assert(portal
->holdSnapshot
== NULL
);
349 * Before closing down the executor, we must copy the tupdesc into
350 * long-term memory, since it was created in executor memory.
352 oldcxt
= MemoryContextSwitchTo(portal
->holdContext
);
354 portal
->tupDesc
= CreateTupleDescCopy(portal
->tupDesc
);
356 MemoryContextSwitchTo(oldcxt
);
359 * Check for improper portal use, and mark portal active.
361 MarkPortalActive(portal
);
364 * Set up global portal context pointers.
366 saveActivePortal
= ActivePortal
;
367 saveResourceOwner
= CurrentResourceOwner
;
368 savePortalContext
= PortalContext
;
371 ScanDirection direction
= ForwardScanDirection
;
373 ActivePortal
= portal
;
374 if (portal
->resowner
)
375 CurrentResourceOwner
= portal
->resowner
;
376 PortalContext
= portal
->portalContext
;
378 MemoryContextSwitchTo(PortalContext
);
380 PushActiveSnapshot(queryDesc
->snapshot
);
383 * If the portal is marked scrollable, we need to store the entire
384 * result set in the tuplestore, so that subsequent backward FETCHs
385 * can be processed. Otherwise, store only the not-yet-fetched rows.
386 * (The latter is not only more efficient, but avoids semantic
387 * problems if the query's output isn't stable.)
389 * In the no-scroll case, tuple indexes in the tuplestore will not
390 * match the cursor's nominal position (portalPos). Currently this
391 * causes no difficulty because we only navigate in the tuplestore by
392 * relative position, except for the tuplestore_skiptuples call below
393 * and the tuplestore_rescan call in DoPortalRewind, both of which are
394 * disabled for no-scroll cursors. But someday we might need to track
395 * the offset between the holdStore and the cursor's nominal position
398 if (portal
->cursorOptions
& CURSOR_OPT_SCROLL
)
400 ExecutorRewind(queryDesc
);
405 * If we already reached end-of-query, set the direction to
406 * NoMovement to avoid trying to fetch any tuples. (This check
407 * exists because not all plan node types are robust about being
408 * called again if they've already returned NULL once.) We'll
409 * still set up an empty tuplestore, though, to keep this from
410 * being a special case later.
413 direction
= NoMovementScanDirection
;
417 * Change the destination to output to the tuplestore. Note we tell
418 * the tuplestore receiver to detoast all data passed through it; this
419 * makes it safe to not keep a snapshot associated with the data.
421 queryDesc
->dest
= CreateDestReceiver(DestTuplestore
);
422 SetTuplestoreDestReceiverParams(queryDesc
->dest
,
429 /* Fetch the result set into the tuplestore */
430 ExecutorRun(queryDesc
, direction
, 0, false);
432 queryDesc
->dest
->rDestroy(queryDesc
->dest
);
433 queryDesc
->dest
= NULL
;
436 * Now shut down the inner executor.
438 portal
->queryDesc
= NULL
; /* prevent double shutdown */
439 ExecutorFinish(queryDesc
);
440 ExecutorEnd(queryDesc
);
441 FreeQueryDesc(queryDesc
);
444 * Set the position in the result set.
446 MemoryContextSwitchTo(portal
->holdContext
);
451 * Just force the tuplestore forward to its end. The size of the
452 * skip request here is arbitrary.
454 while (tuplestore_skiptuples(portal
->holdStore
, 1000000, true))
459 tuplestore_rescan(portal
->holdStore
);
462 * In the no-scroll case, the start of the tuplestore is exactly
463 * where we want to be, so no repositioning is wanted.
465 if (portal
->cursorOptions
& CURSOR_OPT_SCROLL
)
467 if (!tuplestore_skiptuples(portal
->holdStore
,
470 elog(ERROR
, "unexpected end of tuple stream");
476 /* Uncaught error while executing portal: mark it dead */
477 MarkPortalFailed(portal
);
479 /* Restore global vars and propagate error */
480 ActivePortal
= saveActivePortal
;
481 CurrentResourceOwner
= saveResourceOwner
;
482 PortalContext
= savePortalContext
;
488 MemoryContextSwitchTo(oldcxt
);
490 /* Mark portal not active */
491 portal
->status
= PORTAL_READY
;
493 ActivePortal
= saveActivePortal
;
494 CurrentResourceOwner
= saveResourceOwner
;
495 PortalContext
= savePortalContext
;
500 * We can now release any subsidiary memory of the portal's context; we'll
501 * never use it again. The executor already dropped its context, but this
502 * will clean up anything that glommed onto the portal's context via
505 MemoryContextDeleteChildren(portal
->portalContext
);