]> granicus.if.org Git - postgresql/blob - src/backend/utils/mmgr/portalmem.c
Update CVS HEAD for 2007 copyright. Back branches are typically not
[postgresql] / src / backend / utils / mmgr / portalmem.c
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-2007, PostgreSQL Global Development Group
12  * Portions Copyright (c) 1994, Regents of the University of California
13  *
14  * IDENTIFICATION
15  *        $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.98 2007/01/05 22:19:47 momjian Exp $
16  *
17  *-------------------------------------------------------------------------
18  */
19 #include "postgres.h"
20
21 #include "access/heapam.h"
22 #include "access/xact.h"
23 #include "catalog/pg_type.h"
24 #include "commands/portalcmds.h"
25 #include "funcapi.h"
26 #include "miscadmin.h"
27 #include "utils/builtins.h"
28 #include "utils/memutils.h"
29
30 /*
31  * Estimate of the maximum number of open portals a user would have,
32  * used in initially sizing the PortalHashTable in EnablePortalManager().
33  * Since the hash table can expand, there's no need to make this overly
34  * generous, and keeping it small avoids unnecessary overhead in the
35  * hash_seq_search() calls executed during transaction end.
36  */
37 #define PORTALS_PER_USER           16
38
39
40 /* ----------------
41  *              Global state
42  * ----------------
43  */
44
45 #define MAX_PORTALNAME_LEN              NAMEDATALEN
46
47 typedef struct portalhashent
48 {
49         char            portalname[MAX_PORTALNAME_LEN];
50         Portal          portal;
51 } PortalHashEnt;
52
53 static HTAB *PortalHashTable = NULL;
54
55 #define PortalHashTableLookup(NAME, PORTAL) \
56 do { \
57         PortalHashEnt *hentry; \
58         \
59         hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
60                                                                                    (NAME), HASH_FIND, NULL); \
61         if (hentry) \
62                 PORTAL = hentry->portal; \
63         else \
64                 PORTAL = NULL; \
65 } while(0)
66
67 #define PortalHashTableInsert(PORTAL, NAME) \
68 do { \
69         PortalHashEnt *hentry; bool found; \
70         \
71         hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
72                                                                                    (NAME), HASH_ENTER, &found); \
73         if (found) \
74                 elog(ERROR, "duplicate portal name"); \
75         hentry->portal = PORTAL; \
76         /* To avoid duplicate storage, make PORTAL->name point to htab entry */ \
77         PORTAL->name = hentry->portalname; \
78 } while(0)
79
80 #define PortalHashTableDelete(PORTAL) \
81 do { \
82         PortalHashEnt *hentry; \
83         \
84         hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
85                                                                                    PORTAL->name, HASH_REMOVE, NULL); \
86         if (hentry == NULL) \
87                 elog(WARNING, "trying to delete portal name that does not exist"); \
88 } while(0)
89
90 static MemoryContext PortalMemory = NULL;
91
92
93 /* ----------------------------------------------------------------
94  *                                 public portal interface functions
95  * ----------------------------------------------------------------
96  */
97
98 /*
99  * EnablePortalManager
100  *              Enables the portal management module at backend startup.
101  */
102 void
103 EnablePortalManager(void)
104 {
105         HASHCTL         ctl;
106
107         Assert(PortalMemory == NULL);
108
109         PortalMemory = AllocSetContextCreate(TopMemoryContext,
110                                                                                  "PortalMemory",
111                                                                                  ALLOCSET_DEFAULT_MINSIZE,
112                                                                                  ALLOCSET_DEFAULT_INITSIZE,
113                                                                                  ALLOCSET_DEFAULT_MAXSIZE);
114
115         ctl.keysize = MAX_PORTALNAME_LEN;
116         ctl.entrysize = sizeof(PortalHashEnt);
117
118         /*
119          * use PORTALS_PER_USER as a guess of how many hash table entries to
120          * create, initially
121          */
122         PortalHashTable = hash_create("Portal hash", PORTALS_PER_USER,
123                                                                   &ctl, HASH_ELEM);
124 }
125
126 /*
127  * GetPortalByName
128  *              Returns a portal given a portal name, or NULL if name not found.
129  */
130 Portal
131 GetPortalByName(const char *name)
132 {
133         Portal          portal;
134
135         if (PointerIsValid(name))
136                 PortalHashTableLookup(name, portal);
137         else
138                 portal = NULL;
139
140         return portal;
141 }
142
143 /*
144  * PortalListGetPrimaryQuery
145  *              Get the "primary" Query within a portal, ie, the one marked canSetTag.
146  *
147  * Returns NULL if no such Query.  If multiple Query structs within the
148  * portal are marked canSetTag, returns the first one.  Neither of these
149  * cases should occur in present usages of this function.
150  *
151  * Note: the reason this is just handed a List is so that prepared statements
152  * can share the code.  For use with a portal, use PortalGetPrimaryQuery
153  * rather than calling this directly.
154  */
155 Query *
156 PortalListGetPrimaryQuery(List *parseTrees)
157 {
158         ListCell   *lc;
159
160         foreach(lc, parseTrees)
161         {
162                 Query      *query = (Query *) lfirst(lc);
163
164                 Assert(IsA(query, Query));
165                 if (query->canSetTag)
166                         return query;
167         }
168         return NULL;
169 }
170
171 /*
172  * CreatePortal
173  *              Returns a new portal given a name.
174  *
175  * allowDup: if true, automatically drop any pre-existing portal of the
176  * same name (if false, an error is raised).
177  *
178  * dupSilent: if true, don't even emit a WARNING.
179  */
180 Portal
181 CreatePortal(const char *name, bool allowDup, bool dupSilent)
182 {
183         Portal          portal;
184
185         AssertArg(PointerIsValid(name));
186
187         portal = GetPortalByName(name);
188         if (PortalIsValid(portal))
189         {
190                 if (!allowDup)
191                         ereport(ERROR,
192                                         (errcode(ERRCODE_DUPLICATE_CURSOR),
193                                          errmsg("cursor \"%s\" already exists", name)));
194                 if (!dupSilent)
195                         ereport(WARNING,
196                                         (errcode(ERRCODE_DUPLICATE_CURSOR),
197                                          errmsg("closing existing cursor \"%s\"",
198                                                         name)));
199                 PortalDrop(portal, false);
200         }
201
202         /* make new portal structure */
203         portal = (Portal) MemoryContextAllocZero(PortalMemory, sizeof *portal);
204
205         /* initialize portal heap context; typically it won't store much */
206         portal->heap = AllocSetContextCreate(PortalMemory,
207                                                                                  "PortalHeapMemory",
208                                                                                  ALLOCSET_SMALL_MINSIZE,
209                                                                                  ALLOCSET_SMALL_INITSIZE,
210                                                                                  ALLOCSET_SMALL_MAXSIZE);
211
212         /* create a resource owner for the portal */
213         portal->resowner = ResourceOwnerCreate(CurTransactionResourceOwner,
214                                                                                    "Portal");
215
216         /* initialize portal fields that don't start off zero */
217         portal->status = PORTAL_NEW;
218         portal->cleanup = PortalCleanup;
219         portal->createSubid = GetCurrentSubTransactionId();
220         portal->strategy = PORTAL_MULTI_QUERY;
221         portal->cursorOptions = CURSOR_OPT_NO_SCROLL;
222         portal->atStart = true;
223         portal->atEnd = true;           /* disallow fetches until query is set */
224         portal->visible = true;
225         portal->creation_time = GetCurrentStatementStartTimestamp();
226
227         /* put portal in table (sets portal->name) */
228         PortalHashTableInsert(portal, name);
229
230         return portal;
231 }
232
233 /*
234  * CreateNewPortal
235  *              Create a new portal, assigning it a random nonconflicting name.
236  */
237 Portal
238 CreateNewPortal(void)
239 {
240         static unsigned int unnamed_portal_count = 0;
241
242         char            portalname[MAX_PORTALNAME_LEN];
243
244         /* Select a nonconflicting name */
245         for (;;)
246         {
247                 unnamed_portal_count++;
248                 sprintf(portalname, "<unnamed portal %u>", unnamed_portal_count);
249                 if (GetPortalByName(portalname) == NULL)
250                         break;
251         }
252
253         return CreatePortal(portalname, false, false);
254 }
255
256 /*
257  * PortalDefineQuery
258  *              A simple subroutine to establish a portal's query.
259  *
260  * Notes: 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.  The caller is
263  * responsible for ensuring that the passed prepStmtName (if any), sourceText
264  * (if any), parse and plan trees have adequate lifetime.  Also, queryContext
265  * must accurately describe the location of the parse trees.
266  */
267 void
268 PortalDefineQuery(Portal portal,
269                                   const char *prepStmtName,
270                                   const char *sourceText,
271                                   const char *commandTag,
272                                   List *parseTrees,
273                                   List *planTrees,
274                                   MemoryContext queryContext)
275 {
276         AssertArg(PortalIsValid(portal));
277         AssertState(portal->queryContext == NULL);      /* else defined already */
278
279         Assert(list_length(parseTrees) == list_length(planTrees));
280
281         Assert(commandTag != NULL || parseTrees == NIL);
282
283         portal->prepStmtName = prepStmtName;
284         portal->sourceText = sourceText;
285         portal->commandTag = commandTag;
286         portal->parseTrees = parseTrees;
287         portal->planTrees = planTrees;
288         portal->queryContext = queryContext;
289 }
290
291 /*
292  * PortalCreateHoldStore
293  *              Create the tuplestore for a portal.
294  */
295 void
296 PortalCreateHoldStore(Portal portal)
297 {
298         MemoryContext oldcxt;
299
300         Assert(portal->holdContext == NULL);
301         Assert(portal->holdStore == NULL);
302
303         /*
304          * Create the memory context that is used for storage of the tuple set.
305          * Note this is NOT a child of the portal's heap memory.
306          */
307         portal->holdContext =
308                 AllocSetContextCreate(PortalMemory,
309                                                           "PortalHoldContext",
310                                                           ALLOCSET_DEFAULT_MINSIZE,
311                                                           ALLOCSET_DEFAULT_INITSIZE,
312                                                           ALLOCSET_DEFAULT_MAXSIZE);
313
314         /* Create the tuple store, selecting cross-transaction temp files. */
315         oldcxt = MemoryContextSwitchTo(portal->holdContext);
316
317         /* XXX: Should maintenance_work_mem be used for the portal size? */
318         portal->holdStore = tuplestore_begin_heap(true, true, work_mem);
319
320         MemoryContextSwitchTo(oldcxt);
321 }
322
323 /*
324  * PortalDrop
325  *              Destroy the portal.
326  */
327 void
328 PortalDrop(Portal portal, bool isTopCommit)
329 {
330         AssertArg(PortalIsValid(portal));
331
332         /* Not sure if this case can validly happen or not... */
333         if (portal->status == PORTAL_ACTIVE)
334                 elog(ERROR, "cannot drop active portal");
335
336         /*
337          * Remove portal from hash table.  Because we do this first, we will not
338          * come back to try to remove the portal again if there's any error in the
339          * subsequent steps.  Better to leak a little memory than to get into an
340          * infinite error-recovery loop.
341          */
342         PortalHashTableDelete(portal);
343
344         /* let portalcmds.c clean up the state it knows about */
345         if (PointerIsValid(portal->cleanup))
346                 (*portal->cleanup) (portal);
347
348         /*
349          * Release any resources still attached to the portal.  There are several
350          * cases being covered here:
351          *
352          * Top transaction commit (indicated by isTopCommit): normally we should
353          * do nothing here and let the regular end-of-transaction resource
354          * releasing mechanism handle these resources too.      However, if we have a
355          * FAILED portal (eg, a cursor that got an error), we'd better clean up
356          * its resources to avoid resource-leakage warning messages.
357          *
358          * Sub transaction commit: never comes here at all, since we don't kill
359          * any portals in AtSubCommit_Portals().
360          *
361          * Main or sub transaction abort: we will do nothing here because
362          * portal->resowner was already set NULL; the resources were already
363          * cleaned up in transaction abort.
364          *
365          * Ordinary portal drop: must release resources.  However, if the portal
366          * is not FAILED then we do not release its locks.      The locks become the
367          * responsibility of the transaction's ResourceOwner (since it is the
368          * parent of the portal's owner) and will be released when the transaction
369          * eventually ends.
370          */
371         if (portal->resowner &&
372                 (!isTopCommit || portal->status == PORTAL_FAILED))
373         {
374                 bool            isCommit = (portal->status != PORTAL_FAILED);
375
376                 ResourceOwnerRelease(portal->resowner,
377                                                          RESOURCE_RELEASE_BEFORE_LOCKS,
378                                                          isCommit, false);
379                 ResourceOwnerRelease(portal->resowner,
380                                                          RESOURCE_RELEASE_LOCKS,
381                                                          isCommit, false);
382                 ResourceOwnerRelease(portal->resowner,
383                                                          RESOURCE_RELEASE_AFTER_LOCKS,
384                                                          isCommit, false);
385                 ResourceOwnerDelete(portal->resowner);
386         }
387         portal->resowner = NULL;
388
389         /*
390          * Delete tuplestore if present.  We should do this even under error
391          * conditions; since the tuplestore would have been using cross-
392          * transaction storage, its temp files need to be explicitly deleted.
393          */
394         if (portal->holdStore)
395         {
396                 MemoryContext oldcontext;
397
398                 oldcontext = MemoryContextSwitchTo(portal->holdContext);
399                 tuplestore_end(portal->holdStore);
400                 MemoryContextSwitchTo(oldcontext);
401                 portal->holdStore = NULL;
402         }
403
404         /* delete tuplestore storage, if any */
405         if (portal->holdContext)
406                 MemoryContextDelete(portal->holdContext);
407
408         /* release subsidiary storage */
409         MemoryContextDelete(PortalGetHeapMemory(portal));
410
411         /* release portal struct (it's in PortalMemory) */
412         pfree(portal);
413 }
414
415 /*
416  * DropDependentPortals
417  *              Drop any portals using the specified context as queryContext.
418  *
419  * This is normally used to make sure we can safely drop a prepared statement.
420  */
421 void
422 DropDependentPortals(MemoryContext queryContext)
423 {
424         HASH_SEQ_STATUS status;
425         PortalHashEnt *hentry;
426
427         hash_seq_init(&status, PortalHashTable);
428
429         while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
430         {
431                 Portal          portal = hentry->portal;
432
433                 if (portal->queryContext == queryContext)
434                         PortalDrop(portal, false);
435         }
436 }
437
438
439 /*
440  * Pre-commit processing for portals.
441  *
442  * Any holdable cursors created in this transaction need to be converted to
443  * materialized form, since we are going to close down the executor and
444  * release locks.  Other portals are not touched yet.
445  *
446  * Returns TRUE if any holdable cursors were processed, FALSE if not.
447  */
448 bool
449 CommitHoldablePortals(void)
450 {
451         bool            result = false;
452         HASH_SEQ_STATUS status;
453         PortalHashEnt *hentry;
454
455         hash_seq_init(&status, PortalHashTable);
456
457         while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
458         {
459                 Portal          portal = hentry->portal;
460
461                 /* Is it a holdable portal created in the current xact? */
462                 if ((portal->cursorOptions & CURSOR_OPT_HOLD) &&
463                         portal->createSubid != InvalidSubTransactionId &&
464                         portal->status == PORTAL_READY)
465                 {
466                         /*
467                          * We are exiting the transaction that created a holdable cursor.
468                          * Instead of dropping the portal, prepare it for access by later
469                          * transactions.
470                          *
471                          * Note that PersistHoldablePortal() must release all resources
472                          * used by the portal that are local to the creating transaction.
473                          */
474                         PortalCreateHoldStore(portal);
475                         PersistHoldablePortal(portal);
476
477                         /*
478                          * Any resources belonging to the portal will be released in the
479                          * upcoming transaction-wide cleanup; the portal will no longer
480                          * have its own resources.
481                          */
482                         portal->resowner = NULL;
483
484                         /*
485                          * Having successfully exported the holdable cursor, mark it as
486                          * not belonging to this transaction.
487                          */
488                         portal->createSubid = InvalidSubTransactionId;
489
490                         result = true;
491                 }
492         }
493
494         return result;
495 }
496
497 /*
498  * Pre-prepare processing for portals.
499  *
500  * Currently we refuse PREPARE if the transaction created any holdable
501  * cursors, since it's quite unclear what to do with one.  However, this
502  * has the same API as CommitHoldablePortals and is invoked in the same
503  * way by xact.c, so that we can easily do something reasonable if anyone
504  * comes up with something reasonable to do.
505  *
506  * Returns TRUE if any holdable cursors were processed, FALSE if not.
507  */
508 bool
509 PrepareHoldablePortals(void)
510 {
511         bool            result = false;
512         HASH_SEQ_STATUS status;
513         PortalHashEnt *hentry;
514
515         hash_seq_init(&status, PortalHashTable);
516
517         while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
518         {
519                 Portal          portal = hentry->portal;
520
521                 /* Is it a holdable portal created in the current xact? */
522                 if ((portal->cursorOptions & CURSOR_OPT_HOLD) &&
523                         portal->createSubid != InvalidSubTransactionId &&
524                         portal->status == PORTAL_READY)
525                 {
526                         /*
527                          * We are exiting the transaction that created a holdable cursor.
528                          * Can't do PREPARE.
529                          */
530                         ereport(ERROR,
531                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
532                                          errmsg("cannot PREPARE a transaction that has created a cursor WITH HOLD")));
533                 }
534         }
535
536         return result;
537 }
538
539 /*
540  * Pre-commit processing for portals.
541  *
542  * Remove all non-holdable portals created in this transaction.
543  * Portals remaining from prior transactions should be left untouched.
544  */
545 void
546 AtCommit_Portals(void)
547 {
548         HASH_SEQ_STATUS status;
549         PortalHashEnt *hentry;
550
551         hash_seq_init(&status, PortalHashTable);
552
553         while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
554         {
555                 Portal          portal = hentry->portal;
556
557                 /*
558                  * Do not touch active portals --- this can only happen in the case of
559                  * a multi-transaction utility command, such as VACUUM.
560                  *
561                  * Note however that any resource owner attached to such a portal is
562                  * still going to go away, so don't leave a dangling pointer.
563                  */
564                 if (portal->status == PORTAL_ACTIVE)
565                 {
566                         portal->resowner = NULL;
567                         continue;
568                 }
569
570                 /*
571                  * Do nothing to cursors held over from a previous transaction
572                  * (including holdable ones just frozen by CommitHoldablePortals).
573                  */
574                 if (portal->createSubid == InvalidSubTransactionId)
575                         continue;
576
577                 /* Zap all non-holdable portals */
578                 PortalDrop(portal, true);
579
580                 /* Restart the iteration */
581                 hash_seq_init(&status, PortalHashTable);
582         }
583 }
584
585 /*
586  * Abort processing for portals.
587  *
588  * At this point we reset "active" status and run the cleanup hook if
589  * present, but we can't release the portal's memory until the cleanup call.
590  *
591  * The reason we need to reset active is so that we can replace the unnamed
592  * portal, else we'll fail to execute ROLLBACK when it arrives.
593  */
594 void
595 AtAbort_Portals(void)
596 {
597         HASH_SEQ_STATUS status;
598         PortalHashEnt *hentry;
599
600         hash_seq_init(&status, PortalHashTable);
601
602         while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
603         {
604                 Portal          portal = hentry->portal;
605
606                 if (portal->status == PORTAL_ACTIVE)
607                         portal->status = PORTAL_FAILED;
608
609                 /*
610                  * Do nothing else to cursors held over from a previous transaction.
611                  */
612                 if (portal->createSubid == InvalidSubTransactionId)
613                         continue;
614
615                 /* let portalcmds.c clean up the state it knows about */
616                 if (PointerIsValid(portal->cleanup))
617                 {
618                         (*portal->cleanup) (portal);
619                         portal->cleanup = NULL;
620                 }
621
622                 /*
623                  * Any resources belonging to the portal will be released in the
624                  * upcoming transaction-wide cleanup; they will be gone before we run
625                  * PortalDrop.
626                  */
627                 portal->resowner = NULL;
628
629                 /*
630                  * Although we can't delete the portal data structure proper, we can
631                  * release any memory in subsidiary contexts, such as executor state.
632                  * The cleanup hook was the last thing that might have needed data
633                  * there.
634                  */
635                 MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
636         }
637 }
638
639 /*
640  * Post-abort cleanup for portals.
641  *
642  * Delete all portals not held over from prior transactions.  */
643 void
644 AtCleanup_Portals(void)
645 {
646         HASH_SEQ_STATUS status;
647         PortalHashEnt *hentry;
648
649         hash_seq_init(&status, PortalHashTable);
650
651         while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
652         {
653                 Portal          portal = hentry->portal;
654
655                 /* Do nothing to cursors held over from a previous transaction */
656                 if (portal->createSubid == InvalidSubTransactionId)
657                 {
658                         Assert(portal->status != PORTAL_ACTIVE);
659                         Assert(portal->resowner == NULL);
660                         continue;
661                 }
662
663                 /* Else zap it. */
664                 PortalDrop(portal, false);
665         }
666 }
667
668 /*
669  * Pre-subcommit processing for portals.
670  *
671  * Reassign the portals created in the current subtransaction to the parent
672  * subtransaction.
673  */
674 void
675 AtSubCommit_Portals(SubTransactionId mySubid,
676                                         SubTransactionId parentSubid,
677                                         ResourceOwner parentXactOwner)
678 {
679         HASH_SEQ_STATUS status;
680         PortalHashEnt *hentry;
681
682         hash_seq_init(&status, PortalHashTable);
683
684         while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
685         {
686                 Portal          portal = hentry->portal;
687
688                 if (portal->createSubid == mySubid)
689                 {
690                         portal->createSubid = parentSubid;
691                         if (portal->resowner)
692                                 ResourceOwnerNewParent(portal->resowner, parentXactOwner);
693                 }
694         }
695 }
696
697 /*
698  * Subtransaction abort handling for portals.
699  *
700  * Deactivate portals created during the failed subtransaction.
701  * Note that per AtSubCommit_Portals, this will catch portals created
702  * in descendants of the subtransaction too.
703  *
704  * We don't destroy any portals here; that's done in AtSubCleanup_Portals.
705  */
706 void
707 AtSubAbort_Portals(SubTransactionId mySubid,
708                                    SubTransactionId parentSubid,
709                                    ResourceOwner parentXactOwner)
710 {
711         HASH_SEQ_STATUS status;
712         PortalHashEnt *hentry;
713
714         hash_seq_init(&status, PortalHashTable);
715
716         while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
717         {
718                 Portal          portal = hentry->portal;
719
720                 if (portal->createSubid != mySubid)
721                         continue;
722
723                 /*
724                  * Force any active portals of my own transaction into FAILED state.
725                  * This is mostly to ensure that a portal running a FETCH will go
726                  * FAILED if the underlying cursor fails.  (Note we do NOT want to do
727                  * this to upper-level portals, since they may be able to continue.)
728                  *
729                  * This is only needed to dodge the sanity check in PortalDrop.
730                  */
731                 if (portal->status == PORTAL_ACTIVE)
732                         portal->status = PORTAL_FAILED;
733
734                 /*
735                  * If the portal is READY then allow it to survive into the parent
736                  * transaction; otherwise shut it down.
737                  *
738                  * Currently, we can't actually support that because the portal's
739                  * query might refer to objects created or changed in the failed
740                  * subtransaction, leading to crashes if execution is resumed. So,
741                  * even READY portals are deleted.      It would be nice to detect whether
742                  * the query actually depends on any such object, instead.
743                  */
744 #ifdef NOT_USED
745                 if (portal->status == PORTAL_READY)
746                 {
747                         portal->createSubid = parentSubid;
748                         if (portal->resowner)
749                                 ResourceOwnerNewParent(portal->resowner, parentXactOwner);
750                 }
751                 else
752 #endif
753                 {
754                         /* let portalcmds.c clean up the state it knows about */
755                         if (PointerIsValid(portal->cleanup))
756                         {
757                                 (*portal->cleanup) (portal);
758                                 portal->cleanup = NULL;
759                         }
760
761                         /*
762                          * Any resources belonging to the portal will be released in the
763                          * upcoming transaction-wide cleanup; they will be gone before we
764                          * run PortalDrop.
765                          */
766                         portal->resowner = NULL;
767
768                         /*
769                          * Although we can't delete the portal data structure proper, we
770                          * can release any memory in subsidiary contexts, such as executor
771                          * state.  The cleanup hook was the last thing that might have
772                          * needed data there.
773                          */
774                         MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
775                 }
776         }
777 }
778
779 /*
780  * Post-subabort cleanup for portals.
781  *
782  * Drop all portals created in the failed subtransaction (but note that
783  * we will not drop any that were reassigned to the parent above).
784  */
785 void
786 AtSubCleanup_Portals(SubTransactionId mySubid)
787 {
788         HASH_SEQ_STATUS status;
789         PortalHashEnt *hentry;
790
791         hash_seq_init(&status, PortalHashTable);
792
793         while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
794         {
795                 Portal          portal = hentry->portal;
796
797                 if (portal->createSubid != mySubid)
798                         continue;
799
800                 /* Zap it. */
801                 PortalDrop(portal, false);
802         }
803 }
804
805 /* Find all available cursors */
806 Datum
807 pg_cursor(PG_FUNCTION_ARGS)
808 {
809         FuncCallContext *funcctx;
810         HASH_SEQ_STATUS *hash_seq;
811         PortalHashEnt *hentry;
812
813         /* stuff done only on the first call of the function */
814         if (SRF_IS_FIRSTCALL())
815         {
816                 MemoryContext oldcontext;
817                 TupleDesc       tupdesc;
818
819                 /* create a function context for cross-call persistence */
820                 funcctx = SRF_FIRSTCALL_INIT();
821
822                 /*
823                  * switch to memory context appropriate for multiple function calls
824                  */
825                 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
826
827                 if (PortalHashTable)
828                 {
829                         hash_seq = (HASH_SEQ_STATUS *) palloc(sizeof(HASH_SEQ_STATUS));
830                         hash_seq_init(hash_seq, PortalHashTable);
831                         funcctx->user_fctx = (void *) hash_seq;
832                 }
833                 else
834                         funcctx->user_fctx = NULL;
835
836                 /*
837                  * build tupdesc for result tuples. This must match the definition of
838                  * the pg_cursors view in system_views.sql
839                  */
840                 tupdesc = CreateTemplateTupleDesc(6, false);
841                 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
842                                                    TEXTOID, -1, 0);
843                 TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
844                                                    TEXTOID, -1, 0);
845                 TupleDescInitEntry(tupdesc, (AttrNumber) 3, "is_holdable",
846                                                    BOOLOID, -1, 0);
847                 TupleDescInitEntry(tupdesc, (AttrNumber) 4, "is_binary",
848                                                    BOOLOID, -1, 0);
849                 TupleDescInitEntry(tupdesc, (AttrNumber) 5, "is_scrollable",
850                                                    BOOLOID, -1, 0);
851                 TupleDescInitEntry(tupdesc, (AttrNumber) 6, "creation_time",
852                                                    TIMESTAMPTZOID, -1, 0);
853
854                 funcctx->tuple_desc = BlessTupleDesc(tupdesc);
855                 MemoryContextSwitchTo(oldcontext);
856         }
857
858         /* stuff done on every call of the function */
859         funcctx = SRF_PERCALL_SETUP();
860         hash_seq = (HASH_SEQ_STATUS *) funcctx->user_fctx;
861
862         /* if the hash table is uninitialized, we're done */
863         if (hash_seq == NULL)
864                 SRF_RETURN_DONE(funcctx);
865
866         /* loop until we find a visible portal or hit the end of the list */
867         while ((hentry = hash_seq_search(hash_seq)) != NULL)
868         {
869                 if (hentry->portal->visible)
870                         break;
871         }
872
873         if (hentry)
874         {
875                 Portal          portal;
876                 Datum           result;
877                 HeapTuple       tuple;
878                 Datum           values[6];
879                 bool            nulls[6];
880
881                 portal = hentry->portal;
882                 MemSet(nulls, 0, sizeof(nulls));
883
884                 values[0] = DirectFunctionCall1(textin, CStringGetDatum(portal->name));
885                 if (!portal->sourceText)
886                         nulls[1] = true;
887                 else
888                         values[1] = DirectFunctionCall1(textin,
889                                                                                 CStringGetDatum(portal->sourceText));
890                 values[2] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_HOLD);
891                 values[3] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_BINARY);
892                 values[4] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_SCROLL);
893                 values[5] = TimestampTzGetDatum(portal->creation_time);
894
895                 tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
896                 result = HeapTupleGetDatum(tuple);
897                 SRF_RETURN_NEXT(funcctx, result);
898         }
899
900         SRF_RETURN_DONE(funcctx);
901 }