Logo Search packages:      
Sourcecode: postgresql-8.4 version File versions  Download package

resowner.c

/*-------------------------------------------------------------------------
 *
 * resowner.c
 *      POSTGRES resource owner management code.
 *
 * Query-lifespan resources are tracked by associating them with
 * ResourceOwner objects.  This provides a simple mechanism for ensuring
 * that such resources are freed at the right time.
 * See utils/resowner/README for more info.
 *
 *
 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *      $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.32.2.1 2009/12/03 11:03:35 heikki Exp $
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/hash.h"
#include "storage/bufmgr.h"
#include "storage/proc.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/resowner.h"
#include "utils/snapmgr.h"


/*
 * ResourceOwner objects look like this
 */
typedef struct ResourceOwnerData
{
      ResourceOwner parent;         /* NULL if no parent (toplevel owner) */
      ResourceOwner firstchild;     /* head of linked list of children */
      ResourceOwner nextchild;      /* next child of same parent */
      const char *name;             /* name (just for debugging) */

      /* We have built-in support for remembering owned buffers */
      int               nbuffers;         /* number of owned buffer pins */
      Buffer         *buffers;            /* dynamically allocated array */
      int               maxbuffers;       /* currently allocated array size */

      /* We have built-in support for remembering catcache references */
      int               ncatrefs;         /* number of owned catcache pins */
      HeapTuple  *catrefs;          /* dynamically allocated array */
      int               maxcatrefs;       /* currently allocated array size */

      int               ncatlistrefs;     /* number of owned catcache-list pins */
      CatCList  **catlistrefs;      /* dynamically allocated array */
      int               maxcatlistrefs; /* currently allocated array size */

      /* We have built-in support for remembering relcache references */
      int               nrelrefs;         /* number of owned relcache pins */
      Relation   *relrefs;          /* dynamically allocated array */
      int               maxrelrefs;       /* currently allocated array size */

      /* We have built-in support for remembering plancache references */
      int               nplanrefs;        /* number of owned plancache pins */
      CachedPlan **planrefs;        /* dynamically allocated array */
      int               maxplanrefs;      /* currently allocated array size */

      /* We have built-in support for remembering tupdesc references */
      int               ntupdescs;        /* number of owned tupdesc references */
      TupleDesc  *tupdescs;         /* dynamically allocated array */
      int               maxtupdescs;      /* currently allocated array size */

      /* We have built-in support for remembering snapshot references */
      int               nsnapshots;       /* number of owned snapshot references */
      Snapshot   *snapshots;        /* dynamically allocated array */
      int               maxsnapshots;     /* currently allocated array size */

      /* We have built-in support for remembering open temporary files */
      int               nfiles;                 /* number of owned temporary files */
      File     *files;              /* dynamically allocated array */
      int               maxfiles;         /* currently allocated array size */
} ResourceOwnerData;


/*****************************************************************************
 *      GLOBAL MEMORY                                                                                        *
 *****************************************************************************/

ResourceOwner CurrentResourceOwner = NULL;
ResourceOwner CurTransactionResourceOwner = NULL;
ResourceOwner TopTransactionResourceOwner = NULL;

/*
 * List of add-on callbacks for resource releasing
 */
typedef struct ResourceReleaseCallbackItem
{
      struct ResourceReleaseCallbackItem *next;
      ResourceReleaseCallback callback;
      void     *arg;
} ResourceReleaseCallbackItem;

static ResourceReleaseCallbackItem *ResourceRelease_callbacks = NULL;


/* Internal routines */
static void ResourceOwnerReleaseInternal(ResourceOwner owner,
                                           ResourceReleasePhase phase,
                                           bool isCommit,
                                           bool isTopLevel);
static void PrintRelCacheLeakWarning(Relation rel);
static void PrintPlanCacheLeakWarning(CachedPlan *plan);
static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
static void PrintSnapshotLeakWarning(Snapshot snapshot);
static void PrintFileLeakWarning(File file);


