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