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