/*****************************************************************************
 *      EXPORTED ROUTINES                                                                                    *
 *****************************************************************************/


/*
 * ResourceOwnerCreate
 *          Create an empty ResourceOwner.
 *
 * All ResourceOwner objects are kept in TopMemoryContext, since they should
 * only be freed explicitly.
 */
ResourceOwner
ResourceOwnerCreate(ResourceOwner parent, const char *name)
{
      ResourceOwner owner;

      owner = (ResourceOwner) MemoryContextAllocZero(TopMemoryContext,
                                                                           sizeof(ResourceOwnerData));
      owner->name = name;

      if (parent)
      {
            owner->parent = parent;
            owner->nextchild = parent->firstchild;
            parent->firstchild = owner;
      }

      return owner;
}

/*
 * ResourceOwnerRelease
 *          Release all resources owned by a ResourceOwner and its descendants,
 *          but don't delete the owner objects themselves.
 *
 * Note that this executes just one phase of release, and so typically
 * must be called three times.      We do it this way because (a) we want to
 * do all the recursion separately for each phase, thereby preserving
 * the needed order of operations; and (b) xact.c may have other operations
 * to do between the phases.
 *
 * phase: release phase to execute
 * isCommit: true for successful completion of a query or transaction,
 *                false for unsuccessful
 * isTopLevel: true if completing a main transaction, else false
 *
 * isCommit is passed because some modules may expect that their resources
 * were all released already if the transaction or portal finished normally.
 * If so it is reasonable to give a warning (NOT an error) should any
 * unreleased resources be present.  When isCommit is false, such warnings
 * are generally inappropriate.
 *
 * isTopLevel is passed when we are releasing TopTransactionResourceOwner
 * at completion of a main transaction.  This generally means that *all*
 * resources will be released, and so we can optimize things a bit.
 */
void
ResourceOwnerRelease(ResourceOwner owner,
                               ResourceReleasePhase phase,
                               bool isCommit,
                               bool isTopLevel)
{
      /* Rather than PG_TRY at every level of recursion, set it up once */
      ResourceOwner save;

      save = CurrentResourceOwner;
      PG_TRY();
      {
            ResourceOwnerReleaseInternal(owner, phase, isCommit, isTopLevel);
      }
      PG_CATCH();
      {
            CurrentResourceOwner = save;
            PG_RE_THROW();
      }
      PG_END_TRY();
      CurrentResourceOwner = save;
}

