Header And Logo

PostgreSQL
| The world's most advanced open source database.

resowner.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * resowner.c
00004  *    POSTGRES resource owner management code.
00005  *
00006  * Query-lifespan resources are tracked by associating them with
00007  * ResourceOwner objects.  This provides a simple mechanism for ensuring
00008  * that such resources are freed at the right time.
00009  * See utils/resowner/README for more info.
00010  *
00011  *
00012  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00013  * Portions Copyright (c) 1994, Regents of the University of California
00014  *
00015  *
00016  * IDENTIFICATION
00017  *    src/backend/utils/resowner/resowner.c
00018  *
00019  *-------------------------------------------------------------------------
00020  */
00021 #include "postgres.h"
00022 
00023 #include "access/hash.h"
00024 #include "storage/predicate.h"
00025 #include "storage/proc.h"
00026 #include "utils/memutils.h"
00027 #include "utils/rel.h"
00028 #include "utils/resowner_private.h"
00029 #include "utils/snapmgr.h"
00030 
00031 /*
00032  * To speed up bulk releasing or reassigning locks from a resource owner to
00033  * its parent, each resource owner has a small cache of locks it owns. The
00034  * lock manager has the same information in its local lock hash table, and
00035  * we fall back on that if cache overflows, but traversing the hash table
00036  * is slower when there are a lot of locks belonging to other resource owners.
00037  *
00038  * MAX_RESOWNER_LOCKS is the size of the per-resource owner cache. It's
00039  * chosen based on some testing with pg_dump with a large schema. When the
00040  * tests were done (on 9.2), resource owners in a pg_dump run contained up
00041  * to 9 locks, regardless of the schema size, except for the top resource
00042  * owner which contained much more (overflowing the cache). 15 seems like a
00043  * nice round number that's somewhat higher than what pg_dump needs. Note that
00044  * making this number larger is not free - the bigger the cache, the slower
00045  * it is to release locks (in retail), when a resource owner holds many locks.
00046  */
00047 #define MAX_RESOWNER_LOCKS 15
00048 
00049 /*
00050  * ResourceOwner objects look like this
00051  */
00052 typedef struct ResourceOwnerData
00053 {
00054     ResourceOwner parent;       /* NULL if no parent (toplevel owner) */
00055     ResourceOwner firstchild;   /* head of linked list of children */
00056     ResourceOwner nextchild;    /* next child of same parent */
00057     const char *name;           /* name (just for debugging) */
00058 
00059     /* We have built-in support for remembering owned buffers */
00060     int         nbuffers;       /* number of owned buffer pins */
00061     Buffer     *buffers;        /* dynamically allocated array */
00062     int         maxbuffers;     /* currently allocated array size */
00063 
00064     /* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
00065     int         nlocks;     /* number of owned locks */
00066     LOCALLOCK  *locks[MAX_RESOWNER_LOCKS];  /* list of owned locks */
00067 
00068     /* We have built-in support for remembering catcache references */
00069     int         ncatrefs;       /* number of owned catcache pins */
00070     HeapTuple  *catrefs;        /* dynamically allocated array */
00071     int         maxcatrefs;     /* currently allocated array size */
00072 
00073     int         ncatlistrefs;   /* number of owned catcache-list pins */
00074     CatCList  **catlistrefs;    /* dynamically allocated array */
00075     int         maxcatlistrefs; /* currently allocated array size */
00076 
00077     /* We have built-in support for remembering relcache references */
00078     int         nrelrefs;       /* number of owned relcache pins */
00079     Relation   *relrefs;        /* dynamically allocated array */
00080     int         maxrelrefs;     /* currently allocated array size */
00081 
00082     /* We have built-in support for remembering plancache references */
00083     int         nplanrefs;      /* number of owned plancache pins */
00084     CachedPlan **planrefs;      /* dynamically allocated array */
00085     int         maxplanrefs;    /* currently allocated array size */
00086 
00087     /* We have built-in support for remembering tupdesc references */
00088     int         ntupdescs;      /* number of owned tupdesc references */
00089     TupleDesc  *tupdescs;       /* dynamically allocated array */
00090     int         maxtupdescs;    /* currently allocated array size */
00091 
00092     /* We have built-in support for remembering snapshot references */
00093     int         nsnapshots;     /* number of owned snapshot references */
00094     Snapshot   *snapshots;      /* dynamically allocated array */
00095     int         maxsnapshots;   /* currently allocated array size */
00096 
00097     /* We have built-in support for remembering open temporary files */
00098     int         nfiles;         /* number of owned temporary files */
00099     File       *files;          /* dynamically allocated array */
00100     int         maxfiles;       /* currently allocated array size */
00101 }   ResourceOwnerData;
00102 
00103 
00104 /*****************************************************************************
00105  *    GLOBAL MEMORY                                                          *
00106  *****************************************************************************/
00107 
00108 ResourceOwner CurrentResourceOwner = NULL;
00109 ResourceOwner CurTransactionResourceOwner = NULL;
00110 ResourceOwner TopTransactionResourceOwner = NULL;
00111 
00112 /*
00113  * List of add-on callbacks for resource releasing
00114  */
00115 typedef struct ResourceReleaseCallbackItem
00116 {
00117     struct ResourceReleaseCallbackItem *next;
00118     ResourceReleaseCallback callback;
00119     void       *arg;
00120 } ResourceReleaseCallbackItem;
00121 
00122 static ResourceReleaseCallbackItem *ResourceRelease_callbacks = NULL;
00123 
00124 
00125 /* Internal routines */
00126 static void ResourceOwnerReleaseInternal(ResourceOwner owner,
00127                              ResourceReleasePhase phase,
00128                              bool isCommit,
00129                              bool isTopLevel);
00130 static void PrintRelCacheLeakWarning(Relation rel);
00131 static void PrintPlanCacheLeakWarning(CachedPlan *plan);
00132 static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
00133 static void PrintSnapshotLeakWarning(Snapshot snapshot);
00134 static void PrintFileLeakWarning(File file);
00135 
00136 
00137 /*****************************************************************************
00138  *    EXPORTED ROUTINES                                                      *
00139  *****************************************************************************/
00140 
00141 
00142 /*
00143  * ResourceOwnerCreate
00144  *      Create an empty ResourceOwner.
00145  *
00146  * All ResourceOwner objects are kept in TopMemoryContext, since they should
00147  * only be freed explicitly.
00148  */
00149 ResourceOwner
00150 ResourceOwnerCreate(ResourceOwner parent, const char *name)
00151 {
00152     ResourceOwner owner;
00153 
00154     owner = (ResourceOwner) MemoryContextAllocZero(TopMemoryContext,
00155                                                    sizeof(ResourceOwnerData));
00156     owner->name = name;
00157 
00158     if (parent)
00159     {
00160         owner->parent = parent;
00161         owner->nextchild = parent->firstchild;
00162         parent->firstchild = owner;
00163     }
00164 
00165     return owner;
00166 }
00167 
00168 /*
00169  * ResourceOwnerRelease
00170  *      Release all resources owned by a ResourceOwner and its descendants,
00171  *      but don't delete the owner objects themselves.
00172  *
00173  * Note that this executes just one phase of release, and so typically
00174  * must be called three times.  We do it this way because (a) we want to
00175  * do all the recursion separately for each phase, thereby preserving
00176  * the needed order of operations; and (b) xact.c may have other operations
00177  * to do between the phases.
00178  *
00179  * phase: release phase to execute
00180  * isCommit: true for successful completion of a query or transaction,
00181  *          false for unsuccessful
00182  * isTopLevel: true if completing a main transaction, else false
00183  *
00184  * isCommit is passed because some modules may expect that their resources
00185  * were all released already if the transaction or portal finished normally.
00186  * If so it is reasonable to give a warning (NOT an error) should any
00187  * unreleased resources be present.  When isCommit is false, such warnings
00188  * are generally inappropriate.
00189  *
00190  * isTopLevel is passed when we are releasing TopTransactionResourceOwner
00191  * at completion of a main transaction.  This generally means that *all*
00192  * resources will be released, and so we can optimize things a bit.
00193  */
00194 void
00195 ResourceOwnerRelease(ResourceOwner owner,
00196                      ResourceReleasePhase phase,
00197                      bool isCommit,
00198                      bool isTopLevel)
00199 {
00200     /* Rather than PG_TRY at every level of recursion, set it up once */
00201     ResourceOwner save;
00202 
00203     save = CurrentResourceOwner;
00204     PG_TRY();
00205     {
00206         ResourceOwnerReleaseInternal(owner, phase, isCommit, isTopLevel);
00207     }
00208     PG_CATCH();
00209     {
00210         CurrentResourceOwner = save;
00211         PG_RE_THROW();
00212     }
00213     PG_END_TRY();
00214     CurrentResourceOwner = save;
00215 }
00216 
00217 static void
00218 ResourceOwnerReleaseInternal(ResourceOwner owner,
00219                              ResourceReleasePhase phase,
00220                              bool isCommit,
00221                              bool isTopLevel)
00222 {
00223     ResourceOwner child;
00224     ResourceOwner save;
00225     ResourceReleaseCallbackItem *item;
00226 
00227     /* Recurse to handle descendants */
00228     for (child = owner->firstchild; child != NULL; child = child->nextchild)
00229         ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel);
00230 
00231     /*
00232      * Make CurrentResourceOwner point to me, so that ReleaseBuffer etc don't
00233      * get confused.  We needn't PG_TRY here because the outermost level will
00234      * fix it on error abort.
00235      */
00236     save = CurrentResourceOwner;
00237     CurrentResourceOwner = owner;
00238 
00239     if (phase == RESOURCE_RELEASE_BEFORE_LOCKS)
00240     {
00241         /*
00242          * Release buffer pins.  Note that ReleaseBuffer will remove the
00243          * buffer entry from my list, so I just have to iterate till there are
00244          * none.
00245          *
00246          * During a commit, there shouldn't be any remaining pins --- that
00247          * would indicate failure to clean up the executor correctly --- so
00248          * issue warnings.  In the abort case, just clean up quietly.
00249          *
00250          * We are careful to do the releasing back-to-front, so as to avoid
00251          * O(N^2) behavior in ResourceOwnerForgetBuffer().
00252          */
00253         while (owner->nbuffers > 0)
00254         {
00255             if (isCommit)
00256                 PrintBufferLeakWarning(owner->buffers[owner->nbuffers - 1]);
00257             ReleaseBuffer(owner->buffers[owner->nbuffers - 1]);
00258         }
00259 
00260         /*
00261          * Release relcache references.  Note that RelationClose will remove
00262          * the relref entry from my list, so I just have to iterate till there
00263          * are none.
00264          *
00265          * As with buffer pins, warn if any are left at commit time, and
00266          * release back-to-front for speed.
00267          */
00268         while (owner->nrelrefs > 0)
00269         {
00270             if (isCommit)
00271                 PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]);
00272             RelationClose(owner->relrefs[owner->nrelrefs - 1]);
00273         }
00274     }
00275     else if (phase == RESOURCE_RELEASE_LOCKS)
00276     {
00277         if (isTopLevel)
00278         {
00279             /*
00280              * For a top-level xact we are going to release all locks (or at
00281              * least all non-session locks), so just do a single lmgr call at
00282              * the top of the recursion.
00283              */
00284             if (owner == TopTransactionResourceOwner)
00285             {
00286                 ProcReleaseLocks(isCommit);
00287                 ReleasePredicateLocks(isCommit);
00288             }
00289         }
00290         else
00291         {
00292             /*
00293              * Release locks retail.  Note that if we are committing a
00294              * subtransaction, we do NOT release its locks yet, but transfer
00295              * them to the parent.
00296              */
00297             LOCALLOCK **locks;
00298             int         nlocks;
00299 
00300             Assert(owner->parent != NULL);
00301 
00302             /*
00303              * Pass the list of locks owned by this resource owner to the lock
00304              * manager, unless it has overflowed.
00305              */
00306             if (owner->nlocks > MAX_RESOWNER_LOCKS)
00307             {
00308                 locks = NULL;
00309                 nlocks = 0;
00310             }
00311             else
00312             {
00313                 locks = owner->locks;
00314                 nlocks = owner->nlocks;
00315             }
00316 
00317             if (isCommit)
00318                 LockReassignCurrentOwner(locks, nlocks);
00319             else
00320                 LockReleaseCurrentOwner(locks, nlocks);
00321         }
00322     }
00323     else if (phase == RESOURCE_RELEASE_AFTER_LOCKS)
00324     {
00325         /*
00326          * Release catcache references.  Note that ReleaseCatCache will remove
00327          * the catref entry from my list, so I just have to iterate till there
00328          * are none.
00329          *
00330          * As with buffer pins, warn if any are left at commit time, and
00331          * release back-to-front for speed.
00332          */
00333         while (owner->ncatrefs > 0)
00334         {
00335             if (isCommit)
00336                 PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
00337             ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
00338         }
00339         /* Ditto for catcache lists */
00340         while (owner->ncatlistrefs > 0)
00341         {
00342             if (isCommit)
00343                 PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
00344             ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
00345         }
00346         /* Ditto for plancache references */
00347         while (owner->nplanrefs > 0)
00348         {
00349             if (isCommit)
00350                 PrintPlanCacheLeakWarning(owner->planrefs[owner->nplanrefs - 1]);
00351             ReleaseCachedPlan(owner->planrefs[owner->nplanrefs - 1], true);
00352         }
00353         /* Ditto for tupdesc references */
00354         while (owner->ntupdescs > 0)
00355         {
00356             if (isCommit)
00357                 PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]);
00358             DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1]);
00359         }
00360         /* Ditto for snapshot references */
00361         while (owner->nsnapshots > 0)
00362         {
00363             if (isCommit)
00364                 PrintSnapshotLeakWarning(owner->snapshots[owner->nsnapshots - 1]);
00365             UnregisterSnapshot(owner->snapshots[owner->nsnapshots - 1]);
00366         }
00367 
00368         /* Ditto for temporary files */
00369         while (owner->nfiles > 0)
00370         {
00371             if (isCommit)
00372                 PrintFileLeakWarning(owner->files[owner->nfiles - 1]);
00373             FileClose(owner->files[owner->nfiles - 1]);
00374         }
00375 
00376         /* Clean up index scans too */
00377         ReleaseResources_hash();
00378     }
00379 
00380     /* Let add-on modules get a chance too */
00381     for (item = ResourceRelease_callbacks; item; item = item->next)
00382         (*item->callback) (phase, isCommit, isTopLevel, item->arg);
00383 
00384     CurrentResourceOwner = save;
00385 }
00386 
00387 /*
00388  * ResourceOwnerDelete
00389  *      Delete an owner object and its descendants.
00390  *
00391  * The caller must have already released all resources in the object tree.
00392  */
00393 void
00394 ResourceOwnerDelete(ResourceOwner owner)
00395 {
00396     /* We had better not be deleting CurrentResourceOwner ... */
00397     Assert(owner != CurrentResourceOwner);
00398 
00399     /* And it better not own any resources, either */
00400     Assert(owner->nbuffers == 0);
00401     Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
00402     Assert(owner->ncatrefs == 0);
00403     Assert(owner->ncatlistrefs == 0);
00404     Assert(owner->nrelrefs == 0);
00405     Assert(owner->nplanrefs == 0);
00406     Assert(owner->ntupdescs == 0);
00407     Assert(owner->nsnapshots == 0);
00408     Assert(owner->nfiles == 0);
00409 
00410     /*
00411      * Delete children.  The recursive call will delink the child from me, so
00412      * just iterate as long as there is a child.
00413      */
00414     while (owner->firstchild != NULL)
00415         ResourceOwnerDelete(owner->firstchild);
00416 
00417     /*
00418      * We delink the owner from its parent before deleting it, so that if
00419      * there's an error we won't have deleted/busted owners still attached to
00420      * the owner tree.  Better a leak than a crash.
00421      */
00422     ResourceOwnerNewParent(owner, NULL);
00423 
00424     /* And free the object. */
00425     if (owner->buffers)
00426         pfree(owner->buffers);
00427     if (owner->catrefs)
00428         pfree(owner->catrefs);
00429     if (owner->catlistrefs)
00430         pfree(owner->catlistrefs);
00431     if (owner->relrefs)
00432         pfree(owner->relrefs);
00433     if (owner->planrefs)
00434         pfree(owner->planrefs);
00435     if (owner->tupdescs)
00436         pfree(owner->tupdescs);
00437     if (owner->snapshots)
00438         pfree(owner->snapshots);
00439     if (owner->files)
00440         pfree(owner->files);
00441 
00442     pfree(owner);
00443 }
00444 
00445 /*
00446  * Fetch parent of a ResourceOwner (returns NULL if top-level owner)
00447  */
00448 ResourceOwner
00449 ResourceOwnerGetParent(ResourceOwner owner)
00450 {
00451     return owner->parent;
00452 }
00453 
00454 /*
00455  * Reassign a ResourceOwner to have a new parent
00456  */
00457 void
00458 ResourceOwnerNewParent(ResourceOwner owner,
00459                        ResourceOwner newparent)
00460 {
00461     ResourceOwner oldparent = owner->parent;
00462 
00463     if (oldparent)
00464     {
00465         if (owner == oldparent->firstchild)
00466             oldparent->firstchild = owner->nextchild;
00467         else
00468         {
00469             ResourceOwner child;
00470 
00471             for (child = oldparent->firstchild; child; child = child->nextchild)
00472             {
00473                 if (owner == child->nextchild)
00474                 {
00475                     child->nextchild = owner->nextchild;
00476                     break;
00477                 }
00478             }
00479         }
00480     }
00481 
00482     if (newparent)
00483     {
00484         Assert(owner != newparent);
00485         owner->parent = newparent;
00486         owner->nextchild = newparent->firstchild;
00487         newparent->firstchild = owner;
00488     }
00489     else
00490     {
00491         owner->parent = NULL;
00492         owner->nextchild = NULL;
00493     }
00494 }
00495 
00496 /*
00497  * Register or deregister callback functions for resource cleanup
00498  *
00499  * These functions are intended for use by dynamically loaded modules.
00500  * For built-in modules we generally just hardwire the appropriate calls.
00501  *
00502  * Note that the callback occurs post-commit or post-abort, so the callback
00503  * functions can only do noncritical cleanup.
00504  */
00505 void
00506 RegisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
00507 {
00508     ResourceReleaseCallbackItem *item;
00509 
00510     item = (ResourceReleaseCallbackItem *)
00511         MemoryContextAlloc(TopMemoryContext,
00512                            sizeof(ResourceReleaseCallbackItem));
00513     item->callback = callback;
00514     item->arg = arg;
00515     item->next = ResourceRelease_callbacks;
00516     ResourceRelease_callbacks = item;
00517 }
00518 
00519 void
00520 UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
00521 {
00522     ResourceReleaseCallbackItem *item;
00523     ResourceReleaseCallbackItem *prev;
00524 
00525     prev = NULL;
00526     for (item = ResourceRelease_callbacks; item; prev = item, item = item->next)
00527     {
00528         if (item->callback == callback && item->arg == arg)
00529         {
00530             if (prev)
00531                 prev->next = item->next;
00532             else
00533                 ResourceRelease_callbacks = item->next;
00534             pfree(item);
00535             break;
00536         }
00537     }
00538 }
00539 
00540 
00541 /*
00542  * Make sure there is room for at least one more entry in a ResourceOwner's
00543  * buffer array.
00544  *
00545  * This is separate from actually inserting an entry because if we run out
00546  * of memory, it's critical to do so *before* acquiring the resource.
00547  *
00548  * We allow the case owner == NULL because the bufmgr is sometimes invoked
00549  * outside any transaction (for example, during WAL recovery).
00550  */
00551 void
00552 ResourceOwnerEnlargeBuffers(ResourceOwner owner)
00553 {
00554     int         newmax;
00555 
00556     if (owner == NULL ||
00557         owner->nbuffers < owner->maxbuffers)
00558         return;                 /* nothing to do */
00559 
00560     if (owner->buffers == NULL)
00561     {
00562         newmax = 16;
00563         owner->buffers = (Buffer *)
00564             MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Buffer));
00565         owner->maxbuffers = newmax;
00566     }
00567     else
00568     {
00569         newmax = owner->maxbuffers * 2;
00570         owner->buffers = (Buffer *)
00571             repalloc(owner->buffers, newmax * sizeof(Buffer));
00572         owner->maxbuffers = newmax;
00573     }
00574 }
00575 
00576 /*
00577  * Remember that a buffer pin is owned by a ResourceOwner
00578  *
00579  * Caller must have previously done ResourceOwnerEnlargeBuffers()
00580  *
00581  * We allow the case owner == NULL because the bufmgr is sometimes invoked
00582  * outside any transaction (for example, during WAL recovery).
00583  */
00584 void
00585 ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
00586 {
00587     if (owner != NULL)
00588     {
00589         Assert(owner->nbuffers < owner->maxbuffers);
00590         owner->buffers[owner->nbuffers] = buffer;
00591         owner->nbuffers++;
00592     }
00593 }
00594 
00595 /*
00596  * Forget that a buffer pin is owned by a ResourceOwner
00597  *
00598  * We allow the case owner == NULL because the bufmgr is sometimes invoked
00599  * outside any transaction (for example, during WAL recovery).
00600  */
00601 void
00602 ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
00603 {
00604     if (owner != NULL)
00605     {
00606         Buffer     *buffers = owner->buffers;
00607         int         nb1 = owner->nbuffers - 1;
00608         int         i;
00609 
00610         /*
00611          * Scan back-to-front because it's more likely we are releasing a
00612          * recently pinned buffer.  This isn't always the case of course, but
00613          * it's the way to bet.
00614          */
00615         for (i = nb1; i >= 0; i--)
00616         {
00617             if (buffers[i] == buffer)
00618             {
00619                 while (i < nb1)
00620                 {
00621                     buffers[i] = buffers[i + 1];
00622                     i++;
00623                 }
00624                 owner->nbuffers = nb1;
00625                 return;
00626             }
00627         }
00628         elog(ERROR, "buffer %d is not owned by resource owner %s",
00629              buffer, owner->name);
00630     }
00631 }
00632 
00633 /*
00634  * Remember that a Local Lock is owned by a ResourceOwner
00635  *
00636  * This is different from the other Remember functions in that the list of
00637  * locks is only a lossy cache. It can hold up to MAX_RESOWNER_LOCKS entries,
00638  * and when it overflows, we stop tracking locks. The point of only remembering
00639  * only up to MAX_RESOWNER_LOCKS entries is that if a lot of locks are held,
00640  * ResourceOwnerForgetLock doesn't need to scan through a large array to find
00641  * the entry.
00642  */
00643 void
00644 ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK * locallock)
00645 {
00646     if (owner->nlocks > MAX_RESOWNER_LOCKS)
00647         return;     /* we have already overflowed */
00648 
00649     if (owner->nlocks < MAX_RESOWNER_LOCKS)
00650         owner->locks[owner->nlocks] = locallock;
00651     else
00652     {
00653         /* overflowed */
00654     }
00655     owner->nlocks++;
00656 }
00657 
00658 /*
00659  * Forget that a Local Lock is owned by a ResourceOwner
00660  */
00661 void
00662 ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock)
00663 {
00664     int         i;
00665 
00666     if (owner->nlocks > MAX_RESOWNER_LOCKS)
00667         return;     /* we have overflowed */
00668 
00669     Assert(owner->nlocks > 0);
00670     for (i = owner->nlocks - 1; i >= 0; i--)
00671     {
00672         if (locallock == owner->locks[i])
00673         {
00674             owner->locks[i] = owner->locks[owner->nlocks - 1];
00675             owner->nlocks--;
00676             return;
00677         }
00678     }
00679     elog(ERROR, "lock reference %p is not owned by resource owner %s",
00680          locallock, owner->name);
00681 }
00682 
00683 /*
00684  * Make sure there is room for at least one more entry in a ResourceOwner's
00685  * catcache reference array.
00686  *
00687  * This is separate from actually inserting an entry because if we run out
00688  * of memory, it's critical to do so *before* acquiring the resource.
00689  */
00690 void
00691 ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
00692 {
00693     int         newmax;
00694 
00695     if (owner->ncatrefs < owner->maxcatrefs)
00696         return;                 /* nothing to do */
00697 
00698     if (owner->catrefs == NULL)
00699     {
00700         newmax = 16;
00701         owner->catrefs = (HeapTuple *)
00702             MemoryContextAlloc(TopMemoryContext, newmax * sizeof(HeapTuple));
00703         owner->maxcatrefs = newmax;
00704     }
00705     else
00706     {
00707         newmax = owner->maxcatrefs * 2;
00708         owner->catrefs = (HeapTuple *)
00709             repalloc(owner->catrefs, newmax * sizeof(HeapTuple));
00710         owner->maxcatrefs = newmax;
00711     }
00712 }
00713 
00714 /*
00715  * Remember that a catcache reference is owned by a ResourceOwner
00716  *
00717  * Caller must have previously done ResourceOwnerEnlargeCatCacheRefs()
00718  */
00719 void
00720 ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
00721 {
00722     Assert(owner->ncatrefs < owner->maxcatrefs);
00723     owner->catrefs[owner->ncatrefs] = tuple;
00724     owner->ncatrefs++;
00725 }
00726 
00727 /*
00728  * Forget that a catcache reference is owned by a ResourceOwner
00729  */
00730 void
00731 ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
00732 {
00733     HeapTuple  *catrefs = owner->catrefs;
00734     int         nc1 = owner->ncatrefs - 1;
00735     int         i;
00736 
00737     for (i = nc1; i >= 0; i--)
00738     {
00739         if (catrefs[i] == tuple)
00740         {
00741             while (i < nc1)
00742             {
00743                 catrefs[i] = catrefs[i + 1];
00744                 i++;
00745             }
00746             owner->ncatrefs = nc1;
00747             return;
00748         }
00749     }
00750     elog(ERROR, "catcache reference %p is not owned by resource owner %s",
00751          tuple, owner->name);
00752 }
00753 
00754 /*
00755  * Make sure there is room for at least one more entry in a ResourceOwner's
00756  * catcache-list reference array.
00757  *
00758  * This is separate from actually inserting an entry because if we run out
00759  * of memory, it's critical to do so *before* acquiring the resource.
00760  */
00761 void
00762 ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
00763 {
00764     int         newmax;
00765 
00766     if (owner->ncatlistrefs < owner->maxcatlistrefs)
00767         return;                 /* nothing to do */
00768 
00769     if (owner->catlistrefs == NULL)
00770     {
00771         newmax = 16;
00772         owner->catlistrefs = (CatCList **)
00773             MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CatCList *));
00774         owner->maxcatlistrefs = newmax;
00775     }
00776     else
00777     {
00778         newmax = owner->maxcatlistrefs * 2;
00779         owner->catlistrefs = (CatCList **)
00780             repalloc(owner->catlistrefs, newmax * sizeof(CatCList *));
00781         owner->maxcatlistrefs = newmax;
00782     }
00783 }
00784 
00785 /*
00786  * Remember that a catcache-list reference is owned by a ResourceOwner
00787  *
00788  * Caller must have previously done ResourceOwnerEnlargeCatCacheListRefs()
00789  */
00790 void
00791 ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
00792 {
00793     Assert(owner->ncatlistrefs < owner->maxcatlistrefs);
00794     owner->catlistrefs[owner->ncatlistrefs] = list;
00795     owner->ncatlistrefs++;
00796 }
00797 
00798 /*
00799  * Forget that a catcache-list reference is owned by a ResourceOwner
00800  */
00801 void
00802 ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
00803 {
00804     CatCList  **catlistrefs = owner->catlistrefs;
00805     int         nc1 = owner->ncatlistrefs - 1;
00806     int         i;
00807 
00808     for (i = nc1; i >= 0; i--)
00809     {
00810         if (catlistrefs[i] == list)
00811         {
00812             while (i < nc1)
00813             {
00814                 catlistrefs[i] = catlistrefs[i + 1];
00815                 i++;
00816             }
00817             owner->ncatlistrefs = nc1;
00818             return;
00819         }
00820     }
00821     elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
00822          list, owner->name);
00823 }
00824 
00825 /*
00826  * Make sure there is room for at least one more entry in a ResourceOwner's
00827  * relcache reference array.
00828  *
00829  * This is separate from actually inserting an entry because if we run out
00830  * of memory, it's critical to do so *before* acquiring the resource.
00831  */
00832 void
00833 ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
00834 {
00835     int         newmax;
00836 
00837     if (owner->nrelrefs < owner->maxrelrefs)
00838         return;                 /* nothing to do */
00839 
00840     if (owner->relrefs == NULL)
00841     {
00842         newmax = 16;
00843         owner->relrefs = (Relation *)
00844             MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Relation));
00845         owner->maxrelrefs = newmax;
00846     }
00847     else
00848     {
00849         newmax = owner->maxrelrefs * 2;
00850         owner->relrefs = (Relation *)
00851             repalloc(owner->relrefs, newmax * sizeof(Relation));
00852         owner->maxrelrefs = newmax;
00853     }
00854 }
00855 
00856 /*
00857  * Remember that a relcache reference is owned by a ResourceOwner
00858  *
00859  * Caller must have previously done ResourceOwnerEnlargeRelationRefs()
00860  */
00861 void
00862 ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
00863 {
00864     Assert(owner->nrelrefs < owner->maxrelrefs);
00865     owner->relrefs[owner->nrelrefs] = rel;
00866     owner->nrelrefs++;
00867 }
00868 
00869 /*
00870  * Forget that a relcache reference is owned by a ResourceOwner
00871  */
00872 void
00873 ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel)
00874 {
00875     Relation   *relrefs = owner->relrefs;
00876     int         nr1 = owner->nrelrefs - 1;
00877     int         i;
00878 
00879     for (i = nr1; i >= 0; i--)
00880     {
00881         if (relrefs[i] == rel)
00882         {
00883             while (i < nr1)
00884             {
00885                 relrefs[i] = relrefs[i + 1];
00886                 i++;
00887             }
00888             owner->nrelrefs = nr1;
00889             return;
00890         }
00891     }
00892     elog(ERROR, "relcache reference %s is not owned by resource owner %s",
00893          RelationGetRelationName(rel), owner->name);
00894 }
00895 
00896 /*
00897  * Debugging subroutine
00898  */
00899 static void
00900 PrintRelCacheLeakWarning(Relation rel)
00901 {
00902     elog(WARNING, "relcache reference leak: relation \"%s\" not closed",
00903          RelationGetRelationName(rel));
00904 }
00905 
00906 /*
00907  * Make sure there is room for at least one more entry in a ResourceOwner's
00908  * plancache reference array.
00909  *
00910  * This is separate from actually inserting an entry because if we run out
00911  * of memory, it's critical to do so *before* acquiring the resource.
00912  */
00913 void
00914 ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
00915 {
00916     int         newmax;
00917 
00918     if (owner->nplanrefs < owner->maxplanrefs)
00919         return;                 /* nothing to do */
00920 
00921     if (owner->planrefs == NULL)
00922     {
00923         newmax = 16;
00924         owner->planrefs = (CachedPlan **)
00925             MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CachedPlan *));
00926         owner->maxplanrefs = newmax;
00927     }
00928     else
00929     {
00930         newmax = owner->maxplanrefs * 2;
00931         owner->planrefs = (CachedPlan **)
00932             repalloc(owner->planrefs, newmax * sizeof(CachedPlan *));
00933         owner->maxplanrefs = newmax;
00934     }
00935 }
00936 
00937 /*
00938  * Remember that a plancache reference is owned by a ResourceOwner
00939  *
00940  * Caller must have previously done ResourceOwnerEnlargePlanCacheRefs()
00941  */
00942 void
00943 ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
00944 {
00945     Assert(owner->nplanrefs < owner->maxplanrefs);
00946     owner->planrefs[owner->nplanrefs] = plan;
00947     owner->nplanrefs++;
00948 }
00949 
00950 /*
00951  * Forget that a plancache reference is owned by a ResourceOwner
00952  */
00953 void
00954 ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
00955 {
00956     CachedPlan **planrefs = owner->planrefs;
00957     int         np1 = owner->nplanrefs - 1;
00958     int         i;
00959 
00960     for (i = np1; i >= 0; i--)
00961     {
00962         if (planrefs[i] == plan)
00963         {
00964             while (i < np1)
00965             {
00966                 planrefs[i] = planrefs[i + 1];
00967                 i++;
00968             }
00969             owner->nplanrefs = np1;
00970             return;
00971         }
00972     }
00973     elog(ERROR, "plancache reference %p is not owned by resource owner %s",
00974          plan, owner->name);
00975 }
00976 
00977 /*
00978  * Debugging subroutine
00979  */
00980 static void
00981 PrintPlanCacheLeakWarning(CachedPlan *plan)
00982 {
00983     elog(WARNING, "plancache reference leak: plan %p not closed", plan);
00984 }
00985 
00986 /*
00987  * Make sure there is room for at least one more entry in a ResourceOwner's
00988  * tupdesc reference array.
00989  *
00990  * This is separate from actually inserting an entry because if we run out
00991  * of memory, it's critical to do so *before* acquiring the resource.
00992  */
00993 void
00994 ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
00995 {
00996     int         newmax;
00997 
00998     if (owner->ntupdescs < owner->maxtupdescs)
00999         return;                 /* nothing to do */
01000 
01001     if (owner->tupdescs == NULL)
01002     {
01003         newmax = 16;
01004         owner->tupdescs = (TupleDesc *)
01005             MemoryContextAlloc(TopMemoryContext, newmax * sizeof(TupleDesc));
01006         owner->maxtupdescs = newmax;
01007     }
01008     else
01009     {
01010         newmax = owner->maxtupdescs * 2;
01011         owner->tupdescs = (TupleDesc *)
01012             repalloc(owner->tupdescs, newmax * sizeof(TupleDesc));
01013         owner->maxtupdescs = newmax;
01014     }
01015 }
01016 
01017 /*
01018  * Remember that a tupdesc reference is owned by a ResourceOwner
01019  *
01020  * Caller must have previously done ResourceOwnerEnlargeTupleDescs()
01021  */
01022 void
01023 ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
01024 {
01025     Assert(owner->ntupdescs < owner->maxtupdescs);
01026     owner->tupdescs[owner->ntupdescs] = tupdesc;
01027     owner->ntupdescs++;
01028 }
01029 
01030 /*
01031  * Forget that a tupdesc reference is owned by a ResourceOwner
01032  */
01033 void
01034 ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
01035 {
01036     TupleDesc  *tupdescs = owner->tupdescs;
01037     int         nt1 = owner->ntupdescs - 1;
01038     int         i;
01039 
01040     for (i = nt1; i >= 0; i--)
01041     {
01042         if (tupdescs[i] == tupdesc)
01043         {
01044             while (i < nt1)
01045             {
01046                 tupdescs[i] = tupdescs[i + 1];
01047                 i++;
01048             }
01049             owner->ntupdescs = nt1;
01050             return;
01051         }
01052     }
01053     elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
01054          tupdesc, owner->name);
01055 }
01056 
01057 /*
01058  * Debugging subroutine
01059  */
01060 static void
01061 PrintTupleDescLeakWarning(TupleDesc tupdesc)
01062 {
01063     elog(WARNING,
01064          "TupleDesc reference leak: TupleDesc %p (%u,%d) still referenced",
01065          tupdesc, tupdesc->tdtypeid, tupdesc->tdtypmod);
01066 }
01067 
01068 /*
01069  * Make sure there is room for at least one more entry in a ResourceOwner's
01070  * snapshot reference array.
01071  *
01072  * This is separate from actually inserting an entry because if we run out
01073  * of memory, it's critical to do so *before* acquiring the resource.
01074  */
01075 void
01076 ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
01077 {
01078     int         newmax;
01079 
01080     if (owner->nsnapshots < owner->maxsnapshots)
01081         return;                 /* nothing to do */
01082 
01083     if (owner->snapshots == NULL)
01084     {
01085         newmax = 16;
01086         owner->snapshots = (Snapshot *)
01087             MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Snapshot));
01088         owner->maxsnapshots = newmax;
01089     }
01090     else
01091     {
01092         newmax = owner->maxsnapshots * 2;
01093         owner->snapshots = (Snapshot *)
01094             repalloc(owner->snapshots, newmax * sizeof(Snapshot));
01095         owner->maxsnapshots = newmax;
01096     }
01097 }
01098 
01099 /*
01100  * Remember that a snapshot reference is owned by a ResourceOwner
01101  *
01102  * Caller must have previously done ResourceOwnerEnlargeSnapshots()
01103  */
01104 void
01105 ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
01106 {
01107     Assert(owner->nsnapshots < owner->maxsnapshots);
01108     owner->snapshots[owner->nsnapshots] = snapshot;
01109     owner->nsnapshots++;
01110 }
01111 
01112 /*
01113  * Forget that a snapshot reference is owned by a ResourceOwner
01114  */
01115 void
01116 ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snapshot)
01117 {
01118     Snapshot   *snapshots = owner->snapshots;
01119     int         ns1 = owner->nsnapshots - 1;
01120     int         i;
01121 
01122     for (i = ns1; i >= 0; i--)
01123     {
01124         if (snapshots[i] == snapshot)
01125         {
01126             while (i < ns1)
01127             {
01128                 snapshots[i] = snapshots[i + 1];
01129                 i++;
01130             }
01131             owner->nsnapshots = ns1;
01132             return;
01133         }
01134     }
01135     elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
01136          snapshot, owner->name);
01137 }
01138 
01139 /*
01140  * Debugging subroutine
01141  */
01142 static void
01143 PrintSnapshotLeakWarning(Snapshot snapshot)
01144 {
01145     elog(WARNING,
01146          "Snapshot reference leak: Snapshot %p still referenced",
01147          snapshot);
01148 }
01149 
01150 
01151 /*
01152  * Make sure there is room for at least one more entry in a ResourceOwner's
01153  * files reference array.
01154  *
01155  * This is separate from actually inserting an entry because if we run out
01156  * of memory, it's critical to do so *before* acquiring the resource.
01157  */
01158 void
01159 ResourceOwnerEnlargeFiles(ResourceOwner owner)
01160 {
01161     int         newmax;
01162 
01163     if (owner->nfiles < owner->maxfiles)
01164         return;                 /* nothing to do */
01165 
01166     if (owner->files == NULL)
01167     {
01168         newmax = 16;
01169         owner->files = (File *)
01170             MemoryContextAlloc(TopMemoryContext, newmax * sizeof(File));
01171         owner->maxfiles = newmax;
01172     }
01173     else
01174     {
01175         newmax = owner->maxfiles * 2;
01176         owner->files = (File *)
01177             repalloc(owner->files, newmax * sizeof(File));
01178         owner->maxfiles = newmax;
01179     }
01180 }
01181 
01182 /*
01183  * Remember that a temporary file is owned by a ResourceOwner
01184  *
01185  * Caller must have previously done ResourceOwnerEnlargeFiles()
01186  */
01187 void
01188 ResourceOwnerRememberFile(ResourceOwner owner, File file)
01189 {
01190     Assert(owner->nfiles < owner->maxfiles);
01191     owner->files[owner->nfiles] = file;
01192     owner->nfiles++;
01193 }
01194 
01195 /*
01196  * Forget that a temporary file is owned by a ResourceOwner
01197  */
01198 void
01199 ResourceOwnerForgetFile(ResourceOwner owner, File file)
01200 {
01201     File       *files = owner->files;
01202     int         ns1 = owner->nfiles - 1;
01203     int         i;
01204 
01205     for (i = ns1; i >= 0; i--)
01206     {
01207         if (files[i] == file)
01208         {
01209             while (i < ns1)
01210             {
01211                 files[i] = files[i + 1];
01212                 i++;
01213             }
01214             owner->nfiles = ns1;
01215             return;
01216         }
01217     }
01218     elog(ERROR, "temporery file %d is not owned by resource owner %s",
01219          file, owner->name);
01220 }
01221 
01222 
01223 /*
01224  * Debugging subroutine
01225  */
01226 static void
01227 PrintFileLeakWarning(File file)
01228 {
01229     elog(WARNING,
01230          "temporary file leak: File %d still referenced",
01231          file);
01232 }