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