]> granicus.if.org Git - postgresql/blob - src/backend/tcop/pquery.c
Modify processing of DECLARE CURSOR and EXPLAIN so that they can resolve the
[postgresql] / src / backend / tcop / pquery.c
1 /*-------------------------------------------------------------------------
2  *
3  * pquery.c
4  *        POSTGRES process query command code
5  *
6  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.116 2007/04/27 22:05:49 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15
16 #include "postgres.h"
17
18 #include "access/xact.h"
19 #include "commands/prepare.h"
20 #include "commands/trigger.h"
21 #include "miscadmin.h"
22 #include "tcop/pquery.h"
23 #include "tcop/tcopprot.h"
24 #include "tcop/utility.h"
25 #include "utils/memutils.h"
26
27
28 /*
29  * ActivePortal is the currently executing Portal (the most closely nested,
30  * if there are several).
31  */
32 Portal          ActivePortal = NULL;
33
34
35 static void ProcessQuery(PlannedStmt *plan,
36                          ParamListInfo params,
37                          DestReceiver *dest,
38                          char *completionTag);
39 static void FillPortalStore(Portal portal, bool isTopLevel);
40 static uint32 RunFromStore(Portal portal, ScanDirection direction, long count,
41                          DestReceiver *dest);
42 static long PortalRunSelect(Portal portal, bool forward, long count,
43                                 DestReceiver *dest);
44 static void PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
45                                  DestReceiver *dest, char *completionTag);
46 static void PortalRunMulti(Portal portal, bool isTopLevel,
47                            DestReceiver *dest, DestReceiver *altdest,
48                            char *completionTag);
49 static long DoPortalRunFetch(Portal portal,
50                                  FetchDirection fdirection,
51                                  long count,
52                                  DestReceiver *dest);
53 static void DoPortalRewind(Portal portal);
54
55
56 /*
57  * CreateQueryDesc
58  */
59 QueryDesc *
60 CreateQueryDesc(PlannedStmt *plannedstmt,
61                                 Snapshot snapshot,
62                                 Snapshot crosscheck_snapshot,
63                                 DestReceiver *dest,
64                                 ParamListInfo params,
65                                 bool doInstrument)
66 {
67         QueryDesc  *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
68
69         qd->operation = plannedstmt->commandType;       /* operation */
70         qd->plannedstmt = plannedstmt;                          /* plan */
71         qd->utilitystmt = plannedstmt->utilityStmt;     /* in case DECLARE CURSOR */
72         qd->snapshot = snapshot;                                        /* snapshot */
73         qd->crosscheck_snapshot = crosscheck_snapshot;          /* RI check snapshot */
74         qd->dest = dest;                        /* output dest */
75         qd->params = params;            /* parameter values passed into query */
76         qd->doInstrument = doInstrument;        /* instrumentation wanted? */
77
78         /* null these fields until set by ExecutorStart */
79         qd->tupDesc = NULL;
80         qd->estate = NULL;
81         qd->planstate = NULL;
82
83         return qd;
84 }
85
86 /*
87  * CreateUtilityQueryDesc
88  */
89 QueryDesc *
90 CreateUtilityQueryDesc(Node *utilitystmt,
91                                            Snapshot snapshot,
92                                            DestReceiver *dest,
93                                            ParamListInfo params)
94 {
95         QueryDesc  *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
96
97         qd->operation = CMD_UTILITY;                            /* operation */
98         qd->plannedstmt = NULL;
99         qd->utilitystmt = utilitystmt;                          /* utility command */
100         qd->snapshot = snapshot;                                        /* snapshot */
101         qd->crosscheck_snapshot = InvalidSnapshot;      /* RI check snapshot */
102         qd->dest = dest;                        /* output dest */
103         qd->params = params;            /* parameter values passed into query */
104         qd->doInstrument = false;       /* uninteresting for utilities */
105
106         /* null these fields until set by ExecutorStart */
107         qd->tupDesc = NULL;
108         qd->estate = NULL;
109         qd->planstate = NULL;
110
111         return qd;
112 }
113
114 /*
115  * FreeQueryDesc
116  */
117 void
118 FreeQueryDesc(QueryDesc *qdesc)
119 {
120         /* Can't be a live query */
121         Assert(qdesc->estate == NULL);
122         /* Only the QueryDesc itself need be freed */
123         pfree(qdesc);
124 }
125
126
127 /*
128  * ProcessQuery
129  *              Execute a single plannable query within a PORTAL_MULTI_QUERY
130  *              or PORTAL_ONE_RETURNING portal
131  *
132  *      plan: the plan tree for the query
133  *      params: any parameters needed
134  *      dest: where to send results
135  *      completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
136  *              in which to store a command completion status string.
137  *
138  * completionTag may be NULL if caller doesn't want a status string.
139  *
140  * Must be called in a memory context that will be reset or deleted on
141  * error; otherwise the executor's memory usage will be leaked.
142  */
143 static void
144 ProcessQuery(PlannedStmt *plan,
145                          ParamListInfo params,
146                          DestReceiver *dest,
147                          char *completionTag)
148 {
149         QueryDesc  *queryDesc;
150
151         elog(DEBUG3, "ProcessQuery");
152
153         /*
154          * Must always set snapshot for plannable queries.      Note we assume that
155          * caller will take care of restoring ActiveSnapshot on exit/error.
156          */
157         ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
158
159         /*
160          * Create the QueryDesc object
161          */
162         queryDesc = CreateQueryDesc(plan,
163                                                                 ActiveSnapshot, InvalidSnapshot,
164                                                                 dest, params, false);
165
166         /*
167          * Set up to collect AFTER triggers
168          */
169         AfterTriggerBeginQuery();
170
171         /*
172          * Call ExecutorStart to prepare the plan for execution
173          */
174         ExecutorStart(queryDesc, 0);
175
176         /*
177          * Run the plan to completion.
178          */
179         ExecutorRun(queryDesc, ForwardScanDirection, 0L);
180
181         /*
182          * Build command completion status string, if caller wants one.
183          */
184         if (completionTag)
185         {
186                 Oid                     lastOid;
187
188                 switch (queryDesc->operation)
189                 {
190                         case CMD_SELECT:
191                                 strcpy(completionTag, "SELECT");
192                                 break;
193                         case CMD_INSERT:
194                                 if (queryDesc->estate->es_processed == 1)
195                                         lastOid = queryDesc->estate->es_lastoid;
196                                 else
197                                         lastOid = InvalidOid;
198                                 snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
199                                    "INSERT %u %u", lastOid, queryDesc->estate->es_processed);
200                                 break;
201                         case CMD_UPDATE:
202                                 snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
203                                                  "UPDATE %u", queryDesc->estate->es_processed);
204                                 break;
205                         case CMD_DELETE:
206                                 snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
207                                                  "DELETE %u", queryDesc->estate->es_processed);
208                                 break;
209                         default:
210                                 strcpy(completionTag, "???");
211                                 break;
212                 }
213         }
214
215         /* Now take care of any queued AFTER triggers */
216         AfterTriggerEndQuery(queryDesc->estate);
217
218         /*
219          * Now, we close down all the scans and free allocated resources.
220          */
221         ExecutorEnd(queryDesc);
222
223         FreeQueryDesc(queryDesc);
224
225         FreeSnapshot(ActiveSnapshot);
226         ActiveSnapshot = NULL;
227 }
228
229 /*
230  * ChoosePortalStrategy
231  *              Select portal execution strategy given the intended statement list.
232  *
233  * The list elements can be Querys, PlannedStmts, or utility statements.
234  * That's more general than portals need, but plancache.c uses this too.
235  *
236  * See the comments in portal.h.
237  */
238 PortalStrategy
239 ChoosePortalStrategy(List *stmts)
240 {
241         int                     nSetTag;
242         ListCell   *lc;
243
244         /*
245          * PORTAL_ONE_SELECT and PORTAL_UTIL_SELECT need only consider the
246          * single-statement case, since there are no rewrite rules that can add
247          * auxiliary queries to a SELECT or a utility command.
248          */
249         if (list_length(stmts) == 1)
250         {
251                 Node       *stmt = (Node *) linitial(stmts);
252
253                 if (IsA(stmt, Query))
254                 {
255                         Query      *query = (Query *) stmt;
256
257                         if (query->canSetTag)
258                         {
259                                 if (query->commandType == CMD_SELECT &&
260                                         query->utilityStmt == NULL &&
261                                         query->intoClause == NULL)
262                                         return PORTAL_ONE_SELECT;
263                                 if (query->commandType == CMD_UTILITY &&
264                                         query->utilityStmt != NULL)
265                                 {
266                                         if (UtilityReturnsTuples(query->utilityStmt))
267                                                 return PORTAL_UTIL_SELECT;
268                                         /* it can't be ONE_RETURNING, so give up */
269                                         return PORTAL_MULTI_QUERY;
270                                 }
271                         }
272                 }
273                 else if (IsA(stmt, PlannedStmt))
274                 {
275                         PlannedStmt *pstmt = (PlannedStmt *) stmt;
276
277                         if (pstmt->canSetTag)
278                         {
279                                 if (pstmt->commandType == CMD_SELECT &&
280                                         pstmt->utilityStmt == NULL &&
281                                         pstmt->intoClause == NULL)
282                                         return PORTAL_ONE_SELECT;
283                         }
284                 }
285                 else
286                 {
287                         /* must be a utility command; assume it's canSetTag */
288                         if (UtilityReturnsTuples(stmt))
289                                 return PORTAL_UTIL_SELECT;
290                         /* it can't be ONE_RETURNING, so give up */
291                         return PORTAL_MULTI_QUERY;
292                 }
293         }
294
295         /*
296          * PORTAL_ONE_RETURNING has to allow auxiliary queries added by rewrite.
297          * Choose PORTAL_ONE_RETURNING if there is exactly one canSetTag query and
298          * it has a RETURNING list.
299          */
300         nSetTag = 0;
301         foreach(lc, stmts)
302         {
303                 Node       *stmt = (Node *) lfirst(lc);
304
305                 if (IsA(stmt, Query))
306                 {
307                         Query      *query = (Query *) stmt;
308
309                         if (query->canSetTag)
310                         {
311                                 if (++nSetTag > 1)
312                                         return PORTAL_MULTI_QUERY;      /* no need to look further */
313                                 if (query->returningList == NIL)
314                                         return PORTAL_MULTI_QUERY;      /* no need to look further */
315                         }
316                 }
317                 else if (IsA(stmt, PlannedStmt))
318                 {
319                         PlannedStmt *pstmt = (PlannedStmt *) stmt;
320
321                         if (pstmt->canSetTag)
322                         {
323                                 if (++nSetTag > 1)
324                                         return PORTAL_MULTI_QUERY;      /* no need to look further */
325                                 if (pstmt->returningLists == NIL)
326                                         return PORTAL_MULTI_QUERY;      /* no need to look further */
327                         }
328                 }
329                 /* otherwise, utility command, assumed not canSetTag */
330         }
331         if (nSetTag == 1)
332                 return PORTAL_ONE_RETURNING;
333
334         /* Else, it's the general case... */
335         return PORTAL_MULTI_QUERY;
336 }
337
338 /*
339  * FetchPortalTargetList
340  *              Given a portal that returns tuples, extract the query targetlist.
341  *              Returns NIL if the portal doesn't have a determinable targetlist.
342  *
343  * Note: do not modify the result.
344  */
345 List *
346 FetchPortalTargetList(Portal portal)
347 {
348         /* no point in looking if we determined it doesn't return tuples */
349         if (portal->strategy == PORTAL_MULTI_QUERY)
350                 return NIL;
351         /* get the primary statement and find out what it returns */
352         return FetchStatementTargetList(PortalGetPrimaryStmt(portal));
353 }
354
355 /*
356  * FetchStatementTargetList
357  *              Given a statement that returns tuples, extract the query targetlist.
358  *              Returns NIL if the statement doesn't have a determinable targetlist.
359  *
360  * This can be applied to a Query, a PlannedStmt, or a utility statement.
361  * That's more general than portals need, but plancache.c uses this too.
362  *
363  * Note: do not modify the result.
364  *
365  * XXX be careful to keep this in sync with UtilityReturnsTuples.
366  */
367 List *
368 FetchStatementTargetList(Node *stmt)
369 {
370         if (stmt == NULL)
371                 return NIL;
372         if (IsA(stmt, Query))
373         {
374                 Query      *query = (Query *) stmt;
375
376                 if (query->commandType == CMD_UTILITY &&
377                         query->utilityStmt != NULL)
378                 {
379                         /* transfer attention to utility statement */
380                         stmt = query->utilityStmt;
381                 }
382                 else
383                 {
384                         if (query->commandType == CMD_SELECT &&
385                                 query->utilityStmt == NULL &&
386                                 query->intoClause == NULL)
387                                 return query->targetList;
388                         if (query->returningList)
389                                 return query->returningList;
390                         return NIL;
391                 }
392         }
393         if (IsA(stmt, PlannedStmt))
394         {
395                 PlannedStmt *pstmt = (PlannedStmt *) stmt;
396
397                 if (pstmt->commandType == CMD_SELECT &&
398                         pstmt->utilityStmt == NULL &&
399                         pstmt->intoClause == NULL)
400                         return pstmt->planTree->targetlist;
401                 if (pstmt->returningLists)
402                         return (List *) linitial(pstmt->returningLists);
403                 return NIL;
404         }
405         if (IsA(stmt, FetchStmt))
406         {
407                 FetchStmt  *fstmt = (FetchStmt *) stmt;
408                 Portal          subportal;
409
410                 Assert(!fstmt->ismove);
411                 subportal = GetPortalByName(fstmt->portalname);
412                 Assert(PortalIsValid(subportal));
413                 return FetchPortalTargetList(subportal);
414         }
415         if (IsA(stmt, ExecuteStmt))
416         {
417                 ExecuteStmt *estmt = (ExecuteStmt *) stmt;
418                 PreparedStatement *entry;
419
420                 Assert(!estmt->into);
421                 entry = FetchPreparedStatement(estmt->name, true);
422                 return FetchPreparedStatementTargetList(entry);
423         }
424         return NIL;
425 }
426
427 /*
428  * PortalStart
429  *              Prepare a portal for execution.
430  *
431  * Caller must already have created the portal, done PortalDefineQuery(),
432  * and adjusted portal options if needed.  If parameters are needed by
433  * the query, they must be passed in here (caller is responsible for
434  * giving them appropriate lifetime).
435  *
436  * The caller can optionally pass a snapshot to be used; pass InvalidSnapshot
437  * for the normal behavior of setting a new snapshot.  This parameter is
438  * presently ignored for non-PORTAL_ONE_SELECT portals (it's only intended
439  * to be used for cursors).
440  *
441  * On return, portal is ready to accept PortalRun() calls, and the result
442  * tupdesc (if any) is known.
443  */
444 void
445 PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
446 {
447         Portal          saveActivePortal;
448         Snapshot        saveActiveSnapshot;
449         ResourceOwner saveResourceOwner;
450         MemoryContext savePortalContext;
451         MemoryContext oldContext;
452         QueryDesc  *queryDesc;
453         int                     eflags;
454
455         AssertArg(PortalIsValid(portal));
456         AssertState(portal->status == PORTAL_DEFINED);
457
458         /*
459          * Set up global portal context pointers.
460          */
461         saveActivePortal = ActivePortal;
462         saveActiveSnapshot = ActiveSnapshot;
463         saveResourceOwner = CurrentResourceOwner;
464         savePortalContext = PortalContext;
465         PG_TRY();
466         {
467                 ActivePortal = portal;
468                 ActiveSnapshot = NULL;  /* will be set later */
469                 CurrentResourceOwner = portal->resowner;
470                 PortalContext = PortalGetHeapMemory(portal);
471
472                 oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
473
474                 /* Must remember portal param list, if any */
475                 portal->portalParams = params;
476
477                 /*
478                  * Determine the portal execution strategy
479                  */
480                 portal->strategy = ChoosePortalStrategy(portal->stmts);
481
482                 /*
483                  * Fire her up according to the strategy
484                  */
485                 switch (portal->strategy)
486                 {
487                         case PORTAL_ONE_SELECT:
488
489                                 /*
490                                  * Must set snapshot before starting executor.  Be sure to
491                                  * copy it into the portal's context.
492                                  */
493                                 if (snapshot)
494                                         ActiveSnapshot = CopySnapshot(snapshot);
495                                 else
496                                         ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
497
498                                 /*
499                                  * Create QueryDesc in portal's context; for the moment, set
500                                  * the destination to DestNone.
501                                  */
502                                 queryDesc = CreateQueryDesc((PlannedStmt *) linitial(portal->stmts),
503                                                                                         ActiveSnapshot,
504                                                                                         InvalidSnapshot,
505                                                                                         None_Receiver,
506                                                                                         params,
507                                                                                         false);
508
509                                 /*
510                                  * We do *not* call AfterTriggerBeginQuery() here.      We assume
511                                  * that a SELECT cannot queue any triggers.  It would be messy
512                                  * to support triggers since the execution of the portal may
513                                  * be interleaved with other queries.
514                                  */
515
516                                 /*
517                                  * If it's a scrollable cursor, executor needs to support
518                                  * REWIND and backwards scan.
519                                  */
520                                 if (portal->cursorOptions & CURSOR_OPT_SCROLL)
521                                         eflags = EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD;
522                                 else
523                                         eflags = 0; /* default run-to-completion flags */
524
525                                 /*
526                                  * Call ExecutorStart to prepare the plan for execution
527                                  */
528                                 ExecutorStart(queryDesc, eflags);
529
530                                 /*
531                                  * This tells PortalCleanup to shut down the executor
532                                  */
533                                 portal->queryDesc = queryDesc;
534
535                                 /*
536                                  * Remember tuple descriptor (computed by ExecutorStart)
537                                  */
538                                 portal->tupDesc = queryDesc->tupDesc;
539
540                                 /*
541                                  * Reset cursor position data to "start of query"
542                                  */
543                                 portal->atStart = true;
544                                 portal->atEnd = false;  /* allow fetches */
545                                 portal->portalPos = 0;
546                                 portal->posOverflow = false;
547                                 break;
548
549                         case PORTAL_ONE_RETURNING:
550
551                                 /*
552                                  * We don't start the executor until we are told to run the
553                                  * portal.      We do need to set up the result tupdesc.
554                                  */
555                                 {
556                                         PlannedStmt *pstmt;
557
558                                         pstmt = (PlannedStmt *) PortalGetPrimaryStmt(portal);
559                                         Assert(IsA(pstmt, PlannedStmt));
560                                         Assert(pstmt->returningLists);
561                                         portal->tupDesc =
562                                                 ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists),
563                                                                                         false);
564                                 }
565
566                                 /*
567                                  * Reset cursor position data to "start of query"
568                                  */
569                                 portal->atStart = true;
570                                 portal->atEnd = false;  /* allow fetches */
571                                 portal->portalPos = 0;
572                                 portal->posOverflow = false;
573                                 break;
574
575                         case PORTAL_UTIL_SELECT:
576
577                                 /*
578                                  * We don't set snapshot here, because PortalRunUtility will
579                                  * take care of it if needed.
580                                  */
581                                 {
582                                         Node *ustmt = PortalGetPrimaryStmt(portal);
583
584                                         Assert(!IsA(ustmt, PlannedStmt));
585                                         portal->tupDesc = UtilityTupleDescriptor(ustmt);
586                                 }
587
588                                 /*
589                                  * Reset cursor position data to "start of query"
590                                  */
591                                 portal->atStart = true;
592                                 portal->atEnd = false;  /* allow fetches */
593                                 portal->portalPos = 0;
594                                 portal->posOverflow = false;
595                                 break;
596
597                         case PORTAL_MULTI_QUERY:
598                                 /* Need do nothing now */
599                                 portal->tupDesc = NULL;
600                                 break;
601                 }
602         }
603         PG_CATCH();
604         {
605                 /* Uncaught error while executing portal: mark it dead */
606                 portal->status = PORTAL_FAILED;
607
608                 /* Restore global vars and propagate error */
609                 ActivePortal = saveActivePortal;
610                 ActiveSnapshot = saveActiveSnapshot;
611                 CurrentResourceOwner = saveResourceOwner;
612                 PortalContext = savePortalContext;
613
614                 PG_RE_THROW();
615         }
616         PG_END_TRY();
617
618         MemoryContextSwitchTo(oldContext);
619
620         ActivePortal = saveActivePortal;
621         ActiveSnapshot = saveActiveSnapshot;
622         CurrentResourceOwner = saveResourceOwner;
623         PortalContext = savePortalContext;
624
625         portal->status = PORTAL_READY;
626 }
627
628 /*
629  * PortalSetResultFormat
630  *              Select the format codes for a portal's output.
631  *
632  * This must be run after PortalStart for a portal that will be read by
633  * a DestRemote or DestRemoteExecute destination.  It is not presently needed
634  * for other destination types.
635  *
636  * formats[] is the client format request, as per Bind message conventions.
637  */
638 void
639 PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
640 {
641         int                     natts;
642         int                     i;
643
644         /* Do nothing if portal won't return tuples */
645         if (portal->tupDesc == NULL)
646                 return;
647         natts = portal->tupDesc->natts;
648         portal->formats = (int16 *)
649                 MemoryContextAlloc(PortalGetHeapMemory(portal),
650                                                    natts * sizeof(int16));
651         if (nFormats > 1)
652         {
653                 /* format specified for each column */
654                 if (nFormats != natts)
655                         ereport(ERROR,
656                                         (errcode(ERRCODE_PROTOCOL_VIOLATION),
657                                          errmsg("bind message has %d result formats but query has %d columns",
658                                                         nFormats, natts)));
659                 memcpy(portal->formats, formats, natts * sizeof(int16));
660         }
661         else if (nFormats > 0)
662         {
663                 /* single format specified, use for all columns */
664                 int16           format1 = formats[0];
665
666                 for (i = 0; i < natts; i++)
667                         portal->formats[i] = format1;
668         }
669         else
670         {
671                 /* use default format for all columns */
672                 for (i = 0; i < natts; i++)
673                         portal->formats[i] = 0;
674         }
675 }
676
677 /*
678  * PortalRun
679  *              Run a portal's query or queries.
680  *
681  * count <= 0 is interpreted as a no-op: the destination gets started up
682  * and shut down, but nothing else happens.  Also, count == FETCH_ALL is
683  * interpreted as "all rows".  Note that count is ignored in multi-query
684  * situations, where we always run the portal to completion.
685  *
686  * isTopLevel: true if query is being executed at backend "top level"
687  * (that is, directly from a client command message)
688  *
689  * dest: where to send output of primary (canSetTag) query
690  *
691  * altdest: where to send output of non-primary queries
692  *
693  * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
694  *              in which to store a command completion status string.
695  *              May be NULL if caller doesn't want a status string.
696  *
697  * Returns TRUE if the portal's execution is complete, FALSE if it was
698  * suspended due to exhaustion of the count parameter.
699  */
700 bool
701 PortalRun(Portal portal, long count, bool isTopLevel,
702                   DestReceiver *dest, DestReceiver *altdest,
703                   char *completionTag)
704 {
705         bool            result;
706         ResourceOwner saveTopTransactionResourceOwner;
707         MemoryContext saveTopTransactionContext;
708         Portal          saveActivePortal;
709         Snapshot        saveActiveSnapshot;
710         ResourceOwner saveResourceOwner;
711         MemoryContext savePortalContext;
712         MemoryContext saveMemoryContext;
713
714         AssertArg(PortalIsValid(portal));
715
716         /* Initialize completion tag to empty string */
717         if (completionTag)
718                 completionTag[0] = '\0';
719
720         if (log_executor_stats && portal->strategy != PORTAL_MULTI_QUERY)
721         {
722                 elog(DEBUG3, "PortalRun");
723                 /* PORTAL_MULTI_QUERY logs its own stats per query */
724                 ResetUsage();
725         }
726
727         /*
728          * Check for improper portal use, and mark portal active.
729          */
730         if (portal->status != PORTAL_READY)
731                 ereport(ERROR,
732                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
733                                  errmsg("portal \"%s\" cannot be run", portal->name)));
734         portal->status = PORTAL_ACTIVE;
735
736         /*
737          * Set up global portal context pointers.
738          *
739          * We have to play a special game here to support utility commands like
740          * VACUUM and CLUSTER, which internally start and commit transactions.
741          * When we are called to execute such a command, CurrentResourceOwner will
742          * be pointing to the TopTransactionResourceOwner --- which will be
743          * destroyed and replaced in the course of the internal commit and
744          * restart.  So we need to be prepared to restore it as pointing to the
745          * exit-time TopTransactionResourceOwner.  (Ain't that ugly?  This idea of
746          * internally starting whole new transactions is not good.)
747          * CurrentMemoryContext has a similar problem, but the other pointers we
748          * save here will be NULL or pointing to longer-lived objects.
749          */
750         saveTopTransactionResourceOwner = TopTransactionResourceOwner;
751         saveTopTransactionContext = TopTransactionContext;
752         saveActivePortal = ActivePortal;
753         saveActiveSnapshot = ActiveSnapshot;
754         saveResourceOwner = CurrentResourceOwner;
755         savePortalContext = PortalContext;
756         saveMemoryContext = CurrentMemoryContext;
757         PG_TRY();
758         {
759                 ActivePortal = portal;
760                 ActiveSnapshot = NULL;  /* will be set later */
761                 CurrentResourceOwner = portal->resowner;
762                 PortalContext = PortalGetHeapMemory(portal);
763
764                 MemoryContextSwitchTo(PortalContext);
765
766                 switch (portal->strategy)
767                 {
768                         case PORTAL_ONE_SELECT:
769                                 (void) PortalRunSelect(portal, true, count, dest);
770
771                                 /* we know the query is supposed to set the tag */
772                                 if (completionTag && portal->commandTag)
773                                         strcpy(completionTag, portal->commandTag);
774
775                                 /* Mark portal not active */
776                                 portal->status = PORTAL_READY;
777
778                                 /*
779                                  * Since it's a forward fetch, say DONE iff atEnd is now true.
780                                  */
781                                 result = portal->atEnd;
782                                 break;
783
784                         case PORTAL_ONE_RETURNING:
785                         case PORTAL_UTIL_SELECT:
786
787                                 /*
788                                  * If we have not yet run the command, do so, storing its
789                                  * results in the portal's tuplestore.
790                                  */
791                                 if (!portal->holdStore)
792                                         FillPortalStore(portal, isTopLevel);
793
794                                 /*
795                                  * Now fetch desired portion of results.
796                                  */
797                                 (void) PortalRunSelect(portal, true, count, dest);
798
799                                 /* we know the query is supposed to set the tag */
800                                 if (completionTag && portal->commandTag)
801                                         strcpy(completionTag, portal->commandTag);
802
803                                 /* Mark portal not active */
804                                 portal->status = PORTAL_READY;
805
806                                 /*
807                                  * Since it's a forward fetch, say DONE iff atEnd is now true.
808                                  */
809                                 result = portal->atEnd;
810                                 break;
811
812                         case PORTAL_MULTI_QUERY:
813                                 PortalRunMulti(portal, isTopLevel,
814                                                            dest, altdest, completionTag);
815
816                                 /* Prevent portal's commands from being re-executed */
817                                 portal->status = PORTAL_DONE;
818
819                                 /* Always complete at end of RunMulti */
820                                 result = true;
821                                 break;
822
823                         default:
824                                 elog(ERROR, "unrecognized portal strategy: %d",
825                                          (int) portal->strategy);
826                                 result = false; /* keep compiler quiet */
827                                 break;
828                 }
829         }
830         PG_CATCH();
831         {
832                 /* Uncaught error while executing portal: mark it dead */
833                 portal->status = PORTAL_FAILED;
834
835                 /* Restore global vars and propagate error */
836                 if (saveMemoryContext == saveTopTransactionContext)
837                         MemoryContextSwitchTo(TopTransactionContext);
838                 else
839                         MemoryContextSwitchTo(saveMemoryContext);
840                 ActivePortal = saveActivePortal;
841                 ActiveSnapshot = saveActiveSnapshot;
842                 if (saveResourceOwner == saveTopTransactionResourceOwner)
843                         CurrentResourceOwner = TopTransactionResourceOwner;
844                 else
845                         CurrentResourceOwner = saveResourceOwner;
846                 PortalContext = savePortalContext;
847
848                 PG_RE_THROW();
849         }
850         PG_END_TRY();
851
852         if (saveMemoryContext == saveTopTransactionContext)
853                 MemoryContextSwitchTo(TopTransactionContext);
854         else
855                 MemoryContextSwitchTo(saveMemoryContext);
856         ActivePortal = saveActivePortal;
857         ActiveSnapshot = saveActiveSnapshot;
858         if (saveResourceOwner == saveTopTransactionResourceOwner)
859                 CurrentResourceOwner = TopTransactionResourceOwner;
860         else
861                 CurrentResourceOwner = saveResourceOwner;
862         PortalContext = savePortalContext;
863
864         if (log_executor_stats && portal->strategy != PORTAL_MULTI_QUERY)
865                 ShowUsage("EXECUTOR STATISTICS");
866
867         return result;
868 }
869
870 /*
871  * PortalRunSelect
872  *              Execute a portal's query in PORTAL_ONE_SELECT mode, and also
873  *              when fetching from a completed holdStore in PORTAL_ONE_RETURNING
874  *              and PORTAL_UTIL_SELECT cases.
875  *
876  * This handles simple N-rows-forward-or-backward cases.  For more complex
877  * nonsequential access to a portal, see PortalRunFetch.
878  *
879  * count <= 0 is interpreted as a no-op: the destination gets started up
880  * and shut down, but nothing else happens.  Also, count == FETCH_ALL is
881  * interpreted as "all rows".
882  *
883  * Caller must already have validated the Portal and done appropriate
884  * setup (cf. PortalRun).
885  *
886  * Returns number of rows processed (suitable for use in result tag)
887  */
888 static long
889 PortalRunSelect(Portal portal,
890                                 bool forward,
891                                 long count,
892                                 DestReceiver *dest)
893 {
894         QueryDesc  *queryDesc;
895         ScanDirection direction;
896         uint32          nprocessed;
897
898         /*
899          * NB: queryDesc will be NULL if we are fetching from a held cursor or a
900          * completed utility query; can't use it in that path.
901          */
902         queryDesc = PortalGetQueryDesc(portal);
903
904         /* Caller messed up if we have neither a ready query nor held data. */
905         Assert(queryDesc || portal->holdStore);
906
907         /*
908          * Force the queryDesc destination to the right thing.  This supports
909          * MOVE, for example, which will pass in dest = DestNone.  This is okay to
910          * change as long as we do it on every fetch.  (The Executor must not
911          * assume that dest never changes.)
912          */
913         if (queryDesc)
914                 queryDesc->dest = dest;
915
916         /*
917          * Determine which direction to go in, and check to see if we're already
918          * at the end of the available tuples in that direction.  If so, set the
919          * direction to NoMovement to avoid trying to fetch any tuples.  (This
920          * check exists because not all plan node types are robust about being
921          * called again if they've already returned NULL once.)  Then call the
922          * executor (we must not skip this, because the destination needs to see a
923          * setup and shutdown even if no tuples are available).  Finally, update
924          * the portal position state depending on the number of tuples that were
925          * retrieved.
926          */
927         if (forward)
928         {
929                 if (portal->atEnd || count <= 0)
930                         direction = NoMovementScanDirection;
931                 else
932                         direction = ForwardScanDirection;
933
934                 /* In the executor, zero count processes all rows */
935                 if (count == FETCH_ALL)
936                         count = 0;
937
938                 if (portal->holdStore)
939                         nprocessed = RunFromStore(portal, direction, count, dest);
940                 else
941                 {
942                         ActiveSnapshot = queryDesc->snapshot;
943                         ExecutorRun(queryDesc, direction, count);
944                         nprocessed = queryDesc->estate->es_processed;
945                 }
946
947                 if (!ScanDirectionIsNoMovement(direction))
948                 {
949                         long            oldPos;
950
951                         if (nprocessed > 0)
952                                 portal->atStart = false;                /* OK to go backward now */
953                         if (count == 0 ||
954                                 (unsigned long) nprocessed < (unsigned long) count)
955                                 portal->atEnd = true;   /* we retrieved 'em all */
956                         oldPos = portal->portalPos;
957                         portal->portalPos += nprocessed;
958                         /* portalPos doesn't advance when we fall off the end */
959                         if (portal->portalPos < oldPos)
960                                 portal->posOverflow = true;
961                 }
962         }
963         else
964         {
965                 if (portal->cursorOptions & CURSOR_OPT_NO_SCROLL)
966                         ereport(ERROR,
967                                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
968                                          errmsg("cursor can only scan forward"),
969                                          errhint("Declare it with SCROLL option to enable backward scan.")));
970
971                 if (portal->atStart || count <= 0)
972                         direction = NoMovementScanDirection;
973                 else
974                         direction = BackwardScanDirection;
975
976                 /* In the executor, zero count processes all rows */
977                 if (count == FETCH_ALL)
978                         count = 0;
979
980                 if (portal->holdStore)
981                         nprocessed = RunFromStore(portal, direction, count, dest);
982                 else
983                 {
984                         ActiveSnapshot = queryDesc->snapshot;
985                         ExecutorRun(queryDesc, direction, count);
986                         nprocessed = queryDesc->estate->es_processed;
987                 }
988
989                 if (!ScanDirectionIsNoMovement(direction))
990                 {
991                         if (nprocessed > 0 && portal->atEnd)
992                         {
993                                 portal->atEnd = false;  /* OK to go forward now */
994                                 portal->portalPos++;    /* adjust for endpoint case */
995                         }
996                         if (count == 0 ||
997                                 (unsigned long) nprocessed < (unsigned long) count)
998                         {
999                                 portal->atStart = true; /* we retrieved 'em all */
1000                                 portal->portalPos = 0;
1001                                 portal->posOverflow = false;
1002                         }
1003                         else
1004                         {
1005                                 long            oldPos;
1006
1007                                 oldPos = portal->portalPos;
1008                                 portal->portalPos -= nprocessed;
1009                                 if (portal->portalPos > oldPos ||
1010                                         portal->portalPos <= 0)
1011                                         portal->posOverflow = true;
1012                         }
1013                 }
1014         }
1015
1016         return nprocessed;
1017 }
1018
1019 /*
1020  * FillPortalStore
1021  *              Run the query and load result tuples into the portal's tuple store.
1022  *
1023  * This is used for PORTAL_ONE_RETURNING and PORTAL_UTIL_SELECT cases only.
1024  */
1025 static void
1026 FillPortalStore(Portal portal, bool isTopLevel)
1027 {
1028         DestReceiver *treceiver;
1029         char            completionTag[COMPLETION_TAG_BUFSIZE];
1030
1031         PortalCreateHoldStore(portal);
1032         treceiver = CreateDestReceiver(DestTuplestore, portal);
1033
1034         completionTag[0] = '\0';
1035
1036         switch (portal->strategy)
1037         {
1038                 case PORTAL_ONE_RETURNING:
1039
1040                         /*
1041                          * Run the portal to completion just as for the default
1042                          * MULTI_QUERY case, but send the primary query's output to the
1043                          * tuplestore. Auxiliary query outputs are discarded.
1044                          */
1045                         PortalRunMulti(portal, isTopLevel,
1046                                                    treceiver, None_Receiver, completionTag);
1047                         break;
1048
1049                 case PORTAL_UTIL_SELECT:
1050                         PortalRunUtility(portal, (Node *) linitial(portal->stmts),
1051                                                          isTopLevel, treceiver, completionTag);
1052                         break;
1053
1054                 default:
1055                         elog(ERROR, "unsupported portal strategy: %d",
1056                                  (int) portal->strategy);
1057                         break;
1058         }
1059
1060         /* Override default completion tag with actual command result */
1061         if (completionTag[0] != '\0')
1062                 portal->commandTag = pstrdup(completionTag);
1063
1064         (*treceiver->rDestroy) (treceiver);
1065 }
1066
1067 /*
1068  * RunFromStore
1069  *              Fetch tuples from the portal's tuple store.
1070  *
1071  * Calling conventions are similar to ExecutorRun, except that we
1072  * do not depend on having a queryDesc or estate.  Therefore we return the
1073  * number of tuples processed as the result, not in estate->es_processed.
1074  *
1075  * One difference from ExecutorRun is that the destination receiver functions
1076  * are run in the caller's memory context (since we have no estate).  Watch
1077  * out for memory leaks.
1078  */
1079 static uint32
1080 RunFromStore(Portal portal, ScanDirection direction, long count,
1081                          DestReceiver *dest)
1082 {
1083         long            current_tuple_count = 0;
1084         TupleTableSlot *slot;
1085
1086         slot = MakeSingleTupleTableSlot(portal->tupDesc);
1087
1088         (*dest->rStartup) (dest, CMD_SELECT, portal->tupDesc);
1089
1090         if (ScanDirectionIsNoMovement(direction))
1091         {
1092                 /* do nothing except start/stop the destination */
1093         }
1094         else
1095         {
1096                 bool            forward = ScanDirectionIsForward(direction);
1097
1098                 for (;;)
1099                 {
1100                         MemoryContext oldcontext;
1101                         bool            ok;
1102
1103                         oldcontext = MemoryContextSwitchTo(portal->holdContext);
1104
1105                         ok = tuplestore_gettupleslot(portal->holdStore, forward, slot);
1106
1107                         MemoryContextSwitchTo(oldcontext);
1108
1109                         if (!ok)
1110                                 break;
1111
1112                         (*dest->receiveSlot) (slot, dest);
1113
1114                         ExecClearTuple(slot);
1115
1116                         /*
1117                          * check our tuple count.. if we've processed the proper number
1118                          * then quit, else loop again and process more tuples. Zero count
1119                          * means no limit.
1120                          */
1121                         current_tuple_count++;
1122                         if (count && count == current_tuple_count)
1123                                 break;
1124                 }
1125         }
1126
1127         (*dest->rShutdown) (dest);
1128
1129         ExecDropSingleTupleTableSlot(slot);
1130
1131         return (uint32) current_tuple_count;
1132 }
1133
1134 /*
1135  * PortalRunUtility
1136  *              Execute a utility statement inside a portal.
1137  */
1138 static void
1139 PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
1140                                  DestReceiver *dest, char *completionTag)
1141 {
1142         elog(DEBUG3, "ProcessUtility");
1143
1144         /*
1145          * Set snapshot if utility stmt needs one.      Most reliable way to do this
1146          * seems to be to enumerate those that do not need one; this is a short
1147          * list.  Transaction control, LOCK, and SET must *not* set a snapshot
1148          * since they need to be executable at the start of a serializable
1149          * transaction without freezing a snapshot.  By extension we allow SHOW
1150          * not to set a snapshot.  The other stmts listed are just efficiency
1151          * hacks.  Beware of listing anything that can modify the database --- if,
1152          * say, it has to update an index with expressions that invoke
1153          * user-defined functions, then it had better have a snapshot.
1154          *
1155          * Note we assume that caller will take care of restoring ActiveSnapshot
1156          * on exit/error.
1157          */
1158         if (!(IsA(utilityStmt, TransactionStmt) ||
1159                   IsA(utilityStmt, LockStmt) ||
1160                   IsA(utilityStmt, VariableSetStmt) ||
1161                   IsA(utilityStmt, VariableShowStmt) ||
1162                   IsA(utilityStmt, VariableResetStmt) ||
1163                   IsA(utilityStmt, ConstraintsSetStmt) ||
1164         /* efficiency hacks from here down */
1165                   IsA(utilityStmt, FetchStmt) ||
1166                   IsA(utilityStmt, ListenStmt) ||
1167                   IsA(utilityStmt, NotifyStmt) ||
1168                   IsA(utilityStmt, UnlistenStmt) ||
1169                   IsA(utilityStmt, CheckPointStmt)))
1170                 ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
1171         else
1172                 ActiveSnapshot = NULL;
1173
1174         ProcessUtility(utilityStmt,
1175                                    portal->sourceText,
1176                                    portal->portalParams,
1177                                    isTopLevel,
1178                                    dest,
1179                                    completionTag);
1180
1181         /* Some utility statements may change context on us */
1182         MemoryContextSwitchTo(PortalGetHeapMemory(portal));
1183
1184         if (ActiveSnapshot)
1185                 FreeSnapshot(ActiveSnapshot);
1186         ActiveSnapshot = NULL;
1187 }
1188
1189 /*
1190  * PortalRunMulti
1191  *              Execute a portal's queries in the general case (multi queries
1192  *              or non-SELECT-like queries)
1193  */
1194 static void
1195 PortalRunMulti(Portal portal, bool isTopLevel,
1196                            DestReceiver *dest, DestReceiver *altdest,
1197                            char *completionTag)
1198 {
1199         ListCell   *stmtlist_item;
1200
1201         /*
1202          * If the destination is DestRemoteExecute, change to DestNone.  The
1203          * reason is that the client won't be expecting any tuples, and indeed has
1204          * no way to know what they are, since there is no provision for Describe
1205          * to send a RowDescription message when this portal execution strategy is
1206          * in effect.  This presently will only affect SELECT commands added to
1207          * non-SELECT queries by rewrite rules: such commands will be executed,
1208          * but the results will be discarded unless you use "simple Query"
1209          * protocol.
1210          */
1211         if (dest->mydest == DestRemoteExecute)
1212                 dest = None_Receiver;
1213         if (altdest->mydest == DestRemoteExecute)
1214                 altdest = None_Receiver;
1215
1216         /*
1217          * Loop to handle the individual queries generated from a single parsetree
1218          * by analysis and rewrite.
1219          */
1220         foreach(stmtlist_item, portal->stmts)
1221         {
1222                 Node   *stmt = (Node *) lfirst(stmtlist_item);
1223
1224                 /*
1225                  * If we got a cancel signal in prior command, quit
1226                  */
1227                 CHECK_FOR_INTERRUPTS();
1228
1229                 if (IsA(stmt, PlannedStmt) &&
1230                         ((PlannedStmt *) stmt)->utilityStmt == NULL)
1231                 {
1232                         /*
1233                          * process a plannable query.
1234                          */
1235                         PlannedStmt *pstmt = (PlannedStmt *) stmt;
1236
1237                         if (log_executor_stats)
1238                                 ResetUsage();
1239
1240                         if (pstmt->canSetTag)
1241                         {
1242                                 /* statement can set tag string */
1243                                 ProcessQuery(pstmt,
1244                                                          portal->portalParams,
1245                                                          dest, completionTag);
1246                         }
1247                         else
1248                         {
1249                                 /* stmt added by rewrite cannot set tag */
1250                                 ProcessQuery(pstmt,
1251                                                          portal->portalParams,
1252                                                          altdest, NULL);
1253                         }
1254
1255                         if (log_executor_stats)
1256                                 ShowUsage("EXECUTOR STATISTICS");
1257                 }
1258                 else
1259                 {
1260                         /*
1261                          * process utility functions (create, destroy, etc..)
1262                          *
1263                          * These are assumed canSetTag if they're the only stmt in the
1264                          * portal.
1265                          */
1266                         if (list_length(portal->stmts) == 1)
1267                                 PortalRunUtility(portal, stmt, isTopLevel, dest, completionTag);
1268                         else
1269                                 PortalRunUtility(portal, stmt, isTopLevel, altdest, NULL);
1270                 }
1271
1272                 /*
1273                  * Increment command counter between queries, but not after the last
1274                  * one.
1275                  */
1276                 if (lnext(stmtlist_item) != NULL)
1277                         CommandCounterIncrement();
1278
1279                 /*
1280                  * Clear subsidiary contexts to recover temporary memory.
1281                  */
1282                 Assert(PortalGetHeapMemory(portal) == CurrentMemoryContext);
1283
1284                 MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
1285         }
1286
1287         /*
1288          * If a command completion tag was supplied, use it.  Otherwise use the
1289          * portal's commandTag as the default completion tag.
1290          *
1291          * Exception: clients will expect INSERT/UPDATE/DELETE tags to have
1292          * counts, so fake something up if necessary.  (This could happen if the
1293          * original query was replaced by a DO INSTEAD rule.)
1294          */
1295         if (completionTag && completionTag[0] == '\0')
1296         {
1297                 if (portal->commandTag)
1298                         strcpy(completionTag, portal->commandTag);
1299                 if (strcmp(completionTag, "INSERT") == 0)
1300                         strcpy(completionTag, "INSERT 0 0");
1301                 else if (strcmp(completionTag, "UPDATE") == 0)
1302                         strcpy(completionTag, "UPDATE 0");
1303                 else if (strcmp(completionTag, "DELETE") == 0)
1304                         strcpy(completionTag, "DELETE 0");
1305         }
1306 }
1307
1308 /*
1309  * PortalRunFetch
1310  *              Variant form of PortalRun that supports SQL FETCH directions.
1311  *
1312  * Note: we presently assume that no callers of this want isTopLevel = true.
1313  *
1314  * Returns number of rows processed (suitable for use in result tag)
1315  */
1316 long
1317 PortalRunFetch(Portal portal,
1318                            FetchDirection fdirection,
1319                            long count,
1320                            DestReceiver *dest)
1321 {
1322         long            result;
1323         Portal          saveActivePortal;
1324         Snapshot        saveActiveSnapshot;
1325         ResourceOwner saveResourceOwner;
1326         MemoryContext savePortalContext;
1327         MemoryContext oldContext;
1328
1329         AssertArg(PortalIsValid(portal));
1330
1331         /*
1332          * Check for improper portal use, and mark portal active.
1333          */
1334         if (portal->status != PORTAL_READY)
1335                 ereport(ERROR,
1336                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1337                                  errmsg("portal \"%s\" cannot be run", portal->name)));
1338         portal->status = PORTAL_ACTIVE;
1339
1340         /*
1341          * Set up global portal context pointers.
1342          */
1343         saveActivePortal = ActivePortal;
1344         saveActiveSnapshot = ActiveSnapshot;
1345         saveResourceOwner = CurrentResourceOwner;
1346         savePortalContext = PortalContext;
1347         PG_TRY();
1348         {
1349                 ActivePortal = portal;
1350                 ActiveSnapshot = NULL;  /* will be set later */
1351                 CurrentResourceOwner = portal->resowner;
1352                 PortalContext = PortalGetHeapMemory(portal);
1353
1354                 oldContext = MemoryContextSwitchTo(PortalContext);
1355
1356                 switch (portal->strategy)
1357                 {
1358                         case PORTAL_ONE_SELECT:
1359                                 result = DoPortalRunFetch(portal, fdirection, count, dest);
1360                                 break;
1361
1362                         case PORTAL_ONE_RETURNING:
1363                         case PORTAL_UTIL_SELECT:
1364
1365                                 /*
1366                                  * If we have not yet run the command, do so, storing its
1367                                  * results in the portal's tuplestore.
1368                                  */
1369                                 if (!portal->holdStore)
1370                                         FillPortalStore(portal, false /* isTopLevel */);
1371
1372                                 /*
1373                                  * Now fetch desired portion of results.
1374                                  */
1375                                 result = DoPortalRunFetch(portal, fdirection, count, dest);
1376                                 break;
1377
1378                         default:
1379                                 elog(ERROR, "unsupported portal strategy");
1380                                 result = 0;             /* keep compiler quiet */
1381                                 break;
1382                 }
1383         }
1384         PG_CATCH();
1385         {
1386                 /* Uncaught error while executing portal: mark it dead */
1387                 portal->status = PORTAL_FAILED;
1388
1389                 /* Restore global vars and propagate error */
1390                 ActivePortal = saveActivePortal;
1391                 ActiveSnapshot = saveActiveSnapshot;
1392                 CurrentResourceOwner = saveResourceOwner;
1393                 PortalContext = savePortalContext;
1394
1395                 PG_RE_THROW();
1396         }
1397         PG_END_TRY();
1398
1399         MemoryContextSwitchTo(oldContext);
1400
1401         /* Mark portal not active */
1402         portal->status = PORTAL_READY;
1403
1404         ActivePortal = saveActivePortal;
1405         ActiveSnapshot = saveActiveSnapshot;
1406         CurrentResourceOwner = saveResourceOwner;
1407         PortalContext = savePortalContext;
1408
1409         return result;
1410 }
1411
1412 /*
1413  * DoPortalRunFetch
1414  *              Guts of PortalRunFetch --- the portal context is already set up
1415  *
1416  * Returns number of rows processed (suitable for use in result tag)
1417  */
1418 static long
1419 DoPortalRunFetch(Portal portal,
1420                                  FetchDirection fdirection,
1421                                  long count,
1422                                  DestReceiver *dest)
1423 {
1424         bool            forward;
1425
1426         Assert(portal->strategy == PORTAL_ONE_SELECT ||
1427                    portal->strategy == PORTAL_ONE_RETURNING ||
1428                    portal->strategy == PORTAL_UTIL_SELECT);
1429
1430         switch (fdirection)
1431         {
1432                 case FETCH_FORWARD:
1433                         if (count < 0)
1434                         {
1435                                 fdirection = FETCH_BACKWARD;
1436                                 count = -count;
1437                         }
1438                         /* fall out of switch to share code with FETCH_BACKWARD */
1439                         break;
1440                 case FETCH_BACKWARD:
1441                         if (count < 0)
1442                         {
1443                                 fdirection = FETCH_FORWARD;
1444                                 count = -count;
1445                         }
1446                         /* fall out of switch to share code with FETCH_FORWARD */
1447                         break;
1448                 case FETCH_ABSOLUTE:
1449                         if (count > 0)
1450                         {
1451                                 /*
1452                                  * Definition: Rewind to start, advance count-1 rows, return
1453                                  * next row (if any).  In practice, if the goal is less than
1454                                  * halfway back to the start, it's better to scan from where
1455                                  * we are.      In any case, we arrange to fetch the target row
1456                                  * going forwards.
1457                                  */
1458                                 if (portal->posOverflow || portal->portalPos == LONG_MAX ||
1459                                         count - 1 <= portal->portalPos / 2)
1460                                 {
1461                                         DoPortalRewind(portal);
1462                                         if (count > 1)
1463                                                 PortalRunSelect(portal, true, count - 1,
1464                                                                                 None_Receiver);
1465                                 }
1466                                 else
1467                                 {
1468                                         long            pos = portal->portalPos;
1469
1470                                         if (portal->atEnd)
1471                                                 pos++;  /* need one extra fetch if off end */
1472                                         if (count <= pos)
1473                                                 PortalRunSelect(portal, false, pos - count + 1,
1474                                                                                 None_Receiver);
1475                                         else if (count > pos + 1)
1476                                                 PortalRunSelect(portal, true, count - pos - 1,
1477                                                                                 None_Receiver);
1478                                 }
1479                                 return PortalRunSelect(portal, true, 1L, dest);
1480                         }
1481                         else if (count < 0)
1482                         {
1483                                 /*
1484                                  * Definition: Advance to end, back up abs(count)-1 rows,
1485                                  * return prior row (if any).  We could optimize this if we
1486                                  * knew in advance where the end was, but typically we won't.
1487                                  * (Is it worth considering case where count > half of size of
1488                                  * query?  We could rewind once we know the size ...)
1489                                  */
1490                                 PortalRunSelect(portal, true, FETCH_ALL, None_Receiver);
1491                                 if (count < -1)
1492                                         PortalRunSelect(portal, false, -count - 1, None_Receiver);
1493                                 return PortalRunSelect(portal, false, 1L, dest);
1494                         }
1495                         else
1496                         {
1497                                 /* count == 0 */
1498                                 /* Rewind to start, return zero rows */
1499                                 DoPortalRewind(portal);
1500                                 return PortalRunSelect(portal, true, 0L, dest);
1501                         }
1502                         break;
1503                 case FETCH_RELATIVE:
1504                         if (count > 0)
1505                         {
1506                                 /*
1507                                  * Definition: advance count-1 rows, return next row (if any).
1508                                  */
1509                                 if (count > 1)
1510                                         PortalRunSelect(portal, true, count - 1, None_Receiver);
1511                                 return PortalRunSelect(portal, true, 1L, dest);
1512                         }
1513                         else if (count < 0)
1514                         {
1515                                 /*
1516                                  * Definition: back up abs(count)-1 rows, return prior row (if
1517                                  * any).
1518                                  */
1519                                 if (count < -1)
1520                                         PortalRunSelect(portal, false, -count - 1, None_Receiver);
1521                                 return PortalRunSelect(portal, false, 1L, dest);
1522                         }
1523                         else
1524                         {
1525                                 /* count == 0 */
1526                                 /* Same as FETCH FORWARD 0, so fall out of switch */
1527                                 fdirection = FETCH_FORWARD;
1528                         }
1529                         break;
1530                 default:
1531                         elog(ERROR, "bogus direction");
1532                         break;
1533         }
1534
1535         /*
1536          * Get here with fdirection == FETCH_FORWARD or FETCH_BACKWARD, and count
1537          * >= 0.
1538          */
1539         forward = (fdirection == FETCH_FORWARD);
1540
1541         /*
1542          * Zero count means to re-fetch the current row, if any (per SQL92)
1543          */
1544         if (count == 0)
1545         {
1546                 bool            on_row;
1547
1548                 /* Are we sitting on a row? */
1549                 on_row = (!portal->atStart && !portal->atEnd);
1550
1551                 if (dest->mydest == DestNone)
1552                 {
1553                         /* MOVE 0 returns 0/1 based on if FETCH 0 would return a row */
1554                         return on_row ? 1L : 0L;
1555                 }
1556                 else
1557                 {
1558                         /*
1559                          * If we are sitting on a row, back up one so we can re-fetch it.
1560                          * If we are not sitting on a row, we still have to start up and
1561                          * shut down the executor so that the destination is initialized
1562                          * and shut down correctly; so keep going.      To PortalRunSelect,
1563                          * count == 0 means we will retrieve no row.
1564                          */
1565                         if (on_row)
1566                         {
1567                                 PortalRunSelect(portal, false, 1L, None_Receiver);
1568                                 /* Set up to fetch one row forward */
1569                                 count = 1;
1570                                 forward = true;
1571                         }
1572                 }
1573         }
1574
1575         /*
1576          * Optimize MOVE BACKWARD ALL into a Rewind.
1577          */
1578         if (!forward && count == FETCH_ALL && dest->mydest == DestNone)
1579         {
1580                 long            result = portal->portalPos;
1581
1582                 if (result > 0 && !portal->atEnd)
1583                         result--;
1584                 DoPortalRewind(portal);
1585                 /* result is bogus if pos had overflowed, but it's best we can do */
1586                 return result;
1587         }
1588
1589         return PortalRunSelect(portal, forward, count, dest);
1590 }
1591
1592 /*
1593  * DoPortalRewind - rewind a Portal to starting point
1594  */
1595 static void
1596 DoPortalRewind(Portal portal)
1597 {
1598         if (portal->holdStore)
1599         {
1600                 MemoryContext oldcontext;
1601
1602                 oldcontext = MemoryContextSwitchTo(portal->holdContext);
1603                 tuplestore_rescan(portal->holdStore);
1604                 MemoryContextSwitchTo(oldcontext);
1605         }
1606         if (PortalGetQueryDesc(portal))
1607                 ExecutorRewind(PortalGetQueryDesc(portal));
1608
1609         portal->atStart = true;
1610         portal->atEnd = false;
1611         portal->portalPos = 0;
1612         portal->posOverflow = false;
1613 }