1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-07 00:36:50 +03:00

Fix bug in temporary file management with subtransactions. A cursor opened

in a subtransaction stays open even if the subtransaction is aborted, so
any temporary files related to it must stay alive as well. With the patch,
we use ResourceOwners to track open temporary files and don't automatically
close them at subtransaction end (though in the normal case temporary files
are registered with the subtransaction resource owner and will therefore be
closed).

At end of top transaction, we still check that there's no temporary files
marked as close-at-end-of-transaction open, but that's now just a debugging
cross-check as the resource owner cleanup should've closed them already.
This commit is contained in:
Heikki Linnakangas
2009-12-03 11:03:29 +00:00
parent dc588058a0
commit ab3148b712
3 changed files with 146 additions and 34 deletions

View File

@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.32 2009/06/11 14:49:06 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.33 2009/12/03 11:03:29 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@ -72,6 +72,11 @@ typedef struct ResourceOwnerData
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;
@ -105,6 +110,7 @@ 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);
/*****************************************************************************
@ -316,6 +322,14 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
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();
}
@ -347,6 +361,7 @@ ResourceOwnerDelete(ResourceOwner owner)
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
@ -377,6 +392,8 @@ ResourceOwnerDelete(ResourceOwner owner)
pfree(owner->tupdescs);
if (owner->snapshots)
pfree(owner->snapshots);
if (owner->files)
pfree(owner->files);
pfree(owner);
}
@ -1035,3 +1052,87 @@ PrintSnapshotLeakWarning(Snapshot snapshot)
"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);
}