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