static void
ResourceOwnerReleaseInternal(ResourceOwner owner,
                                           ResourceReleasePhase phase,
                                           bool isCommit,
                                           bool isTopLevel)
{
      ResourceOwner child;
      ResourceOwner save;
      ResourceReleaseCallbackItem *item;

      /* Recurse to handle descendants */
      for (child = owner->firstchild; child != NULL; child = child->nextchild)
            ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel);

      /*
       * Make CurrentResourceOwner point to me, so that ReleaseBuffer etc don't
       * get confused.  We needn't PG_TRY here because the outermost level will
       * fix it on error abort.
       */
      save = CurrentResourceOwner;
      CurrentResourceOwner = owner;

      if (phase == RESOURCE_RELEASE_BEFORE_LOCKS)
      {
            /*
             * Release buffer pins.  Note that ReleaseBuffer will remove the
             * buffer entry from my list, so I just have to iterate till there are
             * none.
             *
             * During a commit, there shouldn't be any remaining pins --- that
             * would indicate failure to clean up the executor correctly --- so
             * issue warnings.      In the abort case, just clean up quietly.
             *
             * We are careful to do the releasing back-to-front, so as to avoid
             * O(N^2) behavior in ResourceOwnerForgetBuffer().
             */
            while (owner->nbuffers > 0)
            {
                  if (isCommit)
                        PrintBufferLeakWarning(owner->buffers[owner->nbuffers - 1]);
                  ReleaseBuffer(owner->buffers[owner->nbuffers - 1]);
            }

            /*
             * Release relcache references.  Note that RelationClose will remove
             * the relref entry from my list, so I just have to iterate till there
             * are none.
             *
             * As with buffer pins, warn if any are left at commit time, and
             * release back-to-front for speed.
             */
            while (owner->nrelrefs > 0)
            {
                  if (isCommit)
                        PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]);
                  RelationClose(owner->relrefs[owner->nrelrefs - 1]);
            }
      }
      else if (phase == RESOURCE_RELEASE_LOCKS)
      {
            if (isTopLevel)
            {
                  /*
                   * For a top-level xact we are going to release all locks (or at
                   * least all non-session locks), so just do a single lmgr call at
                   * the top of the recursion.
                   */
                  if (owner == TopTransactionResourceOwner)
                        ProcReleaseLocks(isCommit);
            }
            else
            {
                  /*
                   * Release locks retail.  Note that if we are committing a
                   * subtransaction, we do NOT release its locks yet, but transfer
                   * them to the parent.
                   */
                  Assert(owner->parent != NULL);
                  if (isCommit)
                        LockReassignCurrentOwner();
                  else
                        LockReleaseCurrentOwner();
            }
      }
      else if (phase == RESOURCE_RELEASE_AFTER_LOCKS)
      {
            /*
             * Release catcache references.  Note that ReleaseCatCache will remove
             * the catref entry from my list, so I just have to iterate till there
             * are none.
             *
             * As with buffer pins, warn if any are left at commit time, and
             * release back-to-front for speed.
             */
            while (owner->ncatrefs > 0)
            {
                  if (isCommit)
                        PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
                  ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
            }
            /* Ditto for catcache lists */
            while (owner->ncatlistrefs > 0)
            {
                  if (isCommit)
                        PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
                  ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
            }
            /* Ditto for plancache references */
            while (owner->nplanrefs > 0)
            {
                  if (isCommit)
                        PrintPlanCacheLeakWarning(owner->planrefs[owner->nplanrefs - 1]);
                  ReleaseCachedPlan(owner->planrefs[owner->nplanrefs - 1], true);
            }
            /* Ditto for tupdesc references */
            while (owner->ntupdescs > 0)
            {
                  if (isCommit)
                        PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]);
                  DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1]);
            }
            /* Ditto for snapshot references */
            while (owner->nsnapshots > 0)
            {
                  if (isCommit)
                        PrintSnapshotLeakWarning(owner->snapshots[owner->nsnapshots - 1]);
                  UnregisterSnapshot(owner->snapshots[owner->nsnapshots - 1]);
            }

            /* Ditto for temporary files */
            while (owner->nfiles > 0)
            {
                  if (isCommit)
                        PrintFileLeakWarning(owner->files[owner->nfiles - 1]);
                  FileClose(owner->files[owner->nfiles - 1]);
            }

            /* Clean up index scans too */
            ReleaseResources_hash();
      }

      /* Let add-on modules get a chance too */
      for (item = ResourceRelease_callbacks; item; item = item->next)
            (*item->callback) (phase, isCommit, isTopLevel, item->arg);

      CurrentResourceOwner = save;
}

/*
 * ResourceOwnerDelete
 *          Delete an owner object and its descendants.
 *
 * The caller must have already released all resources in the object tree.
 */
