Coverage Report

Created: 2025-10-09 06:07

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-2025, 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
0
#define PORTALS_PER_USER     16
39
40
41
/* ----------------
42
 *    Global state
43
 * ----------------
44
 */
45
46
0
#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
0
#define PortalHashTableLookup(NAME, PORTAL) \
57
0
do { \
58
0
  PortalHashEnt *hentry; \
59
0
  \
60
0
  hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
61
0
                       (NAME), HASH_FIND, NULL); \
62
0
  if (hentry) \
63
0
    PORTAL = hentry->portal; \
64
0
  else \
65
0
    PORTAL = NULL; \
66
0
} while(0)
67
68
0
#define PortalHashTableInsert(PORTAL, NAME) \
69
0
do { \
70
0
  PortalHashEnt *hentry; bool found; \
71
0
  \
72
0
  hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
73
0
                       (NAME), HASH_ENTER, &found); \
74
0
  if (found) \
75
0
    elog(ERROR, "duplicate portal name"); \
76
0
  hentry->portal = PORTAL; \
77
0
  /* To avoid duplicate storage, make PORTAL->name point to htab entry */ \
78
0
  PORTAL->name = hentry->portalname; \
79
0
} while(0)
80
81
0
#define PortalHashTableDelete(PORTAL) \
82
0
do { \
83
0
  PortalHashEnt *hentry; \
84
0
  \
85
0
  hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
86
0
                       PORTAL->name, HASH_REMOVE, NULL); \
87
0
  if (hentry == NULL) \
88
0
    elog(WARNING, "trying to delete portal name that does not exist"); \
89
0
} 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
EnablePortalManager(void)
105
0
{
106
0
  HASHCTL   ctl;
107
108
0
  Assert(TopPortalContext == NULL);
109
110
0
  TopPortalContext = AllocSetContextCreate(TopMemoryContext,
111
0
                       "TopPortalContext",
112
0
                       ALLOCSET_DEFAULT_SIZES);
113
114
0
  ctl.keysize = MAX_PORTALNAME_LEN;
115
0
  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
0
  PortalHashTable = hash_create("Portal hash", PORTALS_PER_USER,
122
0
                  &ctl, HASH_ELEM | HASH_STRINGS);
123
0
}
124
125
/*
126
 * GetPortalByName
127
 *    Returns a portal given a portal name, or NULL if name not found.
128
 */
129
Portal
130
GetPortalByName(const char *name)
131
0
{
132
0
  Portal    portal;
133
134
0
  if (name)
135
0
    PortalHashTableLookup(name, portal);
136
0
  else
137
0
    portal = NULL;
138
139
0
  return portal;
140
0
}
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
PortalGetPrimaryStmt(Portal portal)
152
0
{
153
0
  ListCell   *lc;
154
155
0
  foreach(lc, portal->stmts)
156
0
  {
157
0
    PlannedStmt *stmt = lfirst_node(PlannedStmt, lc);
158
159
0
    if (stmt->canSetTag)
160
0
      return stmt;
161
0
  }
162
0
  return NULL;
163
0
}
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
CreatePortal(const char *name, bool allowDup, bool dupSilent)
176
0
{
177
0
  Portal    portal;
178
179
0
  Assert(name);
180
181
0
  portal = GetPortalByName(name);
182
0
  if (PortalIsValid(portal))
183
0
  {
184
0
    if (!allowDup)
185
0
      ereport(ERROR,
186
0
          (errcode(ERRCODE_DUPLICATE_CURSOR),
187
0
           errmsg("cursor \"%s\" already exists", name)));
188
0
    if (!dupSilent)
189
0
      ereport(WARNING,
190
0
          (errcode(ERRCODE_DUPLICATE_CURSOR),
191
0
           errmsg("closing existing cursor \"%s\"",
192
0
              name)));
193
0
    PortalDrop(portal, false);
194
0
  }
195
196
  /* make new portal structure */
197
0
  portal = (Portal) MemoryContextAllocZero(TopPortalContext, sizeof *portal);
198
199
  /* initialize portal context; typically it won't store much */
200
0
  portal->portalContext = AllocSetContextCreate(TopPortalContext,
201
0
                          "PortalContext",
202
0
                          ALLOCSET_SMALL_SIZES);
203
204
  /* create a resource owner for the portal */
205
0
  portal->resowner = ResourceOwnerCreate(CurTransactionResourceOwner,
206
0
                       "Portal");
207
208
  /* initialize portal fields that don't start off zero */
209
0
  portal->status = PORTAL_NEW;
210
0
  portal->cleanup = PortalCleanup;
211
0
  portal->createSubid = GetCurrentSubTransactionId();
212
0
  portal->activeSubid = portal->createSubid;
213
0
  portal->createLevel = GetCurrentTransactionNestLevel();
214
0
  portal->strategy = PORTAL_MULTI_QUERY;
215
0
  portal->cursorOptions = CURSOR_OPT_NO_SCROLL;
216
0
  portal->atStart = true;
217
0
  portal->atEnd = true;   /* disallow fetches until query is set */
218
0
  portal->visible = true;
219
0
  portal->creation_time = GetCurrentStatementStartTimestamp();
220
221
  /* put portal in table (sets portal->name) */
222
0
  PortalHashTableInsert(portal, name);
223
224
  /* for named portals reuse portal->name copy */
225
0
  MemoryContextSetIdentifier(portal->portalContext, portal->name[0] ? portal->name : "<unnamed>");
226
227
0
  return portal;
228
0
}
229
230
/*
231
 * CreateNewPortal
232
 *    Create a new portal, assigning it a random nonconflicting name.
233
 */
