]> granicus.if.org Git - postgresql/blob - src/backend/commands/portalcmds.c
Cause FETCH 1 to return the current cursor row, or zero if at
[postgresql] / src / backend / commands / portalcmds.c
1 /*-------------------------------------------------------------------------
2  *
3  * portalcmds.c
4  *        portal support code
5  *
6  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.7 2002/12/30 15:31:47 momjian Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15
16 #include "postgres.h"
17
18 #include <limits.h>
19
20 #include "commands/portalcmds.h"
21 #include "executor/executor.h"
22
23
24 /*
25  * PortalCleanup
26  *
27  * Clean up a portal when it's dropped.  Since this mainly exists to run
28  * ExecutorEnd(), it should not be set as the cleanup hook until we have
29  * called ExecutorStart() on the portal's query.
30  */
31 void
32 PortalCleanup(Portal portal)
33 {
34         /*
35          * sanity checks
36          */
37         AssertArg(PortalIsValid(portal));
38         AssertArg(portal->cleanup == PortalCleanup);
39
40         /*
41          * tell the executor to shutdown the query
42          */
43         ExecutorEnd(PortalGetQueryDesc(portal));
44
45         /*
46          * This should be unnecessary since the querydesc should be in the
47          * portal's memory context, but do it anyway for symmetry.
48          */
49         FreeQueryDesc(PortalGetQueryDesc(portal));
50 }
51
52
53 /*
54  * PerformPortalFetch
55  *
56  *      name: name of portal
57  *      forward: forward or backward fetch?
58  *      count: # of tuples to fetch
59  *      dest: where to send results
60  *      completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
61  *              in which to store a command completion status string.
62  *
63  * completionTag may be NULL if caller doesn't want a status string.
64  */
65 void
66 PerformPortalFetch(char *name,
67                                    bool forward,
68                                    long count,
69                                    CommandDest dest,
70                                    char *completionTag)
71 {
72         Portal          portal;
73         QueryDesc  *queryDesc;
74         EState     *estate;
75         MemoryContext oldcontext;
76         ScanDirection direction;
77         bool            temp_desc = false;
78
79         /* initialize completion status in case of early exit */
80         if (completionTag)
81                 strcpy(completionTag, (dest == None) ? "MOVE 0" : "FETCH 0");
82
83         /*
84          * sanity checks
85          */
86         if (name == NULL)
87         {
88                 elog(WARNING, "PerformPortalFetch: missing portal name");
89                 return;
90         }
91
92         /*
93          * get the portal from the portal name
94          */
95         portal = GetPortalByName(name);
96         if (!PortalIsValid(portal))
97         {
98                 elog(WARNING, "PerformPortalFetch: portal \"%s\" not found",
99                          name);
100                 return;
101         }
102
103         /* If zero count, handle specially */
104         if (count == 0)
105         {
106                 bool on_row = false;
107
108                 /* Are we sitting on a row? */
109                 oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
110                 queryDesc = PortalGetQueryDesc(portal);
111                 estate = queryDesc->estate;
112                 if (portal->atStart == false && portal->atEnd == false)
113                         on_row = true;
114                 MemoryContextSwitchTo(oldcontext);
115
116                 if (dest == None)
117                 {
118                         /* MOVE 0 returns 0/1 based on if FETCH 0 would return a row */
119                         if (completionTag && on_row)
120                                 strcpy(completionTag, "MOVE 1");
121                         return;
122                 }
123                 else
124                 {
125                         /* If we are not on a row, FETCH 0 returns nothing */
126                         if (!on_row)
127                                 return;
128
129                         /* Since we are sitting on a row, return the row */
130                         /* Back up so we can reread the row */
131                         PerformPortalFetch(name, false /* backward */, 1,
132                                                            None, /* throw away output */
133                                                            NULL /* do not modify the command tag */);
134
135                         /* Set up to fetch one row */
136                         count = 1;
137                         forward = true;
138                 }
139         }
140
141         /* Internally, zero count processes all portal rows */
142         if (count == LONG_MAX)
143                 count = 0;
144
145         /*
146          * switch into the portal context
147          */
148         oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
149
150         queryDesc = PortalGetQueryDesc(portal);
151         estate = queryDesc->estate;
152
153         /*
154          * If the requested destination is not the same as the query's
155          * original destination, make a temporary QueryDesc with the proper
156          * destination.  This supports MOVE, for example, which will pass in
157          * dest = None.
158          *
159          * EXCEPTION: if the query's original dest is RemoteInternal (ie, it's a
160          * binary cursor) and the request is Remote, we do NOT override the
161          * original dest.  This is necessary since a FETCH command will pass
162          * dest = Remote, not knowing whether the cursor is binary or not.
163          */
164         if (dest != queryDesc->dest &&
165                 !(queryDesc->dest == RemoteInternal && dest == Remote))
166         {
167                 QueryDesc  *qdesc = (QueryDesc *) palloc(sizeof(QueryDesc));
168
169                 memcpy(qdesc, queryDesc, sizeof(QueryDesc));
170                 qdesc->dest = dest;
171                 queryDesc = qdesc;
172                 temp_desc = true;
173         }
174
175         /*
176          * Determine which direction to go in, and check to see if we're
177          * already at the end of the available tuples in that direction.  If
178          * so, set the direction to NoMovement to avoid trying to fetch any
179          * tuples.      (This check exists because not all plan node types are
180          * robust about being called again if they've already returned NULL
181          * once.)  Then call the executor (we must not skip this, because the
182          * destination needs to see a setup and shutdown even if no tuples are
183          * available).  Finally, update the atStart/atEnd state depending on
184          * the number of tuples that were retrieved.
185          */
186         if (forward)
187         {
188                 if (portal->atEnd)
189                         direction = NoMovementScanDirection;
190                 else
191                         direction = ForwardScanDirection;
192
193                 ExecutorRun(queryDesc, direction, (long) count);
194
195                 if (estate->es_processed > 0)
196                         portal->atStart = false;        /* OK to back up now */
197                 if (count <= 0 || (int) estate->es_processed < count)
198                         portal->atEnd = true;           /* we retrieved 'em all */
199         }
200         else
201         {
202                 if (portal->atStart)
203                         direction = NoMovementScanDirection;
204                 else
205                         direction = BackwardScanDirection;
206
207                 ExecutorRun(queryDesc, direction, (long) count);
208
209                 if (estate->es_processed > 0)
210                         portal->atEnd = false;          /* OK to go forward now */
211                 if (count <= 0 || (int) estate->es_processed < count)
212                         portal->atStart = true;         /* we retrieved 'em all */
213         }
214
215         /* Return command status if wanted */
216         if (completionTag)
217                 snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %u",
218                                  (dest == None) ? "MOVE" : "FETCH",
219                                  estate->es_processed);
220
221         /*
222          * Clean up and switch back to old context.
223          */
224         if (temp_desc)
225                 pfree(queryDesc);
226
227         MemoryContextSwitchTo(oldcontext);
228 }
229
230 /*
231  * PerformPortalClose
232  */
233 void
234 PerformPortalClose(char *name, CommandDest dest)
235 {
236         Portal          portal;
237
238         /*
239          * sanity checks
240          */
241         if (name == NULL)
242         {
243                 elog(WARNING, "PerformPortalClose: missing portal name");
244                 return;
245         }
246
247         /*
248          * get the portal from the portal name
249          */
250         portal = GetPortalByName(name);
251         if (!PortalIsValid(portal))
252         {
253                 elog(WARNING, "PerformPortalClose: portal \"%s\" not found",
254                          name);
255                 return;
256         }
257
258         /*
259          * Note: PortalCleanup is called as a side-effect
260          */
261         PortalDrop(portal);
262 }