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-2008, PostgreSQL Global Development Group
13 * Portions Copyright (c) 1994, Regents of the University of California
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"
32 * ResourceOwner objects look like this
34 typedef struct ResourceOwnerData
36 ResourceOwner parent
; /* NULL if no parent (toplevel owner) */
37 ResourceOwner firstchild
; /* head of linked list of children */
38 ResourceOwner nextchild
; /* next child of same parent */
39 const char *name
; /* name (just for debugging) */
41 /* We have built-in support for remembering owned buffers */
42 int nbuffers
; /* number of owned buffer pins */
43 Buffer
*buffers
; /* dynamically allocated array */
44 int maxbuffers
; /* currently allocated array size */
46 /* We have built-in support for remembering catcache references */
47 int ncatrefs
; /* number of owned catcache pins */
48 HeapTuple
*catrefs
; /* dynamically allocated array */
49 int maxcatrefs
; /* currently allocated array size */
51 int ncatlistrefs
; /* number of owned catcache-list pins */
52 CatCList
**catlistrefs
; /* dynamically allocated array */
53 int maxcatlistrefs
; /* currently allocated array size */
55 /* We have built-in support for remembering relcache references */
56 int nrelrefs
; /* number of owned relcache pins */
57 Relation
*relrefs
; /* dynamically allocated array */
58 int maxrelrefs
; /* currently allocated array size */
60 /* We have built-in support for remembering plancache references */
61 int nplanrefs
; /* number of owned plancache pins */
62 CachedPlan
**planrefs
; /* dynamically allocated array */
63 int maxplanrefs
; /* currently allocated array size */
65 /* We have built-in support for remembering tupdesc references */
66 int ntupdescs
; /* number of owned tupdesc references */
67 TupleDesc
*tupdescs
; /* dynamically allocated array */
68 int maxtupdescs
; /* currently allocated array size */
72 /*****************************************************************************
74 *****************************************************************************/
76 ResourceOwner CurrentResourceOwner
= NULL
;
77 ResourceOwner CurTransactionResourceOwner
= NULL
;
78 ResourceOwner TopTransactionResourceOwner
= NULL
;
81 * List of add-on callbacks for resource releasing
83 typedef struct ResourceReleaseCallbackItem
85 struct ResourceReleaseCallbackItem
*next
;
86 ResourceReleaseCallback callback
;
88 } ResourceReleaseCallbackItem
;
90 static ResourceReleaseCallbackItem
*ResourceRelease_callbacks
= NULL
;
93 /* Internal routines */
94 static void ResourceOwnerReleaseInternal(ResourceOwner owner
,
95 ResourceReleasePhase phase
,
98 static void PrintRelCacheLeakWarning(Relation rel
);
99 static void PrintPlanCacheLeakWarning(CachedPlan
*plan
);
100 static void PrintTupleDescLeakWarning(TupleDesc tupdesc
);
103 /*****************************************************************************
104 * EXPORTED ROUTINES *
105 *****************************************************************************/
109 * ResourceOwnerCreate
110 * Create an empty ResourceOwner.
112 * All ResourceOwner objects are kept in TopMemoryContext, since they should
113 * only be freed explicitly.
116 ResourceOwnerCreate(ResourceOwner parent
, const char *name
)
120 owner
= (ResourceOwner
) MemoryContextAllocZero(TopMemoryContext
,
121 sizeof(ResourceOwnerData
));
126 owner
->parent
= parent
;
127 owner
->nextchild
= parent
->firstchild
;
128 parent
->firstchild
= owner
;
135 * ResourceOwnerRelease
136 * Release all resources owned by a ResourceOwner and its descendants,
137 * but don't delete the owner objects themselves.
139 * Note that this executes just one phase of release, and so typically
140 * must be called three times. We do it this way because (a) we want to
141 * do all the recursion separately for each phase, thereby preserving
142 * the needed order of operations; and (b) xact.c may have other operations
143 * to do between the phases.
145 * phase: release phase to execute
146 * isCommit: true for successful completion of a query or transaction,
147 * false for unsuccessful
148 * isTopLevel: true if completing a main transaction, else false
150 * isCommit is passed because some modules may expect that their resources
151 * were all released already if the transaction or portal finished normally.
152 * If so it is reasonable to give a warning (NOT an error) should any
153 * unreleased resources be present. When isCommit is false, such warnings
154 * are generally inappropriate.
156 * isTopLevel is passed when we are releasing TopTransactionResourceOwner
157 * at completion of a main transaction. This generally means that *all*
158 * resources will be released, and so we can optimize things a bit.
161 ResourceOwnerRelease(ResourceOwner owner
,
162 ResourceReleasePhase phase
,
166 /* Rather than PG_TRY at every level of recursion, set it up once */
169 save
= CurrentResourceOwner
;
172 ResourceOwnerReleaseInternal(owner
, phase
, isCommit
, isTopLevel
);
176 CurrentResourceOwner
= save
;
180 CurrentResourceOwner
= save
;
184 ResourceOwnerReleaseInternal(ResourceOwner owner
,
185 ResourceReleasePhase phase
,
191 ResourceReleaseCallbackItem
*item
;
193 /* Recurse to handle descendants */
194 for (child
= owner
->firstchild
; child
!= NULL
; child
= child
->nextchild
)
195 ResourceOwnerReleaseInternal(child
, phase
, isCommit
, isTopLevel
);
198 * Make CurrentResourceOwner point to me, so that ReleaseBuffer etc don't
199 * get confused. We needn't PG_TRY here because the outermost level will
200 * fix it on error abort.
202 save
= CurrentResourceOwner
;
203 CurrentResourceOwner
= owner
;
205 if (phase
== RESOURCE_RELEASE_BEFORE_LOCKS
)
208 * Release buffer pins. Note that ReleaseBuffer will remove the
209 * buffer entry from my list, so I just have to iterate till there are
212 * During a commit, there shouldn't be any remaining pins --- that
213 * would indicate failure to clean up the executor correctly --- so
214 * issue warnings. In the abort case, just clean up quietly.
216 * We are careful to do the releasing back-to-front, so as to avoid
217 * O(N^2) behavior in ResourceOwnerForgetBuffer().
219 while (owner
->nbuffers
> 0)
222 PrintBufferLeakWarning(owner
->buffers
[owner
->nbuffers
- 1]);
223 ReleaseBuffer(owner
->buffers
[owner
->nbuffers
- 1]);
227 * Release relcache references. Note that RelationClose will remove
228 * the relref entry from my list, so I just have to iterate till there
231 * As with buffer pins, warn if any are left at commit time, and
232 * release back-to-front for speed.
234 while (owner
->nrelrefs
> 0)
237 PrintRelCacheLeakWarning(owner
->relrefs
[owner
->nrelrefs
- 1]);
238 RelationClose(owner
->relrefs
[owner
->nrelrefs
- 1]);
241 else if (phase
== RESOURCE_RELEASE_LOCKS
)
246 * For a top-level xact we are going to release all locks (or at
247 * least all non-session locks), so just do a single lmgr call at
248 * the top of the recursion.
250 if (owner
== TopTransactionResourceOwner
)
251 ProcReleaseLocks(isCommit
);
256 * Release locks retail. Note that if we are committing a
257 * subtransaction, we do NOT release its locks yet, but transfer
258 * them to the parent.
260 Assert(owner
->parent
!= NULL
);
262 LockReassignCurrentOwner();
264 LockReleaseCurrentOwner();
267 else if (phase
== RESOURCE_RELEASE_AFTER_LOCKS
)
270 * Release catcache references. Note that ReleaseCatCache will remove
271 * the catref entry from my list, so I just have to iterate till there
274 * As with buffer pins, warn if any are left at commit time, and
275 * release back-to-front for speed.
277 while (owner
->ncatrefs
> 0)
280 PrintCatCacheLeakWarning(owner
->catrefs
[owner
->ncatrefs
- 1]);
281 ReleaseCatCache(owner
->catrefs
[owner
->ncatrefs
- 1]);
283 /* Ditto for catcache lists */
284 while (owner
->ncatlistrefs
> 0)
287 PrintCatCacheListLeakWarning(owner
->catlistrefs
[owner
->ncatlistrefs
- 1]);
288 ReleaseCatCacheList(owner
->catlistrefs
[owner
->ncatlistrefs
- 1]);
290 /* Ditto for plancache references */
291 while (owner
->nplanrefs
> 0)
294 PrintPlanCacheLeakWarning(owner
->planrefs
[owner
->nplanrefs
- 1]);
295 ReleaseCachedPlan(owner
->planrefs
[owner
->nplanrefs
- 1], true);
297 /* Ditto for tupdesc references */
298 while (owner
->ntupdescs
> 0)
301 PrintTupleDescLeakWarning(owner
->tupdescs
[owner
->ntupdescs
- 1]);
302 DecrTupleDescRefCount(owner
->tupdescs
[owner
->ntupdescs
- 1]);
305 /* Clean up index scans too */
306 ReleaseResources_hash();
309 /* Let add-on modules get a chance too */
310 for (item
= ResourceRelease_callbacks
; item
; item
= item
->next
)
311 (*item
->callback
) (phase
, isCommit
, isTopLevel
, item
->arg
);
313 CurrentResourceOwner
= save
;
317 * ResourceOwnerDelete
318 * Delete an owner object and its descendants.
320 * The caller must have already released all resources in the object tree.
323 ResourceOwnerDelete(ResourceOwner owner
)
325 /* We had better not be deleting CurrentResourceOwner ... */
326 Assert(owner
!= CurrentResourceOwner
);
328 /* And it better not own any resources, either */
329 Assert(owner
->nbuffers
== 0);
330 Assert(owner
->ncatrefs
== 0);
331 Assert(owner
->ncatlistrefs
== 0);
332 Assert(owner
->nrelrefs
== 0);
333 Assert(owner
->nplanrefs
== 0);
334 Assert(owner
->ntupdescs
== 0);
337 * Delete children. The recursive call will delink the child from me, so
338 * just iterate as long as there is a child.
340 while (owner
->firstchild
!= NULL
)
341 ResourceOwnerDelete(owner
->firstchild
);
344 * We delink the owner from its parent before deleting it, so that if
345 * there's an error we won't have deleted/busted owners still attached to
346 * the owner tree. Better a leak than a crash.
348 ResourceOwnerNewParent(owner
, NULL
);
350 /* And free the object. */
352 pfree(owner
->buffers
);
354 pfree(owner
->catrefs
);
355 if (owner
->catlistrefs
)
356 pfree(owner
->catlistrefs
);
358 pfree(owner
->relrefs
);
360 pfree(owner
->planrefs
);
362 pfree(owner
->tupdescs
);
368 * Fetch parent of a ResourceOwner (returns NULL if top-level owner)
371 ResourceOwnerGetParent(ResourceOwner owner
)
373 return owner
->parent
;
377 * Reassign a ResourceOwner to have a new parent
380 ResourceOwnerNewParent(ResourceOwner owner
,
381 ResourceOwner newparent
)
383 ResourceOwner oldparent
= owner
->parent
;
387 if (owner
== oldparent
->firstchild
)
388 oldparent
->firstchild
= owner
->nextchild
;
393 for (child
= oldparent
->firstchild
; child
; child
= child
->nextchild
)
395 if (owner
== child
->nextchild
)
397 child
->nextchild
= owner
->nextchild
;
406 Assert(owner
!= newparent
);
407 owner
->parent
= newparent
;
408 owner
->nextchild
= newparent
->firstchild
;
409 newparent
->firstchild
= owner
;
413 owner
->parent
= NULL
;
414 owner
->nextchild
= NULL
;
419 * Register or deregister callback functions for resource cleanup
421 * These functions are intended for use by dynamically loaded modules.
422 * For built-in modules we generally just hardwire the appropriate calls.
424 * Note that the callback occurs post-commit or post-abort, so the callback
425 * functions can only do noncritical cleanup.
428 RegisterResourceReleaseCallback(ResourceReleaseCallback callback
, void *arg
)
430 ResourceReleaseCallbackItem
*item
;
432 item
= (ResourceReleaseCallbackItem
*)
433 MemoryContextAlloc(TopMemoryContext
,
434 sizeof(ResourceReleaseCallbackItem
));
435 item
->callback
= callback
;
437 item
->next
= ResourceRelease_callbacks
;
438 ResourceRelease_callbacks
= item
;
442 UnregisterResourceReleaseCallback(ResourceReleaseCallback callback
, void *arg
)
444 ResourceReleaseCallbackItem
*item
;
445 ResourceReleaseCallbackItem
*prev
;
448 for (item
= ResourceRelease_callbacks
; item
; prev
= item
, item
= item
->next
)
450 if (item
->callback
== callback
&& item
->arg
== arg
)
453 prev
->next
= item
->next
;
455 ResourceRelease_callbacks
= item
->next
;
464 * Make sure there is room for at least one more entry in a ResourceOwner's
467 * This is separate from actually inserting an entry because if we run out
468 * of memory, it's critical to do so *before* acquiring the resource.
470 * We allow the case owner == NULL because the bufmgr is sometimes invoked
471 * outside any transaction (for example, during WAL recovery).
474 ResourceOwnerEnlargeBuffers(ResourceOwner owner
)
479 owner
->nbuffers
< owner
->maxbuffers
)
480 return; /* nothing to do */
482 if (owner
->buffers
== NULL
)
485 owner
->buffers
= (Buffer
*)
486 MemoryContextAlloc(TopMemoryContext
, newmax
* sizeof(Buffer
));
487 owner
->maxbuffers
= newmax
;
491 newmax
= owner
->maxbuffers
* 2;
492 owner
->buffers
= (Buffer
*)
493 repalloc(owner
->buffers
, newmax
* sizeof(Buffer
));
494 owner
->maxbuffers
= newmax
;
499 * Remember that a buffer pin is owned by a ResourceOwner
501 * Caller must have previously done ResourceOwnerEnlargeBuffers()
503 * We allow the case owner == NULL because the bufmgr is sometimes invoked
504 * outside any transaction (for example, during WAL recovery).
507 ResourceOwnerRememberBuffer(ResourceOwner owner
, Buffer buffer
)
511 Assert(owner
->nbuffers
< owner
->maxbuffers
);
512 owner
->buffers
[owner
->nbuffers
] = buffer
;
518 * Forget that a buffer pin is owned by a ResourceOwner
520 * We allow the case owner == NULL because the bufmgr is sometimes invoked
521 * outside any transaction (for example, during WAL recovery).
524 ResourceOwnerForgetBuffer(ResourceOwner owner
, Buffer buffer
)
528 Buffer
*buffers
= owner
->buffers
;
529 int nb1
= owner
->nbuffers
- 1;
533 * Scan back-to-front because it's more likely we are releasing a
534 * recently pinned buffer. This isn't always the case of course, but
535 * it's the way to bet.
537 for (i
= nb1
; i
>= 0; i
--)
539 if (buffers
[i
] == buffer
)
543 buffers
[i
] = buffers
[i
+ 1];
546 owner
->nbuffers
= nb1
;
550 elog(ERROR
, "buffer %d is not owned by resource owner %s",
551 buffer
, owner
->name
);
556 * Make sure there is room for at least one more entry in a ResourceOwner's
557 * catcache reference array.
559 * This is separate from actually inserting an entry because if we run out
560 * of memory, it's critical to do so *before* acquiring the resource.
563 ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner
)
567 if (owner
->ncatrefs
< owner
->maxcatrefs
)
568 return; /* nothing to do */
570 if (owner
->catrefs
== NULL
)
573 owner
->catrefs
= (HeapTuple
*)
574 MemoryContextAlloc(TopMemoryContext
, newmax
* sizeof(HeapTuple
));
575 owner
->maxcatrefs
= newmax
;
579 newmax
= owner
->maxcatrefs
* 2;
580 owner
->catrefs
= (HeapTuple
*)
581 repalloc(owner
->catrefs
, newmax
* sizeof(HeapTuple
));
582 owner
->maxcatrefs
= newmax
;
587 * Remember that a catcache reference is owned by a ResourceOwner
589 * Caller must have previously done ResourceOwnerEnlargeCatCacheRefs()
592 ResourceOwnerRememberCatCacheRef(ResourceOwner owner
, HeapTuple tuple
)
594 Assert(owner
->ncatrefs
< owner
->maxcatrefs
);
595 owner
->catrefs
[owner
->ncatrefs
] = tuple
;
600 * Forget that a catcache reference is owned by a ResourceOwner
603 ResourceOwnerForgetCatCacheRef(ResourceOwner owner
, HeapTuple tuple
)
605 HeapTuple
*catrefs
= owner
->catrefs
;
606 int nc1
= owner
->ncatrefs
- 1;
609 for (i
= nc1
; i
>= 0; i
--)
611 if (catrefs
[i
] == tuple
)
615 catrefs
[i
] = catrefs
[i
+ 1];
618 owner
->ncatrefs
= nc1
;
622 elog(ERROR
, "catcache reference %p is not owned by resource owner %s",
627 * Make sure there is room for at least one more entry in a ResourceOwner's
628 * catcache-list reference array.
630 * This is separate from actually inserting an entry because if we run out
631 * of memory, it's critical to do so *before* acquiring the resource.
634 ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner
)
638 if (owner
->ncatlistrefs
< owner
->maxcatlistrefs
)
639 return; /* nothing to do */
641 if (owner
->catlistrefs
== NULL
)
644 owner
->catlistrefs
= (CatCList
**)
645 MemoryContextAlloc(TopMemoryContext
, newmax
* sizeof(CatCList
*));
646 owner
->maxcatlistrefs
= newmax
;
650 newmax
= owner
->maxcatlistrefs
* 2;
651 owner
->catlistrefs
= (CatCList
**)
652 repalloc(owner
->catlistrefs
, newmax
* sizeof(CatCList
*));
653 owner
->maxcatlistrefs
= newmax
;
658 * Remember that a catcache-list reference is owned by a ResourceOwner
660 * Caller must have previously done ResourceOwnerEnlargeCatCacheListRefs()
663 ResourceOwnerRememberCatCacheListRef(ResourceOwner owner
, CatCList
*list
)
665 Assert(owner
->ncatlistrefs
< owner
->maxcatlistrefs
);
666 owner
->catlistrefs
[owner
->ncatlistrefs
] = list
;
667 owner
->ncatlistrefs
++;
671 * Forget that a catcache-list reference is owned by a ResourceOwner
674 ResourceOwnerForgetCatCacheListRef(ResourceOwner owner
, CatCList
*list
)
676 CatCList
**catlistrefs
= owner
->catlistrefs
;
677 int nc1
= owner
->ncatlistrefs
- 1;
680 for (i
= nc1
; i
>= 0; i
--)
682 if (catlistrefs
[i
] == list
)
686 catlistrefs
[i
] = catlistrefs
[i
+ 1];
689 owner
->ncatlistrefs
= nc1
;
693 elog(ERROR
, "catcache list reference %p is not owned by resource owner %s",
698 * Make sure there is room for at least one more entry in a ResourceOwner's
699 * relcache reference array.
701 * This is separate from actually inserting an entry because if we run out
702 * of memory, it's critical to do so *before* acquiring the resource.
705 ResourceOwnerEnlargeRelationRefs(ResourceOwner owner
)
709 if (owner
->nrelrefs
< owner
->maxrelrefs
)
710 return; /* nothing to do */
712 if (owner
->relrefs
== NULL
)
715 owner
->relrefs
= (Relation
*)
716 MemoryContextAlloc(TopMemoryContext
, newmax
* sizeof(Relation
));
717 owner
->maxrelrefs
= newmax
;
721 newmax
= owner
->maxrelrefs
* 2;
722 owner
->relrefs
= (Relation
*)
723 repalloc(owner
->relrefs
, newmax
* sizeof(Relation
));
724 owner
->maxrelrefs
= newmax
;
729 * Remember that a relcache reference is owned by a ResourceOwner
731 * Caller must have previously done ResourceOwnerEnlargeRelationRefs()
734 ResourceOwnerRememberRelationRef(ResourceOwner owner
, Relation rel
)
736 Assert(owner
->nrelrefs
< owner
->maxrelrefs
);
737 owner
->relrefs
[owner
->nrelrefs
] = rel
;
742 * Forget that a relcache reference is owned by a ResourceOwner
745 ResourceOwnerForgetRelationRef(ResourceOwner owner
, Relation rel
)
747 Relation
*relrefs
= owner
->relrefs
;
748 int nr1
= owner
->nrelrefs
- 1;
751 for (i
= nr1
; i
>= 0; i
--)
753 if (relrefs
[i
] == rel
)
757 relrefs
[i
] = relrefs
[i
+ 1];
760 owner
->nrelrefs
= nr1
;
764 elog(ERROR
, "relcache reference %s is not owned by resource owner %s",
765 RelationGetRelationName(rel
), owner
->name
);
769 * Debugging subroutine
772 PrintRelCacheLeakWarning(Relation rel
)
774 elog(WARNING
, "relcache reference leak: relation \"%s\" not closed",
775 RelationGetRelationName(rel
));
779 * Make sure there is room for at least one more entry in a ResourceOwner's
780 * plancache reference array.
782 * This is separate from actually inserting an entry because if we run out
783 * of memory, it's critical to do so *before* acquiring the resource.
786 ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner
)
790 if (owner
->nplanrefs
< owner
->maxplanrefs
)
791 return; /* nothing to do */
793 if (owner
->planrefs
== NULL
)
796 owner
->planrefs
= (CachedPlan
**)
797 MemoryContextAlloc(TopMemoryContext
, newmax
* sizeof(CachedPlan
*));
798 owner
->maxplanrefs
= newmax
;
802 newmax
= owner
->maxplanrefs
* 2;
803 owner
->planrefs
= (CachedPlan
**)
804 repalloc(owner
->planrefs
, newmax
* sizeof(CachedPlan
*));
805 owner
->maxplanrefs
= newmax
;
810 * Remember that a plancache reference is owned by a ResourceOwner
812 * Caller must have previously done ResourceOwnerEnlargePlanCacheRefs()
815 ResourceOwnerRememberPlanCacheRef(ResourceOwner owner
, CachedPlan
*plan
)
817 Assert(owner
->nplanrefs
< owner
->maxplanrefs
);
818 owner
->planrefs
[owner
->nplanrefs
] = plan
;
823 * Forget that a plancache reference is owned by a ResourceOwner
826 ResourceOwnerForgetPlanCacheRef(ResourceOwner owner
, CachedPlan
*plan
)
828 CachedPlan
**planrefs
= owner
->planrefs
;
829 int np1
= owner
->nplanrefs
- 1;
832 for (i
= np1
; i
>= 0; i
--)
834 if (planrefs
[i
] == plan
)
838 planrefs
[i
] = planrefs
[i
+ 1];
841 owner
->nplanrefs
= np1
;
845 elog(ERROR
, "plancache reference %p is not owned by resource owner %s",
850 * Debugging subroutine
853 PrintPlanCacheLeakWarning(CachedPlan
*plan
)
855 elog(WARNING
, "plancache reference leak: plan %p not closed", plan
);
859 * Make sure there is room for at least one more entry in a ResourceOwner's
860 * tupdesc reference array.
862 * This is separate from actually inserting an entry because if we run out
863 * of memory, it's critical to do so *before* acquiring the resource.
866 ResourceOwnerEnlargeTupleDescs(ResourceOwner owner
)
870 if (owner
->ntupdescs
< owner
->maxtupdescs
)
871 return; /* nothing to do */
873 if (owner
->tupdescs
== NULL
)
876 owner
->tupdescs
= (TupleDesc
*)
877 MemoryContextAlloc(TopMemoryContext
, newmax
* sizeof(TupleDesc
));
878 owner
->maxtupdescs
= newmax
;
882 newmax
= owner
->maxtupdescs
* 2;
883 owner
->tupdescs
= (TupleDesc
*)
884 repalloc(owner
->tupdescs
, newmax
* sizeof(TupleDesc
));
885 owner
->maxtupdescs
= newmax
;
890 * Remember that a tupdesc reference is owned by a ResourceOwner
892 * Caller must have previously done ResourceOwnerEnlargeTupleDescs()
895 ResourceOwnerRememberTupleDesc(ResourceOwner owner
, TupleDesc tupdesc
)
897 Assert(owner
->ntupdescs
< owner
->maxtupdescs
);
898 owner
->tupdescs
[owner
->ntupdescs
] = tupdesc
;
903 * Forget that a tupdesc reference is owned by a ResourceOwner
906 ResourceOwnerForgetTupleDesc(ResourceOwner owner
, TupleDesc tupdesc
)
908 TupleDesc
*tupdescs
= owner
->tupdescs
;
909 int nt1
= owner
->ntupdescs
- 1;
912 for (i
= nt1
; i
>= 0; i
--)
914 if (tupdescs
[i
] == tupdesc
)
918 tupdescs
[i
] = tupdescs
[i
+ 1];
921 owner
->ntupdescs
= nt1
;
925 elog(ERROR
, "tupdesc reference %p is not owned by resource owner %s",
926 tupdesc
, owner
->name
);
930 * Debugging subroutine
933 PrintTupleDescLeakWarning(TupleDesc tupdesc
)
936 "TupleDesc reference leak: TupleDesc %p (%u,%d) still referenced",
937 tupdesc
, tupdesc
->tdtypeid
, tupdesc
->tdtypmod
);