Commit 7b640b03 authored by Alvaro Herrera's avatar Alvaro Herrera

Fix a couple of snapshot management bugs in the new ResourceOwner world:

non-writable large objects need to have their snapshots registered on the
transaction resowner, not the current portal's, because it must persist until
the large object is closed (which the portal does not).  Also, ensure that the
serializable snapshot is recorded by the transaction resource owner too, even
when a subtransaction has changed the current resource owner before
serializable is taken.

Per bug reports from Pavan Deolasee.
parent 30c52532
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.269 2008/11/19 10:34:50 heikki Exp $ * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.270 2008/12/04 14:51:02 alvherre Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1667,6 +1667,9 @@ CommitTransaction(void) ...@@ -1667,6 +1667,9 @@ CommitTransaction(void)
/* Clean up the relation cache */ /* Clean up the relation cache */
AtEOXact_RelationCache(true); AtEOXact_RelationCache(true);
/* Clean up the snapshot manager */
AtEarlyCommit_Snapshot();
/* /*
* Make catalog changes visible to all backends. This has to happen after * Make catalog changes visible to all backends. This has to happen after
* relcache references are dropped (see comments for * relcache references are dropped (see comments for
...@@ -1906,6 +1909,9 @@ PrepareTransaction(void) ...@@ -1906,6 +1909,9 @@ PrepareTransaction(void)
/* Clean up the relation cache */ /* Clean up the relation cache */
AtEOXact_RelationCache(true); AtEOXact_RelationCache(true);
/* Clean up the snapshot manager */
AtEarlyCommit_Snapshot();
/* notify and flatfiles don't need a postprepare call */ /* notify and flatfiles don't need a postprepare call */
PostPrepare_PgStat(); PostPrepare_PgStat();
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.135 2008/11/02 01:45:28 tgl Exp $ * $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.136 2008/12/04 14:51:02 alvherre Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -247,7 +247,13 @@ inv_open(Oid lobjId, int flags, MemoryContext mcxt) ...@@ -247,7 +247,13 @@ inv_open(Oid lobjId, int flags, MemoryContext mcxt)
} }
else if (flags & INV_READ) else if (flags & INV_READ)
{ {
retval->snapshot = RegisterSnapshot(GetActiveSnapshot()); /*
* We must register the snapshot in TopTransaction's resowner,
* because it must stay alive until the LO is closed rather than until
* the current portal shuts down.
*/
retval->snapshot = RegisterSnapshotOnOwner(GetActiveSnapshot(),
TopTransactionResourceOwner);
retval->flags = IFS_RDLOCK; retval->flags = IFS_RDLOCK;
} }
else else
...@@ -270,8 +276,11 @@ void ...@@ -270,8 +276,11 @@ void
inv_close(LargeObjectDesc *obj_desc) inv_close(LargeObjectDesc *obj_desc)
{ {
Assert(PointerIsValid(obj_desc)); Assert(PointerIsValid(obj_desc));
if (obj_desc->snapshot != SnapshotNow) if (obj_desc->snapshot != SnapshotNow)
UnregisterSnapshot(obj_desc->snapshot); UnregisterSnapshotFromOwner(obj_desc->snapshot,
TopTransactionResourceOwner);
pfree(obj_desc); pfree(obj_desc);
} }
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/time/snapmgr.c,v 1.7 2008/11/25 20:28:29 alvherre Exp $ * $PostgreSQL: pgsql/src/backend/utils/time/snapmgr.c,v 1.8 2008/12/04 14:51:02 alvherre Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -136,7 +136,8 @@ GetTransactionSnapshot(void) ...@@ -136,7 +136,8 @@ GetTransactionSnapshot(void)
*/ */
if (IsXactIsoLevelSerializable) if (IsXactIsoLevelSerializable)
{ {
CurrentSnapshot = RegisterSnapshot(CurrentSnapshot); CurrentSnapshot = RegisterSnapshotOnOwner(CurrentSnapshot,
TopTransactionResourceOwner);
registered_serializable = true; registered_serializable = true;
} }
...@@ -345,14 +346,27 @@ ActiveSnapshotSet(void) ...@@ -345,14 +346,27 @@ ActiveSnapshotSet(void)
/* /*
* RegisterSnapshot * RegisterSnapshot
* Register a snapshot as being in use * Register a snapshot as being in use by the current resource owner
* *
* If InvalidSnapshot is passed, it is not registered. * If InvalidSnapshot is passed, it is not registered.
*/ */
Snapshot Snapshot
RegisterSnapshot(Snapshot snapshot) RegisterSnapshot(Snapshot snapshot)
{ {
Snapshot snap; if (snapshot == InvalidSnapshot)
return InvalidSnapshot;
return RegisterSnapshotOnOwner(snapshot, CurrentResourceOwner);
}
/*
* RegisterSnapshotOnOwner
* As above, but use the specified resource owner
*/
Snapshot
RegisterSnapshotOnOwner(Snapshot snapshot, ResourceOwner owner)
{
Snapshot snap;
if (snapshot == InvalidSnapshot) if (snapshot == InvalidSnapshot)
return InvalidSnapshot; return InvalidSnapshot;
...@@ -361,9 +375,9 @@ RegisterSnapshot(Snapshot snapshot) ...@@ -361,9 +375,9 @@ RegisterSnapshot(Snapshot snapshot)
snap = snapshot->copied ? snapshot : CopySnapshot(snapshot); snap = snapshot->copied ? snapshot : CopySnapshot(snapshot);
/* and tell resowner.c about it */ /* and tell resowner.c about it */
ResourceOwnerEnlargeSnapshots(CurrentResourceOwner); ResourceOwnerEnlargeSnapshots(owner);
snap->regd_count++; snap->regd_count++;
ResourceOwnerRememberSnapshot(CurrentResourceOwner, snap); ResourceOwnerRememberSnapshot(owner, snap);
RegisteredSnapshots++; RegisteredSnapshots++;
...@@ -379,6 +393,19 @@ RegisterSnapshot(Snapshot snapshot) ...@@ -379,6 +393,19 @@ RegisterSnapshot(Snapshot snapshot)
*/ */
void void
UnregisterSnapshot(Snapshot snapshot) UnregisterSnapshot(Snapshot snapshot)
{
if (snapshot == NULL)
return;
UnregisterSnapshotFromOwner(snapshot, CurrentResourceOwner);
}
/*
* UnregisterSnapshotFromOwner
* As above, but use the specified resource owner
*/
void
UnregisterSnapshotFromOwner(Snapshot snapshot, ResourceOwner owner)
{ {
if (snapshot == NULL) if (snapshot == NULL)
return; return;
...@@ -386,7 +413,7 @@ UnregisterSnapshot(Snapshot snapshot) ...@@ -386,7 +413,7 @@ UnregisterSnapshot(Snapshot snapshot)
Assert(snapshot->regd_count > 0); Assert(snapshot->regd_count > 0);
Assert(RegisteredSnapshots > 0); Assert(RegisteredSnapshots > 0);
ResourceOwnerForgetSnapshot(CurrentResourceOwner, snapshot); ResourceOwnerForgetSnapshot(owner, snapshot);
RegisteredSnapshots--; RegisteredSnapshots--;
if (--snapshot->regd_count == 0 && snapshot->active_count == 0) if (--snapshot->regd_count == 0 && snapshot->active_count == 0)
{ {
...@@ -463,6 +490,26 @@ AtSubAbort_Snapshot(int level) ...@@ -463,6 +490,26 @@ AtSubAbort_Snapshot(int level)
SnapshotResetXmin(); SnapshotResetXmin();
} }
/*
* AtEarlyCommit_Snapshot
*
* Snapshot manager's cleanup function, to be called on commit, before
* doing resowner.c resource release.
*/
void
AtEarlyCommit_Snapshot(void)
{
/*
* On a serializable transaction we must unregister our private refcount to
* the serializable snapshot.
*/
if (registered_serializable)
UnregisterSnapshotFromOwner(CurrentSnapshot,
TopTransactionResourceOwner);
registered_serializable = false;
}
/* /*
* AtEOXact_Snapshot * AtEOXact_Snapshot
* Snapshot manager's cleanup function for end of transaction * Snapshot manager's cleanup function for end of transaction
...@@ -475,13 +522,6 @@ AtEOXact_Snapshot(bool isCommit) ...@@ -475,13 +522,6 @@ AtEOXact_Snapshot(bool isCommit)
{ {
ActiveSnapshotElt *active; ActiveSnapshotElt *active;
/*
* On a serializable snapshot we must first unregister our private
* refcount to the serializable snapshot.
*/
if (registered_serializable)
UnregisterSnapshot(CurrentSnapshot);
if (RegisteredSnapshots != 0) if (RegisteredSnapshots != 0)
elog(WARNING, "%d registered snapshots seem to remain after cleanup", elog(WARNING, "%d registered snapshots seem to remain after cleanup",
RegisteredSnapshots); RegisteredSnapshots);
......
...@@ -6,13 +6,14 @@ ...@@ -6,13 +6,14 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/snapmgr.h,v 1.2 2008/05/12 20:02:02 alvherre Exp $ * $PostgreSQL: pgsql/src/include/utils/snapmgr.h,v 1.3 2008/12/04 14:51:02 alvherre Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#ifndef SNAPMGR_H #ifndef SNAPMGR_H
#define SNAPMGR_H #define SNAPMGR_H
#include "utils/resowner.h"
#include "utils/snapshot.h" #include "utils/snapshot.h"
...@@ -34,9 +35,12 @@ extern bool ActiveSnapshotSet(void); ...@@ -34,9 +35,12 @@ extern bool ActiveSnapshotSet(void);
extern Snapshot RegisterSnapshot(Snapshot snapshot); extern Snapshot RegisterSnapshot(Snapshot snapshot);
extern void UnregisterSnapshot(Snapshot snapshot); extern void UnregisterSnapshot(Snapshot snapshot);
extern Snapshot RegisterSnapshotOnOwner(Snapshot snapshot, ResourceOwner owner);
extern void UnregisterSnapshotFromOwner(Snapshot snapshot, ResourceOwner owner);
extern void AtSubCommit_Snapshot(int level); extern void AtSubCommit_Snapshot(int level);
extern void AtSubAbort_Snapshot(int level); extern void AtSubAbort_Snapshot(int level);
extern void AtEarlyCommit_Snapshot(void);
extern void AtEOXact_Snapshot(bool isCommit); extern void AtEOXact_Snapshot(bool isCommit);
#endif /* SNAPMGR_H */ #endif /* SNAPMGR_H */
...@@ -83,6 +83,11 @@ SELECT lo_close(fd) FROM lotest_stash_values; ...@@ -83,6 +83,11 @@ SELECT lo_close(fd) FROM lotest_stash_values;
END; END;
-- Test resource management
BEGIN;
SELECT lo_open(loid, x'40000'::int) from lotest_stash_values;
ABORT;
-- Test truncation. -- Test truncation.
BEGIN; BEGIN;
UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer)); UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer));
......
...@@ -116,6 +116,15 @@ SELECT lo_close(fd) FROM lotest_stash_values; ...@@ -116,6 +116,15 @@ SELECT lo_close(fd) FROM lotest_stash_values;
(1 row) (1 row)
END; END;
-- Test resource management
BEGIN;
SELECT lo_open(loid, x'40000'::int) from lotest_stash_values;
lo_open
---------
0
(1 row)
ABORT;
-- Test truncation. -- Test truncation.
BEGIN; BEGIN;
UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer)); UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer));
......
...@@ -116,6 +116,15 @@ SELECT lo_close(fd) FROM lotest_stash_values; ...@@ -116,6 +116,15 @@ SELECT lo_close(fd) FROM lotest_stash_values;
(1 row) (1 row)
END; END;
-- Test resource management
BEGIN;
SELECT lo_open(loid, x'40000'::int) from lotest_stash_values;
lo_open
---------
0
(1 row)
ABORT;
-- Test truncation. -- Test truncation.
BEGIN; BEGIN;
UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer)); UPDATE lotest_stash_values SET fd=lo_open(loid, CAST(x'20000' | x'40000' AS integer));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment