1 /*-------------------------------------------------------------------------
4 * backend portal memory management
6 * Portals are objects representing the execution state of a query.
7 * This module provides memory management services for portals, but it
8 * doesn't actually run the executor for them.
11 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
12 * Portions Copyright (c) 1994, Regents of the University of California
15 * $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.79 2005/05/11 18:05:37 momjian Exp $
17 *-------------------------------------------------------------------------
21 #include "miscadmin.h"
22 #include "commands/portalcmds.h"
23 #include "executor/executor.h"
24 #include "utils/hsearch.h"
25 #include "utils/memutils.h"
26 #include "utils/portal.h"
29 * Estimate of the maximum number of open portals a user would have,
30 * used in initially sizing the PortalHashTable in EnablePortalManager().
31 * Since the hash table can expand, there's no need to make this overly
32 * generous, and keeping it small avoids unnecessary overhead in the
33 * hash_seq_search() calls executed during transaction end.
35 #define PORTALS_PER_USER 16
43 #define MAX_PORTALNAME_LEN NAMEDATALEN
45 typedef struct portalhashent
47 char portalname[MAX_PORTALNAME_LEN];
51 static HTAB *PortalHashTable = NULL;
53 #define PortalHashTableLookup(NAME, PORTAL) \
55 PortalHashEnt *hentry; char key[MAX_PORTALNAME_LEN]; \
57 MemSet(key, 0, MAX_PORTALNAME_LEN); \
58 StrNCpy(key, NAME, MAX_PORTALNAME_LEN); \
59 hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
60 key, HASH_FIND, NULL); \
62 PORTAL = hentry->portal; \
67 #define PortalHashTableInsert(PORTAL, NAME) \
69 PortalHashEnt *hentry; bool found; char key[MAX_PORTALNAME_LEN]; \
71 MemSet(key, 0, MAX_PORTALNAME_LEN); \
72 StrNCpy(key, NAME, MAX_PORTALNAME_LEN); \
73 hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
74 key, HASH_ENTER, &found); \
77 (errcode(ERRCODE_OUT_OF_MEMORY), \
78 errmsg("out of memory"))); \
80 elog(ERROR, "duplicate portal name"); \
81 hentry->portal = PORTAL; \
82 /* To avoid duplicate storage, make PORTAL->name point to htab entry */ \
83 PORTAL->name = hentry->portalname; \
86 #define PortalHashTableDelete(PORTAL) \
88 PortalHashEnt *hentry; char key[MAX_PORTALNAME_LEN]; \
90 MemSet(key, 0, MAX_PORTALNAME_LEN); \
91 StrNCpy(key, PORTAL->name, MAX_PORTALNAME_LEN); \
92 hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
93 key, HASH_REMOVE, NULL); \
95 elog(WARNING, "trying to delete portal name that does not exist"); \
98 static MemoryContext PortalMemory = NULL;
101 /* ----------------------------------------------------------------
102 * public portal interface functions
103 * ----------------------------------------------------------------
107 * EnablePortalManager
108 * Enables the portal management module at backend startup.
111 EnablePortalManager(void)
115 Assert(PortalMemory == NULL);
117 PortalMemory = AllocSetContextCreate(TopMemoryContext,
119 ALLOCSET_DEFAULT_MINSIZE,
120 ALLOCSET_DEFAULT_INITSIZE,
121 ALLOCSET_DEFAULT_MAXSIZE);
123 ctl.keysize = MAX_PORTALNAME_LEN;
124 ctl.entrysize = sizeof(PortalHashEnt);
127 * use PORTALS_PER_USER as a guess of how many hash table entries to
130 PortalHashTable = hash_create("Portal hash", PORTALS_PER_USER,
136 * Returns a portal given a portal name, or NULL if name not found.
139 GetPortalByName(const char *name)
143 if (PointerIsValid(name))
144 PortalHashTableLookup(name, portal);
153 * Returns a new portal given a name.
155 * allowDup: if true, automatically drop any pre-existing portal of the
156 * same name (if false, an error is raised).
158 * dupSilent: if true, don't even emit a WARNING.
161 CreatePortal(const char *name, bool allowDup, bool dupSilent)
165 AssertArg(PointerIsValid(name));
167 portal = GetPortalByName(name);
168 if (PortalIsValid(portal))
172 (errcode(ERRCODE_DUPLICATE_CURSOR),
173 errmsg("cursor \"%s\" already exists", name)));
176 (errcode(ERRCODE_DUPLICATE_CURSOR),
177 errmsg("closing existing cursor \"%s\"",
179 PortalDrop(portal, false);
182 /* make new portal structure */
183 portal = (Portal) MemoryContextAllocZero(PortalMemory, sizeof *portal);
185 /* initialize portal heap context; typically it won't store much */
186 portal->heap = AllocSetContextCreate(PortalMemory,
188 ALLOCSET_SMALL_MINSIZE,
189 ALLOCSET_SMALL_INITSIZE,
190 ALLOCSET_SMALL_MAXSIZE);
192 /* create a resource owner for the portal */
193 portal->resowner = ResourceOwnerCreate(CurTransactionResourceOwner,
196 /* initialize portal fields that don't start off zero */
197 portal->cleanup = PortalCleanup;
198 portal->createSubid = GetCurrentSubTransactionId();
199 portal->strategy = PORTAL_MULTI_QUERY;
200 portal->cursorOptions = CURSOR_OPT_NO_SCROLL;
201 portal->atStart = true;
202 portal->atEnd = true; /* disallow fetches until query is set */
204 /* put portal in table (sets portal->name) */
205 PortalHashTableInsert(portal, name);
212 * Create a new portal, assigning it a random nonconflicting name.
215 CreateNewPortal(void)
217 static unsigned int unnamed_portal_count = 0;
219 char portalname[MAX_PORTALNAME_LEN];
221 /* Select a nonconflicting name */
224 unnamed_portal_count++;
225 sprintf(portalname, "<unnamed portal %u>", unnamed_portal_count);
226 if (GetPortalByName(portalname) == NULL)
230 return CreatePortal(portalname, false, false);
235 * A simple subroutine to establish a portal's query.
237 * Notes: commandTag shall be NULL if and only if the original query string
238 * (before rewriting) was an empty string. Also, the passed commandTag must
239 * be a pointer to a constant string, since it is not copied. The caller is
240 * responsible for ensuring that the passed sourceText (if any), parse and
241 * plan trees have adequate lifetime. Also, queryContext must accurately
242 * describe the location of the parse and plan trees.
245 PortalDefineQuery(Portal portal,
246 const char *sourceText,
247 const char *commandTag,
250 MemoryContext queryContext)
252 AssertArg(PortalIsValid(portal));
253 AssertState(portal->queryContext == NULL); /* else defined already */
255 Assert(list_length(parseTrees) == list_length(planTrees));
257 Assert(commandTag != NULL || parseTrees == NIL);
259 portal->sourceText = sourceText;
260 portal->commandTag = commandTag;
261 portal->parseTrees = parseTrees;
262 portal->planTrees = planTrees;
263 portal->queryContext = queryContext;
267 * PortalCreateHoldStore
268 * Create the tuplestore for a portal.
271 PortalCreateHoldStore(Portal portal)
273 MemoryContext oldcxt;
275 Assert(portal->holdContext == NULL);
276 Assert(portal->holdStore == NULL);
279 * Create the memory context that is used for storage of the tuple
280 * set. Note this is NOT a child of the portal's heap memory.
282 portal->holdContext =
283 AllocSetContextCreate(PortalMemory,
285 ALLOCSET_DEFAULT_MINSIZE,
286 ALLOCSET_DEFAULT_INITSIZE,
287 ALLOCSET_DEFAULT_MAXSIZE);
289 /* Create the tuple store, selecting cross-transaction temp files. */
290 oldcxt = MemoryContextSwitchTo(portal->holdContext);
292 /* XXX: Should maintenance_work_mem be used for the portal size? */
293 portal->holdStore = tuplestore_begin_heap(true, true, work_mem);
295 MemoryContextSwitchTo(oldcxt);
300 * Destroy the portal.
303 PortalDrop(Portal portal, bool isTopCommit)
305 AssertArg(PortalIsValid(portal));
307 /* Not sure if this case can validly happen or not... */
308 if (portal->status == PORTAL_ACTIVE)
309 elog(ERROR, "cannot drop active portal");
312 * Remove portal from hash table. Because we do this first, we will
313 * not come back to try to remove the portal again if there's any
314 * error in the subsequent steps. Better to leak a little memory than
315 * to get into an infinite error-recovery loop.
317 PortalHashTableDelete(portal);
319 /* let portalcmds.c clean up the state it knows about */
320 if (PointerIsValid(portal->cleanup))
321 (*portal->cleanup) (portal);
324 * Release any resources still attached to the portal. There are
325 * several cases being covered here:
327 * Top transaction commit (indicated by isTopCommit): normally we should
328 * do nothing here and let the regular end-of-transaction resource
329 * releasing mechanism handle these resources too. However, if we
330 * have a FAILED portal (eg, a cursor that got an error), we'd better
331 * clean up its resources to avoid resource-leakage warning messages.
333 * Sub transaction commit: never comes here at all, since we don't kill
334 * any portals in AtSubCommit_Portals().
336 * Main or sub transaction abort: we will do nothing here because
337 * portal->resowner was already set NULL; the resources were already
338 * cleaned up in transaction abort.
340 * Ordinary portal drop: must release resources. However, if the portal
341 * is not FAILED then we do not release its locks. The locks become
342 * the responsibility of the transaction's ResourceOwner (since it is
343 * the parent of the portal's owner) and will be released when the
344 * transaction eventually ends.
346 if (portal->resowner &&
347 (!isTopCommit || portal->status == PORTAL_FAILED))
349 bool isCommit = (portal->status != PORTAL_FAILED);
351 ResourceOwnerRelease(portal->resowner,
352 RESOURCE_RELEASE_BEFORE_LOCKS,
354 ResourceOwnerRelease(portal->resowner,
355 RESOURCE_RELEASE_LOCKS,
357 ResourceOwnerRelease(portal->resowner,
358 RESOURCE_RELEASE_AFTER_LOCKS,
360 ResourceOwnerDelete(portal->resowner);
362 portal->resowner = NULL;
365 * Delete tuplestore if present. We should do this even under error
366 * conditions; since the tuplestore would have been using cross-
367 * transaction storage, its temp files need to be explicitly deleted.
369 if (portal->holdStore)
371 MemoryContext oldcontext;
373 oldcontext = MemoryContextSwitchTo(portal->holdContext);
374 tuplestore_end(portal->holdStore);
375 MemoryContextSwitchTo(oldcontext);
376 portal->holdStore = NULL;
379 /* delete tuplestore storage, if any */
380 if (portal->holdContext)
381 MemoryContextDelete(portal->holdContext);
383 /* release subsidiary storage */
384 MemoryContextDelete(PortalGetHeapMemory(portal));
386 /* release portal struct (it's in PortalMemory) */
391 * DropDependentPortals
392 * Drop any portals using the specified context as queryContext.
394 * This is normally used to make sure we can safely drop a prepared statement.
397 DropDependentPortals(MemoryContext queryContext)
399 HASH_SEQ_STATUS status;
400 PortalHashEnt *hentry;
402 hash_seq_init(&status, PortalHashTable);
404 while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
406 Portal portal = hentry->portal;
408 if (portal->queryContext == queryContext)
409 PortalDrop(portal, false);
415 * Pre-commit processing for portals.
417 * Any holdable cursors created in this transaction need to be converted to
418 * materialized form, since we are going to close down the executor and
419 * release locks. Other portals are not touched yet.
421 * Returns TRUE if any holdable cursors were processed, FALSE if not.
424 CommitHoldablePortals(void)
427 HASH_SEQ_STATUS status;
428 PortalHashEnt *hentry;
430 hash_seq_init(&status, PortalHashTable);
432 while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
434 Portal portal = hentry->portal;
436 /* Is it a holdable portal created in the current xact? */
437 if ((portal->cursorOptions & CURSOR_OPT_HOLD) &&
438 portal->createSubid != InvalidSubTransactionId &&
439 portal->status == PORTAL_READY)
442 * We are exiting the transaction that created a holdable
443 * cursor. Instead of dropping the portal, prepare it for
444 * access by later transactions.
446 * Note that PersistHoldablePortal() must release all resources
447 * used by the portal that are local to the creating
450 PortalCreateHoldStore(portal);
451 PersistHoldablePortal(portal);
454 * Any resources belonging to the portal will be released in
455 * the upcoming transaction-wide cleanup; the portal will no
456 * longer have its own resources.
458 portal->resowner = NULL;
461 * Having successfully exported the holdable cursor, mark it
462 * as not belonging to this transaction.
464 portal->createSubid = InvalidSubTransactionId;
474 * Pre-commit processing for portals.
476 * Remove all non-holdable portals created in this transaction.
477 * Portals remaining from prior transactions should be left untouched.
480 AtCommit_Portals(void)
482 HASH_SEQ_STATUS status;
483 PortalHashEnt *hentry;
485 hash_seq_init(&status, PortalHashTable);
487 while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
489 Portal portal = hentry->portal;
492 * Do not touch active portals --- this can only happen in the
493 * case of a multi-transaction utility command, such as VACUUM.
495 * Note however that any resource owner attached to such a portal is
496 * still going to go away, so don't leave a dangling pointer.
498 if (portal->status == PORTAL_ACTIVE)
500 portal->resowner = NULL;
505 * Do nothing to cursors held over from a previous transaction
506 * (including holdable ones just frozen by CommitHoldablePortals).
508 if (portal->createSubid == InvalidSubTransactionId)
511 /* Zap all non-holdable portals */
512 PortalDrop(portal, true);
514 /* Restart the iteration */
515 hash_seq_init(&status, PortalHashTable);
520 * Abort processing for portals.
522 * At this point we reset "active" status and run the cleanup hook if
523 * present, but we can't release memory until the cleanup call.
525 * The reason we need to reset active is so that we can replace the unnamed
526 * portal, else we'll fail to execute ROLLBACK when it arrives.
529 AtAbort_Portals(void)
531 HASH_SEQ_STATUS status;
532 PortalHashEnt *hentry;
534 hash_seq_init(&status, PortalHashTable);
536 while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
538 Portal portal = hentry->portal;
540 if (portal->status == PORTAL_ACTIVE)
541 portal->status = PORTAL_FAILED;
544 * Do nothing else to cursors held over from a previous
547 if (portal->createSubid == InvalidSubTransactionId)
550 /* let portalcmds.c clean up the state it knows about */
551 if (PointerIsValid(portal->cleanup))
553 (*portal->cleanup) (portal);
554 portal->cleanup = NULL;
558 * Any resources belonging to the portal will be released in the
559 * upcoming transaction-wide cleanup; they will be gone before we
562 portal->resowner = NULL;
567 * Post-abort cleanup for portals.
569 * Delete all portals not held over from prior transactions. */
571 AtCleanup_Portals(void)
573 HASH_SEQ_STATUS status;
574 PortalHashEnt *hentry;
576 hash_seq_init(&status, PortalHashTable);
578 while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
580 Portal portal = hentry->portal;
582 /* Do nothing to cursors held over from a previous transaction */
583 if (portal->createSubid == InvalidSubTransactionId)
585 Assert(portal->status != PORTAL_ACTIVE);
586 Assert(portal->resowner == NULL);
591 PortalDrop(portal, false);
596 * Pre-subcommit processing for portals.
598 * Reassign the portals created in the current subtransaction to the parent
602 AtSubCommit_Portals(SubTransactionId mySubid,
603 SubTransactionId parentSubid,
604 ResourceOwner parentXactOwner)
606 HASH_SEQ_STATUS status;
607 PortalHashEnt *hentry;
609 hash_seq_init(&status, PortalHashTable);
611 while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
613 Portal portal = hentry->portal;
615 if (portal->createSubid == mySubid)
617 portal->createSubid = parentSubid;
618 if (portal->resowner)
619 ResourceOwnerNewParent(portal->resowner, parentXactOwner);
625 * Subtransaction abort handling for portals.
627 * Deactivate portals created during the failed subtransaction.
628 * Note that per AtSubCommit_Portals, this will catch portals created
629 * in descendants of the subtransaction too.
631 * We don't destroy any portals here; that's done in AtSubCleanup_Portals.
634 AtSubAbort_Portals(SubTransactionId mySubid,
635 SubTransactionId parentSubid,
636 ResourceOwner parentXactOwner)
638 HASH_SEQ_STATUS status;
639 PortalHashEnt *hentry;
641 hash_seq_init(&status, PortalHashTable);
643 while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
645 Portal portal = hentry->portal;
647 if (portal->createSubid != mySubid)
651 * Force any active portals of my own transaction into FAILED
652 * state. This is mostly to ensure that a portal running a FETCH
653 * will go FAILED if the underlying cursor fails. (Note we do NOT
654 * want to do this to upper-level portals, since they may be able
657 * This is only needed to dodge the sanity check in PortalDrop.
659 if (portal->status == PORTAL_ACTIVE)
660 portal->status = PORTAL_FAILED;
663 * If the portal is READY then allow it to survive into the parent
664 * transaction; otherwise shut it down.
666 * Currently, we can't actually support that because the portal's
667 * query might refer to objects created or changed in the failed
668 * subtransaction, leading to crashes if execution is resumed.
669 * So, even READY portals are deleted. It would be nice to detect
670 * whether the query actually depends on any such object, instead.
673 if (portal->status == PORTAL_READY)
675 portal->createSubid = parentSubid;
676 if (portal->resowner)
677 ResourceOwnerNewParent(portal->resowner, parentXactOwner);
682 /* let portalcmds.c clean up the state it knows about */
683 if (PointerIsValid(portal->cleanup))
685 (*portal->cleanup) (portal);
686 portal->cleanup = NULL;
690 * Any resources belonging to the portal will be released in
691 * the upcoming transaction-wide cleanup; they will be gone
692 * before we run PortalDrop.
694 portal->resowner = NULL;
700 * Post-subabort cleanup for portals.
702 * Drop all portals created in the failed subtransaction (but note that
703 * we will not drop any that were reassigned to the parent above).
706 AtSubCleanup_Portals(SubTransactionId mySubid)
708 HASH_SEQ_STATUS status;
709 PortalHashEnt *hentry;
711 hash_seq_init(&status, PortalHashTable);
713 while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
715 Portal portal = hentry->portal;
717 if (portal->createSubid != mySubid)
721 PortalDrop(portal, false);