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-2019, 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 "rewrite/rewriteHandler.h" |
31 | #include "tcop/pquery.h" |
32 | #include "tcop/tcopprot.h" |
33 | #include "utils/memutils.h" |
34 | #include "utils/snapmgr.h" |
35 | |
36 | |
37 | /* |
38 | * PerformCursorOpen |
39 | * Execute SQL DECLARE CURSOR command. |
40 | */ |
41 | void |
42 | PerformCursorOpen(DeclareCursorStmt *cstmt, ParamListInfo params, |
43 | const char *queryString, bool isTopLevel) |
44 | { |
45 | Query *query = castNode(Query, cstmt->query); |
46 | List *rewritten; |
47 | PlannedStmt *plan; |
48 | Portal portal; |
49 | MemoryContext oldContext; |
50 | |
51 | /* |
52 | * Disallow empty-string cursor name (conflicts with protocol-level |
53 | * unnamed portal). |
54 | */ |
55 | if (!cstmt->portalname || cstmt->portalname[0] == '\0') |
56 | ereport(ERROR, |
57 | (errcode(ERRCODE_INVALID_CURSOR_NAME), |
58 | errmsg("invalid cursor name: must not be empty" ))); |
59 | |
60 | /* |
61 | * If this is a non-holdable cursor, we require that this statement has |
62 | * been executed inside a transaction block (or else, it would have no |
63 | * user-visible effect). |
64 | */ |
65 | if (!(cstmt->options & CURSOR_OPT_HOLD)) |
66 | RequireTransactionBlock(isTopLevel, "DECLARE CURSOR" ); |
67 | |
68 | /* |
69 | * Parse analysis was done already, but we still have to run the rule |
70 | * rewriter. We do not do AcquireRewriteLocks: we assume the query either |
71 | * came straight from the parser, or suitable locks were acquired by |
72 | * plancache.c. |
73 | * |
74 | * Because the rewriter and planner tend to scribble on the input, we make |
75 | * a preliminary copy of the source querytree. This prevents problems in |
76 | * the case that the DECLARE CURSOR is in a portal or plpgsql function and |
77 | * is executed repeatedly. (See also the same hack in EXPLAIN and |
78 | * PREPARE.) XXX FIXME someday. |
79 | */ |
80 | rewritten = QueryRewrite((Query *) copyObject(query)); |
81 | |
82 | /* SELECT should never rewrite to more or less than one query */ |
83 | if (list_length(rewritten) != 1) |
84 | elog(ERROR, "non-SELECT statement in DECLARE CURSOR" ); |
85 | |
86 | query = linitial_node(Query, rewritten); |
87 | |
88 | if (query->commandType != CMD_SELECT) |
89 | elog(ERROR, "non-SELECT statement in DECLARE CURSOR" ); |
90 | |
91 | /* Plan the query, applying the specified options */ |
92 | plan = pg_plan_query(query, cstmt->options, params); |
93 | |
94 | /* |
95 | * Create a portal and copy the plan and queryString into its memory. |
96 | */ |
97 | portal = CreatePortal(cstmt->portalname, false, false); |
98 | |
99 | oldContext = MemoryContextSwitchTo(portal->portalContext); |
100 | |
101 | plan = copyObject(plan); |
102 | |
103 | queryString = pstrdup(queryString); |
104 | |
105 | PortalDefineQuery(portal, |
106 | NULL, |
107 | queryString, |
108 | "SELECT" , /* cursor's query is always a SELECT */ |
109 | 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 | */ |
122 | params = copyParamList(params); |
123 | |
124 | 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 | */ |
133 | portal->cursorOptions = cstmt->options; |
134 | if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL))) |
135 | { |
136 | if (plan->rowMarks == NIL && |
137 | ExecSupportsBackwardScan(plan->planTree)) |
138 | portal->cursorOptions |= CURSOR_OPT_SCROLL; |
139 | else |
140 | portal->cursorOptions |= CURSOR_OPT_NO_SCROLL; |
141 | } |
142 | |
143 | /* |
144 | * Start execution, inserting parameters if any. |
145 | */ |
146 | PortalStart(portal, params, 0, GetActiveSnapshot()); |
147 | |
148 | 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 | */ |
154 | } |
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 | * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE |
163 | * in which to store a command completion status string. |
164 | * |
165 | * completionTag may be NULL if caller doesn't want a status string. |
166 | */ |
167 | void |
168 | PerformPortalFetch(FetchStmt *stmt, |
169 | DestReceiver *dest, |
170 | char *completionTag) |
171 | { |
172 | Portal portal; |
173 | uint64 nprocessed; |
174 | |
175 | /* |
176 | * Disallow empty-string cursor name (conflicts with protocol-level |
177 | * unnamed portal). |
178 | */ |
179 | if (!stmt->portalname || stmt->portalname[0] == '\0') |
180 | ereport(ERROR, |
181 | (errcode(ERRCODE_INVALID_CURSOR_NAME), |
182 | errmsg("invalid cursor name: must not be empty" ))); |
183 | |
184 | /* get the portal from the portal name */ |
185 | portal = GetPortalByName(stmt->portalname); |
186 | if (!PortalIsValid(portal)) |
187 | { |
188 | ereport(ERROR, |
189 | (errcode(ERRCODE_UNDEFINED_CURSOR), |
190 | errmsg("cursor \"%s\" does not exist" , stmt->portalname))); |
191 | return; /* keep compiler happy */ |
192 | } |
193 | |
194 | /* Adjust dest if needed. MOVE wants destination DestNone */ |
195 | if (stmt->ismove) |
196 | dest = None_Receiver; |
197 | |
198 | /* Do it */ |
199 | nprocessed = PortalRunFetch(portal, |
200 | stmt->direction, |
201 | stmt->howMany, |
202 | dest); |
203 | |
204 | /* Return command status if wanted */ |
205 | if (completionTag) |
206 | snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s " UINT64_FORMAT, |
207 | stmt->ismove ? "MOVE" : "FETCH" , |
208 | nprocessed); |
209 | } |
210 | |
211 | /* |
212 | * PerformPortalClose |
213 | * Close a cursor. |
214 | */ |
215 | void |
216 | PerformPortalClose(const char *name) |
217 | { |
218 | Portal portal; |
219 | |
220 | /* NULL means CLOSE ALL */ |
221 | if (name == NULL) |
222 | { |
223 | PortalHashTableDeleteAll(); |
224 | return; |
225 | } |
226 | |
227 | /* |
228 | * Disallow empty-string cursor name (conflicts with protocol-level |
229 | * unnamed portal). |
230 | */ |
231 | if (name[0] == '\0') |
232 | ereport(ERROR, |
233 | (errcode(ERRCODE_INVALID_CURSOR_NAME), |
234 | errmsg("invalid cursor name: must not be empty" ))); |
235 | |
236 | /* |
237 | * get the portal from the portal name |
238 | */ |
239 | portal = GetPortalByName(name); |
240 | if (!PortalIsValid(portal)) |
241 | { |
242 | ereport(ERROR, |
243 | (errcode(ERRCODE_UNDEFINED_CURSOR), |
244 | errmsg("cursor \"%s\" does not exist" , name))); |
245 | return; /* keep compiler happy */ |
246 | } |
247 | |
248 | /* |
249 | * Note: PortalCleanup is called as a side-effect, if not already done. |
250 | */ |
251 | PortalDrop(portal, false); |
252 | } |
253 | |
254 | /* |
255 | * PortalCleanup |
256 | * |
257 | * Clean up a portal when it's dropped. This is the standard cleanup hook |
258 | * for portals. |
259 | * |
260 | * Note: if portal->status is PORTAL_FAILED, we are probably being called |
261 | * during error abort, and must be careful to avoid doing anything that |
262 | * is likely to fail again. |
263 | */ |
264 | void |
265 | PortalCleanup(Portal portal) |
266 | { |
267 | QueryDesc *queryDesc; |
268 | |
269 | /* |
270 | * sanity checks |
271 | */ |
272 | AssertArg(PortalIsValid(portal)); |
273 | AssertArg(portal->cleanup == PortalCleanup); |
274 | |
275 | /* |
276 | * Shut down executor, if still running. We skip this during error abort, |
277 | * since other mechanisms will take care of releasing executor resources, |
278 | * and we can't be sure that ExecutorEnd itself wouldn't fail. |
279 | */ |
280 | queryDesc = portal->queryDesc; |
281 | if (queryDesc) |
282 | { |
283 | /* |
284 | * Reset the queryDesc before anything else. This prevents us from |
285 | * trying to shut down the executor twice, in case of an error below. |
286 | * The transaction abort mechanisms will take care of resource cleanup |
287 | * in such a case. |
288 | */ |
289 | portal->queryDesc = NULL; |
290 | |
291 | if (portal->status != PORTAL_FAILED) |
292 | { |
293 | ResourceOwner saveResourceOwner; |
294 | |
295 | /* We must make the portal's resource owner current */ |
296 | saveResourceOwner = CurrentResourceOwner; |
297 | if (portal->resowner) |
298 | CurrentResourceOwner = portal->resowner; |
299 | |
300 | ExecutorFinish(queryDesc); |
301 | ExecutorEnd(queryDesc); |
302 | FreeQueryDesc(queryDesc); |
303 | |
304 | CurrentResourceOwner = saveResourceOwner; |
305 | } |
306 | } |
307 | } |
308 | |
309 | /* |
310 | * PersistHoldablePortal |
311 | * |
312 | * Prepare the specified Portal for access outside of the current |
313 | * transaction. When this function returns, all future accesses to the |
314 | * portal must be done via the Tuplestore (not by invoking the |
315 | * executor). |
316 | */ |
317 | void |
318 | PersistHoldablePortal(Portal portal) |
319 | { |
320 | QueryDesc *queryDesc = portal->queryDesc; |
321 | Portal saveActivePortal; |
322 | ResourceOwner saveResourceOwner; |
323 | MemoryContext savePortalContext; |
324 | MemoryContext oldcxt; |
325 | |
326 | /* |
327 | * If we're preserving a holdable portal, we had better be inside the |
328 | * transaction that originally created it. |
329 | */ |
330 | Assert(portal->createSubid != InvalidSubTransactionId); |
331 | Assert(queryDesc != NULL); |
332 | |
333 | /* |
334 | * Caller must have created the tuplestore already ... but not a snapshot. |
335 | */ |
336 | Assert(portal->holdContext != NULL); |
337 | Assert(portal->holdStore != NULL); |
338 | Assert(portal->holdSnapshot == NULL); |
339 | |
340 | /* |
341 | * Before closing down the executor, we must copy the tupdesc into |
342 | * long-term memory, since it was created in executor memory. |
343 | */ |
344 | oldcxt = MemoryContextSwitchTo(portal->holdContext); |
345 | |
346 | portal->tupDesc = CreateTupleDescCopy(portal->tupDesc); |
347 | |
348 | MemoryContextSwitchTo(oldcxt); |
349 | |
350 | /* |
351 | * Check for improper portal use, and mark portal active. |
352 | */ |
353 | MarkPortalActive(portal); |
354 | |
355 | /* |
356 | * Set up global portal context pointers. |
357 | */ |
358 | saveActivePortal = ActivePortal; |
359 | saveResourceOwner = CurrentResourceOwner; |
360 | savePortalContext = PortalContext; |
361 | PG_TRY(); |
362 | { |
363 | ActivePortal = portal; |
364 | if (portal->resowner) |
365 | CurrentResourceOwner = portal->resowner; |
366 | PortalContext = portal->portalContext; |
367 | |
368 | MemoryContextSwitchTo(PortalContext); |
369 | |
370 | PushActiveSnapshot(queryDesc->snapshot); |
371 | |
372 | /* |
373 | * Rewind the executor: we need to store the entire result set in the |
374 | * tuplestore, so that subsequent backward FETCHs can be processed. |
375 | */ |
376 | ExecutorRewind(queryDesc); |
377 | |
378 | /* |
379 | * Change the destination to output to the tuplestore. Note we tell |
380 | * the tuplestore receiver to detoast all data passed through it; this |
381 | * makes it safe to not keep a snapshot associated with the data. |
382 | */ |
383 | queryDesc->dest = CreateDestReceiver(DestTuplestore); |
384 | SetTuplestoreDestReceiverParams(queryDesc->dest, |
385 | portal->holdStore, |
386 | portal->holdContext, |
387 | true); |
388 | |
389 | /* Fetch the result set into the tuplestore */ |
390 | ExecutorRun(queryDesc, ForwardScanDirection, 0L, false); |
391 | |
392 | queryDesc->dest->rDestroy(queryDesc->dest); |
393 | queryDesc->dest = NULL; |
394 | |
395 | /* |
396 | * Now shut down the inner executor. |
397 | */ |
398 | portal->queryDesc = NULL; /* prevent double shutdown */ |
399 | ExecutorFinish(queryDesc); |
400 | ExecutorEnd(queryDesc); |
401 | FreeQueryDesc(queryDesc); |
402 | |
403 | /* |
404 | * Set the position in the result set. |
405 | */ |
406 | MemoryContextSwitchTo(portal->holdContext); |
407 | |
408 | if (portal->atEnd) |
409 | { |
410 | /* |
411 | * Just force the tuplestore forward to its end. The size of the |
412 | * skip request here is arbitrary. |
413 | */ |
414 | while (tuplestore_skiptuples(portal->holdStore, 1000000, true)) |
415 | /* continue */ ; |
416 | } |
417 | else |
418 | { |
419 | tuplestore_rescan(portal->holdStore); |
420 | |
421 | if (!tuplestore_skiptuples(portal->holdStore, |
422 | portal->portalPos, |
423 | true)) |
424 | elog(ERROR, "unexpected end of tuple stream" ); |
425 | } |
426 | } |
427 | PG_CATCH(); |
428 | { |
429 | /* Uncaught error while executing portal: mark it dead */ |
430 | MarkPortalFailed(portal); |
431 | |
432 | /* Restore global vars and propagate error */ |
433 | ActivePortal = saveActivePortal; |
434 | CurrentResourceOwner = saveResourceOwner; |
435 | PortalContext = savePortalContext; |
436 | |
437 | PG_RE_THROW(); |
438 | } |
439 | PG_END_TRY(); |
440 | |
441 | MemoryContextSwitchTo(oldcxt); |
442 | |
443 | /* Mark portal not active */ |
444 | portal->status = PORTAL_READY; |
445 | |
446 | ActivePortal = saveActivePortal; |
447 | CurrentResourceOwner = saveResourceOwner; |
448 | PortalContext = savePortalContext; |
449 | |
450 | PopActiveSnapshot(); |
451 | |
452 | /* |
453 | * We can now release any subsidiary memory of the portal's context; we'll |
454 | * never use it again. The executor already dropped its context, but this |
455 | * will clean up anything that glommed onto the portal's context via |
456 | * PortalContext. |
457 | */ |
458 | MemoryContextDeleteChildren(portal->portalContext); |
459 | } |
460 | |