Age Owner TLA Line data Source code
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-2023, 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"
29 : #include "executor/tstoreReceiver.h"
30 : #include "miscadmin.h"
31 : #include "rewrite/rewriteHandler.h"
32 : #include "tcop/pquery.h"
33 : #include "tcop/tcopprot.h"
34 : #include "utils/memutils.h"
35 : #include "utils/snapmgr.h"
36 :
37 :
38 : /*
39 : * PerformCursorOpen
40 : * Execute SQL DECLARE CURSOR command.
41 : */
42 : void
1191 peter 43 CBC 1331 : PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo params,
44 : bool isTopLevel)
45 : {
2264 tgl 46 1331 : Query *query = castNode(Query, cstmt->query);
47 : List *rewritten;
48 : PlannedStmt *plan;
49 : Portal portal;
50 : MemoryContext oldContext;
51 : char *queryString;
52 :
53 : /*
54 : * Disallow empty-string cursor name (conflicts with protocol-level
55 : * unnamed portal).
56 : */
5826 57 1331 : if (!cstmt->portalname || cstmt->portalname[0] == '\0')
7203 tgl 58 UBC 0 : ereport(ERROR,
59 : (errcode(ERRCODE_INVALID_CURSOR_NAME),
60 : errmsg("invalid cursor name: must not be empty")));
61 :
62 : /*
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).
66 : */
5826 tgl 67 CBC 1331 : if (!(cstmt->options & CURSOR_OPT_HOLD))
1878 peter_e 68 1294 : RequireTransactionBlock(isTopLevel, "DECLARE CURSOR");
881 noah 69 37 : else if (InSecurityRestrictedOperation())
70 6 : ereport(ERROR,
71 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
72 : errmsg("cannot create a cursor WITH HOLD within security-restricted operation")));
73 :
74 : /*
75 : * Parse analysis was done already, but we still have to run the rule
76 : * rewriter. We do not do AcquireRewriteLocks: we assume the query either
77 : * came straight from the parser, or suitable locks were acquired by
78 : * plancache.c.
79 : */
660 tgl 80 1324 : rewritten = QueryRewrite(query);
81 :
82 : /* SELECT should never rewrite to more or less than one query */
2276 83 1324 : if (list_length(rewritten) != 1)
2276 tgl 84 UBC 0 : elog(ERROR, "non-SELECT statement in DECLARE CURSOR");
85 :
2190 tgl 86 CBC 1324 : query = linitial_node(Query, rewritten);
87 :
2276 88 1324 : if (query->commandType != CMD_SELECT)
2276 tgl 89 UBC 0 : elog(ERROR, "non-SELECT statement in DECLARE CURSOR");
90 :
91 : /* Plan the query, applying the specified options */
1105 fujii 92 CBC 1324 : plan = pg_plan_query(query, pstate->p_sourcetext, cstmt->options, params);
93 :
94 : /*
95 : * Create a portal and copy the plan and query string into its memory.
96 : */
5826 tgl 97 1324 : portal = CreatePortal(cstmt->portalname, false, false);
98 :
1940 peter_e 99 1324 : oldContext = MemoryContextSwitchTo(portal->portalContext);
100 :
2276 tgl 101 1324 : plan = copyObject(plan);
102 :
1191 peter 103 1324 : queryString = pstrdup(pstate->p_sourcetext);
104 :
7282 tgl 105 1324 : PortalDefineQuery(portal,
106 : NULL,
107 : queryString,
108 : CMDTAG_SELECT, /* cursor's query is always a SELECT */
2276 109 1324 : list_make1(plan),
110 : NULL);
111 :
112 : /*----------
113 : * Also copy the outer portal's parameter list into the inner portal's
114 : * memory context. We want to pass down the parameter values in case we
115 : * had a command like
116 : * DECLARE c CURSOR FOR SELECT ... WHERE foo = $1
117 : * This will have been parsed using the outer parameter set and the
118 : * parameter value needs to be preserved for use when the cursor is
119 : * executed.
120 : *----------
121 : */
6824 122 1324 : params = copyParamList(params);
123 :
7282 124 1324 : MemoryContextSwitchTo(oldContext);
125 :
126 : /*
127 : * Set up options for portal.
128 : *
129 : * If the user didn't specify a SCROLL type, allow or disallow scrolling
130 : * based on whether it would require any additional runtime overhead to do
131 : * so. Also, we disallow scrolling for FOR UPDATE cursors.
132 : */
5826 133 1324 : portal->cursorOptions = cstmt->options;
7282 134 1324 : if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
135 : {
2276 136 2265 : if (plan->rowMarks == NIL &&
137 1097 : ExecSupportsBackwardScan(plan->planTree))
7282 138 730 : portal->cursorOptions |= CURSOR_OPT_SCROLL;
139 : else
140 438 : portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
141 : }
142 :
143 : /*
144 : * Start execution, inserting parameters if any.
145 : */
3786 146 1324 : PortalStart(portal, params, 0, GetActiveSnapshot());
147 :
7282 148 1324 : Assert(portal->strategy == PORTAL_ONE_SELECT);
149 :
150 : /*
151 : * We're done; the query won't actually be run until PerformPortalFetch is
152 : * called.
153 : */
7335 154 1324 : }
155 :
156 : /*
157 : * PerformPortalFetch
158 : * Execute SQL FETCH or MOVE command.
159 : *
160 : * stmt: parsetree node for command
161 : * dest: where to send results
162 : * qc: where to store a command completion status data.
163 : *
164 : * qc may be NULL if caller doesn't want status data.
165 : */
166 : void
7334 167 2839 : PerformPortalFetch(FetchStmt *stmt,
168 : DestReceiver *dest,
169 : QueryCompletion *qc)
170 : {
171 : Portal portal;
172 : uint64 nprocessed;
173 :
174 : /*
175 : * Disallow empty-string cursor name (conflicts with protocol-level
176 : * unnamed portal).
177 : */
7279 178 2839 : if (!stmt->portalname || stmt->portalname[0] == '\0')
7203 tgl 179 UBC 0 : ereport(ERROR,
180 : (errcode(ERRCODE_INVALID_CURSOR_NAME),
181 : errmsg("invalid cursor name: must not be empty")));
182 :
183 : /* get the portal from the portal name */
7334 tgl 184 CBC 2839 : portal = GetPortalByName(stmt->portalname);
7664 185 2839 : if (!PortalIsValid(portal))
186 : {
7168 peter_e 187 17 : ereport(ERROR,
188 : (errcode(ERRCODE_UNDEFINED_CURSOR),
189 : errmsg("cursor \"%s\" does not exist", stmt->portalname)));
190 : return; /* keep compiler happy */
191 : }
192 :
193 : /* Adjust dest if needed. MOVE wants destination DestNone */
7282 tgl 194 2822 : if (stmt->ismove)
7276 195 47 : dest = None_Receiver;
196 :
197 : /* Do it */
7282 198 2822 : nprocessed = PortalRunFetch(portal,
199 : stmt->direction,
200 : stmt->howMany,
201 : dest);
202 :
203 : /* Return command status if wanted */
1133 alvherre 204 2790 : if (qc)
205 2790 : SetQueryCompletion(qc, stmt->ismove ? CMDTAG_MOVE : CMDTAG_FETCH,
206 : nprocessed);
207 : }
208 :
209 : /*
210 : * PerformPortalClose
211 : * Close a cursor.
212 : */
213 : void
7279 tgl 214 1047 : PerformPortalClose(const char *name)
215 : {
216 : Portal portal;
217 :
218 : /* NULL means CLOSE ALL */
5841 neilc 219 1047 : if (name == NULL)
220 : {
221 6 : PortalHashTableDeleteAll();
222 6 : return;
223 : }
224 :
225 : /*
226 : * Disallow empty-string cursor name (conflicts with protocol-level
227 : * unnamed portal).
228 : */
229 1041 : if (name[0] == '\0')
7203 tgl 230 UBC 0 : ereport(ERROR,
231 : (errcode(ERRCODE_INVALID_CURSOR_NAME),
232 : errmsg("invalid cursor name: must not be empty")));
233 :
234 : /*
235 : * get the portal from the portal name
236 : */
7664 tgl 237 CBC 1041 : portal = GetPortalByName(name);
238 1041 : if (!PortalIsValid(portal))
239 : {
7168 peter_e 240 1 : ereport(ERROR,
241 : (errcode(ERRCODE_UNDEFINED_CURSOR),
242 : errmsg("cursor \"%s\" does not exist", name)));
243 : return; /* keep compiler happy */
244 : }
245 :
246 : /*
247 : * Note: PortalCleanup is called as a side-effect, if not already done.
248 : */
7318 bruce 249 1040 : PortalDrop(portal, false);
250 : }
251 :
252 : /*
253 : * PortalCleanup
254 : *
255 : * Clean up a portal when it's dropped. This is the standard cleanup hook
256 : * for portals.
257 : *
258 : * Note: if portal->status is PORTAL_FAILED, we are probably being called
259 : * during error abort, and must be careful to avoid doing anything that
260 : * is likely to fail again.
261 : */
262 : void
6840 tgl 263 498848 : PortalCleanup(Portal portal)
264 : {
265 : QueryDesc *queryDesc;
266 :
267 : /*
268 : * sanity checks
269 : */
163 peter 270 GNC 498848 : Assert(PortalIsValid(portal));
271 498848 : Assert(portal->cleanup == PortalCleanup);
272 :
273 : /*
274 : * Shut down executor, if still running. We skip this during error abort,
275 : * since other mechanisms will take care of releasing executor resources,
276 : * and we can't be sure that ExecutorEnd itself wouldn't fail.
277 : */
1940 peter_e 278 CBC 498848 : queryDesc = portal->queryDesc;
7282 tgl 279 498848 : if (queryDesc)
280 : {
281 : /*
282 : * Reset the queryDesc before anything else. This prevents us from
283 : * trying to shut down the executor twice, in case of an error below.
284 : * The transaction abort mechanisms will take care of resource cleanup
285 : * in such a case.
286 : */
287 108716 : portal->queryDesc = NULL;
288 :
6840 289 108716 : if (portal->status != PORTAL_FAILED)
290 : {
291 : ResourceOwner saveResourceOwner;
292 :
293 : /* We must make the portal's resource owner current */
294 106062 : saveResourceOwner = CurrentResourceOwner;
2006 295 106062 : if (portal->resowner)
296 106062 : CurrentResourceOwner = portal->resowner;
297 :
298 106062 : ExecutorFinish(queryDesc);
299 106062 : ExecutorEnd(queryDesc);
300 106062 : FreeQueryDesc(queryDesc);
301 :
6840 302 106062 : CurrentResourceOwner = saveResourceOwner;
303 : }
304 : }
7318 bruce 305 498848 : }
306 :
307 : /*
308 : * PersistHoldablePortal
309 : *
310 : * Prepare the specified Portal for access outside of the current
311 : * transaction. When this function returns, all future accesses to the
312 : * portal must be done via the Tuplestore (not by invoking the
313 : * executor).
314 : */
315 : void
316 41 : PersistHoldablePortal(Portal portal)
317 : {
1940 peter_e 318 41 : QueryDesc *queryDesc = portal->queryDesc;
319 : Portal saveActivePortal;
320 : ResourceOwner saveResourceOwner;
321 : MemoryContext savePortalContext;
322 : MemoryContext oldcxt;
323 :
324 : /*
325 : * If we're preserving a holdable portal, we had better be inside the
326 : * transaction that originally created it.
327 : */
6779 tgl 328 41 : Assert(portal->createSubid != InvalidSubTransactionId);
7282 329 41 : Assert(queryDesc != NULL);
330 :
331 : /*
332 : * Caller must have created the tuplestore already ... but not a snapshot.
333 : */
7285 334 41 : Assert(portal->holdContext != NULL);
7278 335 41 : Assert(portal->holdStore != NULL);
2436 336 41 : Assert(portal->holdSnapshot == NULL);
337 :
338 : /*
339 : * Before closing down the executor, we must copy the tupdesc into
340 : * long-term memory, since it was created in executor memory.
341 : */
7278 342 41 : oldcxt = MemoryContextSwitchTo(portal->holdContext);
343 :
7282 344 41 : portal->tupDesc = CreateTupleDescCopy(portal->tupDesc);
345 :
346 41 : MemoryContextSwitchTo(oldcxt);
347 :
348 : /*
349 : * Check for improper portal use, and mark portal active.
350 : */
2774 351 41 : MarkPortalActive(portal);
352 :
353 : /*
354 : * Set up global portal context pointers.
355 : */
6958 356 41 : saveActivePortal = ActivePortal;
6840 357 41 : saveResourceOwner = CurrentResourceOwner;
7282 358 41 : savePortalContext = PortalContext;
6826 359 41 : PG_TRY();
360 : {
577 361 41 : ScanDirection direction = ForwardScanDirection;
362 :
6826 363 41 : ActivePortal = portal;
3587 364 41 : if (portal->resowner)
365 41 : CurrentResourceOwner = portal->resowner;
1940 peter_e 366 41 : PortalContext = portal->portalContext;
367 :
6826 tgl 368 41 : MemoryContextSwitchTo(PortalContext);
369 :
5445 alvherre 370 41 : PushActiveSnapshot(queryDesc->snapshot);
371 :
372 : /*
373 : * If the portal is marked scrollable, we need to store the entire
374 : * result set in the tuplestore, so that subsequent backward FETCHs
375 : * can be processed. Otherwise, store only the not-yet-fetched rows.
376 : * (The latter is not only more efficient, but avoids semantic
377 : * problems if the query's output isn't stable.)
378 : *
379 : * In the no-scroll case, tuple indexes in the tuplestore will not
380 : * match the cursor's nominal position (portalPos). Currently this
381 : * causes no difficulty because we only navigate in the tuplestore by
382 : * relative position, except for the tuplestore_skiptuples call below
383 : * and the tuplestore_rescan call in DoPortalRewind, both of which are
384 : * disabled for no-scroll cursors. But someday we might need to track
385 : * the offset between the holdStore and the cursor's nominal position
386 : * explicitly.
387 : */
670 tgl 388 41 : if (portal->cursorOptions & CURSOR_OPT_SCROLL)
389 : {
390 20 : ExecutorRewind(queryDesc);
391 : }
392 : else
393 : {
394 : /*
395 : * If we already reached end-of-query, set the direction to
396 : * NoMovement to avoid trying to fetch any tuples. (This check
397 : * exists because not all plan node types are robust about being
398 : * called again if they've already returned NULL once.) We'll
399 : * still set up an empty tuplestore, though, to keep this from
400 : * being a special case later.
401 : */
577 402 21 : if (portal->atEnd)
577 tgl 403 UBC 0 : direction = NoMovementScanDirection;
404 : }
405 :
406 : /*
407 : * Change the destination to output to the tuplestore. Note we tell
408 : * the tuplestore receiver to detoast all data passed through it; this
409 : * makes it safe to not keep a snapshot associated with the data.
410 : */
5243 tgl 411 CBC 41 : queryDesc->dest = CreateDestReceiver(DestTuplestore);
412 41 : SetTuplestoreDestReceiverParams(queryDesc->dest,
413 : portal->holdStore,
414 : portal->holdContext,
415 : true,
416 : NULL,
417 : NULL);
418 :
419 : /* Fetch the result set into the tuplestore */
11 peter 420 GNC 41 : ExecutorRun(queryDesc, direction, 0, false);
421 :
2040 peter_e 422 CBC 39 : queryDesc->dest->rDestroy(queryDesc->dest);
6826 tgl 423 39 : queryDesc->dest = NULL;
424 :
425 : /*
426 : * Now shut down the inner executor.
427 : */
2118 428 39 : portal->queryDesc = NULL; /* prevent double shutdown */
4424 429 39 : ExecutorFinish(queryDesc);
6589 430 39 : ExecutorEnd(queryDesc);
5498 alvherre 431 39 : FreeQueryDesc(queryDesc);
432 :
433 : /*
434 : * Set the position in the result set.
435 : */
6826 tgl 436 39 : MemoryContextSwitchTo(portal->holdContext);
437 :
5906 438 39 : if (portal->atEnd)
439 : {
440 : /*
441 : * Just force the tuplestore forward to its end. The size of the
442 : * skip request here is arbitrary.
443 : */
3283 tgl 444 UBC 0 : while (tuplestore_skiptuples(portal->holdStore, 1000000, true))
445 : /* continue */ ;
446 : }
447 : else
448 : {
6826 tgl 449 CBC 39 : tuplestore_rescan(portal->holdStore);
450 :
451 : /*
452 : * In the no-scroll case, the start of the tuplestore is exactly
453 : * where we want to be, so no repositioning is wanted.
454 : */
576 455 39 : if (portal->cursorOptions & CURSOR_OPT_SCROLL)
456 : {
457 20 : if (!tuplestore_skiptuples(portal->holdStore,
458 20 : portal->portalPos,
459 : true))
576 tgl 460 UBC 0 : elog(ERROR, "unexpected end of tuple stream");
461 : }
462 : }
463 : }
6826 tgl 464 CBC 2 : PG_CATCH();
465 : {
466 : /* Uncaught error while executing portal: mark it dead */
4071 467 2 : MarkPortalFailed(portal);
468 :
469 : /* Restore global vars and propagate error */
6826 470 2 : ActivePortal = saveActivePortal;
471 2 : CurrentResourceOwner = saveResourceOwner;
472 2 : PortalContext = savePortalContext;
473 :
474 2 : PG_RE_THROW();
475 : }
476 39 : PG_END_TRY();
477 :
7285 478 39 : MemoryContextSwitchTo(oldcxt);
479 :
480 : /* Mark portal not active */
6840 481 39 : portal->status = PORTAL_READY;
482 :
483 39 : ActivePortal = saveActivePortal;
484 39 : CurrentResourceOwner = saveResourceOwner;
485 39 : PortalContext = savePortalContext;
486 :
5445 alvherre 487 39 : PopActiveSnapshot();
488 :
489 : /*
490 : * We can now release any subsidiary memory of the portal's context; we'll
491 : * never use it again. The executor already dropped its context, but this
492 : * will clean up anything that glommed onto the portal's context via
493 : * PortalContext.
494 : */
1940 peter_e 495 39 : MemoryContextDeleteChildren(portal->portalContext);
7335 tgl 496 39 : }
|