PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
portalcmds.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * portalcmds.c
4 * Utility commands affecting portals (that is, SQL cursor commands)
5 *
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).
10 *
11 *
12 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
13 * Portions Copyright (c) 1994, Regents of the University of California
14 *
15 *
16 * IDENTIFICATION
17 * src/backend/commands/portalcmds.c
18 *
19 *-------------------------------------------------------------------------
20 */
21
22#include "postgres.h"
23
24#include <limits.h>
25
26#include "access/xact.h"
27#include "commands/portalcmds.h"
28#include "executor/executor.h"
30#include "miscadmin.h"
31#include "nodes/queryjumble.h"
32#include "parser/analyze.h"
34#include "tcop/pquery.h"
35#include "tcop/tcopprot.h"
36#include "utils/memutils.h"
37#include "utils/snapmgr.h"
38
39
40/*
41 * PerformCursorOpen
42 * Execute SQL DECLARE CURSOR command.
43 */
44void
46 bool isTopLevel)
47{
48 Query *query = castNode(Query, cstmt->query);
49 JumbleState *jstate = NULL;
50 List *rewritten;
52 Portal portal;
53 MemoryContext oldContext;
54 char *queryString;
55
56 /*
57 * Disallow empty-string cursor name (conflicts with protocol-level
58 * unnamed portal).
59 */
60 if (!cstmt->portalname || cstmt->portalname[0] == '\0')
62 (errcode(ERRCODE_INVALID_CURSOR_NAME),
63 errmsg("invalid cursor name: must not be empty")));
64
65 /*
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).
69 */
70 if (!(cstmt->options & CURSOR_OPT_HOLD))
71 RequireTransactionBlock(isTopLevel, "DECLARE CURSOR");
74 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
75 errmsg("cannot create a cursor WITH HOLD within security-restricted operation")));
76
77 /* Query contained by DeclareCursor needs to be jumbled if requested */
78 if (IsQueryIdEnabled())
79 jstate = JumbleQuery(query);
80
82 (*post_parse_analyze_hook) (pstate, query, jstate);
83
84 /*
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
88 * plancache.c.
89 */
90 rewritten = QueryRewrite(query);
91
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");
95
96 query = linitial_node(Query, rewritten);
97
98 if (query->commandType != CMD_SELECT)
99 elog(ERROR, "non-SELECT statement in DECLARE CURSOR");
100
101 /* Plan the query, applying the specified options */
102 plan = pg_plan_query(query, pstate->p_sourcetext, cstmt->options, params);
103
104 /*
105 * Create a portal and copy the plan and query string into its memory.
106 */
107 portal = CreatePortal(cstmt->portalname, false, false);
108
109 oldContext = MemoryContextSwitchTo(portal->portalContext);
110
112
113 queryString = pstrdup(pstate->p_sourcetext);
114
115 PortalDefineQuery(portal,
116 NULL,
117 queryString,
118 CMDTAG_SELECT, /* cursor's query is always a SELECT */
120 NULL,
121 NULL);
122
123 /*----------
124 * Also copy the outer portal's parameter list into the inner portal's
125 * memory context. We want to pass down the parameter values in case we
126 * had a command like
127 * DECLARE c CURSOR FOR SELECT ... WHERE foo = $1
128 * This will have been parsed using the outer parameter set and the
129 * parameter value needs to be preserved for use when the cursor is
130 * executed.
131 *----------
132 */
133 params = copyParamList(params);
134
135 MemoryContextSwitchTo(oldContext);
136
137 /*
138 * Set up options for portal.
139 *
140 * If the user didn't specify a SCROLL type, allow or disallow scrolling
141 * based on whether it would require any additional runtime overhead to do
142 * so. Also, we disallow scrolling for FOR UPDATE cursors.
143 */
144 portal->cursorOptions = cstmt->options;
146 {
147 if (plan->rowMarks == NIL &&
150 else
152 }
153
154 /*
155 * Start execution, inserting parameters if any.
156 */
157 PortalStart(portal, params, 0, GetActiveSnapshot());
158
160
161 /*
162 * We're done; the query won't actually be run until PerformPortalFetch is
163 * called.
164 */
165}
166
167/*
168 * PerformPortalFetch
169 * Execute SQL FETCH or MOVE command.
170 *
171 * stmt: parsetree node for command
172 * dest: where to send results
173 * qc: where to store a command completion status data.
174 *
175 * qc may be NULL if caller doesn't want status data.
176 */
177void
180 QueryCompletion *qc)
181{
182 Portal portal;
183 uint64 nprocessed;
184
185 /*
186 * Disallow empty-string cursor name (conflicts with protocol-level
187 * unnamed portal).
188 */
189 if (!stmt->portalname || stmt->portalname[0] == '\0')
191 (errcode(ERRCODE_INVALID_CURSOR_NAME),
192 errmsg("invalid cursor name: must not be empty")));
193
194 /* get the portal from the portal name */
195 portal = GetPortalByName(stmt->portalname);
196 if (!PortalIsValid(portal))
197 {
199 (errcode(ERRCODE_UNDEFINED_CURSOR),
200 errmsg("cursor \"%s\" does not exist", stmt->portalname)));
201 return; /* keep compiler happy */
202 }
203
204 /* Adjust dest if needed. MOVE wants destination DestNone */
205 if (stmt->ismove)
207
208 /* Do it */
209 nprocessed = PortalRunFetch(portal,
210 stmt->direction,
211 stmt->howMany,
212 dest);
213
214 /* Return command status if wanted */
215 if (qc)
216 SetQueryCompletion(qc, stmt->ismove ? CMDTAG_MOVE : CMDTAG_FETCH,
217 nprocessed);
218}
219
220/*
221 * PerformPortalClose
222 * Close a cursor.
223 */
224void
226{
227 Portal portal;
228
229 /* NULL means CLOSE ALL */
230 if (name == NULL)
231 {
233 return;
234 }
235
236 /*
237 * Disallow empty-string cursor name (conflicts with protocol-level
238 * unnamed portal).
239 */
240 if (name[0] == '\0')
242 (errcode(ERRCODE_INVALID_CURSOR_NAME),
243 errmsg("invalid cursor name: must not be empty")));
244
245 /*
246 * get the portal from the portal name
247 */
248 portal = GetPortalByName(name);
249 if (!PortalIsValid(portal))
250 {
252 (errcode(ERRCODE_UNDEFINED_CURSOR),
253 errmsg("cursor \"%s\" does not exist", name)));
254 return; /* keep compiler happy */
255 }
256
257 /*
258 * Note: PortalCleanup is called as a side-effect, if not already done.
259 */
260 PortalDrop(portal, false);
261}
262
263/*
264 * PortalCleanup
265 *
266 * Clean up a portal when it's dropped. This is the standard cleanup hook
267 * for portals.
268 *
269 * Note: if portal->status is PORTAL_FAILED, we are probably being called
270 * during error abort, and must be careful to avoid doing anything that
271 * is likely to fail again.
272 */
273void
275{
276 QueryDesc *queryDesc;
277
278 /*
279 * sanity checks
280 */
281 Assert(PortalIsValid(portal));
282 Assert(portal->cleanup == PortalCleanup);
283
284 /*
285 * Shut down executor, if still running. We skip this during error abort,
286 * since other mechanisms will take care of releasing executor resources,
287 * and we can't be sure that ExecutorEnd itself wouldn't fail.
288 */
289 queryDesc = portal->queryDesc;
290 if (queryDesc)
291 {
292 /*
293 * Reset the queryDesc before anything else. This prevents us from
294 * trying to shut down the executor twice, in case of an error below.
295 * The transaction abort mechanisms will take care of resource cleanup
296 * in such a case.
297 */
298 portal->queryDesc = NULL;
299
300 if (portal->status != PORTAL_FAILED)
301 {
302 ResourceOwner saveResourceOwner;
303
304 /* We must make the portal's resource owner current */
305 saveResourceOwner = CurrentResourceOwner;
306 if (portal->resowner)
308
309 ExecutorFinish(queryDesc);
310 ExecutorEnd(queryDesc);
311 FreeQueryDesc(queryDesc);
312
313 CurrentResourceOwner = saveResourceOwner;
314 }
315 }
316}
317
318/*
319 * PersistHoldablePortal
320 *
321 * Prepare the specified Portal for access outside of the current
322 * transaction. When this function returns, all future accesses to the
323 * portal must be done via the Tuplestore (not by invoking the
324 * executor).
325 */
326void
328{
329 QueryDesc *queryDesc = portal->queryDesc;
330 Portal saveActivePortal;
331 ResourceOwner saveResourceOwner;
332 MemoryContext savePortalContext;
333 MemoryContext oldcxt;
334
335 /*
336 * If we're preserving a holdable portal, we had better be inside the
337 * transaction that originally created it.
338 */
340 Assert(queryDesc != NULL);
341
342 /*
343 * Caller must have created the tuplestore already ... but not a snapshot.
344 */
345 Assert(portal->holdContext != NULL);
346 Assert(portal->holdStore != NULL);
347 Assert(portal->holdSnapshot == NULL);
348
349 /*
350 * Before closing down the executor, we must copy the tupdesc into
351 * long-term memory, since it was created in executor memory.
352 */
353 oldcxt = MemoryContextSwitchTo(portal->holdContext);
354
355 portal->tupDesc = CreateTupleDescCopy(portal->tupDesc);
356
357 MemoryContextSwitchTo(oldcxt);
358
359 /*
360 * Check for improper portal use, and mark portal active.
361 */
362 MarkPortalActive(portal);
363
364 /*
365 * Set up global portal context pointers.
366 */
367 saveActivePortal = ActivePortal;
368 saveResourceOwner = CurrentResourceOwner;
369 savePortalContext = PortalContext;
370 PG_TRY();
371 {
373
374 ActivePortal = portal;
375 if (portal->resowner)
378
380
381 PushActiveSnapshot(queryDesc->snapshot);
382
383 /*
384 * If the portal is marked scrollable, we need to store the entire
385 * result set in the tuplestore, so that subsequent backward FETCHs
386 * can be processed. Otherwise, store only the not-yet-fetched rows.
387 * (The latter is not only more efficient, but avoids semantic
388 * problems if the query's output isn't stable.)
389 *
390 * In the no-scroll case, tuple indexes in the tuplestore will not
391 * match the cursor's nominal position (portalPos). Currently this
392 * causes no difficulty because we only navigate in the tuplestore by
393 * relative position, except for the tuplestore_skiptuples call below
394 * and the tuplestore_rescan call in DoPortalRewind, both of which are
395 * disabled for no-scroll cursors. But someday we might need to track
396 * the offset between the holdStore and the cursor's nominal position
397 * explicitly.
398 */
399 if (portal->cursorOptions & CURSOR_OPT_SCROLL)
400 {
401 ExecutorRewind(queryDesc);
402 }
403 else
404 {
405 /*
406 * If we already reached end-of-query, set the direction to
407 * NoMovement to avoid trying to fetch any tuples. (This check
408 * exists because not all plan node types are robust about being
409 * called again if they've already returned NULL once.) We'll
410 * still set up an empty tuplestore, though, to keep this from
411 * being a special case later.
412 */
413 if (portal->atEnd)
414 direction = NoMovementScanDirection;
415 }
416
417 /*
418 * Change the destination to output to the tuplestore. Note we tell
419 * the tuplestore receiver to detoast all data passed through it; this
420 * makes it safe to not keep a snapshot associated with the data.
421 */
424 portal->holdStore,
425 portal->holdContext,
426 true,
427 NULL,
428 NULL);
429
430 /* Fetch the result set into the tuplestore */
431 ExecutorRun(queryDesc, direction, 0);
432
433 queryDesc->dest->rDestroy(queryDesc->dest);
434 queryDesc->dest = NULL;
435
436 /*
437 * Now shut down the inner executor.
438 */
439 portal->queryDesc = NULL; /* prevent double shutdown */
440 ExecutorFinish(queryDesc);
441 ExecutorEnd(queryDesc);
442 FreeQueryDesc(queryDesc);
443
444 /*
445 * Set the position in the result set.
446 */
448
449 if (portal->atEnd)
450 {
451 /*
452 * Just force the tuplestore forward to its end. The size of the
453 * skip request here is arbitrary.
454 */
455 while (tuplestore_skiptuples(portal->holdStore, 1000000, true))
456 /* continue */ ;
457 }
458 else
459 {
461
462 /*
463 * In the no-scroll case, the start of the tuplestore is exactly
464 * where we want to be, so no repositioning is wanted.
465 */
466 if (portal->cursorOptions & CURSOR_OPT_SCROLL)
467 {
468 if (!tuplestore_skiptuples(portal->holdStore,
469 portal->portalPos,
470 true))
471 elog(ERROR, "unexpected end of tuple stream");
472 }
473 }
474 }
475 PG_CATCH();
476 {
477 /* Uncaught error while executing portal: mark it dead */
478 MarkPortalFailed(portal);
479
480 /* Restore global vars and propagate error */
481 ActivePortal = saveActivePortal;
482 CurrentResourceOwner = saveResourceOwner;
483 PortalContext = savePortalContext;
484
485 PG_RE_THROW();
486 }
487 PG_END_TRY();
488
489 MemoryContextSwitchTo(oldcxt);
490
491 /* Mark portal not active */
492 portal->status = PORTAL_READY;
493
494 ActivePortal = saveActivePortal;
495 CurrentResourceOwner = saveResourceOwner;
496 PortalContext = savePortalContext;
497
499
500 /*
501 * We can now release any subsidiary memory of the portal's context; we'll
502 * never use it again. The executor already dropped its context, but this
503 * will clean up anything that glommed onto the portal's context via
504 * PortalContext.
505 */
507}
#define InvalidSubTransactionId
Definition: c.h:629
uint64_t uint64
Definition: c.h:503
static void SetQueryCompletion(QueryCompletion *qc, CommandTag commandTag, uint64 nprocessed)
Definition: cmdtag.h:37
DestReceiver * CreateDestReceiver(CommandDest dest)
Definition: dest.c:113
DestReceiver * None_Receiver
Definition: dest.c:96
@ DestTuplestore
Definition: dest.h:93
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define PG_RE_THROW()
Definition: elog.h:405
#define PG_TRY(...)
Definition: elog.h:372
#define PG_END_TRY(...)
Definition: elog.h:397
#define ERROR
Definition: elog.h:39
#define PG_CATCH(...)
Definition: elog.h:382
#define elog(elevel,...)
Definition: elog.h:226
#define ereport(elevel,...)
Definition: elog.h:149
bool ExecSupportsBackwardScan(Plan *node)
Definition: execAmi.c:511
void ExecutorEnd(QueryDesc *queryDesc)
Definition: execMain.c:538
void ExecutorFinish(QueryDesc *queryDesc)
Definition: execMain.c:475
void ExecutorRewind(QueryDesc *queryDesc)
Definition: execMain.c:615
void ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count)
Definition: execMain.c:365
Assert(PointerIsAligned(start, uint64))
#define stmt
Definition: indent_codes.h:59
char * pstrdup(const char *in)
Definition: mcxt.c:2325
void MemoryContextDeleteChildren(MemoryContext context)
Definition: mcxt.c:570
MemoryContext PortalContext
Definition: mcxt.c:174
bool InSecurityRestrictedOperation(void)
Definition: miscinit.c:690
#define copyObject(obj)
Definition: nodes.h:230
@ CMD_SELECT
Definition: nodes.h:271
#define castNode(_type_, nodeptr)
Definition: nodes.h:182
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
ParamListInfo copyParamList(ParamListInfo from)
Definition: params.c:78
#define CURSOR_OPT_SCROLL
Definition: parsenodes.h:3376
#define CURSOR_OPT_HOLD
Definition: parsenodes.h:3380
#define CURSOR_OPT_NO_SCROLL
Definition: parsenodes.h:3377
post_parse_analyze_hook_type post_parse_analyze_hook
Definition: analyze.c:59
static int list_length(const List *l)
Definition: pg_list.h:152
#define linitial_node(type, l)
Definition: pg_list.h:181
#define NIL
Definition: pg_list.h:68
#define list_make1(x1)
Definition: pg_list.h:212
#define plan(x)
Definition: pg_regress.c:161
@ PORTAL_FAILED
Definition: portal.h:110
@ PORTAL_READY
Definition: portal.h:107
@ PORTAL_ONE_SELECT
Definition: portal.h:91
#define PortalIsValid(p)
Definition: portal.h:212
void PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo params, bool isTopLevel)
Definition: portalcmds.c:45
void PerformPortalClose(const char *name)
Definition: portalcmds.c:225
void PortalCleanup(Portal portal)
Definition: portalcmds.c:274
void PerformPortalFetch(FetchStmt *stmt, DestReceiver *dest, QueryCompletion *qc)
Definition: portalcmds.c:178
void PersistHoldablePortal(Portal portal)
Definition: portalcmds.c:327
void PortalDefineQuery(Portal portal, const char *prepStmtName, const char *sourceText, CommandTag commandTag, List *stmts, CachedPlan *cplan, CachedPlanSource *plansource)
Definition: portalmem.c:282
void MarkPortalFailed(Portal portal)
Definition: portalmem.c:444
void MarkPortalActive(Portal portal)
Definition: portalmem.c:397
void PortalDrop(Portal portal, bool isTopCommit)
Definition: portalmem.c:470
Portal GetPortalByName(const char *name)
Definition: portalmem.c:130
void PortalHashTableDeleteAll(void)
Definition: portalmem.c:609
Portal CreatePortal(const char *name, bool allowDup, bool dupSilent)
Definition: portalmem.c:175
PlannedStmt * pg_plan_query(Query *querytree, const char *query_string, int cursorOptions, ParamListInfo boundParams)
Definition: postgres.c:882
void FreeQueryDesc(QueryDesc *qdesc)
Definition: pquery.c:112
Portal ActivePortal
Definition: pquery.c:37
void PortalStart(Portal portal, ParamListInfo params, int eflags, Snapshot snapshot)
Definition: pquery.c:455
uint64 PortalRunFetch(Portal portal, FetchDirection fdirection, long count, DestReceiver *dest)
Definition: pquery.c:1427
static bool IsQueryIdEnabled(void)
Definition: queryjumble.h:95
JumbleState * JumbleQuery(Query *query)
ResourceOwner CurrentResourceOwner
Definition: resowner.c:173
List * QueryRewrite(Query *parsetree)
ScanDirection
Definition: sdir.h:25
@ NoMovementScanDirection
Definition: sdir.h:27
@ ForwardScanDirection
Definition: sdir.h:28
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:669
void PopActiveSnapshot(void)
Definition: snapmgr.c:762
Snapshot GetActiveSnapshot(void)
Definition: snapmgr.c:787
Definition: pg_list.h:54
const char * p_sourcetext
Definition: parse_node.h:209
SubTransactionId createSubid
Definition: portal.h:131
uint64 portalPos
Definition: portal.h:201
QueryDesc * queryDesc
Definition: portal.h:157
bool atEnd
Definition: portal.h:200
ResourceOwner resowner
Definition: portal.h:121
MemoryContext holdContext
Definition: portal.h:178
MemoryContext portalContext
Definition: portal.h:120
Snapshot holdSnapshot
Definition: portal.h:188
TupleDesc tupDesc
Definition: portal.h:160
Tuplestorestate * holdStore
Definition: portal.h:177
int cursorOptions
Definition: portal.h:148
void(* cleanup)(Portal portal)
Definition: portal.h:122
PortalStrategy strategy
Definition: portal.h:147
PortalStatus status
Definition: portal.h:151
DestReceiver * dest
Definition: execdesc.h:42
Snapshot snapshot
Definition: execdesc.h:40
CmdType commandType
Definition: parsenodes.h:121
void(* rDestroy)(DestReceiver *self)
Definition: dest.h:126
void SetTuplestoreDestReceiverParams(DestReceiver *self, Tuplestorestate *tStore, MemoryContext tContext, bool detoast, TupleDesc target_tupdesc, const char *map_failure_msg)
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:245
void tuplestore_rescan(Tuplestorestate *state)
Definition: tuplestore.c:1285
bool tuplestore_skiptuples(Tuplestorestate *state, int64 ntuples, bool forward)
Definition: tuplestore.c:1187
const char * name
void RequireTransactionBlock(bool isTopLevel, const char *stmtType)
Definition: xact.c:3716