]> granicus.if.org Git - postgresql/blob - src/backend/utils/resowner/resowner.c
pgindent run for 8.3.
[postgresql] / src / backend / utils / resowner / resowner.c
1 /*-------------------------------------------------------------------------
2  *
3  * resowner.c
4  *        POSTGRES resource owner management code.
5  *
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.
10  *
11  *
12  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
13  * Portions Copyright (c) 1994, Regents of the University of California
14  *
15  *
16  * IDENTIFICATION
17  *        $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.25 2007/11/15 21:14:41 momjian Exp $
18  *
19  *-------------------------------------------------------------------------
20  */
21 #include "postgres.h"
22
23 #include "access/hash.h"
24 #include "storage/proc.h"
25 #include "utils/memutils.h"
26 #include "utils/resowner.h"
27 #include "utils/relcache.h"
28
29
30 /*
31  * ResourceOwner objects look like this
32  */
33 typedef struct ResourceOwnerData
34 {
35         ResourceOwner parent;           /* NULL if no parent (toplevel owner) */
36         ResourceOwner firstchild;       /* head of linked list of children */
37         ResourceOwner nextchild;        /* next child of same parent */
38         const char *name;                       /* name (just for debugging) */
39
40         /* We have built-in support for remembering owned buffers */
41         int                     nbuffers;               /* number of owned buffer pins */
42         Buffer     *buffers;            /* dynamically allocated array */
43         int                     maxbuffers;             /* currently allocated array size */
44
45         /* We have built-in support for remembering catcache references */
46         int                     ncatrefs;               /* number of owned catcache pins */
47         HeapTuple  *catrefs;            /* dynamically allocated array */
48         int                     maxcatrefs;             /* currently allocated array size */
49
50         int                     ncatlistrefs;   /* number of owned catcache-list pins */
51         CatCList  **catlistrefs;        /* dynamically allocated array */
52         int                     maxcatlistrefs; /* currently allocated array size */
53
54         /* We have built-in support for remembering relcache references */
55         int                     nrelrefs;               /* number of owned relcache pins */
56         Relation   *relrefs;            /* dynamically allocated array */
57         int                     maxrelrefs;             /* currently allocated array size */
58
59         /* We have built-in support for remembering plancache references */
60         int                     nplanrefs;              /* number of owned plancache pins */
61         CachedPlan **planrefs;          /* dynamically allocated array */
62         int                     maxplanrefs;    /* currently allocated array size */
63
64         /* We have built-in support for remembering tupdesc references */
65         int                     ntupdescs;              /* number of owned tupdesc references */
66         TupleDesc  *tupdescs;           /* dynamically allocated array */
67         int                     maxtupdescs;    /* currently allocated array size */
68 } ResourceOwnerData;
69
70
71 /*****************************************************************************
72  *        GLOBAL MEMORY                                                                                                                  *
73  *****************************************************************************/
74
75 ResourceOwner CurrentResourceOwner = NULL;
76 ResourceOwner CurTransactionResourceOwner = NULL;
77 ResourceOwner TopTransactionResourceOwner = NULL;
78
79 /*
80  * List of add-on callbacks for resource releasing
81  */
82 typedef struct ResourceReleaseCallbackItem
83 {
84         struct ResourceReleaseCallbackItem *next;
85         ResourceReleaseCallback callback;
86         void       *arg;
87 } ResourceReleaseCallbackItem;
88
89 static ResourceReleaseCallbackItem *ResourceRelease_callbacks = NULL;
90
91
92 /* Internal routines */
93 static void ResourceOwnerReleaseInternal(ResourceOwner owner,
94                                                          ResourceReleasePhase phase,
95                                                          bool isCommit,
96                                                          bool isTopLevel);
97 static void PrintRelCacheLeakWarning(Relation rel);
98 static void PrintPlanCacheLeakWarning(CachedPlan * plan);
99 static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
100
101
102 /*****************************************************************************
103  *        EXPORTED ROUTINES                                                                                                              *
104  *****************************************************************************/
105
106
107 /*
108  * ResourceOwnerCreate
109  *              Create an empty ResourceOwner.
110  *
111  * All ResourceOwner objects are kept in TopMemoryContext, since they should
112  * only be freed explicitly.
113  */
114 ResourceOwner
115 ResourceOwnerCreate(ResourceOwner parent, const char *name)
116 {
117         ResourceOwner owner;
118
119         owner = (ResourceOwner) MemoryContextAllocZero(TopMemoryContext,
120                                                                                                    sizeof(ResourceOwnerData));
121         owner->name = name;
122
123         if (parent)
124         {
125                 owner->parent = parent;
126                 owner->nextchild = parent->firstchild;
127                 parent->firstchild = owner;
128         }
129
130         return owner;
131 }
132
133 /*
134  * ResourceOwnerRelease
135  *              Release all resources owned by a ResourceOwner and its descendants,
136  *              but don't delete the owner objects themselves.
137  *
138  * Note that this executes just one phase of release, and so typically
139  * must be called three times.  We do it this way because (a) we want to
140  * do all the recursion separately for each phase, thereby preserving
141  * the needed order of operations; and (b) xact.c may have other operations
142  * to do between the phases.
143  *
144  * phase: release phase to execute
145  * isCommit: true for successful completion of a query or transaction,
146  *                      false for unsuccessful
147  * isTopLevel: true if completing a main transaction, else false
148  *
149  * isCommit is passed because some modules may expect that their resources
150  * were all released already if the transaction or portal finished normally.
151  * If so it is reasonable to give a warning (NOT an error) should any
152  * unreleased resources be present.  When isCommit is false, such warnings
153  * are generally inappropriate.
154  *
155  * isTopLevel is passed when we are releasing TopTransactionResourceOwner
156  * at completion of a main transaction.  This generally means that *all*
157  * resources will be released, and so we can optimize things a bit.
158  */
159 void
160 ResourceOwnerRelease(ResourceOwner owner,
161                                          ResourceReleasePhase phase,
162                                          bool isCommit,
163                                          bool isTopLevel)
164 {
165         /* Rather than PG_TRY at every level of recursion, set it up once */
166         ResourceOwner save;
167
168         save = CurrentResourceOwner;
169         PG_TRY();
170         {
171                 ResourceOwnerReleaseInternal(owner, phase, isCommit, isTopLevel);
172         }
173         PG_CATCH();
174         {
175                 CurrentResourceOwner = save;
176                 PG_RE_THROW();
177         }
178         PG_END_TRY();
179         CurrentResourceOwner = save;
180 }
181
182 static void
183 ResourceOwnerReleaseInternal(ResourceOwner owner,
184                                                          ResourceReleasePhase phase,
185                                                          bool isCommit,
186                                                          bool isTopLevel)
187 {
188         ResourceOwner child;
189         ResourceOwner save;
190         ResourceReleaseCallbackItem *item;
191
192         /* Recurse to handle descendants */
193         for (child = owner->firstchild; child != NULL; child = child->nextchild)
194                 ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel);
195
196         /*
197          * Make CurrentResourceOwner point to me, so that ReleaseBuffer etc don't
198          * get confused.  We needn't PG_TRY here because the outermost level will
199          * fix it on error abort.
200          */
201         save = CurrentResourceOwner;
202         CurrentResourceOwner = owner;
203
204         if (phase == RESOURCE_RELEASE_BEFORE_LOCKS)
205         {
206                 /*
207                  * Release buffer pins.  Note that ReleaseBuffer will remove the
208                  * buffer entry from my list, so I just have to iterate till there are
209                  * none.
210                  *
211                  * During a commit, there shouldn't be any remaining pins --- that
212                  * would indicate failure to clean up the executor correctly --- so
213                  * issue warnings.      In the abort case, just clean up quietly.
214                  *
215                  * We are careful to do the releasing back-to-front, so as to avoid
216                  * O(N^2) behavior in ResourceOwnerForgetBuffer().
217                  */
218                 while (owner->nbuffers > 0)
219                 {
220                         if (isCommit)
221                                 PrintBufferLeakWarning(owner->buffers[owner->nbuffers - 1]);
222                         ReleaseBuffer(owner->buffers[owner->nbuffers - 1]);
223                 }
224
225                 /*
226                  * Release relcache references.  Note that RelationClose will remove
227                  * the relref entry from my list, so I just have to iterate till there
228                  * are none.
229                  *
230                  * As with buffer pins, warn if any are left at commit time, and
231                  * release back-to-front for speed.
232                  */
233                 while (owner->nrelrefs > 0)
234                 {
235                         if (isCommit)
236                                 PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]);
237                         RelationClose(owner->relrefs[owner->nrelrefs - 1]);
238                 }
239         }
240         else if (phase == RESOURCE_RELEASE_LOCKS)
241         {
242                 if (isTopLevel)
243                 {
244                         /*
245                          * For a top-level xact we are going to release all locks (or at
246                          * least all non-session locks), so just do a single lmgr call at
247                          * the top of the recursion.
248                          */
249                         if (owner == TopTransactionResourceOwner)
250                                 ProcReleaseLocks(isCommit);
251                 }
252                 else
253                 {
254                         /*
255                          * Release locks retail.  Note that if we are committing a
256                          * subtransaction, we do NOT release its locks yet, but transfer
257                          * them to the parent.
258                          */
259                         Assert(owner->parent != NULL);
260                         if (isCommit)
261                                 LockReassignCurrentOwner();
262                         else
263                                 LockReleaseCurrentOwner();
264                 }
265         }
266         else if (phase == RESOURCE_RELEASE_AFTER_LOCKS)
267         {
268                 /*
269                  * Release catcache references.  Note that ReleaseCatCache will remove
270                  * the catref entry from my list, so I just have to iterate till there
271                  * are none.
272                  *
273                  * As with buffer pins, warn if any are left at commit time, and
274                  * release back-to-front for speed.
275                  */
276                 while (owner->ncatrefs > 0)
277                 {
278                         if (isCommit)
279                                 PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
280                         ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
281                 }
282                 /* Ditto for catcache lists */
283                 while (owner->ncatlistrefs > 0)
284                 {
285                         if (isCommit)
286                                 PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
287                         ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
288                 }
289                 /* Ditto for plancache references */
290                 while (owner->nplanrefs > 0)
291                 {
292                         if (isCommit)
293                                 PrintPlanCacheLeakWarning(owner->planrefs[owner->nplanrefs - 1]);
294                         ReleaseCachedPlan(owner->planrefs[owner->nplanrefs - 1], true);
295                 }
296                 /* Ditto for tupdesc references */
297                 while (owner->ntupdescs > 0)
298                 {
299                         if (isCommit)
300                                 PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]);
301                         DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1]);
302                 }
303
304                 /* Clean up index scans too */
305                 ReleaseResources_hash();
306         }
307
308         /* Let add-on modules get a chance too */
309         for (item = ResourceRelease_callbacks; item; item = item->next)
310                 (*item->callback) (phase, isCommit, isTopLevel, item->arg);
311
312         CurrentResourceOwner = save;
313 }
314
315 /*
316  * ResourceOwnerDelete
317  *              Delete an owner object and its descendants.
318  *
319  * The caller must have already released all resources in the object tree.
320  */
321 void
322 ResourceOwnerDelete(ResourceOwner owner)
323 {
324         /* We had better not be deleting CurrentResourceOwner ... */
325         Assert(owner != CurrentResourceOwner);
326
327         /* And it better not own any resources, either */
328         Assert(owner->nbuffers == 0);
329         Assert(owner->ncatrefs == 0);
330         Assert(owner->ncatlistrefs == 0);
331         Assert(owner->nrelrefs == 0);
332         Assert(owner->nplanrefs == 0);
333         Assert(owner->ntupdescs == 0);
334
335         /*
336          * Delete children.  The recursive call will delink the child from me, so
337          * just iterate as long as there is a child.
338          */
339         while (owner->firstchild != NULL)
340                 ResourceOwnerDelete(owner->firstchild);
341
342         /*
343          * We delink the owner from its parent before deleting it, so that if
344          * there's an error we won't have deleted/busted owners still attached to
345          * the owner tree.      Better a leak than a crash.
346          */
347         ResourceOwnerNewParent(owner, NULL);
348
349         /* And free the object. */
350         if (owner->buffers)
351                 pfree(owner->buffers);
352         if (owner->catrefs)
353                 pfree(owner->catrefs);
354         if (owner->catlistrefs)
355                 pfree(owner->catlistrefs);
356         if (owner->relrefs)
357                 pfree(owner->relrefs);
358         if (owner->planrefs)
359                 pfree(owner->planrefs);
360         if (owner->tupdescs)
361                 pfree(owner->tupdescs);
362
363         pfree(owner);
364 }
365
366 /*
367  * Fetch parent of a ResourceOwner (returns NULL if top-level owner)
368  */
369 ResourceOwner
370 ResourceOwnerGetParent(ResourceOwner owner)
371 {
372         return owner->parent;
373 }
374
375 /*
376  * Reassign a ResourceOwner to have a new parent
377  */
378 void
379 ResourceOwnerNewParent(ResourceOwner owner,
380                                            ResourceOwner newparent)
381 {
382         ResourceOwner oldparent = owner->parent;
383
384         if (oldparent)
385         {
386                 if (owner == oldparent->firstchild)
387                         oldparent->firstchild = owner->nextchild;
388                 else
389                 {
390                         ResourceOwner child;
391
392                         for (child = oldparent->firstchild; child; child = child->nextchild)
393                         {
394                                 if (owner == child->nextchild)
395                                 {
396                                         child->nextchild = owner->nextchild;
397                                         break;
398                                 }
399                         }
400                 }
401         }
402
403         if (newparent)
404         {
405                 Assert(owner != newparent);
406                 owner->parent = newparent;
407                 owner->nextchild = newparent->firstchild;
408                 newparent->firstchild = owner;
409         }
410         else
411         {
412                 owner->parent = NULL;
413                 owner->nextchild = NULL;
414         }
415 }
416
417 /*
418  * Register or deregister callback functions for resource cleanup
419  *
420  * These functions are intended for use by dynamically loaded modules.
421  * For built-in modules we generally just hardwire the appropriate calls.
422  *
423  * Note that the callback occurs post-commit or post-abort, so the callback
424  * functions can only do noncritical cleanup.
425  */
426 void
427 RegisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
428 {
429         ResourceReleaseCallbackItem *item;
430
431         item = (ResourceReleaseCallbackItem *)
432                 MemoryContextAlloc(TopMemoryContext,
433                                                    sizeof(ResourceReleaseCallbackItem));
434         item->callback = callback;
435         item->arg = arg;
436         item->next = ResourceRelease_callbacks;
437         ResourceRelease_callbacks = item;
438 }
439
440 void
441 UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
442 {
443         ResourceReleaseCallbackItem *item;
444         ResourceReleaseCallbackItem *prev;
445
446         prev = NULL;
447         for (item = ResourceRelease_callbacks; item; prev = item, item = item->next)
448         {
449                 if (item->callback == callback && item->arg == arg)
450                 {
451                         if (prev)
452                                 prev->next = item->next;
453                         else
454                                 ResourceRelease_callbacks = item->next;
455                         pfree(item);
456                         break;
457                 }
458         }
459 }
460
461
462 /*
463  * Make sure there is room for at least one more entry in a ResourceOwner's
464  * buffer array.
465  *
466  * This is separate from actually inserting an entry because if we run out
467  * of memory, it's critical to do so *before* acquiring the resource.
468  *
469  * We allow the case owner == NULL because the bufmgr is sometimes invoked
470  * outside any transaction (for example, during WAL recovery).
471  */
472 void
473 ResourceOwnerEnlargeBuffers(ResourceOwner owner)
474 {
475         int                     newmax;
476
477         if (owner == NULL ||
478                 owner->nbuffers < owner->maxbuffers)
479                 return;                                 /* nothing to do */
480
481         if (owner->buffers == NULL)
482         {
483                 newmax = 16;
484                 owner->buffers = (Buffer *)
485                         MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Buffer));
486                 owner->maxbuffers = newmax;
487         }
488         else
489         {
490                 newmax = owner->maxbuffers * 2;
491                 owner->buffers = (Buffer *)
492                         repalloc(owner->buffers, newmax * sizeof(Buffer));
493                 owner->maxbuffers = newmax;
494         }
495 }
496
497 /*
498  * Remember that a buffer pin is owned by a ResourceOwner
499  *
500  * Caller must have previously done ResourceOwnerEnlargeBuffers()
501  *
502  * We allow the case owner == NULL because the bufmgr is sometimes invoked
503  * outside any transaction (for example, during WAL recovery).
504  */
505 void
506 ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
507 {
508         if (owner != NULL)
509         {
510                 Assert(owner->nbuffers < owner->maxbuffers);
511                 owner->buffers[owner->nbuffers] = buffer;
512                 owner->nbuffers++;
513         }
514 }
515
516 /*
517  * Forget that a buffer pin is owned by a ResourceOwner
518  *
519  * We allow the case owner == NULL because the bufmgr is sometimes invoked
520  * outside any transaction (for example, during WAL recovery).
521  */
522 void
523 ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
524 {
525         if (owner != NULL)
526         {
527                 Buffer     *buffers = owner->buffers;
528                 int                     nb1 = owner->nbuffers - 1;
529                 int                     i;
530
531                 /*
532                  * Scan back-to-front because it's more likely we are releasing a
533                  * recently pinned buffer.      This isn't always the case of course, but
534                  * it's the way to bet.
535                  */
536                 for (i = nb1; i >= 0; i--)
537                 {
538                         if (buffers[i] == buffer)
539                         {
540                                 while (i < nb1)
541                                 {
542                                         buffers[i] = buffers[i + 1];
543                                         i++;
544                                 }
545                                 owner->nbuffers = nb1;
546                                 return;
547                         }
548                 }
549                 elog(ERROR, "buffer %d is not owned by resource owner %s",
550                          buffer, owner->name);
551         }
552 }
553
554 /*
555  * Make sure there is room for at least one more entry in a ResourceOwner's
556  * catcache reference array.
557  *
558  * This is separate from actually inserting an entry because if we run out
559  * of memory, it's critical to do so *before* acquiring the resource.
560  */
561 void
562 ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
563 {
564         int                     newmax;
565
566         if (owner->ncatrefs < owner->maxcatrefs)
567                 return;                                 /* nothing to do */
568
569         if (owner->catrefs == NULL)
570         {
571                 newmax = 16;
572                 owner->catrefs = (HeapTuple *)
573                         MemoryContextAlloc(TopMemoryContext, newmax * sizeof(HeapTuple));
574                 owner->maxcatrefs = newmax;
575         }
576         else
577         {
578                 newmax = owner->maxcatrefs * 2;
579                 owner->catrefs = (HeapTuple *)
580                         repalloc(owner->catrefs, newmax * sizeof(HeapTuple));
581                 owner->maxcatrefs = newmax;
582         }
583 }
584
585 /*
586  * Remember that a catcache reference is owned by a ResourceOwner
587  *
588  * Caller must have previously done ResourceOwnerEnlargeCatCacheRefs()
589  */
590 void
591 ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
592 {
593         Assert(owner->ncatrefs < owner->maxcatrefs);
594         owner->catrefs[owner->ncatrefs] = tuple;
595         owner->ncatrefs++;
596 }
597
598 /*
599  * Forget that a catcache reference is owned by a ResourceOwner
600  */
601 void
602 ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
603 {
604         HeapTuple  *catrefs = owner->catrefs;
605         int                     nc1 = owner->ncatrefs - 1;
606         int                     i;
607
608         for (i = nc1; i >= 0; i--)
609         {
610                 if (catrefs[i] == tuple)
611                 {
612                         while (i < nc1)
613                         {
614                                 catrefs[i] = catrefs[i + 1];
615                                 i++;
616                         }
617                         owner->ncatrefs = nc1;
618                         return;
619                 }
620         }
621         elog(ERROR, "catcache reference %p is not owned by resource owner %s",
622                  tuple, owner->name);
623 }
624
625 /*
626  * Make sure there is room for at least one more entry in a ResourceOwner's
627  * catcache-list reference array.
628  *
629  * This is separate from actually inserting an entry because if we run out
630  * of memory, it's critical to do so *before* acquiring the resource.
631  */
632 void
633 ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
634 {
635         int                     newmax;
636
637         if (owner->ncatlistrefs < owner->maxcatlistrefs)
638                 return;                                 /* nothing to do */
639
640         if (owner->catlistrefs == NULL)
641         {
642                 newmax = 16;
643                 owner->catlistrefs = (CatCList **)
644                         MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CatCList *));
645                 owner->maxcatlistrefs = newmax;
646         }
647         else
648         {
649                 newmax = owner->maxcatlistrefs * 2;
650                 owner->catlistrefs = (CatCList **)
651                         repalloc(owner->catlistrefs, newmax * sizeof(CatCList *));
652                 owner->maxcatlistrefs = newmax;
653         }
654 }
655
656 /*
657  * Remember that a catcache-list reference is owned by a ResourceOwner
658  *
659  * Caller must have previously done ResourceOwnerEnlargeCatCacheListRefs()
660  */
661 void
662 ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
663 {
664         Assert(owner->ncatlistrefs < owner->maxcatlistrefs);
665         owner->catlistrefs[owner->ncatlistrefs] = list;
666         owner->ncatlistrefs++;
667 }
668
669 /*
670  * Forget that a catcache-list reference is owned by a ResourceOwner
671  */
672 void
673 ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
674 {
675         CatCList  **catlistrefs = owner->catlistrefs;
676         int                     nc1 = owner->ncatlistrefs - 1;
677         int                     i;
678
679         for (i = nc1; i >= 0; i--)
680         {
681                 if (catlistrefs[i] == list)
682                 {
683                         while (i < nc1)
684                         {
685                                 catlistrefs[i] = catlistrefs[i + 1];
686                                 i++;
687                         }
688                         owner->ncatlistrefs = nc1;
689                         return;
690                 }
691         }
692         elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
693                  list, owner->name);
694 }
695
696 /*
697  * Make sure there is room for at least one more entry in a ResourceOwner's
698  * relcache reference array.
699  *
700  * This is separate from actually inserting an entry because if we run out
701  * of memory, it's critical to do so *before* acquiring the resource.
702  */
703 void
704 ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
705 {
706         int                     newmax;
707
708         if (owner->nrelrefs < owner->maxrelrefs)
709                 return;                                 /* nothing to do */
710
711         if (owner->relrefs == NULL)
712         {
713                 newmax = 16;
714                 owner->relrefs = (Relation *)
715                         MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Relation));
716                 owner->maxrelrefs = newmax;
717         }
718         else
719         {
720                 newmax = owner->maxrelrefs * 2;
721                 owner->relrefs = (Relation *)
722                         repalloc(owner->relrefs, newmax * sizeof(Relation));
723                 owner->maxrelrefs = newmax;
724         }
725 }
726
727 /*
728  * Remember that a relcache reference is owned by a ResourceOwner
729  *
730  * Caller must have previously done ResourceOwnerEnlargeRelationRefs()
731  */
732 void
733 ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
734 {
735         Assert(owner->nrelrefs < owner->maxrelrefs);
736         owner->relrefs[owner->nrelrefs] = rel;
737         owner->nrelrefs++;
738 }
739
740 /*
741  * Forget that a relcache reference is owned by a ResourceOwner
742  */
743 void
744 ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel)
745 {
746         Relation   *relrefs = owner->relrefs;
747         int                     nr1 = owner->nrelrefs - 1;
748         int                     i;
749
750         for (i = nr1; i >= 0; i--)
751         {
752                 if (relrefs[i] == rel)
753                 {
754                         while (i < nr1)
755                         {
756                                 relrefs[i] = relrefs[i + 1];
757                                 i++;
758                         }
759                         owner->nrelrefs = nr1;
760                         return;
761                 }
762         }
763         elog(ERROR, "relcache reference %s is not owned by resource owner %s",
764                  RelationGetRelationName(rel), owner->name);
765 }
766
767 /*
768  * Debugging subroutine
769  */
770 static void
771 PrintRelCacheLeakWarning(Relation rel)
772 {
773         elog(WARNING, "relcache reference leak: relation \"%s\" not closed",
774                  RelationGetRelationName(rel));
775 }
776
777 /*
778  * Make sure there is room for at least one more entry in a ResourceOwner's
779  * plancache reference array.
780  *
781  * This is separate from actually inserting an entry because if we run out
782  * of memory, it's critical to do so *before* acquiring the resource.
783  */
784 void
785 ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
786 {
787         int                     newmax;
788
789         if (owner->nplanrefs < owner->maxplanrefs)
790                 return;                                 /* nothing to do */
791
792         if (owner->planrefs == NULL)
793         {
794                 newmax = 16;
795                 owner->planrefs = (CachedPlan **)
796                         MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CachedPlan *));
797                 owner->maxplanrefs = newmax;
798         }
799         else
800         {
801                 newmax = owner->maxplanrefs * 2;
802                 owner->planrefs = (CachedPlan **)
803                         repalloc(owner->planrefs, newmax * sizeof(CachedPlan *));
804                 owner->maxplanrefs = newmax;
805         }
806 }
807
808 /*
809  * Remember that a plancache reference is owned by a ResourceOwner
810  *
811  * Caller must have previously done ResourceOwnerEnlargePlanCacheRefs()
812  */
813 void
814 ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan * plan)
815 {
816         Assert(owner->nplanrefs < owner->maxplanrefs);
817         owner->planrefs[owner->nplanrefs] = plan;
818         owner->nplanrefs++;
819 }
820
821 /*
822  * Forget that a plancache reference is owned by a ResourceOwner
823  */
824 void
825 ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan * plan)
826 {
827         CachedPlan **planrefs = owner->planrefs;
828         int                     np1 = owner->nplanrefs - 1;
829         int                     i;
830
831         for (i = np1; i >= 0; i--)
832         {
833                 if (planrefs[i] == plan)
834                 {
835                         while (i < np1)
836                         {
837                                 planrefs[i] = planrefs[i + 1];
838                                 i++;
839                         }
840                         owner->nplanrefs = np1;
841                         return;
842                 }
843         }
844         elog(ERROR, "plancache reference %p is not owned by resource owner %s",
845                  plan, owner->name);
846 }
847
848 /*
849  * Debugging subroutine
850  */
851 static void
852 PrintPlanCacheLeakWarning(CachedPlan * plan)
853 {
854         elog(WARNING, "plancache reference leak: plan %p not closed", plan);
855 }
856
857 /*
858  * Make sure there is room for at least one more entry in a ResourceOwner's
859  * tupdesc reference array.
860  *
861  * This is separate from actually inserting an entry because if we run out
862  * of memory, it's critical to do so *before* acquiring the resource.
863  */
864 void
865 ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
866 {
867         int                     newmax;
868
869         if (owner->ntupdescs < owner->maxtupdescs)
870                 return;                                 /* nothing to do */
871
872         if (owner->tupdescs == NULL)
873         {
874                 newmax = 16;
875                 owner->tupdescs = (TupleDesc *)
876                         MemoryContextAlloc(TopMemoryContext, newmax * sizeof(TupleDesc));
877                 owner->maxtupdescs = newmax;
878         }
879         else
880         {
881                 newmax = owner->maxtupdescs * 2;
882                 owner->tupdescs = (TupleDesc *)
883                         repalloc(owner->tupdescs, newmax * sizeof(TupleDesc));
884                 owner->maxtupdescs = newmax;
885         }
886 }
887
888 /*
889  * Remember that a tupdesc reference is owned by a ResourceOwner
890  *
891  * Caller must have previously done ResourceOwnerEnlargeTupleDescs()
892  */
893 void
894 ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
895 {
896         Assert(owner->ntupdescs < owner->maxtupdescs);
897         owner->tupdescs[owner->ntupdescs] = tupdesc;
898         owner->ntupdescs++;
899 }
900
901 /*
902  * Forget that a tupdesc reference is owned by a ResourceOwner
903  */
904 void
905 ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
906 {
907         TupleDesc  *tupdescs = owner->tupdescs;
908         int                     nt1 = owner->ntupdescs - 1;
909         int                     i;
910
911         for (i = nt1; i >= 0; i--)
912         {
913                 if (tupdescs[i] == tupdesc)
914                 {
915                         while (i < nt1)
916                         {
917                                 tupdescs[i] = tupdescs[i + 1];
918                                 i++;
919                         }
920                         owner->ntupdescs = nt1;
921                         return;
922                 }
923         }
924         elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
925                  tupdesc, owner->name);
926 }
927
928 /*
929  * Debugging subroutine
930  */
931 static void
932 PrintTupleDescLeakWarning(TupleDesc tupdesc)
933 {
934         elog(WARNING,
935                  "TupleDesc reference leak: TupleDesc %p (%u,%d) still referenced",
936                  tupdesc, tupdesc->tdtypeid, tupdesc->tdtypmod);
937 }