]> granicus.if.org Git - postgresql/blob - src/backend/commands/portalcmds.c
8.4 pgindent run, with new combined Linux/FreeBSD/MinGW typedef list
[postgresql] / src / backend / commands / portalcmds.c
1 /*-------------------------------------------------------------------------
2  *
3  * portalcmds.c
4  *        Utility commands affecting portals (that is, SQL cursor commands)
5  *
6  * Note: see also tcop/pquery.c, which implements portal operations for
7  * the FE/BE protocol.  This module uses pquery.c for some operations.
8  * And both modules depend on utils/mmgr/portalmem.c, which controls
9  * storage management for portals (but doesn't run any queries in them).
10  *
11  *
12  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
13  * Portions Copyright (c) 1994, Regents of the University of California
14  *
15  *
16  * IDENTIFICATION
17  *        $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.79 2009/06/11 14:48:56 momjian Exp $
18  *
19  *-------------------------------------------------------------------------
20  */
21
22 #include "postgres.h"
23
24 #include <limits.h>
25
26 #include "access/xact.h"
27 #include "commands/portalcmds.h"
28 #include "executor/executor.h"
29 #include "executor/tstoreReceiver.h"
30 #include "tcop/pquery.h"
31 #include "utils/memutils.h"
32 #include "utils/snapmgr.h"
33
34
35 /*
36  * PerformCursorOpen
37  *              Execute SQL DECLARE CURSOR command.
38  *
39  * The query has already been through parse analysis, rewriting, and planning.
40  * When it gets here, it looks like a SELECT PlannedStmt, except that the
41  * utilityStmt field is set.
42  */
43 void
44 PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
45                                   const char *queryString, bool isTopLevel)
46 {
47         DeclareCursorStmt *cstmt = (DeclareCursorStmt *) stmt->utilityStmt;
48         Portal          portal;
49         MemoryContext oldContext;
50
51         if (cstmt == NULL || !IsA(cstmt, DeclareCursorStmt))
52                 elog(ERROR, "PerformCursorOpen called for non-cursor query");
53
54         /*
55          * Disallow empty-string cursor name (conflicts with protocol-level
56          * unnamed portal).
57          */
58         if (!cstmt->portalname || cstmt->portalname[0] == '\0')
59                 ereport(ERROR,
60                                 (errcode(ERRCODE_INVALID_CURSOR_NAME),
61                                  errmsg("invalid cursor name: must not be empty")));
62
63         /*
64          * If this is a non-holdable cursor, we require that this statement has
65          * been executed inside a transaction block (or else, it would have no
66          * user-visible effect).
67          */
68         if (!(cstmt->options & CURSOR_OPT_HOLD))
69                 RequireTransactionChain(isTopLevel, "DECLARE CURSOR");
70
71         /*
72          * Create a portal and copy the plan and queryString into its memory.
73          */
74         portal = CreatePortal(cstmt->portalname, false, false);
75
76         oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
77
78         stmt = copyObject(stmt);
79         stmt->utilityStmt = NULL;       /* make it look like plain SELECT */
80
81         queryString = pstrdup(queryString);
82
83         PortalDefineQuery(portal,
84                                           NULL,
85                                           queryString,
86                                           "SELECT", /* cursor's query is always a SELECT */
87                                           list_make1(stmt),
88                                           NULL);
89
90         /*----------
91          * Also copy the outer portal's parameter list into the inner portal's
92          * memory context.      We want to pass down the parameter values in case we
93          * had a command like
94          *              DECLARE c CURSOR FOR SELECT ... WHERE foo = $1
95          * This will have been parsed using the outer parameter set and the
96          * parameter value needs to be preserved for use when the cursor is
97          * executed.
98          *----------
99          */
100         params = copyParamList(params);
101
102         MemoryContextSwitchTo(oldContext);
103
104         /*
105          * Set up options for portal.
106          *
107          * If the user didn't specify a SCROLL type, allow or disallow scrolling
108          * based on whether it would require any additional runtime overhead to do
109          * so.  Also, we disallow scrolling for FOR UPDATE cursors.
110          */
111         portal->cursorOptions = cstmt->options;
112         if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
113         {
114                 if (stmt->rowMarks == NIL &&
115                         ExecSupportsBackwardScan(stmt->planTree))
116                         portal->cursorOptions |= CURSOR_OPT_SCROLL;
117                 else
118                         portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
119         }
120
121         /*
122          * Start execution, inserting parameters if any.
123          */
124         PortalStart(portal, params, GetActiveSnapshot());
125
126         Assert(portal->strategy == PORTAL_ONE_SELECT);
127
128         /*
129          * We're done; the query won't actually be run until PerformPortalFetch is
130          * called.
131          */
132 }
133
134 /*
135  * PerformPortalFetch
136  *              Execute SQL FETCH or MOVE command.
137  *
138  *      stmt: parsetree node for command
139  *      dest: where to send results
140  *      completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
141  *              in which to store a command completion status string.
142  *
143  * completionTag may be NULL if caller doesn't want a status string.
144  */
145 void
146 PerformPortalFetch(FetchStmt *stmt,
147                                    DestReceiver *dest,
148                                    char *completionTag)
149 {
150         Portal          portal;
151         long            nprocessed;
152
153         /*
154          * Disallow empty-string cursor name (conflicts with protocol-level
155          * unnamed portal).
156          */
157         if (!stmt->portalname || stmt->portalname[0] == '\0')
158                 ereport(ERROR,
159                                 (errcode(ERRCODE_INVALID_CURSOR_NAME),
160                                  errmsg("invalid cursor name: must not be empty")));
161
162         /* get the portal from the portal name */
163         portal = GetPortalByName(stmt->portalname);
164         if (!PortalIsValid(portal))
165         {
166                 ereport(ERROR,
167                                 (errcode(ERRCODE_UNDEFINED_CURSOR),
168                                  errmsg("cursor \"%s\" does not exist", stmt->portalname)));
169                 return;                                 /* keep compiler happy */
170         }
171
172         /* Adjust dest if needed.  MOVE wants destination DestNone */
173         if (stmt->ismove)
174                 dest = None_Receiver;
175
176         /* Do it */
177         nprocessed = PortalRunFetch(portal,
178                                                                 stmt->direction,
179                                                                 stmt->howMany,
180                                                                 dest);
181
182         /* Return command status if wanted */
183         if (completionTag)
184                 snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
185                                  stmt->ismove ? "MOVE" : "FETCH",
186                                  nprocessed);
187 }
188
189 /*
190  * PerformPortalClose
191  *              Close a cursor.
192  */
193 void
194 PerformPortalClose(const char *name)
195 {
196         Portal          portal;
197
198         /* NULL means CLOSE ALL */
199         if (name == NULL)
200         {
201                 PortalHashTableDeleteAll();
202                 return;
203         }
204
205         /*
206          * Disallow empty-string cursor name (conflicts with protocol-level
207          * unnamed portal).
208          */
209         if (name[0] == '\0')
210                 ereport(ERROR,
211                                 (errcode(ERRCODE_INVALID_CURSOR_NAME),
212                                  errmsg("invalid cursor name: must not be empty")));
213
214         /*
215          * get the portal from the portal name
216          */
217         portal = GetPortalByName(name);
218         if (!PortalIsValid(portal))
219         {
220                 ereport(ERROR,
221                                 (errcode(ERRCODE_UNDEFINED_CURSOR),
222                                  errmsg("cursor \"%s\" does not exist", name)));
223                 return;                                 /* keep compiler happy */
224         }
225
226         /*
227          * Note: PortalCleanup is called as a side-effect
228          */
229         PortalDrop(portal, false);
230 }
231
232 /*
233  * PortalCleanup
234  *
235  * Clean up a portal when it's dropped.  This is the standard cleanup hook
236  * for portals.
237  */
238 void
239 PortalCleanup(Portal portal)
240 {
241         QueryDesc  *queryDesc;
242
243         /*
244          * sanity checks
245          */
246         AssertArg(PortalIsValid(portal));
247         AssertArg(portal->cleanup == PortalCleanup);
248
249         /*
250          * Shut down executor, if still running.  We skip this during error abort,
251          * since other mechanisms will take care of releasing executor resources,
252          * and we can't be sure that ExecutorEnd itself wouldn't fail.
253          */
254         queryDesc = PortalGetQueryDesc(portal);
255         if (queryDesc)
256         {
257                 portal->queryDesc = NULL;
258                 if (portal->status != PORTAL_FAILED)
259                 {
260                         ResourceOwner saveResourceOwner;
261
262                         /* We must make the portal's resource owner current */
263                         saveResourceOwner = CurrentResourceOwner;
264                         PG_TRY();
265                         {
266                                 CurrentResourceOwner = portal->resowner;
267                                 /* we do not need AfterTriggerEndQuery() here */
268                                 ExecutorEnd(queryDesc);
269                                 FreeQueryDesc(queryDesc);
270                         }
271                         PG_CATCH();
272                         {
273                                 /* Ensure CurrentResourceOwner is restored on error */
274                                 CurrentResourceOwner = saveResourceOwner;
275                                 PG_RE_THROW();
276                         }
277                         PG_END_TRY();
278                         CurrentResourceOwner = saveResourceOwner;
279                 }
280         }
281 }
282
283 /*
284  * PersistHoldablePortal
285  *
286  * Prepare the specified Portal for access outside of the current
287  * transaction. When this function returns, all future accesses to the
288  * portal must be done via the Tuplestore (not by invoking the
289  * executor).
290  */
291 void
292 PersistHoldablePortal(Portal portal)
293 {
294         QueryDesc  *queryDesc = PortalGetQueryDesc(portal);
295         Portal          saveActivePortal;
296         ResourceOwner saveResourceOwner;
297         MemoryContext savePortalContext;
298         MemoryContext oldcxt;
299
300         /*
301          * If we're preserving a holdable portal, we had better be inside the
302          * transaction that originally created it.
303          */
304         Assert(portal->createSubid != InvalidSubTransactionId);
305         Assert(queryDesc != NULL);
306
307         /*
308          * Caller must have created the tuplestore already.
309          */
310         Assert(portal->holdContext != NULL);
311         Assert(portal->holdStore != NULL);
312
313         /*
314          * Before closing down the executor, we must copy the tupdesc into
315          * long-term memory, since it was created in executor memory.
316          */
317         oldcxt = MemoryContextSwitchTo(portal->holdContext);
318
319         portal->tupDesc = CreateTupleDescCopy(portal->tupDesc);
320
321         MemoryContextSwitchTo(oldcxt);
322
323         /*
324          * Check for improper portal use, and mark portal active.
325          */
326         if (portal->status != PORTAL_READY)
327                 ereport(ERROR,
328                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
329                                  errmsg("portal \"%s\" cannot be run", portal->name)));
330         portal->status = PORTAL_ACTIVE;
331
332         /*
333          * Set up global portal context pointers.
334          */
335         saveActivePortal = ActivePortal;
336         saveResourceOwner = CurrentResourceOwner;
337         savePortalContext = PortalContext;
338         PG_TRY();
339         {
340                 ActivePortal = portal;
341                 CurrentResourceOwner = portal->resowner;
342                 PortalContext = PortalGetHeapMemory(portal);
343
344                 MemoryContextSwitchTo(PortalContext);
345
346                 PushActiveSnapshot(queryDesc->snapshot);
347
348                 /*
349                  * Rewind the executor: we need to store the entire result set in the
350                  * tuplestore, so that subsequent backward FETCHs can be processed.
351                  */
352                 ExecutorRewind(queryDesc);
353
354                 /*
355                  * Change the destination to output to the tuplestore.  Note we tell
356                  * the tuplestore receiver to detoast all data passed through it.
357                  */
358                 queryDesc->dest = CreateDestReceiver(DestTuplestore);
359                 SetTuplestoreDestReceiverParams(queryDesc->dest,
360                                                                                 portal->holdStore,
361                                                                                 portal->holdContext,
362                                                                                 true);
363
364                 /* Fetch the result set into the tuplestore */
365                 ExecutorRun(queryDesc, ForwardScanDirection, 0L);
366
367                 (*queryDesc->dest->rDestroy) (queryDesc->dest);
368                 queryDesc->dest = NULL;
369
370                 /*
371                  * Now shut down the inner executor.
372                  */
373                 portal->queryDesc = NULL;               /* prevent double shutdown */
374                 /* we do not need AfterTriggerEndQuery() here */
375                 ExecutorEnd(queryDesc);
376                 FreeQueryDesc(queryDesc);
377
378                 /*
379                  * Set the position in the result set: ideally, this could be
380                  * implemented by just skipping straight to the tuple # that we need
381                  * to be at, but the tuplestore API doesn't support that. So we start
382                  * at the beginning of the tuplestore and iterate through it until we
383                  * reach where we need to be.  FIXME someday?  (Fortunately, the
384                  * typical case is that we're supposed to be at or near the start of
385                  * the result set, so this isn't as bad as it sounds.)
386                  */
387                 MemoryContextSwitchTo(portal->holdContext);
388
389                 if (portal->atEnd)
390                 {
391                         /* we can handle this case even if posOverflow */
392                         while (tuplestore_advance(portal->holdStore, true))
393                                  /* continue */ ;
394                 }
395                 else
396                 {
397                         long            store_pos;
398
399                         if (portal->posOverflow)        /* oops, cannot trust portalPos */
400                                 ereport(ERROR,
401                                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
402                                                  errmsg("could not reposition held cursor")));
403
404                         tuplestore_rescan(portal->holdStore);
405
406                         for (store_pos = 0; store_pos < portal->portalPos; store_pos++)
407                         {
408                                 if (!tuplestore_advance(portal->holdStore, true))
409                                         elog(ERROR, "unexpected end of tuple stream");
410                         }
411                 }
412         }
413         PG_CATCH();
414         {
415                 /* Uncaught error while executing portal: mark it dead */
416                 portal->status = PORTAL_FAILED;
417
418                 /* Restore global vars and propagate error */
419                 ActivePortal = saveActivePortal;
420                 CurrentResourceOwner = saveResourceOwner;
421                 PortalContext = savePortalContext;
422
423                 PG_RE_THROW();
424         }
425         PG_END_TRY();
426
427         MemoryContextSwitchTo(oldcxt);
428
429         /* Mark portal not active */
430         portal->status = PORTAL_READY;
431
432         ActivePortal = saveActivePortal;
433         CurrentResourceOwner = saveResourceOwner;
434         PortalContext = savePortalContext;
435
436         PopActiveSnapshot();
437
438         /*
439          * We can now release any subsidiary memory of the portal's heap context;
440          * we'll never use it again.  The executor already dropped its context,
441          * but this will clean up anything that glommed onto the portal's heap via
442          * PortalContext.
443          */
444         MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
445 }