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