void
ResourceOwnerDelete(ResourceOwner owner)
{
      /* We had better not be deleting CurrentResourceOwner ... */
      Assert(owner != CurrentResourceOwner);

      /* And it better not own any resources, either */
      Assert(owner->nbuffers == 0);
      Assert(owner->ncatrefs == 0);
      Assert(owner->ncatlistrefs == 0);
      Assert(owner->nrelrefs == 0);
      Assert(owner->nplanrefs == 0);
      Assert(owner->ntupdescs == 0);
      Assert(owner->nsnapshots == 0);
      Assert(owner->nfiles == 0);

      /*
       * Delete children.  The recursive call will delink the child from me, so
       * just iterate as long as there is a child.
       */
      while (owner->firstchild != NULL)
            ResourceOwnerDelete(owner->firstchild);

      /*
       * We delink the owner from its parent before deleting it, so that if
       * there's an error we won't have deleted/busted owners still attached to
       * the owner tree.      Better a leak than a crash.
       */
      ResourceOwnerNewParent(owner, NULL);

      /* And free the object. */
      if (owner->buffers)
            pfree(owner->buffers);
      if (owner->catrefs)
            pfree(owner->catrefs);
      if (owner->catlistrefs)
            pfree(owner->catlistrefs);
      if (owner->relrefs)
            pfree(owner->relrefs);
      if (owner->planrefs)
            pfree(owner->planrefs);
      if (owner->tupdescs)
            pfree(owner->tupdescs);
      if (owner->snapshots)
            pfree(owner->snapshots);
      if (owner->files)
            pfree(owner->files);

      pfree(owner);
}

/*
 * Fetch parent of a ResourceOwner (returns NULL if top-level owner)
 */
ResourceOwner
ResourceOwnerGetParent(ResourceOwner owner)
{
      return owner->parent;
}

/*
 * Reassign a ResourceOwner to have a new parent
 */
void
ResourceOwnerNewParent(ResourceOwner owner,
                                 ResourceOwner newparent)
{
      ResourceOwner oldparent = owner->parent;

      if (oldparent)
      {
            if (owner == oldparent->firstchild)
                  oldparent->firstchild = owner->nextchild;
            else
            {
                  ResourceOwner child;

                  for (child = oldparent->firstchild; child; child = child->nextchild)
                  {
                        if (owner == child->nextchild)
                        {
                              child->nextchild = owner->nextchild;
                              break;
                        }
                  }
            }
      }

      if (newparent)
      {
            Assert(owner != newparent);
            owner->parent = newparent;
            owner->nextchild = newparent->firstchild;
            newparent->firstchild = owner;
      }
      else
      {
            owner->parent = NULL;
            owner->nextchild = NULL;
      }
}

/*
 * Register or deregister callback functions for resource cleanup
 *
 * These functions are intended for use by dynamically loaded modules.
 * For built-in modules we generally just hardwire the appropriate calls.
 *
 * Note that the callback occurs post-commit or post-abort, so the callback
 * functions can only do noncritical cleanup.
 */
void
RegisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
{
      ResourceReleaseCallbackItem *item;

      item = (ResourceReleaseCallbackItem *)
            MemoryContextAlloc(TopMemoryContext,
                                       sizeof(ResourceReleaseCallbackItem));
      item->callback = callback;
      item->arg = arg;
      item->next = ResourceRelease_callbacks;
      ResourceRelease_callbacks = item;
}

void
UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
{
      ResourceReleaseCallbackItem *item;
      ResourceReleaseCallbackItem *prev;

      prev = NULL;
      for (item = ResourceRelease_callbacks; item; prev = item, item = item->next)
      {
            if (item->callback == callback && item->arg == arg)
            {
                  if (prev)
                        prev->next = item->next;
                  else
                        ResourceRelease_callbacks = item->next;
                  pfree(item);
                  break;
            }
      }
}


/*
 * Make sure there is room for at least one more entry in a ResourceOwner's
 * buffer array.
 *
 * This is separate from actually inserting an entry because if we run out
 * of memory, it's critical to do so *before* acquiring the resource.
 *
 * We allow the case owner == NULL because the bufmgr is sometimes invoked
 * outside any transaction (for example, during WAL recovery).
 */
