diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2004-09-13 20:10:13 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2004-09-13 20:10:13 +0000 |
commit | b2c4071299e02ed96d48d3c8e776de2fab36f88c (patch) | |
tree | ff0db14826870f1c3fe46d94ea3a1e1697c658a7 /src/backend/utils/time/tqual.c | |
parent | d69528881ab72eac5a9f154f23dbf549789c264d (diff) | |
download | postgresql-b2c4071299e02ed96d48d3c8e776de2fab36f88c.tar.gz postgresql-b2c4071299e02ed96d48d3c8e776de2fab36f88c.zip |
Redesign query-snapshot timing so that volatile functions in READ COMMITTED
mode see a fresh snapshot for each command in the function, rather than
using the latest interactive command's snapshot. Also, suppress fresh
snapshots as well as CommandCounterIncrement inside STABLE and IMMUTABLE
functions, instead using the snapshot taken for the most closely nested
regular query. (This behavior is only sane for read-only functions, so
the patch also enforces that such functions contain only SELECT commands.)
As per my proposal of 6-Sep-2004; I note that I floated essentially the
same proposal on 19-Jun-2002, but that discussion tailed off without any
action. Since 8.0 seems like the right place to be taking possibly
nontrivial backwards compatibility hits, let's get it done now.
Diffstat (limited to 'src/backend/utils/time/tqual.c')
-rw-r--r-- | src/backend/utils/time/tqual.c | 138 |
1 files changed, 69 insertions, 69 deletions
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c index ffdc4b9e6d9..5df7beaabdd 100644 --- a/src/backend/utils/time/tqual.c +++ b/src/backend/utils/time/tqual.c @@ -16,7 +16,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.77 2004/08/29 05:06:52 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.78 2004/09/13 20:07:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,18 +28,24 @@ #include "utils/tqual.h" /* - * The SnapshotData structs are static to simplify memory allocation + * These SnapshotData structs are static to simplify memory allocation * (see the hack in GetSnapshotData to avoid repeated malloc/free). */ -static SnapshotData QuerySnapshotData; -static SnapshotData SerializableSnapshotData; -static SnapshotData CurrentSnapshotData; static SnapshotData SnapshotDirtyData; +static SnapshotData SerializableSnapshotData; +static SnapshotData LatestSnapshotData; /* Externally visible pointers to valid snapshots: */ -Snapshot QuerySnapshot = NULL; -Snapshot SerializableSnapshot = NULL; Snapshot SnapshotDirty = &SnapshotDirtyData; +Snapshot SerializableSnapshot = NULL; +Snapshot LatestSnapshot = NULL; + +/* + * This pointer is not maintained by this module, but it's convenient + * to declare it here anyway. Callers typically assign a copy of + * GetTransactionSnapshot's result to ActiveSnapshot. + */ +Snapshot ActiveSnapshot = NULL; /* These are updated by GetSnapshotData: */ TransactionId RecentXmin = InvalidTransactionId; @@ -1028,101 +1034,94 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin) /* - * SetQuerySnapshot - * Initialize query snapshot for a new query + * GetTransactionSnapshot + * Get the appropriate snapshot for a new query in a transaction. * * The SerializableSnapshot is the first one taken in a transaction. * In serializable mode we just use that one throughout the transaction. - * In read-committed mode, we take a new snapshot at the start of each query. + * In read-committed mode, we take a new snapshot each time we are called. + * + * Note that the return value points at static storage that will be modified + * by future calls and by CommandCounterIncrement(). Callers should copy + * the result with CopySnapshot() if it is to be used very long. */ -void -SetQuerySnapshot(void) +Snapshot +GetTransactionSnapshot(void) { - /* 1st call in xaction? */ + /* First call in transaction? */ if (SerializableSnapshot == NULL) { SerializableSnapshot = GetSnapshotData(&SerializableSnapshotData, true); - QuerySnapshot = SerializableSnapshot; - Assert(QuerySnapshot != NULL); - return; + return SerializableSnapshot; } if (IsXactIsoLevelSerializable) - QuerySnapshot = SerializableSnapshot; - else - QuerySnapshot = GetSnapshotData(&QuerySnapshotData, false); + return SerializableSnapshot; - Assert(QuerySnapshot != NULL); + LatestSnapshot = GetSnapshotData(&LatestSnapshotData, false); + + return LatestSnapshot; } /* - * CopyQuerySnapshot - * Copy the current query snapshot. - * - * Copying the snapshot is done so that a query is guaranteed to use a - * consistent snapshot for its entire execution life, even if the command - * counter is incremented or SetQuerySnapshot() is called while it runs - * (as could easily happen, due to triggers etc. executing queries). - * - * The copy is palloc'd in the current memory context. + * GetLatestSnapshot + * Get a snapshot that is up-to-date as of the current instant, + * even if we are executing in SERIALIZABLE mode. */ Snapshot -CopyQuerySnapshot(void) +GetLatestSnapshot(void) { - Snapshot snapshot; - - if (QuerySnapshot == NULL) /* should be set beforehand */ + /* Should not be first call in transaction */ + if (SerializableSnapshot == NULL) elog(ERROR, "no snapshot has been set"); - snapshot = (Snapshot) palloc(sizeof(SnapshotData)); - memcpy(snapshot, QuerySnapshot, sizeof(SnapshotData)); - if (snapshot->xcnt > 0) - { - snapshot->xip = (TransactionId *) - palloc(snapshot->xcnt * sizeof(TransactionId)); - memcpy(snapshot->xip, QuerySnapshot->xip, - snapshot->xcnt * sizeof(TransactionId)); - } - else - snapshot->xip = NULL; + LatestSnapshot = GetSnapshotData(&LatestSnapshotData, false); - return snapshot; + return LatestSnapshot; } /* - * CopyCurrentSnapshot - * Make a snapshot that is up-to-date as of the current instant, - * and return a copy. + * CopySnapshot + * Copy the given snapshot. * * The copy is palloc'd in the current memory context. + * + * Note that this will not work on "special" snapshots. */ Snapshot -CopyCurrentSnapshot(void) +CopySnapshot(Snapshot snapshot) { - Snapshot currentSnapshot; - Snapshot snapshot; - - if (QuerySnapshot == NULL) /* should not be first call in xact */ - elog(ERROR, "no snapshot has been set"); - - /* Update the static struct */ - currentSnapshot = GetSnapshotData(&CurrentSnapshotData, false); - currentSnapshot->curcid = GetCurrentCommandId(); + Snapshot newsnap; - /* Make a copy */ - snapshot = (Snapshot) palloc(sizeof(SnapshotData)); - memcpy(snapshot, currentSnapshot, sizeof(SnapshotData)); + /* We allocate any XID array needed in the same palloc block. */ + newsnap = (Snapshot) palloc(sizeof(SnapshotData) + + snapshot->xcnt * sizeof(TransactionId)); + memcpy(newsnap, snapshot, sizeof(SnapshotData)); if (snapshot->xcnt > 0) { - snapshot->xip = (TransactionId *) - palloc(snapshot->xcnt * sizeof(TransactionId)); - memcpy(snapshot->xip, currentSnapshot->xip, + newsnap->xip = (TransactionId *) (newsnap + 1); + memcpy(newsnap->xip, snapshot->xip, snapshot->xcnt * sizeof(TransactionId)); } else - snapshot->xip = NULL; + newsnap->xip = NULL; - return snapshot; + return newsnap; +} + +/* + * FreeSnapshot + * Free a snapshot previously copied with CopySnapshot. + * + * This is currently identical to pfree, but is provided for cleanliness. + * + * Do *not* apply this to the results of GetTransactionSnapshot or + * GetLatestSnapshot. + */ +void +FreeSnapshot(Snapshot snapshot) +{ + pfree(snapshot); } /* @@ -1133,10 +1132,11 @@ void FreeXactSnapshot(void) { /* - * We do not free the xip arrays for the snapshot structs; they will - * be reused soon. So this is now just a state change to prevent + * We do not free the xip arrays for the static snapshot structs; they + * will be reused soon. So this is now just a state change to prevent * outside callers from accessing the snapshots. */ - QuerySnapshot = NULL; SerializableSnapshot = NULL; + LatestSnapshot = NULL; + ActiveSnapshot = NULL; /* just for cleanliness */ } |