From 662700ceb5bbe8f0c46448fdddbc18a603d54a90 Mon Sep 17 00:00:00 2001 From: Hari Babu Date: Fri, 1 Dec 2017 14:49:18 +1100 Subject: [PATCH 4/8] Adding tuple visibility function to storage AM Tuple visibility functions are now part of the heap storage AM routine. The visibilty execution procedure is changed accoridngly. The snapshot satifies function is changed to an enum to represent what type of snapshot is it and this enum value is used to call the corresponding visibilty function from the storage AM when the visibilty of the tuple is required. The common code is that is part of both server and pluggable storages is now moved into storage_common.c and storage_common.h files. --- contrib/pg_visibility/pg_visibility.c | 11 +- contrib/pgrowlocks/pgrowlocks.c | 7 +- contrib/pgstattuple/pgstatapprox.c | 7 +- contrib/pgstattuple/pgstattuple.c | 3 +- src/backend/access/heap/Makefile | 2 +- src/backend/access/heap/heapam.c | 61 ++- src/backend/access/heap/heapam_storage.c | 10 + .../tqual.c => access/heap/heapam_visibility.c} | 448 +++++---------------- src/backend/access/heap/pruneheap.c | 4 +- src/backend/access/index/genam.c | 4 +- src/backend/access/storage/Makefile | 2 +- src/backend/access/storage/storage_common.c | 277 +++++++++++++ src/backend/catalog/index.c | 6 +- src/backend/commands/analyze.c | 6 +- src/backend/commands/cluster.c | 3 +- src/backend/commands/vacuumlazy.c | 4 +- src/backend/executor/nodeBitmapHeapscan.c | 2 +- src/backend/executor/nodeModifyTable.c | 7 +- src/backend/executor/nodeSamplescan.c | 3 +- src/backend/replication/logical/snapbuild.c | 6 +- src/backend/storage/lmgr/predicate.c | 2 +- src/backend/utils/adt/ri_triggers.c | 2 +- src/backend/utils/time/Makefile | 2 +- src/backend/utils/time/snapmgr.c | 10 +- src/include/access/storage_common.h | 98 +++++ src/include/access/storageamapi.h | 12 + src/include/storage/bufmgr.h | 5 +- src/include/utils/snapshot.h | 14 +- src/include/utils/tqual.h | 54 +-- 29 files changed, 607 insertions(+), 465 deletions(-) rename src/backend/{utils/time/tqual.c => access/heap/heapam_visibility.c} (80%) create mode 100644 src/backend/access/storage/storage_common.c create mode 100644 src/include/access/storage_common.h diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c index 2cc9575d9f..01ff3fed5d 100644 --- a/contrib/pg_visibility/pg_visibility.c +++ b/contrib/pg_visibility/pg_visibility.c @@ -11,6 +11,7 @@ #include "postgres.h" #include "access/htup_details.h" +#include "access/storageamapi.h" #include "access/visibilitymap.h" #include "catalog/pg_type.h" #include "catalog/storage_xlog.h" @@ -51,7 +52,7 @@ static vbits *collect_visibility_data(Oid relid, bool include_pd); static corrupt_items *collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen); static void record_corrupt_item(corrupt_items *items, ItemPointer tid); -static bool tuple_all_visible(HeapTuple tup, TransactionId OldestXmin, +static bool tuple_all_visible(Relation rel, HeapTuple tup, TransactionId OldestXmin, Buffer buffer); static void check_relation_relkind(Relation rel); @@ -656,7 +657,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen) * the tuple to be all-visible. */ if (check_visible && - !tuple_all_visible(&tuple, OldestXmin, buffer)) + !tuple_all_visible(rel, &tuple, OldestXmin, buffer)) { TransactionId RecomputedOldestXmin; @@ -681,7 +682,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen) else { OldestXmin = RecomputedOldestXmin; - if (!tuple_all_visible(&tuple, OldestXmin, buffer)) + if (!tuple_all_visible(rel, &tuple, OldestXmin, buffer)) record_corrupt_item(items, &tuple.t_self); } } @@ -739,12 +740,12 @@ record_corrupt_item(corrupt_items *items, ItemPointer tid) * The buffer should contain the tuple and should be locked and pinned. */ static bool -tuple_all_visible(HeapTuple tup, TransactionId OldestXmin, Buffer buffer) +tuple_all_visible(Relation rel, HeapTuple tup, TransactionId OldestXmin, Buffer buffer) { HTSV_Result state; TransactionId xmin; - state = HeapTupleSatisfiesVacuum(tup, OldestXmin, buffer); + state = rel->rd_stamroutine->snapshot_satisfiesVacuum(tup, OldestXmin, buffer); if (state != HEAPTUPLE_LIVE) return false; /* all-visible implies live */ diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c index eabca65bd2..830e74fd07 100644 --- a/contrib/pgrowlocks/pgrowlocks.c +++ b/contrib/pgrowlocks/pgrowlocks.c @@ -26,6 +26,7 @@ #include "access/multixact.h" #include "access/relscan.h" +#include "access/storageamapi.h" #include "access/xact.h" #include "catalog/namespace.h" #include "catalog/pg_authid.h" @@ -149,9 +150,9 @@ pgrowlocks(PG_FUNCTION_ARGS) /* must hold a buffer lock to call HeapTupleSatisfiesUpdate */ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE); - htsu = HeapTupleSatisfiesUpdate(tuple, - GetCurrentCommandId(false), - scan->rs_cbuf); + htsu = rel->rd_stamroutine->snapshot_satisfiesUpdate(tuple, + GetCurrentCommandId(false), + scan->rs_cbuf); xmax = HeapTupleHeaderGetRawXmax(tuple->t_data); infomask = tuple->t_data->t_infomask; diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c index 5bf06138a5..284eabc970 100644 --- a/contrib/pgstattuple/pgstatapprox.c +++ b/contrib/pgstattuple/pgstatapprox.c @@ -12,12 +12,13 @@ */ #include "postgres.h" -#include "access/visibilitymap.h" #include "access/transam.h" +#include "access/visibilitymap.h" #include "access/xact.h" #include "access/multixact.h" #include "access/htup_details.h" #include "catalog/namespace.h" +#include "commands/vacuum.h" #include "funcapi.h" #include "miscadmin.h" #include "storage/bufmgr.h" @@ -26,7 +27,7 @@ #include "storage/lmgr.h" #include "utils/builtins.h" #include "utils/tqual.h" -#include "commands/vacuum.h" + PG_FUNCTION_INFO_V1(pgstattuple_approx); PG_FUNCTION_INFO_V1(pgstattuple_approx_v1_5); @@ -156,7 +157,7 @@ statapprox_heap(Relation rel, output_type *stat) * We count live and dead tuples, but we also need to add up * others in order to feed vac_estimate_reltuples. */ - switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf)) + switch (rel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf)) { case HEAPTUPLE_RECENTLY_DEAD: misc_count++; diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c index 7ca1bb24d2..e098202f84 100644 --- a/contrib/pgstattuple/pgstattuple.c +++ b/contrib/pgstattuple/pgstattuple.c @@ -322,6 +322,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo) Buffer buffer; pgstattuple_type stat = {0}; SnapshotData SnapshotDirty; + StorageAmRoutine *method = rel->rd_stamroutine; /* Disable syncscan because we assume we scan from block zero upwards */ scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false); @@ -337,7 +338,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo) /* must hold a buffer lock to call HeapTupleSatisfiesVisibility */ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE); - if (HeapTupleSatisfiesVisibility(tuple, &SnapshotDirty, scan->rs_cbuf)) + if (HeapTupleSatisfiesVisibility(method, tuple, &SnapshotDirty, scan->rs_cbuf)) { stat.tuple_len += tuple->t_len; stat.tuple_count++; diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile index 816f03a86f..f5c628395b 100644 --- a/src/backend/access/heap/Makefile +++ b/src/backend/access/heap/Makefile @@ -12,7 +12,7 @@ subdir = src/backend/access/heap top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global -OBJS = heapam.o hio.o heapam_storage.o pruneheap.o rewriteheap.o \ +OBJS = heapam.o hio.o heapam_storage.o heapam_visibility.o pruneheap.o rewriteheap.o \ syncscan.o tuptoaster.o visibilitymap.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 3acef279f4..d63afbaf69 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -45,6 +45,7 @@ #include "access/multixact.h" #include "access/parallel.h" #include "access/relscan.h" +#include "access/storageamapi.h" #include "access/sysattr.h" #include "access/transam.h" #include "access/tuptoaster.h" @@ -438,7 +439,7 @@ heapgetpage(HeapScanDesc scan, BlockNumber page) if (all_visible) valid = true; else - valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer); + valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer); CheckForSerializableConflictOut(valid, scan->rs_rd, &loctup, buffer, snapshot); @@ -653,7 +654,8 @@ heapgettup(HeapScanDesc scan, /* * if current tuple qualifies, return it. */ - valid = HeapTupleSatisfiesVisibility(tuple, + valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, + tuple, snapshot, scan->rs_cbuf); @@ -841,6 +843,7 @@ heapgettup_pagemode(HeapScanDesc scan, lineindex = scan->rs_cindex + 1; } + LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE); dp = BufferGetPage(scan->rs_cbuf); TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp); lines = scan->rs_ntuples; @@ -885,6 +888,7 @@ heapgettup_pagemode(HeapScanDesc scan, page = scan->rs_cblock; /* current page */ } + LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE); dp = BufferGetPage(scan->rs_cbuf); TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp); lines = scan->rs_ntuples; @@ -954,23 +958,31 @@ heapgettup_pagemode(HeapScanDesc scan, /* * if current tuple qualifies, return it. */ - if (key != NULL) + if (HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, tuple, scan->rs_snapshot, scan->rs_cbuf)) { - bool valid; + /* + * if current tuple qualifies, return it. + */ + if (key != NULL) + { + bool valid; - HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd), - nkeys, key, valid); - if (valid) + HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd), + nkeys, key, valid); + if (valid) + { + scan->rs_cindex = lineindex; + LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); + return; + } + } + else { scan->rs_cindex = lineindex; + LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); return; } } - else - { - scan->rs_cindex = lineindex; - return; - } /* * otherwise move to the next item on the page @@ -982,6 +994,12 @@ heapgettup_pagemode(HeapScanDesc scan, ++lineindex; } + /* + * if we get here, it means we've exhausted the items on this page and + * it's time to move to the next. + */ + LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); + /* * if we get here, it means we've exhausted the items on this page and * it's time to move to the next. @@ -1039,6 +1057,7 @@ heapgettup_pagemode(HeapScanDesc scan, heapgetpage(scan, page); + LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE); dp = BufferGetPage(scan->rs_cbuf); TestForOldSnapshot(scan->rs_snapshot, scan->rs_rd, dp); lines = scan->rs_ntuples; @@ -1831,7 +1850,7 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction) pgstat_count_heap_getnext(scan->rs_rd); - return &(scan->rs_ctup); + return heap_copytuple(&(scan->rs_ctup)); } /* @@ -1950,7 +1969,7 @@ heap_fetch(Relation relation, /* * check time qualification of tuple, then release lock */ - valid = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer); + valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, tuple, snapshot, buffer); if (valid) PredicateLockTuple(relation, tuple, snapshot); @@ -2097,7 +2116,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, ItemPointerSet(&(heapTuple->t_self), BufferGetBlockNumber(buffer), offnum); /* If it's visible per the snapshot, we must return it */ - valid = HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer); + valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, heapTuple, snapshot, buffer); CheckForSerializableConflictOut(valid, relation, heapTuple, buffer, snapshot); /* reset to original, non-redirected, tid */ @@ -2271,7 +2290,7 @@ heap_get_latest_tid(Relation relation, * Check time qualification of tuple; if visible, set it as the new * result candidate. */ - valid = HeapTupleSatisfiesVisibility(&tp, snapshot, buffer); + valid = HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, snapshot, buffer); CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot); if (valid) *tid = ctid; @@ -3097,7 +3116,7 @@ heap_delete(Relation relation, ItemPointer tid, tp.t_self = *tid; l1: - result = HeapTupleSatisfiesUpdate(&tp, cid, buffer); + result = relation->rd_stamroutine->snapshot_satisfiesUpdate(&tp, cid, buffer); if (result == HeapTupleInvisible) { @@ -3208,7 +3227,7 @@ l1: if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated) { /* Perform additional check for transaction-snapshot mode RI updates */ - if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer)) + if (!HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &tp, crosscheck, buffer)) result = HeapTupleUpdated; } @@ -3668,7 +3687,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, l2: checked_lockers = false; locker_remains = false; - result = HeapTupleSatisfiesUpdate(&oldtup, cid, buffer); + result = relation->rd_stamroutine->snapshot_satisfiesUpdate(&oldtup, cid, buffer); /* see below about the "no wait" case */ Assert(result != HeapTupleBeingUpdated || wait); @@ -3849,7 +3868,7 @@ l2: if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated) { /* Perform additional check for transaction-snapshot mode RI updates */ - if (!HeapTupleSatisfiesVisibility(&oldtup, crosscheck, buffer)) + if (!HeapTupleSatisfiesVisibility(relation->rd_stamroutine, &oldtup, crosscheck, buffer)) result = HeapTupleUpdated; } @@ -4600,7 +4619,7 @@ heap_lock_tuple(Relation relation, HeapTuple tuple, tuple->t_tableOid = RelationGetRelid(relation); l3: - result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer); + result = relation->rd_stamroutine->snapshot_satisfiesUpdate(tuple, cid, *buffer); if (result == HeapTupleInvisible) { diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c index 792e9cb436..2d3ed44baf 100644 --- a/src/backend/access/heap/heapam_storage.c +++ b/src/backend/access/heap/heapam_storage.c @@ -23,11 +23,21 @@ #include "access/storageamapi.h" #include "utils/builtins.h" +extern bool HeapTupleSatisfies(StorageTuple stup, Snapshot snapshot, Buffer buffer); +extern HTSU_Result HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid, + Buffer buffer); +extern HTSV_Result HeapTupleSatisfiesVacuum(StorageTuple stup, TransactionId OldestXmin, + Buffer buffer); Datum heapam_storage_handler(PG_FUNCTION_ARGS) { StorageAmRoutine *amroutine = makeNode(StorageAmRoutine); + amroutine->snapshot_satisfies = HeapTupleSatisfies; + + amroutine->snapshot_satisfiesUpdate = HeapTupleSatisfiesUpdate; + amroutine->snapshot_satisfiesVacuum = HeapTupleSatisfiesVacuum; + PG_RETURN_POINTER(amroutine); } diff --git a/src/backend/utils/time/tqual.c b/src/backend/access/heap/heapam_visibility.c similarity index 80% rename from src/backend/utils/time/tqual.c rename to src/backend/access/heap/heapam_visibility.c index a821e2eed1..3bfddae506 100644 --- a/src/backend/utils/time/tqual.c +++ b/src/backend/access/heap/heapam_visibility.c @@ -1,7 +1,33 @@ /*------------------------------------------------------------------------- * - * tqual.c - * POSTGRES "time qualification" code, ie, tuple visibility rules. + * heapam_visibility.c + * heapam access method visibility functions + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/heap/heapam_visibility.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/heapam.h" +#include "access/heapam_xlog.h" +#include "access/hio.h" +#include "access/htup_details.h" +#include "access/multixact.h" +#include "storage/procarray.h" +#include "utils/builtins.h" +#include "utils/rel.h" +#include "utils/tqual.h" + +/*------------------------------------------------------------------------- + * + * POSTGRES "time qualification" code, ie, tuple visibility rules. * * NOTE: all the HeapTupleSatisfies routines will update the tuple's * "hint" status bits if we see that the inserting or deleting transaction @@ -45,108 +71,21 @@ * like HeapTupleSatisfiesSelf(), but includes open transactions * HeapTupleSatisfiesVacuum() * visible to any running transaction, used by VACUUM - * HeapTupleSatisfiesNonVacuumable() - * Snapshot-style API for HeapTupleSatisfiesVacuum + * HeapTupleSatisfiesNonVacuumable() + * Snapshot-style API for HeapTupleSatisfiesVacuum * HeapTupleSatisfiesToast() * visible unless part of interrupted vacuum, used for TOAST * HeapTupleSatisfiesAny() * all tuples are visible * - * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * IDENTIFICATION - * src/backend/utils/time/tqual.c - * - *------------------------------------------------------------------------- - */ - -#include "postgres.h" - -#include "access/htup_details.h" -#include "access/multixact.h" -#include "access/subtrans.h" -#include "access/transam.h" -#include "access/xact.h" -#include "access/xlog.h" -#include "storage/bufmgr.h" -#include "storage/procarray.h" -#include "utils/builtins.h" -#include "utils/combocid.h" -#include "utils/snapmgr.h" -#include "utils/tqual.h" - - -/* Static variables representing various special snapshot semantics */ -SnapshotData SnapshotSelfData = {HeapTupleSatisfiesSelf}; -SnapshotData SnapshotAnyData = {HeapTupleSatisfiesAny}; - - -/* - * SetHintBits() - * - * Set commit/abort hint bits on a tuple, if appropriate at this time. - * - * It is only safe to set a transaction-committed hint bit if we know the - * transaction's commit record is guaranteed to be flushed to disk before the - * buffer, or if the table is temporary or unlogged and will be obliterated by - * a crash anyway. We cannot change the LSN of the page here, because we may - * hold only a share lock on the buffer, so we can only use the LSN to - * interlock this if the buffer's LSN already is newer than the commit LSN; - * otherwise we have to just refrain from setting the hint bit until some - * future re-examination of the tuple. - * - * We can always set hint bits when marking a transaction aborted. (Some - * code in heapam.c relies on that!) - * - * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then - * we can always set the hint bits, since pre-9.0 VACUUM FULL always used - * synchronous commits and didn't move tuples that weren't previously - * hinted. (This is not known by this subroutine, but is applied by its - * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this - * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we - * support in-place update from pre-9.0 databases. - * - * Normal commits may be asynchronous, so for those we need to get the LSN - * of the transaction and then check whether this is flushed. - * - * The caller should pass xid as the XID of the transaction to check, or - * InvalidTransactionId if no check is needed. - */ -static inline void -SetHintBits(HeapTupleHeader tuple, Buffer buffer, - uint16 infomask, TransactionId xid) -{ - if (TransactionIdIsValid(xid)) - { - /* NB: xid must be known committed here! */ - XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid); - - if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) && - BufferGetLSNAtomic(buffer) < commitLSN) - { - /* not flushed and no LSN interlock, so don't set hint */ - return; - } - } - - tuple->t_infomask |= infomask; - MarkBufferDirtyHint(buffer, true); -} - -/* - * HeapTupleSetHintBits --- exported version of SetHintBits() - * - * This must be separate because of C99's brain-dead notions about how to - * implement inline functions. + * ------------------------------------------------------------------------- */ -void -HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer, - uint16 infomask, TransactionId xid) -{ - SetHintBits(tuple, buffer, infomask, xid); -} +extern bool HeapTupleSatisfies(StorageTuple stup, Snapshot snapshot, Buffer buffer); +extern HTSU_Result HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid, + Buffer buffer); +extern HTSV_Result HeapTupleSatisfiesVacuum(StorageTuple stup, TransactionId OldestXmin, + Buffer buffer); /* * HeapTupleSatisfiesSelf @@ -172,9 +111,10 @@ HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer, * (Xmax != my-transaction && the row was deleted by another transaction * Xmax is not committed))) that has not been committed */ -bool -HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer) +static bool +HeapTupleSatisfiesSelf(StorageTuple stup, Snapshot snapshot, Buffer buffer) { + HeapTuple htup = (HeapTuple) stup; HeapTupleHeader tuple = htup->t_data; Assert(ItemPointerIsValid(&htup->t_self)); @@ -342,8 +282,8 @@ HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer) * HeapTupleSatisfiesAny * Dummy "satisfies" routine: any tuple satisfies SnapshotAny. */ -bool -HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer) +static bool +HeapTupleSatisfiesAny(StorageTuple stup, Snapshot snapshot, Buffer buffer) { return true; } @@ -362,10 +302,11 @@ HeapTupleSatisfiesAny(HeapTuple htup, Snapshot snapshot, Buffer buffer) * Among other things, this means you can't do UPDATEs of rows in a TOAST * table. */ -bool -HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot, +static bool +HeapTupleSatisfiesToast(StorageTuple stup, Snapshot snapshot, Buffer buffer) { + HeapTuple htup = (HeapTuple) stup; HeapTupleHeader tuple = htup->t_data; Assert(ItemPointerIsValid(&htup->t_self)); @@ -457,9 +398,10 @@ HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot, * distinguish that case must test for it themselves.) */ HTSU_Result -HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid, +HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid, Buffer buffer) { + HeapTuple htup = (HeapTuple) stup; HeapTupleHeader tuple = htup->t_data; Assert(ItemPointerIsValid(&htup->t_self)); @@ -735,10 +677,11 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid, * on the insertion without aborting the whole transaction, the associated * token is also returned in snapshot->speculativeToken. */ -bool -HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot, +static bool +HeapTupleSatisfiesDirty(StorageTuple stup, Snapshot snapshot, Buffer buffer) { + HeapTuple htup = (HeapTuple) stup; HeapTupleHeader tuple = htup->t_data; Assert(ItemPointerIsValid(&htup->t_self)); @@ -959,10 +902,11 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot, * inserting/deleting transaction was still running --- which was more cycles * and more contention on the PGXACT array. */ -bool -HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot, +static bool +HeapTupleSatisfiesMVCC(StorageTuple stup, Snapshot snapshot, Buffer buffer) { + HeapTuple htup = (HeapTuple) stup; HeapTupleHeader tuple = htup->t_data; Assert(ItemPointerIsValid(&htup->t_self)); @@ -1161,9 +1105,10 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot, * even if we see that the deleting transaction has committed. */ HTSV_Result -HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, +HeapTupleSatisfiesVacuum(StorageTuple stup, TransactionId OldestXmin, Buffer buffer) { + HeapTuple htup = (HeapTuple) stup; HeapTupleHeader tuple = htup->t_data; Assert(ItemPointerIsValid(&htup->t_self)); @@ -1392,258 +1337,24 @@ HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, return HEAPTUPLE_DEAD; } - /* * HeapTupleSatisfiesNonVacuumable * - * True if tuple might be visible to some transaction; false if it's - * surely dead to everyone, ie, vacuumable. + * True if tuple might be visible to some transaction; false if it's + * surely dead to everyone, ie, vacuumable. * - * This is an interface to HeapTupleSatisfiesVacuum that meets the - * SnapshotSatisfiesFunc API, so it can be used through a Snapshot. - * snapshot->xmin must have been set up with the xmin horizon to use. + * This is an interface to HeapTupleSatisfiesVacuum that meets the + * SnapshotSatisfiesFunc API, so it can be used through a Snapshot. + * snapshot->xmin must have been set up with the xmin horizon to use. */ -bool -HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot, +static bool +HeapTupleSatisfiesNonVacuumable(StorageTuple htup, Snapshot snapshot, Buffer buffer) { return HeapTupleSatisfiesVacuum(htup, snapshot->xmin, buffer) != HEAPTUPLE_DEAD; } - -/* - * HeapTupleIsSurelyDead - * - * Cheaply determine whether a tuple is surely dead to all onlookers. - * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the - * tuple has just been tested by another visibility routine (usually - * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set - * should already be set. We assume that if no hint bits are set, the xmin - * or xmax transaction is still running. This is therefore faster than - * HeapTupleSatisfiesVacuum, because we don't consult PGXACT nor CLOG. - * It's okay to return false when in doubt, but we must return true only - * if the tuple is removable. - */ -bool -HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin) -{ - HeapTupleHeader tuple = htup->t_data; - - Assert(ItemPointerIsValid(&htup->t_self)); - Assert(htup->t_tableOid != InvalidOid); - - /* - * If the inserting transaction is marked invalid, then it aborted, and - * the tuple is definitely dead. If it's marked neither committed nor - * invalid, then we assume it's still alive (since the presumption is that - * all relevant hint bits were just set moments ago). - */ - if (!HeapTupleHeaderXminCommitted(tuple)) - return HeapTupleHeaderXminInvalid(tuple) ? true : false; - - /* - * If the inserting transaction committed, but any deleting transaction - * aborted, the tuple is still alive. - */ - if (tuple->t_infomask & HEAP_XMAX_INVALID) - return false; - - /* - * If the XMAX is just a lock, the tuple is still alive. - */ - if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) - return false; - - /* - * If the Xmax is a MultiXact, it might be dead or alive, but we cannot - * know without checking pg_multixact. - */ - if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) - return false; - - /* If deleter isn't known to have committed, assume it's still running. */ - if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) - return false; - - /* Deleter committed, so tuple is dead if the XID is old enough. */ - return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin); -} - -/* - * XidInMVCCSnapshot - * Is the given XID still-in-progress according to the snapshot? - * - * Note: GetSnapshotData never stores either top xid or subxids of our own - * backend into a snapshot, so these xids will not be reported as "running" - * by this function. This is OK for current uses, because we always check - * TransactionIdIsCurrentTransactionId first, except when it's known the - * XID could not be ours anyway. - */ -bool -XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot) -{ - uint32 i; - - /* - * Make a quick range check to eliminate most XIDs without looking at the - * xip arrays. Note that this is OK even if we convert a subxact XID to - * its parent below, because a subxact with XID < xmin has surely also got - * a parent with XID < xmin, while one with XID >= xmax must belong to a - * parent that was not yet committed at the time of this snapshot. - */ - - /* Any xid < xmin is not in-progress */ - if (TransactionIdPrecedes(xid, snapshot->xmin)) - return false; - /* Any xid >= xmax is in-progress */ - if (TransactionIdFollowsOrEquals(xid, snapshot->xmax)) - return true; - - /* - * Snapshot information is stored slightly differently in snapshots taken - * during recovery. - */ - if (!snapshot->takenDuringRecovery) - { - /* - * If the snapshot contains full subxact data, the fastest way to - * check things is just to compare the given XID against both subxact - * XIDs and top-level XIDs. If the snapshot overflowed, we have to - * use pg_subtrans to convert a subxact XID to its parent XID, but - * then we need only look at top-level XIDs not subxacts. - */ - if (!snapshot->suboverflowed) - { - /* we have full data, so search subxip */ - int32 j; - - for (j = 0; j < snapshot->subxcnt; j++) - { - if (TransactionIdEquals(xid, snapshot->subxip[j])) - return true; - } - - /* not there, fall through to search xip[] */ - } - else - { - /* - * Snapshot overflowed, so convert xid to top-level. This is safe - * because we eliminated too-old XIDs above. - */ - xid = SubTransGetTopmostTransaction(xid); - - /* - * If xid was indeed a subxact, we might now have an xid < xmin, - * so recheck to avoid an array scan. No point in rechecking - * xmax. - */ - if (TransactionIdPrecedes(xid, snapshot->xmin)) - return false; - } - - for (i = 0; i < snapshot->xcnt; i++) - { - if (TransactionIdEquals(xid, snapshot->xip[i])) - return true; - } - } - else - { - int32 j; - - /* - * In recovery we store all xids in the subxact array because it is by - * far the bigger array, and we mostly don't know which xids are - * top-level and which are subxacts. The xip array is empty. - * - * We start by searching subtrans, if we overflowed. - */ - if (snapshot->suboverflowed) - { - /* - * Snapshot overflowed, so convert xid to top-level. This is safe - * because we eliminated too-old XIDs above. - */ - xid = SubTransGetTopmostTransaction(xid); - - /* - * If xid was indeed a subxact, we might now have an xid < xmin, - * so recheck to avoid an array scan. No point in rechecking - * xmax. - */ - if (TransactionIdPrecedes(xid, snapshot->xmin)) - return false; - } - - /* - * We now have either a top-level xid higher than xmin or an - * indeterminate xid. We don't know whether it's top level or subxact - * but it doesn't matter. If it's present, the xid is visible. - */ - for (j = 0; j < snapshot->subxcnt; j++) - { - if (TransactionIdEquals(xid, snapshot->subxip[j])) - return true; - } - } - - return false; -} - -/* - * Is the tuple really only locked? That is, is it not updated? - * - * It's easy to check just infomask bits if the locker is not a multi; but - * otherwise we need to verify that the updating transaction has not aborted. - * - * This function is here because it follows the same time qualification rules - * laid out at the top of this file. - */ -bool -HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple) -{ - TransactionId xmax; - - /* if there's no valid Xmax, then there's obviously no update either */ - if (tuple->t_infomask & HEAP_XMAX_INVALID) - return true; - - if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY) - return true; - - /* invalid xmax means no update */ - if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple))) - return true; - - /* - * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must - * necessarily have been updated - */ - if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)) - return false; - - /* ... but if it's a multi, then perhaps the updating Xid aborted. */ - xmax = HeapTupleGetUpdateXid(tuple); - - /* not LOCKED_ONLY, so it has to have an xmax */ - Assert(TransactionIdIsValid(xmax)); - - if (TransactionIdIsCurrentTransactionId(xmax)) - return false; - if (TransactionIdIsInProgress(xmax)) - return false; - if (TransactionIdDidCommit(xmax)) - return false; - - /* - * not current, not in progress, not committed -- must have aborted or - * crashed - */ - return true; -} - /* * check whether the transaction id 'xid' is in the pre-sorted array 'xip'. */ @@ -1668,10 +1379,11 @@ TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num) * dangerous to do so as the semantics of doing so during timetravel are more * complicated than when dealing "only" with the present. */ -bool -HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot, +static bool +HeapTupleSatisfiesHistoricMVCC(StorageTuple stup, Snapshot snapshot, Buffer buffer) { + HeapTuple htup = (HeapTuple) stup; HeapTupleHeader tuple = htup->t_data; TransactionId xmin = HeapTupleHeaderGetXmin(tuple); TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple); @@ -1805,3 +1517,35 @@ HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot, else return true; } + +bool +HeapTupleSatisfies(StorageTuple stup, Snapshot snapshot, Buffer buffer) +{ + switch (snapshot->visibility_type) + { + case MVCC_VISIBILITY: + return HeapTupleSatisfiesMVCC(stup, snapshot, buffer); + break; + case SELF_VISIBILITY: + return HeapTupleSatisfiesSelf(stup, snapshot, buffer); + break; + case ANY_VISIBILITY: + return HeapTupleSatisfiesAny(stup, snapshot, buffer); + break; + case TOAST_VISIBILITY: + return HeapTupleSatisfiesToast(stup, snapshot, buffer); + break; + case DIRTY_VISIBILITY: + return HeapTupleSatisfiesDirty(stup, snapshot, buffer); + break; + case HISTORIC_MVCC_VISIBILITY: + return HeapTupleSatisfiesHistoricMVCC(stup, snapshot, buffer); + break; + case NON_VACUUMABLE_VISIBILTY: + return HeapTupleSatisfiesNonVacuumable(stup, snapshot, buffer); + break; + default: + Assert(0); + break; + } +} diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c index 9f33e0ce07..ed40357976 100644 --- a/src/backend/access/heap/pruneheap.c +++ b/src/backend/access/heap/pruneheap.c @@ -402,7 +402,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum, * either here or while following a chain below. Whichever path * gets there first will mark the tuple unused. */ - if (HeapTupleSatisfiesVacuum(&tup, OldestXmin, buffer) + if (relation->rd_stamroutine->snapshot_satisfiesVacuum(&tup, OldestXmin, buffer) == HEAPTUPLE_DEAD && !HeapTupleHeaderIsHotUpdated(htup)) { heap_prune_record_unused(prstate, rootoffnum); @@ -486,7 +486,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum, */ tupdead = recent_dead = false; - switch (HeapTupleSatisfiesVacuum(&tup, OldestXmin, buffer)) + switch (relation->rd_stamroutine->snapshot_satisfiesVacuum(&tup, OldestXmin, buffer)) { case HEAPTUPLE_DEAD: tupdead = true; diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c index 05d7da001a..01321a2543 100644 --- a/src/backend/access/index/genam.c +++ b/src/backend/access/index/genam.c @@ -472,7 +472,7 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup) Assert(BufferIsValid(scan->xs_cbuf)); /* must hold a buffer lock to call HeapTupleSatisfiesVisibility */ LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE); - result = HeapTupleSatisfiesVisibility(tup, freshsnap, scan->xs_cbuf); + result = HeapTupleSatisfiesVisibility(sysscan->heap_rel->rd_stamroutine, tup, freshsnap, scan->xs_cbuf); LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK); } else @@ -484,7 +484,7 @@ systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup) Assert(BufferIsValid(scan->rs_cbuf)); /* must hold a buffer lock to call HeapTupleSatisfiesVisibility */ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE); - result = HeapTupleSatisfiesVisibility(tup, freshsnap, scan->rs_cbuf); + result = HeapTupleSatisfiesVisibility(sysscan->heap_rel->rd_stamroutine, tup, freshsnap, scan->rs_cbuf); LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); } return result; diff --git a/src/backend/access/storage/Makefile b/src/backend/access/storage/Makefile index 2a05c7ce66..321676820f 100644 --- a/src/backend/access/storage/Makefile +++ b/src/backend/access/storage/Makefile @@ -12,6 +12,6 @@ subdir = src/backend/access/storage top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global -OBJS = storageam.o storageamapi.o +OBJS = storageam.o storageamapi.o storage_common.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/access/storage/storage_common.c b/src/backend/access/storage/storage_common.c new file mode 100644 index 0000000000..3ede680c89 --- /dev/null +++ b/src/backend/access/storage/storage_common.c @@ -0,0 +1,277 @@ +/*------------------------------------------------------------------------- + * + * storage_common.c + * storage access method code that is common across all pluggable + * storage modules + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/storage/storage_common.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/heapam.h" +#include "access/htup_details.h" +#include "access/storage_common.h" +#include "access/subtrans.h" +#include "access/transam.h" +#include "access/xact.h" +#include "access/xlog.h" +#include "storage/bufmgr.h" +#include "storage/procarray.h" + +/* Static variables representing various special snapshot semantics */ +SnapshotData SnapshotSelfData = {SELF_VISIBILITY}; +SnapshotData SnapshotAnyData = {ANY_VISIBILITY}; + +/* + * HeapTupleSetHintBits --- exported version of SetHintBits() + * + * This must be separate because of C99's brain-dead notions about how to + * implement inline functions. + */ +void +HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer, + uint16 infomask, TransactionId xid) +{ + SetHintBits(tuple, buffer, infomask, xid); +} + + +/* + * Is the tuple really only locked? That is, is it not updated? + * + * It's easy to check just infomask bits if the locker is not a multi; but + * otherwise we need to verify that the updating transaction has not aborted. + * + * This function is here because it follows the same time qualification rules + * laid out at the top of this file. + */ +bool +HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple) +{ + TransactionId xmax; + + /* if there's no valid Xmax, then there's obviously no update either */ + if (tuple->t_infomask & HEAP_XMAX_INVALID) + return true; + + if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY) + return true; + + /* invalid xmax means no update */ + if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple))) + return true; + + /* + * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must + * necessarily have been updated + */ + if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)) + return false; + + /* ... but if it's a multi, then perhaps the updating Xid aborted. */ + xmax = HeapTupleGetUpdateXid(tuple); + + /* not LOCKED_ONLY, so it has to have an xmax */ + Assert(TransactionIdIsValid(xmax)); + + if (TransactionIdIsCurrentTransactionId(xmax)) + return false; + if (TransactionIdIsInProgress(xmax)) + return false; + if (TransactionIdDidCommit(xmax)) + return false; + + /* + * not current, not in progress, not committed -- must have aborted or + * crashed + */ + return true; +} + + +/* + * HeapTupleIsSurelyDead + * + * Cheaply determine whether a tuple is surely dead to all onlookers. + * We sometimes use this in lieu of HeapTupleSatisfiesVacuum when the + * tuple has just been tested by another visibility routine (usually + * HeapTupleSatisfiesMVCC) and, therefore, any hint bits that can be set + * should already be set. We assume that if no hint bits are set, the xmin + * or xmax transaction is still running. This is therefore faster than + * HeapTupleSatisfiesVacuum, because we don't consult PGXACT nor CLOG. + * It's okay to return false when in doubt, but we must return TRUE only + * if the tuple is removable. + */ +bool +HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin) +{ + HeapTupleHeader tuple = htup->t_data; + + Assert(ItemPointerIsValid(&htup->t_self)); + Assert(htup->t_tableOid != InvalidOid); + + /* + * If the inserting transaction is marked invalid, then it aborted, and + * the tuple is definitely dead. If it's marked neither committed nor + * invalid, then we assume it's still alive (since the presumption is that + * all relevant hint bits were just set moments ago). + */ + if (!HeapTupleHeaderXminCommitted(tuple)) + return HeapTupleHeaderXminInvalid(tuple) ? true : false; + + /* + * If the inserting transaction committed, but any deleting transaction + * aborted, the tuple is still alive. + */ + if (tuple->t_infomask & HEAP_XMAX_INVALID) + return false; + + /* + * If the XMAX is just a lock, the tuple is still alive. + */ + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) + return false; + + /* + * If the Xmax is a MultiXact, it might be dead or alive, but we cannot + * know without checking pg_multixact. + */ + if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) + return false; + + /* If deleter isn't known to have committed, assume it's still running. */ + if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) + return false; + + /* Deleter committed, so tuple is dead if the XID is old enough. */ + return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin); +} + +/* + * XidInMVCCSnapshot + * Is the given XID still-in-progress according to the snapshot? + * + * Note: GetSnapshotData never stores either top xid or subxids of our own + * backend into a snapshot, so these xids will not be reported as "running" + * by this function. This is OK for current uses, because we always check + * TransactionIdIsCurrentTransactionId first, except when it's known the + * XID could not be ours anyway. + */ +bool +XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot) +{ + uint32 i; + + /* + * Make a quick range check to eliminate most XIDs without looking at the + * xip arrays. Note that this is OK even if we convert a subxact XID to + * its parent below, because a subxact with XID < xmin has surely also got + * a parent with XID < xmin, while one with XID >= xmax must belong to a + * parent that was not yet committed at the time of this snapshot. + */ + + /* Any xid < xmin is not in-progress */ + if (TransactionIdPrecedes(xid, snapshot->xmin)) + return false; + /* Any xid >= xmax is in-progress */ + if (TransactionIdFollowsOrEquals(xid, snapshot->xmax)) + return true; + + /* + * Snapshot information is stored slightly differently in snapshots taken + * during recovery. + */ + if (!snapshot->takenDuringRecovery) + { + /* + * If the snapshot contains full subxact data, the fastest way to + * check things is just to compare the given XID against both subxact + * XIDs and top-level XIDs. If the snapshot overflowed, we have to + * use pg_subtrans to convert a subxact XID to its parent XID, but + * then we need only look at top-level XIDs not subxacts. + */ + if (!snapshot->suboverflowed) + { + /* we have full data, so search subxip */ + int32 j; + + for (j = 0; j < snapshot->subxcnt; j++) + { + if (TransactionIdEquals(xid, snapshot->subxip[j])) + return true; + } + + /* not there, fall through to search xip[] */ + } + else + { + /* + * Snapshot overflowed, so convert xid to top-level. This is safe + * because we eliminated too-old XIDs above. + */ + xid = SubTransGetTopmostTransaction(xid); + + /* + * If xid was indeed a subxact, we might now have an xid < xmin, + * so recheck to avoid an array scan. No point in rechecking + * xmax. + */ + if (TransactionIdPrecedes(xid, snapshot->xmin)) + return false; + } + + for (i = 0; i < snapshot->xcnt; i++) + { + if (TransactionIdEquals(xid, snapshot->xip[i])) + return true; + } + } + else + { + int32 j; + + /* + * In recovery we store all xids in the subxact array because it is by + * far the bigger array, and we mostly don't know which xids are + * top-level and which are subxacts. The xip array is empty. + * + * We start by searching subtrans, if we overflowed. + */ + if (snapshot->suboverflowed) + { + /* + * Snapshot overflowed, so convert xid to top-level. This is safe + * because we eliminated too-old XIDs above. + */ + xid = SubTransGetTopmostTransaction(xid); + + /* + * If xid was indeed a subxact, we might now have an xid < xmin, + * so recheck to avoid an array scan. No point in rechecking + * xmax. + */ + if (TransactionIdPrecedes(xid, snapshot->xmin)) + return false; + } + + /* + * We now have either a top-level xid higher than xmin or an + * indeterminate xid. We don't know whether it's top level or subxact + * but it doesn't matter. If it's present, the xid is visible. + */ + for (j = 0; j < snapshot->subxcnt; j++) + { + if (TransactionIdEquals(xid, snapshot->subxip[j])) + return true; + } + } + + return false; +} diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 0125c18bc1..ead8d2abdf 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -2225,6 +2225,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, TransactionId OldestXmin; BlockNumber root_blkno = InvalidBlockNumber; OffsetNumber root_offsets[MaxHeapTuplesPerPage]; + StorageAmRoutine *method; /* * sanity checks @@ -2280,6 +2281,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, OldestXmin = GetOldestXmin(heapRelation, PROCARRAY_FLAGS_VACUUM); } + method = heapRelation->rd_stamroutine; scan = heap_beginscan_strat(heapRelation, /* relation */ snapshot, /* snapshot */ 0, /* number of keys */ @@ -2360,8 +2362,8 @@ IndexBuildHeapRangeScan(Relation heapRelation, */ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE); - switch (HeapTupleSatisfiesVacuum(heapTuple, OldestXmin, - scan->rs_cbuf)) + switch (method->snapshot_satisfiesVacuum(heapTuple, OldestXmin, + scan->rs_cbuf)) { case HEAPTUPLE_DEAD: /* Definitely dead, we can ignore it */ diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f952b3c732..4411c1d4de 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -1119,9 +1119,9 @@ acquire_sample_rows(Relation onerel, int elevel, targtuple.t_data = (HeapTupleHeader) PageGetItem(targpage, itemid); targtuple.t_len = ItemIdGetLength(itemid); - switch (HeapTupleSatisfiesVacuum(&targtuple, - OldestXmin, - targbuffer)) + switch (onerel->rd_stamroutine->snapshot_satisfiesVacuum(&targtuple, + OldestXmin, + targbuffer)) { case HEAPTUPLE_LIVE: sample_it = true; diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 48f1e6e2ad..dbcc5bc172 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -21,6 +21,7 @@ #include "access/multixact.h" #include "access/relscan.h" #include "access/rewriteheap.h" +#include "access/storageamapi.h" #include "access/transam.h" #include "access/tuptoaster.h" #include "access/xact.h" @@ -967,7 +968,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose, LockBuffer(buf, BUFFER_LOCK_SHARE); - switch (HeapTupleSatisfiesVacuum(tuple, OldestXmin, buf)) + switch (OldHeap->rd_stamroutine->snapshot_satisfiesVacuum(tuple, OldestXmin, buf)) { case HEAPTUPLE_DEAD: /* Definitely dead */ diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index 20ce431e46..04729e5c27 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -986,7 +986,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, tupgone = false; - switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf)) + switch (onerel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf)) { case HEAPTUPLE_DEAD: @@ -2151,7 +2151,7 @@ heap_page_is_all_visible(Relation rel, Buffer buf, tuple.t_len = ItemIdGetLength(itemid); tuple.t_tableOid = RelationGetRelid(rel); - switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf)) + switch (rel->rd_stamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf)) { case HEAPTUPLE_LIVE: { diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index eb5bbb57ef..2c5c95d425 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -461,7 +461,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres) loctup.t_len = ItemIdGetLength(lp); loctup.t_tableOid = scan->rs_rd->rd_id; ItemPointerSet(&loctup.t_self, page, offnum); - valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer); + valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, &loctup, snapshot, buffer); if (valid) { scan->rs_vistuples[ntup++] = offnum; diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index afb83ed3ae..79c34a6e6c 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -191,6 +191,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo, */ static void ExecCheckHeapTupleVisible(EState *estate, + Relation rel, HeapTuple tuple, Buffer buffer) { @@ -202,7 +203,7 @@ ExecCheckHeapTupleVisible(EState *estate, * Caller should be holding pin, but not lock. */ LockBuffer(buffer, BUFFER_LOCK_SHARE); - if (!HeapTupleSatisfiesVisibility(tuple, estate->es_snapshot, buffer)) + if (!HeapTupleSatisfiesVisibility(rel->rd_stamroutine, tuple, estate->es_snapshot, buffer)) { /* * We should not raise a serialization failure if the conflict is @@ -237,7 +238,7 @@ ExecCheckTIDVisible(EState *estate, tuple.t_self = *tid; if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL)) elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT"); - ExecCheckHeapTupleVisible(estate, &tuple, buffer); + ExecCheckHeapTupleVisible(estate, rel, &tuple, buffer); ReleaseBuffer(buffer); } @@ -1313,7 +1314,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, * snapshot. This is in line with the way UPDATE deals with newer tuple * versions. */ - ExecCheckHeapTupleVisible(estate, &tuple, buffer); + ExecCheckHeapTupleVisible(estate, relation, &tuple, buffer); /* Store target's existing tuple in the state's dedicated slot */ ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false); diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9c74a836e4..6a118d1883 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -588,7 +588,8 @@ SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan) else { /* Otherwise, we have to check the tuple individually. */ - return HeapTupleSatisfiesVisibility(tuple, + return HeapTupleSatisfiesVisibility(scan->rs_rd->rd_stamroutine, + tuple, scan->rs_snapshot, scan->rs_cbuf); } diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c index ad65b9831d..86efe3a66b 100644 --- a/src/backend/replication/logical/snapbuild.c +++ b/src/backend/replication/logical/snapbuild.c @@ -376,7 +376,7 @@ static void SnapBuildFreeSnapshot(Snapshot snap) { /* make sure we don't get passed an external snapshot */ - Assert(snap->satisfies == HeapTupleSatisfiesHistoricMVCC); + Assert(snap->visibility_type == HISTORIC_MVCC_VISIBILITY); /* make sure nobody modified our snapshot */ Assert(snap->curcid == FirstCommandId); @@ -434,7 +434,7 @@ void SnapBuildSnapDecRefcount(Snapshot snap) { /* make sure we don't get passed an external snapshot */ - Assert(snap->satisfies == HeapTupleSatisfiesHistoricMVCC); + Assert(snap->visibility_type == HISTORIC_MVCC_VISIBILITY); /* make sure nobody modified our snapshot */ Assert(snap->curcid == FirstCommandId); @@ -476,7 +476,7 @@ SnapBuildBuildSnapshot(SnapBuild *builder) snapshot = MemoryContextAllocZero(builder->context, ssize); - snapshot->satisfies = HeapTupleSatisfiesHistoricMVCC; + snapshot->visibility_type = HISTORIC_MVCC_VISIBILITY; /* * We misuse the original meaning of SnapshotData's xip and subxip fields diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c index 251a359bff..4fbad9f0f6 100644 --- a/src/backend/storage/lmgr/predicate.c +++ b/src/backend/storage/lmgr/predicate.c @@ -3972,7 +3972,7 @@ CheckForSerializableConflictOut(bool visible, Relation relation, * tuple is visible to us, while HeapTupleSatisfiesVacuum checks what else * is going on with it. */ - htsvResult = HeapTupleSatisfiesVacuum(tuple, TransactionXmin, buffer); + htsvResult = relation->rd_stamroutine->snapshot_satisfiesVacuum(tuple, TransactionXmin, buffer); switch (htsvResult) { case HEAPTUPLE_LIVE: diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index b1ae9e5f96..640e9634b3 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -287,7 +287,7 @@ RI_FKey_check(TriggerData *trigdata) * should be holding pin, but not lock. */ LockBuffer(new_row_buf, BUFFER_LOCK_SHARE); - if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf)) + if (!HeapTupleSatisfiesVisibility(trigdata->tg_relation->rd_stamroutine, new_row, SnapshotSelf, new_row_buf)) { LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK); return PointerGetDatum(NULL); diff --git a/src/backend/utils/time/Makefile b/src/backend/utils/time/Makefile index 5a6e6fa4c8..f17b1c5324 100644 --- a/src/backend/utils/time/Makefile +++ b/src/backend/utils/time/Makefile @@ -12,6 +12,6 @@ subdir = src/backend/utils/time top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global -OBJS = combocid.o tqual.o snapmgr.o +OBJS = combocid.o snapmgr.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c index 0b032905a5..b8f195593f 100644 --- a/src/backend/utils/time/snapmgr.c +++ b/src/backend/utils/time/snapmgr.c @@ -141,9 +141,9 @@ static volatile OldSnapshotControlData *oldSnapshotControl; * These SnapshotData structs are static to simplify memory allocation * (see the hack in GetSnapshotData to avoid repeated malloc/free). */ -static SnapshotData CurrentSnapshotData = {HeapTupleSatisfiesMVCC}; -static SnapshotData SecondarySnapshotData = {HeapTupleSatisfiesMVCC}; -SnapshotData CatalogSnapshotData = {HeapTupleSatisfiesMVCC}; +static SnapshotData CurrentSnapshotData = {MVCC_VISIBILITY}; +static SnapshotData SecondarySnapshotData = {MVCC_VISIBILITY}; +SnapshotData CatalogSnapshotData = {MVCC_VISIBILITY}; /* Pointers to valid snapshots */ static Snapshot CurrentSnapshot = NULL; @@ -2046,7 +2046,7 @@ EstimateSnapshotSpace(Snapshot snap) Size size; Assert(snap != InvalidSnapshot); - Assert(snap->satisfies == HeapTupleSatisfiesMVCC); + Assert(snap->visibility_type == MVCC_VISIBILITY); /* We allocate any XID arrays needed in the same palloc block. */ size = add_size(sizeof(SerializedSnapshotData), @@ -2143,7 +2143,7 @@ RestoreSnapshot(char *start_address) /* Copy all required fields */ snapshot = (Snapshot) MemoryContextAlloc(TopTransactionContext, size); - snapshot->satisfies = HeapTupleSatisfiesMVCC; + snapshot->visibility_type = MVCC_VISIBILITY; snapshot->xmin = serialized_snapshot.xmin; snapshot->xmax = serialized_snapshot.xmax; snapshot->xip = NULL; diff --git a/src/include/access/storage_common.h b/src/include/access/storage_common.h new file mode 100644 index 0000000000..791f42dd31 --- /dev/null +++ b/src/include/access/storage_common.h @@ -0,0 +1,98 @@ +/*------------------------------------------------------------------------- + * + * storage_common.h + * POSTGRES storage access method definitions shared across + * all pluggable storage methods and server. + * + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/access/storage_common.h + * + *------------------------------------------------------------------------- + */ +#ifndef STORAGE_COMMON_H +#define STORAGE_COMMON_H + +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/transam.h" +#include "access/xact.h" +#include "access/xlog.h" +#include "storage/bufpage.h" +#include "storage/bufmgr.h" + + +/* Result codes for HeapTupleSatisfiesVacuum */ +typedef enum +{ + HEAPTUPLE_DEAD, /* tuple is dead and deletable */ + HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */ + HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */ + HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */ + HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */ +} HTSV_Result; + + +/* in storage/storage_common.c */ +extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer, + uint16 infomask, TransactionId xid); +extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple); +extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin); +extern bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot); + +/* + * SetHintBits() + * + * Set commit/abort hint bits on a tuple, if appropriate at this time. + * + * It is only safe to set a transaction-committed hint bit if we know the + * transaction's commit record is guaranteed to be flushed to disk before the + * buffer, or if the table is temporary or unlogged and will be obliterated by + * a crash anyway. We cannot change the LSN of the page here, because we may + * hold only a share lock on the buffer, so we can only use the LSN to + * interlock this if the buffer's LSN already is newer than the commit LSN; + * otherwise we have to just refrain from setting the hint bit until some + * future re-examination of the tuple. + * + * We can always set hint bits when marking a transaction aborted. (Some + * code in heapam.c relies on that!) + * + * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then + * we can always set the hint bits, since pre-9.0 VACUUM FULL always used + * synchronous commits and didn't move tuples that weren't previously + * hinted. (This is not known by this subroutine, but is applied by its + * callers.) Note: old-style VACUUM FULL is gone, but we have to keep this + * module's support for MOVED_OFF/MOVED_IN flag bits for as long as we + * support in-place update from pre-9.0 databases. + * + * Normal commits may be asynchronous, so for those we need to get the LSN + * of the transaction and then check whether this is flushed. + * + * The caller should pass xid as the XID of the transaction to check, or + * InvalidTransactionId if no check is needed. + */ +static inline void +SetHintBits(HeapTupleHeader tuple, Buffer buffer, + uint16 infomask, TransactionId xid) +{ + if (TransactionIdIsValid(xid)) + { + /* NB: xid must be known committed here! */ + XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid); + + if (BufferIsPermanent(buffer) && XLogNeedsFlush(commitLSN) && + BufferGetLSNAtomic(buffer) < commitLSN) + { + /* not flushed and no LSN interlock, so don't set hint */ + return; + } + } + + tuple->t_infomask |= infomask; + MarkBufferDirtyHint(buffer, true); +} + +#endif /* STORAGE_COMMON_H */ diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h index 6fae4eea5c..f5d5edd704 100644 --- a/src/include/access/storageamapi.h +++ b/src/include/access/storageamapi.h @@ -11,12 +11,20 @@ #ifndef STORAGEAMAPI_H #define STORAGEAMAPI_H +#include "access/storage_common.h" #include "nodes/nodes.h" +#include "utils/snapshot.h" #include "fmgr.h" /* A physical tuple coming from a storage AM scan */ typedef void *StorageTuple; +typedef bool (*SnapshotSatisfies_function) (StorageTuple htup, Snapshot snapshot, Buffer buffer); +typedef HTSU_Result (*SnapshotSatisfiesUpdate_function) (StorageTuple htup, CommandId curcid, Buffer buffer); +typedef HTSV_Result (*SnapshotSatisfiesVacuum_function) (StorageTuple htup, TransactionId OldestXmin, Buffer buffer); + + + /* * API struct for a storage AM. Note this must be stored in a single palloc'd * chunk of memory. @@ -30,6 +38,10 @@ typedef struct StorageAmRoutine { NodeTag type; + SnapshotSatisfies_function snapshot_satisfies; + SnapshotSatisfiesUpdate_function snapshot_satisfiesUpdate; /* HeapTupleSatisfiesUpdate */ + SnapshotSatisfiesVacuum_function snapshot_satisfiesVacuum; /* HeapTupleSatisfiesVacuum */ + } StorageAmRoutine; extern StorageAmRoutine * GetStorageAmRoutine(Oid amhandler); diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h index 98b63fc5ba..1a403b8e21 100644 --- a/src/include/storage/bufmgr.h +++ b/src/include/storage/bufmgr.h @@ -20,7 +20,6 @@ #include "storage/relfilenode.h" #include "utils/relcache.h" #include "utils/snapmgr.h" -#include "utils/tqual.h" typedef void *Block; @@ -268,8 +267,8 @@ TestForOldSnapshot(Snapshot snapshot, Relation relation, Page page) if (old_snapshot_threshold >= 0 && (snapshot) != NULL - && ((snapshot)->satisfies == HeapTupleSatisfiesMVCC - || (snapshot)->satisfies == HeapTupleSatisfiesToast) + && ((snapshot)->visibility_type == MVCC_VISIBILITY + || (snapshot)->visibility_type == TOAST_VISIBILITY) && !XLogRecPtrIsInvalid((snapshot)->lsn) && PageGetLSN(page) > (snapshot)->lsn) TestForOldSnapshot_impl(snapshot, relation); diff --git a/src/include/utils/snapshot.h b/src/include/utils/snapshot.h index bf519778df..3896349b39 100644 --- a/src/include/utils/snapshot.h +++ b/src/include/utils/snapshot.h @@ -19,6 +19,18 @@ #include "lib/pairingheap.h" #include "storage/buf.h" +typedef enum tuple_visibility_type +{ + MVCC_VISIBILITY = 0, /* HeapTupleSatisfiesMVCC */ + SELF_VISIBILITY, /* HeapTupleSatisfiesSelf */ + ANY_VISIBILITY, /* HeapTupleSatisfiesAny */ + TOAST_VISIBILITY, /* HeapTupleSatisfiesToast */ + DIRTY_VISIBILITY, /* HeapTupleSatisfiesDirty */ + HISTORIC_MVCC_VISIBILITY, /* HeapTupleSatisfiesHistoricMVCC */ + NON_VACUUMABLE_VISIBILTY, /* HeapTupleSatisfiesNonVacuumable */ + + END_OF_VISIBILITY +} tuple_visibility_type; typedef struct SnapshotData *Snapshot; @@ -52,7 +64,7 @@ typedef bool (*SnapshotSatisfiesFunc) (HeapTuple htup, */ typedef struct SnapshotData { - SnapshotSatisfiesFunc satisfies; /* tuple test function */ + tuple_visibility_type visibility_type; /* tuple visibility test type */ /* * The remaining fields are used only for MVCC snapshots, and are normally diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h index 96eaf01ca0..c2ec33e57c 100644 --- a/src/include/utils/tqual.h +++ b/src/include/utils/tqual.h @@ -16,6 +16,7 @@ #define TQUAL_H #include "utils/snapshot.h" +#include "access/storageamapi.h" #include "access/xlogdefs.h" @@ -29,8 +30,8 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData; /* This macro encodes the knowledge of which snapshots are MVCC-safe */ #define IsMVCCSnapshot(snapshot) \ - ((snapshot)->satisfies == HeapTupleSatisfiesMVCC || \ - (snapshot)->satisfies == HeapTupleSatisfiesHistoricMVCC) + ((snapshot)->visibility_type == MVCC_VISIBILITY || \ + (snapshot)->visibility_type == HISTORIC_MVCC_VISIBILITY) /* * HeapTupleSatisfiesVisibility @@ -42,47 +43,8 @@ extern PGDLLIMPORT SnapshotData CatalogSnapshotData; * Hint bits in the HeapTuple's t_infomask may be updated as a side effect; * if so, the indicated buffer is marked dirty. */ -#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer) \ - ((*(snapshot)->satisfies) (tuple, snapshot, buffer)) - -/* Result codes for HeapTupleSatisfiesVacuum */ -typedef enum -{ - HEAPTUPLE_DEAD, /* tuple is dead and deletable */ - HEAPTUPLE_LIVE, /* tuple is live (committed, no deleter) */ - HEAPTUPLE_RECENTLY_DEAD, /* tuple is dead, but not deletable yet */ - HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */ - HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */ -} HTSV_Result; - -/* These are the "satisfies" test routines for the various snapshot types */ -extern bool HeapTupleSatisfiesMVCC(HeapTuple htup, - Snapshot snapshot, Buffer buffer); -extern bool HeapTupleSatisfiesSelf(HeapTuple htup, - Snapshot snapshot, Buffer buffer); -extern bool HeapTupleSatisfiesAny(HeapTuple htup, - Snapshot snapshot, Buffer buffer); -extern bool HeapTupleSatisfiesToast(HeapTuple htup, - Snapshot snapshot, Buffer buffer); -extern bool HeapTupleSatisfiesDirty(HeapTuple htup, - Snapshot snapshot, Buffer buffer); -extern bool HeapTupleSatisfiesNonVacuumable(HeapTuple htup, - Snapshot snapshot, Buffer buffer); -extern bool HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, - Snapshot snapshot, Buffer buffer); - -/* Special "satisfies" routines with different APIs */ -extern HTSU_Result HeapTupleSatisfiesUpdate(HeapTuple htup, - CommandId curcid, Buffer buffer); -extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, - TransactionId OldestXmin, Buffer buffer); -extern bool HeapTupleIsSurelyDead(HeapTuple htup, - TransactionId OldestXmin); -extern bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot); - -extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer, - uint16 infomask, TransactionId xid); -extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple); +#define HeapTupleSatisfiesVisibility(method, tuple, snapshot, buffer) \ + (((method)->snapshot_satisfies) (tuple, snapshot, buffer)) /* * To avoid leaking too much knowledge about reorderbuffer implementation @@ -101,14 +63,14 @@ extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data, * local variable of type SnapshotData, and initialize it with this macro. */ #define InitDirtySnapshot(snapshotdata) \ - ((snapshotdata).satisfies = HeapTupleSatisfiesDirty) + ((snapshotdata).visibility_type = DIRTY_VISIBILITY) /* * Similarly, some initialization is required for a NonVacuumable snapshot. * The caller must supply the xmin horizon to use (e.g., RecentGlobalXmin). */ #define InitNonVacuumableSnapshot(snapshotdata, xmin_horizon) \ - ((snapshotdata).satisfies = HeapTupleSatisfiesNonVacuumable, \ + ((snapshotdata).visibility_type = NON_VACUUMABLE_VISIBILTY, \ (snapshotdata).xmin = (xmin_horizon)) /* @@ -116,7 +78,7 @@ extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data, * to set lsn and whenTaken correctly to support snapshot_too_old. */ #define InitToastSnapshot(snapshotdata, l, w) \ - ((snapshotdata).satisfies = HeapTupleSatisfiesToast, \ + ((snapshotdata).visibility_type = TOAST_VISIBILITY, \ (snapshotdata).lsn = (l), \ (snapshotdata).whenTaken = (w)) -- 2.15.0.windows.1