void
ResourceOwnerEnlargeBuffers(ResourceOwner owner)
{
      int               newmax;

      if (owner == NULL ||
            owner->nbuffers < owner->maxbuffers)
            return;                             /* nothing to do */

      if (owner->buffers == NULL)
      {
            newmax = 16;
            owner->buffers = (Buffer *)
                  MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Buffer));
            owner->maxbuffers = newmax;
      }
      else
      {
            newmax = owner->maxbuffers * 2;
            owner->buffers = (Buffer *)
                  repalloc(owner->buffers, newmax * sizeof(Buffer));
            owner->maxbuffers = newmax;
      }
}

/*
 * Remember that a buffer pin is owned by a ResourceOwner
 *
 * Caller must have previously done ResourceOwnerEnlargeBuffers()
 *
 * We allow the case owner == NULL because the bufmgr is sometimes invoked
 * outside any transaction (for example, during WAL recovery).
 */
void
ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
{
      if (owner != NULL)
      {
            Assert(owner->nbuffers < owner->maxbuffers);
            owner->buffers[owner->nbuffers] = buffer;
            owner->nbuffers++;
      }
}

/*
 * Forget that a buffer pin is owned by a ResourceOwner
 *
 * We allow the case owner == NULL because the bufmgr is sometimes invoked
 * outside any transaction (for example, during WAL recovery).
 */
void
ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
{
      if (owner != NULL)
      {
            Buffer         *buffers = owner->buffers;
            int               nb1 = owner->nbuffers - 1;
            int               i;

            /*
             * Scan back-to-front because it's more likely we are releasing a
             * recently pinned buffer.    This isn't always the case of course, but
             * it's the way to bet.
             */
            for (i = nb1; i >= 0; i--)
            {
                  if (buffers[i] == buffer)
                  {
                        while (i < nb1)
                        {
                              buffers[i] = buffers[i + 1];
                              i++;
                        }
                        owner->nbuffers = nb1;
                        return;
                  }
            }
            elog(ERROR, "buffer %d is not owned by resource owner %s",
                   buffer, owner->name);
      }
}

/*
 * Make sure there is room for at least one more entry in a ResourceOwner's
 * catcache reference array.
 *
 * This is separate from actually inserting an entry because if we run out
 * of memory, it's critical to do so *before* acquiring the resource.
 */
void
ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
{
      int               newmax;

      if (owner->ncatrefs < owner->maxcatrefs)
            return;                             /* nothing to do */

      if (owner->catrefs == NULL)
      {
            newmax = 16;
            owner->catrefs = (HeapTuple *)
                  MemoryContextAlloc(TopMemoryContext, newmax * sizeof(HeapTuple));
            owner->maxcatrefs = newmax;
      }
      else
      {
            newmax = owner->maxcatrefs * 2;
            owner->catrefs = (HeapTuple *)
                  repalloc(owner->catrefs, newmax * sizeof(HeapTuple));
            owner->maxcatrefs = newmax;
      }
}

/*
 * Remember that a catcache reference is owned by a ResourceOwner
 *
 * Caller must have previously done ResourceOwnerEnlargeCatCacheRefs()
 */
void
ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
{
      Assert(owner->ncatrefs < owner->maxcatrefs);
      owner->catrefs[owner->ncatrefs] = tuple;
      owner->ncatrefs++;
}

/*
 * Forget that a catcache reference is owned by a ResourceOwner
 */
void
ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
{
      HeapTuple  *catrefs = owner->catrefs;
      int               nc1 = owner->ncatrefs - 1;
      int               i;

      for (i = nc1; i >= 0; i--)
      {
            if (catrefs[i] == tuple)
            {
                  while (i < nc1)
                  {
                        catrefs[i] = catrefs[i + 1];
                        i++;
                  }
                  owner->ncatrefs = nc1;
                  return;
            }
      }
      elog(ERROR, "catcache reference %p is not owned by resource owner %s",
             tuple, owner->name);
}

/*
 * Make sure there is room for at least one more entry in a ResourceOwner's
 * catcache-list reference array.
 *
 * This is separate from actually inserting an entry because if we run out
 * of memory, it's critical to do so *before* acquiring the resource.
 */
