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