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