234
Portal
235
CreateNewPortal(void)
236
0
{
237
0
  static unsigned int unnamed_portal_count = 0;
238
239
0
  char    portalname[MAX_PORTALNAME_LEN];
240
241
  /* Select a nonconflicting name */
242
0
  for (;;)
243
0
  {
244
0
    unnamed_portal_count++;
245
0
    sprintf(portalname, "<unnamed portal %u>", unnamed_portal_count);
246
0
    if (GetPortalByName(portalname) == NULL)
247
0
      break;
248
0
  }
249
250
0
  return CreatePortal(portalname, false, false);
251
0
}
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
PortalDefineQuery(Portal portal,
283
          const char *prepStmtName,
284
          const char *sourceText,
285
          CommandTag commandTag,
286
          List *stmts,
287
          CachedPlan *cplan)
288
0
{
289
0
  Assert(PortalIsValid(portal));
290
0
  Assert(portal->status == PORTAL_NEW);
291
292
0
  Assert(sourceText != NULL);
293
0
  Assert(commandTag != CMDTAG_UNKNOWN || stmts == NIL);
294
295
0
  portal->prepStmtName = prepStmtName;
296
0
  portal->sourceText = sourceText;
297
0
  portal->qc.commandTag = commandTag;
298
0
  portal->qc.nprocessed = 0;
299
0
  portal->commandTag = commandTag;
300
0
  portal->stmts = stmts;
301
0
  portal->cplan = cplan;
302
0
  portal->status = PORTAL_DEFINED;
303
0
}
304
305
/*
306
 * PortalReleaseCachedPlan
307
 *    Release a portal's reference to its cached plan, if any.
308
 */
309
static void
310
PortalReleaseCachedPlan(Portal portal)
311
0
{
312
0
  if (portal->cplan)
313
0
  {
314
0
    ReleaseCachedPlan(portal->cplan, NULL);
315
0
    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
0
    portal->stmts = NIL;
323
0
  }
324
0
}
325
326
/*
327
 * PortalCreateHoldStore
328
 *    Create the tuplestore for a portal.
329
 */