void
ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
{
      int               newmax;

      if (owner->ncatlistrefs < owner->maxcatlistrefs)
            return;                             /* nothing to do */

      if (owner->catlistrefs == NULL)
      {
            newmax = 16;
            owner->catlistrefs = (CatCList **)
                  MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CatCList *));
            owner->maxcatlistrefs = newmax;
      }
      else
      {
            newmax = owner->maxcatlistrefs * 2;
            owner->catlistrefs = (CatCList **)
                  repalloc(owner->catlistrefs, newmax * sizeof(CatCList *));
            owner->maxcatlistrefs = newmax;
      }
}

/*
 * Remember that a catcache-list reference is owned by a ResourceOwner
 *
 * Caller must have previously done ResourceOwnerEnlargeCatCacheListRefs()
 */
void
ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
{
      Assert(owner->ncatlistrefs < owner->maxcatlistrefs);
      owner->catlistrefs[owner->ncatlistrefs] = list;
      owner->ncatlistrefs++;
}

/*
 * Forget that a catcache-list reference is owned by a ResourceOwner
 */
void
ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
{
      CatCList  **catlistrefs = owner->catlistrefs;
      int               nc1 = owner->ncatlistrefs - 1;
      int               i;

      for (i = nc1; i >= 0; i--)
      {
            if (catlistrefs[i] == list)
            {
                  while (i < nc1)
                  {
                        catlistrefs[i] = catlistrefs[i + 1];
                        i++;
                  }
                  owner->ncatlistrefs = nc1;
                  return;
            }
      }
      elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
             list, owner->name);
}

/*
 * Make sure there is room for at least one more entry in a ResourceOwner's
 * relcache reference array.
 *
 * This is separate from actually inserting an entry because if we run out
 * of memory, it's critical to do so *before* acquiring the resource.
 */
void
ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
{
      int               newmax;

      if (owner->nrelrefs < owner->maxrelrefs)
            return;                             /* nothing to do */

      if (owner->relrefs == NULL)
      {
            newmax = 16;
            owner->relrefs = (Relation *)
                  MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Relation));
            owner->maxrelrefs = newmax;
      }
      else
      {
            newmax = owner->maxrelrefs * 2;
            owner->relrefs = (Relation *)
                  repalloc(owner->relrefs, newmax * sizeof(Relation));
            owner->maxrelrefs = newmax;
      }
}

/*
 * Remember that a relcache reference is owned by a ResourceOwner
 *
 * Caller must have previously done ResourceOwnerEnlargeRelationRefs()
 */
void
ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
{
      Assert(owner->nrelrefs < owner->maxrelrefs);
      owner->relrefs[owner->nrelrefs] = rel;
      owner->nrelrefs++;
}

/*
 * Forget that a relcache reference is owned by a ResourceOwner
 */
void
ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel)
{
      Relation   *relrefs = owner->relrefs;
      int               nr1 = owner->nrelrefs - 1;
      int               i;

      for (i = nr1; i >= 0; i--)
      {
            if (relrefs[i] == rel)
            {
                  while (i < nr1)
                  {
                        relrefs[i] = relrefs[i + 1];
                        i++;
                  }
                  owner->nrelrefs = nr1;
                  return;
            }
      }
      elog(ERROR, "relcache reference %s is not owned by resource owner %s",
             RelationGetRelationName(rel), owner->name);
}

/*
 * Debugging subroutine
 */
static void
PrintRelCacheLeakWarning(Relation rel)
{
      elog(WARNING, "relcache reference leak: relation \"%s\" not closed",
             RelationGetRelationName(rel));
}

/*
 * Make sure there is room for at least one more entry in a ResourceOwner's
 * plancache reference array.
 *
 * This is separate from actually inserting an entry because if we run out
 * of memory, it's critical to do so *before* acquiring the resource.
 */
