On 06/01/2025 23:30, Heikki Linnakangas wrote:
On 20/12/2024 19:31, Heikki Linnakangas wrote:
/*
* Struct representing all kind of possible snapshots.
*
* There are several different kinds of snapshots:
* * Normal MVCC snapshots
* * MVCC snapshots taken during recovery (in Hot-Standby mode)
* * Historic MVCC snapshots used during logical decoding
* * snapshots passed to HeapTupleSatisfiesDirty()
* * snapshots passed to HeapTupleSatisfiesNonVacuumable()
* * snapshots used for SatisfiesAny, Toast, Self where no members are
* accessed.
*
* TODO: It's probably a good idea to split this struct using a NodeTag
* similar to how parser and executor nodes are handled, with one
type for
* each different kind of snapshot to avoid overloading the meaning of
* individual fields.
*/
typedef struct SnapshotData
I'm thinking of implementing that TODO, splitting SnapshotData into
separate structs like MVCCSnapshotData, SnapshotDirtyData, etc. It
seems to me most places can assume that you're dealing with MVCC
snapshots, and if we had separate types for them, could be using
MVCCSnapshot instead of the generic Snapshot. Only the table and index
AM functions need to deal with non-MVCC snapshots.
Here's a draft of that. Going through this exercise clarified a few
things to me that I didn't realize before:
- The executor only deals with MVCC snapshots. Special snapshots are
only for the lower-level AM interfaces.
- Only MVCC snapshots can be pushed to the active stack
- Only MVCC or historic MVCC snapshots can be registered with a resource
owner
I committed the patches adding comments on Tuesday. Here's an updated
version of the patch to split SnapshotData into different structs.
The second, new patch simplifies the historic snapshot reference
counting during logical decoding. It's in principle independent from the
first patch, but it was hard to see how the opportunity before splitting
the structs.
--
Heikki Linnakangas
Neon (https://neon.tech)
From 987036f64f95862312cc141fa797bda79c33106e Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakan...@iki.fi>
Date: Fri, 20 Dec 2024 00:36:33 +0200
Subject: [PATCH v2 1/2] Split SnapshotData into separate structs for each kind
of snapshot
The SnapshotData fields were repurposed for different uses depending
the kind of snapshot. Split it into separate structs for different
kinds of snapshots, so that it is more clear which fields are used
with which snapshot kind, and the fields can have more descriptive
names.
---
contrib/amcheck/verify_heapam.c | 2 +-
contrib/amcheck/verify_nbtree.c | 2 +-
src/backend/access/heap/heapam.c | 3 +-
src/backend/access/heap/heapam_handler.c | 6 +-
src/backend/access/heap/heapam_visibility.c | 24 +--
src/backend/access/index/indexam.c | 11 +-
src/backend/access/nbtree/nbtinsert.c | 4 +-
src/backend/access/spgist/spgvacuum.c | 2 +-
src/backend/access/table/tableam.c | 8 +-
src/backend/access/transam/parallel.c | 14 +-
src/backend/catalog/pg_inherits.c | 2 +-
src/backend/commands/async.c | 4 +-
src/backend/commands/indexcmds.c | 4 +-
src/backend/commands/tablecmds.c | 2 +-
src/backend/executor/execIndexing.c | 4 +-
src/backend/executor/execReplication.c | 8 +-
src/backend/partitioning/partdesc.c | 2 +-
src/backend/replication/logical/decode.c | 2 +-
src/backend/replication/logical/origin.c | 4 +-
.../replication/logical/reorderbuffer.c | 114 +++++-----
src/backend/replication/logical/snapbuild.c | 114 +++++-----
src/backend/replication/walsender.c | 2 +-
src/backend/storage/ipc/procarray.c | 6 +-
src/backend/storage/lmgr/predicate.c | 32 +--
src/backend/utils/adt/xid8funcs.c | 4 +-
src/backend/utils/time/snapmgr.c | 198 +++++++++++-------
src/include/access/heapam.h | 2 +-
src/include/access/relscan.h | 6 +-
src/include/replication/reorderbuffer.h | 12 +-
src/include/replication/snapbuild.h | 6 +-
src/include/replication/snapbuild_internal.h | 2 +-
src/include/storage/predicate.h | 4 +-
src/include/storage/procarray.h | 2 +-
src/include/utils/snapmgr.h | 16 +-
src/include/utils/snapshot.h | 155 +++++++++-----
src/tools/pgindent/typedefs.list | 4 +
36 files changed, 451 insertions(+), 336 deletions(-)
diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index 827312306f6..2b25b281d80 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -276,7 +276,7 @@ verify_heapam(PG_FUNCTION_ARGS)
* Any xmin newer than the xmin of our snapshot can't become all-visible
* while we're running.
*/
- ctx.safe_xmin = GetTransactionSnapshot()->xmin;
+ ctx.safe_xmin = GetTransactionSnapshot()->mvcc.xmin;
/*
* If we report corruption when not examining some individual attribute,
diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index 825b677c47c..c7f312959c4 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -582,7 +582,7 @@ bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace,
*/
if (IsolationUsesXactSnapshot() && rel->rd_index->indcheckxmin &&
!TransactionIdPrecedes(HeapTupleHeaderGetXmin(rel->rd_indextuple->t_data),
- snapshot->xmin))
+ snapshot->mvcc.xmin))
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("index \"%s\" cannot be verified using transaction snapshot",
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index fa7935a0ed3..493aad2e8de 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -538,7 +538,8 @@ heap_prepare_pagescan(TableScanDesc sscan)
* full page write. Until we can prove that beyond doubt, let's check each
* tuple for visibility the hard way.
*/
- all_visible = PageIsAllVisible(page) && !snapshot->takenDuringRecovery;
+ all_visible = PageIsAllVisible(page) &&
+ (snapshot->snapshot_type != SNAPSHOT_MVCC || !snapshot->mvcc.takenDuringRecovery);
check_serializable =
CheckForSerializableConflictOutNeeded(scan->rs_base.rs_rd, snapshot);
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index d74f0fbc5cd..10f36fc1f93 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -386,7 +386,7 @@ tuple_lock_retry:
if (!ItemPointerEquals(&tmfd->ctid, &tuple->t_self))
{
- SnapshotData SnapshotDirty;
+ DirtySnapshotData SnapshotDirty;
TransactionId priorXmax;
/* it was updated, so look at the updated version */
@@ -411,7 +411,7 @@ tuple_lock_retry:
errmsg("tuple to be locked was already moved to another partition due to concurrent update")));
tuple->t_self = *tid;
- if (heap_fetch(relation, &SnapshotDirty, tuple, &buffer, true))
+ if (heap_fetch(relation, (Snapshot) &SnapshotDirty, tuple, &buffer, true))
{
/*
* If xmin isn't what we're expecting, the slot must have
@@ -2457,7 +2457,7 @@ heapam_scan_sample_next_tuple(TableScanDesc scan, SampleScanState *scanstate,
page = (Page) BufferGetPage(hscan->rs_cbuf);
all_visible = PageIsAllVisible(page) &&
- !scan->rs_snapshot->takenDuringRecovery;
+ (scan->rs_snapshot->snapshot_type != SNAPSHOT_MVCC || !scan->rs_snapshot->mvcc.takenDuringRecovery);
maxoffset = PageGetMaxOffsetNumber(page);
for (;;)
diff --git a/src/backend/access/heap/heapam_visibility.c b/src/backend/access/heap/heapam_visibility.c
index 05f6946fe60..f5d69b558f1 100644
--- a/src/backend/access/heap/heapam_visibility.c
+++ b/src/backend/access/heap/heapam_visibility.c
@@ -740,7 +740,7 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
* token is also returned in snapshot->speculativeToken.
*/
static bool
-HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
+HeapTupleSatisfiesDirty(HeapTuple htup, DirtySnapshotData *snapshot,
Buffer buffer)
{
HeapTupleHeader tuple = htup->t_data;
@@ -957,7 +957,7 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
* and more contention on ProcArrayLock.
*/
static bool
-HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
+HeapTupleSatisfiesMVCC(HeapTuple htup, MVCCSnapshot snapshot,
Buffer buffer)
{
HeapTupleHeader tuple = htup->t_data;
@@ -1435,7 +1435,7 @@ HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *de
* snapshot->vistest must have been set up with the horizon to use.
*/
static bool
-HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot,
+HeapTupleSatisfiesNonVacuumable(HeapTuple htup, NonVacuumableSnapshotData *snapshot,
Buffer buffer)
{
TransactionId dead_after = InvalidTransactionId;
@@ -1593,7 +1593,7 @@ TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
* complicated than when dealing "only" with the present.
*/
static bool
-HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
+HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, HistoricMVCCSnapshot snapshot,
Buffer buffer)
{
HeapTupleHeader tuple = htup->t_data;
@@ -1610,7 +1610,7 @@ HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
return false;
}
/* check if it's one of our txids, toplevel is also in there */
- else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt))
+ else if (TransactionIdInArray(xmin, snapshot->curxip, snapshot->curxcnt))
{
bool resolved;
CommandId cmin = HeapTupleHeaderGetRawCommandId(tuple);
@@ -1669,7 +1669,7 @@ HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
return false;
}
/* check if it's a committed transaction in [xmin, xmax) */
- else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt))
+ else if (TransactionIdInArray(xmin, snapshot->committed_xids, snapshot->xcnt))
{
/* fall through */
}
@@ -1702,7 +1702,7 @@ HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
}
/* check if it's one of our txids, toplevel is also in there */
- if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt))
+ if (TransactionIdInArray(xmax, snapshot->curxip, snapshot->curxcnt))
{
bool resolved;
CommandId cmin;
@@ -1755,7 +1755,7 @@ HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax))
return true;
/* xmax is between [xmin, xmax), check known committed array */
- else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt))
+ else if (TransactionIdInArray(xmax, snapshot->committed_xids, snapshot->xcnt))
return false;
/* xmax is between [xmin, xmax), but known not to have committed yet */
else
@@ -1778,7 +1778,7 @@ HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
switch (snapshot->snapshot_type)
{
case SNAPSHOT_MVCC:
- return HeapTupleSatisfiesMVCC(htup, snapshot, buffer);
+ return HeapTupleSatisfiesMVCC(htup, &snapshot->mvcc, buffer);
case SNAPSHOT_SELF:
return HeapTupleSatisfiesSelf(htup, snapshot, buffer);
case SNAPSHOT_ANY:
@@ -1786,11 +1786,11 @@ HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
case SNAPSHOT_TOAST:
return HeapTupleSatisfiesToast(htup, snapshot, buffer);
case SNAPSHOT_DIRTY:
- return HeapTupleSatisfiesDirty(htup, snapshot, buffer);
+ return HeapTupleSatisfiesDirty(htup, &snapshot->dirty, buffer);
case SNAPSHOT_HISTORIC_MVCC:
- return HeapTupleSatisfiesHistoricMVCC(htup, snapshot, buffer);
+ return HeapTupleSatisfiesHistoricMVCC(htup, &snapshot->historic_mvcc, buffer);
case SNAPSHOT_NON_VACUUMABLE:
- return HeapTupleSatisfiesNonVacuumable(htup, snapshot, buffer);
+ return HeapTupleSatisfiesNonVacuumable(htup, &snapshot->nonvacuumable, buffer);
}
return false; /* keep compiler quiet */
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 55ec4c10352..769170a37d5 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -469,7 +469,7 @@ index_parallelscan_estimate(Relation indexRelation, int nkeys, int norderbys,
RELATION_CHECKS;
nbytes = offsetof(ParallelIndexScanDescData, ps_snapshot_data);
- nbytes = add_size(nbytes, EstimateSnapshotSpace(snapshot));
+ nbytes = add_size(nbytes, EstimateSnapshotSpace(&snapshot->mvcc));
nbytes = MAXALIGN(nbytes);
if (instrument)
@@ -517,16 +517,17 @@ index_parallelscan_initialize(Relation heapRelation, Relation indexRelation,
Assert(instrument || parallel_aware);
RELATION_CHECKS;
+ Assert(snapshot->snapshot_type == SNAPSHOT_MVCC);
offset = add_size(offsetof(ParallelIndexScanDescData, ps_snapshot_data),
- EstimateSnapshotSpace(snapshot));
+ EstimateSnapshotSpace((MVCCSnapshot) snapshot));
offset = MAXALIGN(offset);
target->ps_locator = heapRelation->rd_locator;
target->ps_indexlocator = indexRelation->rd_locator;
target->ps_offset_ins = 0;
target->ps_offset_am = 0;
- SerializeSnapshot(snapshot, target->ps_snapshot_data);
+ SerializeSnapshot((MVCCSnapshot) snapshot, target->ps_snapshot_data);
if (instrument)
{
@@ -590,8 +591,8 @@ index_beginscan_parallel(Relation heaprel, Relation indexrel,
Assert(RelFileLocatorEquals(heaprel->rd_locator, pscan->ps_locator));
Assert(RelFileLocatorEquals(indexrel->rd_locator, pscan->ps_indexlocator));
- snapshot = RestoreSnapshot(pscan->ps_snapshot_data);
- RegisterSnapshot(snapshot);
+ snapshot = (Snapshot) RestoreSnapshot(pscan->ps_snapshot_data);
+ snapshot = RegisterSnapshot(snapshot);
scan = index_beginscan_internal(indexrel, nkeys, norderbys, snapshot,
pscan, true);
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index aa82cede30a..714e4ee3f0b 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -413,7 +413,7 @@ _bt_check_unique(Relation rel, BTInsertState insertstate, Relation heapRel,
IndexTuple curitup = NULL;
ItemId curitemid = NULL;
BTScanInsert itup_key = insertstate->itup_key;
- SnapshotData SnapshotDirty;
+ DirtySnapshotData SnapshotDirty;
OffsetNumber offset;
OffsetNumber maxoff;
Page page;
@@ -558,7 +558,7 @@ _bt_check_unique(Relation rel, BTInsertState insertstate, Relation heapRel,
* index entry for the entire chain.
*/
else if (table_index_fetch_tuple_check(heapRel, &htid,
- &SnapshotDirty,
+ (Snapshot) &SnapshotDirty,
&all_dead))
{
TransactionId xwait;
diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.c
index eeddacd0d52..524374a7dd5 100644
--- a/src/backend/access/spgist/spgvacuum.c
+++ b/src/backend/access/spgist/spgvacuum.c
@@ -811,7 +811,7 @@ spgvacuumscan(spgBulkDeleteState *bds)
/* Finish setting up spgBulkDeleteState */
initSpGistState(&bds->spgstate, index);
bds->pendingList = NULL;
- bds->myXmin = GetActiveSnapshot()->xmin;
+ bds->myXmin = GetActiveSnapshot()->mvcc.xmin;
bds->lastFilledBlock = SPGIST_LAST_FIXED_BLKNO;
/*
diff --git a/src/backend/access/table/tableam.c b/src/backend/access/table/tableam.c
index a56c5eceb14..4eb81e40d99 100644
--- a/src/backend/access/table/tableam.c
+++ b/src/backend/access/table/tableam.c
@@ -133,7 +133,7 @@ table_parallelscan_estimate(Relation rel, Snapshot snapshot)
Size sz = 0;
if (IsMVCCSnapshot(snapshot))
- sz = add_size(sz, EstimateSnapshotSpace(snapshot));
+ sz = add_size(sz, EstimateSnapshotSpace((MVCCSnapshot) snapshot));
else
Assert(snapshot == SnapshotAny);
@@ -152,7 +152,7 @@ table_parallelscan_initialize(Relation rel, ParallelTableScanDesc pscan,
if (IsMVCCSnapshot(snapshot))
{
- SerializeSnapshot(snapshot, (char *) pscan + pscan->phs_snapshot_off);
+ SerializeSnapshot((MVCCSnapshot) snapshot, (char *) pscan + pscan->phs_snapshot_off);
pscan->phs_snapshot_any = false;
}
else
@@ -174,8 +174,8 @@ table_beginscan_parallel(Relation relation, ParallelTableScanDesc pscan)
if (!pscan->phs_snapshot_any)
{
/* Snapshot was serialized -- restore it */
- snapshot = RestoreSnapshot((char *) pscan + pscan->phs_snapshot_off);
- RegisterSnapshot(snapshot);
+ snapshot = (Snapshot) RestoreSnapshot((char *) pscan + pscan->phs_snapshot_off);
+ snapshot = RegisterSnapshot(snapshot);
flags |= SO_TEMP_SNAPSHOT;
}
else
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index 94db1ec3012..8046e14abf7 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -275,10 +275,10 @@ InitializeParallelDSM(ParallelContext *pcxt)
shm_toc_estimate_chunk(&pcxt->estimator, combocidlen);
if (IsolationUsesXactSnapshot())
{
- tsnaplen = EstimateSnapshotSpace(transaction_snapshot);
+ tsnaplen = EstimateSnapshotSpace((MVCCSnapshot) transaction_snapshot);
shm_toc_estimate_chunk(&pcxt->estimator, tsnaplen);
}
- asnaplen = EstimateSnapshotSpace(active_snapshot);
+ asnaplen = EstimateSnapshotSpace((MVCCSnapshot) active_snapshot);
shm_toc_estimate_chunk(&pcxt->estimator, asnaplen);
tstatelen = EstimateTransactionStateSpace();
shm_toc_estimate_chunk(&pcxt->estimator, tstatelen);
@@ -400,14 +400,14 @@ InitializeParallelDSM(ParallelContext *pcxt)
if (IsolationUsesXactSnapshot())
{
tsnapspace = shm_toc_allocate(pcxt->toc, tsnaplen);
- SerializeSnapshot(transaction_snapshot, tsnapspace);
+ SerializeSnapshot((MVCCSnapshot) transaction_snapshot, tsnapspace);
shm_toc_insert(pcxt->toc, PARALLEL_KEY_TRANSACTION_SNAPSHOT,
tsnapspace);
}
/* Serialize the active snapshot. */
asnapspace = shm_toc_allocate(pcxt->toc, asnaplen);
- SerializeSnapshot(active_snapshot, asnapspace);
+ SerializeSnapshot((MVCCSnapshot) active_snapshot, asnapspace);
shm_toc_insert(pcxt->toc, PARALLEL_KEY_ACTIVE_SNAPSHOT, asnapspace);
/* Provide the handle for per-session segment. */
@@ -1493,9 +1493,9 @@ ParallelWorkerMain(Datum main_arg)
*/
asnapspace = shm_toc_lookup(toc, PARALLEL_KEY_ACTIVE_SNAPSHOT, false);
tsnapspace = shm_toc_lookup(toc, PARALLEL_KEY_TRANSACTION_SNAPSHOT, true);
- asnapshot = RestoreSnapshot(asnapspace);
- tsnapshot = tsnapspace ? RestoreSnapshot(tsnapspace) : asnapshot;
- RestoreTransactionSnapshot(tsnapshot,
+ asnapshot = (Snapshot) RestoreSnapshot(asnapspace);
+ tsnapshot = tsnapspace ? (Snapshot) RestoreSnapshot(tsnapspace) : asnapshot;
+ RestoreTransactionSnapshot((MVCCSnapshot) tsnapshot,
fps->parallel_leader_pgproc);
PushActiveSnapshot(asnapshot);
diff --git a/src/backend/catalog/pg_inherits.c b/src/backend/catalog/pg_inherits.c
index 929bb53b620..b658601bf77 100644
--- a/src/backend/catalog/pg_inherits.c
+++ b/src/backend/catalog/pg_inherits.c
@@ -148,7 +148,7 @@ find_inheritance_children_extended(Oid parentrelId, bool omit_detached,
xmin = HeapTupleHeaderGetXmin(inheritsTuple->t_data);
snap = GetActiveSnapshot();
- if (!XidInMVCCSnapshot(xmin, snap))
+ if (!XidInMVCCSnapshot(xmin, (MVCCSnapshot) snap))
{
if (detached_xmin)
{
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 4bd37d5beb5..1ffb6f5fa70 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -2022,6 +2022,8 @@ asyncQueueProcessPageEntries(volatile QueuePosition *current,
bool reachedEndOfPage;
AsyncQueueEntry *qe;
+ Assert(snapshot->snapshot_type == SNAPSHOT_MVCC);
+
do
{
QueuePosition thisentry = *current;
@@ -2041,7 +2043,7 @@ asyncQueueProcessPageEntries(volatile QueuePosition *current,
/* Ignore messages destined for other databases */
if (qe->dboid == MyDatabaseId)
{
- if (XidInMVCCSnapshot(qe->xid, snapshot))
+ if (XidInMVCCSnapshot(qe->xid, (MVCCSnapshot) snapshot))
{
/*
* The source transaction is still in progress, so we can't
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 32ff3ca9a28..06d1f4d0bd5 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -1767,7 +1767,7 @@ DefineIndex(Oid tableId,
* they must wait for. But first, save the snapshot's xmin to use as
* limitXmin for GetCurrentVirtualXIDs().
*/
- limitXmin = snapshot->xmin;
+ limitXmin = snapshot->mvcc.xmin;
PopActiveSnapshot();
UnregisterSnapshot(snapshot);
@@ -4162,7 +4162,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
* We can now do away with our active snapshot, we still need to save
* the xmin limit to wait for older snapshots.
*/
- limitXmin = snapshot->xmin;
+ limitXmin = snapshot->mvcc.xmin;
PopActiveSnapshot();
UnregisterSnapshot(snapshot);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 18ff8956577..ef92cc06812 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -20676,7 +20676,7 @@ ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
* all such queries are complete (otherwise we would present them with an
* inconsistent view of catalogs).
*/
- WaitForOlderSnapshots(snap->xmin, false);
+ WaitForOlderSnapshots(snap->mvcc.xmin, false);
DetachPartitionFinalize(rel, partRel, true, InvalidOid);
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index e3fe9b78bb5..a3955792729 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -717,7 +717,7 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
int indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index);
IndexScanDesc index_scan;
ScanKeyData scankeys[INDEX_MAX_KEYS];
- SnapshotData DirtySnapshot;
+ DirtySnapshotData DirtySnapshot;
int i;
bool conflict;
bool found_self;
@@ -816,7 +816,7 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
retry:
conflict = false;
found_self = false;
- index_scan = index_beginscan(heap, index, &DirtySnapshot, NULL, indnkeyatts, 0);
+ index_scan = index_beginscan(heap, index, (Snapshot) &DirtySnapshot, NULL, indnkeyatts, 0);
index_rescan(index_scan, scankeys, indnkeyatts, NULL, 0);
while (index_getnext_slot(index_scan, ForwardScanDirection, existing_slot))
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 0a9b880d250..3b817fb3a79 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -184,7 +184,7 @@ RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
ScanKeyData skey[INDEX_MAX_KEYS];
int skey_attoff;
IndexScanDesc scan;
- SnapshotData snap;
+ DirtySnapshotData snap;
TransactionId xwait;
Relation idxrel;
bool found;
@@ -202,7 +202,7 @@ RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
skey_attoff = build_replindex_scan_key(skey, rel, idxrel, searchslot);
/* Start an index scan. */
- scan = index_beginscan(rel, idxrel, &snap, NULL, skey_attoff, 0);
+ scan = index_beginscan(rel, idxrel, (Snapshot) &snap, NULL, skey_attoff, 0);
retry:
found = false;
@@ -357,7 +357,7 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
{
TupleTableSlot *scanslot;
TableScanDesc scan;
- SnapshotData snap;
+ DirtySnapshotData snap;
TypeCacheEntry **eq;
TransactionId xwait;
bool found;
@@ -369,7 +369,7 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
/* Start a heap scan. */
InitDirtySnapshot(snap);
- scan = table_beginscan(rel, &snap, 0, NULL);
+ scan = table_beginscan(rel, (Snapshot) &snap, 0, NULL);
scanslot = table_slot_create(rel, NULL);
retry:
diff --git a/src/backend/partitioning/partdesc.c b/src/backend/partitioning/partdesc.c
index 328b4d450e4..7c15c634181 100644
--- a/src/backend/partitioning/partdesc.c
+++ b/src/backend/partitioning/partdesc.c
@@ -102,7 +102,7 @@ RelationGetPartitionDesc(Relation rel, bool omit_detached)
Assert(TransactionIdIsValid(rel->rd_partdesc_nodetached_xmin));
activesnap = GetActiveSnapshot();
- if (!XidInMVCCSnapshot(rel->rd_partdesc_nodetached_xmin, activesnap))
+ if (!XidInMVCCSnapshot(rel->rd_partdesc_nodetached_xmin, &activesnap->mvcc))
return rel->rd_partdesc_nodetached;
}
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index 78f9a0a11c4..6a428e9720e 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -586,7 +586,7 @@ logicalmsg_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
TransactionId xid = XLogRecGetXid(r);
uint8 info = XLogRecGetInfo(r) & ~XLR_INFO_MASK;
RepOriginId origin_id = XLogRecGetOrigin(r);
- Snapshot snapshot = NULL;
+ HistoricMVCCSnapshot snapshot = NULL;
xl_logical_message *message;
if (info != XLOG_LOGICAL_MESSAGE)
diff --git a/src/backend/replication/logical/origin.c b/src/backend/replication/logical/origin.c
index c3c1d7a2a51..45e87e7b672 100644
--- a/src/backend/replication/logical/origin.c
+++ b/src/backend/replication/logical/origin.c
@@ -259,7 +259,7 @@ replorigin_create(const char *roname)
HeapTuple tuple = NULL;
Relation rel;
Datum roname_d;
- SnapshotData SnapshotDirty;
+ DirtySnapshotData SnapshotDirty;
SysScanDesc scan;
ScanKeyData key;
@@ -301,7 +301,7 @@ replorigin_create(const char *roname)
scan = systable_beginscan(rel, ReplicationOriginIdentIndex,
true /* indexOK */ ,
- &SnapshotDirty,
+ (Snapshot) &SnapshotDirty,
1, &key);
collides = HeapTupleIsValid(systable_getnext(scan));
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index 977fbcd2474..e8196a8d5d5 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -268,9 +268,9 @@ static void ReorderBufferSerializedPath(char *path, ReplicationSlot *slot,
TransactionId xid, XLogSegNo segno);
static int ReorderBufferTXNSizeCompare(const pairingheap_node *a, const pairingheap_node *b, void *arg);
-static void ReorderBufferFreeSnap(ReorderBuffer *rb, Snapshot snap);
-static Snapshot ReorderBufferCopySnap(ReorderBuffer *rb, Snapshot orig_snap,
- ReorderBufferTXN *txn, CommandId cid);
+static void ReorderBufferFreeSnap(ReorderBuffer *rb, HistoricMVCCSnapshot snap);
+static HistoricMVCCSnapshot ReorderBufferCopySnap(ReorderBuffer *rb, HistoricMVCCSnapshot orig_snap,
+ ReorderBufferTXN *txn, CommandId cid);
/*
* ---------------------------------------
@@ -852,7 +852,7 @@ ReorderBufferQueueChange(ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn,
*/
void
ReorderBufferQueueMessage(ReorderBuffer *rb, TransactionId xid,
- Snapshot snap, XLogRecPtr lsn,
+ HistoricMVCCSnapshot snap, XLogRecPtr lsn,
bool transactional, const char *prefix,
Size message_size, const char *message)
{
@@ -886,7 +886,7 @@ ReorderBufferQueueMessage(ReorderBuffer *rb, TransactionId xid,
else
{
ReorderBufferTXN *txn = NULL;
- volatile Snapshot snapshot_now = snap;
+ volatile HistoricMVCCSnapshot snapshot_now = snap;
/* Non-transactional changes require a valid snapshot. */
Assert(snapshot_now);
@@ -1886,55 +1886,55 @@ ReorderBufferBuildTupleCidHash(ReorderBuffer *rb, ReorderBufferTXN *txn)
* that catalog modifying transactions can look into intermediate catalog
* states.
*/
-static Snapshot
-ReorderBufferCopySnap(ReorderBuffer *rb, Snapshot orig_snap,
+static HistoricMVCCSnapshot
+ReorderBufferCopySnap(ReorderBuffer *rb, HistoricMVCCSnapshot orig_snap,
ReorderBufferTXN *txn, CommandId cid)
{
- Snapshot snap;
+ HistoricMVCCSnapshot snap;
dlist_iter iter;
int i = 0;
Size size;
- size = sizeof(SnapshotData) +
+ size = sizeof(HistoricMVCCSnapshotData) +
sizeof(TransactionId) * orig_snap->xcnt +
sizeof(TransactionId) * (txn->nsubtxns + 1);
snap = MemoryContextAllocZero(rb->context, size);
- memcpy(snap, orig_snap, sizeof(SnapshotData));
+ memcpy(snap, orig_snap, sizeof(HistoricMVCCSnapshotData));
snap->copied = true;
- snap->active_count = 1; /* mark as active so nobody frees it */
+ snap->refcount = 1; /* mark as active so nobody frees it */
snap->regd_count = 0;
- snap->xip = (TransactionId *) (snap + 1);
+ snap->committed_xids = (TransactionId *) (snap + 1);
- memcpy(snap->xip, orig_snap->xip, sizeof(TransactionId) * snap->xcnt);
+ memcpy(snap->committed_xids, orig_snap->committed_xids, sizeof(TransactionId) * snap->xcnt);
/*
- * snap->subxip contains all txids that belong to our transaction which we
+ * snap->curxip contains all txids that belong to our transaction which we
* need to check via cmin/cmax. That's why we store the toplevel
* transaction in there as well.
*/
- snap->subxip = snap->xip + snap->xcnt;
- snap->subxip[i++] = txn->xid;
+ snap->curxip = snap->committed_xids + snap->xcnt;
+ snap->curxip[i++] = txn->xid;
/*
* txn->nsubtxns isn't decreased when subtransactions abort, so count
* manually. Since it's an upper boundary it is safe to use it for the
* allocation above.
*/
- snap->subxcnt = 1;
+ snap->curxcnt = 1;
dlist_foreach(iter, &txn->subtxns)
{
ReorderBufferTXN *sub_txn;
sub_txn = dlist_container(ReorderBufferTXN, node, iter.cur);
- snap->subxip[i++] = sub_txn->xid;
- snap->subxcnt++;
+ snap->curxip[i++] = sub_txn->xid;
+ snap->curxcnt++;
}
/* sort so we can bsearch() later */
- qsort(snap->subxip, snap->subxcnt, sizeof(TransactionId), xidComparator);
+ qsort(snap->curxip, snap->curxcnt, sizeof(TransactionId), xidComparator);
/* store the specified current CommandId */
snap->curcid = cid;
@@ -1946,7 +1946,7 @@ ReorderBufferCopySnap(ReorderBuffer *rb, Snapshot orig_snap,
* Free a previously ReorderBufferCopySnap'ed snapshot
*/
static void
-ReorderBufferFreeSnap(ReorderBuffer *rb, Snapshot snap)
+ReorderBufferFreeSnap(ReorderBuffer *rb, HistoricMVCCSnapshot snap)
{
if (snap->copied)
pfree(snap);
@@ -2099,7 +2099,7 @@ ReorderBufferApplyMessage(ReorderBuffer *rb, ReorderBufferTXN *txn,
*/
static inline void
ReorderBufferSaveTXNSnapshot(ReorderBuffer *rb, ReorderBufferTXN *txn,
- Snapshot snapshot_now, CommandId command_id)
+ HistoricMVCCSnapshot snapshot_now, CommandId command_id)
{
txn->command_id = command_id;
@@ -2144,7 +2144,7 @@ ReorderBufferMaybeMarkTXNStreamed(ReorderBuffer *rb, ReorderBufferTXN *txn)
*/
static void
ReorderBufferResetTXN(ReorderBuffer *rb, ReorderBufferTXN *txn,
- Snapshot snapshot_now,
+ HistoricMVCCSnapshot snapshot_now,
CommandId command_id,
XLogRecPtr last_lsn,
ReorderBufferChange *specinsert)
@@ -2191,7 +2191,7 @@ ReorderBufferResetTXN(ReorderBuffer *rb, ReorderBufferTXN *txn,
static void
ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn,
XLogRecPtr commit_lsn,
- volatile Snapshot snapshot_now,
+ volatile HistoricMVCCSnapshot snapshot_now,
volatile CommandId command_id,
bool streaming)
{
@@ -2779,7 +2779,7 @@ ReorderBufferReplay(ReorderBufferTXN *txn,
TimestampTz commit_time,
RepOriginId origin_id, XLogRecPtr origin_lsn)
{
- Snapshot snapshot_now;
+ HistoricMVCCSnapshot snapshot_now;
CommandId command_id = FirstCommandId;
txn->final_lsn = commit_lsn;
@@ -3251,7 +3251,7 @@ ReorderBufferProcessXid(ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn)
*/
void
ReorderBufferAddSnapshot(ReorderBuffer *rb, TransactionId xid,
- XLogRecPtr lsn, Snapshot snap)
+ XLogRecPtr lsn, HistoricMVCCSnapshot snap)
{
ReorderBufferChange *change = ReorderBufferAllocChange(rb);
@@ -3269,7 +3269,7 @@ ReorderBufferAddSnapshot(ReorderBuffer *rb, TransactionId xid,
*/
void
ReorderBufferSetBaseSnapshot(ReorderBuffer *rb, TransactionId xid,
- XLogRecPtr lsn, Snapshot snap)
+ XLogRecPtr lsn, HistoricMVCCSnapshot snap)
{
ReorderBufferTXN *txn;
bool is_new;
@@ -4043,14 +4043,14 @@ ReorderBufferSerializeChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
}
case REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT:
{
- Snapshot snap;
+ HistoricMVCCSnapshot snap;
char *data;
snap = change->data.snapshot;
- sz += sizeof(SnapshotData) +
+ sz += sizeof(HistoricMVCCSnapshotData) +
sizeof(TransactionId) * snap->xcnt +
- sizeof(TransactionId) * snap->subxcnt;
+ sizeof(TransactionId) * snap->curxcnt;
/* make sure we have enough space */
ReorderBufferSerializeReserve(rb, sz);
@@ -4058,21 +4058,21 @@ ReorderBufferSerializeChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
/* might have been reallocated above */
ondisk = (ReorderBufferDiskChange *) rb->outbuf;
- memcpy(data, snap, sizeof(SnapshotData));
- data += sizeof(SnapshotData);
+ memcpy(data, snap, sizeof(HistoricMVCCSnapshotData));
+ data += sizeof(HistoricMVCCSnapshotData);
if (snap->xcnt)
{
- memcpy(data, snap->xip,
+ memcpy(data, snap->committed_xids,
sizeof(TransactionId) * snap->xcnt);
data += sizeof(TransactionId) * snap->xcnt;
}
- if (snap->subxcnt)
+ if (snap->curxcnt)
{
- memcpy(data, snap->subxip,
- sizeof(TransactionId) * snap->subxcnt);
- data += sizeof(TransactionId) * snap->subxcnt;
+ memcpy(data, snap->curxip,
+ sizeof(TransactionId) * snap->curxcnt);
+ data += sizeof(TransactionId) * snap->curxcnt;
}
break;
}
@@ -4177,7 +4177,7 @@ ReorderBufferCanStartStreaming(ReorderBuffer *rb)
static void
ReorderBufferStreamTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
{
- Snapshot snapshot_now;
+ HistoricMVCCSnapshot snapshot_now;
CommandId command_id;
Size stream_bytes;
bool txn_is_streamed;
@@ -4196,10 +4196,10 @@ ReorderBufferStreamTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
* After that we need to reuse the snapshot from the previous run.
*
* Unlike DecodeCommit which adds xids of all the subtransactions in
- * snapshot's xip array via SnapBuildCommitTxn, we can't do that here but
- * we do add them to subxip array instead via ReorderBufferCopySnap. This
- * allows the catalog changes made in subtransactions decoded till now to
- * be visible.
+ * snapshot's committed_xids array via SnapBuildCommitTxn, we can't do
+ * that here but we do add them to curxip array instead via
+ * ReorderBufferCopySnap. This allows the catalog changes made in
+ * subtransactions decoded till now to be visible.
*/
if (txn->snapshot_now == NULL)
{
@@ -4345,13 +4345,13 @@ ReorderBufferChangeSize(ReorderBufferChange *change)
}
case REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT:
{
- Snapshot snap;
+ HistoricMVCCSnapshot snap;
snap = change->data.snapshot;
- sz += sizeof(SnapshotData) +
+ sz += sizeof(HistoricMVCCSnapshotData) +
sizeof(TransactionId) * snap->xcnt +
- sizeof(TransactionId) * snap->subxcnt;
+ sizeof(TransactionId) * snap->curxcnt;
break;
}
@@ -4629,24 +4629,24 @@ ReorderBufferRestoreChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
}
case REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT:
{
- Snapshot oldsnap;
- Snapshot newsnap;
+ HistoricMVCCSnapshot oldsnap;
+ HistoricMVCCSnapshot newsnap;
Size size;
- oldsnap = (Snapshot) data;
+ oldsnap = (HistoricMVCCSnapshot) data;
- size = sizeof(SnapshotData) +
+ size = sizeof(HistoricMVCCSnapshotData) +
sizeof(TransactionId) * oldsnap->xcnt +
- sizeof(TransactionId) * (oldsnap->subxcnt + 0);
+ sizeof(TransactionId) * (oldsnap->curxcnt + 0);
change->data.snapshot = MemoryContextAllocZero(rb->context, size);
newsnap = change->data.snapshot;
memcpy(newsnap, data, size);
- newsnap->xip = (TransactionId *)
- (((char *) newsnap) + sizeof(SnapshotData));
- newsnap->subxip = newsnap->xip + newsnap->xcnt;
+ newsnap->committed_xids = (TransactionId *)
+ (((char *) newsnap) + sizeof(HistoricMVCCSnapshotData));
+ newsnap->curxip = newsnap->committed_xids + newsnap->xcnt;
newsnap->copied = true;
break;
}
@@ -5316,7 +5316,7 @@ file_sort_by_lsn(const ListCell *a_p, const ListCell *b_p)
* transaction for relid.
*/
static void
-UpdateLogicalMappings(HTAB *tuplecid_data, Oid relid, Snapshot snapshot)
+UpdateLogicalMappings(HTAB *tuplecid_data, Oid relid, HistoricMVCCSnapshot snapshot)
{
DIR *mapping_dir;
struct dirent *mapping_de;
@@ -5364,7 +5364,7 @@ UpdateLogicalMappings(HTAB *tuplecid_data, Oid relid, Snapshot snapshot)
continue;
/* not for our transaction */
- if (!TransactionIdInArray(f_mapped_xid, snapshot->subxip, snapshot->subxcnt))
+ if (!TransactionIdInArray(f_mapped_xid, snapshot->curxip, snapshot->curxcnt))
continue;
/* ok, relevant, queue for apply */
@@ -5383,7 +5383,7 @@ UpdateLogicalMappings(HTAB *tuplecid_data, Oid relid, Snapshot snapshot)
RewriteMappingFile *f = (RewriteMappingFile *) lfirst(file);
elog(DEBUG1, "applying mapping: \"%s\" in %u", f->fname,
- snapshot->subxip[0]);
+ snapshot->curxip[0]);
ApplyLogicalMappingFile(tuplecid_data, relid, f->fname);
pfree(f);
}
@@ -5395,7 +5395,7 @@ UpdateLogicalMappings(HTAB *tuplecid_data, Oid relid, Snapshot snapshot)
*/
bool
ResolveCminCmaxDuringDecoding(HTAB *tuplecid_data,
- Snapshot snapshot,
+ HistoricMVCCSnapshot snapshot,
HeapTuple htup, Buffer buffer,
CommandId *cmin, CommandId *cmax)
{
diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c
index b64e53de017..7a341418a74 100644
--- a/src/backend/replication/logical/snapbuild.c
+++ b/src/backend/replication/logical/snapbuild.c
@@ -155,11 +155,11 @@ static bool ExportInProgress = false;
static void SnapBuildPurgeOlderTxn(SnapBuild *builder);
/* snapshot building/manipulation/distribution functions */
-static Snapshot SnapBuildBuildSnapshot(SnapBuild *builder);
+static HistoricMVCCSnapshot SnapBuildBuildSnapshot(SnapBuild *builder);
-static void SnapBuildFreeSnapshot(Snapshot snap);
+static void SnapBuildFreeSnapshot(HistoricMVCCSnapshot snap);
-static void SnapBuildSnapIncRefcount(Snapshot snap);
+static void SnapBuildSnapIncRefcount(HistoricMVCCSnapshot snap);
static void SnapBuildDistributeNewCatalogSnapshot(SnapBuild *builder, XLogRecPtr lsn);
@@ -249,23 +249,21 @@ FreeSnapshotBuilder(SnapBuild *builder)
* Free an unreferenced snapshot that has previously been built by us.
*/
static void
-SnapBuildFreeSnapshot(Snapshot snap)
+SnapBuildFreeSnapshot(HistoricMVCCSnapshot snap)
{
/* make sure we don't get passed an external snapshot */
Assert(snap->snapshot_type == SNAPSHOT_HISTORIC_MVCC);
/* make sure nobody modified our snapshot */
Assert(snap->curcid == FirstCommandId);
- Assert(!snap->suboverflowed);
- Assert(!snap->takenDuringRecovery);
Assert(snap->regd_count == 0);
/* slightly more likely, so it's checked even without c-asserts */
if (snap->copied)
elog(ERROR, "cannot free a copied snapshot");
- if (snap->active_count)
- elog(ERROR, "cannot free an active snapshot");
+ if (snap->refcount)
+ elog(ERROR, "cannot free a snapshot that's in use");
pfree(snap);
}
@@ -313,9 +311,9 @@ SnapBuildXactNeedsSkip(SnapBuild *builder, XLogRecPtr ptr)
* adding a Snapshot as builder->snapshot.
*/
static void
-SnapBuildSnapIncRefcount(Snapshot snap)
+SnapBuildSnapIncRefcount(HistoricMVCCSnapshot snap)
{
- snap->active_count++;
+ snap->refcount++;
}
/*
@@ -325,26 +323,23 @@ SnapBuildSnapIncRefcount(Snapshot snap)
* IncRef'ed Snapshot can adjust its refcount easily.
*/
void
-SnapBuildSnapDecRefcount(Snapshot snap)
+SnapBuildSnapDecRefcount(HistoricMVCCSnapshot snap)
{
/* make sure we don't get passed an external snapshot */
Assert(snap->snapshot_type == SNAPSHOT_HISTORIC_MVCC);
/* make sure nobody modified our snapshot */
Assert(snap->curcid == FirstCommandId);
- Assert(!snap->suboverflowed);
- Assert(!snap->takenDuringRecovery);
+ Assert(snap->refcount > 0);
Assert(snap->regd_count == 0);
- Assert(snap->active_count > 0);
-
/* slightly more likely, so it's checked even without casserts */
if (snap->copied)
elog(ERROR, "cannot free a copied snapshot");
- snap->active_count--;
- if (snap->active_count == 0)
+ snap->refcount--;
+ if (snap->refcount == 0)
SnapBuildFreeSnapshot(snap);
}
@@ -356,15 +351,15 @@ SnapBuildSnapDecRefcount(Snapshot snap)
* these snapshots; they have to copy them and fill in appropriate ->curcid
* and ->subxip/subxcnt values.
*/
-static Snapshot
+static HistoricMVCCSnapshot
SnapBuildBuildSnapshot(SnapBuild *builder)
{
- Snapshot snapshot;
+ HistoricMVCCSnapshot snapshot;
Size ssize;
Assert(builder->state >= SNAPBUILD_FULL_SNAPSHOT);
- ssize = sizeof(SnapshotData)
+ ssize = sizeof(HistoricMVCCSnapshotData)
+ sizeof(TransactionId) * builder->committed.xcnt
+ sizeof(TransactionId) * 1 /* toplevel xid */ ;
@@ -400,31 +395,28 @@ SnapBuildBuildSnapshot(SnapBuild *builder)
snapshot->xmax = builder->xmax;
/* store all transactions to be treated as committed by this snapshot */
- snapshot->xip =
- (TransactionId *) ((char *) snapshot + sizeof(SnapshotData));
+ snapshot->committed_xids =
+ (TransactionId *) ((char *) snapshot + sizeof(HistoricMVCCSnapshotData));
snapshot->xcnt = builder->committed.xcnt;
- memcpy(snapshot->xip,
+ memcpy(snapshot->committed_xids,
builder->committed.xip,
builder->committed.xcnt * sizeof(TransactionId));
/* sort so we can bsearch() */
- qsort(snapshot->xip, snapshot->xcnt, sizeof(TransactionId), xidComparator);
+ qsort(snapshot->committed_xids, snapshot->xcnt, sizeof(TransactionId), xidComparator);
/*
- * Initially, subxip is empty, i.e. it's a snapshot to be used by
+ * Initially, curxip is empty, i.e. it's a snapshot to be used by
* transactions that don't modify the catalog. Will be filled by
* ReorderBufferCopySnap() if necessary.
*/
- snapshot->subxcnt = 0;
- snapshot->subxip = NULL;
+ snapshot->curxcnt = 0;
+ snapshot->curxip = NULL;
- snapshot->suboverflowed = false;
- snapshot->takenDuringRecovery = false;
snapshot->copied = false;
snapshot->curcid = FirstCommandId;
- snapshot->active_count = 0;
+ snapshot->refcount = 0;
snapshot->regd_count = 0;
- snapshot->snapXactCompletionCount = 0;
return snapshot;
}
@@ -436,13 +428,13 @@ SnapBuildBuildSnapshot(SnapBuild *builder)
* The snapshot will be usable directly in current transaction or exported
* for loading in different transaction.
*/
-Snapshot
+MVCCSnapshot
SnapBuildInitialSnapshot(SnapBuild *builder)
{
- Snapshot snap;
+ HistoricMVCCSnapshot historicsnap;
+ MVCCSnapshot mvccsnap;
TransactionId xid;
TransactionId safeXid;
- TransactionId *newxip;
int newxcnt = 0;
Assert(XactIsoLevel == XACT_REPEATABLE_READ);
@@ -464,10 +456,10 @@ SnapBuildInitialSnapshot(SnapBuild *builder)
if (TransactionIdIsValid(MyProc->xmin))
elog(ERROR, "cannot build an initial slot snapshot when MyProc->xmin already is valid");
- snap = SnapBuildBuildSnapshot(builder);
+ historicsnap = SnapBuildBuildSnapshot(builder);
/*
- * We know that snap->xmin is alive, enforced by the logical xmin
+ * We know that historicsnap->xmin is alive, enforced by the logical xmin
* mechanism. Due to that we can do this without locks, we're only
* changing our own value.
*
@@ -479,15 +471,18 @@ SnapBuildInitialSnapshot(SnapBuild *builder)
safeXid = GetOldestSafeDecodingTransactionId(false);
LWLockRelease(ProcArrayLock);
- if (TransactionIdFollows(safeXid, snap->xmin))
+ if (TransactionIdFollows(safeXid, historicsnap->xmin))
elog(ERROR, "cannot build an initial slot snapshot as oldest safe xid %u follows snapshot's xmin %u",
- safeXid, snap->xmin);
+ safeXid, historicsnap->xmin);
- MyProc->xmin = snap->xmin;
+ MyProc->xmin = historicsnap->xmin;
/* allocate in transaction context */
- newxip = (TransactionId *)
- palloc(sizeof(TransactionId) * GetMaxSnapshotXidCount());
+ mvccsnap = palloc(sizeof(MVCCSnapshotData) + sizeof(TransactionId) * GetMaxSnapshotXidCount());
+ mvccsnap->snapshot_type = SNAPSHOT_MVCC;
+ mvccsnap->xmin = historicsnap->xmin;
+ mvccsnap->xmax = historicsnap->xmax;
+ mvccsnap->xip = (TransactionId *) ((char *) mvccsnap + sizeof(MVCCSnapshotData));
/*
* snapbuild.c builds transactions in an "inverted" manner, which means it
@@ -495,15 +490,15 @@ SnapBuildInitialSnapshot(SnapBuild *builder)
* classical snapshot by marking all non-committed transactions as
* in-progress. This can be expensive.
*/
- for (xid = snap->xmin; NormalTransactionIdPrecedes(xid, snap->xmax);)
+ for (xid = historicsnap->xmin; NormalTransactionIdPrecedes(xid, historicsnap->xmax);)
{
void *test;
/*
- * Check whether transaction committed using the decoding snapshot
- * meaning of ->xip.
+ * Check whether transaction committed using the decoding snapshot's
+ * committed_xids array.
*/
- test = bsearch(&xid, snap->xip, snap->xcnt,
+ test = bsearch(&xid, historicsnap->committed_xids, historicsnap->xcnt,
sizeof(TransactionId), xidComparator);
if (test == NULL)
@@ -513,18 +508,27 @@ SnapBuildInitialSnapshot(SnapBuild *builder)
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("initial slot snapshot too large")));
- newxip[newxcnt++] = xid;
+ mvccsnap->xip[newxcnt++] = xid;
}
TransactionIdAdvance(xid);
}
-
- /* adjust remaining snapshot fields as needed */
- snap->snapshot_type = SNAPSHOT_MVCC;
- snap->xcnt = newxcnt;
- snap->xip = newxip;
-
- return snap;
+ mvccsnap->xcnt = newxcnt;
+
+ /* Initialize remaining MVCCSnapshot fields */
+ mvccsnap->subxip = NULL;
+ mvccsnap->subxcnt = 0;
+ mvccsnap->suboverflowed = false;
+ mvccsnap->takenDuringRecovery = false;
+ mvccsnap->copied = true;
+ mvccsnap->curcid = FirstCommandId;
+ mvccsnap->active_count = 0;
+ mvccsnap->regd_count = 0;
+ mvccsnap->snapXactCompletionCount = 0;
+
+ pfree(historicsnap);
+
+ return mvccsnap;
}
/*
@@ -538,7 +542,7 @@ SnapBuildInitialSnapshot(SnapBuild *builder)
const char *
SnapBuildExportSnapshot(SnapBuild *builder)
{
- Snapshot snap;
+ MVCCSnapshot snap;
char *snapname;
if (IsTransactionOrTransactionBlock())
@@ -575,7 +579,7 @@ SnapBuildExportSnapshot(SnapBuild *builder)
/*
* Ensure there is a snapshot and if not build one for current transaction.
*/
-Snapshot
+HistoricMVCCSnapshot
SnapBuildGetOrBuildSnapshot(SnapBuild *builder)
{
Assert(builder->state == SNAPBUILD_CONSISTENT);
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index d96121b3aad..8a749c89af1 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1305,7 +1305,7 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd)
}
else if (snapshot_action == CRS_USE_SNAPSHOT)
{
- Snapshot snap;
+ MVCCSnapshot snap;
snap = SnapBuildInitialSnapshot(ctx->snapshot_builder);
RestoreTransactionSnapshot(snap, MyProc);
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 2e54c11f880..b2751dfa63b 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -2092,7 +2092,7 @@ GetMaxSnapshotSubxidCount(void)
* least in the case we already hold a snapshot), but that's for another day.
*/
static bool
-GetSnapshotDataReuse(Snapshot snapshot)
+GetSnapshotDataReuse(MVCCSnapshot snapshot)
{
uint64 curXactCompletionCount;
@@ -2171,8 +2171,8 @@ GetSnapshotDataReuse(Snapshot snapshot)
* Note: this function should probably not be called with an argument that's
* not statically allocated (see xip allocation below).
*/
-Snapshot
-GetSnapshotData(Snapshot snapshot)
+MVCCSnapshot
+GetSnapshotData(MVCCSnapshot snapshot)
{
ProcArrayStruct *arrayP = procArray;
TransactionId *other_xids = ProcGlobal->xids;
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 5b21a053981..dd52782ff22 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -449,10 +449,10 @@ static void SerialSetActiveSerXmin(TransactionId xid);
static uint32 predicatelock_hash(const void *key, Size keysize);
static void SummarizeOldestCommittedSxact(void);
-static Snapshot GetSafeSnapshot(Snapshot origSnapshot);
-static Snapshot GetSerializableTransactionSnapshotInt(Snapshot snapshot,
- VirtualTransactionId *sourcevxid,
- int sourcepid);
+static MVCCSnapshot GetSafeSnapshot(MVCCSnapshot origSnapshot);
+static MVCCSnapshot GetSerializableTransactionSnapshotInt(MVCCSnapshot snapshot,
+ VirtualTransactionId *sourcevxid,
+ int sourcepid);
static bool PredicateLockExists(const PREDICATELOCKTARGETTAG *targettag);
static bool GetParentPredicateLockTag(const PREDICATELOCKTARGETTAG *tag,
PREDICATELOCKTARGETTAG *parent);
@@ -1544,10 +1544,10 @@ SummarizeOldestCommittedSxact(void)
* for), the passed-in Snapshot pointer should reference a static data
* area that can safely be passed to GetSnapshotData.
*/
-static Snapshot
-GetSafeSnapshot(Snapshot origSnapshot)
+static MVCCSnapshot
+GetSafeSnapshot(MVCCSnapshot origSnapshot)
{
- Snapshot snapshot;
+ MVCCSnapshot snapshot;
Assert(XactReadOnly && XactDeferrable);
@@ -1668,8 +1668,8 @@ GetSafeSnapshotBlockingPids(int blocked_pid, int *output, int output_size)
* always this same pointer; no new snapshot data structure is allocated
* within this function.
*/
-Snapshot
-GetSerializableTransactionSnapshot(Snapshot snapshot)
+MVCCSnapshot
+GetSerializableTransactionSnapshot(MVCCSnapshot snapshot)
{
Assert(IsolationIsSerializable());
@@ -1709,7 +1709,7 @@ GetSerializableTransactionSnapshot(Snapshot snapshot)
* read-only.
*/
void
-SetSerializableTransactionSnapshot(Snapshot snapshot,
+SetSerializableTransactionSnapshot(MVCCSnapshot snapshot,
VirtualTransactionId *sourcevxid,
int sourcepid)
{
@@ -1750,8 +1750,8 @@ SetSerializableTransactionSnapshot(Snapshot snapshot,
* source xact is still running after we acquire SerializableXactHashLock.
* We do that by calling ProcArrayInstallImportedXmin.
*/
-static Snapshot
-GetSerializableTransactionSnapshotInt(Snapshot snapshot,
+static MVCCSnapshot
+GetSerializableTransactionSnapshotInt(MVCCSnapshot snapshot,
VirtualTransactionId *sourcevxid,
int sourcepid)
{
@@ -3961,12 +3961,12 @@ ReleaseOneSerializableXact(SERIALIZABLEXACT *sxact, bool partial,
static bool
XidIsConcurrent(TransactionId xid)
{
- Snapshot snap;
+ MVCCSnapshot snap;
Assert(TransactionIdIsValid(xid));
Assert(!TransactionIdEquals(xid, GetTopTransactionIdIfAny()));
- snap = GetTransactionSnapshot();
+ snap = (MVCCSnapshot) GetTransactionSnapshot();
if (TransactionIdPrecedes(xid, snap->xmin))
return false;
@@ -4214,7 +4214,7 @@ CheckTargetForConflictsIn(PREDICATELOCKTARGETTAG *targettag)
}
else if (!SxactIsDoomed(sxact)
&& (!SxactIsCommitted(sxact)
- || TransactionIdPrecedes(GetTransactionSnapshot()->xmin,
+ || TransactionIdPrecedes(TransactionXmin,
sxact->finishedBefore))
&& !RWConflictExists(sxact, MySerializableXact))
{
@@ -4227,7 +4227,7 @@ CheckTargetForConflictsIn(PREDICATELOCKTARGETTAG *targettag)
*/
if (!SxactIsDoomed(sxact)
&& (!SxactIsCommitted(sxact)
- || TransactionIdPrecedes(GetTransactionSnapshot()->xmin,
+ || TransactionIdPrecedes(TransactionXmin,
sxact->finishedBefore))
&& !RWConflictExists(sxact, MySerializableXact))
{
diff --git a/src/backend/utils/adt/xid8funcs.c b/src/backend/utils/adt/xid8funcs.c
index 88d798fbf4b..0a27f0dd8a0 100644
--- a/src/backend/utils/adt/xid8funcs.c
+++ b/src/backend/utils/adt/xid8funcs.c
@@ -372,10 +372,10 @@ pg_current_snapshot(PG_FUNCTION_ARGS)
pg_snapshot *snap;
uint32 nxip,
i;
- Snapshot cur;
+ MVCCSnapshot cur;
FullTransactionId next_fxid = ReadNextFullTransactionId();
- cur = GetActiveSnapshot();
+ cur = (MVCCSnapshot) GetActiveSnapshot();
if (cur == NULL)
elog(ERROR, "no active snapshot set");
diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c
index ea35f30f494..78adb6d575a 100644
--- a/src/backend/utils/time/snapmgr.c
+++ b/src/backend/utils/time/snapmgr.c
@@ -137,18 +137,18 @@
* These SnapshotData structs are static to simplify memory allocation
* (see the hack in GetSnapshotData to avoid repeated malloc/free).
*/
-static SnapshotData CurrentSnapshotData = {SNAPSHOT_MVCC};
-static SnapshotData SecondarySnapshotData = {SNAPSHOT_MVCC};
-static SnapshotData CatalogSnapshotData = {SNAPSHOT_MVCC};
+static MVCCSnapshotData CurrentSnapshotData = {SNAPSHOT_MVCC};
+static MVCCSnapshotData SecondarySnapshotData = {SNAPSHOT_MVCC};
+static MVCCSnapshotData CatalogSnapshotData = {SNAPSHOT_MVCC};
SnapshotData SnapshotSelfData = {SNAPSHOT_SELF};
SnapshotData SnapshotAnyData = {SNAPSHOT_ANY};
SnapshotData SnapshotToastData = {SNAPSHOT_TOAST};
/* Pointers to valid snapshots */
-static Snapshot CurrentSnapshot = NULL;
-static Snapshot SecondarySnapshot = NULL;
-static Snapshot CatalogSnapshot = NULL;
-static Snapshot HistoricSnapshot = NULL;
+static MVCCSnapshot CurrentSnapshot = NULL;
+static MVCCSnapshot SecondarySnapshot = NULL;
+static MVCCSnapshot CatalogSnapshot = NULL;
+static HistoricMVCCSnapshot HistoricSnapshot = NULL;
/*
* These are updated by GetSnapshotData. We initialize them this way
@@ -171,7 +171,7 @@ static HTAB *tuplecid_data = NULL;
*/
typedef struct ActiveSnapshotElt
{
- Snapshot as_snap;
+ MVCCSnapshot as_snap;
int as_level;
struct ActiveSnapshotElt *as_next;
} ActiveSnapshotElt;
@@ -196,7 +196,7 @@ bool FirstSnapshotSet = false;
* FirstSnapshotSet in combination with IsolationUsesXactSnapshot(), because
* GUC may be reset before us, changing the value of IsolationUsesXactSnapshot.
*/
-static Snapshot FirstXactSnapshot = NULL;
+static MVCCSnapshot FirstXactSnapshot = NULL;
/* Define pathname of exported-snapshot files */
#define SNAPSHOT_EXPORT_DIR "pg_snapshots"
@@ -205,16 +205,16 @@ static Snapshot FirstXactSnapshot = NULL;
typedef struct ExportedSnapshot
{
char *snapfile;
- Snapshot snapshot;
+ MVCCSnapshot snapshot;
} ExportedSnapshot;
/* Current xact's exported snapshots (a list of ExportedSnapshot structs) */
static List *exportedSnapshots = NIL;
/* Prototypes for local functions */
-static Snapshot CopySnapshot(Snapshot snapshot);
+static MVCCSnapshot CopyMVCCSnapshot(MVCCSnapshot snapshot);
static void UnregisterSnapshotNoOwner(Snapshot snapshot);
-static void FreeSnapshot(Snapshot snapshot);
+static void FreeMVCCSnapshot(MVCCSnapshot snapshot);
static void SnapshotResetXmin(void);
/* ResourceOwner callbacks to track snapshot references */
@@ -308,8 +308,9 @@ GetTransactionSnapshot(void)
CurrentSnapshot = GetSerializableTransactionSnapshot(&CurrentSnapshotData);
else
CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData);
+
/* Make a saved copy */
- CurrentSnapshot = CopySnapshot(CurrentSnapshot);
+ CurrentSnapshot = CopyMVCCSnapshot(CurrentSnapshot);
FirstXactSnapshot = CurrentSnapshot;
/* Mark it as "registered" in FirstXactSnapshot */
FirstXactSnapshot->regd_count++;
@@ -319,18 +320,18 @@ GetTransactionSnapshot(void)
CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData);
FirstSnapshotSet = true;
- return CurrentSnapshot;
+ return (Snapshot) CurrentSnapshot;
}
if (IsolationUsesXactSnapshot())
- return CurrentSnapshot;
+ return (Snapshot) CurrentSnapshot;
/* Don't allow catalog snapshot to be older than xact snapshot. */
InvalidateCatalogSnapshot();
CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData);
- return CurrentSnapshot;
+ return (Snapshot) CurrentSnapshot;
}
/*
@@ -361,7 +362,7 @@ GetLatestSnapshot(void)
SecondarySnapshot = GetSnapshotData(&SecondarySnapshotData);
- return SecondarySnapshot;
+ return (Snapshot) SecondarySnapshot;
}
/*
@@ -380,7 +381,7 @@ GetCatalogSnapshot(Oid relid)
* finishing decoding.
*/
if (HistoricSnapshotActive())
- return HistoricSnapshot;
+ return (Snapshot) HistoricSnapshot;
return GetNonHistoricCatalogSnapshot(relid);
}
@@ -426,7 +427,7 @@ GetNonHistoricCatalogSnapshot(Oid relid)
pairingheap_add(&RegisteredSnapshots, &CatalogSnapshot->ph_node);
}
- return CatalogSnapshot;
+ return (Snapshot) CatalogSnapshot;
}
/*
@@ -495,7 +496,7 @@ SnapshotSetCommandId(CommandId curcid)
* in GetTransactionSnapshot.
*/
static void
-SetTransactionSnapshot(Snapshot sourcesnap, VirtualTransactionId *sourcevxid,
+SetTransactionSnapshot(MVCCSnapshot sourcesnap, VirtualTransactionId *sourcevxid,
int sourcepid, PGPROC *sourceproc)
{
/* Caller should have checked this already */
@@ -574,7 +575,7 @@ SetTransactionSnapshot(Snapshot sourcesnap, VirtualTransactionId *sourcevxid,
SetSerializableTransactionSnapshot(CurrentSnapshot, sourcevxid,
sourcepid);
/* Make a saved copy */
- CurrentSnapshot = CopySnapshot(CurrentSnapshot);
+ CurrentSnapshot = CopyMVCCSnapshot(CurrentSnapshot);
FirstXactSnapshot = CurrentSnapshot;
/* Mark it as "registered" in FirstXactSnapshot */
FirstXactSnapshot->regd_count++;
@@ -585,29 +586,27 @@ SetTransactionSnapshot(Snapshot sourcesnap, VirtualTransactionId *sourcevxid,
}
/*
- * CopySnapshot
+ * CopyMVCCSnapshot
* Copy the given snapshot.
*
* The copy is palloc'd in TopTransactionContext and has initial refcounts set
* to 0. The returned snapshot has the copied flag set.
*/
-static Snapshot
-CopySnapshot(Snapshot snapshot)
+static MVCCSnapshot
+CopyMVCCSnapshot(MVCCSnapshot snapshot)
{
- Snapshot newsnap;
+ MVCCSnapshot newsnap;
Size subxipoff;
Size size;
- Assert(snapshot != InvalidSnapshot);
-
/* We allocate any XID arrays needed in the same palloc block. */
- size = subxipoff = sizeof(SnapshotData) +
+ size = subxipoff = sizeof(MVCCSnapshotData) +
snapshot->xcnt * sizeof(TransactionId);
if (snapshot->subxcnt > 0)
size += snapshot->subxcnt * sizeof(TransactionId);
- newsnap = (Snapshot) MemoryContextAlloc(TopTransactionContext, size);
- memcpy(newsnap, snapshot, sizeof(SnapshotData));
+ newsnap = (MVCCSnapshot) MemoryContextAlloc(TopTransactionContext, size);
+ memcpy(newsnap, snapshot, sizeof(MVCCSnapshotData));
newsnap->regd_count = 0;
newsnap->active_count = 0;
@@ -644,11 +643,11 @@ CopySnapshot(Snapshot snapshot)
}
/*
- * FreeSnapshot
+ * FreeMVCCSnapshot
* Free the memory associated with a snapshot.
*/
static void
-FreeSnapshot(Snapshot snapshot)
+FreeMVCCSnapshot(MVCCSnapshot snapshot)
{
Assert(snapshot->regd_count == 0);
Assert(snapshot->active_count == 0);
@@ -664,6 +663,8 @@ FreeSnapshot(Snapshot snapshot)
* If the passed snapshot is a statically-allocated one, or it is possibly
* subject to a future command counter update, create a new long-lived copy
* with active refcount=1. Otherwise, only increment the refcount.
+ *
+ * Only regular MVCC snaphots can be used as the active snapshot.
*/
void
PushActiveSnapshot(Snapshot snapshot)
@@ -682,9 +683,12 @@ PushActiveSnapshot(Snapshot snapshot)
void
PushActiveSnapshotWithLevel(Snapshot snapshot, int snap_level)
{
+ MVCCSnapshot origsnap;
ActiveSnapshotElt *newactive;
- Assert(snapshot != InvalidSnapshot);
+ Assert(snapshot->snapshot_type == SNAPSHOT_MVCC);
+ origsnap = &snapshot->mvcc;
+
Assert(ActiveSnapshot == NULL || snap_level >= ActiveSnapshot->as_level);
newactive = MemoryContextAlloc(TopTransactionContext, sizeof(ActiveSnapshotElt));
@@ -693,11 +697,11 @@ PushActiveSnapshotWithLevel(Snapshot snapshot, int snap_level)
* Checking SecondarySnapshot is probably useless here, but it seems
* better to be sure.
*/
- if (snapshot == CurrentSnapshot || snapshot == SecondarySnapshot ||
- !snapshot->copied)
- newactive->as_snap = CopySnapshot(snapshot);
+ if (origsnap == CurrentSnapshot || origsnap == SecondarySnapshot ||
+ !origsnap->copied)
+ newactive->as_snap = CopyMVCCSnapshot(origsnap);
else
- newactive->as_snap = snapshot;
+ newactive->as_snap = origsnap;
newactive->as_next = ActiveSnapshot;
newactive->as_level = snap_level;
@@ -718,7 +722,8 @@ PushActiveSnapshotWithLevel(Snapshot snapshot, int snap_level)
void
PushCopiedSnapshot(Snapshot snapshot)
{
- PushActiveSnapshot(CopySnapshot(snapshot));
+ Assert(snapshot->snapshot_type == SNAPSHOT_MVCC);
+ PushActiveSnapshot((Snapshot) CopyMVCCSnapshot(&snapshot->mvcc));
}
/*
@@ -771,7 +776,7 @@ PopActiveSnapshot(void)
if (ActiveSnapshot->as_snap->active_count == 0 &&
ActiveSnapshot->as_snap->regd_count == 0)
- FreeSnapshot(ActiveSnapshot->as_snap);
+ FreeMVCCSnapshot(ActiveSnapshot->as_snap);
pfree(ActiveSnapshot);
ActiveSnapshot = newstack;
@@ -788,7 +793,7 @@ GetActiveSnapshot(void)
{
Assert(ActiveSnapshot != NULL);
- return ActiveSnapshot->as_snap;
+ return (Snapshot) ActiveSnapshot->as_snap;
}
/*
@@ -805,7 +810,8 @@ ActiveSnapshotSet(void)
* RegisterSnapshot
* Register a snapshot as being in use by the current resource owner
*
- * If InvalidSnapshot is passed, it is not registered.
+ * Only regular MVCC snaphots and "historic" MVCC snapshots can be registered.
+ * InvalidSnapshot is also accepted, as a no-op.
*/
Snapshot
RegisterSnapshot(Snapshot snapshot)
@@ -821,25 +827,39 @@ RegisterSnapshot(Snapshot snapshot)
* As above, but use the specified resource owner
*/
Snapshot
-RegisterSnapshotOnOwner(Snapshot snapshot, ResourceOwner owner)
+RegisterSnapshotOnOwner(Snapshot orig_snapshot, ResourceOwner owner)
{
- Snapshot snap;
+ MVCCSnapshot snapshot;
- if (snapshot == InvalidSnapshot)
+ if (orig_snapshot == InvalidSnapshot)
return InvalidSnapshot;
+ if (orig_snapshot->snapshot_type == SNAPSHOT_HISTORIC_MVCC)
+ {
+ HistoricMVCCSnapshot historicsnap = &orig_snapshot->historic_mvcc;
+
+ ResourceOwnerEnlarge(owner);
+ historicsnap->regd_count++;
+ ResourceOwnerRememberSnapshot(owner, (Snapshot) historicsnap);
+
+ return (Snapshot) historicsnap;
+ }
+
+ Assert(orig_snapshot->snapshot_type == SNAPSHOT_MVCC);
+ snapshot = &orig_snapshot->mvcc;
+
/* Static snapshot? Create a persistent copy */
- snap = snapshot->copied ? snapshot : CopySnapshot(snapshot);
+ snapshot = snapshot->copied ? snapshot : CopyMVCCSnapshot(snapshot);
/* and tell resowner.c about it */
ResourceOwnerEnlarge(owner);
- snap->regd_count++;
- ResourceOwnerRememberSnapshot(owner, snap);
+ snapshot->regd_count++;
+ ResourceOwnerRememberSnapshot(owner, (Snapshot) snapshot);
- if (snap->regd_count == 1)
- pairingheap_add(&RegisteredSnapshots, &snap->ph_node);
+ if (snapshot->regd_count == 1)
+ pairingheap_add(&RegisteredSnapshots, &snapshot->ph_node);
- return snap;
+ return (Snapshot) snapshot;
}
/*
@@ -875,18 +895,41 @@ UnregisterSnapshotFromOwner(Snapshot snapshot, ResourceOwner owner)
static void
UnregisterSnapshotNoOwner(Snapshot snapshot)
{
- Assert(snapshot->regd_count > 0);
- Assert(!pairingheap_is_empty(&RegisteredSnapshots));
+ if (snapshot->snapshot_type == SNAPSHOT_MVCC)
+ {
+ MVCCSnapshot mvccsnap = &snapshot->mvcc;
+
+ Assert(mvccsnap->regd_count > 0);
+ Assert(!pairingheap_is_empty(&RegisteredSnapshots));
- snapshot->regd_count--;
- if (snapshot->regd_count == 0)
- pairingheap_remove(&RegisteredSnapshots, &snapshot->ph_node);
+ mvccsnap->regd_count--;
+ if (mvccsnap->regd_count == 0)
+ pairingheap_remove(&RegisteredSnapshots, &mvccsnap->ph_node);
- if (snapshot->regd_count == 0 && snapshot->active_count == 0)
+ if (mvccsnap->regd_count == 0 && mvccsnap->active_count == 0)
+ {
+ FreeMVCCSnapshot(mvccsnap);
+ SnapshotResetXmin();
+ }
+ }
+ else if (snapshot->snapshot_type == SNAPSHOT_HISTORIC_MVCC)
{
- FreeSnapshot(snapshot);
- SnapshotResetXmin();
+ HistoricMVCCSnapshot historicsnap = &snapshot->historic_mvcc;
+
+ /*
+ * Historic snapshots don't rely on the resource owner machinery for
+ * cleanup, the snapbuild.c machinery ensures that whenever a historic
+ * snapshot is in use, it has a non-zero refcount. Registration is
+ * only supported so that the callers don't need to treat regular MVCC
+ * catalog snapshots and historic snapshots differently.
+ */
+ Assert(historicsnap->refcount > 0);
+
+ Assert(historicsnap->regd_count > 0);
+ historicsnap->regd_count--;
}
+ else
+ elog(ERROR, "registered snapshot has unexpected type");
}
/*
@@ -896,8 +939,8 @@ UnregisterSnapshotNoOwner(Snapshot snapshot)
static int
xmin_cmp(const pairingheap_node *a, const pairingheap_node *b, void *arg)
{
- const SnapshotData *asnap = pairingheap_const_container(SnapshotData, ph_node, a);
- const SnapshotData *bsnap = pairingheap_const_container(SnapshotData, ph_node, b);
+ const MVCCSnapshotData *asnap = pairingheap_const_container(MVCCSnapshotData, ph_node, a);
+ const MVCCSnapshotData *bsnap = pairingheap_const_container(MVCCSnapshotData, ph_node, b);
if (TransactionIdPrecedes(asnap->xmin, bsnap->xmin))
return 1;
@@ -923,7 +966,7 @@ xmin_cmp(const pairingheap_node *a, const pairingheap_node *b, void *arg)
static void
SnapshotResetXmin(void)
{
- Snapshot minSnapshot;
+ MVCCSnapshot minSnapshot;
if (ActiveSnapshot != NULL)
return;
@@ -934,7 +977,7 @@ SnapshotResetXmin(void)
return;
}
- minSnapshot = pairingheap_container(SnapshotData, ph_node,
+ minSnapshot = pairingheap_container(MVCCSnapshotData, ph_node,
pairingheap_first(&RegisteredSnapshots));
if (TransactionIdPrecedes(MyProc->xmin, minSnapshot->xmin))
@@ -984,7 +1027,7 @@ AtSubAbort_Snapshot(int level)
if (ActiveSnapshot->as_snap->active_count == 0 &&
ActiveSnapshot->as_snap->regd_count == 0)
- FreeSnapshot(ActiveSnapshot->as_snap);
+ FreeMVCCSnapshot(ActiveSnapshot->as_snap);
/* and free the stack element */
pfree(ActiveSnapshot);
@@ -1006,7 +1049,7 @@ AtEOXact_Snapshot(bool isCommit, bool resetXmin)
* In transaction-snapshot mode we must release our privately-managed
* reference to the transaction snapshot. We must remove it from
* RegisteredSnapshots to keep the check below happy. But we don't bother
- * to do FreeSnapshot, for two reasons: the memory will go away with
+ * to do FreeMVCCSnapshot, for two reasons: the memory will go away with
* TopTransactionContext anyway, and if someone has left the snapshot
* stacked as active, we don't want the code below to be chasing through a
* dangling pointer.
@@ -1099,7 +1142,7 @@ AtEOXact_Snapshot(bool isCommit, bool resetXmin)
* snapshot.
*/
char *
-ExportSnapshot(Snapshot snapshot)
+ExportSnapshot(MVCCSnapshot snapshot)
{
TransactionId topXid;
TransactionId *children;
@@ -1163,7 +1206,7 @@ ExportSnapshot(Snapshot snapshot)
* ensure that the snapshot's xmin is honored for the rest of the
* transaction.
*/
- snapshot = CopySnapshot(snapshot);
+ snapshot = CopyMVCCSnapshot(snapshot);
oldcxt = MemoryContextSwitchTo(TopTransactionContext);
esnap = (ExportedSnapshot *) palloc(sizeof(ExportedSnapshot));
@@ -1280,7 +1323,7 @@ pg_export_snapshot(PG_FUNCTION_ARGS)
{
char *snapshotName;
- snapshotName = ExportSnapshot(GetActiveSnapshot());
+ snapshotName = ExportSnapshot((MVCCSnapshot) GetActiveSnapshot());
PG_RETURN_TEXT_P(cstring_to_text(snapshotName));
}
@@ -1384,7 +1427,7 @@ ImportSnapshot(const char *idstr)
Oid src_dbid;
int src_isolevel;
bool src_readonly;
- SnapshotData snapshot;
+ MVCCSnapshotData snapshot;
/*
* Must be at top level of a fresh transaction. Note in particular that
@@ -1653,7 +1696,7 @@ HaveRegisteredOrActiveSnapshot(void)
* Needed for logical decoding.
*/
void
-SetupHistoricSnapshot(Snapshot historic_snapshot, HTAB *tuplecids)
+SetupHistoricSnapshot(HistoricMVCCSnapshot historic_snapshot, HTAB *tuplecids)
{
Assert(historic_snapshot != NULL);
@@ -1696,11 +1739,10 @@ HistoricSnapshotGetTupleCids(void)
* SerializedSnapshotData.
*/
Size
-EstimateSnapshotSpace(Snapshot snapshot)
+EstimateSnapshotSpace(MVCCSnapshot snapshot)
{
Size size;
- Assert(snapshot != InvalidSnapshot);
Assert(snapshot->snapshot_type == SNAPSHOT_MVCC);
/* We allocate any XID arrays needed in the same palloc block. */
@@ -1720,7 +1762,7 @@ EstimateSnapshotSpace(Snapshot snapshot)
* memory location at start_address.
*/
void
-SerializeSnapshot(Snapshot snapshot, char *start_address)
+SerializeSnapshot(MVCCSnapshot snapshot, char *start_address)
{
SerializedSnapshotData serialized_snapshot;
@@ -1776,12 +1818,12 @@ SerializeSnapshot(Snapshot snapshot, char *start_address)
* The copy is palloc'd in TopTransactionContext and has initial refcounts set
* to 0. The returned snapshot has the copied flag set.
*/
-Snapshot
+MVCCSnapshot
RestoreSnapshot(char *start_address)
{
SerializedSnapshotData serialized_snapshot;
Size size;
- Snapshot snapshot;
+ MVCCSnapshot snapshot;
TransactionId *serialized_xids;
memcpy(&serialized_snapshot, start_address,
@@ -1790,12 +1832,12 @@ RestoreSnapshot(char *start_address)
(start_address + sizeof(SerializedSnapshotData));
/* We allocate any XID arrays needed in the same palloc block. */
- size = sizeof(SnapshotData)
+ size = sizeof(MVCCSnapshotData)
+ serialized_snapshot.xcnt * sizeof(TransactionId)
+ serialized_snapshot.subxcnt * sizeof(TransactionId);
/* Copy all required fields */
- snapshot = (Snapshot) MemoryContextAlloc(TopTransactionContext, size);
+ snapshot = (MVCCSnapshot) MemoryContextAlloc(TopTransactionContext, size);
snapshot->snapshot_type = SNAPSHOT_MVCC;
snapshot->xmin = serialized_snapshot.xmin;
snapshot->xmax = serialized_snapshot.xmax;
@@ -1840,7 +1882,7 @@ RestoreSnapshot(char *start_address)
* the declaration for PGPROC.
*/
void
-RestoreTransactionSnapshot(Snapshot snapshot, void *source_pgproc)
+RestoreTransactionSnapshot(MVCCSnapshot snapshot, void *source_pgproc)
{
SetTransactionSnapshot(snapshot, NULL, InvalidPid, source_pgproc);
}
@@ -1856,7 +1898,7 @@ RestoreTransactionSnapshot(Snapshot snapshot, void *source_pgproc)
* XID could not be ours anyway.
*/
bool
-XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
+XidInMVCCSnapshot(TransactionId xid, MVCCSnapshot snapshot)
{
/*
* Make a quick range check to eliminate most XIDs without looking at the
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 1640d9c32f7..3d3ea109a4c 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -431,7 +431,7 @@ extern bool HeapTupleIsSurelyDead(HeapTuple htup,
*/
struct HTAB;
extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
- Snapshot snapshot,
+ HistoricMVCCSnapshot snapshot,
HeapTuple htup,
Buffer buffer,
CommandId *cmin, CommandId *cmax);
diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h
index b5e0fb386c0..2626f2996d8 100644
--- a/src/include/access/relscan.h
+++ b/src/include/access/relscan.h
@@ -34,7 +34,7 @@ typedef struct TableScanDescData
{
/* scan parameters */
Relation rs_rd; /* heap relation descriptor */
- struct SnapshotData *rs_snapshot; /* snapshot to see */
+ union SnapshotData *rs_snapshot; /* snapshot to see */
int rs_nkeys; /* number of scan keys */
struct ScanKeyData *rs_key; /* array of scan key descriptors */
@@ -135,7 +135,7 @@ typedef struct IndexScanDescData
/* scan parameters */
Relation heapRelation; /* heap relation descriptor, or NULL */
Relation indexRelation; /* index relation descriptor */
- struct SnapshotData *xs_snapshot; /* snapshot to see */
+ union SnapshotData *xs_snapshot; /* snapshot to see */
int numberOfKeys; /* number of index qualifier conditions */
int numberOfOrderBys; /* number of ordering operators */
struct ScanKeyData *keyData; /* array of index qualifier descriptors */
@@ -210,7 +210,7 @@ typedef struct SysScanDescData
Relation irel; /* NULL if doing heap scan */
struct TableScanDescData *scan; /* only valid in storage-scan case */
struct IndexScanDescData *iscan; /* only valid in index-scan case */
- struct SnapshotData *snapshot; /* snapshot to unregister at end of scan */
+ union SnapshotData *snapshot; /* snapshot to unregister at end of scan */
struct TupleTableSlot *slot;
} SysScanDescData;
diff --git a/src/include/replication/reorderbuffer.h b/src/include/replication/reorderbuffer.h
index 3be0cbd7ebe..8bf72c64c94 100644
--- a/src/include/replication/reorderbuffer.h
+++ b/src/include/replication/reorderbuffer.h
@@ -127,7 +127,7 @@ typedef struct ReorderBufferChange
} msg;
/* New snapshot, set when action == *_INTERNAL_SNAPSHOT */
- Snapshot snapshot;
+ HistoricMVCCSnapshot snapshot;
/*
* New command id for existing snapshot in a catalog changing tx. Set
@@ -359,7 +359,7 @@ typedef struct ReorderBufferTXN
* transaction modifies the catalog, or another catalog-modifying
* transaction commits.
*/
- Snapshot base_snapshot;
+ HistoricMVCCSnapshot base_snapshot;
XLogRecPtr base_snapshot_lsn;
dlist_node base_snapshot_node; /* link in txns_by_base_snapshot_lsn */
@@ -367,7 +367,7 @@ typedef struct ReorderBufferTXN
* Snapshot/CID from the previous streaming run. Only valid for already
* streamed transactions (NULL/InvalidCommandId otherwise).
*/
- Snapshot snapshot_now;
+ HistoricMVCCSnapshot snapshot_now;
CommandId command_id;
/*
@@ -703,7 +703,7 @@ extern void ReorderBufferQueueChange(ReorderBuffer *rb, TransactionId xid,
XLogRecPtr lsn, ReorderBufferChange *change,
bool toast_insert);
extern void ReorderBufferQueueMessage(ReorderBuffer *rb, TransactionId xid,
- Snapshot snap, XLogRecPtr lsn,
+ HistoricMVCCSnapshot snap, XLogRecPtr lsn,
bool transactional, const char *prefix,
Size message_size, const char *message);
extern void ReorderBufferCommit(ReorderBuffer *rb, TransactionId xid,
@@ -727,9 +727,9 @@ extern void ReorderBufferForget(ReorderBuffer *rb, TransactionId xid, XLogRecPtr
extern void ReorderBufferInvalidate(ReorderBuffer *rb, TransactionId xid, XLogRecPtr lsn);
extern void ReorderBufferSetBaseSnapshot(ReorderBuffer *rb, TransactionId xid,
- XLogRecPtr lsn, Snapshot snap);
+ XLogRecPtr lsn, HistoricMVCCSnapshot snap);
extern void ReorderBufferAddSnapshot(ReorderBuffer *rb, TransactionId xid,
- XLogRecPtr lsn, Snapshot snap);
+ XLogRecPtr lsn, HistoricMVCCSnapshot snap);
extern void ReorderBufferAddNewCommandId(ReorderBuffer *rb, TransactionId xid,
XLogRecPtr lsn, CommandId cid);
extern void ReorderBufferAddNewTupleCids(ReorderBuffer *rb, TransactionId xid,
diff --git a/src/include/replication/snapbuild.h b/src/include/replication/snapbuild.h
index 44031dcf6e3..5930ffb55a8 100644
--- a/src/include/replication/snapbuild.h
+++ b/src/include/replication/snapbuild.h
@@ -70,15 +70,15 @@ extern SnapBuild *AllocateSnapshotBuilder(struct ReorderBuffer *reorder,
XLogRecPtr two_phase_at);
extern void FreeSnapshotBuilder(SnapBuild *builder);
-extern void SnapBuildSnapDecRefcount(Snapshot snap);
+extern void SnapBuildSnapDecRefcount(HistoricMVCCSnapshot snap);
-extern Snapshot SnapBuildInitialSnapshot(SnapBuild *builder);
+extern MVCCSnapshot SnapBuildInitialSnapshot(SnapBuild *builder);
extern const char *SnapBuildExportSnapshot(SnapBuild *builder);
extern void SnapBuildClearExportedSnapshot(void);
extern void SnapBuildResetExportedSnapshotState(void);
extern SnapBuildState SnapBuildCurrentState(SnapBuild *builder);
-extern Snapshot SnapBuildGetOrBuildSnapshot(SnapBuild *builder);
+extern HistoricMVCCSnapshot SnapBuildGetOrBuildSnapshot(SnapBuild *builder);
extern bool SnapBuildXactNeedsSkip(SnapBuild *builder, XLogRecPtr ptr);
extern XLogRecPtr SnapBuildGetTwoPhaseAt(SnapBuild *builder);
diff --git a/src/include/replication/snapbuild_internal.h b/src/include/replication/snapbuild_internal.h
index 3b915dc8793..9bed20efa31 100644
--- a/src/include/replication/snapbuild_internal.h
+++ b/src/include/replication/snapbuild_internal.h
@@ -74,7 +74,7 @@ struct SnapBuild
/*
* Snapshot that's valid to see the catalog state seen at this moment.
*/
- Snapshot snapshot;
+ HistoricMVCCSnapshot snapshot;
/*
* LSN of the last location we are sure a snapshot has been serialized to.
diff --git a/src/include/storage/predicate.h b/src/include/storage/predicate.h
index 267d5d90e94..6a78dfeac96 100644
--- a/src/include/storage/predicate.h
+++ b/src/include/storage/predicate.h
@@ -47,8 +47,8 @@ extern void CheckPointPredicate(void);
extern bool PageIsPredicateLocked(Relation relation, BlockNumber blkno);
/* predicate lock maintenance */
-extern Snapshot GetSerializableTransactionSnapshot(Snapshot snapshot);
-extern void SetSerializableTransactionSnapshot(Snapshot snapshot,
+extern MVCCSnapshot GetSerializableTransactionSnapshot(MVCCSnapshot snapshot);
+extern void SetSerializableTransactionSnapshot(MVCCSnapshot snapshot,
VirtualTransactionId *sourcevxid,
int sourcepid);
extern void RegisterPredicateLockingXid(TransactionId xid);
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index ef0b733ebe8..7f5727c2586 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -44,7 +44,7 @@ extern void KnownAssignedTransactionIdsIdleMaintenance(void);
extern int GetMaxSnapshotXidCount(void);
extern int GetMaxSnapshotSubxidCount(void);
-extern Snapshot GetSnapshotData(Snapshot snapshot);
+extern MVCCSnapshot GetSnapshotData(MVCCSnapshot snapshot);
extern bool ProcArrayInstallImportedXmin(TransactionId xmin,
VirtualTransactionId *sourcevxid);
diff --git a/src/include/utils/snapmgr.h b/src/include/utils/snapmgr.h
index d346be71642..1f627ff966d 100644
--- a/src/include/utils/snapmgr.h
+++ b/src/include/utils/snapmgr.h
@@ -49,7 +49,7 @@ extern PGDLLIMPORT SnapshotData SnapshotToastData;
*/
#define InitNonVacuumableSnapshot(snapshotdata, vistestp) \
((snapshotdata).snapshot_type = SNAPSHOT_NON_VACUUMABLE, \
- (snapshotdata).vistest = (vistestp))
+ (snapshotdata).nonvacuumable.vistest = (vistestp))
/* This macro encodes the knowledge of which snapshots are MVCC-safe */
#define IsMVCCSnapshot(snapshot) \
@@ -89,7 +89,7 @@ extern void WaitForOlderSnapshots(TransactionId limitXmin, bool progress);
extern bool ThereAreNoPriorRegisteredSnapshots(void);
extern bool HaveRegisteredOrActiveSnapshot(void);
-extern char *ExportSnapshot(Snapshot snapshot);
+extern char *ExportSnapshot(MVCCSnapshot snapshot);
/*
* These live in procarray.c because they're intimately linked to the
@@ -105,18 +105,18 @@ extern bool GlobalVisCheckRemovableFullXid(Relation rel, FullTransactionId fxid)
/*
* Utility functions for implementing visibility routines in table AMs.
*/
-extern bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
+extern bool XidInMVCCSnapshot(TransactionId xid, MVCCSnapshot snapshot);
/* Support for catalog timetravel for logical decoding */
struct HTAB;
extern struct HTAB *HistoricSnapshotGetTupleCids(void);
-extern void SetupHistoricSnapshot(Snapshot historic_snapshot, struct HTAB *tuplecids);
+extern void SetupHistoricSnapshot(HistoricMVCCSnapshot historic_snapshot, struct HTAB *tuplecids);
extern void TeardownHistoricSnapshot(bool is_error);
extern bool HistoricSnapshotActive(void);
-extern Size EstimateSnapshotSpace(Snapshot snapshot);
-extern void SerializeSnapshot(Snapshot snapshot, char *start_address);
-extern Snapshot RestoreSnapshot(char *start_address);
-extern void RestoreTransactionSnapshot(Snapshot snapshot, void *source_pgproc);
+extern Size EstimateSnapshotSpace(MVCCSnapshot snapshot);
+extern void SerializeSnapshot(MVCCSnapshot snapshot, char *start_address);
+extern MVCCSnapshot RestoreSnapshot(char *start_address);
+extern void RestoreTransactionSnapshot(MVCCSnapshot snapshot, void *source_pgproc);
#endif /* SNAPMGR_H */
diff --git a/src/include/utils/snapshot.h b/src/include/utils/snapshot.h
index 0e546ec1497..93c1f51784f 100644
--- a/src/include/utils/snapshot.h
+++ b/src/include/utils/snapshot.h
@@ -17,7 +17,7 @@
/*
- * The different snapshot types. We use SnapshotData structures to represent
+ * The different snapshot types. We use the SnapshotData union to represent
* both "regular" (MVCC) snapshots and "special" snapshots that have non-MVCC
* semantics. The specific semantics of a snapshot are encoded by its type.
*
@@ -27,6 +27,9 @@
* The reason the snapshot type rather than a callback as it used to be is
* that that allows to use the same snapshot for different table AMs without
* having one callback per AM.
+ *
+ * The executor deals with MVCC snapshots, but the table AM and some other
+ * parts of the system also support the special snapshots.
*/
typedef enum SnapshotType
{
@@ -100,7 +103,9 @@ typedef enum SnapshotType
/*
* A tuple is visible iff it follows the rules of SNAPSHOT_MVCC, but
* supports being called in timetravel context (for decoding catalog
- * contents in the context of logical decoding).
+ * contents in the context of logical decoding). A historic MVCC snapshot
+ * should only be used on catalog tables, as we only track XIDs that
+ * modify catalogs during logical decoding.
*/
SNAPSHOT_HISTORIC_MVCC,
@@ -114,37 +119,18 @@ typedef enum SnapshotType
SNAPSHOT_NON_VACUUMABLE,
} SnapshotType;
-typedef struct SnapshotData *Snapshot;
-
-#define InvalidSnapshot ((Snapshot) NULL)
-
/*
- * Struct representing all kind of possible snapshots.
+ * Struct representing a normal MVCC snapshot.
*
- * There are several different kinds of snapshots:
- * * Normal MVCC snapshots
- * * MVCC snapshots taken during recovery (in Hot-Standby mode)
- * * Historic MVCC snapshots used during logical decoding
- * * snapshots passed to HeapTupleSatisfiesDirty()
- * * snapshots passed to HeapTupleSatisfiesNonVacuumable()
- * * snapshots used for SatisfiesAny, Toast, Self where no members are
- * accessed.
- *
- * TODO: It's probably a good idea to split this struct using a NodeTag
- * similar to how parser and executor nodes are handled, with one type for
- * each different kind of snapshot to avoid overloading the meaning of
- * individual fields.
+ * MVCC snapshots come in two variants: those taken during recovery in hot
+ * standby mode, and "normal" MVCC snapshots. They are distinguished by
+ * takenDuringRecovery.
*/
-typedef struct SnapshotData
+typedef struct MVCCSnapshotData
{
- SnapshotType snapshot_type; /* type of snapshot */
+ SnapshotType snapshot_type; /* type of snapshot, must be first */
/*
- * The remaining fields are used only for MVCC snapshots, and are normally
- * just zeroes in special snapshots. (But xmin and xmax are used
- * specially by HeapTupleSatisfiesDirty, and xmin is used specially by
- * HeapTupleSatisfiesNonVacuumable.)
- *
* An MVCC snapshot can never see the effects of XIDs >= xmax. It can see
* the effects of all older XIDs except those listed in the snapshot. xmin
* is stored as an optimization to avoid needing to search the XID arrays
@@ -154,10 +140,8 @@ typedef struct SnapshotData
TransactionId xmax; /* all XID >= xmax are invisible to me */
/*
- * For normal MVCC snapshot this contains the all xact IDs that are in
- * progress, unless the snapshot was taken during recovery in which case
- * it's empty. For historic MVCC snapshots, the meaning is inverted, i.e.
- * it contains *committed* transactions between xmin and xmax.
+ * xip contains the all xact IDs that are in progress, unless the snapshot
+ * was taken during recovery in which case it's empty.
*
* note: all ids in xip[] satisfy xmin <= xip[i] < xmax
*/
@@ -165,10 +149,8 @@ typedef struct SnapshotData
uint32 xcnt; /* # of xact ids in xip[] */
/*
- * For non-historic MVCC snapshots, this contains subxact IDs that are in
- * progress (and other transactions that are in progress if taken during
- * recovery). For historic snapshot it contains *all* xids assigned to the
- * replayed transaction, including the toplevel xid.
+ * subxip contains subxact IDs that are in progress (and other
+ * transactions that are in progress if taken during recovery).
*
* note: all ids in subxip[] are >= xmin, but we don't bother filtering
* out any that are >= xmax
@@ -182,18 +164,6 @@ typedef struct SnapshotData
CommandId curcid; /* in my xact, CID < curcid are visible */
- /*
- * An extra return value for HeapTupleSatisfiesDirty, not used in MVCC
- * snapshots.
- */
- uint32 speculativeToken;
-
- /*
- * For SNAPSHOT_NON_VACUUMABLE (and hopefully more in the future) this is
- * used to determine whether row could be vacuumed.
- */
- struct GlobalVisState *vistest;
-
/*
* Book-keeping information, used by the snapshot manager
*/
@@ -207,6 +177,97 @@ typedef struct SnapshotData
* transactions completed since the last GetSnapshotData().
*/
uint64 snapXactCompletionCount;
+} MVCCSnapshotData;
+
+typedef struct MVCCSnapshotData *MVCCSnapshot;
+
+#define InvalidMVCCSnapshot ((MVCCSnapshot) NULL)
+
+/*
+ * Struct representing a "historic" MVCC snapshot during logical decoding.
+ * These are constructed by src/replication/logical/snapbuild.c.
+ */
+typedef struct HistoricMVCCSnapshotData
+{
+ SnapshotType snapshot_type; /* type of snapshot, must be first */
+
+ /*
+ * xmin and xmax like in a normal MVCC snapshot.
+ */
+ TransactionId xmin; /* all XID < xmin are visible to me */
+ TransactionId xmax; /* all XID >= xmax are invisible to me */
+
+ /*
+ * committed_xids contains *committed* transactions between xmin and xmax.
+ * (This is the inverse of 'xip' in normal MVCC snapshots, which contains
+ * all non-committed transactions.) The array is sorted by XID to allow
+ * binary search.
+ *
+ * note: all ids in committed_xids[] satisfy xmin <= committed_xids[i] <
+ * xmax
+ */
+ TransactionId *committed_xids;
+ uint32 xcnt; /* # of xact ids in committed_xids[] */
+
+ /*
+ * curxip contains *all* xids assigned to the replayed transaction,
+ * including the toplevel xid.
+ */
+ TransactionId *curxip;
+ int32 curxcnt; /* # of xact ids in curxip[] */
+
+ CommandId curcid; /* in my xact, CID < curcid are visible */
+
+ bool copied; /* false if it's a "base" snapshot */
+
+ uint32 refcount; /* refcount managed by snapbuild.c */
+ uint32 regd_count; /* refcount registered with resource owners */
+
+} HistoricMVCCSnapshotData;
+
+typedef struct HistoricMVCCSnapshotData *HistoricMVCCSnapshot;
+
+/*
+ * Struct representing a special "snapshot" which sees all tuples as visible
+ * if they are visible to anyone, i.e. if they are not vacuumable.
+ * i.e. SNAPSHOT_NON_VACUUMABLE.
+ */
+typedef struct NonVacuumableSnapshotData
+{
+ SnapshotType snapshot_type; /* type of snapshot, must be first */
+
+ /* This is used to determine whether row could be vacuumed. */
+ struct GlobalVisState *vistest;
+} NonVacuumableSnapshotData;
+
+/*
+ * Return values to the caller of HeapTupleSatisfyDirty.
+ */
+typedef struct DirtySnapshotData
+{
+ SnapshotType snapshot_type; /* type of snapshot, must be first */
+
+ TransactionId xmin;
+ TransactionId xmax;
+ uint32 speculativeToken;
+} DirtySnapshotData;
+
+/*
+ * Generic union representing all kind of possible snapshots. Some have
+ * type-specific structs.
+ */
+typedef union SnapshotData
+{
+ SnapshotType snapshot_type; /* type of snapshot */
+
+ MVCCSnapshotData mvcc;
+ DirtySnapshotData dirty;
+ HistoricMVCCSnapshotData historic_mvcc;
+ NonVacuumableSnapshotData nonvacuumable;
} SnapshotData;
+typedef union SnapshotData *Snapshot;
+
+#define InvalidSnapshot ((Snapshot) NULL)
+
#endif /* SNAPSHOT_H */
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 93339ef3c58..b1a144917c8 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -628,6 +628,7 @@ DictThesaurus
DimensionInfo
DirectoryMethodData
DirectoryMethodFile
+DirtySnapshotData
DisableTimeoutParams
DiscardMode
DiscardStmt
@@ -1175,6 +1176,7 @@ HeapTupleFreeze
HeapTupleHeader
HeapTupleHeaderData
HeapTupleTableSlot
+HistoricMVCCSnapshotData
HistControl
HotStandbyState
I32
@@ -1623,6 +1625,7 @@ MINIDUMPWRITEDUMP
MINIDUMP_TYPE
MJEvalResult
MTTargetRelLookup
+MVCCSnapshotData
MVDependencies
MVDependency
MVNDistinct
@@ -1722,6 +1725,7 @@ NextValueExpr
Node
NodeTag
NonEmptyRange
+NonVacuumableSnapshotData
Notification
NotificationList
NotifyStmt
--
2.39.5
From 3a0c4d145d95f9b39603980cace6114de338acfd Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakan...@iki.fi>
Date: Thu, 13 Mar 2025 16:45:12 +0200
Subject: [PATCH v2 2/2] Simplify historic snapshot refcounting
ReorderBufferProcessTXN() handled "copied" snapshots created with
ReorderBufferCopySnap() differently from "base" historic snapshots
created by snapbuild.c. The base snapshots used a reference count,
while copied snapshots did not. Simplify by using the reference count
for both.
---
.../replication/logical/reorderbuffer.c | 97 ++++++++-----------
src/backend/replication/logical/snapbuild.c | 48 +--------
src/include/replication/snapbuild.h | 1 +
src/include/utils/snapshot.h | 2 -
4 files changed, 46 insertions(+), 102 deletions(-)
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index e8196a8d5d5..e47970f1c82 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -103,7 +103,7 @@
#include "replication/logical.h"
#include "replication/reorderbuffer.h"
#include "replication/slot.h"
-#include "replication/snapbuild.h" /* just for SnapBuildSnapDecRefcount */
+#include "replication/snapbuild.h"
#include "storage/bufmgr.h"
#include "storage/fd.h"
#include "storage/procarray.h"
@@ -268,7 +268,6 @@ static void ReorderBufferSerializedPath(char *path, ReplicationSlot *slot,
TransactionId xid, XLogSegNo segno);
static int ReorderBufferTXNSizeCompare(const pairingheap_node *a, const pairingheap_node *b, void *arg);
-static void ReorderBufferFreeSnap(ReorderBuffer *rb, HistoricMVCCSnapshot snap);
static HistoricMVCCSnapshot ReorderBufferCopySnap(ReorderBuffer *rb, HistoricMVCCSnapshot orig_snap,
ReorderBufferTXN *txn, CommandId cid);
@@ -543,7 +542,7 @@ ReorderBufferFreeChange(ReorderBuffer *rb, ReorderBufferChange *change,
case REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT:
if (change->data.snapshot)
{
- ReorderBufferFreeSnap(rb, change->data.snapshot);
+ SnapBuildSnapDecRefcount(change->data.snapshot);
change->data.snapshot = NULL;
}
break;
@@ -1593,7 +1592,8 @@ ReorderBufferCleanupTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
if (txn->snapshot_now != NULL)
{
Assert(rbtxn_is_streamed(txn));
- ReorderBufferFreeSnap(rb, txn->snapshot_now);
+ SnapBuildSnapDecRefcount(txn->snapshot_now);
+ txn->snapshot_now = NULL;
}
/*
@@ -1902,7 +1902,6 @@ ReorderBufferCopySnap(ReorderBuffer *rb, HistoricMVCCSnapshot orig_snap,
snap = MemoryContextAllocZero(rb->context, size);
memcpy(snap, orig_snap, sizeof(HistoricMVCCSnapshotData));
- snap->copied = true;
snap->refcount = 1; /* mark as active so nobody frees it */
snap->regd_count = 0;
snap->committed_xids = (TransactionId *) (snap + 1);
@@ -1942,18 +1941,6 @@ ReorderBufferCopySnap(ReorderBuffer *rb, HistoricMVCCSnapshot orig_snap,
return snap;
}
-/*
- * Free a previously ReorderBufferCopySnap'ed snapshot
- */
-static void
-ReorderBufferFreeSnap(ReorderBuffer *rb, HistoricMVCCSnapshot snap)
-{
- if (snap->copied)
- pfree(snap);
- else
- SnapBuildSnapDecRefcount(snap);
-}
-
/*
* If the transaction was (partially) streamed, we need to prepare or commit
* it in a 'streamed' way. That is, we first stream the remaining part of the
@@ -2104,11 +2091,8 @@ ReorderBufferSaveTXNSnapshot(ReorderBuffer *rb, ReorderBufferTXN *txn,
txn->command_id = command_id;
/* Avoid copying if it's already copied. */
- if (snapshot_now->copied)
- txn->snapshot_now = snapshot_now;
- else
- txn->snapshot_now = ReorderBufferCopySnap(rb, snapshot_now,
- txn, command_id);
+ txn->snapshot_now = snapshot_now;
+ SnapBuildSnapIncRefcount(txn->snapshot_now);
}
/*
@@ -2208,6 +2192,8 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn,
/* setup the initial snapshot */
SetupHistoricSnapshot(snapshot_now, txn->tuplecid_hash);
+ /* increase refcount for the installed historic snapshot */
+ SnapBuildSnapIncRefcount(snapshot_now);
/*
* Decoding needs access to syscaches et al., which in turn use
@@ -2511,33 +2497,12 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn,
case REORDER_BUFFER_CHANGE_INTERNAL_SNAPSHOT:
/* get rid of the old */
TeardownHistoricSnapshot(false);
-
- if (snapshot_now->copied)
- {
- ReorderBufferFreeSnap(rb, snapshot_now);
- snapshot_now =
- ReorderBufferCopySnap(rb, change->data.snapshot,
- txn, command_id);
- }
-
- /*
- * Restored from disk, need to be careful not to double
- * free. We could introduce refcounting for that, but for
- * now this seems infrequent enough not to care.
- */
- else if (change->data.snapshot->copied)
- {
- snapshot_now =
- ReorderBufferCopySnap(rb, change->data.snapshot,
- txn, command_id);
- }
- else
- {
- snapshot_now = change->data.snapshot;
- }
+ SnapBuildSnapDecRefcount(snapshot_now);
/* and continue with the new one */
+ snapshot_now = change->data.snapshot;
SetupHistoricSnapshot(snapshot_now, txn->tuplecid_hash);
+ SnapBuildSnapIncRefcount(snapshot_now);
break;
case REORDER_BUFFER_CHANGE_INTERNAL_COMMAND_ID:
@@ -2547,16 +2512,26 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn,
{
command_id = change->data.command_id;
- if (!snapshot_now->copied)
+ TeardownHistoricSnapshot(false);
+
+ /*
+ * Construct a new snapshot with the new command ID.
+ *
+ * If this is the only reference to the snapshot, and
+ * it's a "copied" snapshot that already contains all
+ * the replayed transaction's XIDs (curxnct > 0), we
+ * can take a shortcut and update the snapshot's
+ * command ID in place.
+ */
+ if (snapshot_now->refcount == 1 && snapshot_now->curxcnt > 0)
+ snapshot_now->curcid = command_id;
+ else
{
- /* we don't use the global one anymore */
+ SnapBuildSnapDecRefcount(snapshot_now);
snapshot_now = ReorderBufferCopySnap(rb, snapshot_now,
txn, command_id);
}
- snapshot_now->curcid = command_id;
-
- TeardownHistoricSnapshot(false);
SetupHistoricSnapshot(snapshot_now, txn->tuplecid_hash);
}
@@ -2646,11 +2621,11 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn,
*/
if (streaming)
ReorderBufferSaveTXNSnapshot(rb, txn, snapshot_now, command_id);
- else if (snapshot_now->copied)
- ReorderBufferFreeSnap(rb, snapshot_now);
/* cleanup */
TeardownHistoricSnapshot(false);
+ SnapBuildSnapDecRefcount(snapshot_now);
+ snapshot_now = NULL;
/*
* Aborting the current (sub-)transaction as a whole has the right
@@ -2703,6 +2678,11 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn,
TeardownHistoricSnapshot(true);
+ /*
+ * don't decrement the refcount on snapshot_now yet, we still use it
+ * in the ReorderBufferResetTXN() call below.
+ */
+
/*
* Force cache invalidation to happen outside of a valid transaction
* to prevent catalog access as we just caught an error.
@@ -2751,9 +2731,15 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn,
ReorderBufferResetTXN(rb, txn, snapshot_now,
command_id, prev_lsn,
specinsert);
+
+ SnapBuildSnapDecRefcount(snapshot_now);
+ snapshot_now = NULL;
}
else
{
+ SnapBuildSnapDecRefcount(snapshot_now);
+ snapshot_now = NULL;
+
ReorderBufferCleanupTXN(rb, txn);
MemoryContextSwitchTo(ecxt);
PG_RE_THROW();
@@ -4256,8 +4242,7 @@ ReorderBufferStreamTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
txn, command_id);
/* Free the previously copied snapshot. */
- Assert(txn->snapshot_now->copied);
- ReorderBufferFreeSnap(rb, txn->snapshot_now);
+ SnapBuildSnapDecRefcount(txn->snapshot_now);
txn->snapshot_now = NULL;
}
@@ -4647,7 +4632,7 @@ ReorderBufferRestoreChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
newsnap->committed_xids = (TransactionId *)
(((char *) newsnap) + sizeof(HistoricMVCCSnapshotData));
newsnap->curxip = newsnap->committed_xids + newsnap->xcnt;
- newsnap->copied = true;
+ newsnap->refcount = 1;
break;
}
/* the base struct contains all the data, easy peasy */
diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c
index 7a341418a74..50dca7cb758 100644
--- a/src/backend/replication/logical/snapbuild.c
+++ b/src/backend/replication/logical/snapbuild.c
@@ -157,10 +157,6 @@ static void SnapBuildPurgeOlderTxn(SnapBuild *builder);
/* snapshot building/manipulation/distribution functions */
static HistoricMVCCSnapshot SnapBuildBuildSnapshot(SnapBuild *builder);
-static void SnapBuildFreeSnapshot(HistoricMVCCSnapshot snap);
-
-static void SnapBuildSnapIncRefcount(HistoricMVCCSnapshot snap);
-
static void SnapBuildDistributeNewCatalogSnapshot(SnapBuild *builder, XLogRecPtr lsn);
static inline bool SnapBuildXidHasCatalogChanges(SnapBuild *builder, TransactionId xid,
@@ -245,29 +241,6 @@ FreeSnapshotBuilder(SnapBuild *builder)
MemoryContextDelete(context);
}
-/*
- * Free an unreferenced snapshot that has previously been built by us.
- */
-static void
-SnapBuildFreeSnapshot(HistoricMVCCSnapshot snap)
-{
- /* make sure we don't get passed an external snapshot */
- Assert(snap->snapshot_type == SNAPSHOT_HISTORIC_MVCC);
-
- /* make sure nobody modified our snapshot */
- Assert(snap->curcid == FirstCommandId);
- Assert(snap->regd_count == 0);
-
- /* slightly more likely, so it's checked even without c-asserts */
- if (snap->copied)
- elog(ERROR, "cannot free a copied snapshot");
-
- if (snap->refcount)
- elog(ERROR, "cannot free a snapshot that's in use");
-
- pfree(snap);
-}
-
/*
* In which state of snapshot building are we?
*/
@@ -310,7 +283,7 @@ SnapBuildXactNeedsSkip(SnapBuild *builder, XLogRecPtr ptr)
* This is used when handing out a snapshot to some external resource or when
* adding a Snapshot as builder->snapshot.
*/
-static void
+void
SnapBuildSnapIncRefcount(HistoricMVCCSnapshot snap)
{
snap->refcount++;
@@ -318,9 +291,6 @@ SnapBuildSnapIncRefcount(HistoricMVCCSnapshot snap)
/*
* Decrease refcount of a snapshot and free if the refcount reaches zero.
- *
- * Externally visible, so that external resources that have been handed an
- * IncRef'ed Snapshot can adjust its refcount easily.
*/
void
SnapBuildSnapDecRefcount(HistoricMVCCSnapshot snap)
@@ -328,19 +298,12 @@ SnapBuildSnapDecRefcount(HistoricMVCCSnapshot snap)
/* make sure we don't get passed an external snapshot */
Assert(snap->snapshot_type == SNAPSHOT_HISTORIC_MVCC);
- /* make sure nobody modified our snapshot */
- Assert(snap->curcid == FirstCommandId);
-
Assert(snap->refcount > 0);
Assert(snap->regd_count == 0);
- /* slightly more likely, so it's checked even without casserts */
- if (snap->copied)
- elog(ERROR, "cannot free a copied snapshot");
-
snap->refcount--;
if (snap->refcount == 0)
- SnapBuildFreeSnapshot(snap);
+ pfree(snap);
}
/*
@@ -413,7 +376,6 @@ SnapBuildBuildSnapshot(SnapBuild *builder)
snapshot->curxcnt = 0;
snapshot->curxip = NULL;
- snapshot->copied = false;
snapshot->curcid = FirstCommandId;
snapshot->refcount = 0;
snapshot->regd_count = 0;
@@ -1037,18 +999,16 @@ SnapBuildCommitTxn(SnapBuild *builder, XLogRecPtr lsn, TransactionId xid,
SnapBuildSnapDecRefcount(builder->snapshot);
builder->snapshot = SnapBuildBuildSnapshot(builder);
+ SnapBuildSnapIncRefcount(builder->snapshot);
/* we might need to execute invalidations, add snapshot */
if (!ReorderBufferXidHasBaseSnapshot(builder->reorder, xid))
{
- SnapBuildSnapIncRefcount(builder->snapshot);
ReorderBufferSetBaseSnapshot(builder->reorder, xid, lsn,
builder->snapshot);
+ SnapBuildSnapIncRefcount(builder->snapshot);
}
- /* refcount of the snapshot builder for the new snapshot */
- SnapBuildSnapIncRefcount(builder->snapshot);
-
/* add a new catalog snapshot to all currently running transactions */
SnapBuildDistributeNewCatalogSnapshot(builder, lsn);
}
diff --git a/src/include/replication/snapbuild.h b/src/include/replication/snapbuild.h
index 5930ffb55a8..6095013a299 100644
--- a/src/include/replication/snapbuild.h
+++ b/src/include/replication/snapbuild.h
@@ -70,6 +70,7 @@ extern SnapBuild *AllocateSnapshotBuilder(struct ReorderBuffer *reorder,
XLogRecPtr two_phase_at);
extern void FreeSnapshotBuilder(SnapBuild *builder);
+extern void SnapBuildSnapIncRefcount(HistoricMVCCSnapshot snap);
extern void SnapBuildSnapDecRefcount(HistoricMVCCSnapshot snap);
extern MVCCSnapshot SnapBuildInitialSnapshot(SnapBuild *builder);
diff --git a/src/include/utils/snapshot.h b/src/include/utils/snapshot.h
index 93c1f51784f..bca0ad16e68 100644
--- a/src/include/utils/snapshot.h
+++ b/src/include/utils/snapshot.h
@@ -218,8 +218,6 @@ typedef struct HistoricMVCCSnapshotData
CommandId curcid; /* in my xact, CID < curcid are visible */
- bool copied; /* false if it's a "base" snapshot */
-
uint32 refcount; /* refcount managed by snapbuild.c */
uint32 regd_count; /* refcount registered with resource owners */
--
2.39.5