1 /*-------------------------------------------------------------------------
4 * backend portal memory context management stuff
6 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.55 2003/04/29 03:21:29 tgl Exp $
13 *-------------------------------------------------------------------------
17 * A "Portal" is a structure used to keep track of cursor queries.
19 * When the backend sees a "declare cursor" query, it allocates a
20 * "PortalData" structure, plans the query and then stores the query
21 * in the portal without executing it. Later, when the backend
24 * the system looks up the portal named "foo" in the portal table,
25 * gets the planned query and then calls the executor with a count
26 * of 1. The executor then runs the query and returns a single
27 * tuple. The problem is that we have to hold onto the state of the
28 * portal query until we see a "close". This means we have to be
29 * careful about memory management.
31 * I hope this makes things clearer to whoever reads this -cim 2/22/91
36 #include "commands/portalcmds.h"
37 #include "executor/executor.h"
38 #include "utils/hsearch.h"
39 #include "utils/memutils.h"
40 #include "utils/portal.h"
43 * estimate of the maximum number of open portals a user would have,
44 * used in initially sizing the PortalHashTable in EnablePortalManager()
46 #define PORTALS_PER_USER 64
54 #define MAX_PORTALNAME_LEN NAMEDATALEN
56 typedef struct portalhashent
58 char portalname[MAX_PORTALNAME_LEN];
62 static HTAB *PortalHashTable = NULL;
64 #define PortalHashTableLookup(NAME, PORTAL) \
66 PortalHashEnt *hentry; char key[MAX_PORTALNAME_LEN]; \
68 MemSet(key, 0, MAX_PORTALNAME_LEN); \
69 snprintf(key, MAX_PORTALNAME_LEN - 1, "%s", NAME); \
70 hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
71 key, HASH_FIND, NULL); \
73 PORTAL = hentry->portal; \
78 #define PortalHashTableInsert(PORTAL) \
80 PortalHashEnt *hentry; bool found; char key[MAX_PORTALNAME_LEN]; \
82 MemSet(key, 0, MAX_PORTALNAME_LEN); \
83 snprintf(key, MAX_PORTALNAME_LEN - 1, "%s", PORTAL->name); \
84 hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
85 key, HASH_ENTER, &found); \
87 elog(ERROR, "out of memory in PortalHashTable"); \
89 elog(WARNING, "trying to insert a portal name that exists."); \
90 hentry->portal = PORTAL; \
93 #define PortalHashTableDelete(PORTAL) \
95 PortalHashEnt *hentry; char key[MAX_PORTALNAME_LEN]; \
97 MemSet(key, 0, MAX_PORTALNAME_LEN); \
98 snprintf(key, MAX_PORTALNAME_LEN - 1, "%s", PORTAL->name); \
99 hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
100 key, HASH_REMOVE, NULL); \
101 if (hentry == NULL) \
102 elog(WARNING, "trying to delete portal name that does not exist."); \
105 static MemoryContext PortalMemory = NULL;
108 /* ----------------------------------------------------------------
109 * public portal interface functions
110 * ----------------------------------------------------------------
114 * EnablePortalManager
115 * Enables the portal management module at backend startup.
118 EnablePortalManager(void)
122 Assert(PortalMemory == NULL);
124 PortalMemory = AllocSetContextCreate(TopMemoryContext,
126 ALLOCSET_DEFAULT_MINSIZE,
127 ALLOCSET_DEFAULT_INITSIZE,
128 ALLOCSET_DEFAULT_MAXSIZE);
130 ctl.keysize = MAX_PORTALNAME_LEN;
131 ctl.entrysize = sizeof(PortalHashEnt);
134 * use PORTALS_PER_USER as a guess of how many hash table entries to
137 PortalHashTable = hash_create("Portal hash", PORTALS_PER_USER,
143 * Returns a portal given a portal name, or NULL if name not found.
146 GetPortalByName(const char *name)
150 if (PointerIsValid(name))
151 PortalHashTableLookup(name, portal);
160 * Attaches a QueryDesc to the specified portal. This should be
161 * called only after successfully doing ExecutorStart for the query.
163 * Note that in the case of DECLARE CURSOR, some Portal options have
164 * already been set in portalcmds.c's PreparePortal(). This is grotty.
167 PortalSetQuery(Portal portal, QueryDesc *queryDesc)
169 AssertArg(PortalIsValid(portal));
172 * If the user didn't specify a SCROLL type, allow or disallow
173 * scrolling based on whether it would require any additional
174 * runtime overhead to do so.
176 if (portal->scrollType == DEFAULT_SCROLL)
178 if (ExecSupportsBackwardScan(queryDesc->plantree))
179 portal->scrollType = ENABLE_SCROLL;
181 portal->scrollType = DISABLE_SCROLL;
184 portal->queryDesc = queryDesc;
185 portal->executorRunning = true; /* now need to shut down executor */
187 portal->atStart = true;
188 portal->atEnd = false; /* allow fetches */
189 portal->portalPos = 0;
190 portal->posOverflow = false;
195 * Returns a new portal given a name.
197 * An elog(WARNING) is emitted if portal name is in use (existing
198 * portal is returned!)
201 CreatePortal(const char *name)
205 AssertArg(PointerIsValid(name));
207 portal = GetPortalByName(name);
208 if (PortalIsValid(portal))
210 elog(WARNING, "CreatePortal: portal \"%s\" already exists", name);
214 /* make new portal structure */
215 portal = (Portal) MemoryContextAlloc(PortalMemory, sizeof *portal);
217 /* initialize portal name */
218 portal->name = MemoryContextStrdup(PortalMemory, name);
220 /* initialize portal heap context */
221 portal->heap = AllocSetContextCreate(PortalMemory,
223 ALLOCSET_DEFAULT_MINSIZE,
224 ALLOCSET_DEFAULT_INITSIZE,
225 ALLOCSET_DEFAULT_MAXSIZE);
227 /* initialize portal query */
228 portal->queryDesc = NULL;
229 portal->cleanup = PortalCleanup;
230 portal->scrollType = DEFAULT_SCROLL;
231 portal->executorRunning = false;
232 portal->holdOpen = false;
233 portal->createXact = GetCurrentTransactionId();
234 portal->holdStore = NULL;
235 portal->holdContext = NULL;
236 portal->atStart = true;
237 portal->atEnd = true; /* disallow fetches until query is set */
238 portal->portalPos = 0;
239 portal->posOverflow = false;
241 /* put portal in table */
242 PortalHashTableInsert(portal);
249 * Destroy the portal.
251 * isError: if true, we are destroying portals at the end of a failed
252 * transaction. (This causes PortalCleanup to skip unneeded steps.)
255 PortalDrop(Portal portal, bool isError)
257 AssertArg(PortalIsValid(portal));
260 * Remove portal from hash table. Because we do this first, we will
261 * not come back to try to remove the portal again if there's any error
262 * in the subsequent steps. Better to leak a little memory than to get
263 * into an infinite error-recovery loop.
265 PortalHashTableDelete(portal);
267 /* let portalcmds.c clean up the state it knows about */
268 if (PointerIsValid(portal->cleanup))
269 (*portal->cleanup) (portal, isError);
271 /* delete tuplestore storage, if any */
272 if (portal->holdContext)
273 MemoryContextDelete(portal->holdContext);
275 /* release subsidiary storage */
276 if (PortalGetHeapMemory(portal))
277 MemoryContextDelete(PortalGetHeapMemory(portal));
279 /* release name and portal data (both are in PortalMemory) */
285 * Cleanup the portals created in the current transaction. If the
286 * transaction was aborted, all the portals created in this transaction
287 * should be removed. If the transaction was successfully committed, any
288 * holdable cursors created in this transaction need to be kept
289 * open. In any case, portals remaining from prior transactions should
292 * XXX This assumes that portals can be deleted in a random order, ie,
293 * no portal has a reference to any other (at least not one that will be
294 * exercised during deletion). I think this is okay at the moment, but
295 * we've had bugs of that ilk in the past. Keep a close eye on cursor
299 AtEOXact_portals(bool isCommit)
301 HASH_SEQ_STATUS status;
302 PortalHashEnt *hentry;
303 TransactionId xact = GetCurrentTransactionId();
305 hash_seq_init(&status, PortalHashTable);
307 while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
309 Portal portal = hentry->portal;
311 if (portal->createXact != xact)
314 if (portal->holdOpen && isCommit)
317 * We are exiting the transaction that created a holdable
318 * cursor. Instead of dropping the portal, prepare it for
319 * access by later transactions.
323 * Create the memory context that is used for storage of
324 * the held cursor's tuple set.
326 portal->holdContext =
327 AllocSetContextCreate(PortalMemory,
329 ALLOCSET_DEFAULT_MINSIZE,
330 ALLOCSET_DEFAULT_INITSIZE,
331 ALLOCSET_DEFAULT_MAXSIZE);
334 * Transfer data into the held tuplestore.
336 * Note that PersistHoldablePortal() must release all
337 * resources used by the portal that are local to the creating
340 PersistHoldablePortal(portal);
344 PortalDrop(portal, !isCommit);