void
ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
{
      int               newmax;

      if (owner->nplanrefs < owner->maxplanrefs)
            return;                             /* nothing to do */

      if (owner->planrefs == NULL)
      {
            newmax = 16;
            owner->planrefs = (CachedPlan **)
                  MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CachedPlan *));
            owner->maxplanrefs = newmax;
      }
      else
      {
            newmax = owner->maxplanrefs * 2;
            owner->planrefs = (CachedPlan **)
                  repalloc(owner->planrefs, newmax * sizeof(CachedPlan *));
            owner->maxplanrefs = newmax;
      }
}

/*
 * Remember that a plancache reference is owned by a ResourceOwner
 *
 * Caller must have previously done ResourceOwnerEnlargePlanCacheRefs()
 */
void
ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
{
      Assert(owner->nplanrefs < owner->maxplanrefs);
      owner->planrefs[owner->nplanrefs] = plan;
      owner->nplanrefs++;
}

/*
 * Forget that a plancache reference is owned by a ResourceOwner
 */
void
ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
{
      CachedPlan **planrefs = owner->planrefs;
      int               np1 = owner->nplanrefs - 1;
      int               i;

      for (i = np1; i >= 0; i--)
      {
            if (planrefs[i] == plan)
            {
                  while (i < np1)
                  {
                        planrefs[i] = planrefs[i + 1];
                        i++;
                  }
                  owner->nplanrefs = np1;
                  return;
            }
      }
      elog(ERROR, "plancache reference %p is not owned by resource owner %s",
             plan, owner->name);
}

/*
 * Debugging subroutine
 */
static void
PrintPlanCacheLeakWarning(CachedPlan *plan)
{
      elog(WARNING, "plancache reference leak: plan %p not closed", plan);
}

/*
 * Make sure there is room for at least one more entry in a ResourceOwner's
 * tupdesc reference array.
 *
 * This is separate from actually inserting an entry because if we run out
 * of memory, it's critical to do so *before* acquiring the resource.
 */
void
ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
{
      int               newmax;

      if (owner->ntupdescs < owner->maxtupdescs)
            return;                             /* nothing to do */

      if (owner->tupdescs == NULL)
      {
            newmax = 16;
            owner->tupdescs = (TupleDesc *)
                  MemoryContextAlloc(TopMemoryContext, newmax * sizeof(TupleDesc));
            owner->maxtupdescs = newmax;
      }
      else
      {
            newmax = owner->maxtupdescs * 2;
            owner->tupdescs = (TupleDesc *)
                  repalloc(owner->tupdescs, newmax * sizeof(TupleDesc));
            owner->maxtupdescs = newmax;
      }
}

/*
 * Remember that a tupdesc reference is owned by a ResourceOwner
 *
 * Caller must have previously done ResourceOwnerEnlargeTupleDescs()
 */
void
ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
{
      Assert(owner->ntupdescs < owner->maxtupdescs);
      owner->tupdescs[owner->ntupdescs] = tupdesc;
      owner->ntupdescs++;
}

/*
 * Forget that a tupdesc reference is owned by a ResourceOwner
 */
void
ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
{
      TupleDesc  *tupdescs = owner->tupdescs;
      int               nt1 = owner->ntupdescs - 1;
      int               i;

      for (i = nt1; i >= 0; i--)
      {
            if (tupdescs[i] == tupdesc)
            {
                  while (i < nt1)
                  {
                        tupdescs[i] = tupdescs[i + 1];
                        i++;
                  }
                  owner->ntupdescs = nt1;
                  return;
            }
      }
      elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
             tupdesc, owner->name);
}

/*
 * Debugging subroutine
 */
static void
PrintTupleDescLeakWarning(TupleDesc tupdesc)
{
      elog(WARNING,
             "TupleDesc reference leak: TupleDesc %p (%u,%d) still referenced",
             tupdesc, tupdesc->tdtypeid, tupdesc->tdtypmod);
}

