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