diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c index 4cffd21..e76148a 100644 --- a/src/backend/access/heap/tuptoaster.c +++ b/src/backend/access/heap/tuptoaster.c @@ -40,6 +40,7 @@ #include "utils/expandeddatum.h" #include "utils/fmgroids.h" #include "utils/rel.h" +#include "utils/snapmgr.h" #include "utils/typcache.h" #include "utils/tqual.h" @@ -81,6 +82,7 @@ static int toast_open_indexes(Relation toastrel, int *num_indexes); static void toast_close_indexes(Relation *toastidxs, int num_indexes, LOCKMODE lock); +static void init_toast_snapshot(Snapshot toast_snapshot); /* ---------- @@ -1665,6 +1667,7 @@ toast_delete_datum(Relation rel, Datum value) HeapTuple toasttup; int num_indexes; int validIndex; + SnapshotData SnapshotToast; if (!VARATT_IS_EXTERNAL_ONDISK(attr)) return; @@ -1696,8 +1699,9 @@ toast_delete_datum(Relation rel, Datum value) * sequence or not, but since we've already locked the index we might as * well use systable_beginscan_ordered.) */ + init_toast_snapshot(&SnapshotToast); toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex], - SnapshotToast, 1, &toastkey); + &SnapshotToast, 1, &toastkey); while ((toasttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL) { /* @@ -1730,6 +1734,7 @@ toastrel_valueid_exists(Relation toastrel, Oid valueid) int num_indexes; int validIndex; Relation *toastidxs; + SnapshotData SnapshotToast; /* Fetch a valid index relation */ validIndex = toast_open_indexes(toastrel, @@ -1748,9 +1753,10 @@ toastrel_valueid_exists(Relation toastrel, Oid valueid) /* * Is there any such chunk? */ + init_toast_snapshot(&SnapshotToast); toastscan = systable_beginscan(toastrel, RelationGetRelid(toastidxs[validIndex]), - true, SnapshotToast, 1, &toastkey); + true, &SnapshotToast, 1, &toastkey); if (systable_getnext(toastscan) != NULL) result = true; @@ -1813,6 +1819,7 @@ toast_fetch_datum(struct varlena * attr) int32 chunksize; int num_indexes; int validIndex; + SnapshotData SnapshotToast; if (!VARATT_IS_EXTERNAL_ONDISK(attr)) elog(ERROR, "toast_fetch_datum shouldn't be called for non-ondisk datums"); @@ -1859,8 +1866,9 @@ toast_fetch_datum(struct varlena * attr) */ nextidx = 0; + init_toast_snapshot(&SnapshotToast); toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex], - SnapshotToast, 1, &toastkey); + &SnapshotToast, 1, &toastkey); while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL) { /* @@ -1990,6 +1998,7 @@ toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length) int32 chcpyend; int num_indexes; int validIndex; + SnapshotData SnapshotToast; if (!VARATT_IS_EXTERNAL_ONDISK(attr)) elog(ERROR, "toast_fetch_datum_slice shouldn't be called for non-ondisk datums"); @@ -2082,9 +2091,10 @@ toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length) * * The index is on (valueid, chunkidx) so they will come in order */ + init_toast_snapshot(&SnapshotToast); nextidx = startchunk; toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex], - SnapshotToast, nscankeys, toastkey); + &SnapshotToast, nscankeys, toastkey); while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL) { /* @@ -2289,3 +2299,16 @@ toast_close_indexes(Relation *toastidxs, int num_indexes, LOCKMODE lock) index_close(toastidxs[i], lock); pfree(toastidxs); } + +/* ---------- + * init_toast_snapshot + * + * Initialize an appropriate TOAST snapshot. We must use an MVCC snapshot + * to initialize the TOAST snapshot; since we don't know which one to use, + * just use the oldest one. + */ +static void +init_toast_snapshot(Snapshot toast_snapshot) +{ + InitToastSnapshot(toast_snapshot, GetMinimumSnapshot()); +} diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c index e1caf01..c62e895 100644 --- a/src/backend/utils/time/snapmgr.c +++ b/src/backend/utils/time/snapmgr.c @@ -394,6 +394,25 @@ GetLatestSnapshot(void) } /* + * GetMinimumSnapshot + * + * Get the oldest snapshot for this backend. + */ +Snapshot +GetMinimumSnapshot(void) +{ + Snapshot minSnapshot; + + if (pairingheap_is_empty(&RegisteredSnapshots)) + return NULL; + + minSnapshot = pairingheap_container(SnapshotData, ph_node, + pairingheap_first(&RegisteredSnapshots)); + + return minSnapshot; +} + +/* * GetCatalogSnapshot * Get a snapshot that is sufficiently up-to-date for scan of the * system catalog with the specified OID. diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c index 2960455..d99c847 100644 --- a/src/backend/utils/time/tqual.c +++ b/src/backend/utils/time/tqual.c @@ -78,7 +78,6 @@ /* Static variables representing various special snapshot semantics */ SnapshotData SnapshotSelfData = {HeapTupleSatisfiesSelf}; SnapshotData SnapshotAnyData = {HeapTupleSatisfiesAny}; -SnapshotData SnapshotToastData = {HeapTupleSatisfiesToast}; /* local functions */ static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot); diff --git a/src/include/access/xlogdefs.h b/src/include/access/xlogdefs.h index c2c6632..ea17aeb 100644 --- a/src/include/access/xlogdefs.h +++ b/src/include/access/xlogdefs.h @@ -28,6 +28,8 @@ typedef uint64 XLogRecPtr; #define InvalidXLogRecPtr 0 #define XLogRecPtrIsInvalid(r) ((r) == InvalidXLogRecPtr) +#define MaximumXLogRecPtr PG_UINT64_MAX + /* * XLogSegNo - physical log file sequence number. */ diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h index 3d5dea7..fcd0c75 100644 --- a/src/include/storage/bufmgr.h +++ b/src/include/storage/bufmgr.h @@ -279,7 +279,8 @@ TestForOldSnapshot(Snapshot snapshot, Relation relation, Page page) if (old_snapshot_threshold >= 0 && (snapshot) != NULL - && (snapshot)->satisfies == HeapTupleSatisfiesMVCC + && ((snapshot)->satisfies == HeapTupleSatisfiesMVCC + || (snapshot)->satisfies == HeapTupleSatisfiesToast) && !XLogRecPtrIsInvalid((snapshot)->lsn) && PageGetLSN(page) > (snapshot)->lsn) TestForOldSnapshot_impl(snapshot, relation); diff --git a/src/include/utils/snapmgr.h b/src/include/utils/snapmgr.h index e941427..027a64d 100644 --- a/src/include/utils/snapmgr.h +++ b/src/include/utils/snapmgr.h @@ -64,6 +64,7 @@ extern TransactionId RecentGlobalDataXmin; extern Snapshot GetTransactionSnapshot(void); extern Snapshot GetLatestSnapshot(void); extern void SnapshotSetCommandId(CommandId curcid); +extern Snapshot GetMinimumSnapshot(void); extern Snapshot GetCatalogSnapshot(Oid relid); extern Snapshot GetNonHistoricCatalogSnapshot(Oid relid); diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h index 2445944..2507e4a 100644 --- a/src/include/utils/tqual.h +++ b/src/include/utils/tqual.h @@ -15,26 +15,17 @@ #ifndef TQUAL_H #define TQUAL_H +#include "access/xlogdefs.h" #include "utils/snapshot.h" /* Static variables representing various special snapshot semantics */ extern PGDLLIMPORT SnapshotData SnapshotSelfData; extern PGDLLIMPORT SnapshotData SnapshotAnyData; -extern PGDLLIMPORT SnapshotData SnapshotToastData; extern PGDLLIMPORT SnapshotData CatalogSnapshotData; #define SnapshotSelf (&SnapshotSelfData) #define SnapshotAny (&SnapshotAnyData) -#define SnapshotToast (&SnapshotToastData) - -/* - * We don't provide a static SnapshotDirty variable because it would be - * non-reentrant. Instead, users of that snapshot type should declare a - * local variable of type SnapshotData, and initialize it with this macro. - */ -#define InitDirtySnapshot(snapshotdata) \ - ((snapshotdata).satisfies = HeapTupleSatisfiesDirty) /* This macro encodes the knowledge of which snapshots are MVCC-safe */ #define IsMVCCSnapshot(snapshot) \ @@ -100,4 +91,33 @@ extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data, HeapTuple htup, Buffer buffer, CommandId *cmin, CommandId *cmax); + +/* + * We don't provide a static SnapshotDirty variable because it would be + * non-reentrant. Instead, users of that snapshot type should declare a + * local variable of type SnapshotData, and initialize it with this macro. + */ +#define InitDirtySnapshot(snapshotdata) \ + ((snapshotdata).satisfies = HeapTupleSatisfiesDirty) + +/* + * Similarly, some initialization is required for SnapshotToast. We need + * to set lsn correctly to support snapshot_too_old; the idea is to initialize + * these values from the MVCC snapshot that was used to read the data from the + * heap. In practice, we don't know which MVCC snapshot was used, but it's OK + * to use an older one; at worst, we will get a "snapshot too old" error that + * might have been avoided otherwise. + * + * If there's no snapshot from which to initialize, we assume that we need + * not worry about throwing a "snapshot too old" error. + */ +static inline void +InitToastSnapshot(Snapshot toast_snapshot, Snapshot snapshot) +{ + toast_snapshot->satisfies = HeapTupleSatisfiesToast; + toast_snapshot->lsn = MaximumXLogRecPtr; + if (snapshot != NULL) + toast_snapshot->lsn = snapshot->lsn; +} + #endif /* TQUAL_H */