1 /*-------------------------------------------------------------------------
4 * POSTGRES resource owner management code.
6 * Query-lifespan resources are tracked by associating them with
7 * ResourceOwner objects. This provides a simple mechanism for ensuring
8 * that such resources are freed at the right time.
9 * See utils/resowner/README for more info.
12 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
13 * Portions Copyright (c) 1994, Regents of the University of California
17 * $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.32 2009/06/11 14:49:06 momjian Exp $
19 *-------------------------------------------------------------------------
23 #include "access/hash.h"
24 #include "storage/bufmgr.h"
25 #include "storage/proc.h"
26 #include "utils/memutils.h"
27 #include "utils/rel.h"
28 #include "utils/resowner.h"
29 #include "utils/snapmgr.h"
33 * ResourceOwner objects look like this
35 typedef struct ResourceOwnerData
37 ResourceOwner parent; /* NULL if no parent (toplevel owner) */
38 ResourceOwner firstchild; /* head of linked list of children */
39 ResourceOwner nextchild; /* next child of same parent */
40 const char *name; /* name (just for debugging) */
42 /* We have built-in support for remembering owned buffers */
43 int nbuffers; /* number of owned buffer pins */
44 Buffer *buffers; /* dynamically allocated array */
45 int maxbuffers; /* currently allocated array size */
47 /* We have built-in support for remembering catcache references */
48 int ncatrefs; /* number of owned catcache pins */
49 HeapTuple *catrefs; /* dynamically allocated array */
50 int maxcatrefs; /* currently allocated array size */
52 int ncatlistrefs; /* number of owned catcache-list pins */
53 CatCList **catlistrefs; /* dynamically allocated array */
54 int maxcatlistrefs; /* currently allocated array size */
56 /* We have built-in support for remembering relcache references */
57 int nrelrefs; /* number of owned relcache pins */
58 Relation *relrefs; /* dynamically allocated array */
59 int maxrelrefs; /* currently allocated array size */
61 /* We have built-in support for remembering plancache references */
62 int nplanrefs; /* number of owned plancache pins */
63 CachedPlan **planrefs; /* dynamically allocated array */
64 int maxplanrefs; /* currently allocated array size */
66 /* We have built-in support for remembering tupdesc references */
67 int ntupdescs; /* number of owned tupdesc references */
68 TupleDesc *tupdescs; /* dynamically allocated array */
69 int maxtupdescs; /* currently allocated array size */
71 /* We have built-in support for remembering snapshot references */
72 int nsnapshots; /* number of owned snapshot references */
73 Snapshot *snapshots; /* dynamically allocated array */
74 int maxsnapshots; /* currently allocated array size */
78 /*****************************************************************************
80 *****************************************************************************/
82 ResourceOwner CurrentResourceOwner = NULL;
83 ResourceOwner CurTransactionResourceOwner = NULL;
84 ResourceOwner TopTransactionResourceOwner = NULL;
87 * List of add-on callbacks for resource releasing
89 typedef struct ResourceReleaseCallbackItem
91 struct ResourceReleaseCallbackItem *next;
92 ResourceReleaseCallback callback;
94 } ResourceReleaseCallbackItem;
96 static ResourceReleaseCallbackItem *ResourceRelease_callbacks = NULL;
99 /* Internal routines */
100 static void ResourceOwnerReleaseInternal(ResourceOwner owner,
101 ResourceReleasePhase phase,
104 static void PrintRelCacheLeakWarning(Relation rel);
105 static void PrintPlanCacheLeakWarning(CachedPlan *plan);
106 static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
107 static void PrintSnapshotLeakWarning(Snapshot snapshot);
110 /*****************************************************************************
111 * EXPORTED ROUTINES *
112 *****************************************************************************/
116 * ResourceOwnerCreate
117 * Create an empty ResourceOwner.
119 * All ResourceOwner objects are kept in TopMemoryContext, since they should
120 * only be freed explicitly.
123 ResourceOwnerCreate(ResourceOwner parent, const char *name)
127 owner = (ResourceOwner) MemoryContextAllocZero(TopMemoryContext,
128 sizeof(ResourceOwnerData));
133 owner->parent = parent;
134 owner->nextchild = parent->firstchild;
135 parent->firstchild = owner;
142 * ResourceOwnerRelease
143 * Release all resources owned by a ResourceOwner and its descendants,
144 * but don't delete the owner objects themselves.
146 * Note that this executes just one phase of release, and so typically
147 * must be called three times. We do it this way because (a) we want to
148 * do all the recursion separately for each phase, thereby preserving
149 * the needed order of operations; and (b) xact.c may have other operations
150 * to do between the phases.
152 * phase: release phase to execute
153 * isCommit: true for successful completion of a query or transaction,
154 * false for unsuccessful
155 * isTopLevel: true if completing a main transaction, else false
157 * isCommit is passed because some modules may expect that their resources
158 * were all released already if the transaction or portal finished normally.
159 * If so it is reasonable to give a warning (NOT an error) should any
160 * unreleased resources be present. When isCommit is false, such warnings
161 * are generally inappropriate.
163 * isTopLevel is passed when we are releasing TopTransactionResourceOwner
164 * at completion of a main transaction. This generally means that *all*
165 * resources will be released, and so we can optimize things a bit.
168 ResourceOwnerRelease(ResourceOwner owner,
169 ResourceReleasePhase phase,
173 /* Rather than PG_TRY at every level of recursion, set it up once */
176 save = CurrentResourceOwner;
179 ResourceOwnerReleaseInternal(owner, phase, isCommit, isTopLevel);
183 CurrentResourceOwner = save;
187 CurrentResourceOwner = save;
191 ResourceOwnerReleaseInternal(ResourceOwner owner,
192 ResourceReleasePhase phase,
198 ResourceReleaseCallbackItem *item;
200 /* Recurse to handle descendants */
201 for (child = owner->firstchild; child != NULL; child = child->nextchild)
202 ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel);
205 * Make CurrentResourceOwner point to me, so that ReleaseBuffer etc don't
206 * get confused. We needn't PG_TRY here because the outermost level will
207 * fix it on error abort.
209 save = CurrentResourceOwner;
210 CurrentResourceOwner = owner;
212 if (phase == RESOURCE_RELEASE_BEFORE_LOCKS)
215 * Release buffer pins. Note that ReleaseBuffer will remove the
216 * buffer entry from my list, so I just have to iterate till there are
219 * During a commit, there shouldn't be any remaining pins --- that
220 * would indicate failure to clean up the executor correctly --- so
221 * issue warnings. In the abort case, just clean up quietly.
223 * We are careful to do the releasing back-to-front, so as to avoid
224 * O(N^2) behavior in ResourceOwnerForgetBuffer().
226 while (owner->nbuffers > 0)
229 PrintBufferLeakWarning(owner->buffers[owner->nbuffers - 1]);
230 ReleaseBuffer(owner->buffers[owner->nbuffers - 1]);
234 * Release relcache references. Note that RelationClose will remove
235 * the relref entry from my list, so I just have to iterate till there
238 * As with buffer pins, warn if any are left at commit time, and
239 * release back-to-front for speed.
241 while (owner->nrelrefs > 0)
244 PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]);
245 RelationClose(owner->relrefs[owner->nrelrefs - 1]);
248 else if (phase == RESOURCE_RELEASE_LOCKS)
253 * For a top-level xact we are going to release all locks (or at
254 * least all non-session locks), so just do a single lmgr call at
255 * the top of the recursion.
257 if (owner == TopTransactionResourceOwner)
258 ProcReleaseLocks(isCommit);
263 * Release locks retail. Note that if we are committing a
264 * subtransaction, we do NOT release its locks yet, but transfer
265 * them to the parent.
267 Assert(owner->parent != NULL);
269 LockReassignCurrentOwner();
271 LockReleaseCurrentOwner();
274 else if (phase == RESOURCE_RELEASE_AFTER_LOCKS)
277 * Release catcache references. Note that ReleaseCatCache will remove
278 * the catref entry from my list, so I just have to iterate till there
281 * As with buffer pins, warn if any are left at commit time, and
282 * release back-to-front for speed.
284 while (owner->ncatrefs > 0)
287 PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
288 ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
290 /* Ditto for catcache lists */
291 while (owner->ncatlistrefs > 0)
294 PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
295 ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
297 /* Ditto for plancache references */
298 while (owner->nplanrefs > 0)
301 PrintPlanCacheLeakWarning(owner->planrefs[owner->nplanrefs - 1]);
302 ReleaseCachedPlan(owner->planrefs[owner->nplanrefs - 1], true);
304 /* Ditto for tupdesc references */
305 while (owner->ntupdescs > 0)
308 PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]);
309 DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1]);
311 /* Ditto for snapshot references */
312 while (owner->nsnapshots > 0)
315 PrintSnapshotLeakWarning(owner->snapshots[owner->nsnapshots - 1]);
316 UnregisterSnapshot(owner->snapshots[owner->nsnapshots - 1]);
319 /* Clean up index scans too */
320 ReleaseResources_hash();
323 /* Let add-on modules get a chance too */
324 for (item = ResourceRelease_callbacks; item; item = item->next)
325 (*item->callback) (phase, isCommit, isTopLevel, item->arg);
327 CurrentResourceOwner = save;
331 * ResourceOwnerDelete
332 * Delete an owner object and its descendants.
334 * The caller must have already released all resources in the object tree.
337 ResourceOwnerDelete(ResourceOwner owner)
339 /* We had better not be deleting CurrentResourceOwner ... */
340 Assert(owner != CurrentResourceOwner);
342 /* And it better not own any resources, either */
343 Assert(owner->nbuffers == 0);
344 Assert(owner->ncatrefs == 0);
345 Assert(owner->ncatlistrefs == 0);
346 Assert(owner->nrelrefs == 0);
347 Assert(owner->nplanrefs == 0);
348 Assert(owner->ntupdescs == 0);
349 Assert(owner->nsnapshots == 0);
352 * Delete children. The recursive call will delink the child from me, so
353 * just iterate as long as there is a child.
355 while (owner->firstchild != NULL)
356 ResourceOwnerDelete(owner->firstchild);
359 * We delink the owner from its parent before deleting it, so that if
360 * there's an error we won't have deleted/busted owners still attached to
361 * the owner tree. Better a leak than a crash.
363 ResourceOwnerNewParent(owner, NULL);
365 /* And free the object. */
367 pfree(owner->buffers);
369 pfree(owner->catrefs);
370 if (owner->catlistrefs)
371 pfree(owner->catlistrefs);
373 pfree(owner->relrefs);
375 pfree(owner->planrefs);
377 pfree(owner->tupdescs);
378 if (owner->snapshots)
379 pfree(owner->snapshots);
385 * Fetch parent of a ResourceOwner (returns NULL if top-level owner)
388 ResourceOwnerGetParent(ResourceOwner owner)
390 return owner->parent;
394 * Reassign a ResourceOwner to have a new parent
397 ResourceOwnerNewParent(ResourceOwner owner,
398 ResourceOwner newparent)
400 ResourceOwner oldparent = owner->parent;
404 if (owner == oldparent->firstchild)
405 oldparent->firstchild = owner->nextchild;
410 for (child = oldparent->firstchild; child; child = child->nextchild)
412 if (owner == child->nextchild)
414 child->nextchild = owner->nextchild;
423 Assert(owner != newparent);
424 owner->parent = newparent;
425 owner->nextchild = newparent->firstchild;
426 newparent->firstchild = owner;
430 owner->parent = NULL;
431 owner->nextchild = NULL;
436 * Register or deregister callback functions for resource cleanup
438 * These functions are intended for use by dynamically loaded modules.
439 * For built-in modules we generally just hardwire the appropriate calls.
441 * Note that the callback occurs post-commit or post-abort, so the callback
442 * functions can only do noncritical cleanup.
445 RegisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
447 ResourceReleaseCallbackItem *item;
449 item = (ResourceReleaseCallbackItem *)
450 MemoryContextAlloc(TopMemoryContext,
451 sizeof(ResourceReleaseCallbackItem));
452 item->callback = callback;
454 item->next = ResourceRelease_callbacks;
455 ResourceRelease_callbacks = item;
459 UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
461 ResourceReleaseCallbackItem *item;
462 ResourceReleaseCallbackItem *prev;
465 for (item = ResourceRelease_callbacks; item; prev = item, item = item->next)
467 if (item->callback == callback && item->arg == arg)
470 prev->next = item->next;
472 ResourceRelease_callbacks = item->next;
481 * Make sure there is room for at least one more entry in a ResourceOwner's
484 * This is separate from actually inserting an entry because if we run out
485 * of memory, it's critical to do so *before* acquiring the resource.
487 * We allow the case owner == NULL because the bufmgr is sometimes invoked
488 * outside any transaction (for example, during WAL recovery).
491 ResourceOwnerEnlargeBuffers(ResourceOwner owner)
496 owner->nbuffers < owner->maxbuffers)
497 return; /* nothing to do */
499 if (owner->buffers == NULL)
502 owner->buffers = (Buffer *)
503 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Buffer));
504 owner->maxbuffers = newmax;
508 newmax = owner->maxbuffers * 2;
509 owner->buffers = (Buffer *)
510 repalloc(owner->buffers, newmax * sizeof(Buffer));
511 owner->maxbuffers = newmax;
516 * Remember that a buffer pin is owned by a ResourceOwner
518 * Caller must have previously done ResourceOwnerEnlargeBuffers()
520 * We allow the case owner == NULL because the bufmgr is sometimes invoked
521 * outside any transaction (for example, during WAL recovery).
524 ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
528 Assert(owner->nbuffers < owner->maxbuffers);
529 owner->buffers[owner->nbuffers] = buffer;
535 * Forget that a buffer pin is owned by a ResourceOwner
537 * We allow the case owner == NULL because the bufmgr is sometimes invoked
538 * outside any transaction (for example, during WAL recovery).
541 ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
545 Buffer *buffers = owner->buffers;
546 int nb1 = owner->nbuffers - 1;
550 * Scan back-to-front because it's more likely we are releasing a
551 * recently pinned buffer. This isn't always the case of course, but
552 * it's the way to bet.
554 for (i = nb1; i >= 0; i--)
556 if (buffers[i] == buffer)
560 buffers[i] = buffers[i + 1];
563 owner->nbuffers = nb1;
567 elog(ERROR, "buffer %d is not owned by resource owner %s",
568 buffer, owner->name);
573 * Make sure there is room for at least one more entry in a ResourceOwner's
574 * catcache reference array.
576 * This is separate from actually inserting an entry because if we run out
577 * of memory, it's critical to do so *before* acquiring the resource.
580 ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
584 if (owner->ncatrefs < owner->maxcatrefs)
585 return; /* nothing to do */
587 if (owner->catrefs == NULL)
590 owner->catrefs = (HeapTuple *)
591 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(HeapTuple));
592 owner->maxcatrefs = newmax;
596 newmax = owner->maxcatrefs * 2;
597 owner->catrefs = (HeapTuple *)
598 repalloc(owner->catrefs, newmax * sizeof(HeapTuple));
599 owner->maxcatrefs = newmax;
604 * Remember that a catcache reference is owned by a ResourceOwner
606 * Caller must have previously done ResourceOwnerEnlargeCatCacheRefs()
609 ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
611 Assert(owner->ncatrefs < owner->maxcatrefs);
612 owner->catrefs[owner->ncatrefs] = tuple;
617 * Forget that a catcache reference is owned by a ResourceOwner
620 ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
622 HeapTuple *catrefs = owner->catrefs;
623 int nc1 = owner->ncatrefs - 1;
626 for (i = nc1; i >= 0; i--)
628 if (catrefs[i] == tuple)
632 catrefs[i] = catrefs[i + 1];
635 owner->ncatrefs = nc1;
639 elog(ERROR, "catcache reference %p is not owned by resource owner %s",
644 * Make sure there is room for at least one more entry in a ResourceOwner's
645 * catcache-list reference array.
647 * This is separate from actually inserting an entry because if we run out
648 * of memory, it's critical to do so *before* acquiring the resource.
651 ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
655 if (owner->ncatlistrefs < owner->maxcatlistrefs)
656 return; /* nothing to do */
658 if (owner->catlistrefs == NULL)
661 owner->catlistrefs = (CatCList **)
662 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CatCList *));
663 owner->maxcatlistrefs = newmax;
667 newmax = owner->maxcatlistrefs * 2;
668 owner->catlistrefs = (CatCList **)
669 repalloc(owner->catlistrefs, newmax * sizeof(CatCList *));
670 owner->maxcatlistrefs = newmax;
675 * Remember that a catcache-list reference is owned by a ResourceOwner
677 * Caller must have previously done ResourceOwnerEnlargeCatCacheListRefs()
680 ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
682 Assert(owner->ncatlistrefs < owner->maxcatlistrefs);
683 owner->catlistrefs[owner->ncatlistrefs] = list;
684 owner->ncatlistrefs++;
688 * Forget that a catcache-list reference is owned by a ResourceOwner
691 ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
693 CatCList **catlistrefs = owner->catlistrefs;
694 int nc1 = owner->ncatlistrefs - 1;
697 for (i = nc1; i >= 0; i--)
699 if (catlistrefs[i] == list)
703 catlistrefs[i] = catlistrefs[i + 1];
706 owner->ncatlistrefs = nc1;
710 elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
715 * Make sure there is room for at least one more entry in a ResourceOwner's
716 * relcache reference array.
718 * This is separate from actually inserting an entry because if we run out
719 * of memory, it's critical to do so *before* acquiring the resource.
722 ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
726 if (owner->nrelrefs < owner->maxrelrefs)
727 return; /* nothing to do */
729 if (owner->relrefs == NULL)
732 owner->relrefs = (Relation *)
733 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Relation));
734 owner->maxrelrefs = newmax;
738 newmax = owner->maxrelrefs * 2;
739 owner->relrefs = (Relation *)
740 repalloc(owner->relrefs, newmax * sizeof(Relation));
741 owner->maxrelrefs = newmax;
746 * Remember that a relcache reference is owned by a ResourceOwner
748 * Caller must have previously done ResourceOwnerEnlargeRelationRefs()
751 ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
753 Assert(owner->nrelrefs < owner->maxrelrefs);
754 owner->relrefs[owner->nrelrefs] = rel;
759 * Forget that a relcache reference is owned by a ResourceOwner
762 ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel)
764 Relation *relrefs = owner->relrefs;
765 int nr1 = owner->nrelrefs - 1;
768 for (i = nr1; i >= 0; i--)
770 if (relrefs[i] == rel)
774 relrefs[i] = relrefs[i + 1];
777 owner->nrelrefs = nr1;
781 elog(ERROR, "relcache reference %s is not owned by resource owner %s",
782 RelationGetRelationName(rel), owner->name);
786 * Debugging subroutine
789 PrintRelCacheLeakWarning(Relation rel)
791 elog(WARNING, "relcache reference leak: relation \"%s\" not closed",
792 RelationGetRelationName(rel));
796 * Make sure there is room for at least one more entry in a ResourceOwner's
797 * plancache reference array.
799 * This is separate from actually inserting an entry because if we run out
800 * of memory, it's critical to do so *before* acquiring the resource.
803 ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
807 if (owner->nplanrefs < owner->maxplanrefs)
808 return; /* nothing to do */
810 if (owner->planrefs == NULL)
813 owner->planrefs = (CachedPlan **)
814 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CachedPlan *));
815 owner->maxplanrefs = newmax;
819 newmax = owner->maxplanrefs * 2;
820 owner->planrefs = (CachedPlan **)
821 repalloc(owner->planrefs, newmax * sizeof(CachedPlan *));
822 owner->maxplanrefs = newmax;
827 * Remember that a plancache reference is owned by a ResourceOwner
829 * Caller must have previously done ResourceOwnerEnlargePlanCacheRefs()
832 ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
834 Assert(owner->nplanrefs < owner->maxplanrefs);
835 owner->planrefs[owner->nplanrefs] = plan;
840 * Forget that a plancache reference is owned by a ResourceOwner
843 ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
845 CachedPlan **planrefs = owner->planrefs;
846 int np1 = owner->nplanrefs - 1;
849 for (i = np1; i >= 0; i--)
851 if (planrefs[i] == plan)
855 planrefs[i] = planrefs[i + 1];
858 owner->nplanrefs = np1;
862 elog(ERROR, "plancache reference %p is not owned by resource owner %s",
867 * Debugging subroutine
870 PrintPlanCacheLeakWarning(CachedPlan *plan)
872 elog(WARNING, "plancache reference leak: plan %p not closed", plan);
876 * Make sure there is room for at least one more entry in a ResourceOwner's
877 * tupdesc reference array.
879 * This is separate from actually inserting an entry because if we run out
880 * of memory, it's critical to do so *before* acquiring the resource.
883 ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
887 if (owner->ntupdescs < owner->maxtupdescs)
888 return; /* nothing to do */
890 if (owner->tupdescs == NULL)
893 owner->tupdescs = (TupleDesc *)
894 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(TupleDesc));
895 owner->maxtupdescs = newmax;
899 newmax = owner->maxtupdescs * 2;
900 owner->tupdescs = (TupleDesc *)
901 repalloc(owner->tupdescs, newmax * sizeof(TupleDesc));
902 owner->maxtupdescs = newmax;
907 * Remember that a tupdesc reference is owned by a ResourceOwner
909 * Caller must have previously done ResourceOwnerEnlargeTupleDescs()
912 ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
914 Assert(owner->ntupdescs < owner->maxtupdescs);
915 owner->tupdescs[owner->ntupdescs] = tupdesc;
920 * Forget that a tupdesc reference is owned by a ResourceOwner
923 ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
925 TupleDesc *tupdescs = owner->tupdescs;
926 int nt1 = owner->ntupdescs - 1;
929 for (i = nt1; i >= 0; i--)
931 if (tupdescs[i] == tupdesc)
935 tupdescs[i] = tupdescs[i + 1];
938 owner->ntupdescs = nt1;
942 elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
943 tupdesc, owner->name);
947 * Debugging subroutine
950 PrintTupleDescLeakWarning(TupleDesc tupdesc)
953 "TupleDesc reference leak: TupleDesc %p (%u,%d) still referenced",
954 tupdesc, tupdesc->tdtypeid, tupdesc->tdtypmod);
958 * Make sure there is room for at least one more entry in a ResourceOwner's
959 * snapshot reference array.
961 * This is separate from actually inserting an entry because if we run out
962 * of memory, it's critical to do so *before* acquiring the resource.
965 ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
969 if (owner->nsnapshots < owner->maxsnapshots)
970 return; /* nothing to do */
972 if (owner->snapshots == NULL)
975 owner->snapshots = (Snapshot *)
976 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Snapshot));
977 owner->maxsnapshots = newmax;
981 newmax = owner->maxsnapshots * 2;
982 owner->snapshots = (Snapshot *)
983 repalloc(owner->snapshots, newmax * sizeof(Snapshot));
984 owner->maxsnapshots = newmax;
989 * Remember that a snapshot reference is owned by a ResourceOwner
991 * Caller must have previously done ResourceOwnerEnlargeSnapshots()
994 ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
996 Assert(owner->nsnapshots < owner->maxsnapshots);
997 owner->snapshots[owner->nsnapshots] = snapshot;
1002 * Forget that a snapshot reference is owned by a ResourceOwner
1005 ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snapshot)
1007 Snapshot *snapshots = owner->snapshots;
1008 int ns1 = owner->nsnapshots - 1;
1011 for (i = ns1; i >= 0; i--)
1013 if (snapshots[i] == snapshot)
1017 snapshots[i] = snapshots[i + 1];
1020 owner->nsnapshots = ns1;
1024 elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
1025 snapshot, owner->name);
1029 * Debugging subroutine
1032 PrintSnapshotLeakWarning(Snapshot snapshot)
1035 "Snapshot reference leak: Snapshot %p still referenced",