330
void
331
PortalCreateHoldStore(Portal portal)
332
0
{
333
0
  MemoryContext oldcxt;
334
335
0
  Assert(portal->holdContext == NULL);
336
0
  Assert(portal->holdStore == NULL);
337
0
  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
0
  portal->holdContext =
344
0
    AllocSetContextCreate(TopPortalContext,
345
0
                "PortalHoldContext",
346
0
                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
0
  oldcxt = MemoryContextSwitchTo(portal->holdContext);
355
356
0
  portal->holdStore =
357
0
    tuplestore_begin_heap(portal->cursorOptions & CURSOR_OPT_SCROLL,
358
0
                true, work_mem);
359
360
0
  MemoryContextSwitchTo(oldcxt);
361
0
}
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
PinPortal(Portal portal)
372
0
{
373
0
  if (portal->portalPinned)
374
0
    elog(ERROR, "portal already pinned");
375
376
0
  portal->portalPinned = true;
377
0
}
378
379
void
380
UnpinPortal(Portal portal)
381
0
{
382
0
  if (!portal->portalPinned)
383
0
    elog(ERROR, "portal not pinned");
384
385
0
  portal->portalPinned = false;
386
0
}
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
MarkPortalActive(Portal portal)
396
0
{
397
  /* For safety, this is a runtime test not just an Assert */
398
0
  if (portal->status != PORTAL_READY)
399
0
    ereport(ERROR,
400
0
        (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
401
0
         errmsg("portal \"%s\" cannot be run", portal->name)));
402
  /* Perform the state transition */
403
0
  portal->status = PORTAL_ACTIVE;
404
0
  portal->activeSubid = GetCurrentSubTransactionId();
405
0
}
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
MarkPortalDone(Portal portal)
415
0
{
416
  /* Perform the state transition */
417
0
  Assert(portal->status == PORTAL_ACTIVE);
418
0
  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
0
  if (portal->cleanup)
429
0
  {
430
0
    portal->cleanup(portal);
431
0
    portal->cleanup = NULL;
432
0
  }
433
0
}
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
MarkPortalFailed(Portal portal)
443
0
{
444
  /* Perform the state transition */
445
0
  Assert(portal->status != PORTAL_DONE);
446
0
  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
0
  if (portal->cleanup)
457
0
  {
458
0
    portal->cleanup(portal);
459
0
    portal->cleanup = NULL;
460
0
  }
461
0
}
462
463
/*
464
 * PortalDrop
465
 *    Destroy the portal.
466
 */
467
void
468
PortalDrop(Portal portal, bool isTopCommit)
469
0
{
470
0
  Assert(PortalIsValid(portal));
471
472
  /*
473
   * Don't allow dropping a pinned portal, it's still needed by whoever
474
   * pinned it.
475
   */
476
0
  if (portal->portalPinned)
477
0
    ereport(ERROR,
478
0
        (errcode(ERRCODE_INVALID_CURSOR_STATE),
479
0
         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
0
  if (portal->status == PORTAL_ACTIVE)
485
0
    ereport(ERROR,
486
0
        (errcode(ERRCODE_INVALID_CURSOR_STATE),
487
0
         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
0
  if (portal->cleanup)
501
0
  {
502
0
    portal->cleanup(portal);
503
0
    portal->cleanup = NULL;
504
0
  }
505
506
  /* There shouldn't be an active snapshot anymore, except after error */
507
0
  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
0
  PortalHashTableDelete(portal);
516
517
  /* drop cached plan reference, if any */
518
0
  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
0
  if (portal->holdSnapshot)
527
0
  {
528
0
    if (portal->resowner)
529
0
      UnregisterSnapshotFromOwner(portal->holdSnapshot,
530
0
                    portal->resowner);
531
0
    portal->holdSnapshot = NULL;
532
0
  }
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
0
  if (portal->resowner &&
558
0
    (!isTopCommit || portal->status == PORTAL_FAILED))
559
0
  {
560
0
    bool    isCommit = (portal->status != PORTAL_FAILED);
561
562
0
    ResourceOwnerRelease(portal->resowner,
563
0
               RESOURCE_RELEASE_BEFORE_LOCKS,
564
0
               isCommit, false);
565
0
    ResourceOwnerRelease(portal->resowner,
566
0
               RESOURCE_RELEASE_LOCKS,
567
0
               isCommit, false);
568
0
    ResourceOwnerRelease(portal->resowner,
569
0
               RESOURCE_RELEASE_AFTER_LOCKS,
570
0
               isCommit, false);
571
0
    ResourceOwnerDelete(portal->resowner);
572
0
  }
573
0
  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
0
  if (portal->holdStore)
581
0
  {
582
0
    MemoryContext oldcontext;
583
584
0
    oldcontext = MemoryContextSwitchTo(portal->holdContext);
585
0
    tuplestore_end(portal->holdStore);
586
0
    MemoryContextSwitchTo(oldcontext);
587
0
    portal->holdStore = NULL;
588
0
  }
589
590
  /* delete tuplestore storage, if any */
591
0
  if (portal->holdContext)
592
0
    MemoryContextDelete(portal->holdContext);
593
594
  /* release subsidiary storage */
595
0
  MemoryContextDelete(portal->portalContext);
596
597
  /* release portal struct (it's in TopPortalContext) */
598
0
  pfree(portal);
599
0
}
600
601
/*
602
 * Delete all declared cursors.
603
 *
604
 * Used by commands: CLOSE ALL, DISCARD ALL
605
 */
606
void
607
PortalHashTableDeleteAll(void)
608
0
{
609
0
  HASH_SEQ_STATUS status;
610
0
  PortalHashEnt *hentry;
611
612
0
  if (PortalHashTable == NULL)
613
0
    return;
614
615
0
  hash_seq_init(&status, PortalHashTable);
616
0
  while ((hentry = hash_seq_search(&status)) != NULL)
617
0
  {
618
0
    Portal    portal = hentry->portal;
619
620
    /* Can't close the active portal (the one running the command) */
621
0
    if (portal->status == PORTAL_ACTIVE)
622
0
      continue;
623
624
0
    PortalDrop(portal, false);
625
626
    /* Restart the iteration in case that led to other drops */
627
0
    hash_seq_term(&status);
628
0
    hash_seq_init(&status, PortalHashTable);
629
0
  }
630
0
}
631
632
/*
633
 * "Hold" a portal.  Prepare it for access by later transactions.
634
 */
635
static void
636
HoldPortal(Portal portal)
637
0
{
638
  /*
639
   * Note that PersistHoldablePortal() must release all resources used by
640
   * the portal that are local to the creating transaction.
641
   */
642
0
  PortalCreateHoldStore(portal);
643
0
  PersistHoldablePortal(portal);
644
645
  /* drop cached plan reference, if any */
646
0
  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
0
  portal->resowner = NULL;
654
655
  /*
656
   * Having successfully exported the holdable cursor, mark it as not
657
   * belonging to this transaction.
658
   */
659
0
  portal->createSubid = InvalidSubTransactionId;
660
0
  portal->activeSubid = InvalidSubTransactionId;
661
0
  portal->createLevel = 0;
662
0
}
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
PreCommit_Portals(bool isPrepare)
678
0
{
679
0
  bool    result = false;
680
0
  HASH_SEQ_STATUS status;
681
0
  PortalHashEnt *hentry;
682
683
0
  hash_seq_init(&status, PortalHashTable);
684
685
0
  while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
686
0
  {
687
0
    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
0
    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
0
    if (portal->status == PORTAL_ACTIVE)
708
0
    {
709
0
      if (portal->holdSnapshot)
710
0
      {
711
0
        if (portal->resowner)
712
0
          UnregisterSnapshotFromOwner(portal->holdSnapshot,
713
0
                        portal->resowner);
714
0
        portal->holdSnapshot = NULL;
715
0
      }
716
0
      portal->resowner = NULL;
717
      /* Clear portalSnapshot too, for cleanliness */
718
0
      portal->portalSnapshot = NULL;
719
0
      continue;
720
0
    }
721
722
    /* Is it a holdable portal created in the current xact? */
723
0
    if ((portal->cursorOptions & CURSOR_OPT_HOLD) &&
724
0
      portal->createSubid != InvalidSubTransactionId &&
725
0
      portal->status == PORTAL_READY)
726
0
    {
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
0
      if (isPrepare)
736
0
        ereport(ERROR,
737
0
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
738
0
             errmsg("cannot PREPARE a transaction that has created a cursor WITH HOLD")));
739
740
0
      HoldPortal(portal);
741
742
      /* Report we changed state */
743
0
      result = true;
744
0
    }
745
0
    else if (portal->createSubid == InvalidSubTransactionId)
746
0
    {
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
0
      continue;
752
0
    }
753
0
    else
754
0
    {
755
      /* Zap all non-holdable portals */
756
0
      PortalDrop(portal, true);
757
758
      /* Report we changed state */
759
0
      result = true;
760
0
    }
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
0
    hash_seq_term(&status);
768
0
    hash_seq_init(&status, PortalHashTable);
769
0
  }
770
771
0
  return result;
772
0
}
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
AtAbort_Portals(void)
782
0
{
783
0
  HASH_SEQ_STATUS status;
784
0
  PortalHashEnt *hentry;
785
786
0
  hash_seq_init(&status, PortalHashTable);
787
788
0
  while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
789
0
  {
790
0
    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
0
    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
0
    if (portal->createSubid == InvalidSubTransactionId)
803
0
      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
0
    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
0
    if (portal->status == PORTAL_READY)
820
0
      MarkPortalFailed(portal);
821
822
    /*
823
     * Allow portalcmds.c to clean up the state it knows about, if we
824
     * haven't already.
825
     */
826
0
    if (portal->cleanup)
827
0
    {
828
0
      portal->cleanup(portal);
829
0
      portal->cleanup = NULL;
830
0
    }
831
832
    /* drop cached plan reference, if any */
833
0
    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
0
    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
0
    if (portal->status != PORTAL_ACTIVE)
849
0
      MemoryContextDeleteChildren(portal->portalContext);
850
0
  }
851
0
}
852
853
/*
854
 * Post-abort cleanup for portals.
855
 *
856
 * Delete all portals not held over from prior transactions.  */
857
void
858
AtCleanup_Portals(void)
859
{
860
  HASH_SEQ_STATUS status;
861
  PortalHashEnt *hentry;
862
863
  hash_seq_init(&status, PortalHashTable);
864
865
  while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
866
  {
867
    Portal    portal = hentry->portal;
868
869
    /*
870
     * Do not touch active portals --- this can only happen in the case of
871
     * a multi-transaction command.
872
     */
873
    if (portal->status == PORTAL_ACTIVE)
874
      continue;
875
876
    /*
877
     * Do nothing to cursors held over from a previous transaction or
878
     * auto-held ones.
879
     */
880
    if (portal->createSubid == InvalidSubTransactionId || portal->autoHeld)
881
    {
882
      Assert(portal->status != PORTAL_ACTIVE);
883
      Assert(portal->resowner == NULL);
884
      continue;
885
    }
886
887
    /*
888
     * If a portal is still pinned, forcibly unpin it. PortalDrop will not
889
     * let us drop the portal otherwise. Whoever pinned the portal was
890
     * interrupted by the abort too and won't try to use it anymore.
891
     */
892
    if (portal->portalPinned)
893
      portal->portalPinned = false;
894
895
    /*
896
     * We had better not call any user-defined code during cleanup, so if
897
     * the cleanup hook hasn't been run yet, too bad; we'll just skip it.
898
     */
899
    if (portal->cleanup)
900
    {
901
      elog(WARNING, "skipping cleanup for portal \"%s\"", portal->name);
902
      portal->cleanup = NULL;
903
    }
904
905
    /* Zap it. */
906
    PortalDrop(portal, false);
907
  }
908
}
909
910
/*
911
 * Portal-related cleanup when we return to the main loop on error.
912
 *
913
 * This is different from the cleanup at transaction abort.  Auto-held portals
914
 * are cleaned up on error but not on transaction abort.
915
 */
916
void
917
PortalErrorCleanup(void)
918
0
{
919
0
  HASH_SEQ_STATUS status;
920
0
  PortalHashEnt *hentry;
921
922
0
  hash_seq_init(&status, PortalHashTable);
923
924
0
  while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
925
0
  {
926
0
    Portal    portal = hentry->portal;
927
928
0
    if (portal->autoHeld)
929
0
    {
930
0
      portal->portalPinned = false;
931
0
      PortalDrop(portal, false);
932
0
    }
933
0
  }
934
0
}
935
936
/*
937
 * Pre-subcommit processing for portals.
938
 *
939
 * Reassign portals created or used in the current subtransaction to the
940
 * parent subtransaction.
941
 */
942
void
943
AtSubCommit_Portals(SubTransactionId mySubid,
944
          SubTransactionId parentSubid,
945
          int parentLevel,
946
          ResourceOwner parentXactOwner)
947
0
{
948
0
  HASH_SEQ_STATUS status;
949
0
  PortalHashEnt *hentry;
950
951
0
  hash_seq_init(&status, PortalHashTable);
952
953
0
  while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
954
0
  {
955
0
    Portal    portal = hentry->portal;
956
957
0
    if (portal->createSubid == mySubid)
958
0
    {
959
0
      portal->createSubid = parentSubid;
960
0
      portal->createLevel = parentLevel;
961
0
      if (portal->resowner)
962
0
        ResourceOwnerNewParent(portal->resowner, parentXactOwner);
963
0
    }
964
0
    if (portal->activeSubid == mySubid)
965
0
      portal->activeSubid = parentSubid;
966
0
  }
967
0
}
968
969
/*
970
 * Subtransaction abort handling for portals.
971
 *
972
 * Deactivate portals created or used during the failed subtransaction.
973
 * Note that per AtSubCommit_Portals, this will catch portals created/used
974
 * in descendants of the subtransaction too.
975
 *
976
 * We don't destroy any portals here; that's done in AtSubCleanup_Portals.
977
 */
978
void
979
AtSubAbort_Portals(SubTransactionId mySubid,
980
           SubTransactionId parentSubid,
981
           ResourceOwner myXactOwner,
982
           ResourceOwner parentXactOwner)
983
0
{
984
0
  HASH_SEQ_STATUS status;
985
0
  PortalHashEnt *hentry;
986
987
0
  hash_seq_init(&status, PortalHashTable);
988
989
0
  while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
990
0
  {
991
0
    Portal    portal = hentry->portal;
992
993
    /* Was it created in this subtransaction? */
994
0
    if (portal->createSubid != mySubid)
995
0
    {
996
      /* No, but maybe it was used in this subtransaction? */
997
0
      if (portal->activeSubid == mySubid)
998
0
      {
999
        /* Maintain activeSubid until the portal is removed */
1000
0
        portal->activeSubid = parentSubid;
1001
1002
        /*
1003
         * A MarkPortalActive() caller ran an upper-level portal in
1004
         * this subtransaction and left the portal ACTIVE.  This can't
1005
         * happen, but force the portal into FAILED state for the same
1006
         * reasons discussed below.
1007
         *
1008
         * We assume we can get away without forcing upper-level READY
1009
         * portals to fail, even if they were run and then suspended.
1010
         * In theory a suspended upper-level portal could have
1011
         * acquired some references to objects that are about to be
1012
         * destroyed, but there should be sufficient defenses against
1013
         * such cases: the portal's original query cannot contain such
1014
         * references, and any references within, say, cached plans of
1015
         * PL/pgSQL functions are not from active queries and should
1016
         * be protected by revalidation logic.
1017
         */
1018
0
        if (portal->status == PORTAL_ACTIVE)
1019
0
          MarkPortalFailed(portal);
1020
1021
        /*
1022
         * Also, if we failed it during the current subtransaction
1023
         * (either just above, or earlier), reattach its resource
1024
         * owner to the current subtransaction's resource owner, so
1025
         * that any resources it still holds will be released while
1026
         * cleaning up this subtransaction.  This prevents some corner
1027
         * cases wherein we might get Asserts or worse while cleaning
1028
         * up objects created during the current subtransaction
1029
         * (because they're still referenced within this portal).
1030
         */
1031
0
        if (portal->status == PORTAL_FAILED && portal->resowner)
1032
0
        {
1033
0
          ResourceOwnerNewParent(portal->resowner, myXactOwner);
1034
0
          portal->resowner = NULL;
1035
0
        }
1036
0
      }
1037
      /* Done if it wasn't created in this subtransaction */
1038
0
      continue;
1039
0
    }
1040
1041
    /*
1042
     * Force any live portals of my own subtransaction into FAILED state.
1043
     * We have to do this because they might refer to objects created or
1044
     * changed in the failed subtransaction, leading to crashes within
1045
     * ExecutorEnd when portalcmds.c tries to close down the portal.
1046
     * Currently, every MarkPortalActive() caller ensures it updates the
1047
     * portal status again before relinquishing control, so ACTIVE can't
1048
     * happen here.  If it does happen, dispose the portal like existing
1049
     * MarkPortalActive() callers would.
1050
     */
1051
0
    if (portal->status == PORTAL_READY ||
1052
0
      portal->status == PORTAL_ACTIVE)
1053
0
      MarkPortalFailed(portal);
1054
1055
    /*
1056
     * Allow portalcmds.c to clean up the state it knows about, if we
1057
     * haven't already.
1058
     */
1059
0
    if (portal->cleanup)
1060
0
    {
1061
0
      portal->cleanup(portal);
1062
0
      portal->cleanup = NULL;
1063
0
    }
1064
1065
    /* drop cached plan reference, if any */
1066
0
    PortalReleaseCachedPlan(portal);
1067
1068
    /*
1069
     * Any resources belonging to the portal will be released in the
1070
     * upcoming transaction-wide cleanup; they will be gone before we run
1071
     * PortalDrop.
1072
     */
1073
0
    portal->resowner = NULL;
1074
1075
    /*
1076
     * Although we can't delete the portal data structure proper, we can
1077
     * release any memory in subsidiary contexts, such as executor state.
1078
     * The cleanup hook was the last thing that might have needed data
1079
     * there.
1080
     */
1081
0
    MemoryContextDeleteChildren(portal->portalContext);
1082
0
  }
1083
0
}
1084
1085
/*
1086
 * Post-subabort cleanup for portals.
1087
 *
1088
 * Drop all portals created in the failed subtransaction (but note that
1089
 * we will not drop any that were reassigned to the parent above).
1090
 */
1091
void
1092
AtSubCleanup_Portals(SubTransactionId mySubid)
1093
{
1094
  HASH_SEQ_STATUS status;
1095
  PortalHashEnt *hentry;
1096
1097
  hash_seq_init(&status, PortalHashTable);
1098
1099
  while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
1100
  {
1101
    Portal    portal = hentry->portal;
1102
1103
    if (portal->createSubid != mySubid)
1104
      continue;
1105
1106
    /*
1107
     * If a portal is still pinned, forcibly unpin it. PortalDrop will not
1108
     * let us drop the portal otherwise. Whoever pinned the portal was
1109
     * interrupted by the abort too and won't try to use it anymore.
1110
     */
1111
    if (portal->portalPinned)
1112
      portal->portalPinned = false;
1113
1114
    /*
1115
     * We had better not call any user-defined code during cleanup, so if
1116
     * the cleanup hook hasn't been run yet, too bad; we'll just skip it.
1117
     */
1118
    if (portal->cleanup)
1119
    {
1120
      elog(WARNING, "skipping cleanup for portal \"%s\"", portal->name);
1121
      portal->cleanup = NULL;
1122
    }
1123
1124
    /* Zap it. */
1125
    PortalDrop(portal, false);
1126
  }
1127
}
1128
1129
/* Find all available cursors */
1130
Datum
1131
pg_cursor(PG_FUNCTION_ARGS)
1132
0
{
1133
0
  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1134
0
  HASH_SEQ_STATUS hash_seq;
1135
0
  PortalHashEnt *hentry;
1136
1137
  /*
1138
   * We put all the tuples into a tuplestore in one scan of the hashtable.
1139
   * This avoids any issue of the hashtable possibly changing between calls.
1140
   */
1141
0
  InitMaterializedSRF(fcinfo, 0);
1142
1143
0
  hash_seq_init(&hash_seq, PortalHashTable);
1144
0
  while ((hentry = hash_seq_search(&hash_seq)) != NULL)
1145
0
  {
1146
0
    Portal    portal = hentry->portal;
1147
0
    Datum   values[6];
1148
0
    bool    nulls[6] = {0};
1149
1150
    /* report only "visible" entries */
1151
0
    if (!portal->visible)
1152
0
      continue;
1153
    /* also ignore it if PortalDefineQuery hasn't been called yet */
1154
0
    if (!portal->sourceText)
1155
0
      continue;
1156
1157
0
    values[0] = CStringGetTextDatum(portal->name);
1158
0
    values[1] = CStringGetTextDatum(portal->sourceText);
1159
0
    values[2] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_HOLD);
1160
0
    values[3] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_BINARY);
1161
0
    values[4] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_SCROLL);
1162
0
    values[5] = TimestampTzGetDatum(portal->creation_time);
1163
1164
0
    tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
1165
0
  }
1166
1167
0
  return (Datum) 0;
1168
0
}
1169
1170
bool
1171
ThereAreNoReadyPortals(void)
1172
0
{
1173
0
  HASH_SEQ_STATUS status;
1174
0
  PortalHashEnt *hentry;
1175
1176
0
  hash_seq_init(&status, PortalHashTable);
1177
1178
0
  while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
1179
0
  {
1180
0
    Portal    portal = hentry->portal;
1181
1182
0
    if (portal->status == PORTAL_READY)
1183
0
      return false;
1184
0
  }
1185
1186
0
  return true;
1187
0
}
1188
1189
/*
1190
 * Hold all pinned portals.
1191
 *
1192
 * When initiating a COMMIT or ROLLBACK inside a procedure, this must be
1193
 * called to protect internally-generated cursors from being dropped during
1194
 * the transaction shutdown.  Currently, SPI calls this automatically; PLs
1195
 * that initiate COMMIT or ROLLBACK some other way are on the hook to do it
1196
 * themselves.  (Note that we couldn't do this in, say, AtAbort_Portals
1197
 * because we need to run user-defined code while persisting a portal.
1198
 * It's too late to do that once transaction abort has started.)
1199
 *
1200
 * We protect such portals by converting them to held cursors.  We mark them
1201
 * as "auto-held" so that exception exit knows to clean them up.  (In normal,
1202
 * non-exception code paths, the PL needs to clean such portals itself, since
1203
 * transaction end won't do it anymore; but that should be normal practice
1204
 * anyway.)
1205
 */
1206
void
1207
HoldPinnedPortals(void)
1208
0
{
1209
0
  HASH_SEQ_STATUS status;
1210
0
  PortalHashEnt *hentry;
1211
1212
0
  hash_seq_init(&status, PortalHashTable);
1213
1214
0
  while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
1215
0
  {
1216
0
    Portal    portal = hentry->portal;
1217
1218
0
    if (portal->portalPinned && !portal->autoHeld)
1219
0
    {
1220
      /*
1221
       * Doing transaction control, especially abort, inside a cursor
1222
       * loop that is not read-only, for example using UPDATE ...
1223
       * RETURNING, has weird semantics issues.  Also, this
1224
       * implementation wouldn't work, because such portals cannot be
1225
       * held.  (The core grammar enforces that only SELECT statements
1226
       * can drive a cursor, but for example PL/pgSQL does not restrict
1227
       * it.)
1228
       */
1229
0
      if (portal->strategy != PORTAL_ONE_SELECT)
1230
0
        ereport(ERROR,
1231
0
            (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1232
0
             errmsg("cannot perform transaction commands inside a cursor loop that is not read-only")));
1233
1234
      /* Verify it's in a suitable state to be held */
1235
0
      if (portal->status != PORTAL_READY)
1236
0
        elog(ERROR, "pinned portal is not ready to be auto-held");
1237
1238
0
      HoldPortal(portal);
1239
0
      portal->autoHeld = true;
1240
0
    }
1241
0
  }
1242
0
}
1243
1244
/*
1245
 * Drop the outer active snapshots for all portals, so that no snapshots
1246
 * remain active.
1247
 *
1248
 * Like HoldPinnedPortals, this must be called when initiating a COMMIT or
1249
 * ROLLBACK inside a procedure.  This has to be separate from that since it
1250
 * should not be run until we're done with steps that are likely to fail.
1251
 *
1252
 * It's tempting to fold this into PreCommit_Portals, but to do so, we'd
1253
 * need to clean up snapshot management in VACUUM and perhaps other places.
1254
 */
1255
void
1256
ForgetPortalSnapshots(void)
1257
0
{
1258
0
  HASH_SEQ_STATUS status;
1259
0
  PortalHashEnt *hentry;
1260
0
  int     numPortalSnaps = 0;
1261
0
  int     numActiveSnaps = 0;
1262
1263
  /* First, scan PortalHashTable and clear portalSnapshot fields */
1264
0
  hash_seq_init(&status, PortalHashTable);
1265
1266
0
  while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
1267
0
  {
1268
0
    Portal    portal = hentry->portal;
1269
1270
0
    if (portal->portalSnapshot != NULL)
1271
0
    {
1272
0
      portal->portalSnapshot = NULL;
1273
0
      numPortalSnaps++;
1274
0
    }
1275
    /* portal->holdSnapshot will be cleaned up in PreCommit_Portals */
1276
0
  }
1277
1278
  /*
1279
   * Now, pop all the active snapshots, which should be just those that were
1280
   * portal snapshots.  Ideally we'd drive this directly off the portal
1281
   * scan, but there's no good way to visit the portals in the correct
1282
   * order.  So just cross-check after the fact.
1283
   */
1284
0
  while (ActiveSnapshotSet())
1285
0
  {
1286
0
    PopActiveSnapshot();
1287
0
    numActiveSnaps++;
1288
0
  }
1289
1290
0
  if (numPortalSnaps != numActiveSnaps)
1291
0
    elog(ERROR, "portal snapshots (%d) did not account for all active snapshots (%d)",
1292
0
       numPortalSnaps, numActiveSnaps);
1293
0
}