Add support for user-defined I/O conversion casts.
[PostgreSQL.git] / src / backend / utils / resowner / resowner.c
blob7ea4f160bd2696bf7258ab8401ec582a45fd11f2
1 /*-------------------------------------------------------------------------
3 * resowner.c
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
16 * IDENTIFICATION
17 * $PostgreSQL$
19 *-------------------------------------------------------------------------
21 #include "postgres.h"
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 */
69 } ResourceOwnerData;
72 /*****************************************************************************
73 * GLOBAL MEMORY *
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;
87 void *arg;
88 } ResourceReleaseCallbackItem;
90 static ResourceReleaseCallbackItem *ResourceRelease_callbacks = NULL;
93 /* Internal routines */
94 static void ResourceOwnerReleaseInternal(ResourceOwner owner,
95 ResourceReleasePhase phase,
96 bool isCommit,
97 bool isTopLevel);
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.
115 ResourceOwner
116 ResourceOwnerCreate(ResourceOwner parent, const char *name)
118 ResourceOwner owner;
120 owner = (ResourceOwner) MemoryContextAllocZero(TopMemoryContext,
121 sizeof(ResourceOwnerData));
122 owner->name = name;
124 if (parent)
126 owner->parent = parent;
127 owner->nextchild = parent->firstchild;
128 parent->firstchild = owner;
131 return 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.
160 void
161 ResourceOwnerRelease(ResourceOwner owner,
162 ResourceReleasePhase phase,
163 bool isCommit,
164 bool isTopLevel)
166 /* Rather than PG_TRY at every level of recursion, set it up once */
167 ResourceOwner save;
169 save = CurrentResourceOwner;
170 PG_TRY();
172 ResourceOwnerReleaseInternal(owner, phase, isCommit, isTopLevel);
174 PG_CATCH();
176 CurrentResourceOwner = save;
177 PG_RE_THROW();
179 PG_END_TRY();
180 CurrentResourceOwner = save;
183 static void
184 ResourceOwnerReleaseInternal(ResourceOwner owner,
185 ResourceReleasePhase phase,
186 bool isCommit,
187 bool isTopLevel)
189 ResourceOwner child;
190 ResourceOwner save;
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
210 * none.
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)
221 if (isCommit)
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
229 * are none.
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)
236 if (isCommit)
237 PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]);
238 RelationClose(owner->relrefs[owner->nrelrefs - 1]);
241 else if (phase == RESOURCE_RELEASE_LOCKS)
243 if (isTopLevel)
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);
253 else
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);
261 if (isCommit)
262 LockReassignCurrentOwner();
263 else
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
272 * are none.
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)
279 if (isCommit)
280 PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
281 ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
283 /* Ditto for catcache lists */
284 while (owner->ncatlistrefs > 0)
286 if (isCommit)
287 PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
288 ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
290 /* Ditto for plancache references */
291 while (owner->nplanrefs > 0)
293 if (isCommit)
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)
300 if (isCommit)
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.
322 void
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. */
351 if (owner->buffers)
352 pfree(owner->buffers);
353 if (owner->catrefs)
354 pfree(owner->catrefs);
355 if (owner->catlistrefs)
356 pfree(owner->catlistrefs);
357 if (owner->relrefs)
358 pfree(owner->relrefs);
359 if (owner->planrefs)
360 pfree(owner->planrefs);
361 if (owner->tupdescs)
362 pfree(owner->tupdescs);
364 pfree(owner);
368 * Fetch parent of a ResourceOwner (returns NULL if top-level owner)
370 ResourceOwner
371 ResourceOwnerGetParent(ResourceOwner owner)
373 return owner->parent;
377 * Reassign a ResourceOwner to have a new parent
379 void
380 ResourceOwnerNewParent(ResourceOwner owner,
381 ResourceOwner newparent)
383 ResourceOwner oldparent = owner->parent;
385 if (oldparent)
387 if (owner == oldparent->firstchild)
388 oldparent->firstchild = owner->nextchild;
389 else
391 ResourceOwner child;
393 for (child = oldparent->firstchild; child; child = child->nextchild)
395 if (owner == child->nextchild)
397 child->nextchild = owner->nextchild;
398 break;
404 if (newparent)
406 Assert(owner != newparent);
407 owner->parent = newparent;
408 owner->nextchild = newparent->firstchild;
409 newparent->firstchild = owner;
411 else
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.
427 void
428 RegisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
430 ResourceReleaseCallbackItem *item;
432 item = (ResourceReleaseCallbackItem *)
433 MemoryContextAlloc(TopMemoryContext,
434 sizeof(ResourceReleaseCallbackItem));
435 item->callback = callback;
436 item->arg = arg;
437 item->next = ResourceRelease_callbacks;
438 ResourceRelease_callbacks = item;
441 void
442 UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
444 ResourceReleaseCallbackItem *item;
445 ResourceReleaseCallbackItem *prev;
447 prev = NULL;
448 for (item = ResourceRelease_callbacks; item; prev = item, item = item->next)
450 if (item->callback == callback && item->arg == arg)
452 if (prev)
453 prev->next = item->next;
454 else
455 ResourceRelease_callbacks = item->next;
456 pfree(item);
457 break;
464 * Make sure there is room for at least one more entry in a ResourceOwner's
465 * buffer array.
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).
473 void
474 ResourceOwnerEnlargeBuffers(ResourceOwner owner)
476 int newmax;
478 if (owner == NULL ||
479 owner->nbuffers < owner->maxbuffers)
480 return; /* nothing to do */
482 if (owner->buffers == NULL)
484 newmax = 16;
485 owner->buffers = (Buffer *)
486 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Buffer));
487 owner->maxbuffers = newmax;
489 else
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).
506 void
507 ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
509 if (owner != NULL)
511 Assert(owner->nbuffers < owner->maxbuffers);
512 owner->buffers[owner->nbuffers] = buffer;
513 owner->nbuffers++;
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).
523 void
524 ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
526 if (owner != NULL)
528 Buffer *buffers = owner->buffers;
529 int nb1 = owner->nbuffers - 1;
530 int i;
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)
541 while (i < nb1)
543 buffers[i] = buffers[i + 1];
544 i++;
546 owner->nbuffers = nb1;
547 return;
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.
562 void
563 ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
565 int newmax;
567 if (owner->ncatrefs < owner->maxcatrefs)
568 return; /* nothing to do */
570 if (owner->catrefs == NULL)
572 newmax = 16;
573 owner->catrefs = (HeapTuple *)
574 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(HeapTuple));
575 owner->maxcatrefs = newmax;
577 else
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()
591 void
592 ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
594 Assert(owner->ncatrefs < owner->maxcatrefs);
595 owner->catrefs[owner->ncatrefs] = tuple;
596 owner->ncatrefs++;
600 * Forget that a catcache reference is owned by a ResourceOwner
602 void
603 ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
605 HeapTuple *catrefs = owner->catrefs;
606 int nc1 = owner->ncatrefs - 1;
607 int i;
609 for (i = nc1; i >= 0; i--)
611 if (catrefs[i] == tuple)
613 while (i < nc1)
615 catrefs[i] = catrefs[i + 1];
616 i++;
618 owner->ncatrefs = nc1;
619 return;
622 elog(ERROR, "catcache reference %p is not owned by resource owner %s",
623 tuple, owner->name);
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.
633 void
634 ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
636 int newmax;
638 if (owner->ncatlistrefs < owner->maxcatlistrefs)
639 return; /* nothing to do */
641 if (owner->catlistrefs == NULL)
643 newmax = 16;
644 owner->catlistrefs = (CatCList **)
645 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CatCList *));
646 owner->maxcatlistrefs = newmax;
648 else
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()
662 void
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
673 void
674 ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
676 CatCList **catlistrefs = owner->catlistrefs;
677 int nc1 = owner->ncatlistrefs - 1;
678 int i;
680 for (i = nc1; i >= 0; i--)
682 if (catlistrefs[i] == list)
684 while (i < nc1)
686 catlistrefs[i] = catlistrefs[i + 1];
687 i++;
689 owner->ncatlistrefs = nc1;
690 return;
693 elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
694 list, owner->name);
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.
704 void
705 ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
707 int newmax;
709 if (owner->nrelrefs < owner->maxrelrefs)
710 return; /* nothing to do */
712 if (owner->relrefs == NULL)
714 newmax = 16;
715 owner->relrefs = (Relation *)
716 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Relation));
717 owner->maxrelrefs = newmax;
719 else
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()
733 void
734 ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
736 Assert(owner->nrelrefs < owner->maxrelrefs);
737 owner->relrefs[owner->nrelrefs] = rel;
738 owner->nrelrefs++;
742 * Forget that a relcache reference is owned by a ResourceOwner
744 void
745 ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel)
747 Relation *relrefs = owner->relrefs;
748 int nr1 = owner->nrelrefs - 1;
749 int i;
751 for (i = nr1; i >= 0; i--)
753 if (relrefs[i] == rel)
755 while (i < nr1)
757 relrefs[i] = relrefs[i + 1];
758 i++;
760 owner->nrelrefs = nr1;
761 return;
764 elog(ERROR, "relcache reference %s is not owned by resource owner %s",
765 RelationGetRelationName(rel), owner->name);
769 * Debugging subroutine
771 static void
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.
785 void
786 ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
788 int newmax;
790 if (owner->nplanrefs < owner->maxplanrefs)
791 return; /* nothing to do */
793 if (owner->planrefs == NULL)
795 newmax = 16;
796 owner->planrefs = (CachedPlan **)
797 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CachedPlan *));
798 owner->maxplanrefs = newmax;
800 else
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()
814 void
815 ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
817 Assert(owner->nplanrefs < owner->maxplanrefs);
818 owner->planrefs[owner->nplanrefs] = plan;
819 owner->nplanrefs++;
823 * Forget that a plancache reference is owned by a ResourceOwner
825 void
826 ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
828 CachedPlan **planrefs = owner->planrefs;
829 int np1 = owner->nplanrefs - 1;
830 int i;
832 for (i = np1; i >= 0; i--)
834 if (planrefs[i] == plan)
836 while (i < np1)
838 planrefs[i] = planrefs[i + 1];
839 i++;
841 owner->nplanrefs = np1;
842 return;
845 elog(ERROR, "plancache reference %p is not owned by resource owner %s",
846 plan, owner->name);
850 * Debugging subroutine
852 static void
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.
865 void
866 ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
868 int newmax;
870 if (owner->ntupdescs < owner->maxtupdescs)
871 return; /* nothing to do */
873 if (owner->tupdescs == NULL)
875 newmax = 16;
876 owner->tupdescs = (TupleDesc *)
877 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(TupleDesc));
878 owner->maxtupdescs = newmax;
880 else
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()
894 void
895 ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
897 Assert(owner->ntupdescs < owner->maxtupdescs);
898 owner->tupdescs[owner->ntupdescs] = tupdesc;
899 owner->ntupdescs++;
903 * Forget that a tupdesc reference is owned by a ResourceOwner
905 void
906 ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
908 TupleDesc *tupdescs = owner->tupdescs;
909 int nt1 = owner->ntupdescs - 1;
910 int i;
912 for (i = nt1; i >= 0; i--)
914 if (tupdescs[i] == tupdesc)
916 while (i < nt1)
918 tupdescs[i] = tupdescs[i + 1];
919 i++;
921 owner->ntupdescs = nt1;
922 return;
925 elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
926 tupdesc, owner->name);
930 * Debugging subroutine
932 static void
933 PrintTupleDescLeakWarning(TupleDesc tupdesc)
935 elog(WARNING,
936 "TupleDesc reference leak: TupleDesc %p (%u,%d) still referenced",
937 tupdesc, tupdesc->tdtypeid, tupdesc->tdtypmod);