Hi,
As promised I'm currently updating the patch. Some questions arose
during that:
* Should HeapTupleHeaderXminFrozen also check for FrozenTransactionId?
It seems quite possible that people think they've delt with frozen
xmin entirely after checking, but they still might get
FrozenTransactionId back in a pg_upgraded cluster.
* Existing htup_details boolean checks contain an 'Is', but
HeapTupleHeaderXminCommitted, HeapTupleHeaderXminInvalid,
HeapTupleHeaderXminFrozen don't contain any verb. Not sure.
* EvalPlanQualFetch() uses priorXmax like logic to find updated versions
of tuples. I am not 100% sure there aren't cases where that's
problematic even with the current code, but I think it's better not to
use the raw xid there - priorXmax can never be FrozenTransactionId, so
comparing it to the GetXmin() seems better.
It also has the following wrong comment:
* .... (Should be safe to examine xmin without getting
* buffer's content lock, since xmin never changes in an existing
* tuple.)
But I don't see that causing any problems.
* ri_trigger.c did do a TransactionIdIsCurrentTransactionId() on a raw
xmin value, the consequences are minor though.
The rewrite to introduce HeapTupleHeaderGet[Raw]Xmin() indeed somewhat
increases the patchsize - but I think it's enough increase in
expressiveness to be well worth the cost. Indeed it led me to find at
least one issue (with minor consequences).
I think once we have this we should start opportunistically try to
freeze tuples during vacuum using OldestXmin instead of FreezeLimit if
the page is already dirty.
Patch 01 is a rebased version of Robert's patch without further changes,
02 contains my suggested modifications.
Greetings,
Andres Freund
--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
>From ac6c9a15836414208c49393d05d2d7d29e8fae4a Mon Sep 17 00:00:00 2001
From: Andres Freund <[email protected]>
Date: Thu, 21 Nov 2013 12:57:20 +0100
Subject: [PATCH 1/2] freeze-forensically-v1
Robert Haas
---
src/backend/access/heap/heapam.c | 50 +++++++++++++++--------------------
src/backend/access/heap/rewriteheap.c | 1 +
src/backend/catalog/index.c | 1 +
src/backend/commands/analyze.c | 1 +
src/backend/commands/cluster.c | 1 +
src/backend/commands/sequence.c | 4 +--
src/backend/commands/typecmds.c | 3 ++-
src/backend/commands/vacuumlazy.c | 9 +++++--
src/backend/optimizer/util/plancat.c | 1 +
src/backend/storage/lmgr/predicate.c | 7 ++++-
src/backend/utils/adt/ri_triggers.c | 6 +++--
src/backend/utils/cache/relcache.c | 8 ++++--
src/backend/utils/time/combocid.c | 8 +++---
src/backend/utils/time/tqual.c | 31 +++++++++++-----------
src/include/access/htup_details.h | 35 +++++++++++++++++++++++-
15 files changed, 108 insertions(+), 58 deletions(-)
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index a21f31b..eb56230 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2221,13 +2221,9 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
tup->t_data->t_infomask &= ~(HEAP_XACT_MASK);
tup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK);
tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
+ HeapTupleHeaderSetXmin(tup->t_data, xid);
if (options & HEAP_INSERT_FROZEN)
- {
- tup->t_data->t_infomask |= HEAP_XMIN_COMMITTED;
- HeapTupleHeaderSetXmin(tup->t_data, FrozenTransactionId);
- }
- else
- HeapTupleHeaderSetXmin(tup->t_data, xid);
+ HeapTupleHeaderSetXminFrozen(tup->t_data);
HeapTupleHeaderSetCmin(tup->t_data, cid);
HeapTupleHeaderSetXmax(tup->t_data, 0); /* for cleanliness */
tup->t_tableOid = RelationGetRelid(relation);
@@ -5120,19 +5116,15 @@ heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
bool changed = false;
TransactionId xid;
- xid = HeapTupleHeaderGetXmin(tuple);
- if (TransactionIdIsNormal(xid) &&
- TransactionIdPrecedes(xid, cutoff_xid))
+ if (!HeapTupleHeaderXminFrozen(tuple))
{
- HeapTupleHeaderSetXmin(tuple, FrozenTransactionId);
-
- /*
- * Might as well fix the hint bits too; usually XMIN_COMMITTED will
- * already be set here, but there's a small chance not.
- */
- Assert(!(tuple->t_infomask & HEAP_XMIN_INVALID));
- tuple->t_infomask |= HEAP_XMIN_COMMITTED;
- changed = true;
+ xid = HeapTupleHeaderGetXmin(tuple);
+ if (TransactionIdIsNormal(xid) &&
+ TransactionIdPrecedes(xid, cutoff_xid))
+ {
+ HeapTupleHeaderSetXminFrozen(tuple);
+ changed = true;
+ }
}
/*
@@ -5185,8 +5177,7 @@ heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
* Might as well fix the hint bits too; usually XMIN_COMMITTED
* will already be set here, but there's a small chance not.
*/
- Assert(!(tuple->t_infomask & HEAP_XMIN_INVALID));
- tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+ HeapTupleHeaderSetXminCommitted(tuple);
changed = true;
}
}
@@ -5477,10 +5468,13 @@ heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
{
TransactionId xid;
- xid = HeapTupleHeaderGetXmin(tuple);
- if (TransactionIdIsNormal(xid) &&
- TransactionIdPrecedes(xid, cutoff_xid))
- return true;
+ if (!HeapTupleHeaderXminFrozen(tuple))
+ {
+ xid = HeapTupleHeaderGetXmin(tuple);
+ if (TransactionIdIsNormal(xid) &&
+ TransactionIdPrecedes(xid, cutoff_xid))
+ return true;
+ }
if (!(tuple->t_infomask & HEAP_XMAX_INVALID))
{
@@ -5606,12 +5600,10 @@ HeapTupleHeaderAdvanceLatestRemovedXid(HeapTupleHeader tuple,
* This needs to work on both master and standby, where it is used to
* assess btree delete records.
*/
- if ((tuple->t_infomask & HEAP_XMIN_COMMITTED) ||
- (!(tuple->t_infomask & HEAP_XMIN_COMMITTED) &&
- !(tuple->t_infomask & HEAP_XMIN_INVALID) &&
- TransactionIdDidCommit(xmin)))
+ if (HeapTupleHeaderXminCommitted(tuple) ||
+ (!HeapTupleHeaderXminInvalid(tuple) && TransactionIdDidCommit(xmin)))
{
- if (xmax != xmin &&
+ if ((HeapTupleHeaderXminFrozen(tuple) || xmax != xmin) &&
TransactionIdFollows(xmax, *latestRemovedXid))
*latestRemovedXid = xmax;
}
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 951894c..f236a46 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -437,6 +437,7 @@ rewrite_heap_tuple(RewriteState state,
* RECENTLY_DEAD if and only if the xmin is not before OldestXmin.
*/
if ((new_tuple->t_data->t_infomask & HEAP_UPDATED) &&
+ !HeapTupleHeaderXminFrozen(new_tuple->t_data) &&
!TransactionIdPrecedes(HeapTupleHeaderGetXmin(new_tuple->t_data),
state->rs_oldest_xmin))
{
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 0275240..48e0c2a 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -2277,6 +2277,7 @@ IndexBuildHeapScan(Relation heapRelation,
* before commit there. Give a warning if neither case
* applies.
*/
+ Assert(!HeapTupleHeaderXminFrozen(heapTuple->t_data));
xwait = HeapTupleHeaderGetXmin(heapTuple->t_data);
if (!TransactionIdIsCurrentTransactionId(xwait))
{
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 9845b0b..9568918 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1178,6 +1178,7 @@ acquire_sample_rows(Relation onerel, int elevel,
* has to adjust the numbers we send to the stats
* collector to make this come out right.)
*/
+ Assert(!HeapTupleHeaderXminFrozen(targtuple.t_data));
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(targtuple.t_data)))
{
sample_it = true;
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index f6a5bfe..8b6e7e1 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -993,6 +993,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
* there. Give a warning if neither case applies; but in any
* case we had better copy it.
*/
+ Assert(!HeapTupleHeaderXminFrozen(tuple->t_data));
if (!is_system_catalog &&
!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple->t_data)))
elog(WARNING, "concurrent insert in progress within table \"%s\"",
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 67b8a5d..24c7d07 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -360,10 +360,10 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
item = PageGetItem((Page) page, itemId);
HeapTupleHeaderSetXmin((HeapTupleHeader) item, FrozenTransactionId);
- ((HeapTupleHeader) item)->t_infomask |= HEAP_XMIN_COMMITTED;
+ HeapTupleHeaderSetXminFrozen((HeapTupleHeader) item);
HeapTupleHeaderSetXmin(tuple->t_data, FrozenTransactionId);
- tuple->t_data->t_infomask |= HEAP_XMIN_COMMITTED;
+ HeapTupleHeaderSetXminFrozen(tuple->t_data);
}
MarkBufferDirty(buf);
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index d4a14ca..4ad8386 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1202,7 +1202,8 @@ AlterEnum(AlterEnumStmt *stmt, bool isTopLevel)
* cases that could theoretically be safe; but fortunately pg_dump only
* needs the simplest case.
*/
- if (HeapTupleHeaderGetXmin(tup->t_data) == GetCurrentTransactionId() &&
+ if (!HeapTupleHeaderXminFrozen(tup->t_data) &&
+ HeapTupleHeaderGetXmin(tup->t_data) == GetCurrentTransactionId() &&
!(tup->t_data->t_infomask & HEAP_UPDATED))
/* safe to do inside transaction block */ ;
else
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 6778c7d..7ac1352 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -812,16 +812,19 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
{
TransactionId xmin;
- if (!(tuple.t_data->t_infomask & HEAP_XMIN_COMMITTED))
+ if (!HeapTupleHeaderXminCommitted(tuple.t_data))
{
all_visible = false;
break;
}
+
/*
* The inserter definitely committed. But is it old
* enough that everyone sees it as committed?
*/
+ if (HeapTupleHeaderXminFrozen(tuple.t_data))
+ break;
xmin = HeapTupleHeaderGetXmin(tuple.t_data);
if (!TransactionIdPrecedes(xmin, OldestXmin))
{
@@ -1736,7 +1739,7 @@ heap_page_is_all_visible(Relation rel, Buffer buf, TransactionId *visibility_cut
TransactionId xmin;
/* Check comments in lazy_scan_heap. */
- if (!(tuple.t_data->t_infomask & HEAP_XMIN_COMMITTED))
+ if (!HeapTupleHeaderXminCommitted(tuple.t_data))
{
all_visible = false;
break;
@@ -1746,6 +1749,8 @@ heap_page_is_all_visible(Relation rel, Buffer buf, TransactionId *visibility_cut
* The inserter definitely committed. But is it old enough
* that everyone sees it as committed?
*/
+ if (HeapTupleHeaderXminFrozen(tuple.t_data))
+ break;
xmin = HeapTupleHeaderGetXmin(tuple.t_data);
if (!TransactionIdPrecedes(xmin, OldestXmin))
{
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 954666c..e22be0e 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -188,6 +188,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
* src/backend/access/heap/README.HOT for discussion.
*/
if (index->indcheckxmin &&
+ !HeapTupleHeaderXminFrozen(indexRelation->rd_indextuple->t_data) &&
!TransactionIdPrecedes(HeapTupleHeaderGetXmin(indexRelation->rd_indextuple->t_data),
TransactionXmin))
{
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index a8a0e98..77d87c7 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -2475,7 +2475,10 @@ PredicateLockTuple(Relation relation, HeapTuple tuple, Snapshot snapshot)
{
TransactionId myxid;
- targetxmin = HeapTupleHeaderGetXmin(tuple->t_data);
+ if (HeapTupleHeaderXminFrozen(tuple->t_data))
+ targetxmin = FrozenTransactionId;
+ else
+ targetxmin = HeapTupleHeaderGetXmin(tuple->t_data);
myxid = GetTopTransactionIdIfAny();
if (TransactionIdIsValid(myxid))
@@ -3898,6 +3901,7 @@ CheckForSerializableConflictOut(bool visible, Relation relation,
case HEAPTUPLE_LIVE:
if (visible)
return;
+ Assert(!HeapTupleHeaderXminFrozen(tuple->t_data));
xid = HeapTupleHeaderGetXmin(tuple->t_data);
break;
case HEAPTUPLE_RECENTLY_DEAD:
@@ -3909,6 +3913,7 @@ CheckForSerializableConflictOut(bool visible, Relation relation,
xid = HeapTupleHeaderGetUpdateXid(tuple->t_data);
break;
case HEAPTUPLE_INSERT_IN_PROGRESS:
+ Assert(!HeapTupleHeaderXminFrozen(tuple->t_data));
xid = HeapTupleHeaderGetXmin(tuple->t_data);
break;
case HEAPTUPLE_DEAD:
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 917130f..ed906ee 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -2166,7 +2166,8 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
* UPDATE check. (We could skip this if we knew the INSERT
* trigger already fired, but there is no easy way to know that.)
*/
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
+ if (!HeapTupleHeaderXminFrozen(old_row->t_data) &&
+ TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
return true;
/* If all old and new key values are equal, no check is needed */
@@ -2204,7 +2205,8 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
* UPDATE check. (We could skip this if we knew the INSERT
* trigger already fired, but there is no easy way to know that.)
*/
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
+ if (!HeapTupleHeaderXminFrozen(old_row->t_data) &&
+ TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
return true;
/* If all old and new key values are equal, no check is needed */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index d0acca8..4b92fbf 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -1764,6 +1764,7 @@ RelationReloadIndexInfo(Relation relation)
{
HeapTuple tuple;
Form_pg_index index;
+ TransactionId xmin;
tuple = SearchSysCache1(INDEXRELID,
ObjectIdGetDatum(RelationGetRelid(relation)));
@@ -1789,8 +1790,11 @@ RelationReloadIndexInfo(Relation relation)
relation->rd_index->indislive = index->indislive;
/* Copy xmin too, as that is needed to make sense of indcheckxmin */
- HeapTupleHeaderSetXmin(relation->rd_indextuple->t_data,
- HeapTupleHeaderGetXmin(tuple->t_data));
+ if (HeapTupleHeaderXminFrozen(relation->rd_indextuple->t_data))
+ xmin = FrozenTransactionId;
+ else
+ xmin = HeapTupleHeaderGetXmin(relation->rd_indextuple->t_data);
+ HeapTupleHeaderSetXmin(relation->rd_indextuple->t_data, xmin);
ReleaseSysCache(tuple);
}
diff --git a/src/backend/utils/time/combocid.c b/src/backend/utils/time/combocid.c
index 923355d..baacd4a 100644
--- a/src/backend/utils/time/combocid.c
+++ b/src/backend/utils/time/combocid.c
@@ -148,10 +148,12 @@ HeapTupleHeaderAdjustCmax(HeapTupleHeader tup,
/*
* If we're marking a tuple deleted that was inserted by (any
* subtransaction of) our transaction, we need to use a combo command id.
- * Test for HEAP_XMIN_COMMITTED first, because it's cheaper than a
- * TransactionIdIsCurrentTransactionId call.
+ * It's cheaper to test HeapTupleHeaderXminCommitted before checking
+ * TransactionIdIsCurrentTransactionId, but it's also necessary for
+ * correctness: if HeapTupleHeaderXminCommitted returns true, the tuple
+ * might even be frozen.
*/
- if (!(tup->t_infomask & HEAP_XMIN_COMMITTED) &&
+ if (!HeapTupleHeaderXminCommitted(tup) &&
TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tup)))
{
CommandId cmin = HeapTupleHeaderGetCmin(tup);
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
index ed66c49..bf8c9919 100644
--- a/src/backend/utils/time/tqual.c
+++ b/src/backend/utils/time/tqual.c
@@ -166,9 +166,9 @@ HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
Assert(ItemPointerIsValid(&htup->t_self));
Assert(htup->t_tableOid != InvalidOid);
- if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
+ if (!HeapTupleHeaderXminCommitted(tuple))
{
- if (tuple->t_infomask & HEAP_XMIN_INVALID)
+ if (HeapTupleHeaderXminInvalid(tuple))
return false;
/* Used by pre-9.0 binary upgrades */
@@ -352,9 +352,9 @@ HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
Assert(ItemPointerIsValid(&htup->t_self));
Assert(htup->t_tableOid != InvalidOid);
- if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
+ if (!HeapTupleHeaderXminCommitted(tuple))
{
- if (tuple->t_infomask & HEAP_XMIN_INVALID)
+ if (HeapTupleHeaderXminInvalid(tuple))
return false;
/* Used by pre-9.0 binary upgrades */
@@ -437,9 +437,9 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
Assert(ItemPointerIsValid(&htup->t_self));
Assert(htup->t_tableOid != InvalidOid);
- if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
+ if (!HeapTupleHeaderXminCommitted(tuple))
{
- if (tuple->t_infomask & HEAP_XMIN_INVALID)
+ if (HeapTupleHeaderXminInvalid(tuple))
return HeapTupleInvisible;
/* Used by pre-9.0 binary upgrades */
@@ -665,9 +665,9 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
snapshot->xmin = snapshot->xmax = InvalidTransactionId;
- if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
+ if (!HeapTupleHeaderXminCommitted(tuple))
{
- if (tuple->t_infomask & HEAP_XMIN_INVALID)
+ if (HeapTupleHeaderXminInvalid(tuple))
return false;
/* Used by pre-9.0 binary upgrades */
@@ -855,9 +855,9 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
Assert(ItemPointerIsValid(&htup->t_self));
Assert(htup->t_tableOid != InvalidOid);
- if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
+ if (!HeapTupleHeaderXminCommitted(tuple))
{
- if (tuple->t_infomask & HEAP_XMIN_INVALID)
+ if (HeapTupleHeaderXminInvalid(tuple))
return false;
/* Used by pre-9.0 binary upgrades */
@@ -958,7 +958,8 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
* By here, the inserting transaction has committed - have to check
* when...
*/
- if (XidInMVCCSnapshot(HeapTupleHeaderGetXmin(tuple), snapshot))
+ if (!HeapTupleHeaderXminFrozen(tuple)
+ && XidInMVCCSnapshot(HeapTupleHeaderGetXmin(tuple), snapshot))
return false; /* treat as still in progress */
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
@@ -1058,9 +1059,9 @@ HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
* If the inserting transaction aborted, then the tuple was never visible
* to any other transaction, so we can delete it immediately.
*/
- if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
+ if (!HeapTupleHeaderXminCommitted(tuple))
{
- if (tuple->t_infomask & HEAP_XMIN_INVALID)
+ if (HeapTupleHeaderXminInvalid(tuple))
return HEAPTUPLE_DEAD;
/* Used by pre-9.0 binary upgrades */
else if (tuple->t_infomask & HEAP_MOVED_OFF)
@@ -1294,8 +1295,8 @@ HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin)
* invalid, then we assume it's still alive (since the presumption is that
* all relevant hint bits were just set moments ago).
*/
- if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
- return (tuple->t_infomask & HEAP_XMIN_INVALID) != 0 ? true : false;
+ if (!HeapTupleHeaderXminCommitted(tuple))
+ return HeapTupleHeaderXminInvalid(tuple) ? true : false;
/*
* If the inserting transaction committed, but any deleting transaction
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index 0a832e9..f91dbc1 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -175,6 +175,7 @@ struct HeapTupleHeaderData
HEAP_XMAX_KEYSHR_LOCK)
#define HEAP_XMIN_COMMITTED 0x0100 /* t_xmin committed */
#define HEAP_XMIN_INVALID 0x0200 /* t_xmin invalid/aborted */
+#define HEAP_XMIN_FROZEN (HEAP_XMIN_COMMITTED|HEAP_XMIN_INVALID)
#define HEAP_XMAX_COMMITTED 0x0400 /* t_xmax committed */
#define HEAP_XMAX_INVALID 0x0800 /* t_xmax invalid/aborted */
#define HEAP_XMAX_IS_MULTI 0x1000 /* t_xmax is a MultiXactId */
@@ -254,6 +255,37 @@ struct HeapTupleHeaderData
(tup)->t_choice.t_heap.t_xmin = (xid) \
)
+#define HeapTupleHeaderXminCommitted(tup) \
+( \
+ ((tup)->t_infomask & HEAP_XMIN_COMMITTED) == HEAP_XMIN_COMMITTED \
+)
+#define HeapTupleHeaderXminInvalid(tup) \
+( \
+ ((tup)->t_infomask & (HEAP_XMIN_COMMITTED|HEAP_XMIN_INVALID)) == \
+ HEAP_XMIN_INVALID \
+)
+#define HeapTupleHeaderXminFrozen(tup) \
+( \
+ ((tup)->t_infomask & (HEAP_XMIN_COMMITTED|HEAP_XMIN_INVALID)) == \
+ HEAP_XMIN_FROZEN \
+)
+
+#define HeapTupleHeaderSetXminCommitted(tup) \
+( \
+ AssertMacro(!HeapTupleHeaderXminInvalid(tup)), \
+ ((tup)->t_infomask |= HEAP_XMIN_COMMITTED) \
+)
+#define HeapTupleHeaderSetXminInvalid(tup) \
+( \
+ AssertMacro(!HeapTupleHeaderXminCommitted(tup)), \
+ ((tup)->t_infomask |= HEAP_XMIN_INVALID) \
+)
+#define HeapTupleHeaderSetXminFrozen(tup) \
+( \
+ AssertMacro(!HeapTupleHeaderXminInvalid(tup)), \
+ ((tup)->t_infomask |= HEAP_XMIN_FROZEN) \
+)
+
/*
* HeapTupleHeaderGetRawXmax gets you the raw Xmax field. To find out the Xid
* that updated a tuple, you might need to resolve the MultiXactId if certain
@@ -374,7 +406,8 @@ do { \
#define HeapTupleHeaderIsHotUpdated(tup) \
( \
((tup)->t_infomask2 & HEAP_HOT_UPDATED) != 0 && \
- ((tup)->t_infomask & (HEAP_XMIN_INVALID | HEAP_XMAX_INVALID)) == 0 \
+ ((tup)->t_infomask & HEAP_XMAX_INVALID) == 0 && \
+ !HeapTupleHeaderXminInvalid(tup) \
)
#define HeapTupleHeaderSetHotUpdated(tup) \
--
1.8.5.rc2.dirty
>From 4ba3789b3f37bfe9676ec8af0aa5c3daa2489ca6 Mon Sep 17 00:00:00 2001
From: Andres Freund <[email protected]>
Date: Thu, 21 Nov 2013 14:21:39 +0100
Subject: [PATCH 2/2] Updates to the patch from Andres
---
contrib/pageinspect/heapfuncs.c | 2 +-
src/backend/access/common/heaptuple.c | 2 +-
src/backend/access/heap/heapam.c | 15 +++++++------
src/backend/access/heap/pruneheap.c | 4 ++--
src/backend/access/heap/rewriteheap.c | 1 -
src/backend/commands/typecmds.c | 3 +--
src/backend/commands/vacuumlazy.c | 5 -----
src/backend/optimizer/util/plancat.c | 1 -
src/backend/storage/lmgr/predicate.c | 5 +----
src/backend/utils/adt/ri_triggers.c | 6 ++---
src/backend/utils/cache/relcache.c | 8 ++-----
src/backend/utils/fmgr/fmgr.c | 4 ++--
src/backend/utils/time/combocid.c | 6 ++---
src/backend/utils/time/tqual.c | 42 +++++++++++++++++------------------
src/include/access/htup_details.h | 25 +++++++++++++++++----
src/pl/plperl/plperl.c | 4 ++--
src/pl/plpgsql/src/pl_comp.c | 4 ++--
src/pl/plpython/plpy_procedure.c | 6 ++---
src/pl/plpython/plpy_typeio.c | 4 ++--
src/pl/tcl/pltcl.c | 4 ++--
20 files changed, 75 insertions(+), 76 deletions(-)
diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c
index 6d8f6f1..a78cff3 100644
--- a/contrib/pageinspect/heapfuncs.c
+++ b/contrib/pageinspect/heapfuncs.c
@@ -162,7 +162,7 @@ heap_page_items(PG_FUNCTION_ARGS)
tuphdr = (HeapTupleHeader) PageGetItem(page, id);
- values[4] = UInt32GetDatum(HeapTupleHeaderGetXmin(tuphdr));
+ values[4] = UInt32GetDatum(HeapTupleHeaderGetRawXmin(tuphdr));
values[5] = UInt32GetDatum(HeapTupleHeaderGetRawXmax(tuphdr));
values[6] = UInt32GetDatum(HeapTupleHeaderGetRawCommandId(tuphdr)); /* shared with xvac */
values[7] = PointerGetDatum(&tuphdr->t_ctid);
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index e39b977..347d616 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -539,7 +539,7 @@ heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
result = ObjectIdGetDatum(HeapTupleGetOid(tup));
break;
case MinTransactionIdAttributeNumber:
- result = TransactionIdGetDatum(HeapTupleHeaderGetXmin(tup->t_data));
+ result = TransactionIdGetDatum(HeapTupleHeaderGetRawXmin(tup->t_data));
break;
case MaxTransactionIdAttributeNumber:
result = TransactionIdGetDatum(HeapTupleHeaderGetRawXmax(tup->t_data));
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index eb56230..fbc3043 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -1733,7 +1733,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
*/
if (TransactionIdIsValid(prev_xmax) &&
!TransactionIdEquals(prev_xmax,
- HeapTupleHeaderGetXmin(heapTuple->t_data)))
+ HeapTupleHeaderGetRawXmin(heapTuple->t_data)))
break;
/*
@@ -2221,9 +2221,11 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
tup->t_data->t_infomask &= ~(HEAP_XACT_MASK);
tup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK);
tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
- HeapTupleHeaderSetXmin(tup->t_data, xid);
if (options & HEAP_INSERT_FROZEN)
HeapTupleHeaderSetXminFrozen(tup->t_data);
+ else
+ HeapTupleHeaderSetXmin(tup->t_data, xid);
+
HeapTupleHeaderSetCmin(tup->t_data, cid);
HeapTupleHeaderSetXmax(tup->t_data, 0); /* for cleanliness */
tup->t_tableOid = RelationGetRelid(relation);
@@ -5118,7 +5120,7 @@ heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
if (!HeapTupleHeaderXminFrozen(tuple))
{
- xid = HeapTupleHeaderGetXmin(tuple);
+ xid = HeapTupleHeaderGetRawXmin(tuple);
if (TransactionIdIsNormal(xid) &&
TransactionIdPrecedes(xid, cutoff_xid))
{
@@ -5470,7 +5472,7 @@ heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
if (!HeapTupleHeaderXminFrozen(tuple))
{
- xid = HeapTupleHeaderGetXmin(tuple);
+ xid = HeapTupleHeaderGetRawXmin(tuple);
if (TransactionIdIsNormal(xid) &&
TransactionIdPrecedes(xid, cutoff_xid))
return true;
@@ -5597,14 +5599,13 @@ HeapTupleHeaderAdvanceLatestRemovedXid(HeapTupleHeader tuple,
* updated/deleted by the inserting transaction.
*
* Look for a committed hint bit, or if no xmin bit is set, check clog.
- * This needs to work on both master and standby, where it is used to
+ * This needs to work on both master, and standby where it is used to
* assess btree delete records.
*/
if (HeapTupleHeaderXminCommitted(tuple) ||
(!HeapTupleHeaderXminInvalid(tuple) && TransactionIdDidCommit(xmin)))
{
- if ((HeapTupleHeaderXminFrozen(tuple) || xmax != xmin) &&
- TransactionIdFollows(xmax, *latestRemovedXid))
+ if (xmax != xmin && TransactionIdFollows(xmax, *latestRemovedXid))
*latestRemovedXid = xmax;
}
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 3ec10a0..d731e43 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -448,7 +448,7 @@ heap_prune_chain(Relation relation, Buffer buffer, OffsetNumber rootoffnum,
* Check the tuple XMIN against prior XMAX, if any
*/
if (TransactionIdIsValid(priorXmax) &&
- !TransactionIdEquals(HeapTupleHeaderGetXmin(htup), priorXmax))
+ !TransactionIdEquals(HeapTupleHeaderGetRawXmin(htup), priorXmax))
break;
/*
@@ -788,7 +788,7 @@ heap_get_root_tuples(Page page, OffsetNumber *root_offsets)
htup = (HeapTupleHeader) PageGetItem(page, lp);
if (TransactionIdIsValid(priorXmax) &&
- !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(htup)))
+ !TransactionIdEquals(priorXmax, HeapTupleHeaderGetRawXmin(htup)))
break;
/* Remember the root line pointer for this item */
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index f236a46..951894c 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -437,7 +437,6 @@ rewrite_heap_tuple(RewriteState state,
* RECENTLY_DEAD if and only if the xmin is not before OldestXmin.
*/
if ((new_tuple->t_data->t_infomask & HEAP_UPDATED) &&
- !HeapTupleHeaderXminFrozen(new_tuple->t_data) &&
!TransactionIdPrecedes(HeapTupleHeaderGetXmin(new_tuple->t_data),
state->rs_oldest_xmin))
{
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 4ad8386..d4a14ca 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1202,8 +1202,7 @@ AlterEnum(AlterEnumStmt *stmt, bool isTopLevel)
* cases that could theoretically be safe; but fortunately pg_dump only
* needs the simplest case.
*/
- if (!HeapTupleHeaderXminFrozen(tup->t_data) &&
- HeapTupleHeaderGetXmin(tup->t_data) == GetCurrentTransactionId() &&
+ if (HeapTupleHeaderGetXmin(tup->t_data) == GetCurrentTransactionId() &&
!(tup->t_data->t_infomask & HEAP_UPDATED))
/* safe to do inside transaction block */ ;
else
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 7ac1352..cc8aaf7 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -818,13 +818,10 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
break;
}
-
/*
* The inserter definitely committed. But is it old
* enough that everyone sees it as committed?
*/
- if (HeapTupleHeaderXminFrozen(tuple.t_data))
- break;
xmin = HeapTupleHeaderGetXmin(tuple.t_data);
if (!TransactionIdPrecedes(xmin, OldestXmin))
{
@@ -1749,8 +1746,6 @@ heap_page_is_all_visible(Relation rel, Buffer buf, TransactionId *visibility_cut
* The inserter definitely committed. But is it old enough
* that everyone sees it as committed?
*/
- if (HeapTupleHeaderXminFrozen(tuple.t_data))
- break;
xmin = HeapTupleHeaderGetXmin(tuple.t_data);
if (!TransactionIdPrecedes(xmin, OldestXmin))
{
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index e22be0e..954666c 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -188,7 +188,6 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
* src/backend/access/heap/README.HOT for discussion.
*/
if (index->indcheckxmin &&
- !HeapTupleHeaderXminFrozen(indexRelation->rd_indextuple->t_data) &&
!TransactionIdPrecedes(HeapTupleHeaderGetXmin(indexRelation->rd_indextuple->t_data),
TransactionXmin))
{
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 77d87c7..8143ed0 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -2475,10 +2475,7 @@ PredicateLockTuple(Relation relation, HeapTuple tuple, Snapshot snapshot)
{
TransactionId myxid;
- if (HeapTupleHeaderXminFrozen(tuple->t_data))
- targetxmin = FrozenTransactionId;
- else
- targetxmin = HeapTupleHeaderGetXmin(tuple->t_data);
+ targetxmin = HeapTupleHeaderGetXmin(tuple->t_data);
myxid = GetTopTransactionIdIfAny();
if (TransactionIdIsValid(myxid))
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index ed906ee..917130f 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -2166,8 +2166,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
* UPDATE check. (We could skip this if we knew the INSERT
* trigger already fired, but there is no easy way to know that.)
*/
- if (!HeapTupleHeaderXminFrozen(old_row->t_data) &&
- TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
return true;
/* If all old and new key values are equal, no check is needed */
@@ -2205,8 +2204,7 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
* UPDATE check. (We could skip this if we knew the INSERT
* trigger already fired, but there is no easy way to know that.)
*/
- if (!HeapTupleHeaderXminFrozen(old_row->t_data) &&
- TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
+ if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
return true;
/* If all old and new key values are equal, no check is needed */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 4b92fbf..d0acca8 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -1764,7 +1764,6 @@ RelationReloadIndexInfo(Relation relation)
{
HeapTuple tuple;
Form_pg_index index;
- TransactionId xmin;
tuple = SearchSysCache1(INDEXRELID,
ObjectIdGetDatum(RelationGetRelid(relation)));
@@ -1790,11 +1789,8 @@ RelationReloadIndexInfo(Relation relation)
relation->rd_index->indislive = index->indislive;
/* Copy xmin too, as that is needed to make sense of indcheckxmin */
- if (HeapTupleHeaderXminFrozen(relation->rd_indextuple->t_data))
- xmin = FrozenTransactionId;
- else
- xmin = HeapTupleHeaderGetXmin(relation->rd_indextuple->t_data);
- HeapTupleHeaderSetXmin(relation->rd_indextuple->t_data, xmin);
+ HeapTupleHeaderSetXmin(relation->rd_indextuple->t_data,
+ HeapTupleHeaderGetXmin(tuple->t_data));
ReleaseSysCache(tuple);
}
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 9246a00..cae1116 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -514,7 +514,7 @@ lookup_C_func(HeapTuple procedureTuple)
NULL);
if (entry == NULL)
return NULL; /* no such entry */
- if (entry->fn_xmin == HeapTupleHeaderGetXmin(procedureTuple->t_data) &&
+ if (entry->fn_xmin == HeapTupleHeaderGetRawXmin(procedureTuple->t_data) &&
ItemPointerEquals(&entry->fn_tid, &procedureTuple->t_self))
return entry; /* OK */
return NULL; /* entry is out of date */
@@ -552,7 +552,7 @@ record_C_func(HeapTuple procedureTuple,
HASH_ENTER,
&found);
/* OID is already filled in */
- entry->fn_xmin = HeapTupleHeaderGetXmin(procedureTuple->t_data);
+ entry->fn_xmin = HeapTupleHeaderGetRawXmin(procedureTuple->t_data);
entry->fn_tid = procedureTuple->t_self;
entry->user_fn = user_fn;
entry->inforec = inforec;
diff --git a/src/backend/utils/time/combocid.c b/src/backend/utils/time/combocid.c
index baacd4a..08955f3 100644
--- a/src/backend/utils/time/combocid.c
+++ b/src/backend/utils/time/combocid.c
@@ -148,10 +148,8 @@ HeapTupleHeaderAdjustCmax(HeapTupleHeader tup,
/*
* If we're marking a tuple deleted that was inserted by (any
* subtransaction of) our transaction, we need to use a combo command id.
- * It's cheaper to test HeapTupleHeaderXminCommitted before checking
- * TransactionIdIsCurrentTransactionId, but it's also necessary for
- * correctness: if HeapTupleHeaderXminCommitted returns true, the tuple
- * might even be frozen.
+ * Test for HeapTupleHeaderXminCommitted() first, because it's cheaper than a
+ * TransactionIdIsCurrentTransactionId call.
*/
if (!HeapTupleHeaderXminCommitted(tup) &&
TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tup)))
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
index bf8c9919..341cc4c 100644
--- a/src/backend/utils/time/tqual.c
+++ b/src/backend/utils/time/tqual.c
@@ -210,7 +210,7 @@ HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
}
}
}
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
{
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
return true;
@@ -243,11 +243,11 @@ HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
return false;
}
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
return false;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetXmin(tuple));
+ HeapTupleHeaderGetRawXmin(tuple));
else
{
/* it must have aborted or crashed */
@@ -481,7 +481,7 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
}
}
}
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
{
if (HeapTupleHeaderGetCmin(tuple) >= curcid)
return HeapTupleInvisible; /* inserted after scan started */
@@ -527,11 +527,11 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
else
return HeapTupleInvisible; /* updated before scan started */
}
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
return HeapTupleInvisible;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetXmin(tuple));
+ HeapTupleHeaderGetRawXmin(tuple));
else
{
/* it must have aborted or crashed */
@@ -709,7 +709,7 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
}
}
}
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
{
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
return true;
@@ -742,15 +742,15 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
return false;
}
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
{
- snapshot->xmin = HeapTupleHeaderGetXmin(tuple);
+ snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
/* XXX shouldn't we fall through to look at xmax? */
return true; /* in insertion by other */
}
- else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetXmin(tuple));
+ HeapTupleHeaderGetRawXmin(tuple));
else
{
/* it must have aborted or crashed */
@@ -899,7 +899,7 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
}
}
}
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
{
if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
return false; /* inserted after scan started */
@@ -940,11 +940,11 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
else
return false; /* deleted before scan started */
}
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
return false;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetXmin(tuple));
+ HeapTupleHeaderGetRawXmin(tuple));
else
{
/* it must have aborted or crashed */
@@ -959,7 +959,7 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
* when...
*/
if (!HeapTupleHeaderXminFrozen(tuple)
- && XidInMVCCSnapshot(HeapTupleHeaderGetXmin(tuple), snapshot))
+ && XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
return false; /* treat as still in progress */
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
@@ -1100,7 +1100,7 @@ HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
return HEAPTUPLE_DEAD;
}
}
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
+ else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
{
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
return HEAPTUPLE_INSERT_IN_PROGRESS;
@@ -1111,9 +1111,9 @@ HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
/* inserted and then deleted by same xact */
return HEAPTUPLE_DELETE_IN_PROGRESS;
}
- else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
+ else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetXmin(tuple));
+ HeapTupleHeaderGetRawXmin(tuple));
else
{
/*
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index f91dbc1..b3b8894 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -17,6 +17,7 @@
#include "access/htup.h"
#include "access/tupdesc.h"
#include "access/tupmacs.h"
+#include "access/transam.h"
#include "storage/bufpage.h"
/*
@@ -245,11 +246,24 @@ struct HeapTupleHeaderData
* macros evaluate their other argument only once.
*/
-#define HeapTupleHeaderGetXmin(tup) \
+/*
+ * HeapTupleHeaderGetRawXmin returns the "raw" xmin field - it might actually
+ * be frozen in which case it may never be used for anything but diagnostics
+ * purposes. Use HeapTupleHeaderGetXmin if not interested in such values or
+ * check for frozen xids with HeapTupleHeaderXminFrozen(). Not that even using
+ * *RawXmin might return FrozenTransactionId in pg_upgrade'd clusters.
+ */
+#define HeapTupleHeaderGetRawXmin(tup) \
( \
(tup)->t_choice.t_heap.t_xmin \
)
+#define HeapTupleHeaderGetXmin(tup) \
+( \
+ HeapTupleHeaderXminFrozen(tup) ? \
+ FrozenTransactionId : HeapTupleHeaderGetRawXmin(tup) \
+)
+
#define HeapTupleHeaderSetXmin(tup, xid) \
( \
(tup)->t_choice.t_heap.t_xmin = (xid) \
@@ -257,17 +271,18 @@ struct HeapTupleHeaderData
#define HeapTupleHeaderXminCommitted(tup) \
( \
- ((tup)->t_infomask & HEAP_XMIN_COMMITTED) == HEAP_XMIN_COMMITTED \
+ (tup)->t_infomask & HEAP_XMIN_COMMITTED \
)
+
#define HeapTupleHeaderXminInvalid(tup) \
( \
((tup)->t_infomask & (HEAP_XMIN_COMMITTED|HEAP_XMIN_INVALID)) == \
HEAP_XMIN_INVALID \
)
+
#define HeapTupleHeaderXminFrozen(tup) \
( \
- ((tup)->t_infomask & (HEAP_XMIN_COMMITTED|HEAP_XMIN_INVALID)) == \
- HEAP_XMIN_FROZEN \
+ ((tup)->t_infomask & (HEAP_XMIN_FROZEN)) == HEAP_XMIN_FROZEN \
)
#define HeapTupleHeaderSetXminCommitted(tup) \
@@ -275,11 +290,13 @@ struct HeapTupleHeaderData
AssertMacro(!HeapTupleHeaderXminInvalid(tup)), \
((tup)->t_infomask |= HEAP_XMIN_COMMITTED) \
)
+
#define HeapTupleHeaderSetXminInvalid(tup) \
( \
AssertMacro(!HeapTupleHeaderXminCommitted(tup)), \
((tup)->t_infomask |= HEAP_XMIN_INVALID) \
)
+
#define HeapTupleHeaderSetXminFrozen(tup) \
( \
AssertMacro(!HeapTupleHeaderXminInvalid(tup)), \
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index de8cb0e..5d418c1 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -2399,7 +2399,7 @@ validate_plperl_function(plperl_proc_ptr *proc_ptr, HeapTuple procTup)
* This is needed because CREATE OR REPLACE FUNCTION can modify the
* function's pg_proc entry without changing its OID.
************************************************************/
- uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
+ uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self));
if (uptodate)
@@ -2517,7 +2517,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
}
- prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
+ prodesc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
prodesc->fn_tid = procTup->t_self;
/* Remember if function is STABLE/IMMUTABLE */
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 426aeb5..9a16b0d 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -167,7 +167,7 @@ recheck:
if (function)
{
/* We have a compiled function, but is it still valid? */
- if (function->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
+ if (function->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
ItemPointerEquals(&function->fn_tid, &procTup->t_self))
function_valid = true;
else
@@ -345,7 +345,7 @@ do_compile(FunctionCallInfo fcinfo,
function->fn_signature = format_procedure(fcinfo->flinfo->fn_oid);
function->fn_oid = fcinfo->flinfo->fn_oid;
- function->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
+ function->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
function->fn_tid = procTup->t_self;
function->fn_input_collation = fcinfo->fncollation;
function->fn_cxt = func_cxt;
diff --git a/src/pl/plpython/plpy_procedure.c b/src/pl/plpython/plpy_procedure.c
index d278d6e..fad80b2 100644
--- a/src/pl/plpython/plpy_procedure.c
+++ b/src/pl/plpython/plpy_procedure.c
@@ -157,7 +157,7 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
proc = PLy_malloc(sizeof(PLyProcedure));
proc->proname = PLy_strdup(NameStr(procStruct->proname));
proc->pyname = PLy_strdup(procName);
- proc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
+ proc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
proc->fn_tid = procTup->t_self;
/* Remember if function is STABLE/IMMUTABLE */
proc->fn_readonly =
@@ -446,7 +446,7 @@ PLy_procedure_argument_valid(PLyTypeInfo *arg)
elog(ERROR, "cache lookup failed for relation %u", arg->typ_relid);
/* If it has changed, the cached data is not valid */
- valid = (arg->typrel_xmin == HeapTupleHeaderGetXmin(relTup->t_data) &&
+ valid = (arg->typrel_xmin == HeapTupleHeaderGetRawXmin(relTup->t_data) &&
ItemPointerEquals(&arg->typrel_tid, &relTup->t_self));
ReleaseSysCache(relTup);
@@ -466,7 +466,7 @@ PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup)
Assert(proc != NULL);
/* If the pg_proc tuple has changed, it's not valid */
- if (!(proc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
+ if (!(proc->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
ItemPointerEquals(&proc->fn_tid, &procTup->t_self)))
return false;
diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index caccbf9..40c67ca 100644
--- a/src/pl/plpython/plpy_typeio.c
+++ b/src/pl/plpython/plpy_typeio.c
@@ -157,7 +157,7 @@ PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
elog(ERROR, "cache lookup failed for relation %u", arg->typ_relid);
/* Remember XMIN and TID for later validation if cache is still OK */
- arg->typrel_xmin = HeapTupleHeaderGetXmin(relTup->t_data);
+ arg->typrel_xmin = HeapTupleHeaderGetRawXmin(relTup->t_data);
arg->typrel_tid = relTup->t_self;
ReleaseSysCache(relTup);
@@ -221,7 +221,7 @@ PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc)
elog(ERROR, "cache lookup failed for relation %u", arg->typ_relid);
/* Remember XMIN and TID for later validation if cache is still OK */
- arg->typrel_xmin = HeapTupleHeaderGetXmin(relTup->t_data);
+ arg->typrel_xmin = HeapTupleHeaderGetRawXmin(relTup->t_data);
arg->typrel_tid = relTup->t_self;
ReleaseSysCache(relTup);
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index c94d0d8..b703bda 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -1205,7 +1205,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
{
bool uptodate;
- uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
+ uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self));
if (!uptodate)
@@ -1267,7 +1267,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid, bool pltrusted)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
- prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
+ prodesc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
prodesc->fn_tid = procTup->t_self;
/* Remember if function is STABLE/IMMUTABLE */
--
1.8.5.rc2.dirty
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers