LCOV - code coverage report
Current view: top level - src/backend/utils/mmgr - portalmem.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 82.1 % 420 345
Test Date: 2026-01-26 10:56:24 Functions: 92.6 % 27 25
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 50.0 % 304 152

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

Generated by: LCOV version 2.3.2-1