Fix xslt_process() to ensure that it inserts a NULL terminator after the
[PostgreSQL.git] / src / backend / utils / resowner / resowner.c
blobd6a57ee85af36365dd42c7eefa03ec74fee9cddb
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-2009, 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"
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 */
75 } ResourceOwnerData;
78 /*****************************************************************************
79 * GLOBAL MEMORY *
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;
93 void *arg;
94 } ResourceReleaseCallbackItem;
96 static ResourceReleaseCallbackItem *ResourceRelease_callbacks = NULL;
99 /* Internal routines */
100 static void ResourceOwnerReleaseInternal(ResourceOwner owner,
101 ResourceReleasePhase phase,
102 bool isCommit,
103 bool isTopLevel);
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.
122 ResourceOwner
123 ResourceOwnerCreate(ResourceOwner parent, const char *name)
125 ResourceOwner owner;
127 owner = (ResourceOwner) MemoryContextAllocZero(TopMemoryContext,
128 sizeof(ResourceOwnerData));
129 owner->name = name;
131 if (parent)
133 owner->parent = parent;
134 owner->nextchild = parent->firstchild;
135 parent->firstchild = owner;
138 return 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.
167 void
168 ResourceOwnerRelease(ResourceOwner owner,
169 ResourceReleasePhase phase,
170 bool isCommit,
171 bool isTopLevel)
173 /* Rather than PG_TRY at every level of recursion, set it up once */
174 ResourceOwner save;
176 save = CurrentResourceOwner;
177 PG_TRY();
179 ResourceOwnerReleaseInternal(owner, phase, isCommit, isTopLevel);
181 PG_CATCH();
183 CurrentResourceOwner = save;
184 PG_RE_THROW();
186 PG_END_TRY();
187 CurrentResourceOwner = save;
190 static void
191 ResourceOwnerReleaseInternal(ResourceOwner owner,
192 ResourceReleasePhase phase,
193 bool isCommit,
194 bool isTopLevel)
196 ResourceOwner child;
197 ResourceOwner save;
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
217 * none.
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)
228 if (isCommit)
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
236 * are none.
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)
243 if (isCommit)
244 PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]);
245 RelationClose(owner->relrefs[owner->nrelrefs - 1]);
248 else if (phase == RESOURCE_RELEASE_LOCKS)
250 if (isTopLevel)
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);
260 else
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);
268 if (isCommit)
269 LockReassignCurrentOwner();
270 else
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
279 * are none.
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)
286 if (isCommit)
287 PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
288 ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
290 /* Ditto for catcache lists */
291 while (owner->ncatlistrefs > 0)
293 if (isCommit)
294 PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
295 ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
297 /* Ditto for plancache references */
298 while (owner->nplanrefs > 0)
300 if (isCommit)
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)
307 if (isCommit)
308 PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]);
309 DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1]);
311 /* Ditto for snapshot references */
312 while (owner->nsnapshots > 0)
314 if (isCommit)
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.
336 void
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. */
366 if (owner->buffers)
367 pfree(owner->buffers);
368 if (owner->catrefs)
369 pfree(owner->catrefs);
370 if (owner->catlistrefs)
371 pfree(owner->catlistrefs);
372 if (owner->relrefs)
373 pfree(owner->relrefs);
374 if (owner->planrefs)
375 pfree(owner->planrefs);
376 if (owner->tupdescs)
377 pfree(owner->tupdescs);
378 if (owner->snapshots)
379 pfree(owner->snapshots);
381 pfree(owner);
385 * Fetch parent of a ResourceOwner (returns NULL if top-level owner)
387 ResourceOwner
388 ResourceOwnerGetParent(ResourceOwner owner)
390 return owner->parent;
394 * Reassign a ResourceOwner to have a new parent
396 void
397 ResourceOwnerNewParent(ResourceOwner owner,
398 ResourceOwner newparent)
400 ResourceOwner oldparent = owner->parent;
402 if (oldparent)
404 if (owner == oldparent->firstchild)
405 oldparent->firstchild = owner->nextchild;
406 else
408 ResourceOwner child;
410 for (child = oldparent->firstchild; child; child = child->nextchild)
412 if (owner == child->nextchild)
414 child->nextchild = owner->nextchild;
415 break;
421 if (newparent)
423 Assert(owner != newparent);
424 owner->parent = newparent;
425 owner->nextchild = newparent->firstchild;
426 newparent->firstchild = owner;
428 else
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.
444 void
445 RegisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
447 ResourceReleaseCallbackItem *item;
449 item = (ResourceReleaseCallbackItem *)
450 MemoryContextAlloc(TopMemoryContext,
451 sizeof(ResourceReleaseCallbackItem));
452 item->callback = callback;
453 item->arg = arg;
454 item->next = ResourceRelease_callbacks;
455 ResourceRelease_callbacks = item;
458 void
459 UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
461 ResourceReleaseCallbackItem *item;
462 ResourceReleaseCallbackItem *prev;
464 prev = NULL;
465 for (item = ResourceRelease_callbacks; item; prev = item, item = item->next)
467 if (item->callback == callback && item->arg == arg)
469 if (prev)
470 prev->next = item->next;
471 else
472 ResourceRelease_callbacks = item->next;
473 pfree(item);
474 break;
481 * Make sure there is room for at least one more entry in a ResourceOwner's
482 * buffer array.
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).
490 void
491 ResourceOwnerEnlargeBuffers(ResourceOwner owner)
493 int newmax;
495 if (owner == NULL ||
496 owner->nbuffers < owner->maxbuffers)
497 return; /* nothing to do */
499 if (owner->buffers == NULL)
501 newmax = 16;
502 owner->buffers = (Buffer *)
503 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Buffer));
504 owner->maxbuffers = newmax;
506 else
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).
523 void
524 ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
526 if (owner != NULL)
528 Assert(owner->nbuffers < owner->maxbuffers);
529 owner->buffers[owner->nbuffers] = buffer;
530 owner->nbuffers++;
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).
540 void
541 ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
543 if (owner != NULL)
545 Buffer *buffers = owner->buffers;
546 int nb1 = owner->nbuffers - 1;
547 int i;
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)
558 while (i < nb1)
560 buffers[i] = buffers[i + 1];
561 i++;
563 owner->nbuffers = nb1;
564 return;
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.
579 void
580 ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
582 int newmax;
584 if (owner->ncatrefs < owner->maxcatrefs)
585 return; /* nothing to do */
587 if (owner->catrefs == NULL)
589 newmax = 16;
590 owner->catrefs = (HeapTuple *)
591 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(HeapTuple));
592 owner->maxcatrefs = newmax;
594 else
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()
608 void
609 ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
611 Assert(owner->ncatrefs < owner->maxcatrefs);
612 owner->catrefs[owner->ncatrefs] = tuple;
613 owner->ncatrefs++;
617 * Forget that a catcache reference is owned by a ResourceOwner
619 void
620 ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
622 HeapTuple *catrefs = owner->catrefs;
623 int nc1 = owner->ncatrefs - 1;
624 int i;
626 for (i = nc1; i >= 0; i--)
628 if (catrefs[i] == tuple)
630 while (i < nc1)
632 catrefs[i] = catrefs[i + 1];
633 i++;
635 owner->ncatrefs = nc1;
636 return;
639 elog(ERROR, "catcache reference %p is not owned by resource owner %s",
640 tuple, owner->name);
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.
650 void
651 ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
653 int newmax;
655 if (owner->ncatlistrefs < owner->maxcatlistrefs)
656 return; /* nothing to do */
658 if (owner->catlistrefs == NULL)
660 newmax = 16;
661 owner->catlistrefs = (CatCList **)
662 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CatCList *));
663 owner->maxcatlistrefs = newmax;
665 else
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()
679 void
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
690 void
691 ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
693 CatCList **catlistrefs = owner->catlistrefs;
694 int nc1 = owner->ncatlistrefs - 1;
695 int i;
697 for (i = nc1; i >= 0; i--)
699 if (catlistrefs[i] == list)
701 while (i < nc1)
703 catlistrefs[i] = catlistrefs[i + 1];
704 i++;
706 owner->ncatlistrefs = nc1;
707 return;
710 elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
711 list, owner->name);
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.
721 void
722 ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
724 int newmax;
726 if (owner->nrelrefs < owner->maxrelrefs)
727 return; /* nothing to do */
729 if (owner->relrefs == NULL)
731 newmax = 16;
732 owner->relrefs = (Relation *)
733 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Relation));
734 owner->maxrelrefs = newmax;
736 else
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()
750 void
751 ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
753 Assert(owner->nrelrefs < owner->maxrelrefs);
754 owner->relrefs[owner->nrelrefs] = rel;
755 owner->nrelrefs++;
759 * Forget that a relcache reference is owned by a ResourceOwner
761 void
762 ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel)
764 Relation *relrefs = owner->relrefs;
765 int nr1 = owner->nrelrefs - 1;
766 int i;
768 for (i = nr1; i >= 0; i--)
770 if (relrefs[i] == rel)
772 while (i < nr1)
774 relrefs[i] = relrefs[i + 1];
775 i++;
777 owner->nrelrefs = nr1;
778 return;
781 elog(ERROR, "relcache reference %s is not owned by resource owner %s",
782 RelationGetRelationName(rel), owner->name);
786 * Debugging subroutine
788 static void
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.
802 void
803 ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
805 int newmax;
807 if (owner->nplanrefs < owner->maxplanrefs)
808 return; /* nothing to do */
810 if (owner->planrefs == NULL)
812 newmax = 16;
813 owner->planrefs = (CachedPlan **)
814 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CachedPlan *));
815 owner->maxplanrefs = newmax;
817 else
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()
831 void
832 ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
834 Assert(owner->nplanrefs < owner->maxplanrefs);
835 owner->planrefs[owner->nplanrefs] = plan;
836 owner->nplanrefs++;
840 * Forget that a plancache reference is owned by a ResourceOwner
842 void
843 ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
845 CachedPlan **planrefs = owner->planrefs;
846 int np1 = owner->nplanrefs - 1;
847 int i;
849 for (i = np1; i >= 0; i--)
851 if (planrefs[i] == plan)
853 while (i < np1)
855 planrefs[i] = planrefs[i + 1];
856 i++;
858 owner->nplanrefs = np1;
859 return;
862 elog(ERROR, "plancache reference %p is not owned by resource owner %s",
863 plan, owner->name);
867 * Debugging subroutine
869 static void
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.
882 void
883 ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
885 int newmax;
887 if (owner->ntupdescs < owner->maxtupdescs)
888 return; /* nothing to do */
890 if (owner->tupdescs == NULL)
892 newmax = 16;
893 owner->tupdescs = (TupleDesc *)
894 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(TupleDesc));
895 owner->maxtupdescs = newmax;
897 else
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()
911 void
912 ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
914 Assert(owner->ntupdescs < owner->maxtupdescs);
915 owner->tupdescs[owner->ntupdescs] = tupdesc;
916 owner->ntupdescs++;
920 * Forget that a tupdesc reference is owned by a ResourceOwner
922 void
923 ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
925 TupleDesc *tupdescs = owner->tupdescs;
926 int nt1 = owner->ntupdescs - 1;
927 int i;
929 for (i = nt1; i >= 0; i--)
931 if (tupdescs[i] == tupdesc)
933 while (i < nt1)
935 tupdescs[i] = tupdescs[i + 1];
936 i++;
938 owner->ntupdescs = nt1;
939 return;
942 elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
943 tupdesc, owner->name);
947 * Debugging subroutine
949 static void
950 PrintTupleDescLeakWarning(TupleDesc tupdesc)
952 elog(WARNING,
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.
964 void
965 ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
967 int newmax;
969 if (owner->nsnapshots < owner->maxsnapshots)
970 return; /* nothing to do */
972 if (owner->snapshots == NULL)
974 newmax = 16;
975 owner->snapshots = (Snapshot *)
976 MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Snapshot));
977 owner->maxsnapshots = newmax;
979 else
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()
993 void
994 ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
996 Assert(owner->nsnapshots < owner->maxsnapshots);
997 owner->snapshots[owner->nsnapshots] = snapshot;
998 owner->nsnapshots++;
1002 * Forget that a snapshot reference is owned by a ResourceOwner
1004 void
1005 ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snapshot)
1007 Snapshot *snapshots = owner->snapshots;
1008 int ns1 = owner->nsnapshots - 1;
1009 int i;
1011 for (i = ns1; i >= 0; i--)
1013 if (snapshots[i] == snapshot)
1015 while (i < ns1)
1017 snapshots[i] = snapshots[i + 1];
1018 i++;
1020 owner->nsnapshots = ns1;
1021 return;
1024 elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
1025 snapshot, owner->name);
1029 * Debugging subroutine
1031 static void
1032 PrintSnapshotLeakWarning(Snapshot snapshot)
1034 elog(WARNING,
1035 "Snapshot reference leak: Snapshot %p still referenced",
1036 snapshot);