/*
 * Make sure there is room for at least one more entry in a ResourceOwner's
 * snapshot reference array.
 *
 * This is separate from actually inserting an entry because if we run out
 * of memory, it's critical to do so *before* acquiring the resource.
 */
void
ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
{
      int               newmax;

      if (owner->nsnapshots < owner->maxsnapshots)
            return;                             /* nothing to do */

      if (owner->snapshots == NULL)
      {
            newmax = 16;
            owner->snapshots = (Snapshot *)
                  MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Snapshot));
            owner->maxsnapshots = newmax;
      }
      else
      {
            newmax = owner->maxsnapshots * 2;
            owner->snapshots = (Snapshot *)
                  repalloc(owner->snapshots, newmax * sizeof(Snapshot));
            owner->maxsnapshots = newmax;
      }
}

/*
 * Remember that a snapshot reference is owned by a ResourceOwner
 *
 * Caller must have previously done ResourceOwnerEnlargeSnapshots()
 */
void
ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
{
      Assert(owner->nsnapshots < owner->maxsnapshots);
      owner->snapshots[owner->nsnapshots] = snapshot;
      owner->nsnapshots++;
}

/*
 * Forget that a snapshot reference is owned by a ResourceOwner
 */
void
ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snapshot)
{
      Snapshot   *snapshots = owner->snapshots;
      int               ns1 = owner->nsnapshots - 1;
      int               i;

      for (i = ns1; i >= 0; i--)
      {
            if (snapshots[i] == snapshot)
            {
                  while (i < ns1)
                  {
                        snapshots[i] = snapshots[i + 1];
                        i++;
                  }
                  owner->nsnapshots = ns1;
                  return;
            }
      }
      elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
             snapshot, owner->name);
}

/*
 * Debugging subroutine
 */
static void
PrintSnapshotLeakWarning(Snapshot snapshot)
{
      elog(WARNING,
             "Snapshot reference leak: Snapshot %p still referenced",
             snapshot);
}


/*
 * Make sure there is room for at least one more entry in a ResourceOwner's
 * files reference array.
 *
 * This is separate from actually inserting an entry because if we run out
 * of memory, it's critical to do so *before* acquiring the resource.
 */
void
ResourceOwnerEnlargeFiles(ResourceOwner owner)
{
      int               newmax;

      if (owner->nfiles < owner->maxfiles)
            return;                             /* nothing to do */

      if (owner->files == NULL)
      {
            newmax = 16;
            owner->files = (File *)
                  MemoryContextAlloc(TopMemoryContext, newmax * sizeof(File));
            owner->maxfiles = newmax;
      }
      else
      {
            newmax = owner->maxfiles * 2;
            owner->files = (File *)
                  repalloc(owner->files, newmax * sizeof(File));
            owner->maxfiles = newmax;
      }
}

/*
 * Remember that a temporary file is owned by a ResourceOwner
 *
 * Caller must have previously done ResourceOwnerEnlargeFiles()
 */
void
ResourceOwnerRememberFile(ResourceOwner owner, File file)
{
      Assert(owner->nfiles < owner->maxfiles);
      owner->files[owner->nfiles] = file;
      owner->nfiles++;
}

/*
 * Forget that a temporary file is owned by a ResourceOwner
 */
void
ResourceOwnerForgetFile(ResourceOwner owner, File file)
{
      File     *files = owner->files;
      int               ns1 = owner->nfiles - 1;
      int               i;

      for (i = ns1; i >= 0; i--)
      {
            if (files[i] == file)
            {
                  while (i < ns1)
                  {
                        files[i] = files[i + 1];
                        i++;
                  }
                  owner->nfiles = ns1;
                  return;
            }
      }
      elog(ERROR, "temporery file %d is not owned by resource owner %s",
             file, owner->name);
}


/*
 * Debugging subroutine
 */
static void
PrintFileLeakWarning(File file)
{
      elog(WARNING,
             "temporary file leak: File %d still referenced",
             file);
}

Generated by  Doxygen 1.6.0   Back to index