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