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