Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * pquery.c
4 : : * POSTGRES process query command code
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/tcop/pquery.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : :
16 : : #include "postgres.h"
17 : :
18 : : #include <limits.h>
19 : :
20 : : #include "access/xact.h"
21 : : #include "commands/prepare.h"
22 : : #include "executor/executor.h"
23 : : #include "executor/tstoreReceiver.h"
24 : : #include "miscadmin.h"
25 : : #include "pg_trace.h"
26 : : #include "port/pg_bitutils.h"
27 : : #include "tcop/pquery.h"
28 : : #include "tcop/utility.h"
29 : : #include "utils/memutils.h"
30 : : #include "utils/snapmgr.h"
31 : :
32 : : /*
33 : : * ActivePortal is the currently executing Portal (the most closely nested,
34 : : * if there are several).
35 : : */
36 : : Portal ActivePortal = NULL;
37 : :
38 : : /* Private data for QueryDesc.extension_state. */
39 : : static const char **QueryDescExtensionNameArray = NULL;
40 : : static int QueryDescExtensionNamesAssigned = 0;
41 : : static int QueryDescExtensionNamesAllocated = 0;
42 : :
43 : : static void ProcessQuery(PlannedStmt *plan,
44 : : const char *sourceText,
45 : : ParamListInfo params,
46 : : QueryEnvironment *queryEnv,
47 : : DestReceiver *dest,
48 : : QueryCompletion *qc);
49 : : static void FillPortalStore(Portal portal, bool isTopLevel);
50 : : static uint64 RunFromStore(Portal portal, ScanDirection direction, uint64 count,
51 : : DestReceiver *dest);
52 : : static uint64 PortalRunSelect(Portal portal, bool forward, long count,
53 : : DestReceiver *dest);
54 : : static void PortalRunUtility(Portal portal, PlannedStmt *pstmt,
55 : : bool isTopLevel, bool setHoldSnapshot,
56 : : DestReceiver *dest, QueryCompletion *qc);
57 : : static void PortalRunMulti(Portal portal,
58 : : bool isTopLevel, bool setHoldSnapshot,
59 : : DestReceiver *dest, DestReceiver *altdest,
60 : : QueryCompletion *qc);
61 : : static uint64 DoPortalRunFetch(Portal portal,
62 : : FetchDirection fdirection,
63 : : long count,
64 : : DestReceiver *dest);
65 : : static void DoPortalRewind(Portal portal);
66 : :
67 : :
68 : : /*
69 : : * CreateQueryDesc
70 : : */
71 : : QueryDesc *
6930 bruce@momjian.us 72 :CBC 371699 : CreateQueryDesc(PlannedStmt *plannedstmt,
73 : : const char *sourceText,
74 : : Snapshot snapshot,
75 : : Snapshot crosscheck_snapshot,
76 : : DestReceiver *dest,
77 : : ParamListInfo params,
78 : : QueryEnvironment *queryEnv,
79 : : int instrument_options)
80 : : {
330 michael@paquier.xyz 81 : 371699 : QueryDesc *qd = palloc_object(QueryDesc);
82 : :
7198 tgl@sss.pgh.pa.us 83 : 371699 : qd->operation = plannedstmt->commandType; /* operation */
3424 84 : 371699 : qd->plannedstmt = plannedstmt; /* plan */
6356 bruce@momjian.us 85 : 371699 : qd->sourceText = sourceText; /* query text */
6751 alvherre@alvh.no-ip. 86 : 371699 : qd->snapshot = RegisterSnapshot(snapshot); /* snapshot */
87 : : /* RI check snapshot */
88 : 371699 : qd->crosscheck_snapshot = RegisterSnapshot(crosscheck_snapshot);
10651 bruce@momjian.us 89 : 371699 : qd->dest = dest; /* output dest */
8736 tgl@sss.pgh.pa.us 90 : 371699 : qd->params = params; /* parameter values passed into query */
3506 kgrittn@postgresql.o 91 : 371699 : qd->queryEnv = queryEnv;
3424 tgl@sss.pgh.pa.us 92 : 371699 : qd->instrument_options = instrument_options; /* instrumentation wanted? */
211 andres@anarazel.de 93 : 371699 : qd->query_instr_options = 0;
94 : :
95 : : /* null these fields until set by ExecutorStart */
8736 tgl@sss.pgh.pa.us 96 : 371699 : qd->tupDesc = NULL;
97 : 371699 : qd->estate = NULL;
98 : 371699 : qd->planstate = NULL;
211 andres@anarazel.de 99 : 371699 : qd->query_instr = NULL;
100 : :
101 : : /* not yet executed */
3514 rhaas@postgresql.org 102 : 371699 : qd->already_executed = false;
103 : :
104 : : /* no extension state yet */
9 michael@paquier.xyz 105 :GNC 371699 : qd->extension_state = NULL;
106 : 371699 : qd->extension_state_allocated = 0;
107 : :
8736 tgl@sss.pgh.pa.us 108 :CBC 371699 : return qd;
109 : : }
110 : :
111 : : /*
112 : : * FreeQueryDesc
113 : : */
114 : : void
8726 115 : 352431 : FreeQueryDesc(QueryDesc *qdesc)
116 : : {
117 : : /* Can't be a live query */
118 [ - + ]: 352431 : Assert(qdesc->estate == NULL);
119 : :
120 : : /* forget our snapshots */
6751 alvherre@alvh.no-ip. 121 : 352431 : UnregisterSnapshot(qdesc->snapshot);
122 : 352431 : UnregisterSnapshot(qdesc->crosscheck_snapshot);
123 : :
124 : : /* Free extension state pointers */
9 michael@paquier.xyz 125 [ + + ]:GNC 352431 : if (qdesc->extension_state != NULL)
126 : 39653 : pfree(qdesc->extension_state);
127 : :
128 : : /* Free the QueryDesc itself */
8726 tgl@sss.pgh.pa.us 129 :CBC 352431 : pfree(qdesc);
130 : 352431 : }
131 : :
132 : : /*
133 : : * Map the name of an extension to an integer ID, to be used with
134 : : * SetQueryDescExtensionState() and GetQueryDescExtensionState().
135 : : *
136 : : * Within the lifetime of a particular backend, the same name will be mapped
137 : : * to the same ID every time. IDs are not stable across backends.
138 : : */
139 : : int
9 michael@paquier.xyz 140 :GNC 9 : GetQueryDescExtensionId(const char *extension_name)
141 : : {
142 : : /* Search for an existing extension by this name; if found, return ID. */
143 [ - + ]: 9 : for (int i = 0; i < QueryDescExtensionNamesAssigned; ++i)
9 michael@paquier.xyz 144 [ # # ]:UNC 0 : if (strcmp(QueryDescExtensionNameArray[i], extension_name) == 0)
145 : 0 : return i;
146 : :
147 : : /* If there is no array yet, create one. */
9 michael@paquier.xyz 148 [ + - ]:GNC 9 : if (QueryDescExtensionNameArray == NULL)
149 : : {
150 : 9 : QueryDescExtensionNamesAllocated = 16;
151 : 9 : QueryDescExtensionNameArray = (const char **)
152 : 9 : MemoryContextAlloc(TopMemoryContext,
153 : : QueryDescExtensionNamesAllocated
154 : : * sizeof(char *));
155 : : }
156 : :
157 : : /* If there's an array but it's currently full, expand it. */
158 [ - + ]: 9 : if (QueryDescExtensionNamesAssigned >= QueryDescExtensionNamesAllocated)
159 : : {
9 michael@paquier.xyz 160 :UNC 0 : int i = pg_nextpower2_32(QueryDescExtensionNamesAssigned + 1);
161 : :
162 : 0 : QueryDescExtensionNameArray =
163 : 0 : repalloc_array(QueryDescExtensionNameArray, const char *, i);
164 : 0 : QueryDescExtensionNamesAllocated = i;
165 : : }
166 : :
167 : : /* Assign and return new ID. The name is copied, to be on the safe side. */
9 michael@paquier.xyz 168 :GNC 18 : QueryDescExtensionNameArray[QueryDescExtensionNamesAssigned] =
169 : 9 : MemoryContextStrdup(TopMemoryContext, extension_name);
170 : 9 : return QueryDescExtensionNamesAssigned++;
171 : : }
172 : :
173 : : /*
174 : : * Get extension-specific state from a QueryDesc, or NULL if none was set.
175 : : */
176 : : void *
177 : 56546 : GetQueryDescExtensionState(QueryDesc *qdesc, int extension_id)
178 : : {
179 [ - + ]: 56546 : Assert(extension_id >= 0);
180 : :
181 [ + + ]: 56546 : if (extension_id >= qdesc->extension_state_allocated)
182 : 16893 : return NULL;
183 : :
184 : 39653 : return qdesc->extension_state[extension_id];
185 : : }
186 : :
187 : : /*
188 : : * Store extension-specific state into a QueryDesc.
189 : : *
190 : : * The array of pointers is allocated in the memory context of the QueryDesc,
191 : : * and is freed by FreeQueryDesc().
192 : : */
193 : : void
194 : 41618 : SetQueryDescExtensionState(QueryDesc *qdesc, int extension_id, void *opaque)
195 : : {
196 [ - + ]: 41618 : Assert(extension_id >= 0);
197 : :
198 : : /* If there is no array yet, create one. */
199 [ + - ]: 41618 : if (qdesc->extension_state == NULL)
200 : : {
201 : 41618 : qdesc->extension_state_allocated =
202 [ - + ]: 41618 : Max(4, pg_nextpower2_32(extension_id + 1));
203 : 41618 : qdesc->extension_state =
204 : 41618 : MemoryContextAllocZero(GetMemoryChunkContext(qdesc),
205 : 41618 : qdesc->extension_state_allocated
206 : : * sizeof(void *));
207 : : }
208 : :
209 : : /* If there's an array but it's currently full, expand it. */
210 [ - + ]: 41618 : if (extension_id >= qdesc->extension_state_allocated)
211 : : {
9 michael@paquier.xyz 212 :UNC 0 : int i = pg_nextpower2_32(extension_id + 1);
213 : :
214 : 0 : qdesc->extension_state =
215 : 0 : repalloc0_array(qdesc->extension_state, void *,
216 : : qdesc->extension_state_allocated, i);
217 : 0 : qdesc->extension_state_allocated = i;
218 : : }
219 : :
9 michael@paquier.xyz 220 :GNC 41618 : qdesc->extension_state[extension_id] = opaque;
221 : 41618 : }
222 : :
223 : :
224 : : /*
225 : : * ProcessQuery
226 : : * Execute a single plannable query within a PORTAL_MULTI_QUERY,
227 : : * PORTAL_ONE_RETURNING, or PORTAL_ONE_MOD_WITH portal
228 : : *
229 : : * plan: the plan tree for the query
230 : : * sourceText: the source text of the query
231 : : * params: any parameters needed
232 : : * dest: where to send results
233 : : * qc: where to store the command completion status data.
234 : : *
235 : : * qc may be NULL if caller doesn't want a status string.
236 : : *
237 : : * Must be called in a memory context that will be reset or deleted on
238 : : * error; otherwise the executor's memory usage will be leaked.
239 : : */
240 : : static void
6930 bruce@momjian.us 241 :CBC 54373 : ProcessQuery(PlannedStmt *plan,
242 : : const char *sourceText,
243 : : ParamListInfo params,
244 : : QueryEnvironment *queryEnv,
245 : : DestReceiver *dest,
246 : : QueryCompletion *qc)
247 : : {
248 : : QueryDesc *queryDesc;
249 : :
250 : : /*
251 : : * Create the QueryDesc object
252 : : */
532 amitlan@postgresql.o 253 : 54373 : queryDesc = CreateQueryDesc(plan, sourceText,
254 : : GetActiveSnapshot(), InvalidSnapshot,
255 : : dest, params, queryEnv, 0);
256 : :
257 : : /*
258 : : * Call ExecutorStart to prepare the plan for execution
259 : : */
260 : 54373 : ExecutorStart(queryDesc, 0);
261 : :
262 : : /*
263 : : * Run the plan to completion.
264 : : */
696 tgl@sss.pgh.pa.us 265 : 53680 : ExecutorRun(queryDesc, ForwardScanDirection, 0);
266 : :
267 : : /*
268 : : * Build command completion status data, if caller wants one.
269 : : */
2439 alvherre@alvh.no-ip. 270 [ + + ]: 51511 : if (qc)
271 : : {
272 : : CommandTag tag;
273 : :
371 alvherre@kurilemu.de 274 [ + + ]: 51071 : if (queryDesc->operation == CMD_SELECT)
275 : 81 : tag = CMDTAG_SELECT;
276 [ + + ]: 50990 : else if (queryDesc->operation == CMD_INSERT)
277 : 41067 : tag = CMDTAG_INSERT;
278 [ + + ]: 9923 : else if (queryDesc->operation == CMD_UPDATE)
279 : 6897 : tag = CMDTAG_UPDATE;
280 [ + + ]: 3026 : else if (queryDesc->operation == CMD_DELETE)
281 : 2278 : tag = CMDTAG_DELETE;
282 [ + - ]: 748 : else if (queryDesc->operation == CMD_MERGE)
283 : 748 : tag = CMDTAG_MERGE;
284 : : else
371 alvherre@kurilemu.de 285 :UBC 0 : tag = CMDTAG_UNKNOWN;
286 : :
371 alvherre@kurilemu.de 287 :CBC 51071 : SetQueryCompletion(qc, tag, queryDesc->estate->es_processed);
288 : : }
289 : :
290 : : /*
291 : : * Now, we close down all the scans and free allocated resources.
292 : : */
5730 tgl@sss.pgh.pa.us 293 : 51511 : ExecutorFinish(queryDesc);
8736 294 : 50693 : ExecutorEnd(queryDesc);
295 : :
8726 296 : 50693 : FreeQueryDesc(queryDesc);
11076 scrappy@hub.org 297 : 50693 : }
298 : :
299 : : /*
300 : : * ChoosePortalStrategy
301 : : * Select portal execution strategy given the intended statement list.
302 : : *
303 : : * The list elements can be Querys or PlannedStmts.
304 : : * That's more general than portals need, but plancache.c uses this too.
305 : : *
306 : : * See the comments in portal.h.
307 : : */
308 : : PortalStrategy
7198 tgl@sss.pgh.pa.us 309 : 519740 : ChoosePortalStrategy(List *stmts)
310 : : {
311 : : int nSetTag;
312 : : ListCell *lc;
313 : :
314 : : /*
315 : : * PORTAL_ONE_SELECT and PORTAL_UTIL_SELECT need only consider the
316 : : * single-statement case, since there are no rewrite rules that can add
317 : : * auxiliary queries to a SELECT or a utility command. PORTAL_ONE_MOD_WITH
318 : : * likewise allows only one top-level statement.
319 : : */
320 [ + + ]: 519740 : if (list_length(stmts) == 1)
321 : : {
322 : 519492 : Node *stmt = (Node *) linitial(stmts);
323 : :
324 [ + + ]: 519492 : if (IsA(stmt, Query))
325 : : {
326 : 50195 : Query *query = (Query *) stmt;
327 : :
328 [ + - ]: 50195 : if (query->canSetTag)
329 : : {
3582 330 [ + + ]: 50195 : if (query->commandType == CMD_SELECT)
331 : : {
5732 332 [ - + ]: 33985 : if (query->hasModifyingCTE)
5732 tgl@sss.pgh.pa.us 333 :UBC 0 : return PORTAL_ONE_MOD_WITH;
334 : : else
5732 tgl@sss.pgh.pa.us 335 :CBC 33985 : return PORTAL_ONE_SELECT;
336 : : }
3582 337 [ + + ]: 16210 : if (query->commandType == CMD_UTILITY)
338 : : {
7198 339 [ + + ]: 12888 : if (UtilityReturnsTuples(query->utilityStmt))
340 : 6717 : return PORTAL_UTIL_SELECT;
341 : : /* it can't be ONE_RETURNING, so give up */
342 : 6171 : return PORTAL_MULTI_QUERY;
343 : : }
344 : : }
345 : : }
346 [ + - ]: 469297 : else if (IsA(stmt, PlannedStmt))
347 : : {
348 : 469297 : PlannedStmt *pstmt = (PlannedStmt *) stmt;
349 : :
350 [ + + ]: 469297 : if (pstmt->canSetTag)
351 : : {
3582 352 [ + + ]: 469273 : if (pstmt->commandType == CMD_SELECT)
353 : : {
5732 354 [ + + ]: 186919 : if (pstmt->hasModifyingCTE)
355 : 89 : return PORTAL_ONE_MOD_WITH;
356 : : else
357 : 186830 : return PORTAL_ONE_SELECT;
358 : : }
3582 359 [ + + ]: 282354 : if (pstmt->commandType == CMD_UTILITY)
360 : : {
361 [ + + ]: 228750 : if (UtilityReturnsTuples(pstmt->utilityStmt))
362 : 29781 : return PORTAL_UTIL_SELECT;
363 : : /* it can't be ONE_RETURNING, so give up */
364 : 198969 : return PORTAL_MULTI_QUERY;
365 : : }
366 : : }
367 : : }
368 : : else
3582 tgl@sss.pgh.pa.us 369 [ # # ]:UBC 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
370 : : }
371 : :
372 : : /*
373 : : * PORTAL_ONE_RETURNING has to allow auxiliary queries added by rewrite.
374 : : * Choose PORTAL_ONE_RETURNING if there is exactly one canSetTag query and
375 : : * it has a RETURNING list.
376 : : */
7388 tgl@sss.pgh.pa.us 377 :CBC 57198 : nSetTag = 0;
7198 378 [ + + + + : 59639 : foreach(lc, stmts)
+ + ]
379 : : {
380 : 57326 : Node *stmt = (Node *) lfirst(lc);
381 : :
382 [ + + ]: 57326 : if (IsA(stmt, Query))
383 : : {
384 : 3330 : Query *query = (Query *) stmt;
385 : :
386 [ + + ]: 3330 : if (query->canSetTag)
387 : : {
388 [ - + ]: 3326 : if (++nSetTag > 1)
389 : 54885 : return PORTAL_MULTI_QUERY; /* no need to look further */
3582 390 [ + - ]: 3326 : if (query->commandType == CMD_UTILITY ||
391 [ + + ]: 3326 : query->returningList == NIL)
7198 392 : 3134 : return PORTAL_MULTI_QUERY; /* no need to look further */
393 : : }
394 : : }
395 [ + - ]: 53996 : else if (IsA(stmt, PlannedStmt))
396 : : {
397 : 53996 : PlannedStmt *pstmt = (PlannedStmt *) stmt;
398 : :
399 [ + + ]: 53996 : if (pstmt->canSetTag)
400 : : {
401 [ - + ]: 53840 : if (++nSetTag > 1)
7198 tgl@sss.pgh.pa.us 402 :UBC 0 : return PORTAL_MULTI_QUERY; /* no need to look further */
3582 tgl@sss.pgh.pa.us 403 [ + - ]:CBC 53840 : if (pstmt->commandType == CMD_UTILITY ||
404 [ + + ]: 53840 : !pstmt->hasReturning)
7198 405 : 51751 : return PORTAL_MULTI_QUERY; /* no need to look further */
406 : : }
407 : : }
408 : : else
3582 tgl@sss.pgh.pa.us 409 [ # # ]:UBC 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
410 : : }
7388 tgl@sss.pgh.pa.us 411 [ + + ]:CBC 2313 : if (nSetTag == 1)
412 : 2281 : return PORTAL_ONE_RETURNING;
413 : :
414 : : /* Else, it's the general case... */
415 : 32 : return PORTAL_MULTI_QUERY;
416 : : }
417 : :
418 : : /*
419 : : * FetchPortalTargetList
420 : : * Given a portal that returns tuples, extract the query targetlist.
421 : : * Returns NIL if the portal doesn't have a determinable targetlist.
422 : : *
423 : : * Note: do not modify the result.
424 : : */
425 : : List *
7806 426 : 201740 : FetchPortalTargetList(Portal portal)
427 : : {
428 : : /* no point in looking if we determined it doesn't return tuples */
7198 429 [ + + ]: 201740 : if (portal->strategy == PORTAL_MULTI_QUERY)
430 : 20 : return NIL;
431 : : /* get the primary statement and find out what it returns */
3582 432 : 201720 : return FetchStatementTargetList((Node *) PortalGetPrimaryStmt(portal));
433 : : }
434 : :
435 : : /*
436 : : * FetchStatementTargetList
437 : : * Given a statement that returns tuples, extract the query targetlist.
438 : : * Returns NIL if the statement doesn't have a determinable targetlist.
439 : : *
440 : : * This can be applied to a Query or a PlannedStmt.
441 : : * That's more general than portals need, but plancache.c uses this too.
442 : : *
443 : : * Note: do not modify the result.
444 : : *
445 : : * XXX be careful to keep this in sync with UtilityReturnsTuples.
446 : : */
447 : : List *
7198 448 : 210100 : FetchStatementTargetList(Node *stmt)
449 : : {
450 [ - + ]: 210100 : if (stmt == NULL)
7198 tgl@sss.pgh.pa.us 451 :UBC 0 : return NIL;
7198 tgl@sss.pgh.pa.us 452 [ + + ]:CBC 210100 : if (IsA(stmt, Query))
453 : : {
454 : 8380 : Query *query = (Query *) stmt;
455 : :
3582 456 [ + + ]: 8380 : if (query->commandType == CMD_UTILITY)
457 : : {
458 : : /* transfer attention to utility statement */
7198 459 : 9 : stmt = query->utilityStmt;
460 : : }
461 : : else
462 : : {
3582 463 [ + + ]: 8371 : if (query->commandType == CMD_SELECT)
7198 464 : 8361 : return query->targetList;
465 [ + - ]: 10 : if (query->returningList)
466 : 10 : return query->returningList;
7198 tgl@sss.pgh.pa.us 467 :UBC 0 : return NIL;
468 : : }
469 : : }
7198 tgl@sss.pgh.pa.us 470 [ + + ]:CBC 201729 : if (IsA(stmt, PlannedStmt))
471 : : {
472 : 201720 : PlannedStmt *pstmt = (PlannedStmt *) stmt;
473 : :
3582 474 [ + + ]: 201720 : if (pstmt->commandType == CMD_UTILITY)
475 : : {
476 : : /* transfer attention to utility statement */
477 : 24120 : stmt = pstmt->utilityStmt;
478 : : }
479 : : else
480 : : {
481 [ + + ]: 177600 : if (pstmt->commandType == CMD_SELECT)
482 : 175651 : return pstmt->planTree->targetlist;
483 [ + - ]: 1949 : if (pstmt->hasReturning)
484 : 1949 : return pstmt->planTree->targetlist;
3582 tgl@sss.pgh.pa.us 485 :UBC 0 : return NIL;
486 : : }
487 : : }
7198 tgl@sss.pgh.pa.us 488 [ + + ]:CBC 24129 : if (IsA(stmt, FetchStmt))
489 : : {
490 : 3128 : FetchStmt *fstmt = (FetchStmt *) stmt;
491 : : Portal subportal;
492 : :
493 [ - + ]: 3128 : Assert(!fstmt->ismove);
494 : 3128 : subportal = GetPortalByName(fstmt->portalname);
34 michael@paquier.xyz 495 [ + + ]: 3128 : if (!PortalIsValid(subportal))
496 [ + - ]: 1 : ereport(ERROR,
497 : : (errcode(ERRCODE_UNDEFINED_CURSOR),
498 : : errmsg("cursor \"%s\" does not exist",
499 : : fstmt->portalname)));
7198 tgl@sss.pgh.pa.us 500 : 3127 : return FetchPortalTargetList(subportal);
501 : : }
502 [ + + ]: 21001 : if (IsA(stmt, ExecuteStmt))
503 : : {
504 : 8329 : ExecuteStmt *estmt = (ExecuteStmt *) stmt;
505 : : PreparedStatement *entry;
506 : :
507 : 8329 : entry = FetchPreparedStatement(estmt->name, true);
508 : 8329 : return FetchPreparedStatementTargetList(entry);
509 : : }
7806 510 : 12672 : return NIL;
511 : : }
512 : :
513 : : /*
514 : : * PortalStart
515 : : * Prepare a portal for execution.
516 : : *
517 : : * Caller must already have created the portal, done PortalDefineQuery(),
518 : : * and adjusted portal options if needed.
519 : : *
520 : : * If parameters are needed by the query, they must be passed in "params"
521 : : * (caller is responsible for giving them appropriate lifetime).
522 : : *
523 : : * The caller can also provide an initial set of "eflags" to be passed to
524 : : * ExecutorStart (but note these can be modified internally, and they are
525 : : * currently only honored for PORTAL_ONE_SELECT portals). Most callers
526 : : * should simply pass zero.
527 : : *
528 : : * The caller can optionally pass a snapshot to be used; pass InvalidSnapshot
529 : : * for the normal behavior of setting a new snapshot. This parameter is
530 : : * presently ignored for non-PORTAL_ONE_SELECT portals (it's only intended
531 : : * to be used for cursors).
532 : : *
533 : : * On return, portal is ready to accept PortalRun() calls, and the result
534 : : * tupdesc (if any) is known.
535 : : */
536 : : void
5344 537 : 469537 : PortalStart(Portal portal, ParamListInfo params,
538 : : int eflags, Snapshot snapshot)
539 : : {
540 : : Portal saveActivePortal;
541 : : ResourceOwner saveResourceOwner;
542 : : MemoryContext savePortalContext;
543 : : MemoryContext oldContext;
544 : : QueryDesc *queryDesc;
545 : : int myeflags;
546 : :
1469 peter@eisentraut.org 547 [ - + ]: 469537 : Assert(PortalIsValid(portal));
548 [ - + ]: 469537 : Assert(portal->status == PORTAL_DEFINED);
549 : :
550 : : /*
551 : : * Set up global portal context pointers.
552 : : */
8146 tgl@sss.pgh.pa.us 553 : 469537 : saveActivePortal = ActivePortal;
554 : 469537 : saveResourceOwner = CurrentResourceOwner;
555 : 469537 : savePortalContext = PortalContext;
8132 556 [ + + ]: 469537 : PG_TRY();
557 : : {
558 : 469537 : ActivePortal = portal;
4893 559 [ + - ]: 469537 : if (portal->resowner)
560 : 469537 : CurrentResourceOwner = portal->resowner;
3246 peter_e@gmx.net 561 : 469537 : PortalContext = portal->portalContext;
562 : :
563 : 469537 : oldContext = MemoryContextSwitchTo(PortalContext);
564 : :
565 : : /* Must remember portal param list, if any */
8132 tgl@sss.pgh.pa.us 566 : 469537 : portal->portalParams = params;
567 : :
568 : : /*
569 : : * Determine the portal execution strategy
570 : : */
7198 571 : 469537 : portal->strategy = ChoosePortalStrategy(portal->stmts);
572 : :
573 : : /*
574 : : * Fire her up according to the strategy
575 : : */
8132 576 [ + + + + : 469537 : switch (portal->strategy)
- ]
577 : : {
578 : 186830 : case PORTAL_ONE_SELECT:
579 : :
580 : : /* Must set snapshot before starting executor. */
5092 581 [ + + ]: 186830 : if (snapshot)
582 : 13847 : PushActiveSnapshot(snapshot);
583 : : else
6751 alvherre@alvh.no-ip. 584 : 172983 : PushActiveSnapshot(GetTransactionSnapshot());
585 : :
586 : : /*
587 : : * We could remember the snapshot in portal->portalSnapshot,
588 : : * but presently there seems no need to, as this code path
589 : : * cannot be used for non-atomic execution. Hence there can't
590 : : * be any commit/abort that might destroy the snapshot. Since
591 : : * we don't do that, there's also no need to force a
592 : : * non-default nesting level for the snapshot.
593 : : */
594 : :
595 : : /*
596 : : * Create QueryDesc in portal's context; for the moment, set
597 : : * the destination to DestNone.
598 : : */
3496 tgl@sss.pgh.pa.us 599 : 186830 : queryDesc = CreateQueryDesc(linitial_node(PlannedStmt, portal->stmts),
600 : : portal->sourceText,
601 : : GetActiveSnapshot(),
602 : : InvalidSnapshot,
603 : : None_Receiver,
604 : : params,
605 : : portal->queryEnv,
606 : : 0);
607 : :
608 : : /*
609 : : * If it's a scrollable cursor, executor needs to support
610 : : * REWIND and backwards scan, as well as whatever the caller
611 : : * might've asked for.
612 : : */
7555 613 [ + + ]: 186830 : if (portal->cursorOptions & CURSOR_OPT_SCROLL)
5344 614 : 2408 : myeflags = eflags | EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD;
615 : : else
616 : 184422 : myeflags = eflags;
617 : :
618 : : /*
619 : : * Call ExecutorStart to prepare the plan for execution
620 : : */
532 amitlan@postgresql.o 621 : 186830 : ExecutorStart(queryDesc, myeflags);
622 : :
623 : : /*
624 : : * This tells PortalCleanup to shut down the executor
625 : : */
8132 tgl@sss.pgh.pa.us 626 : 186417 : portal->queryDesc = queryDesc;
627 : :
628 : : /*
629 : : * Remember tuple descriptor (computed by ExecutorStart)
630 : : */
631 : 186417 : portal->tupDesc = queryDesc->tupDesc;
632 : :
633 : : /*
634 : : * Reset cursor position data to "start of query"
635 : : */
636 : 186417 : portal->atStart = true;
8103 bruce@momjian.us 637 : 186417 : portal->atEnd = false; /* allow fetches */
8132 tgl@sss.pgh.pa.us 638 : 186417 : portal->portalPos = 0;
639 : :
6751 alvherre@alvh.no-ip. 640 : 186417 : PopActiveSnapshot();
8132 tgl@sss.pgh.pa.us 641 : 186417 : break;
642 : :
7390 643 : 2178 : case PORTAL_ONE_RETURNING:
644 : : case PORTAL_ONE_MOD_WITH:
645 : :
646 : : /*
647 : : * We don't start the executor until we are told to run the
648 : : * portal. We do need to set up the result tupdesc.
649 : : */
650 : : {
651 : : PlannedStmt *pstmt;
652 : :
3582 653 : 2178 : pstmt = PortalGetPrimaryStmt(portal);
7198 654 : 2178 : portal->tupDesc =
2907 andres@anarazel.de 655 : 2178 : ExecCleanTypeFromTL(pstmt->planTree->targetlist);
656 : : }
657 : :
658 : : /*
659 : : * Reset cursor position data to "start of query"
660 : : */
7390 tgl@sss.pgh.pa.us 661 : 2178 : portal->atStart = true;
662 : 2178 : portal->atEnd = false; /* allow fetches */
663 : 2178 : portal->portalPos = 0;
664 : 2178 : break;
665 : :
8132 666 : 29781 : case PORTAL_UTIL_SELECT:
667 : :
668 : : /*
669 : : * We don't set snapshot here, because PortalRunUtility will
670 : : * take care of it if needed.
671 : : */
672 : : {
3582 673 : 29781 : PlannedStmt *pstmt = PortalGetPrimaryStmt(portal);
674 : :
675 [ - + ]: 29781 : Assert(pstmt->commandType == CMD_UTILITY);
676 : 29781 : portal->tupDesc = UtilityTupleDescriptor(pstmt->utilityStmt);
677 : : }
678 : :
679 : : /*
680 : : * Reset cursor position data to "start of query"
681 : : */
8132 682 : 29763 : portal->atStart = true;
8103 bruce@momjian.us 683 : 29763 : portal->atEnd = false; /* allow fetches */
8132 tgl@sss.pgh.pa.us 684 : 29763 : portal->portalPos = 0;
685 : 29763 : break;
686 : :
687 : 250748 : case PORTAL_MULTI_QUERY:
688 : : /* Need do nothing now */
689 : 250748 : portal->tupDesc = NULL;
690 : 250748 : break;
691 : : }
692 : : }
693 : 431 : PG_CATCH();
694 : : {
695 : : /* Uncaught error while executing portal: mark it dead */
5377 696 : 431 : MarkPortalFailed(portal);
697 : :
698 : : /* Restore global vars and propagate error */
8132 699 : 431 : ActivePortal = saveActivePortal;
700 : 431 : CurrentResourceOwner = saveResourceOwner;
701 : 431 : PortalContext = savePortalContext;
702 : :
703 : 431 : PG_RE_THROW();
704 : : }
705 [ - + ]: 469106 : PG_END_TRY();
706 : :
8588 707 : 469106 : MemoryContextSwitchTo(oldContext);
708 : :
8146 709 : 469106 : ActivePortal = saveActivePortal;
710 : 469106 : CurrentResourceOwner = saveResourceOwner;
711 : 469106 : PortalContext = savePortalContext;
712 : :
713 : 469106 : portal->status = PORTAL_READY;
8588 714 : 469106 : }
715 : :
716 : : /*
717 : : * PortalSetResultFormat
718 : : * Select the format codes for a portal's output.
719 : : *
720 : : * This must be run after PortalStart for a portal that will be read by
721 : : * a DestRemote or DestRemoteExecute destination. It is not presently needed
722 : : * for other destination types.
723 : : *
724 : : * formats[] is the client format request, as per Bind message conventions.
725 : : */
726 : : void
8582 727 : 449743 : PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
728 : : {
729 : : int natts;
730 : : int i;
731 : :
732 : : /* Do nothing if portal won't return tuples */
733 [ + + ]: 449743 : if (portal->tupDesc == NULL)
734 : 250686 : return;
735 : 199057 : natts = portal->tupDesc->natts;
736 : 199057 : portal->formats = (int16 *)
3246 peter_e@gmx.net 737 : 199057 : MemoryContextAlloc(portal->portalContext,
738 : : natts * sizeof(int16));
8582 tgl@sss.pgh.pa.us 739 [ - + ]: 199057 : if (nFormats > 1)
740 : : {
741 : : /* format specified for each column */
8582 tgl@sss.pgh.pa.us 742 [ # # ]:UBC 0 : if (nFormats != natts)
8507 743 [ # # ]: 0 : ereport(ERROR,
744 : : (errcode(ERRCODE_PROTOCOL_VIOLATION),
745 : : errmsg("bind message has %d result formats but query has %d columns",
746 : : nFormats, natts)));
8582 747 : 0 : memcpy(portal->formats, formats, natts * sizeof(int16));
748 : : }
8494 bruce@momjian.us 749 [ + - ]:CBC 199057 : else if (nFormats > 0)
750 : : {
751 : : /* single format specified, use for all columns */
8582 tgl@sss.pgh.pa.us 752 : 199057 : int16 format1 = formats[0];
753 : :
754 [ + + ]: 801367 : for (i = 0; i < natts; i++)
755 : 602310 : portal->formats[i] = format1;
756 : : }
757 : : else
758 : : {
759 : : /* use default format for all columns */
8582 tgl@sss.pgh.pa.us 760 [ # # ]:UBC 0 : for (i = 0; i < natts; i++)
761 : 0 : portal->formats[i] = 0;
762 : : }
763 : : }
764 : :
765 : : /*
766 : : * PortalRun
767 : : * Run a portal's query or queries.
768 : : *
769 : : * count <= 0 is interpreted as a no-op: the destination gets started up
770 : : * and shut down, but nothing else happens. Also, count == FETCH_ALL is
771 : : * interpreted as "all rows". Note that count is ignored in multi-query
772 : : * situations, where we always run the portal to completion.
773 : : *
774 : : * isTopLevel: true if query is being executed at backend "top level"
775 : : * (that is, directly from a client command message)
776 : : *
777 : : * dest: where to send output of primary (canSetTag) query
778 : : *
779 : : * altdest: where to send output of non-primary queries
780 : : *
781 : : * qc: where to store command completion status data.
782 : : * May be NULL if caller doesn't want status data.
783 : : *
784 : : * Returns true if the portal's execution is complete, false if it was
785 : : * suspended due to exhaustion of the count parameter.
786 : : */
787 : : bool
696 tgl@sss.pgh.pa.us 788 :CBC 458165 : PortalRun(Portal portal, long count, bool isTopLevel,
789 : : DestReceiver *dest, DestReceiver *altdest,
790 : : QueryCompletion *qc)
791 : : {
792 : : bool result;
793 : : uint64 nprocessed;
794 : : ResourceOwner saveTopTransactionResourceOwner;
795 : : MemoryContext saveTopTransactionContext;
796 : : Portal saveActivePortal;
797 : : ResourceOwner saveResourceOwner;
798 : : MemoryContext savePortalContext;
799 : : MemoryContext saveMemoryContext;
800 : :
1469 peter@eisentraut.org 801 [ - + ]: 458165 : Assert(PortalIsValid(portal));
802 : :
803 : : TRACE_POSTGRESQL_QUERY_EXECUTE_START();
804 : :
805 : : /* Initialize empty completion data */
2439 alvherre@alvh.no-ip. 806 [ + - ]: 458165 : if (qc)
807 : 458165 : InitializeQueryCompletion(qc);
808 : :
8267 bruce@momjian.us 809 [ - + - - ]: 458165 : if (log_executor_stats && portal->strategy != PORTAL_MULTI_QUERY)
810 : : {
7177 tgl@sss.pgh.pa.us 811 [ # # ]:UBC 0 : elog(DEBUG3, "PortalRun");
812 : : /* PORTAL_MULTI_QUERY logs its own stats per query */
8267 bruce@momjian.us 813 : 0 : ResetUsage();
814 : : }
815 : :
816 : : /*
817 : : * Check for improper portal use, and mark portal active.
818 : : */
4080 tgl@sss.pgh.pa.us 819 :CBC 458165 : MarkPortalActive(portal);
820 : :
821 : : /*
822 : : * Set up global portal context pointers.
823 : : *
824 : : * We have to play a special game here to support utility commands like
825 : : * VACUUM and CLUSTER, which internally start and commit transactions.
826 : : * When we are called to execute such a command, CurrentResourceOwner will
827 : : * be pointing to the TopTransactionResourceOwner --- which will be
828 : : * destroyed and replaced in the course of the internal commit and
829 : : * restart. So we need to be prepared to restore it as pointing to the
830 : : * exit-time TopTransactionResourceOwner. (Ain't that ugly? This idea of
831 : : * internally starting whole new transactions is not good.)
832 : : * CurrentMemoryContext has a similar problem, but the other pointers we
833 : : * save here will be NULL or pointing to longer-lived objects.
834 : : */
8067 835 : 458165 : saveTopTransactionResourceOwner = TopTransactionResourceOwner;
836 : 458165 : saveTopTransactionContext = TopTransactionContext;
8264 837 : 458165 : saveActivePortal = ActivePortal;
8146 838 : 458165 : saveResourceOwner = CurrentResourceOwner;
8588 839 : 458165 : savePortalContext = PortalContext;
8067 840 : 458165 : saveMemoryContext = CurrentMemoryContext;
8132 841 [ + + ]: 458165 : PG_TRY();
842 : : {
843 : 458165 : ActivePortal = portal;
4893 844 [ + - ]: 458165 : if (portal->resowner)
845 : 458165 : CurrentResourceOwner = portal->resowner;
3246 peter_e@gmx.net 846 : 458165 : PortalContext = portal->portalContext;
847 : :
8067 tgl@sss.pgh.pa.us 848 : 458165 : MemoryContextSwitchTo(PortalContext);
849 : :
8132 850 [ + + - ]: 458165 : switch (portal->strategy)
851 : : {
852 : 207417 : case PORTAL_ONE_SELECT:
853 : : case PORTAL_ONE_RETURNING:
854 : : case PORTAL_ONE_MOD_WITH:
855 : : case PORTAL_UTIL_SELECT:
856 : :
857 : : /*
858 : : * If we have not yet run the command, do so, storing its
859 : : * results in the portal's tuplestore. But we don't do that
860 : : * for the PORTAL_ONE_SELECT case.
861 : : */
6106 bruce@momjian.us 862 [ + + + - ]: 207417 : if (portal->strategy != PORTAL_ONE_SELECT && !portal->holdStore)
7177 tgl@sss.pgh.pa.us 863 : 26501 : FillPortalStore(portal, isTopLevel);
864 : :
865 : : /*
866 : : * Now fetch desired portion of results.
867 : : */
6106 bruce@momjian.us 868 : 207074 : nprocessed = PortalRunSelect(portal, true, count, dest);
869 : :
870 : : /*
871 : : * If the portal result contains a command tag and the caller
872 : : * gave us a pointer to store it, copy it and update the
873 : : * rowcount.
874 : : */
2439 alvherre@alvh.no-ip. 875 [ + - + - ]: 201930 : if (qc && portal->qc.commandTag != CMDTAG_UNKNOWN)
876 : : {
877 : 201930 : CopyQueryCompletion(qc, &portal->qc);
878 : 201930 : qc->nprocessed = nprocessed;
879 : : }
880 : :
881 : : /* Mark portal not active */
8132 tgl@sss.pgh.pa.us 882 : 201930 : portal->status = PORTAL_READY;
883 : :
884 : : /*
885 : : * Since it's a forward fetch, say DONE iff atEnd is now true.
886 : : */
887 : 201930 : result = portal->atEnd;
888 : 201930 : break;
889 : :
890 : 250748 : case PORTAL_MULTI_QUERY:
3742 891 : 250748 : PortalRunMulti(portal, isTopLevel, false,
892 : : dest, altdest, qc);
893 : :
894 : : /* Prevent portal's commands from being re-executed */
5726 895 : 236061 : MarkPortalDone(portal);
896 : :
897 : : /* Always complete at end of RunMulti */
8132 898 : 236061 : result = true;
899 : 236061 : break;
900 : :
8132 tgl@sss.pgh.pa.us 901 :UBC 0 : default:
902 [ # # ]: 0 : elog(ERROR, "unrecognized portal strategy: %d",
903 : : (int) portal->strategy);
904 : : result = false; /* keep compiler quiet */
905 : : break;
906 : : }
907 : : }
8132 tgl@sss.pgh.pa.us 908 :CBC 20169 : PG_CATCH();
909 : : {
910 : : /* Uncaught error while executing portal: mark it dead */
5377 911 : 20169 : MarkPortalFailed(portal);
912 : :
913 : : /* Restore global vars and propagate error */
8067 914 [ + + ]: 20169 : if (saveMemoryContext == saveTopTransactionContext)
915 : 19888 : MemoryContextSwitchTo(TopTransactionContext);
916 : : else
917 : 281 : MemoryContextSwitchTo(saveMemoryContext);
8132 918 : 20169 : ActivePortal = saveActivePortal;
8067 919 [ + + ]: 20169 : if (saveResourceOwner == saveTopTransactionResourceOwner)
920 : 19941 : CurrentResourceOwner = TopTransactionResourceOwner;
921 : : else
922 : 228 : CurrentResourceOwner = saveResourceOwner;
8132 923 : 20169 : PortalContext = savePortalContext;
924 : :
925 : 20169 : PG_RE_THROW();
926 : : }
927 [ - + ]: 437991 : PG_END_TRY();
928 : :
8067 929 [ + + ]: 437991 : if (saveMemoryContext == saveTopTransactionContext)
930 : 413350 : MemoryContextSwitchTo(TopTransactionContext);
931 : : else
932 : 24641 : MemoryContextSwitchTo(saveMemoryContext);
8264 933 : 437991 : ActivePortal = saveActivePortal;
8067 934 [ + + ]: 437991 : if (saveResourceOwner == saveTopTransactionResourceOwner)
935 : 425422 : CurrentResourceOwner = TopTransactionResourceOwner;
936 : : else
937 : 12569 : CurrentResourceOwner = saveResourceOwner;
8588 938 : 437991 : PortalContext = savePortalContext;
939 : :
8280 bruce@momjian.us 940 [ - + - - ]: 437991 : if (log_executor_stats && portal->strategy != PORTAL_MULTI_QUERY)
8280 bruce@momjian.us 941 :UBC 0 : ShowUsage("EXECUTOR STATISTICS");
942 : :
943 : : TRACE_POSTGRESQL_QUERY_EXECUTE_DONE();
944 : :
8588 tgl@sss.pgh.pa.us 945 :CBC 437991 : return result;
946 : : }
947 : :
948 : : /*
949 : : * PortalRunSelect
950 : : * Execute a portal's query in PORTAL_ONE_SELECT mode, and also
951 : : * when fetching from a completed holdStore in PORTAL_ONE_RETURNING,
952 : : * PORTAL_ONE_MOD_WITH, and PORTAL_UTIL_SELECT cases.
953 : : *
954 : : * This handles simple N-rows-forward-or-backward cases. For more complex
955 : : * nonsequential access to a portal, see PortalRunFetch.
956 : : *
957 : : * count <= 0 is interpreted as a no-op: the destination gets started up
958 : : * and shut down, but nothing else happens. Also, count == FETCH_ALL is
959 : : * interpreted as "all rows". (cf FetchStmt.howMany)
960 : : *
961 : : * Caller must already have validated the Portal and done appropriate
962 : : * setup (cf. PortalRun).
963 : : *
964 : : * Returns number of rows processed (suitable for use in result tag)
965 : : */
966 : : static uint64
967 : 241251 : PortalRunSelect(Portal portal,
968 : : bool forward,
969 : : long count,
970 : : DestReceiver *dest)
971 : : {
972 : : QueryDesc *queryDesc;
973 : : ScanDirection direction;
974 : : uint64 nprocessed;
975 : :
976 : : /*
977 : : * NB: queryDesc will be NULL if we are fetching from a held cursor or a
978 : : * completed utility query; can't use it in that path.
979 : : */
3246 peter_e@gmx.net 980 : 241251 : queryDesc = portal->queryDesc;
981 : :
982 : : /* Caller messed up if we have neither a ready query nor held data. */
8588 tgl@sss.pgh.pa.us 983 [ + + - + ]: 241251 : Assert(queryDesc || portal->holdStore);
984 : :
985 : : /*
986 : : * Force the queryDesc destination to the right thing. This supports
987 : : * MOVE, for example, which will pass in dest = DestNone. This is okay to
988 : : * change as long as we do it on every fetch. (The Executor must not
989 : : * assume that dest never changes.)
990 : : */
991 [ + + ]: 241251 : if (queryDesc)
992 : 193629 : queryDesc->dest = dest;
993 : :
994 : : /*
995 : : * Determine which direction to go in, and check to see if we're already
996 : : * at the end of the available tuples in that direction. If so, set the
997 : : * direction to NoMovement to avoid trying to fetch any tuples. (This
998 : : * check exists because not all plan node types are robust about being
999 : : * called again if they've already returned NULL once.) Then call the
1000 : : * executor (we must not skip this, because the destination needs to see a
1001 : : * setup and shutdown even if no tuples are available). Finally, update
1002 : : * the portal position state depending on the number of tuples that were
1003 : : * retrieved.
1004 : : */
1005 [ + + ]: 241251 : if (forward)
1006 : : {
1007 [ + + + + ]: 240827 : if (portal->atEnd || count <= 0)
1008 : : {
1009 : 2498 : direction = NoMovementScanDirection;
3890 1010 : 2498 : count = 0; /* don't pass negative count to executor */
1011 : : }
1012 : : else
8588 1013 : 238329 : direction = ForwardScanDirection;
1014 : :
1015 : : /* In the executor, zero count processes all rows */
1016 [ + + ]: 240827 : if (count == FETCH_ALL)
1017 : 207293 : count = 0;
1018 : :
1019 [ + + ]: 240827 : if (portal->holdStore)
3890 1020 : 47610 : nprocessed = RunFromStore(portal, direction, (uint64) count, dest);
1021 : : else
1022 : : {
6751 alvherre@alvh.no-ip. 1023 : 193217 : PushActiveSnapshot(queryDesc->snapshot);
696 tgl@sss.pgh.pa.us 1024 : 193217 : ExecutorRun(queryDesc, direction, (uint64) count);
8588 1025 : 188045 : nprocessed = queryDesc->estate->es_processed;
6751 alvherre@alvh.no-ip. 1026 : 188045 : PopActiveSnapshot();
1027 : : }
1028 : :
7562 neilc@samurai.com 1029 [ + + ]: 235655 : if (!ScanDirectionIsNoMovement(direction))
1030 : : {
8588 tgl@sss.pgh.pa.us 1031 [ + + ]: 233157 : if (nprocessed > 0)
3424 1032 : 195012 : portal->atStart = false; /* OK to go backward now */
3890 1033 [ + + + + ]: 233157 : if (count == 0 || nprocessed < (uint64) count)
8494 bruce@momjian.us 1034 : 211401 : portal->atEnd = true; /* we retrieved 'em all */
8588 tgl@sss.pgh.pa.us 1035 : 233157 : portal->portalPos += nprocessed;
1036 : : }
1037 : : }
1038 : : else
1039 : : {
1040 [ + + ]: 424 : if (portal->cursorOptions & CURSOR_OPT_NO_SCROLL)
8507 1041 [ + - ]: 16 : ereport(ERROR,
1042 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1043 : : errmsg("cursor can only scan forward"),
1044 : : errhint("Declare it with SCROLL option to enable backward scan.")));
1045 : :
8588 1046 [ + + - + ]: 408 : if (portal->atStart || count <= 0)
1047 : : {
1048 : 48 : direction = NoMovementScanDirection;
3890 1049 : 48 : count = 0; /* don't pass negative count to executor */
1050 : : }
1051 : : else
8588 1052 : 360 : direction = BackwardScanDirection;
1053 : :
1054 : : /* In the executor, zero count processes all rows */
1055 [ + + ]: 408 : if (count == FETCH_ALL)
1056 : 42 : count = 0;
1057 : :
1058 [ + + ]: 408 : if (portal->holdStore)
3890 1059 : 8 : nprocessed = RunFromStore(portal, direction, (uint64) count, dest);
1060 : : else
1061 : : {
6751 alvherre@alvh.no-ip. 1062 : 400 : PushActiveSnapshot(queryDesc->snapshot);
696 tgl@sss.pgh.pa.us 1063 : 400 : ExecutorRun(queryDesc, direction, (uint64) count);
8588 1064 : 400 : nprocessed = queryDesc->estate->es_processed;
6751 alvherre@alvh.no-ip. 1065 : 400 : PopActiveSnapshot();
1066 : : }
1067 : :
7562 neilc@samurai.com 1068 [ + + ]: 408 : if (!ScanDirectionIsNoMovement(direction))
1069 : : {
8588 tgl@sss.pgh.pa.us 1070 [ + + + + ]: 360 : if (nprocessed > 0 && portal->atEnd)
1071 : : {
8494 bruce@momjian.us 1072 : 110 : portal->atEnd = false; /* OK to go forward now */
1073 : 110 : portal->portalPos++; /* adjust for endpoint case */
1074 : : }
3890 tgl@sss.pgh.pa.us 1075 [ + + + + ]: 360 : if (count == 0 || nprocessed < (uint64) count)
1076 : : {
8494 bruce@momjian.us 1077 : 126 : portal->atStart = true; /* we retrieved 'em all */
8588 tgl@sss.pgh.pa.us 1078 : 126 : portal->portalPos = 0;
1079 : : }
1080 : : else
1081 : : {
1082 : 234 : portal->portalPos -= nprocessed;
1083 : : }
1084 : : }
1085 : : }
1086 : :
1087 : 236063 : return nprocessed;
1088 : : }
1089 : :
1090 : : /*
1091 : : * FillPortalStore
1092 : : * Run the query and load result tuples into the portal's tuple store.
1093 : : *
1094 : : * This is used for PORTAL_ONE_RETURNING, PORTAL_ONE_MOD_WITH, and
1095 : : * PORTAL_UTIL_SELECT cases only.
1096 : : */
1097 : : static void
7177 1098 : 31941 : FillPortalStore(Portal portal, bool isTopLevel)
1099 : : {
1100 : : DestReceiver *treceiver;
1101 : : QueryCompletion qc;
1102 : :
2439 alvherre@alvh.no-ip. 1103 : 31941 : InitializeQueryCompletion(&qc);
7390 tgl@sss.pgh.pa.us 1104 : 31941 : PortalCreateHoldStore(portal);
6549 1105 : 31941 : treceiver = CreateDestReceiver(DestTuplestore);
1106 : 31941 : SetTuplestoreDestReceiverParams(treceiver,
1107 : : portal->holdStore,
1108 : : portal->holdContext,
1109 : : false,
1110 : : portal->tupDesc,
1111 : : gettext_noop("query result type does not match portal result type"));
1112 : :
7390 1113 [ + + - ]: 31941 : switch (portal->strategy)
1114 : : {
1115 : 2178 : case PORTAL_ONE_RETURNING:
1116 : : case PORTAL_ONE_MOD_WITH:
1117 : :
1118 : : /*
1119 : : * Run the portal to completion just as for the default
1120 : : * PORTAL_MULTI_QUERY case, but send the primary query's output to
1121 : : * the tuplestore. Auxiliary query outputs are discarded. Set the
1122 : : * portal's holdSnapshot to the snapshot used (or a copy of it).
1123 : : */
3742 1124 : 2178 : PortalRunMulti(portal, isTopLevel, true,
1125 : : treceiver, None_Receiver, &qc);
7390 1126 : 2039 : break;
1127 : :
1128 : 29763 : case PORTAL_UTIL_SELECT:
3496 1129 : 29763 : PortalRunUtility(portal, linitial_node(PlannedStmt, portal->stmts),
1130 : : isTopLevel, true, treceiver, &qc);
7390 1131 : 29555 : break;
1132 : :
7390 tgl@sss.pgh.pa.us 1133 :UBC 0 : default:
1134 [ # # ]: 0 : elog(ERROR, "unsupported portal strategy: %d",
1135 : : (int) portal->strategy);
1136 : : break;
1137 : : }
1138 : :
1139 : : /* Override portal completion data with actual command results */
2439 alvherre@alvh.no-ip. 1140 [ + + ]:CBC 31594 : if (qc.commandTag != CMDTAG_UNKNOWN)
1141 : 13491 : CopyQueryCompletion(&portal->qc, &qc);
1142 : :
3346 peter_e@gmx.net 1143 : 31594 : treceiver->rDestroy(treceiver);
7390 tgl@sss.pgh.pa.us 1144 : 31594 : }
1145 : :
1146 : : /*
1147 : : * RunFromStore
1148 : : * Fetch tuples from the portal's tuple store.
1149 : : *
1150 : : * Calling conventions are similar to ExecutorRun, except that we
1151 : : * do not depend on having a queryDesc or estate. Therefore we return the
1152 : : * number of tuples processed as the result, not in estate->es_processed.
1153 : : *
1154 : : * One difference from ExecutorRun is that the destination receiver functions
1155 : : * are run in the caller's memory context (since we have no estate). Watch
1156 : : * out for memory leaks.
1157 : : */
1158 : : static uint64
3890 1159 : 47618 : RunFromStore(Portal portal, ScanDirection direction, uint64 count,
1160 : : DestReceiver *dest)
1161 : : {
1162 : 47618 : uint64 current_tuple_count = 0;
1163 : : TupleTableSlot *slot;
1164 : :
2912 andres@anarazel.de 1165 : 47618 : slot = MakeSingleTupleTableSlot(portal->tupDesc, &TTSOpsMinimalTuple);
1166 : :
3346 peter_e@gmx.net 1167 : 47618 : dest->rStartup(dest, CMD_SELECT, portal->tupDesc);
1168 : :
7562 neilc@samurai.com 1169 [ + + ]: 47618 : if (ScanDirectionIsNoMovement(direction))
1170 : : {
1171 : : /* do nothing except start/stop the destination */
1172 : : }
1173 : : else
1174 : : {
1175 : 45860 : bool forward = ScanDirectionIsForward(direction);
1176 : :
1177 : : for (;;)
8588 tgl@sss.pgh.pa.us 1178 : 270183 : {
1179 : : MemoryContext oldcontext;
1180 : : bool ok;
1181 : :
1182 : 316043 : oldcontext = MemoryContextSwitchTo(portal->holdContext);
1183 : :
6432 1184 : 316043 : ok = tuplestore_gettupleslot(portal->holdStore, forward, false,
1185 : : slot);
1186 : :
8588 1187 : 316043 : MemoryContextSwitchTo(oldcontext);
1188 : :
7436 1189 [ + + ]: 316043 : if (!ok)
8588 1190 : 31626 : break;
1191 : :
1192 : : /*
1193 : : * If we are not able to send the tuple, we assume the destination
1194 : : * has closed and no more tuples can be sent. If that's the case,
1195 : : * end the loop.
1196 : : */
3346 peter_e@gmx.net 1197 [ - + ]: 284417 : if (!dest->receiveSlot(slot, dest))
3804 rhaas@postgresql.org 1198 :UBC 0 : break;
1199 : :
7904 tgl@sss.pgh.pa.us 1200 :CBC 284417 : ExecClearTuple(slot);
1201 : :
1202 : : /*
1203 : : * check our tuple count.. if we've processed the proper number
1204 : : * then quit, else loop again and process more tuples. Zero count
1205 : : * means no limit.
1206 : : */
8588 1207 : 284417 : current_tuple_count++;
1208 [ + + + + ]: 284417 : if (count && count == current_tuple_count)
1209 : 14234 : break;
1210 : : }
1211 : : }
1212 : :
3346 peter_e@gmx.net 1213 : 47618 : dest->rShutdown(dest);
1214 : :
7904 tgl@sss.pgh.pa.us 1215 : 47618 : ExecDropSingleTupleTableSlot(slot);
1216 : :
3890 1217 : 47618 : return current_tuple_count;
1218 : : }
1219 : :
1220 : : /*
1221 : : * PortalRunUtility
1222 : : * Execute a utility statement inside a portal.
1223 : : */
1224 : : static void
3582 1225 : 228732 : PortalRunUtility(Portal portal, PlannedStmt *pstmt,
1226 : : bool isTopLevel, bool setHoldSnapshot,
1227 : : DestReceiver *dest, QueryCompletion *qc)
1228 : : {
1229 : : /*
1230 : : * Set snapshot if utility stmt needs one.
1231 : : */
1994 1232 [ + + ]: 228732 : if (PlannedStmtRequiresSnapshot(pstmt))
1233 : : {
1234 : 178795 : Snapshot snapshot = GetTransactionSnapshot();
1235 : :
1236 : : /* If told to, register the snapshot we're using and save in portal */
3742 1237 [ + + ]: 178795 : if (setHoldSnapshot)
1238 : : {
1239 : 25741 : snapshot = RegisterSnapshot(snapshot);
1240 : 25741 : portal->holdSnapshot = snapshot;
1241 : : }
1242 : :
1243 : : /*
1244 : : * In any case, make the snapshot active and remember it in portal.
1245 : : * Because the portal now references the snapshot, we must tell
1246 : : * snapmgr.c that the snapshot belongs to the portal's transaction
1247 : : * level, else we risk portalSnapshot becoming a dangling pointer.
1248 : : */
1861 1249 : 178795 : PushActiveSnapshotWithLevel(snapshot, portal->createLevel);
1250 : : /* PushActiveSnapshotWithLevel might have copied the snapshot */
1994 1251 : 178795 : portal->portalSnapshot = GetActiveSnapshot();
1252 : : }
1253 : : else
1254 : 49937 : portal->portalSnapshot = NULL;
1255 : :
3582 1256 : 228732 : ProcessUtility(pstmt,
1257 : : portal->sourceText,
1966 1258 : 228732 : (portal->cplan != NULL), /* protect tree if in plancache */
3424 1259 : 228732 : isTopLevel ? PROCESS_UTILITY_TOPLEVEL : PROCESS_UTILITY_QUERY,
1260 : : portal->portalParams,
1261 : : portal->queryEnv,
1262 : : dest,
1263 : : qc);
1264 : :
1265 : : /* Some utility statements may change context on us */
3246 peter_e@gmx.net 1266 : 217378 : MemoryContextSwitchTo(portal->portalContext);
1267 : :
1268 : : /*
1269 : : * Some utility commands (e.g., VACUUM, WAIT FOR) pop the ActiveSnapshot
1270 : : * stack from under us, so don't complain if it's now empty. Otherwise,
1271 : : * our snapshot should be the top one; pop it. Note that this could be a
1272 : : * different snapshot from the one we made above; see
1273 : : * EnsurePortalSnapshotExists.
1274 : : */
1994 tgl@sss.pgh.pa.us 1275 [ + + + - ]: 217378 : if (portal->portalSnapshot != NULL && ActiveSnapshotSet())
1276 : : {
1277 [ - + ]: 161451 : Assert(portal->portalSnapshot == GetActiveSnapshot());
6751 alvherre@alvh.no-ip. 1278 : 161451 : PopActiveSnapshot();
1279 : : }
1994 tgl@sss.pgh.pa.us 1280 : 217378 : portal->portalSnapshot = NULL;
8588 1281 : 217378 : }
1282 : :
1283 : : /*
1284 : : * PortalRunMulti
1285 : : * Execute a portal's queries in the general case (multi queries
1286 : : * or non-SELECT-like queries)
1287 : : */
1288 : : static void
3742 1289 : 252926 : PortalRunMulti(Portal portal,
1290 : : bool isTopLevel, bool setHoldSnapshot,
1291 : : DestReceiver *dest, DestReceiver *altdest,
1292 : : QueryCompletion *qc)
1293 : : {
5729 1294 : 252926 : bool active_snapshot_set = false;
1295 : : ListCell *stmtlist_item;
1296 : :
1297 : : /*
1298 : : * If the destination is DestRemoteExecute, change to DestNone. The
1299 : : * reason is that the client won't be expecting any tuples, and indeed has
1300 : : * no way to know what they are, since there is no provision for Describe
1301 : : * to send a RowDescription message when this portal execution strategy is
1302 : : * in effect. This presently will only affect SELECT commands added to
1303 : : * non-SELECT queries by rewrite rules: such commands will be executed,
1304 : : * but the results will be discarded unless you use "simple Query"
1305 : : * protocol.
1306 : : */
7672 alvherre@alvh.no-ip. 1307 [ + + ]: 252926 : if (dest->mydest == DestRemoteExecute)
8584 tgl@sss.pgh.pa.us 1308 : 5175 : dest = None_Receiver;
7672 alvherre@alvh.no-ip. 1309 [ + + ]: 252926 : if (altdest->mydest == DestRemoteExecute)
8584 tgl@sss.pgh.pa.us 1310 : 5175 : altdest = None_Receiver;
1311 : :
1312 : : /*
1313 : : * Loop to handle the individual queries generated from a single parsetree
1314 : : * by analysis and rewrite.
1315 : : */
7198 1316 [ + - + + : 491442 : foreach(stmtlist_item, portal->stmts)
+ + ]
1317 : : {
3496 1318 : 253342 : PlannedStmt *pstmt = lfirst_node(PlannedStmt, stmtlist_item);
1319 : :
1320 : : /*
1321 : : * If we got a cancel signal in prior command, quit
1322 : : */
8588 1323 [ - + ]: 253342 : CHECK_FOR_INTERRUPTS();
1324 : :
3582 1325 [ + + ]: 253342 : if (pstmt->utilityStmt == NULL)
1326 : : {
1327 : : /*
1328 : : * process a plannable query.
1329 : : */
1330 : : TRACE_POSTGRESQL_QUERY_EXECUTE_START();
1331 : :
8588 1332 [ - + ]: 54373 : if (log_executor_stats)
8588 tgl@sss.pgh.pa.us 1333 :UBC 0 : ResetUsage();
1334 : :
1335 : : /*
1336 : : * Must always have a snapshot for plannable queries. First time
1337 : : * through, take a new snapshot; for subsequent queries in the
1338 : : * same portal, just update the snapshot's copy of the command
1339 : : * counter.
1340 : : */
5729 tgl@sss.pgh.pa.us 1341 [ + + ]:CBC 54373 : if (!active_snapshot_set)
1342 : : {
3742 1343 : 53957 : Snapshot snapshot = GetTransactionSnapshot();
1344 : :
1345 : : /* If told to, register the snapshot and save in portal */
1346 [ + + ]: 53957 : if (setHoldSnapshot)
1347 : : {
1348 : 2178 : snapshot = RegisterSnapshot(snapshot);
1349 : 2178 : portal->holdSnapshot = snapshot;
1350 : : }
1351 : :
1352 : : /*
1353 : : * We can't have the holdSnapshot also be the active one,
1354 : : * because UpdateActiveSnapshotCommandId would complain. So
1355 : : * force an extra snapshot copy. Plain PushActiveSnapshot
1356 : : * would have copied the transaction snapshot anyway, so this
1357 : : * only adds a copy step when setHoldSnapshot is true. (It's
1358 : : * okay for the command ID of the active snapshot to diverge
1359 : : * from what holdSnapshot has.)
1360 : : */
1361 : 53957 : PushCopiedSnapshot(snapshot);
1362 : :
1363 : : /*
1364 : : * As for PORTAL_ONE_SELECT portals, it does not seem
1365 : : * necessary to maintain portal->portalSnapshot here.
1366 : : */
1367 : :
5729 1368 : 53957 : active_snapshot_set = true;
1369 : : }
1370 : : else
1371 : 416 : UpdateActiveSnapshotCommandId();
1372 : :
7198 1373 [ + + ]: 54373 : if (pstmt->canSetTag)
1374 : : {
1375 : : /* statement can set tag string */
1376 : 53929 : ProcessQuery(pstmt,
1377 : : portal->sourceText,
1378 : : portal->portalParams,
1379 : : portal->queryEnv,
1380 : : dest, qc);
1381 : : }
1382 : : else
1383 : : {
1384 : : /* stmt added by rewrite cannot set tag */
1385 : 444 : ProcessQuery(pstmt,
1386 : : portal->sourceText,
1387 : : portal->portalParams,
1388 : : portal->queryEnv,
1389 : : altdest, NULL);
1390 : : }
1391 : :
8588 1392 [ - + ]: 50693 : if (log_executor_stats)
8588 tgl@sss.pgh.pa.us 1393 :UBC 0 : ShowUsage("EXECUTOR STATISTICS");
1394 : :
1395 : : TRACE_POSTGRESQL_QUERY_EXECUTE_DONE();
1396 : : }
1397 : : else
1398 : : {
1399 : : /*
1400 : : * process utility functions (create, destroy, etc..)
1401 : : *
1402 : : * We must not set a snapshot here for utility commands (if one is
1403 : : * needed, PortalRunUtility will do it). If a utility command is
1404 : : * alone in a portal then everything's fine. The only case where
1405 : : * a utility command can be part of a longer list is that rules
1406 : : * are allowed to include NotifyStmt. NotifyStmt doesn't care
1407 : : * whether it has a snapshot or not, so we just leave the current
1408 : : * snapshot alone if we have one.
1409 : : */
3582 tgl@sss.pgh.pa.us 1410 [ + - ]:CBC 198969 : if (pstmt->canSetTag)
1411 : : {
5729 1412 [ - + ]: 198969 : Assert(!active_snapshot_set);
1413 : : /* statement can set tag string */
3582 1414 : 198969 : PortalRunUtility(portal, pstmt, isTopLevel, false,
1415 : : dest, qc);
1416 : : }
1417 : : else
1418 : : {
3582 tgl@sss.pgh.pa.us 1419 [ # # ]:UBC 0 : Assert(IsA(pstmt->utilityStmt, NotifyStmt));
1420 : : /* stmt added by rewrite cannot set tag */
1421 : 0 : PortalRunUtility(portal, pstmt, isTopLevel, false,
1422 : : altdest, NULL);
1423 : : }
1424 : : }
1425 : :
1426 : : /*
1427 : : * Clear subsidiary contexts to recover temporary memory.
1428 : : */
3246 peter_e@gmx.net 1429 [ - + ]:CBC 238516 : Assert(portal->portalContext == CurrentMemoryContext);
1430 : :
1431 : 238516 : MemoryContextDeleteChildren(portal->portalContext);
1432 : :
1433 : : /*
1434 : : * Avoid crashing if portal->stmts has been reset. This can only
1435 : : * occur if a CALL or DO utility statement executed an internal
1436 : : * COMMIT/ROLLBACK (cf PortalReleaseCachedPlan). The CALL or DO must
1437 : : * have been the only statement in the portal, so there's nothing left
1438 : : * for us to do; but we don't want to dereference a now-dangling list
1439 : : * pointer.
1440 : : */
2101 tgl@sss.pgh.pa.us 1441 [ - + ]: 238516 : if (portal->stmts == NIL)
2101 tgl@sss.pgh.pa.us 1442 :UBC 0 : break;
1443 : :
1444 : : /*
1445 : : * Increment command counter between queries, but not after the last
1446 : : * one.
1447 : : */
2101 tgl@sss.pgh.pa.us 1448 [ + + ]:CBC 238516 : if (lnext(portal->stmts, stmtlist_item) != NULL)
1449 : 416 : CommandCounterIncrement();
1450 : : }
1451 : :
1452 : : /* Pop the snapshot if we pushed one. */
5729 1453 [ + + ]: 238100 : if (active_snapshot_set)
1454 : 50277 : PopActiveSnapshot();
1455 : :
1456 : : /*
1457 : : * If a command tag was requested and we did not fill in a run-time-
1458 : : * determined tag above, copy the parse-time tag from the Portal. (There
1459 : : * might not be any tag there either, in edge cases such as empty prepared
1460 : : * statements. That's OK.)
1461 : : */
476 alvherre@kurilemu.de 1462 [ + - ]: 238100 : if (qc &&
1463 [ + + ]: 238100 : qc->commandTag == CMDTAG_UNKNOWN &&
1464 [ + - ]: 180311 : portal->qc.commandTag != CMDTAG_UNKNOWN)
1465 : 180311 : CopyQueryCompletion(qc, &portal->qc);
8588 tgl@sss.pgh.pa.us 1466 : 238100 : }
1467 : :
1468 : : /*
1469 : : * PortalRunFetch
1470 : : * Variant form of PortalRun that supports SQL FETCH directions.
1471 : : *
1472 : : * Note: we presently assume that no callers of this want isTopLevel = true.
1473 : : *
1474 : : * count <= 0 is interpreted as a no-op: the destination gets started up
1475 : : * and shut down, but nothing else happens. Also, count == FETCH_ALL is
1476 : : * interpreted as "all rows". (cf FetchStmt.howMany)
1477 : : *
1478 : : * Returns number of rows processed (suitable for use in result tag)
1479 : : */
1480 : : uint64
1481 : 34130 : PortalRunFetch(Portal portal,
1482 : : FetchDirection fdirection,
1483 : : long count,
1484 : : DestReceiver *dest)
1485 : : {
1486 : : uint64 result;
1487 : : Portal saveActivePortal;
1488 : : ResourceOwner saveResourceOwner;
1489 : : MemoryContext savePortalContext;
1490 : : MemoryContext oldContext;
1491 : :
1469 peter@eisentraut.org 1492 [ - + ]: 34130 : Assert(PortalIsValid(portal));
1493 : :
1494 : : /*
1495 : : * Check for improper portal use, and mark portal active.
1496 : : */
4080 tgl@sss.pgh.pa.us 1497 : 34130 : MarkPortalActive(portal);
1498 : :
1499 : : /*
1500 : : * Set up global portal context pointers.
1501 : : */
8264 1502 : 34118 : saveActivePortal = ActivePortal;
8146 1503 : 34118 : saveResourceOwner = CurrentResourceOwner;
8588 1504 : 34118 : savePortalContext = PortalContext;
8132 1505 [ + + ]: 34118 : PG_TRY();
1506 : : {
1507 : 34118 : ActivePortal = portal;
4893 1508 [ + + ]: 34118 : if (portal->resowner)
1509 : 34011 : CurrentResourceOwner = portal->resowner;
3246 peter_e@gmx.net 1510 : 34118 : PortalContext = portal->portalContext;
1511 : :
8132 tgl@sss.pgh.pa.us 1512 : 34118 : oldContext = MemoryContextSwitchTo(PortalContext);
1513 : :
1514 [ + + - ]: 34118 : switch (portal->strategy)
1515 : : {
1516 : 12757 : case PORTAL_ONE_SELECT:
1517 : 12757 : result = DoPortalRunFetch(portal, fdirection, count, dest);
1518 : 12709 : break;
1519 : :
7390 1520 : 21361 : case PORTAL_ONE_RETURNING:
1521 : : case PORTAL_ONE_MOD_WITH:
1522 : : case PORTAL_UTIL_SELECT:
1523 : :
1524 : : /*
1525 : : * If we have not yet run the command, do so, storing its
1526 : : * results in the portal's tuplestore.
1527 : : */
1528 [ + + ]: 21361 : if (!portal->holdStore)
6930 bruce@momjian.us 1529 : 5440 : FillPortalStore(portal, false /* isTopLevel */ );
1530 : :
1531 : : /*
1532 : : * Now fetch desired portion of results.
1533 : : */
7938 tgl@sss.pgh.pa.us 1534 : 21357 : result = DoPortalRunFetch(portal, fdirection, count, dest);
1535 : 21357 : break;
1536 : :
8132 tgl@sss.pgh.pa.us 1537 :UBC 0 : default:
1538 [ # # ]: 0 : elog(ERROR, "unsupported portal strategy");
1539 : : result = 0; /* keep compiler quiet */
1540 : : break;
1541 : : }
1542 : : }
8132 tgl@sss.pgh.pa.us 1543 :CBC 52 : PG_CATCH();
1544 : : {
1545 : : /* Uncaught error while executing portal: mark it dead */
5377 1546 : 52 : MarkPortalFailed(portal);
1547 : :
1548 : : /* Restore global vars and propagate error */
8132 1549 : 52 : ActivePortal = saveActivePortal;
1550 : 52 : CurrentResourceOwner = saveResourceOwner;
1551 : 52 : PortalContext = savePortalContext;
1552 : :
1553 : 52 : PG_RE_THROW();
1554 : : }
1555 [ - + ]: 34066 : PG_END_TRY();
1556 : :
8588 1557 : 34066 : MemoryContextSwitchTo(oldContext);
1558 : :
1559 : : /* Mark portal not active */
8146 1560 : 34066 : portal->status = PORTAL_READY;
1561 : :
8264 1562 : 34066 : ActivePortal = saveActivePortal;
8146 1563 : 34066 : CurrentResourceOwner = saveResourceOwner;
8588 1564 : 34066 : PortalContext = savePortalContext;
1565 : :
1566 : 34066 : return result;
1567 : : }
1568 : :
1569 : : /*
1570 : : * DoPortalRunFetch
1571 : : * Guts of PortalRunFetch --- the portal context is already set up
1572 : : *
1573 : : * Here, count < 0 typically reverses the direction. Also, count == FETCH_ALL
1574 : : * is interpreted as "all rows". (cf FetchStmt.howMany)
1575 : : *
1576 : : * Returns number of rows processed (suitable for use in result tag)
1577 : : */
1578 : : static uint64
1579 : 34114 : DoPortalRunFetch(Portal portal,
1580 : : FetchDirection fdirection,
1581 : : long count,
1582 : : DestReceiver *dest)
1583 : : {
1584 : : bool forward;
1585 : :
7938 1586 [ + + + + : 34114 : Assert(portal->strategy == PORTAL_ONE_SELECT ||
+ - - + ]
1587 : : portal->strategy == PORTAL_ONE_RETURNING ||
1588 : : portal->strategy == PORTAL_ONE_MOD_WITH ||
1589 : : portal->strategy == PORTAL_UTIL_SELECT);
1590 : :
1591 : : /*
1592 : : * Note: we disallow backwards fetch (including re-fetch of current row)
1593 : : * for NO SCROLL cursors, but we interpret that very loosely: you can use
1594 : : * any of the FetchDirection options, so long as the end result is to move
1595 : : * forwards by at least one row. Currently it's sufficient to check for
1596 : : * NO SCROLL in DoPortalRewind() and in the forward == false path in
1597 : : * PortalRunSelect(); but someday we might prefer to account for that
1598 : : * restriction explicitly here.
1599 : : */
8588 1600 [ + + + + : 34114 : switch (fdirection)
- ]
1601 : : {
1602 : 33598 : case FETCH_FORWARD:
1603 [ + + ]: 33598 : if (count < 0)
1604 : : {
1605 : 2 : fdirection = FETCH_BACKWARD;
1606 : 2 : count = -count;
1607 : : }
1608 : : /* fall out of switch to share code with FETCH_BACKWARD */
1609 : 33598 : break;
1610 : 352 : case FETCH_BACKWARD:
1611 [ + + ]: 352 : if (count < 0)
1612 : : {
1613 : 1 : fdirection = FETCH_FORWARD;
1614 : 1 : count = -count;
1615 : : }
1616 : : /* fall out of switch to share code with FETCH_FORWARD */
1617 : 352 : break;
1618 : 105 : case FETCH_ABSOLUTE:
1619 [ + + ]: 105 : if (count > 0)
1620 : : {
1621 : : /*
1622 : : * Definition: Rewind to start, advance count-1 rows, return
1623 : : * next row (if any).
1624 : : *
1625 : : * In practice, if the goal is less than halfway back to the
1626 : : * start, it's better to scan from where we are.
1627 : : *
1628 : : * Also, if current portalPos is outside the range of "long",
1629 : : * do it the hard way to avoid possible overflow of the count
1630 : : * argument to PortalRunSelect. We must exclude exactly
1631 : : * LONG_MAX, as well, lest the count look like FETCH_ALL.
1632 : : *
1633 : : * In any case, we arrange to fetch the target row going
1634 : : * forwards.
1635 : : */
3890 1636 [ + + ]: 63 : if ((uint64) (count - 1) <= portal->portalPos / 2 ||
1637 [ - + ]: 25 : portal->portalPos >= (uint64) LONG_MAX)
1638 : : {
8588 1639 : 38 : DoPortalRewind(portal);
1640 [ - + ]: 34 : if (count > 1)
8494 bruce@momjian.us 1641 :UBC 0 : PortalRunSelect(portal, true, count - 1,
1642 : : None_Receiver);
1643 : : }
1644 : : else
1645 : : {
3890 tgl@sss.pgh.pa.us 1646 :CBC 25 : long pos = (long) portal->portalPos;
1647 : :
8588 1648 [ - + ]: 25 : if (portal->atEnd)
8588 tgl@sss.pgh.pa.us 1649 :UBC 0 : pos++; /* need one extra fetch if off end */
8588 tgl@sss.pgh.pa.us 1650 [ + + ]:CBC 25 : if (count <= pos)
8494 bruce@momjian.us 1651 : 8 : PortalRunSelect(portal, false, pos - count + 1,
1652 : : None_Receiver);
1653 [ + + ]: 17 : else if (count > pos + 1)
1654 : 8 : PortalRunSelect(portal, true, count - pos - 1,
1655 : : None_Receiver);
1656 : : }
8588 tgl@sss.pgh.pa.us 1657 : 55 : return PortalRunSelect(portal, true, 1L, dest);
1658 : : }
1659 [ + + ]: 42 : else if (count < 0)
1660 : : {
1661 : : /*
1662 : : * Definition: Advance to end, back up abs(count)-1 rows,
1663 : : * return prior row (if any). We could optimize this if we
1664 : : * knew in advance where the end was, but typically we won't.
1665 : : * (Is it worth considering case where count > half of size of
1666 : : * query? We could rewind once we know the size ...)
1667 : : */
8584 1668 : 38 : PortalRunSelect(portal, true, FETCH_ALL, None_Receiver);
8588 1669 [ - + ]: 38 : if (count < -1)
8494 bruce@momjian.us 1670 :UBC 0 : PortalRunSelect(portal, false, -count - 1, None_Receiver);
8588 tgl@sss.pgh.pa.us 1671 :CBC 38 : return PortalRunSelect(portal, false, 1L, dest);
1672 : : }
1673 : : else
1674 : : {
1675 : : /* count == 0 */
1676 : : /* Rewind to start, return zero rows */
1677 : 4 : DoPortalRewind(portal);
1678 : 4 : return PortalRunSelect(portal, true, 0L, dest);
1679 : : }
1680 : : break;
1681 : 59 : case FETCH_RELATIVE:
1682 [ + + ]: 59 : if (count > 0)
1683 : : {
1684 : : /*
1685 : : * Definition: advance count-1 rows, return next row (if any).
1686 : : */
1687 [ + + ]: 26 : if (count > 1)
8494 bruce@momjian.us 1688 : 17 : PortalRunSelect(portal, true, count - 1, None_Receiver);
8588 tgl@sss.pgh.pa.us 1689 : 26 : return PortalRunSelect(portal, true, 1L, dest);
1690 : : }
1691 [ + + ]: 33 : else if (count < 0)
1692 : : {
1693 : : /*
1694 : : * Definition: back up abs(count)-1 rows, return prior row (if
1695 : : * any).
1696 : : */
1697 [ + + ]: 21 : if (count < -1)
8494 bruce@momjian.us 1698 : 12 : PortalRunSelect(portal, false, -count - 1, None_Receiver);
8588 tgl@sss.pgh.pa.us 1699 : 21 : return PortalRunSelect(portal, false, 1L, dest);
1700 : : }
1701 : : else
1702 : : {
1703 : : /* count == 0 */
1704 : : /* Same as FETCH FORWARD 0, so fall out of switch */
1705 : 12 : fdirection = FETCH_FORWARD;
1706 : : }
1707 : 12 : break;
8588 tgl@sss.pgh.pa.us 1708 :UBC 0 : default:
8507 1709 [ # # ]: 0 : elog(ERROR, "bogus direction");
1710 : : break;
1711 : : }
1712 : :
1713 : : /*
1714 : : * Get here with fdirection == FETCH_FORWARD or FETCH_BACKWARD, and count
1715 : : * >= 0.
1716 : : */
8588 tgl@sss.pgh.pa.us 1717 :CBC 33962 : forward = (fdirection == FETCH_FORWARD);
1718 : :
1719 : : /*
1720 : : * Zero count means to re-fetch the current row, if any (per SQL)
1721 : : */
1722 [ + + ]: 33962 : if (count == 0)
1723 : : {
1724 : : bool on_row;
1725 : :
1726 : : /* Are we sitting on a row? */
1727 [ + - + - ]: 12 : on_row = (!portal->atStart && !portal->atEnd);
1728 : :
7672 alvherre@alvh.no-ip. 1729 [ - + ]: 12 : if (dest->mydest == DestNone)
1730 : : {
1731 : : /* MOVE 0 returns 0/1 based on if FETCH 0 would return a row */
3890 tgl@sss.pgh.pa.us 1732 :UBC 0 : return on_row ? 1 : 0;
1733 : : }
1734 : : else
1735 : : {
1736 : : /*
1737 : : * If we are sitting on a row, back up one so we can re-fetch it.
1738 : : * If we are not sitting on a row, we still have to start up and
1739 : : * shut down the executor so that the destination is initialized
1740 : : * and shut down correctly; so keep going. To PortalRunSelect,
1741 : : * count == 0 means we will retrieve no row.
1742 : : */
8588 tgl@sss.pgh.pa.us 1743 [ + - ]:CBC 12 : if (on_row)
1744 : : {
8584 1745 : 12 : PortalRunSelect(portal, false, 1L, None_Receiver);
1746 : : /* Set up to fetch one row forward */
8588 1747 : 8 : count = 1;
1748 : 8 : forward = true;
1749 : : }
1750 : : }
1751 : : }
1752 : :
1753 : : /*
1754 : : * Optimize MOVE BACKWARD ALL into a Rewind.
1755 : : */
7672 alvherre@alvh.no-ip. 1756 [ + + + + : 33958 : if (!forward && count == FETCH_ALL && dest->mydest == DestNone)
+ + ]
1757 : : {
3890 tgl@sss.pgh.pa.us 1758 : 20 : uint64 result = portal->portalPos;
1759 : :
8588 1760 [ + + + + ]: 20 : if (result > 0 && !portal->atEnd)
1761 : 4 : result--;
1762 : 20 : DoPortalRewind(portal);
1763 : 20 : return result;
1764 : : }
1765 : :
1766 : 33938 : return PortalRunSelect(portal, forward, count, dest);
1767 : : }
1768 : :
1769 : : /*
1770 : : * DoPortalRewind - rewind a Portal to starting point
1771 : : */
1772 : : static void
1773 : 62 : DoPortalRewind(Portal portal)
1774 : : {
1775 : : QueryDesc *queryDesc;
1776 : :
1777 : : /*
1778 : : * No work is needed if we've not advanced nor attempted to advance the
1779 : : * cursor (and we don't want to throw a NO SCROLL error in this case).
1780 : : */
1882 1781 [ + + + - ]: 62 : if (portal->atStart && !portal->atEnd)
1782 : 12 : return;
1783 : :
1784 : : /* Otherwise, cursor must allow scrolling */
1785 [ + + ]: 50 : if (portal->cursorOptions & CURSOR_OPT_NO_SCROLL)
1786 [ + - ]: 4 : ereport(ERROR,
1787 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1788 : : errmsg("cursor can only scan forward"),
1789 : : errhint("Declare it with SCROLL option to enable backward scan.")));
1790 : :
1791 : : /* Rewind holdStore, if we have one */
8588 1792 [ + + ]: 46 : if (portal->holdStore)
1793 : : {
1794 : : MemoryContext oldcontext;
1795 : :
1796 : 4 : oldcontext = MemoryContextSwitchTo(portal->holdContext);
1797 : 4 : tuplestore_rescan(portal->holdStore);
1798 : 4 : MemoryContextSwitchTo(oldcontext);
1799 : : }
1800 : :
1801 : : /* Rewind executor, if active */
3246 peter_e@gmx.net 1802 : 46 : queryDesc = portal->queryDesc;
4565 tgl@sss.pgh.pa.us 1803 [ + + ]: 46 : if (queryDesc)
1804 : : {
1805 : 42 : PushActiveSnapshot(queryDesc->snapshot);
1806 : 42 : ExecutorRewind(queryDesc);
1807 : 42 : PopActiveSnapshot();
1808 : : }
1809 : :
8588 1810 : 46 : portal->atStart = true;
1811 : 46 : portal->atEnd = false;
1812 : 46 : portal->portalPos = 0;
1813 : : }
1814 : :
1815 : : /*
1816 : : * PlannedStmtRequiresSnapshot - what it says on the tin
1817 : : */
1818 : : bool
1994 1819 : 306738 : PlannedStmtRequiresSnapshot(PlannedStmt *pstmt)
1820 : : {
1821 : 306738 : Node *utilityStmt = pstmt->utilityStmt;
1822 : :
1823 : : /* If it's not a utility statement, it definitely needs a snapshot */
1824 [ + + ]: 306738 : if (utilityStmt == NULL)
1825 : 60389 : return true;
1826 : :
1827 : : /*
1828 : : * Most utility statements need a snapshot, and the default presumption
1829 : : * about new ones should be that they do too. Hence, enumerate those that
1830 : : * do not need one.
1831 : : *
1832 : : * Transaction control, LOCK, and SET must *not* set a snapshot, since
1833 : : * they need to be executable at the start of a transaction-snapshot-mode
1834 : : * transaction without freezing a snapshot. By extension we allow SHOW
1835 : : * not to set a snapshot. The other stmts listed are just efficiency
1836 : : * hacks. Beware of listing anything that can modify the database --- if,
1837 : : * say, it has to update an index with expressions that invoke
1838 : : * user-defined functions, then it had better have a snapshot.
1839 : : */
1840 [ + + ]: 246349 : if (IsA(utilityStmt, TransactionStmt) ||
1841 [ + + ]: 219366 : IsA(utilityStmt, LockStmt) ||
1842 [ + + ]: 218765 : IsA(utilityStmt, VariableSetStmt) ||
1843 [ + + ]: 193712 : IsA(utilityStmt, VariableShowStmt) ||
1844 [ + + ]: 193158 : IsA(utilityStmt, ConstraintsSetStmt) ||
1845 : : /* efficiency hacks from here down */
1846 [ + + ]: 193087 : IsA(utilityStmt, FetchStmt) ||
1847 [ + + ]: 188708 : IsA(utilityStmt, ListenStmt) ||
1848 [ + + ]: 188656 : IsA(utilityStmt, NotifyStmt) ||
1849 [ + + ]: 188604 : IsA(utilityStmt, UnlistenStmt) ||
365 akorotkov@postgresql 1850 [ + + ]: 188542 : IsA(utilityStmt, CheckPointStmt) ||
1851 [ + + ]: 188067 : IsA(utilityStmt, WaitStmt))
1994 tgl@sss.pgh.pa.us 1852 : 58579 : return false;
1853 : :
1854 : 187770 : return true;
1855 : : }
1856 : :
1857 : : /*
1858 : : * EnsurePortalSnapshotExists - recreate Portal-level snapshot, if needed
1859 : : *
1860 : : * Generally, we will have an active snapshot whenever we are executing
1861 : : * inside a Portal, unless the Portal's query is one of the utility
1862 : : * statements exempted from that rule (see PlannedStmtRequiresSnapshot).
1863 : : * However, procedures and DO blocks can commit or abort the transaction,
1864 : : * and thereby destroy all snapshots. This function can be called to
1865 : : * re-establish the Portal-level snapshot when none exists.
1866 : : */
1867 : : void
1868 : 290424 : EnsurePortalSnapshotExists(void)
1869 : : {
1870 : : Portal portal;
1871 : :
1872 : : /*
1873 : : * Nothing to do if a snapshot is set. (We take it on faith that the
1874 : : * outermost active snapshot belongs to some Portal; or if there is no
1875 : : * Portal, it's somebody else's responsibility to manage things.)
1876 : : */
1877 [ + + ]: 290424 : if (ActiveSnapshotSet())
1878 : 288174 : return;
1879 : :
1880 : : /* Otherwise, we'd better have an active Portal */
1881 : 2250 : portal = ActivePortal;
1923 1882 [ - + ]: 2250 : if (unlikely(portal == NULL))
1923 tgl@sss.pgh.pa.us 1883 [ # # ]:UBC 0 : elog(ERROR, "cannot execute SQL without an outer snapshot or portal");
1994 tgl@sss.pgh.pa.us 1884 [ - + ]:CBC 2250 : Assert(portal->portalSnapshot == NULL);
1885 : :
1886 : : /*
1887 : : * Create a new snapshot, make it active, and remember it in portal.
1888 : : * Because the portal now references the snapshot, we must tell snapmgr.c
1889 : : * that the snapshot belongs to the portal's transaction level, else we
1890 : : * risk portalSnapshot becoming a dangling pointer.
1891 : : */
1861 1892 : 2250 : PushActiveSnapshotWithLevel(GetTransactionSnapshot(), portal->createLevel);
1893 : : /* PushActiveSnapshotWithLevel might have copied the snapshot */
1994 1894 : 2250 : portal->portalSnapshot = GetActiveSnapshot();
1895 : : }
|