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-2007, 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.98 2007/01/05 22:19:47 momjian Exp $
17 *-------------------------------------------------------------------------
21 #include "access/heapam.h"
22 #include "access/xact.h"
23 #include "catalog/pg_type.h"
24 #include "commands/portalcmds.h"
26 #include "miscadmin.h"
27 #include "utils/builtins.h"
28 #include "utils/memutils.h"
31 * Estimate of the maximum number of open portals a user would have,
32 * used in initially sizing the PortalHashTable in EnablePortalManager().
33 * Since the hash table can expand, there's no need to make this overly
34 * generous, and keeping it small avoids unnecessary overhead in the
35 * hash_seq_search() calls executed during transaction end.
37 #define PORTALS_PER_USER 16
45 #define MAX_PORTALNAME_LEN NAMEDATALEN
47 typedef struct portalhashent
49 char portalname[MAX_PORTALNAME_LEN];
53 static HTAB *PortalHashTable = NULL;
55 #define PortalHashTableLookup(NAME, PORTAL) \
57 PortalHashEnt *hentry; \
59 hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
60 (NAME), HASH_FIND, NULL); \
62 PORTAL = hentry->portal; \
67 #define PortalHashTableInsert(PORTAL, NAME) \
69 PortalHashEnt *hentry; bool found; \
71 hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
72 (NAME), HASH_ENTER, &found); \
74 elog(ERROR, "duplicate portal name"); \
75 hentry->portal = PORTAL; \
76 /* To avoid duplicate storage, make PORTAL->name point to htab entry */ \
77 PORTAL->name = hentry->portalname; \
80 #define PortalHashTableDelete(PORTAL) \
82 PortalHashEnt *hentry; \
84 hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
85 PORTAL->name, HASH_REMOVE, NULL); \
87 elog(WARNING, "trying to delete portal name that does not exist"); \
90 static MemoryContext PortalMemory = NULL;
93 /* ----------------------------------------------------------------
94 * public portal interface functions
95 * ----------------------------------------------------------------
100 * Enables the portal management module at backend startup.
103 EnablePortalManager(void)
107 Assert(PortalMemory == NULL);
109 PortalMemory = AllocSetContextCreate(TopMemoryContext,
111 ALLOCSET_DEFAULT_MINSIZE,
112 ALLOCSET_DEFAULT_INITSIZE,
113 ALLOCSET_DEFAULT_MAXSIZE);
115 ctl.keysize = MAX_PORTALNAME_LEN;
116 ctl.entrysize = sizeof(PortalHashEnt);
119 * use PORTALS_PER_USER as a guess of how many hash table entries to
122 PortalHashTable = hash_create("Portal hash", PORTALS_PER_USER,
128 * Returns a portal given a portal name, or NULL if name not found.
131 GetPortalByName(const char *name)
135 if (PointerIsValid(name))
136 PortalHashTableLookup(name, portal);
144 * PortalListGetPrimaryQuery
145 * Get the "primary" Query within a portal, ie, the one marked canSetTag.
147 * Returns NULL if no such Query. If multiple Query structs within the
148 * portal are marked canSetTag, returns the first one. Neither of these
149 * cases should occur in present usages of this function.
151 * Note: the reason this is just handed a List is so that prepared statements
152 * can share the code. For use with a portal, use PortalGetPrimaryQuery
153 * rather than calling this directly.
156 PortalListGetPrimaryQuery(List *parseTrees)
160 foreach(lc, parseTrees)
162 Query *query = (Query *) lfirst(lc);
164 Assert(IsA(query, Query));
165 if (query->canSetTag)
173 * Returns a new portal given a name.
175 * allowDup: if true, automatically drop any pre-existing portal of the
176 * same name (if false, an error is raised).
178 * dupSilent: if true, don't even emit a WARNING.
181 CreatePortal(const char *name, bool allowDup, bool dupSilent)
185 AssertArg(PointerIsValid(name));
187 portal = GetPortalByName(name);
188 if (PortalIsValid(portal))
192 (errcode(ERRCODE_DUPLICATE_CURSOR),
193 errmsg("cursor \"%s\" already exists", name)));
196 (errcode(ERRCODE_DUPLICATE_CURSOR),
197 errmsg("closing existing cursor \"%s\"",
199 PortalDrop(portal, false);
202 /* make new portal structure */
203 portal = (Portal) MemoryContextAllocZero(PortalMemory, sizeof *portal);
205 /* initialize portal heap context; typically it won't store much */
206 portal->heap = AllocSetContextCreate(PortalMemory,
208 ALLOCSET_SMALL_MINSIZE,
209 ALLOCSET_SMALL_INITSIZE,
210 ALLOCSET_SMALL_MAXSIZE);
212 /* create a resource owner for the portal */
213 portal->resowner = ResourceOwnerCreate(CurTransactionResourceOwner,
216 /* initialize portal fields that don't start off zero */
217 portal->status = PORTAL_NEW;
218 portal->cleanup = PortalCleanup;
219 portal->createSubid = GetCurrentSubTransactionId();
220 portal->strategy = PORTAL_MULTI_QUERY;
221 portal->cursorOptions = CURSOR_OPT_NO_SCROLL;
222 portal->atStart = true;
223 portal->atEnd = true; /* disallow fetches until query is set */
224 portal->visible = true;
225 portal->creation_time = GetCurrentStatementStartTimestamp();
227 /* put portal in table (sets portal->name) */
228 PortalHashTableInsert(portal, name);
235 * Create a new portal, assigning it a random nonconflicting name.
238 CreateNewPortal(void)
240 static unsigned int unnamed_portal_count = 0;
242 char portalname[MAX_PORTALNAME_LEN];
244 /* Select a nonconflicting name */
247 unnamed_portal_count++;
248 sprintf(portalname, "<unnamed portal %u>", unnamed_portal_count);
249 if (GetPortalByName(portalname) == NULL)
253 return CreatePortal(portalname, false, false);
258 * A simple subroutine to establish a portal's query.
260 * Notes: commandTag shall be NULL if and only if the original query string
261 * (before rewriting) was an empty string. Also, the passed commandTag must
262 * be a pointer to a constant string, since it is not copied. The caller is
263 * responsible for ensuring that the passed prepStmtName (if any), sourceText
264 * (if any), parse and plan trees have adequate lifetime. Also, queryContext
265 * must accurately describe the location of the parse trees.
268 PortalDefineQuery(Portal portal,
269 const char *prepStmtName,
270 const char *sourceText,
271 const char *commandTag,
274 MemoryContext queryContext)
276 AssertArg(PortalIsValid(portal));
277 AssertState(portal->queryContext == NULL); /* else defined already */
279 Assert(list_length(parseTrees) == list_length(planTrees));
281 Assert(commandTag != NULL || parseTrees == NIL);
283 portal->prepStmtName = prepStmtName;
284 portal->sourceText = sourceText;
285 portal->commandTag = commandTag;
286 portal->parseTrees = parseTrees;
287 portal->planTrees = planTrees;
288 portal->queryContext = queryContext;
292 * PortalCreateHoldStore
293 * Create the tuplestore for a portal.
296 PortalCreateHoldStore(Portal portal)
298 MemoryContext oldcxt;
300 Assert(portal->holdContext == NULL);
301 Assert(portal->holdStore == NULL);
304 * Create the memory context that is used for storage of the tuple set.
305 * Note this is NOT a child of the portal's heap memory.
307 portal->holdContext =
308 AllocSetContextCreate(PortalMemory,
310 ALLOCSET_DEFAULT_MINSIZE,
311 ALLOCSET_DEFAULT_INITSIZE,
312 ALLOCSET_DEFAULT_MAXSIZE);
314 /* Create the tuple store, selecting cross-transaction temp files. */
315 oldcxt = MemoryContextSwitchTo(portal->holdContext);
317 /* XXX: Should maintenance_work_mem be used for the portal size? */
318 portal->holdStore = tuplestore_begin_heap(true, true, work_mem);
320 MemoryContextSwitchTo(oldcxt);
325 * Destroy the portal.
328 PortalDrop(Portal portal, bool isTopCommit)
330 AssertArg(PortalIsValid(portal));
332 /* Not sure if this case can validly happen or not... */
333 if (portal->status == PORTAL_ACTIVE)
334 elog(ERROR, "cannot drop active portal");
337 * Remove portal from hash table. Because we do this first, we will not
338 * come back to try to remove the portal again if there's any error in the
339 * subsequent steps. Better to leak a little memory than to get into an
340 * infinite error-recovery loop.
342 PortalHashTableDelete(portal);
344 /* let portalcmds.c clean up the state it knows about */
345 if (PointerIsValid(portal->cleanup))
346 (*portal->cleanup) (portal);
349 * Release any resources still attached to the portal. There are several
350 * cases being covered here:
352 * Top transaction commit (indicated by isTopCommit): normally we should
353 * do nothing here and let the regular end-of-transaction resource
354 * releasing mechanism handle these resources too. However, if we have a
355 * FAILED portal (eg, a cursor that got an error), we'd better clean up
356 * its resources to avoid resource-leakage warning messages.
358 * Sub transaction commit: never comes here at all, since we don't kill
359 * any portals in AtSubCommit_Portals().
361 * Main or sub transaction abort: we will do nothing here because
362 * portal->resowner was already set NULL; the resources were already
363 * cleaned up in transaction abort.
365 * Ordinary portal drop: must release resources. However, if the portal
366 * is not FAILED then we do not release its locks. The locks become the
367 * responsibility of the transaction's ResourceOwner (since it is the
368 * parent of the portal's owner) and will be released when the transaction
371 if (portal->resowner &&
372 (!isTopCommit || portal->status == PORTAL_FAILED))
374 bool isCommit = (portal->status != PORTAL_FAILED);
376 ResourceOwnerRelease(portal->resowner,
377 RESOURCE_RELEASE_BEFORE_LOCKS,
379 ResourceOwnerRelease(portal->resowner,
380 RESOURCE_RELEASE_LOCKS,
382 ResourceOwnerRelease(portal->resowner,
383 RESOURCE_RELEASE_AFTER_LOCKS,
385 ResourceOwnerDelete(portal->resowner);
387 portal->resowner = NULL;
390 * Delete tuplestore if present. We should do this even under error
391 * conditions; since the tuplestore would have been using cross-
392 * transaction storage, its temp files need to be explicitly deleted.
394 if (portal->holdStore)
396 MemoryContext oldcontext;
398 oldcontext = MemoryContextSwitchTo(portal->holdContext);
399 tuplestore_end(portal->holdStore);
400 MemoryContextSwitchTo(oldcontext);
401 portal->holdStore = NULL;
404 /* delete tuplestore storage, if any */
405 if (portal->holdContext)
406 MemoryContextDelete(portal->holdContext);
408 /* release subsidiary storage */
409 MemoryContextDelete(PortalGetHeapMemory(portal));
411 /* release portal struct (it's in PortalMemory) */
416 * DropDependentPortals
417 * Drop any portals using the specified context as queryContext.
419 * This is normally used to make sure we can safely drop a prepared statement.
422 DropDependentPortals(MemoryContext queryContext)
424 HASH_SEQ_STATUS status;
425 PortalHashEnt *hentry;
427 hash_seq_init(&status, PortalHashTable);
429 while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
431 Portal portal = hentry->portal;
433 if (portal->queryContext == queryContext)
434 PortalDrop(portal, false);
440 * Pre-commit processing for portals.
442 * Any holdable cursors created in this transaction need to be converted to
443 * materialized form, since we are going to close down the executor and
444 * release locks. Other portals are not touched yet.
446 * Returns TRUE if any holdable cursors were processed, FALSE if not.
449 CommitHoldablePortals(void)
452 HASH_SEQ_STATUS status;
453 PortalHashEnt *hentry;
455 hash_seq_init(&status, PortalHashTable);
457 while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
459 Portal portal = hentry->portal;
461 /* Is it a holdable portal created in the current xact? */
462 if ((portal->cursorOptions & CURSOR_OPT_HOLD) &&
463 portal->createSubid != InvalidSubTransactionId &&
464 portal->status == PORTAL_READY)
467 * We are exiting the transaction that created a holdable cursor.
468 * Instead of dropping the portal, prepare it for access by later
471 * Note that PersistHoldablePortal() must release all resources
472 * used by the portal that are local to the creating transaction.
474 PortalCreateHoldStore(portal);
475 PersistHoldablePortal(portal);
478 * Any resources belonging to the portal will be released in the
479 * upcoming transaction-wide cleanup; the portal will no longer
480 * have its own resources.
482 portal->resowner = NULL;
485 * Having successfully exported the holdable cursor, mark it as
486 * not belonging to this transaction.
488 portal->createSubid = InvalidSubTransactionId;
498 * Pre-prepare processing for portals.
500 * Currently we refuse PREPARE if the transaction created any holdable
501 * cursors, since it's quite unclear what to do with one. However, this
502 * has the same API as CommitHoldablePortals and is invoked in the same
503 * way by xact.c, so that we can easily do something reasonable if anyone
504 * comes up with something reasonable to do.
506 * Returns TRUE if any holdable cursors were processed, FALSE if not.
509 PrepareHoldablePortals(void)
512 HASH_SEQ_STATUS status;
513 PortalHashEnt *hentry;
515 hash_seq_init(&status, PortalHashTable);
517 while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
519 Portal portal = hentry->portal;
521 /* Is it a holdable portal created in the current xact? */
522 if ((portal->cursorOptions & CURSOR_OPT_HOLD) &&
523 portal->createSubid != InvalidSubTransactionId &&
524 portal->status == PORTAL_READY)
527 * We are exiting the transaction that created a holdable cursor.
531 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
532 errmsg("cannot PREPARE a transaction that has created a cursor WITH HOLD")));
540 * Pre-commit processing for portals.
542 * Remove all non-holdable portals created in this transaction.
543 * Portals remaining from prior transactions should be left untouched.
546 AtCommit_Portals(void)
548 HASH_SEQ_STATUS status;
549 PortalHashEnt *hentry;
551 hash_seq_init(&status, PortalHashTable);
553 while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
555 Portal portal = hentry->portal;
558 * Do not touch active portals --- this can only happen in the case of
559 * a multi-transaction utility command, such as VACUUM.
561 * Note however that any resource owner attached to such a portal is
562 * still going to go away, so don't leave a dangling pointer.
564 if (portal->status == PORTAL_ACTIVE)
566 portal->resowner = NULL;
571 * Do nothing to cursors held over from a previous transaction
572 * (including holdable ones just frozen by CommitHoldablePortals).
574 if (portal->createSubid == InvalidSubTransactionId)
577 /* Zap all non-holdable portals */
578 PortalDrop(portal, true);
580 /* Restart the iteration */
581 hash_seq_init(&status, PortalHashTable);
586 * Abort processing for portals.
588 * At this point we reset "active" status and run the cleanup hook if
589 * present, but we can't release the portal's memory until the cleanup call.
591 * The reason we need to reset active is so that we can replace the unnamed
592 * portal, else we'll fail to execute ROLLBACK when it arrives.
595 AtAbort_Portals(void)
597 HASH_SEQ_STATUS status;
598 PortalHashEnt *hentry;
600 hash_seq_init(&status, PortalHashTable);
602 while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
604 Portal portal = hentry->portal;
606 if (portal->status == PORTAL_ACTIVE)
607 portal->status = PORTAL_FAILED;
610 * Do nothing else to cursors held over from a previous transaction.
612 if (portal->createSubid == InvalidSubTransactionId)
615 /* let portalcmds.c clean up the state it knows about */
616 if (PointerIsValid(portal->cleanup))
618 (*portal->cleanup) (portal);
619 portal->cleanup = NULL;
623 * Any resources belonging to the portal will be released in the
624 * upcoming transaction-wide cleanup; they will be gone before we run
627 portal->resowner = NULL;
630 * Although we can't delete the portal data structure proper, we can
631 * release any memory in subsidiary contexts, such as executor state.
632 * The cleanup hook was the last thing that might have needed data
635 MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
640 * Post-abort cleanup for portals.
642 * Delete all portals not held over from prior transactions. */
644 AtCleanup_Portals(void)
646 HASH_SEQ_STATUS status;
647 PortalHashEnt *hentry;
649 hash_seq_init(&status, PortalHashTable);
651 while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
653 Portal portal = hentry->portal;
655 /* Do nothing to cursors held over from a previous transaction */
656 if (portal->createSubid == InvalidSubTransactionId)
658 Assert(portal->status != PORTAL_ACTIVE);
659 Assert(portal->resowner == NULL);
664 PortalDrop(portal, false);
669 * Pre-subcommit processing for portals.
671 * Reassign the portals created in the current subtransaction to the parent
675 AtSubCommit_Portals(SubTransactionId mySubid,
676 SubTransactionId parentSubid,
677 ResourceOwner parentXactOwner)
679 HASH_SEQ_STATUS status;
680 PortalHashEnt *hentry;
682 hash_seq_init(&status, PortalHashTable);
684 while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
686 Portal portal = hentry->portal;
688 if (portal->createSubid == mySubid)
690 portal->createSubid = parentSubid;
691 if (portal->resowner)
692 ResourceOwnerNewParent(portal->resowner, parentXactOwner);
698 * Subtransaction abort handling for portals.
700 * Deactivate portals created during the failed subtransaction.
701 * Note that per AtSubCommit_Portals, this will catch portals created
702 * in descendants of the subtransaction too.
704 * We don't destroy any portals here; that's done in AtSubCleanup_Portals.
707 AtSubAbort_Portals(SubTransactionId mySubid,
708 SubTransactionId parentSubid,
709 ResourceOwner parentXactOwner)
711 HASH_SEQ_STATUS status;
712 PortalHashEnt *hentry;
714 hash_seq_init(&status, PortalHashTable);
716 while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
718 Portal portal = hentry->portal;
720 if (portal->createSubid != mySubid)
724 * Force any active portals of my own transaction into FAILED state.
725 * This is mostly to ensure that a portal running a FETCH will go
726 * FAILED if the underlying cursor fails. (Note we do NOT want to do
727 * this to upper-level portals, since they may be able to continue.)
729 * This is only needed to dodge the sanity check in PortalDrop.
731 if (portal->status == PORTAL_ACTIVE)
732 portal->status = PORTAL_FAILED;
735 * If the portal is READY then allow it to survive into the parent
736 * transaction; otherwise shut it down.
738 * Currently, we can't actually support that because the portal's
739 * query might refer to objects created or changed in the failed
740 * subtransaction, leading to crashes if execution is resumed. So,
741 * even READY portals are deleted. It would be nice to detect whether
742 * the query actually depends on any such object, instead.
745 if (portal->status == PORTAL_READY)
747 portal->createSubid = parentSubid;
748 if (portal->resowner)
749 ResourceOwnerNewParent(portal->resowner, parentXactOwner);
754 /* let portalcmds.c clean up the state it knows about */
755 if (PointerIsValid(portal->cleanup))
757 (*portal->cleanup) (portal);
758 portal->cleanup = NULL;
762 * Any resources belonging to the portal will be released in the
763 * upcoming transaction-wide cleanup; they will be gone before we
766 portal->resowner = NULL;
769 * Although we can't delete the portal data structure proper, we
770 * can release any memory in subsidiary contexts, such as executor
771 * state. The cleanup hook was the last thing that might have
774 MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
780 * Post-subabort cleanup for portals.
782 * Drop all portals created in the failed subtransaction (but note that
783 * we will not drop any that were reassigned to the parent above).
786 AtSubCleanup_Portals(SubTransactionId mySubid)
788 HASH_SEQ_STATUS status;
789 PortalHashEnt *hentry;
791 hash_seq_init(&status, PortalHashTable);
793 while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
795 Portal portal = hentry->portal;
797 if (portal->createSubid != mySubid)
801 PortalDrop(portal, false);
805 /* Find all available cursors */
807 pg_cursor(PG_FUNCTION_ARGS)
809 FuncCallContext *funcctx;
810 HASH_SEQ_STATUS *hash_seq;
811 PortalHashEnt *hentry;
813 /* stuff done only on the first call of the function */
814 if (SRF_IS_FIRSTCALL())
816 MemoryContext oldcontext;
819 /* create a function context for cross-call persistence */
820 funcctx = SRF_FIRSTCALL_INIT();
823 * switch to memory context appropriate for multiple function calls
825 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
829 hash_seq = (HASH_SEQ_STATUS *) palloc(sizeof(HASH_SEQ_STATUS));
830 hash_seq_init(hash_seq, PortalHashTable);
831 funcctx->user_fctx = (void *) hash_seq;
834 funcctx->user_fctx = NULL;
837 * build tupdesc for result tuples. This must match the definition of
838 * the pg_cursors view in system_views.sql
840 tupdesc = CreateTemplateTupleDesc(6, false);
841 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
843 TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
845 TupleDescInitEntry(tupdesc, (AttrNumber) 3, "is_holdable",
847 TupleDescInitEntry(tupdesc, (AttrNumber) 4, "is_binary",
849 TupleDescInitEntry(tupdesc, (AttrNumber) 5, "is_scrollable",
851 TupleDescInitEntry(tupdesc, (AttrNumber) 6, "creation_time",
852 TIMESTAMPTZOID, -1, 0);
854 funcctx->tuple_desc = BlessTupleDesc(tupdesc);
855 MemoryContextSwitchTo(oldcontext);
858 /* stuff done on every call of the function */
859 funcctx = SRF_PERCALL_SETUP();
860 hash_seq = (HASH_SEQ_STATUS *) funcctx->user_fctx;
862 /* if the hash table is uninitialized, we're done */
863 if (hash_seq == NULL)
864 SRF_RETURN_DONE(funcctx);
866 /* loop until we find a visible portal or hit the end of the list */
867 while ((hentry = hash_seq_search(hash_seq)) != NULL)
869 if (hentry->portal->visible)
881 portal = hentry->portal;
882 MemSet(nulls, 0, sizeof(nulls));
884 values[0] = DirectFunctionCall1(textin, CStringGetDatum(portal->name));
885 if (!portal->sourceText)
888 values[1] = DirectFunctionCall1(textin,
889 CStringGetDatum(portal->sourceText));
890 values[2] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_HOLD);
891 values[3] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_BINARY);
892 values[4] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_SCROLL);
893 values[5] = TimestampTzGetDatum(portal->creation_time);
895 tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
896 result = HeapTupleGetDatum(tuple);
897 SRF_RETURN_NEXT(funcctx, result);
900 SRF_RETURN_DONE(funcctx);