mirror of
https://github.com/postgres/postgres.git
synced 2025-07-07 00:36:50 +03:00
Fix problems with cached tuple descriptors disappearing while still in use
by creating a reference-count mechanism, similar to what we did a long time ago for catcache entries. The back branches have an ugly solution involving lots of extra copies, but this way is more efficient. Reference counting is only applied to tupdescs that are actually in caches --- there seems no need to use it for tupdescs that are generated in the executor, since they'll go away during plan shutdown by virtue of being in the per-query memory context. Neil Conway and Tom Lane
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
$PostgreSQL: pgsql/src/backend/utils/resowner/README,v 1.3 2004/08/25 18:43:43 tgl Exp $
|
||||
$PostgreSQL: pgsql/src/backend/utils/resowner/README,v 1.4 2006/06/16 18:42:23 tgl Exp $
|
||||
|
||||
Notes about resource owners
|
||||
---------------------------
|
||||
@ -61,9 +61,9 @@ ResourceOwner transfers lock ownership to the parent instead of actually
|
||||
releasing the lock, if isCommit is true.
|
||||
|
||||
Currently, ResourceOwners contain direct support for recording ownership
|
||||
of buffer pins, lmgr locks, and catcache and relcache references. Other
|
||||
objects can be associated with a ResourceOwner by recording the address of
|
||||
the owning ResourceOwner in such an object. There is an API for other
|
||||
of buffer pins, lmgr locks, and catcache, relcache, and tupdesc references.
|
||||
Other objects can be associated with a ResourceOwner by recording the address
|
||||
of the owning ResourceOwner in such an object. There is an API for other
|
||||
modules to get control during ResourceOwner release, so that they can scan
|
||||
their own data structures to find the objects that need to be deleted.
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.19 2006/04/03 13:44:33 teodor Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.20 2006/06/16 18:42:23 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -57,6 +57,11 @@ typedef struct ResourceOwnerData
|
||||
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 tupdesc references */
|
||||
int ntupdescs; /* number of owned tupdesc references */
|
||||
TupleDesc *tupdescs; /* dynamically allocated array */
|
||||
int maxtupdescs; /* currently allocated array size */
|
||||
} ResourceOwnerData;
|
||||
|
||||
|
||||
@ -87,6 +92,7 @@ static void ResourceOwnerReleaseInternal(ResourceOwner owner,
|
||||
bool isCommit,
|
||||
bool isTopLevel);
|
||||
static void PrintRelCacheLeakWarning(Relation rel);
|
||||
static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
@ -258,7 +264,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
|
||||
/*
|
||||
* Release catcache references. Note that ReleaseCatCache will remove
|
||||
* the catref entry from my list, so I just have to iterate till there
|
||||
* are none. Ditto for catcache lists.
|
||||
* are none.
|
||||
*
|
||||
* As with buffer pins, warn if any are left at commit time, and
|
||||
* release back-to-front for speed.
|
||||
@ -269,12 +275,20 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
|
||||
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 tupdesc references */
|
||||
while (owner->ntupdescs > 0)
|
||||
{
|
||||
if (isCommit)
|
||||
PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]);
|
||||
DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1]);
|
||||
}
|
||||
|
||||
/* Clean up index scans too */
|
||||
ReleaseResources_hash();
|
||||
@ -304,6 +318,7 @@ ResourceOwnerDelete(ResourceOwner owner)
|
||||
Assert(owner->ncatrefs == 0);
|
||||
Assert(owner->ncatlistrefs == 0);
|
||||
Assert(owner->nrelrefs == 0);
|
||||
Assert(owner->ntupdescs == 0);
|
||||
|
||||
/*
|
||||
* Delete children. The recursive call will delink the child from me, so
|
||||
@ -328,6 +343,8 @@ ResourceOwnerDelete(ResourceOwner owner)
|
||||
pfree(owner->catlistrefs);
|
||||
if (owner->relrefs)
|
||||
pfree(owner->relrefs);
|
||||
if (owner->tupdescs)
|
||||
pfree(owner->tupdescs);
|
||||
|
||||
pfree(owner);
|
||||
}
|
||||
@ -742,3 +759,85 @@ 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
|
||||
* 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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user