From 6abc4434300f239594abb4105819701cdcc792e3 Mon Sep 17 00:00:00 2001 From: Hari Babu Date: Wed, 3 Jan 2018 17:09:40 +1100 Subject: [PATCH 04/12] 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 | 6 + .../tqual.c => access/heap/heapam_visibility.c} | 244 ++++++++++++--------- 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 | 26 +++ 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/heapam.h | 13 ++ src/include/access/storage_common.h | 41 ++++ src/include/access/storageamapi.h | 16 +- src/include/storage/bufmgr.h | 5 +- src/include/utils/snapshot.h | 14 +- src/include/utils/tqual.h | 54 +---- 30 files changed, 354 insertions(+), 219 deletions(-) rename src/backend/{utils/time/tqual.c => access/heap/heapam_visibility.c} (95%) 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 944dea66fc..fcc3e36a33 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 3cfbc08649..dc0c3753a9 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 dbc8f2d6c7..bc092056f8 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..a340c46a80 100644 --- a/src/backend/access/heap/heapam_storage.c +++ b/src/backend/access/heap/heapam_storage.c @@ -20,6 +20,7 @@ */ #include "postgres.h" +#include "access/heapam.h" #include "access/storageamapi.h" #include "utils/builtins.h" @@ -29,5 +30,10 @@ 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 95% rename from src/backend/utils/time/tqual.c rename to src/backend/access/heap/heapam_visibility.c index f7c4c9188c..daeb9bdb29 100644 --- a/src/backend/utils/time/tqual.c +++ b/src/backend/access/heap/heapam_visibility.c @@ -1,7 +1,6 @@ /*------------------------------------------------------------------------- * - * tqual.c - * POSTGRES "time qualification" code, ie, tuple visibility rules. + * 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 @@ -56,13 +55,14 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * src/backend/utils/time/tqual.c + * src/backend/access/heap/heapam_visibilty.c * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "access/heapam.h" #include "access/htup_details.h" #include "access/multixact.h" #include "access/subtrans.h" @@ -76,11 +76,9 @@ #include "utils/snapmgr.h" #include "utils/tqual.h" - /* Static variables representing various special snapshot semantics */ -SnapshotData SnapshotSelfData = {HeapTupleSatisfiesSelf}; -SnapshotData SnapshotAnyData = {HeapTupleSatisfiesAny}; - +SnapshotData SnapshotSelfData = {SELF_VISIBILITY}; +SnapshotData SnapshotAnyData = {ANY_VISIBILITY}; /* * SetHintBits() @@ -172,9 +170,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 +341,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 +361,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 +457,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 +736,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 +961,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 +1164,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)); @@ -1383,84 +1387,77 @@ 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 + * Is the tuple really only locked? That is, is it not updated? * - * 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. + * 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 -HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin) +HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple) { - HeapTupleHeader tuple = htup->t_data; + TransactionId xmax; - Assert(ItemPointerIsValid(&htup->t_self)); - Assert(htup->t_tableOid != InvalidOid); + /* if there's no valid Xmax, then there's obviously no update either */ + if (tuple->t_infomask & HEAP_XMAX_INVALID) + return true; - /* - * 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 (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY) + return true; - /* - * If the inserting transaction committed, but any deleting transaction - * aborted, the tuple is still alive. - */ - if (tuple->t_infomask & HEAP_XMAX_INVALID) - return false; + /* invalid xmax means no update */ + if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple))) + return true; /* - * If the XMAX is just a lock, the tuple is still alive. + * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must + * necessarily have been updated */ - if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) + if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)) 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; + /* ... but if it's a multi, then perhaps the updating Xid aborted. */ + xmax = HeapTupleGetUpdateXid(tuple); - /* If deleter isn't known to have committed, assume it's still running. */ - if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) + /* 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; - /* Deleter committed, so tuple is dead if the XID is old enough. */ - return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin); + /* + * not current, not in progress, not committed -- must have aborted or + * crashed + */ + return true; } + /* * XidInMVCCSnapshot * Is the given XID still-in-progress according to the snapshot? @@ -1584,55 +1581,61 @@ XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot) } /* - * 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. + * HeapTupleIsSurelyDead * - * This function is here because it follows the same time qualification rules - * laid out at the top of this file. + * 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 -HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple) +HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin) { - TransactionId xmax; - - /* if there's no valid Xmax, then there's obviously no update either */ - if (tuple->t_infomask & HEAP_XMAX_INVALID) - return true; + HeapTupleHeader tuple = htup->t_data; - if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY) - return true; + Assert(ItemPointerIsValid(&htup->t_self)); + Assert(htup->t_tableOid != InvalidOid); - /* invalid xmax means no update */ - if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple))) - return true; + /* + * 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 HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this must - * necessarily have been updated + * If the inserting transaction committed, but any deleting transaction + * aborted, the tuple is still alive. */ - if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)) + if (tuple->t_infomask & HEAP_XMAX_INVALID) 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)) + /* + * If the XMAX is just a lock, the tuple is still alive. + */ + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return false; /* - * not current, not in progress, not committed -- must have aborted or - * crashed + * If the Xmax is a MultiXact, it might be dead or alive, but we cannot + * know without checking pg_multixact. */ - return true; + 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); } /* @@ -1659,10 +1662,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); @@ -1796,3 +1800,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 f67d7d15df..f19814059e 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 214825114e..8f0a827b5d 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..e83da822ea --- /dev/null +++ b/src/backend/access/storage/storage_common.c @@ -0,0 +1,26 @@ +/*------------------------------------------------------------------------- + * + * 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" diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 330488b96f..16819abb68 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 5f21fcb5f4..e672529dce 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 eb73299199..27362cbbbf 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" @@ -971,7 +972,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 cf7f5e1162..2b36f680c5 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -988,7 +988,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: @@ -2162,7 +2162,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 7ba1db7d7e..5f11c94713 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 e52a3bb95e..b44fc7cda8 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 e88cd18737..e105f6758e 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 5b35f22a32..9c5329812d 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 d1ff2b1edc..f2f6e31b9d 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 8faae1d069..8da65d1d2e 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 e58c69dbd7..dd486fc6a3 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/heapam.h b/src/include/access/heapam.h index 4c0256b18a..5bbe55f18b 100644 --- a/src/include/access/heapam.h +++ b/src/include/access/heapam.h @@ -16,6 +16,7 @@ #include "access/sdir.h" #include "access/skey.h" +#include "access/storage_common.h" #include "nodes/lockoptions.h" #include "nodes/primnodes.h" #include "storage/bufpage.h" @@ -200,4 +201,16 @@ extern BlockNumber ss_get_location(Relation rel, BlockNumber relnblocks); extern void SyncScanShmemInit(void); extern Size SyncScanShmemSize(void); +/* in heap/heapam_visibility.c */ +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); +extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer, + uint16 infomask, TransactionId xid); +extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple); +extern bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot); +extern bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin); + #endif /* HEAPAM_H */ diff --git a/src/include/access/storage_common.h b/src/include/access/storage_common.h new file mode 100644 index 0000000000..ca140c1745 --- /dev/null +++ b/src/include/access/storage_common.h @@ -0,0 +1,41 @@ +/*------------------------------------------------------------------------- + * + * 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" + + +/* A physical tuple coming from a storage AM scan */ +typedef void *StorageTuple; + +/* 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; + +#endif /* STORAGE_COMMON_H */ diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h index 6fae4eea5c..56a791d0c6 100644 --- a/src/include/access/storageamapi.h +++ b/src/include/access/storageamapi.h @@ -11,11 +11,19 @@ #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; + +/* + * Storage routine function hooks + */ +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 @@ -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 3cce3906a0..95915bdc92 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 a8a5a8f4c0..ca96fd00fa 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 d3b6e99bb4..d62124b016 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