From d78cba30fee704ae16adae8a11dfa558af569b97 Mon Sep 17 00:00:00 2001 From: Hari Babu Date: Thu, 29 Mar 2018 16:59:20 +1100 Subject: [PATCH 04/16] Adding tuple visibility functions to table AM Tuple visibility functions are now part of the heap table 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 table access methods is now moved into tableam_common.h file. --- contrib/amcheck/verify_nbtree.c | 2 +- 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 | 4 +- src/backend/access/heap/heapam.c | 61 ++++-- src/backend/access/heap/heapam_handler.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/nbtree/nbtsort.c | 2 +- src/backend/catalog/index.c | 7 +- 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/tableam_common.h | 41 ++++ src/include/access/tableamapi.h | 15 +- src/include/storage/bufmgr.h | 5 +- src/include/utils/snapshot.h | 14 +- src/include/utils/tqual.h | 54 +---- 30 files changed, 330 insertions(+), 221 deletions(-) rename src/backend/{utils/time/tqual.c => access/heap/heapam_visibility.c} (95%) create mode 100644 src/include/access/tableam_common.h diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c index a1438a2855..075e5548b9 100644 --- a/contrib/amcheck/verify_nbtree.c +++ b/contrib/amcheck/verify_nbtree.c @@ -35,7 +35,7 @@ #include "storage/lmgr.h" #include "utils/memutils.h" #include "utils/snapmgr.h" - +#include "utils/tqual.h" PG_MODULE_MAGIC; diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c index 944dea66fc..0102f3d1d7 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/tableamapi.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_tableamroutine->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 94e051d642..b0ed27e883 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/tableamapi.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_tableamroutine->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 ef33cacec6..e805981bb9 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); @@ -158,7 +159,7 @@ statapprox_heap(Relation rel, output_type *stat) * bother distinguishing tuples inserted/deleted by our own * transaction. */ - switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf)) + switch (rel->rd_tableamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf)) { case HEAPTUPLE_LIVE: case HEAPTUPLE_DELETE_IN_PROGRESS: diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c index 6d67bd8271..03f67c0beb 100644 --- a/contrib/pgstattuple/pgstattuple.c +++ b/contrib/pgstattuple/pgstattuple.c @@ -325,6 +325,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo) Buffer buffer; pgstattuple_type stat = {0}; SnapshotData SnapshotDirty; + TableAmRoutine *method = rel->rd_tableamroutine; /* Disable syncscan because we assume we scan from block zero upwards */ scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false); @@ -340,7 +341,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 87bea410f9..297ad9ddc1 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 heapam_handler.o hio.o pruneheap.o rewriteheap.o \ - syncscan.o tuptoaster.o visibilitymap.o +OBJS = heapam.o heapam_handler.o heapam_visibility.o hio.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 72395a50b8..ee407084c5 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/tableamapi.h" #include "access/sysattr.h" #include "access/transam.h" #include "access/tuptoaster.h" @@ -442,7 +443,7 @@ heapgetpage(HeapScanDesc scan, BlockNumber page) if (all_visible) valid = true; else - valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer); + valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_tableamroutine, &loctup, snapshot, buffer); CheckForSerializableConflictOut(valid, scan->rs_rd, &loctup, buffer, snapshot); @@ -657,7 +658,8 @@ heapgettup(HeapScanDesc scan, /* * if current tuple qualifies, return it. */ - valid = HeapTupleSatisfiesVisibility(tuple, + valid = HeapTupleSatisfiesVisibility(scan->rs_rd->rd_tableamroutine, + tuple, snapshot, scan->rs_cbuf); @@ -845,6 +847,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; @@ -889,6 +892,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; @@ -958,23 +962,31 @@ heapgettup_pagemode(HeapScanDesc scan, /* * if current tuple qualifies, return it. */ - if (key != NULL) + if (HeapTupleSatisfiesVisibility(scan->rs_rd->rd_tableamroutine, 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 @@ -986,6 +998,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. @@ -1043,6 +1061,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; @@ -1858,7 +1877,7 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction) pgstat_count_heap_getnext(scan->rs_rd); - return &(scan->rs_ctup); + return heap_copytuple(&(scan->rs_ctup)); } /* @@ -1977,7 +1996,7 @@ heap_fetch(Relation relation, /* * check time qualification of tuple, then release lock */ - valid = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer); + valid = HeapTupleSatisfiesVisibility(relation->rd_tableamroutine, tuple, snapshot, buffer); if (valid) PredicateLockTuple(relation, tuple, snapshot); @@ -2124,7 +2143,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_tableamroutine, heapTuple, snapshot, buffer); CheckForSerializableConflictOut(valid, relation, heapTuple, buffer, snapshot); /* reset to original, non-redirected, tid */ @@ -2298,7 +2317,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_tableamroutine, &tp, snapshot, buffer); CheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot); if (valid) *tid = ctid; @@ -3127,7 +3146,7 @@ heap_delete(Relation relation, ItemPointer tid, tp.t_self = *tid; l1: - result = HeapTupleSatisfiesUpdate(&tp, cid, buffer); + result = relation->rd_tableamroutine->snapshot_satisfiesUpdate(&tp, cid, buffer); if (result == HeapTupleInvisible) { @@ -3238,7 +3257,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_tableamroutine, &tp, crosscheck, buffer)) result = HeapTupleUpdated; } @@ -3707,7 +3726,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, l2: checked_lockers = false; locker_remains = false; - result = HeapTupleSatisfiesUpdate(&oldtup, cid, buffer); + result = relation->rd_tableamroutine->snapshot_satisfiesUpdate(&oldtup, cid, buffer); /* see below about the "no wait" case */ Assert(result != HeapTupleBeingUpdated || wait); @@ -3888,7 +3907,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_tableamroutine, &oldtup, crosscheck, buffer)) result = HeapTupleUpdated; } @@ -4728,7 +4747,7 @@ heap_lock_tuple(Relation relation, HeapTuple tuple, tuple->t_tableOid = RelationGetRelid(relation); l3: - result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer); + result = relation->rd_tableamroutine->snapshot_satisfiesUpdate(tuple, cid, *buffer); if (result == HeapTupleInvisible) { diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 6d4323152e..61086fe64c 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -20,6 +20,7 @@ */ #include "postgres.h" +#include "access/heapam.h" #include "access/tableamapi.h" #include "utils/builtins.h" @@ -29,5 +30,10 @@ heap_tableam_handler(PG_FUNCTION_ARGS) { TableAmRoutine *amroutine = makeNode(TableAmRoutine); + 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..c45575f049 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(TableTuple 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(TableTuple 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(TableTuple 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(TableTuple 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(TableTuple 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(TableTuple 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(TableTuple 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(TableTuple 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(TableTuple 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(TableTuple 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 c2f5343dac..1b00519137 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_tableamroutine->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_tableamroutine->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 58b4411796..c8e06fdef3 100644 --- a/src/backend/access/index/genam.c +++ b/src/backend/access/index/genam.c @@ -475,7 +475,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_tableamroutine, tup, freshsnap, scan->xs_cbuf); LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK); } else @@ -487,7 +487,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_tableamroutine, tup, freshsnap, scan->rs_cbuf); LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); } return result; diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c index e012df596e..89c2ba3285 100644 --- a/src/backend/access/nbtree/nbtsort.c +++ b/src/backend/access/nbtree/nbtsort.c @@ -80,7 +80,7 @@ #include "utils/rel.h" #include "utils/sortsupport.h" #include "utils/tuplesort.h" - +#include "utils/tqual.h" /* Magic numbers for parallel state sharing */ #define PARALLEL_KEY_BTREE_SHARED UINT64CONST(0xA000000000000001) diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 8b276bc430..9286e78bcc 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -2467,6 +2467,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, TransactionId OldestXmin; BlockNumber root_blkno = InvalidBlockNumber; OffsetNumber root_offsets[MaxHeapTuplesPerPage]; + TableAmRoutine *method; /* * sanity checks @@ -2560,6 +2561,8 @@ IndexBuildHeapRangeScan(Relation heapRelation, Assert(snapshot == SnapshotAny ? TransactionIdIsValid(OldestXmin) : !TransactionIdIsValid(OldestXmin)); Assert(snapshot == SnapshotAny || !anyvisible); + + method = heapRelation->rd_tableamroutine; /* set our scan endpoints */ if (!allow_sync) @@ -2640,8 +2643,8 @@ IndexBuildHeapRangeScan(Relation heapRelation, * CREATE INDEX and ANALYZE may produce wildly different reltuples * values, e.g. when there are many recently-dead tuples. */ - 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 25194e871c..cde34b9529 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_tableamroutine->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 482d463420..5e9462c63e 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/tableamapi.h" #include "access/transam.h" #include "access/tuptoaster.h" #include "access/xact.h" @@ -987,7 +988,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_tableamroutine->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 5649a70800..fc6c58982e 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -1009,7 +1009,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, * cases impossible (e.g. in-progress insert from the same * transaction). */ - switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf)) + switch (onerel->rd_tableamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf)) { case HEAPTUPLE_DEAD: @@ -2236,7 +2236,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_tableamroutine->snapshot_satisfiesVacuum(&tuple, OldestXmin, buf)) { case HEAPTUPLE_LIVE: { diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 3e1c9e0714..bdb82db149 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -459,7 +459,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_tableamroutine, &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 7e0b867971..85c67772de 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -200,6 +200,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo, */ static void ExecCheckHeapTupleVisible(EState *estate, + Relation rel, HeapTuple tuple, Buffer buffer) { @@ -211,7 +212,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_tableamroutine, tuple, estate->es_snapshot, buffer)) { /* * We should not raise a serialization failure if the conflict is @@ -246,7 +247,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); } @@ -1442,7 +1443,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 15177dbed7..950f88100c 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -566,7 +566,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_tableamroutine, + tuple, scan->rs_snapshot, scan->rs_cbuf); } diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c index 4123cdebcf..250aa92e80 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 e8390311d0..8167e14ec1 100644 --- a/src/backend/storage/lmgr/predicate.c +++ b/src/backend/storage/lmgr/predicate.c @@ -3926,7 +3926,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_tableamroutine->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 fc034ce601..c7d35961ad 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -286,7 +286,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_tableamroutine, 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 4b45d3cccd..407672e462 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 ca5cad7497..23f97df249 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/tableam_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(TableTuple stup, Snapshot snapshot, Buffer buffer); +extern HTSU_Result HeapTupleSatisfiesUpdate(TableTuple stup, CommandId curcid, + Buffer buffer); +extern HTSV_Result HeapTupleSatisfiesVacuum(TableTuple 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/tableam_common.h b/src/include/access/tableam_common.h new file mode 100644 index 0000000000..78b24d76c7 --- /dev/null +++ b/src/include/access/tableam_common.h @@ -0,0 +1,41 @@ +/*------------------------------------------------------------------------- + * + * tableam_common.h + * POSTGRES table access method definitions shared across + * all pluggable table access 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/tableam_common.h + * + *------------------------------------------------------------------------- + */ +#ifndef TABLEAM_COMMON_H +#define TABLEAM_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 table AM scan */ +typedef void *TableTuple; + +/* 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 /* TABLEAM_COMMON_H */ diff --git a/src/include/access/tableamapi.h b/src/include/access/tableamapi.h index 55ddad68fb..4bd50b48f1 100644 --- a/src/include/access/tableamapi.h +++ b/src/include/access/tableamapi.h @@ -11,11 +11,18 @@ #ifndef TABLEEAMAPI_H #define TABLEEAMAPI_H +#include "access/tableam_common.h" #include "nodes/nodes.h" #include "fmgr.h" +#include "utils/snapshot.h" -/* A physical tuple coming from a table AM scan */ -typedef void *TableTuple; + +/* + * Storage routine function hooks + */ +typedef bool (*SnapshotSatisfies_function) (TableTuple htup, Snapshot snapshot, Buffer buffer); +typedef HTSU_Result (*SnapshotSatisfiesUpdate_function) (TableTuple htup, CommandId curcid, Buffer buffer); +typedef HTSV_Result (*SnapshotSatisfiesVacuum_function) (TableTuple htup, TransactionId OldestXmin, Buffer buffer); /* * API struct for a table AM. Note this must be stored in a single palloc'd @@ -30,6 +37,10 @@ typedef struct TableAmRoutine { NodeTag type; + SnapshotSatisfies_function snapshot_satisfies; + SnapshotSatisfiesUpdate_function snapshot_satisfiesUpdate; /* HeapTupleSatisfiesUpdate */ + SnapshotSatisfiesVacuum_function snapshot_satisfiesVacuum; /* HeapTupleSatisfiesVacuum */ + } TableAmRoutine; extern TableAmRoutine * GetTableAmRoutine(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..075303b410 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/tableamapi.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.16.1.windows.4