LCOV - code coverage report
Current view: top level - src/backend/utils/mmgr - portalmem.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 296 320 92.5 %
Date: 2019-09-19 16:06:56 Functions: 26 26 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * portalmem.c
       4             :  *    backend portal memory management
       5             :  *
       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.
       9             :  *
      10             :  *
      11             :  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
      12             :  * Portions Copyright (c) 1994, Regents of the University of California
      13             :  *
      14             :  * IDENTIFICATION
      15             :  *    src/backend/utils/mmgr/portalmem.c
      16             :  *
      17             :  *-------------------------------------------------------------------------
      18             :  */
      19             : #include "postgres.h"
      20             : 
      21             : #include "access/xact.h"
      22             : #include "catalog/pg_type.h"
      23             : #include "commands/portalcmds.h"
      24             : #include "miscadmin.h"
      25             : #include "storage/ipc.h"
      26             : #include "utils/builtins.h"
      27             : #include "utils/memutils.h"
      28             : #include "utils/snapmgr.h"
      29             : #include "utils/timestamp.h"
      30             : 
      31             : /*
      32             :  * Estimate of the maximum number of open portals a user would have,
      33             :  * used in initially sizing the PortalHashTable in EnablePortalManager().
      34             :  * Since the hash table can expand, there's no need to make this overly
      35             :  * generous, and keeping it small avoids unnecessary overhead in the
      36             :  * hash_seq_search() calls executed during transaction end.
      37             :  */
      38             : #define PORTALS_PER_USER       16
      39             : 
      40             : 
      41             : /* ----------------
      42             :  *      Global state
      43             :  * ----------------
      44             :  */
      45             : 
      46             : #define MAX_PORTALNAME_LEN      NAMEDATALEN
      47             : 
      48             : typedef struct portalhashent
      49             : {
      50             :     char        portalname[MAX_PORTALNAME_LEN];
      51             :     Portal      portal;
      52             : } PortalHashEnt;
      53             : 
      54             : static HTAB *PortalHashTable = NULL;
      55             : 
      56             : #define PortalHashTableLookup(NAME, PORTAL) \
      57             : do { \
      58             :     PortalHashEnt *hentry; \
      59             :     \
      60             :     hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
      61             :                                            (NAME), HASH_FIND, NULL); \
      62             :     if (hentry) \
      63             :         PORTAL = hentry->portal; \
      64             :     else \
      65             :         PORTAL = NULL; \
      66             : } while(0)
      67             : 
      68             : #define PortalHashTableInsert(PORTAL, NAME) \
      69             : do { \
      70             :     PortalHashEnt *hentry; bool found; \
      71             :     \
      72             :     hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
      73             :                                            (NAME), HASH_ENTER, &found); \
      74             :     if (found) \
      75             :         elog(ERROR, "duplicate portal name"); \
      76             :     hentry->portal = PORTAL; \
      77             :     /* To avoid duplicate storage, make PORTAL->name point to htab entry */ \
      78             :     PORTAL->name = hentry->portalname; \
      79             : } while(0)
      80             : 
      81             : #define PortalHashTableDelete(PORTAL) \
      82             : do { \
      83             :     PortalHashEnt *hentry; \
      84             :     \
      85             :     hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
      86             :                                            PORTAL->name, HASH_REMOVE, NULL); \
      87             :     if (hentry == NULL) \
      88             :         elog(WARNING, "trying to delete portal name that does not exist"); \
      89             : } while(0)
      90             : 
      91             : static MemoryContext TopPortalContext = NULL;
      92             : 
      93             : 
      94             : /* ----------------------------------------------------------------
      95             :  *                 public portal interface functions
      96             :  * ----------------------------------------------------------------
      97             :  */
      98             : 
      99             : /*
     100             :  * EnablePortalManager
     101             :  *      Enables the portal management module at backend startup.
     102             :  */
     103             : void
     104        9902 : EnablePortalManager(void)
     105             : {
     106             :     HASHCTL     ctl;
     107             : 
     108             :     Assert(TopPortalContext == NULL);
     109             : 
     110        9902 :     TopPortalContext = AllocSetContextCreate(TopMemoryContext,
     111             :                                              "TopPortalContext",
     112             :                                              ALLOCSET_DEFAULT_SIZES);
     113             : 
     114        9902 :     ctl.keysize = MAX_PORTALNAME_LEN;
     115        9902 :     ctl.entrysize = sizeof(PortalHashEnt);
     116             : 
     117             :     /*
     118             :      * use PORTALS_PER_USER as a guess of how many hash table entries to
     119             :      * create, initially
     120             :      */
     121        9902 :     PortalHashTable = hash_create("Portal hash", PORTALS_PER_USER,
     122             :                                   &ctl, HASH_ELEM);
     123        9902 : }
     124             : 
     125             : /*
     126             :  * GetPortalByName
     127             :  *      Returns a portal given a portal name, or NULL if name not found.
     128             :  */
     129             : Portal
     130      585466 : GetPortalByName(const char *name)
     131             : {
     132             :     Portal      portal;
     133             : 
     134      585466 :     if (PointerIsValid(name))
     135      585466 :         PortalHashTableLookup(name, portal);
     136             :     else
     137           0 :         portal = NULL;
     138             : 
     139      585466 :     return portal;
     140             : }
     141             : 
     142             : /*
     143             :  * PortalGetPrimaryStmt
     144             :  *      Get the "primary" stmt within a portal, ie, the one marked canSetTag.
     145             :  *
     146             :  * Returns NULL if no such stmt.  If multiple PlannedStmt structs within the
     147             :  * portal are marked canSetTag, returns the first one.  Neither of these
     148             :  * cases should occur in present usages of this function.
     149             :  */
     150             : PlannedStmt *
     151      169198 : PortalGetPrimaryStmt(Portal portal)
     152             : {
     153             :     ListCell   *lc;
     154             : 
     155      169198 :     foreach(lc, portal->stmts)
     156             :     {
     157      169198 :         PlannedStmt *stmt = lfirst_node(PlannedStmt, lc);
     158             : 
     159      169198 :         if (stmt->canSetTag)
     160      169198 :             return stmt;
     161             :     }
     162           0 :     return NULL;
     163             : }
     164             : 
     165             : /*
     166             :  * CreatePortal
     167             :  *      Returns a new portal given a name.
     168             :  *
     169             :  * allowDup: if true, automatically drop any pre-existing portal of the
     170             :  * same name (if false, an error is raised).
     171             :  *
     172             :  * dupSilent: if true, don't even emit a WARNING.
     173             :  */
     174             : Portal
     175      505386 : CreatePortal(const char *name, bool allowDup, bool dupSilent)
     176             : {
     177             :     Portal      portal;
     178             : 
     179             :     AssertArg(PointerIsValid(name));
     180             : 
     181      505386 :     portal = GetPortalByName(name);
     182      505386 :     if (PortalIsValid(portal))
     183             :     {
     184        5804 :         if (!allowDup)
     185           0 :             ereport(ERROR,
     186             :                     (errcode(ERRCODE_DUPLICATE_CURSOR),
     187             :                      errmsg("cursor \"%s\" already exists", name)));
     188        5804 :         if (!dupSilent)
     189           0 :             ereport(WARNING,
     190             :                     (errcode(ERRCODE_DUPLICATE_CURSOR),
     191             :                      errmsg("closing existing cursor \"%s\"",
     192             :                             name)));
     193        5804 :         PortalDrop(portal, false);
     194             :     }
     195             : 
     196             :     /* make new portal structure */
     197      505386 :     portal = (Portal) MemoryContextAllocZero(TopPortalContext, sizeof *portal);
     198             : 
     199             :     /* initialize portal context; typically it won't store much */
     200      505386 :     portal->portalContext = AllocSetContextCreate(TopPortalContext,
     201             :                                                   "PortalContext",
     202             :                                                   ALLOCSET_SMALL_SIZES);
     203             : 
     204             :     /* create a resource owner for the portal */
     205      505386 :     portal->resowner = ResourceOwnerCreate(CurTransactionResourceOwner,
     206             :                                            "Portal");
     207             : 
     208             :     /* initialize portal fields that don't start off zero */
     209      505386 :     portal->status = PORTAL_NEW;
     210      505386 :     portal->cleanup = PortalCleanup;
     211      505386 :     portal->createSubid = GetCurrentSubTransactionId();
     212      505386 :     portal->activeSubid = portal->createSubid;
     213      505386 :     portal->strategy = PORTAL_MULTI_QUERY;
     214      505386 :     portal->cursorOptions = CURSOR_OPT_NO_SCROLL;
     215      505386 :     portal->atStart = true;
     216      505386 :     portal->atEnd = true;        /* disallow fetches until query is set */
     217      505386 :     portal->visible = true;
     218      505386 :     portal->creation_time = GetCurrentStatementStartTimestamp();
     219             : 
     220             :     /* put portal in table (sets portal->name) */
     221      505386 :     PortalHashTableInsert(portal, name);
     222             : 
     223             :     /* reuse portal->name copy */
     224      505386 :     MemoryContextSetIdentifier(portal->portalContext, portal->name);
     225             : 
     226      505386 :     return portal;
     227             : }
     228             : 
     229             : /*
     230             :  * CreateNewPortal
     231             :  *      Create a new portal, assigning it a random nonconflicting name.
     232             :  */
     233             : Portal
     234        5206 : CreateNewPortal(void)
     235             : {
     236             :     static unsigned int unnamed_portal_count = 0;
     237             : 
     238             :     char        portalname[MAX_PORTALNAME_LEN];
     239             : 
     240             :     /* Select a nonconflicting name */
     241             :     for (;;)
     242             :     {
     243        5206 :         unnamed_portal_count++;
     244        5206 :         sprintf(portalname, "<unnamed portal %u>", unnamed_portal_count);
     245        5206 :         if (GetPortalByName(portalname) == NULL)
     246        5206 :             break;
     247             :     }
     248             : 
     249        5206 :     return CreatePortal(portalname, false, false);
     250             : }
     251             : 
     252             : /*
     253             :  * PortalDefineQuery
     254             :  *      A simple subroutine to establish a portal's query.
     255             :  *
     256             :  * Notes: as of PG 8.4, caller MUST supply a sourceText string; it is not
     257             :  * allowed anymore to pass NULL.  (If you really don't have source text,
     258             :  * you can pass a constant string, perhaps "(query not available)".)
     259             :  *
     260             :  * 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.
     263             :  *
     264             :  * If cplan is provided, then it is a cached plan containing the stmts, and
     265             :  * the caller must have done GetCachedPlan(), causing a refcount increment.
     266             :  * The refcount will be released when the portal is destroyed.
     267             :  *
     268             :  * If cplan is NULL, then it is the caller's responsibility to ensure that
     269             :  * the passed plan trees have adequate lifetime.  Typically this is done by
     270             :  * copying them into the portal's context.
     271             :  *
     272             :  * The caller is also responsible for ensuring that the passed prepStmtName
     273             :  * (if not NULL) and sourceText have adequate lifetime.
     274             :  *
     275             :  * NB: this function mustn't do much beyond storing the passed values; in
     276             :  * particular don't do anything that risks elog(ERROR).  If that were to
     277             :  * happen here before storing the cplan reference, we'd leak the plancache
     278             :  * refcount that the caller is trying to hand off to us.
     279             :  */
     280             : void
     281      505366 : PortalDefineQuery(Portal portal,
     282             :                   const char *prepStmtName,
     283             :                   const char *sourceText,
     284             :                   const char *commandTag,
     285             :                   List *stmts,
     286             :                   CachedPlan *cplan)
     287             : {
     288             :     AssertArg(PortalIsValid(portal));
     289             :     AssertState(portal->status == PORTAL_NEW);
     290             : 
     291             :     AssertArg(sourceText != NULL);
     292             :     AssertArg(commandTag != NULL || stmts == NIL);
     293             : 
     294      505366 :     portal->prepStmtName = prepStmtName;
     295      505366 :     portal->sourceText = sourceText;
     296      505366 :     portal->commandTag = commandTag;
     297      505366 :     portal->stmts = stmts;
     298      505366 :     portal->cplan = cplan;
     299      505366 :     portal->status = PORTAL_DEFINED;
     300      505366 : }
     301             : 
     302             : /*
     303             :  * PortalReleaseCachedPlan
     304             :  *      Release a portal's reference to its cached plan, if any.
     305             :  */
     306             : static void
     307      518008 : PortalReleaseCachedPlan(Portal portal)
     308             : {
     309      518008 :     if (portal->cplan)
     310             :     {
     311       28390 :         ReleaseCachedPlan(portal->cplan, false);
     312       28390 :         portal->cplan = NULL;
     313             : 
     314             :         /*
     315             :          * We must also clear portal->stmts which is now a dangling reference
     316             :          * to the cached plan's plan list.  This protects any code that might
     317             :          * try to examine the Portal later.
     318             :          */
     319       28390 :         portal->stmts = NIL;
     320             :     }
     321      518008 : }
     322             : 
     323             : /*
     324             :  * PortalCreateHoldStore
     325             :  *      Create the tuplestore for a portal.
     326             :  */
     327             : void
     328       15890 : PortalCreateHoldStore(Portal portal)
     329             : {
     330             :     MemoryContext oldcxt;
     331             : 
     332             :     Assert(portal->holdContext == NULL);
     333             :     Assert(portal->holdStore == NULL);
     334             :     Assert(portal->holdSnapshot == NULL);
     335             : 
     336             :     /*
     337             :      * Create the memory context that is used for storage of the tuple set.
     338             :      * Note this is NOT a child of the portal's portalContext.
     339             :      */
     340       15890 :     portal->holdContext =
     341       15890 :         AllocSetContextCreate(TopPortalContext,
     342             :                               "PortalHoldContext",
     343             :                               ALLOCSET_DEFAULT_SIZES);
     344             : 
     345             :     /*
     346             :      * Create the tuple store, selecting cross-transaction temp files, and
     347             :      * enabling random access only if cursor requires scrolling.
     348             :      *
     349             :      * XXX: Should maintenance_work_mem be used for the portal size?
     350             :      */
     351       15890 :     oldcxt = MemoryContextSwitchTo(portal->holdContext);
     352             : 
     353       15890 :     portal->holdStore =
     354       15890 :         tuplestore_begin_heap(portal->cursorOptions & CURSOR_OPT_SCROLL,
     355             :                               true, work_mem);
     356             : 
     357       15890 :     MemoryContextSwitchTo(oldcxt);
     358       15890 : }
     359             : 
     360             : /*
     361             :  * PinPortal
     362             :  *      Protect a portal from dropping.
     363             :  *
     364             :  * A pinned portal is still unpinned and dropped at transaction or
     365             :  * subtransaction abort.
     366             :  */
     367             : void
     368        3734 : PinPortal(Portal portal)
     369             : {
     370        3734 :     if (portal->portalPinned)
     371           0 :         elog(ERROR, "portal already pinned");
     372             : 
     373        3734 :     portal->portalPinned = true;
     374        3734 : }
     375             : 
     376             : void
     377        3698 : UnpinPortal(Portal portal)
     378             : {
     379        3698 :     if (!portal->portalPinned)
     380           0 :         elog(ERROR, "portal not pinned");
     381             : 
     382        3698 :     portal->portalPinned = false;
     383        3698 : }
     384             : 
     385             : /*
     386             :  * MarkPortalActive
     387             :  *      Transition a portal from READY to ACTIVE state.
     388             :  *
     389             :  * NOTE: never set portal->status = PORTAL_ACTIVE directly; call this instead.
     390             :  */
     391             : void
     392      511616 : MarkPortalActive(Portal portal)
     393             : {
     394             :     /* For safety, this is a runtime test not just an Assert */
     395      511616 :     if (portal->status != PORTAL_READY)
     396          12 :         ereport(ERROR,
     397             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     398             :                  errmsg("portal \"%s\" cannot be run", portal->name)));
     399             :     /* Perform the state transition */
     400      511604 :     portal->status = PORTAL_ACTIVE;
     401      511604 :     portal->activeSubid = GetCurrentSubTransactionId();
     402      511604 : }
     403             : 
     404             : /*
     405             :  * MarkPortalDone
     406             :  *      Transition a portal from ACTIVE to DONE state.
     407             :  *
     408             :  * NOTE: never set portal->status = PORTAL_DONE directly; call this instead.
     409             :  */
     410             : void
     411      339216 : MarkPortalDone(Portal portal)
     412             : {
     413             :     /* Perform the state transition */
     414             :     Assert(portal->status == PORTAL_ACTIVE);
     415      339216 :     portal->status = PORTAL_DONE;
     416             : 
     417             :     /*
     418             :      * Allow portalcmds.c to clean up the state it knows about.  We might as
     419             :      * well do that now, since the portal can't be executed any more.
     420             :      *
     421             :      * In some cases involving execution of a ROLLBACK command in an already
     422             :      * aborted transaction, this is necessary, or we'd reach AtCleanup_Portals
     423             :      * with the cleanup hook still unexecuted.
     424             :      */
     425      339216 :     if (PointerIsValid(portal->cleanup))
     426             :     {
     427      339180 :         portal->cleanup(portal);
     428      339180 :         portal->cleanup = NULL;
     429             :     }
     430      339216 : }
     431             : 
     432             : /*
     433             :  * MarkPortalFailed
     434             :  *      Transition a portal into FAILED state.
     435             :  *
     436             :  * NOTE: never set portal->status = PORTAL_FAILED directly; call this instead.
     437             :  */
     438             : void
     439       12416 : MarkPortalFailed(Portal portal)
     440             : {
     441             :     /* Perform the state transition */
     442             :     Assert(portal->status != PORTAL_DONE);
     443       12416 :     portal->status = PORTAL_FAILED;
     444             : 
     445             :     /*
     446             :      * Allow portalcmds.c to clean up the state it knows about.  We might as
     447             :      * well do that now, since the portal can't be executed any more.
     448             :      *
     449             :      * In some cases involving cleanup of an already aborted transaction, this
     450             :      * is necessary, or we'd reach AtCleanup_Portals with the cleanup hook
     451             :      * still unexecuted.
     452             :      */
     453       12416 :     if (PointerIsValid(portal->cleanup))
     454             :     {
     455       12416 :         portal->cleanup(portal);
     456       12416 :         portal->cleanup = NULL;
     457             :     }
     458       12416 : }
     459             : 
     460             : /*
     461             :  * PortalDrop
     462             :  *      Destroy the portal.
     463             :  */
     464             : void
     465      505378 : PortalDrop(Portal portal, bool isTopCommit)
     466             : {
     467             :     AssertArg(PortalIsValid(portal));
     468             : 
     469             :     /*
     470             :      * Don't allow dropping a pinned portal, it's still needed by whoever
     471             :      * pinned it.
     472             :      */
     473      505378 :     if (portal->portalPinned)
     474           0 :         ereport(ERROR,
     475             :                 (errcode(ERRCODE_INVALID_CURSOR_STATE),
     476             :                  errmsg("cannot drop pinned portal \"%s\"", portal->name)));
     477             : 
     478             :     /*
     479             :      * Not sure if the PORTAL_ACTIVE case can validly happen or not...
     480             :      */
     481      505378 :     if (portal->status == PORTAL_ACTIVE)
     482           0 :         ereport(ERROR,
     483             :                 (errcode(ERRCODE_INVALID_CURSOR_STATE),
     484             :                  errmsg("cannot drop active portal \"%s\"", portal->name)));
     485             : 
     486             :     /*
     487             :      * Allow portalcmds.c to clean up the state it knows about, in particular
     488             :      * shutting down the executor if still active.  This step potentially runs
     489             :      * user-defined code so failure has to be expected.  It's the cleanup
     490             :      * hook's responsibility to not try to do that more than once, in the case
     491             :      * that failure occurs and then we come back to drop the portal again
     492             :      * during transaction abort.
     493             :      *
     494             :      * Note: in most paths of control, this will have been done already in
     495             :      * MarkPortalDone or MarkPortalFailed.  We're just making sure.
     496             :      */
     497      505378 :     if (PointerIsValid(portal->cleanup))
     498             :     {
     499      153724 :         portal->cleanup(portal);
     500      153724 :         portal->cleanup = NULL;
     501             :     }
     502             : 
     503             :     /*
     504             :      * Remove portal from hash table.  Because we do this here, we will not
     505             :      * come back to try to remove the portal again if there's any error in the
     506             :      * subsequent steps.  Better to leak a little memory than to get into an
     507             :      * infinite error-recovery loop.
     508             :      */
     509      505378 :     PortalHashTableDelete(portal);
     510             : 
     511             :     /* drop cached plan reference, if any */
     512      505378 :     PortalReleaseCachedPlan(portal);
     513             : 
     514             :     /*
     515             :      * If portal has a snapshot protecting its data, release that.  This needs
     516             :      * a little care since the registration will be attached to the portal's
     517             :      * resowner; if the portal failed, we will already have released the
     518             :      * resowner (and the snapshot) during transaction abort.
     519             :      */
     520      505378 :     if (portal->holdSnapshot)
     521             :     {
     522       11200 :         if (portal->resowner)
     523       11060 :             UnregisterSnapshotFromOwner(portal->holdSnapshot,
     524             :                                         portal->resowner);
     525       11200 :         portal->holdSnapshot = NULL;
     526             :     }
     527             : 
     528             :     /*
     529             :      * Release any resources still attached to the portal.  There are several
     530             :      * cases being covered here:
     531             :      *
     532             :      * Top transaction commit (indicated by isTopCommit): normally we should
     533             :      * do nothing here and let the regular end-of-transaction resource
     534             :      * releasing mechanism handle these resources too.  However, if we have a
     535             :      * FAILED portal (eg, a cursor that got an error), we'd better clean up
     536             :      * its resources to avoid resource-leakage warning messages.
     537             :      *
     538             :      * Sub transaction commit: never comes here at all, since we don't kill
     539             :      * any portals in AtSubCommit_Portals().
     540             :      *
     541             :      * Main or sub transaction abort: we will do nothing here because
     542             :      * portal->resowner was already set NULL; the resources were already
     543             :      * cleaned up in transaction abort.
     544             :      *
     545             :      * Ordinary portal drop: must release resources.  However, if the portal
     546             :      * is not FAILED then we do not release its locks.  The locks become the
     547             :      * responsibility of the transaction's ResourceOwner (since it is the
     548             :      * parent of the portal's owner) and will be released when the transaction
     549             :      * eventually ends.
     550             :      */
     551      993980 :     if (portal->resowner &&
     552      509466 :         (!isTopCommit || portal->status == PORTAL_FAILED))
     553             :     {
     554      467738 :         bool        isCommit = (portal->status != PORTAL_FAILED);
     555             : 
     556      467738 :         ResourceOwnerRelease(portal->resowner,
     557             :                              RESOURCE_RELEASE_BEFORE_LOCKS,
     558             :                              isCommit, false);
     559      467738 :         ResourceOwnerRelease(portal->resowner,
     560             :                              RESOURCE_RELEASE_LOCKS,
     561             :                              isCommit, false);
     562      467738 :         ResourceOwnerRelease(portal->resowner,
     563             :                              RESOURCE_RELEASE_AFTER_LOCKS,
     564             :                              isCommit, false);
     565      467738 :         ResourceOwnerDelete(portal->resowner);
     566             :     }
     567      505378 :     portal->resowner = NULL;
     568             : 
     569             :     /*
     570             :      * Delete tuplestore if present.  We should do this even under error
     571             :      * conditions; since the tuplestore would have been using cross-
     572             :      * transaction storage, its temp files need to be explicitly deleted.
     573             :      */
     574      505378 :     if (portal->holdStore)
     575             :     {
     576             :         MemoryContext oldcontext;
     577             : 
     578       15882 :         oldcontext = MemoryContextSwitchTo(portal->holdContext);
     579       15882 :         tuplestore_end(portal->holdStore);
     580       15882 :         MemoryContextSwitchTo(oldcontext);
     581       15882 :         portal->holdStore = NULL;
     582             :     }
     583             : 
     584             :     /* delete tuplestore storage, if any */
     585      505378 :     if (portal->holdContext)
     586       15882 :         MemoryContextDelete(portal->holdContext);
     587             : 
     588             :     /* release subsidiary storage */
     589      505378 :     MemoryContextDelete(portal->portalContext);
     590             : 
     591             :     /* release portal struct (it's in TopPortalContext) */
     592      505378 :     pfree(portal);
     593      505378 : }
     594             : 
     595             : /*
     596             :  * Delete all declared cursors.
     597             :  *
     598             :  * Used by commands: CLOSE ALL, DISCARD ALL
     599             :  */
     600             : void
     601          12 : PortalHashTableDeleteAll(void)
     602             : {
     603             :     HASH_SEQ_STATUS status;
     604             :     PortalHashEnt *hentry;
     605             : 
     606          12 :     if (PortalHashTable == NULL)
     607           0 :         return;
     608             : 
     609          12 :     hash_seq_init(&status, PortalHashTable);
     610          60 :     while ((hentry = hash_seq_search(&status)) != NULL)
     611             :     {
     612          36 :         Portal      portal = hentry->portal;
     613             : 
     614             :         /* Can't close the active portal (the one running the command) */
     615          36 :         if (portal->status == PORTAL_ACTIVE)
     616          20 :             continue;
     617             : 
     618          16 :         PortalDrop(portal, false);
     619             : 
     620             :         /* Restart the iteration in case that led to other drops */
     621          16 :         hash_seq_term(&status);
     622          16 :         hash_seq_init(&status, PortalHashTable);
     623             :     }
     624             : }
     625             : 
     626             : /*
     627             :  * "Hold" a portal.  Prepare it for access by later transactions.
     628             :  */
     629             : static void
     630          48 : HoldPortal(Portal portal)
     631             : {
     632             :     /*
     633             :      * Note that PersistHoldablePortal() must release all resources used by
     634             :      * the portal that are local to the creating transaction.
     635             :      */
     636          48 :     PortalCreateHoldStore(portal);
     637          48 :     PersistHoldablePortal(portal);
     638             : 
     639             :     /* drop cached plan reference, if any */
     640          44 :     PortalReleaseCachedPlan(portal);
     641             : 
     642             :     /*
     643             :      * Any resources belonging to the portal will be released in the upcoming
     644             :      * transaction-wide cleanup; the portal will no longer have its own
     645             :      * resources.
     646             :      */
     647          44 :     portal->resowner = NULL;
     648             : 
     649             :     /*
     650             :      * Having successfully exported the holdable cursor, mark it as not
     651             :      * belonging to this transaction.
     652             :      */
     653          44 :     portal->createSubid = InvalidSubTransactionId;
     654          44 :     portal->activeSubid = InvalidSubTransactionId;
     655          44 : }
     656             : 
     657             : /*
     658             :  * Pre-commit processing for portals.
     659             :  *
     660             :  * Holdable cursors created in this transaction need to be converted to
     661             :  * materialized form, since we are going to close down the executor and
     662             :  * release locks.  Non-holdable portals created in this transaction are
     663             :  * simply removed.  Portals remaining from prior transactions should be
     664             :  * left untouched.
     665             :  *
     666             :  * Returns true if any portals changed state (possibly causing user-defined
     667             :  * code to be run), false if not.
     668             :  */
     669             : bool
     670      461830 : PreCommit_Portals(bool isPrepare)
     671             : {
     672      461830 :     bool        result = false;
     673             :     HASH_SEQ_STATUS status;
     674             :     PortalHashEnt *hentry;
     675             : 
     676      461830 :     hash_seq_init(&status, PortalHashTable);
     677             : 
     678     1021972 :     while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
     679             :     {
     680       98312 :         Portal      portal = hentry->portal;
     681             : 
     682             :         /*
     683             :          * There should be no pinned portals anymore. Complain if someone
     684             :          * leaked one. Auto-held portals are allowed; we assume that whoever
     685             :          * pinned them is managing them.
     686             :          */
     687       98312 :         if (portal->portalPinned && !portal->autoHeld)
     688           0 :             elog(ERROR, "cannot commit while a portal is pinned");
     689             : 
     690             :         /*
     691             :          * Do not touch active portals --- this can only happen in the case of
     692             :          * a multi-transaction utility command, such as VACUUM, or a commit in
     693             :          * a procedure.
     694             :          *
     695             :          * Note however that any resource owner attached to such a portal is
     696             :          * still going to go away, so don't leave a dangling pointer.  Also
     697             :          * unregister any snapshots held by the portal, mainly to avoid
     698             :          * snapshot leak warnings from ResourceOwnerRelease().
     699             :          */
     700       98312 :         if (portal->status == PORTAL_ACTIVE)
     701             :         {
     702       77148 :             if (portal->holdSnapshot)
     703             :             {
     704           2 :                 if (portal->resowner)
     705           2 :                     UnregisterSnapshotFromOwner(portal->holdSnapshot,
     706             :                                                 portal->resowner);
     707           2 :                 portal->holdSnapshot = NULL;
     708             :             }
     709       77148 :             portal->resowner = NULL;
     710       77148 :             continue;
     711             :         }
     712             : 
     713             :         /* Is it a holdable portal created in the current xact? */
     714       21404 :         if ((portal->cursorOptions & CURSOR_OPT_HOLD) &&
     715         260 :             portal->createSubid != InvalidSubTransactionId &&
     716          20 :             portal->status == PORTAL_READY)
     717             :         {
     718             :             /*
     719             :              * We are exiting the transaction that created a holdable cursor.
     720             :              * Instead of dropping the portal, prepare it for access by later
     721             :              * transactions.
     722             :              *
     723             :              * However, if this is PREPARE TRANSACTION rather than COMMIT,
     724             :              * refuse PREPARE, because the semantics seem pretty unclear.
     725             :              */
     726          20 :             if (isPrepare)
     727           0 :                 ereport(ERROR,
     728             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     729             :                          errmsg("cannot PREPARE a transaction that has created a cursor WITH HOLD")));
     730             : 
     731          20 :             HoldPortal(portal);
     732             : 
     733             :             /* Report we changed state */
     734          20 :             result = true;
     735             :         }
     736       21144 :         else if (portal->createSubid == InvalidSubTransactionId)
     737             :         {
     738             :             /*
     739             :              * Do nothing to cursors held over from a previous transaction
     740             :              * (including ones we just froze in a previous cycle of this loop)
     741             :              */
     742         280 :             continue;
     743             :         }
     744             :         else
     745             :         {
     746             :             /* Zap all non-holdable portals */
     747       20864 :             PortalDrop(portal, true);
     748             : 
     749             :             /* Report we changed state */
     750       20864 :             result = true;
     751             :         }
     752             : 
     753             :         /*
     754             :          * After either freezing or dropping a portal, we have to restart the
     755             :          * iteration, because we could have invoked user-defined code that
     756             :          * caused a drop of the next portal in the hash chain.
     757             :          */
     758       20884 :         hash_seq_term(&status);
     759       20884 :         hash_seq_init(&status, PortalHashTable);
     760             :     }
     761             : 
     762      461830 :     return result;
     763             : }
     764             : 
     765             : /*
     766             :  * Abort processing for portals.
     767             :  *
     768             :  * At this point we run the cleanup hook if present, but we can't release the
     769             :  * portal's memory until the cleanup call.
     770             :  */
     771             : void
     772       19156 : AtAbort_Portals(void)
     773             : {
     774             :     HASH_SEQ_STATUS status;
     775             :     PortalHashEnt *hentry;
     776             : 
     777       19156 :     hash_seq_init(&status, PortalHashTable);
     778             : 
     779       50876 :     while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
     780             :     {
     781       12564 :         Portal      portal = hentry->portal;
     782             : 
     783             :         /*
     784             :          * When elog(FATAL) is progress, we need to set the active portal to
     785             :          * failed, so that PortalCleanup() doesn't run the executor shutdown.
     786             :          */
     787       12564 :         if (portal->status == PORTAL_ACTIVE && shmem_exit_inprogress)
     788           0 :             MarkPortalFailed(portal);
     789             : 
     790             :         /*
     791             :          * Do nothing else to cursors held over from a previous transaction.
     792             :          */
     793       12564 :         if (portal->createSubid == InvalidSubTransactionId)
     794         102 :             continue;
     795             : 
     796             :         /*
     797             :          * Do nothing to auto-held cursors.  This is similar to the case of a
     798             :          * cursor from a previous transaction, but it could also be that the
     799             :          * cursor was auto-held in this transaction, so it wants to live on.
     800             :          */
     801       12462 :         if (portal->autoHeld)
     802           0 :             continue;
     803             : 
     804             :         /*
     805             :          * If it was created in the current transaction, we can't do normal
     806             :          * shutdown on a READY portal either; it might refer to objects
     807             :          * created in the failed transaction.  See comments in
     808             :          * AtSubAbort_Portals.
     809             :          */
     810       12462 :         if (portal->status == PORTAL_READY)
     811         208 :             MarkPortalFailed(portal);
     812             : 
     813             :         /*
     814             :          * Allow portalcmds.c to clean up the state it knows about, if we
     815             :          * haven't already.
     816             :          */
     817       12462 :         if (PointerIsValid(portal->cleanup))
     818             :         {
     819          58 :             portal->cleanup(portal);
     820          58 :             portal->cleanup = NULL;
     821             :         }
     822             : 
     823             :         /* drop cached plan reference, if any */
     824       12462 :         PortalReleaseCachedPlan(portal);
     825             : 
     826             :         /*
     827             :          * Any resources belonging to the portal will be released in the
     828             :          * upcoming transaction-wide cleanup; they will be gone before we run
     829             :          * PortalDrop.
     830             :          */
     831       12462 :         portal->resowner = NULL;
     832             : 
     833             :         /*
     834             :          * Although we can't delete the portal data structure proper, we can
     835             :          * release any memory in subsidiary contexts, such as executor state.
     836             :          * The cleanup hook was the last thing that might have needed data
     837             :          * there.  But leave active portals alone.
     838             :          */
     839       12462 :         if (portal->status != PORTAL_ACTIVE)
     840       12318 :             MemoryContextDeleteChildren(portal->portalContext);
     841             :     }
     842       19156 : }
     843             : 
     844             : /*
     845             :  * Post-abort cleanup for portals.
     846             :  *
     847             :  * Delete all portals not held over from prior transactions.  */
     848             : void
     849       19146 : AtCleanup_Portals(void)
     850             : {
     851             :     HASH_SEQ_STATUS status;
     852             :     PortalHashEnt *hentry;
     853             : 
     854       19146 :     hash_seq_init(&status, PortalHashTable);
     855             : 
     856       50084 :     while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
     857             :     {
     858       11792 :         Portal      portal = hentry->portal;
     859             : 
     860             :         /*
     861             :          * Do not touch active portals --- this can only happen in the case of
     862             :          * a multi-transaction command.
     863             :          */
     864       11792 :         if (portal->status == PORTAL_ACTIVE)
     865         144 :             continue;
     866             : 
     867             :         /*
     868             :          * Do nothing to cursors held over from a previous transaction or
     869             :          * auto-held ones.
     870             :          */
     871       11648 :         if (portal->createSubid == InvalidSubTransactionId || portal->autoHeld)
     872             :         {
     873             :             Assert(portal->status != PORTAL_ACTIVE);
     874             :             Assert(portal->resowner == NULL);
     875         102 :             continue;
     876             :         }
     877             : 
     878             :         /*
     879             :          * If a portal is still pinned, forcibly unpin it. PortalDrop will not
     880             :          * let us drop the portal otherwise. Whoever pinned the portal was
     881             :          * interrupted by the abort too and won't try to use it anymore.
     882             :          */
     883       11546 :         if (portal->portalPinned)
     884          26 :             portal->portalPinned = false;
     885             : 
     886             :         /*
     887             :          * We had better not call any user-defined code during cleanup, so if
     888             :          * the cleanup hook hasn't been run yet, too bad; we'll just skip it.
     889             :          */
     890       11546 :         if (PointerIsValid(portal->cleanup))
     891             :         {
     892           0 :             elog(WARNING, "skipping cleanup for portal \"%s\"", portal->name);
     893           0 :             portal->cleanup = NULL;
     894             :         }
     895             : 
     896             :         /* Zap it. */
     897       11546 :         PortalDrop(portal, false);
     898             :     }
     899       19146 : }
     900             : 
     901             : /*
     902             :  * Portal-related cleanup when we return to the main loop on error.
     903             :  *
     904             :  * This is different from the cleanup at transaction abort.  Auto-held portals
     905             :  * are cleaned up on error but not on transaction abort.
     906             :  */
     907             : void
     908       17896 : PortalErrorCleanup(void)
     909             : {
     910             :     HASH_SEQ_STATUS status;
     911             :     PortalHashEnt *hentry;
     912             : 
     913       17896 :     hash_seq_init(&status, PortalHashTable);
     914             : 
     915       36854 :     while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
     916             :     {
     917        1062 :         Portal      portal = hentry->portal;
     918             : 
     919        1062 :         if (portal->autoHeld)
     920             :         {
     921           4 :             portal->portalPinned = false;
     922           4 :             PortalDrop(portal, false);
     923             :         }
     924             :     }
     925       17896 : }
     926             : 
     927             : /*
     928             :  * Pre-subcommit processing for portals.
     929             :  *
     930             :  * Reassign portals created or used in the current subtransaction to the
     931             :  * parent subtransaction.
     932             :  */
     933             : void
     934        4434 : AtSubCommit_Portals(SubTransactionId mySubid,
     935             :                     SubTransactionId parentSubid,
     936             :                     ResourceOwner parentXactOwner)
     937             : {
     938             :     HASH_SEQ_STATUS status;
     939             :     PortalHashEnt *hentry;
     940             : 
     941        4434 :     hash_seq_init(&status, PortalHashTable);
     942             : 
     943       13038 :     while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
     944             :     {
     945        4170 :         Portal      portal = hentry->portal;
     946             : 
     947        4170 :         if (portal->createSubid == mySubid)
     948             :         {
     949          58 :             portal->createSubid = parentSubid;
     950          58 :             if (portal->resowner)
     951          58 :                 ResourceOwnerNewParent(portal->resowner, parentXactOwner);
     952             :         }
     953        4170 :         if (portal->activeSubid == mySubid)
     954         208 :             portal->activeSubid = parentSubid;
     955             :     }
     956        4434 : }
     957             : 
     958             : /*
     959             :  * Subtransaction abort handling for portals.
     960             :  *
     961             :  * Deactivate portals created or used during the failed subtransaction.
     962             :  * Note that per AtSubCommit_Portals, this will catch portals created/used
     963             :  * in descendants of the subtransaction too.
     964             :  *
     965             :  * We don't destroy any portals here; that's done in AtSubCleanup_Portals.
     966             :  */
     967             : void
     968        2928 : AtSubAbort_Portals(SubTransactionId mySubid,
     969             :                    SubTransactionId parentSubid,
     970             :                    ResourceOwner myXactOwner,
     971             :                    ResourceOwner parentXactOwner)
     972             : {
     973             :     HASH_SEQ_STATUS status;
     974             :     PortalHashEnt *hentry;
     975             : 
     976        2928 :     hash_seq_init(&status, PortalHashTable);
     977             : 
     978       10758 :     while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
     979             :     {
     980        4902 :         Portal      portal = hentry->portal;
     981             : 
     982             :         /* Was it created in this subtransaction? */
     983        4902 :         if (portal->createSubid != mySubid)
     984             :         {
     985             :             /* No, but maybe it was used in this subtransaction? */
     986        4778 :             if (portal->activeSubid == mySubid)
     987             :             {
     988             :                 /* Maintain activeSubid until the portal is removed */
     989          28 :                 portal->activeSubid = parentSubid;
     990             : 
     991             :                 /*
     992             :                  * A MarkPortalActive() caller ran an upper-level portal in
     993             :                  * this subtransaction and left the portal ACTIVE.  This can't
     994             :                  * happen, but force the portal into FAILED state for the same
     995             :                  * reasons discussed below.
     996             :                  *
     997             :                  * We assume we can get away without forcing upper-level READY
     998             :                  * portals to fail, even if they were run and then suspended.
     999             :                  * In theory a suspended upper-level portal could have
    1000             :                  * acquired some references to objects that are about to be
    1001             :                  * destroyed, but there should be sufficient defenses against
    1002             :                  * such cases: the portal's original query cannot contain such
    1003             :                  * references, and any references within, say, cached plans of
    1004             :                  * PL/pgSQL functions are not from active queries and should
    1005             :                  * be protected by revalidation logic.
    1006             :                  */
    1007          28 :                 if (portal->status == PORTAL_ACTIVE)
    1008           0 :                     MarkPortalFailed(portal);
    1009             : 
    1010             :                 /*
    1011             :                  * Also, if we failed it during the current subtransaction
    1012             :                  * (either just above, or earlier), reattach its resource
    1013             :                  * owner to the current subtransaction's resource owner, so
    1014             :                  * that any resources it still holds will be released while
    1015             :                  * cleaning up this subtransaction.  This prevents some corner
    1016             :                  * cases wherein we might get Asserts or worse while cleaning
    1017             :                  * up objects created during the current subtransaction
    1018             :                  * (because they're still referenced within this portal).
    1019             :                  */
    1020          28 :                 if (portal->status == PORTAL_FAILED && portal->resowner)
    1021             :                 {
    1022           8 :                     ResourceOwnerNewParent(portal->resowner, myXactOwner);
    1023           8 :                     portal->resowner = NULL;
    1024             :                 }
    1025             :             }
    1026             :             /* Done if it wasn't created in this subtransaction */
    1027        4778 :             continue;
    1028             :         }
    1029             : 
    1030             :         /*
    1031             :          * Force any live portals of my own subtransaction into FAILED state.
    1032             :          * We have to do this because they might refer to objects created or
    1033             :          * changed in the failed subtransaction, leading to crashes within
    1034             :          * ExecutorEnd when portalcmds.c tries to close down the portal.
    1035             :          * Currently, every MarkPortalActive() caller ensures it updates the
    1036             :          * portal status again before relinquishing control, so ACTIVE can't
    1037             :          * happen here.  If it does happen, dispose the portal like existing
    1038             :          * MarkPortalActive() callers would.
    1039             :          */
    1040         238 :         if (portal->status == PORTAL_READY ||
    1041         114 :             portal->status == PORTAL_ACTIVE)
    1042          10 :             MarkPortalFailed(portal);
    1043             : 
    1044             :         /*
    1045             :          * Allow portalcmds.c to clean up the state it knows about, if we
    1046             :          * haven't already.
    1047             :          */
    1048         124 :         if (PointerIsValid(portal->cleanup))
    1049             :         {
    1050           0 :             portal->cleanup(portal);
    1051           0 :             portal->cleanup = NULL;
    1052             :         }
    1053             : 
    1054             :         /* drop cached plan reference, if any */
    1055         124 :         PortalReleaseCachedPlan(portal);
    1056             : 
    1057             :         /*
    1058             :          * Any resources belonging to the portal will be released in the
    1059             :          * upcoming transaction-wide cleanup; they will be gone before we run
    1060             :          * PortalDrop.
    1061             :          */
    1062         124 :         portal->resowner = NULL;
    1063             : 
    1064             :         /*
    1065             :          * Although we can't delete the portal data structure proper, we can
    1066             :          * release any memory in subsidiary contexts, such as executor state.
    1067             :          * The cleanup hook was the last thing that might have needed data
    1068             :          * there.
    1069             :          */
    1070         124 :         MemoryContextDeleteChildren(portal->portalContext);
    1071             :     }
    1072        2928 : }
    1073             : 
    1074             : /*
    1075             :  * Post-subabort cleanup for portals.
    1076             :  *
    1077             :  * Drop all portals created in the failed subtransaction (but note that
    1078             :  * we will not drop any that were reassigned to the parent above).
    1079             :  */
    1080             : void
    1081        2928 : AtSubCleanup_Portals(SubTransactionId mySubid)
    1082             : {
    1083             :     HASH_SEQ_STATUS status;
    1084             :     PortalHashEnt *hentry;
    1085             : 
    1086        2928 :     hash_seq_init(&status, PortalHashTable);
    1087             : 
    1088       10646 :     while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
    1089             :     {
    1090        4790 :         Portal      portal = hentry->portal;
    1091             : 
    1092        4790 :         if (portal->createSubid != mySubid)
    1093        4778 :             continue;
    1094             : 
    1095             :         /*
    1096             :          * If a portal is still pinned, forcibly unpin it. PortalDrop will not
    1097             :          * let us drop the portal otherwise. Whoever pinned the portal was
    1098             :          * interrupted by the abort too and won't try to use it anymore.
    1099             :          */
    1100          12 :         if (portal->portalPinned)
    1101           6 :             portal->portalPinned = false;
    1102             : 
    1103             :         /*
    1104             :          * We had better not call any user-defined code during cleanup, so if
    1105             :          * the cleanup hook hasn't been run yet, too bad; we'll just skip it.
    1106             :          */
    1107          12 :         if (PointerIsValid(portal->cleanup))
    1108             :         {
    1109           0 :             elog(WARNING, "skipping cleanup for portal \"%s\"", portal->name);
    1110           0 :             portal->cleanup = NULL;
    1111             :         }
    1112             : 
    1113             :         /* Zap it. */
    1114          12 :         PortalDrop(portal, false);
    1115             :     }
    1116        2928 : }
    1117             : 
    1118             : /* Find all available cursors */
    1119             : Datum
    1120          82 : pg_cursor(PG_FUNCTION_ARGS)
    1121             : {
    1122          82 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
    1123             :     TupleDesc   tupdesc;
    1124             :     Tuplestorestate *tupstore;
    1125             :     MemoryContext per_query_ctx;
    1126             :     MemoryContext oldcontext;
    1127             :     HASH_SEQ_STATUS hash_seq;
    1128             :     PortalHashEnt *hentry;
    1129             : 
    1130             :     /* check to see if caller supports us returning a tuplestore */
    1131          82 :     if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
    1132           0 :         ereport(ERROR,
    1133             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1134             :                  errmsg("set-valued function called in context that cannot accept a set")));
    1135          82 :     if (!(rsinfo->allowedModes & SFRM_Materialize))
    1136           0 :         ereport(ERROR,
    1137             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1138             :                  errmsg("materialize mode required, but it is not " \
    1139             :                         "allowed in this context")));
    1140             : 
    1141             :     /* need to build tuplestore in query context */
    1142          82 :     per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
    1143          82 :     oldcontext = MemoryContextSwitchTo(per_query_ctx);
    1144             : 
    1145             :     /*
    1146             :      * build tupdesc for result tuples. This must match the definition of the
    1147             :      * pg_cursors view in system_views.sql
    1148             :      */
    1149          82 :     tupdesc = CreateTemplateTupleDesc(6);
    1150          82 :     TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
    1151             :                        TEXTOID, -1, 0);
    1152          82 :     TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
    1153             :                        TEXTOID, -1, 0);
    1154          82 :     TupleDescInitEntry(tupdesc, (AttrNumber) 3, "is_holdable",
    1155             :                        BOOLOID, -1, 0);
    1156          82 :     TupleDescInitEntry(tupdesc, (AttrNumber) 4, "is_binary",
    1157             :                        BOOLOID, -1, 0);
    1158          82 :     TupleDescInitEntry(tupdesc, (AttrNumber) 5, "is_scrollable",
    1159             :                        BOOLOID, -1, 0);
    1160          82 :     TupleDescInitEntry(tupdesc, (AttrNumber) 6, "creation_time",
    1161             :                        TIMESTAMPTZOID, -1, 0);
    1162             : 
    1163             :     /*
    1164             :      * We put all the tuples into a tuplestore in one scan of the hashtable.
    1165             :      * This avoids any issue of the hashtable possibly changing between calls.
    1166             :      */
    1167          82 :     tupstore =
    1168          82 :         tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random,
    1169             :                               false, work_mem);
    1170             : 
    1171             :     /* generate junk in short-term context */
    1172          82 :     MemoryContextSwitchTo(oldcontext);
    1173             : 
    1174          82 :     hash_seq_init(&hash_seq, PortalHashTable);
    1175         330 :     while ((hentry = hash_seq_search(&hash_seq)) != NULL)
    1176             :     {
    1177         166 :         Portal      portal = hentry->portal;
    1178             :         Datum       values[6];
    1179             :         bool        nulls[6];
    1180             : 
    1181             :         /* report only "visible" entries */
    1182         166 :         if (!portal->visible)
    1183          86 :             continue;
    1184             : 
    1185          80 :         MemSet(nulls, 0, sizeof(nulls));
    1186             : 
    1187          80 :         values[0] = CStringGetTextDatum(portal->name);
    1188          80 :         values[1] = CStringGetTextDatum(portal->sourceText);
    1189          80 :         values[2] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_HOLD);
    1190          80 :         values[3] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_BINARY);
    1191          80 :         values[4] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_SCROLL);
    1192          80 :         values[5] = TimestampTzGetDatum(portal->creation_time);
    1193             : 
    1194          80 :         tuplestore_putvalues(tupstore, tupdesc, values, nulls);
    1195             :     }
    1196             : 
    1197             :     /* clean up and return the tuplestore */
    1198             :     tuplestore_donestoring(tupstore);
    1199             : 
    1200          82 :     rsinfo->returnMode = SFRM_Materialize;
    1201          82 :     rsinfo->setResult = tupstore;
    1202          82 :     rsinfo->setDesc = tupdesc;
    1203             : 
    1204          82 :     return (Datum) 0;
    1205             : }
    1206             : 
    1207             : bool
    1208          28 : ThereAreNoReadyPortals(void)
    1209             : {
    1210             :     HASH_SEQ_STATUS status;
    1211             :     PortalHashEnt *hentry;
    1212             : 
    1213          28 :     hash_seq_init(&status, PortalHashTable);
    1214             : 
    1215          28 :     while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
    1216             :     {
    1217          28 :         Portal      portal = hentry->portal;
    1218             : 
    1219          28 :         if (portal->status == PORTAL_READY)
    1220           0 :             return false;
    1221             :     }
    1222             : 
    1223          28 :     return true;
    1224             : }
    1225             : 
    1226             : /*
    1227             :  * Hold all pinned portals.
    1228             :  *
    1229             :  * When initiating a COMMIT or ROLLBACK inside a procedure, this must be
    1230             :  * called to protect internally-generated cursors from being dropped during
    1231             :  * the transaction shutdown.  Currently, SPI calls this automatically; PLs
    1232             :  * that initiate COMMIT or ROLLBACK some other way are on the hook to do it
    1233             :  * themselves.  (Note that we couldn't do this in, say, AtAbort_Portals
    1234             :  * because we need to run user-defined code while persisting a portal.
    1235             :  * It's too late to do that once transaction abort has started.)
    1236             :  *
    1237             :  * We protect such portals by converting them to held cursors.  We mark them
    1238             :  * as "auto-held" so that exception exit knows to clean them up.  (In normal,
    1239             :  * non-exception code paths, the PL needs to clean such portals itself, since
    1240             :  * transaction end won't do it anymore; but that should be normal practice
    1241             :  * anyway.)
    1242             :  */
    1243             : void
    1244        4330 : HoldPinnedPortals(void)
    1245             : {
    1246             :     HASH_SEQ_STATUS status;
    1247             :     PortalHashEnt *hentry;
    1248             : 
    1249        4330 :     hash_seq_init(&status, PortalHashTable);
    1250             : 
    1251       13086 :     while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
    1252             :     {
    1253        4432 :         Portal      portal = hentry->portal;
    1254             : 
    1255        4432 :         if (portal->portalPinned && !portal->autoHeld)
    1256             :         {
    1257             :             /*
    1258             :              * Doing transaction control, especially abort, inside a cursor
    1259             :              * loop that is not read-only, for example using UPDATE ...
    1260             :              * RETURNING, has weird semantics issues.  Also, this
    1261             :              * implementation wouldn't work, because such portals cannot be
    1262             :              * held.  (The core grammar enforces that only SELECT statements
    1263             :              * can drive a cursor, but for example PL/pgSQL does not restrict
    1264             :              * it.)
    1265             :              */
    1266          30 :             if (portal->strategy != PORTAL_ONE_SELECT)
    1267           2 :                 ereport(ERROR,
    1268             :                         (errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION),
    1269             :                          errmsg("cannot perform transaction commands inside a cursor loop that is not read-only")));
    1270             : 
    1271             :             /* Verify it's in a suitable state to be held */
    1272          28 :             if (portal->status != PORTAL_READY)
    1273           0 :                 elog(ERROR, "pinned portal is not ready to be auto-held");
    1274             : 
    1275          28 :             HoldPortal(portal);
    1276          24 :             portal->autoHeld = true;
    1277             :         }
    1278             :     }
    1279        4324 : }

Generated by: LCOV version 1.13