]> granicus.if.org Git - postgresql/blob - src/backend/commands/portalcmds.c
A visit from the message-style police ...
[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-2002, PostgreSQL Global Development Group
13  * Portions Copyright (c) 1994, Regents of the University of California
14  *
15  *
16  * IDENTIFICATION
17  *        $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.18 2003/07/28 00:09:14 tgl Exp $
18  *
19  *-------------------------------------------------------------------------
20  */
21
22 #include "postgres.h"
23
24 #include <limits.h>
25
26 #include "commands/portalcmds.h"
27 #include "executor/executor.h"
28 #include "optimizer/planner.h"
29 #include "rewrite/rewriteHandler.h"
30 #include "tcop/pquery.h"
31 #include "utils/memutils.h"
32
33
34 /*
35  * PerformCursorOpen
36  *              Execute SQL DECLARE CURSOR command.
37  */
38 void
39 PerformCursorOpen(DeclareCursorStmt *stmt)
40 {
41         List       *rewritten;
42         Query      *query;
43         Plan       *plan;
44         Portal          portal;
45         MemoryContext oldContext;
46
47         /*
48          * Disallow empty-string cursor name (conflicts with protocol-level
49          * unnamed portal).
50          */
51         if (!stmt->portalname || stmt->portalname[0] == '\0')
52                 ereport(ERROR,
53                                 (errcode(ERRCODE_INVALID_CURSOR_NAME),
54                                  errmsg("invalid cursor name: must not be empty")));
55
56         /*
57          * If this is a non-holdable cursor, we require that this statement
58          * has been executed inside a transaction block (or else, it would
59          * have no user-visible effect).
60          */
61         if (!(stmt->options & CURSOR_OPT_HOLD))
62                 RequireTransactionChain((void *) stmt, "DECLARE CURSOR");
63
64         /*
65          * The query has been through parse analysis, but not rewriting or
66          * planning as yet.  Note that the grammar ensured we have a SELECT
67          * query, so we are not expecting rule rewriting to do anything strange.
68          */
69         rewritten = QueryRewrite((Query *) stmt->query);
70         if (length(rewritten) != 1 || !IsA(lfirst(rewritten), Query))
71                 elog(ERROR, "unexpected rewrite result");
72         query = (Query *) lfirst(rewritten);
73         if (query->commandType != CMD_SELECT)
74                 elog(ERROR, "unexpected rewrite result");
75
76         if (query->into)
77                 ereport(ERROR,
78                                 (errcode(ERRCODE_SYNTAX_ERROR),
79                                  errmsg("DECLARE CURSOR may not specify INTO")));
80         if (query->rowMarks != NIL)
81                 ereport(ERROR,
82                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
83                                  errmsg("DECLARE CURSOR ... FOR UPDATE is not supported"),
84                                  errdetail("Cursors must be READ ONLY.")));
85
86         plan = planner(query, true, stmt->options);
87
88         /*
89          * Create a portal and copy the query and plan into its memory context.
90          * (If a duplicate cursor name already exists, warn and drop it.)
91          */
92         portal = CreatePortal(stmt->portalname, true, false);
93
94         oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
95
96         query = copyObject(query);
97         plan = copyObject(plan);
98
99         PortalDefineQuery(portal,
100                                           NULL,         /* unfortunately don't have sourceText */
101                                           "SELECT",     /* cursor's query is always a SELECT */
102                                           makeList1(query),
103                                           makeList1(plan),
104                                           PortalGetHeapMemory(portal));
105
106         MemoryContextSwitchTo(oldContext);
107
108         /*
109          * Set up options for portal.
110          *
111          * If the user didn't specify a SCROLL type, allow or disallow
112          * scrolling based on whether it would require any additional
113          * runtime overhead to do so.
114          */
115         portal->cursorOptions = stmt->options;
116         if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
117         {
118                 if (ExecSupportsBackwardScan(plan))
119                         portal->cursorOptions |= CURSOR_OPT_SCROLL;
120                 else
121                         portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
122         }
123
124         /*
125          * Start execution --- never any params for a cursor.
126          */
127         PortalStart(portal, NULL);
128
129         Assert(portal->strategy == PORTAL_ONE_SELECT);
130
131         /*
132          * We're done; the query won't actually be run until PerformPortalFetch
133          * is called.
134          */
135 }
136
137 /*
138  * PerformPortalFetch
139  *              Execute SQL FETCH or MOVE command.
140  *
141  *      stmt: parsetree node for command
142  *      dest: where to send results
143  *      completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
144  *              in which to store a command completion status string.
145  *
146  * completionTag may be NULL if caller doesn't want a status string.
147  */
148 void
149 PerformPortalFetch(FetchStmt *stmt,
150                                    DestReceiver *dest,
151                                    char *completionTag)
152 {
153         Portal          portal;
154         long            nprocessed;
155
156         /*
157          * Disallow empty-string cursor name (conflicts with protocol-level
158          * unnamed portal).
159          */
160         if (!stmt->portalname || stmt->portalname[0] == '\0')
161                 ereport(ERROR,
162                                 (errcode(ERRCODE_INVALID_CURSOR_NAME),
163                                  errmsg("invalid cursor name: must not be empty")));
164
165         /* get the portal from the portal name */
166         portal = GetPortalByName(stmt->portalname);
167         if (!PortalIsValid(portal))
168         {
169                 /* FIXME: shouldn't this be an ERROR? */
170                 ereport(WARNING,
171                                 (errcode(ERRCODE_UNDEFINED_CURSOR),
172                                  errmsg("portal \"%s\" does not exist", stmt->portalname),
173                                  errfunction("PerformPortalFetch"))); /* for ecpg */
174                 if (completionTag)
175                         strcpy(completionTag, stmt->ismove ? "MOVE 0" : "FETCH 0");
176                 return;
177         }
178
179         /* Adjust dest if needed.  MOVE wants destination None */
180         if (stmt->ismove)
181                 dest = None_Receiver;
182
183         /* Do it */
184         nprocessed = PortalRunFetch(portal,
185                                                                 stmt->direction,
186                                                                 stmt->howMany,
187                                                                 dest);
188
189         /* Return command status if wanted */
190         if (completionTag)
191                 snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
192                                  stmt->ismove ? "MOVE" : "FETCH",
193                                  nprocessed);
194 }
195
196 /*
197  * PerformPortalClose
198  *              Close a cursor.
199  */
200 void
201 PerformPortalClose(const char *name)
202 {
203         Portal          portal;
204
205         /*
206          * Disallow empty-string cursor name (conflicts with protocol-level
207          * unnamed portal).
208          */
209         if (!name || 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(WARNING,
221                                 (errcode(ERRCODE_UNDEFINED_CURSOR),
222                                  errmsg("portal \"%s\" does not exist", name),
223                                  errfunction("PerformPortalClose"))); /* for ecpg */
224                 return;
225         }
226
227         /*
228          * Note: PortalCleanup is called as a side-effect
229          */
230         PortalDrop(portal, false);
231 }
232
233 /*
234  * PortalCleanup
235  *
236  * Clean up a portal when it's dropped.  This is the standard cleanup hook
237  * for portals.
238  */
239 void
240 PortalCleanup(Portal portal, bool isError)
241 {
242         QueryDesc  *queryDesc;
243
244         /*
245          * sanity checks
246          */
247         AssertArg(PortalIsValid(portal));
248         AssertArg(portal->cleanup == PortalCleanup);
249
250         /*
251          * Shut down executor, if still running.  We skip this during error
252          * abort, since other mechanisms will take care of releasing executor
253          * resources, and we can't be sure that ExecutorEnd itself wouldn't fail.
254          */
255         queryDesc = PortalGetQueryDesc(portal);
256         if (queryDesc)
257         {
258                 portal->queryDesc = NULL;
259                 if (!isError)
260                         ExecutorEnd(queryDesc);
261         }
262 }
263
264 /*
265  * PersistHoldablePortal
266  *
267  * Prepare the specified Portal for access outside of the current
268  * transaction. When this function returns, all future accesses to the
269  * portal must be done via the Tuplestore (not by invoking the
270  * executor).
271  */
272 void
273 PersistHoldablePortal(Portal portal)
274 {
275         QueryDesc *queryDesc = PortalGetQueryDesc(portal);
276         MemoryContext savePortalContext;
277         MemoryContext saveQueryContext;
278         MemoryContext oldcxt;
279
280         /*
281          * If we're preserving a holdable portal, we had better be
282          * inside the transaction that originally created it.
283          */
284         Assert(portal->createXact == GetCurrentTransactionId());
285         Assert(queryDesc != NULL);
286         Assert(portal->portalReady);
287         Assert(!portal->portalDone);
288
289         /*
290          * Caller must have created the tuplestore already.
291          */
292         Assert(portal->holdContext != NULL);
293         Assert(portal->holdStore != NULL);
294
295         /*
296          * Before closing down the executor, we must copy the tupdesc into
297          * long-term memory, since it was created in executor memory.
298          */
299         oldcxt = MemoryContextSwitchTo(portal->holdContext);
300
301         portal->tupDesc = CreateTupleDescCopy(portal->tupDesc);
302
303         MemoryContextSwitchTo(oldcxt);
304
305         /*
306          * Check for improper portal use, and mark portal active.
307          */
308         if (portal->portalActive)
309                 ereport(ERROR,
310                                 (errcode(ERRCODE_OBJECT_IN_USE),
311                                  errmsg("portal \"%s\" already active", portal->name)));
312         portal->portalActive = true;
313
314         /*
315          * Set global portal context pointers.
316          */
317         savePortalContext = PortalContext;
318         PortalContext = PortalGetHeapMemory(portal);
319         saveQueryContext = QueryContext;
320         QueryContext = portal->queryContext;
321
322         MemoryContextSwitchTo(PortalContext);
323
324         /*
325          * Rewind the executor: we need to store the entire result set in
326          * the tuplestore, so that subsequent backward FETCHs can be
327          * processed.
328          */
329         ExecutorRewind(queryDesc);
330
331         /* Change the destination to output to the tuplestore */
332         queryDesc->dest = CreateDestReceiver(Tuplestore, portal);
333
334         /* Fetch the result set into the tuplestore */
335         ExecutorRun(queryDesc, ForwardScanDirection, 0L);
336
337         (*queryDesc->dest->destroy) (queryDesc->dest);
338         queryDesc->dest = NULL;
339
340         /*
341          * Now shut down the inner executor.
342          */
343         portal->queryDesc = NULL;       /* prevent double shutdown */
344         ExecutorEnd(queryDesc);
345
346         /* Mark portal not active */
347         portal->portalActive = false;
348
349         PortalContext = savePortalContext;
350         QueryContext = saveQueryContext;
351
352         /*
353          * Reset the position in the result set: ideally, this could be
354          * implemented by just skipping straight to the tuple # that we need
355          * to be at, but the tuplestore API doesn't support that. So we
356          * start at the beginning of the tuplestore and iterate through it
357          * until we reach where we need to be.  FIXME someday?
358          */
359         MemoryContextSwitchTo(portal->holdContext);
360
361         if (!portal->atEnd)
362         {
363                 long    store_pos;
364
365                 if (portal->posOverflow)                /* oops, cannot trust portalPos */
366                         ereport(ERROR,
367                                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
368                                          errmsg("could not reposition held cursor")));
369
370                 tuplestore_rescan(portal->holdStore);
371
372                 for (store_pos = 0; store_pos < portal->portalPos; store_pos++)
373                 {
374                         HeapTuple tup;
375                         bool should_free;
376
377                         tup = tuplestore_gettuple(portal->holdStore, true,
378                                                                           &should_free);
379
380                         if (tup == NULL)
381                                 elog(ERROR, "unexpected end of tuple stream");
382
383                         if (should_free)
384                                 pfree(tup);
385                 }
386         }
387
388         MemoryContextSwitchTo(oldcxt);
389
390         /*
391          * We can now release any subsidiary memory of the portal's heap
392          * context; we'll never use it again.  The executor already dropped
393          * its context, but this will clean up anything that glommed onto
394          * the portal's heap via PortalContext.
395          */
396         MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
397 }