Coverage Report

Created: 2026-08-13 07:12

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