From 37484ad2aacef5ec794f4dd3d5cf814475180a78 Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Sun, 22 Dec 2013 15:49:09 -0500 Subject: [PATCH] Change the way we mark tuples as frozen. Instead of changing the tuple xmin to FrozenTransactionId, the combination of HEAP_XMIN_COMMITTED and HEAP_XMIN_INVALID, which were previously never set together, is now defined as HEAP_XMIN_FROZEN. A variety of previous proposals to freeze tuples opportunistically before vacuum_freeze_min_age is reached have foundered on the objection that replacing xmin by FrozenTransactionId might hinder debugging efforts when things in this area go awry; this patch is intended to solve that problem by keeping the XID around (but largely ignoring the value to which it is set). Third-party code that checks for HEAP_XMIN_INVALID on tuples where HEAP_XMIN_COMMITTED might be set will be broken by this change. To fix, use the new accessor macros in htup_details.h rather than consulting the bits directly. HeapTupleHeaderGetXmin has been modified to return FrozenTransactionId when the infomask bits indicate that the tuple is frozen; use HeapTupleHeaderGetRawXmin when you already know that the tuple isn't marked commited or frozen, or want the raw value anyway. We currently do this in routines that display the xmin for user consumption, in tqual.c where it's known to be safe and important for the avoidance of extra cycles, and in the function-caching code for various procedural languages, which shouldn't invalidate the cache just because the tuple gets frozen. Robert Haas and Andres Freund --- contrib/pageinspect/heapfuncs.c | 2 +- src/backend/access/common/heaptuple.c | 2 +- src/backend/access/heap/heapam.c | 28 +++-------- src/backend/commands/sequence.c | 4 +- src/backend/commands/vacuumlazy.c | 8 +-- src/backend/storage/buffer/README | 3 ++ src/backend/utils/fmgr/fmgr.c | 4 +- src/backend/utils/time/combocid.c | 6 +-- src/backend/utils/time/tqual.c | 71 ++++++++++++++------------- src/include/access/heapam_xlog.h | 2 +- src/include/access/htup_details.h | 55 ++++++++++++++++++++- src/include/access/xlog_internal.h | 2 +- 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 +- 17 files changed, 125 insertions(+), 84 deletions(-) diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c index 6d8f6f1c74..a78cff3179 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 e39b9770cb..347d6163d3 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 4dfc8b1de5..f8545c1eb7 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -2257,13 +2257,10 @@ 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); @@ -5732,13 +5729,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid, if (TransactionIdIsNormal(xid) && TransactionIdPrecedes(xid, cutoff_xid)) { - frz->frzflags |= XLH_FREEZE_XMIN; - - /* - * Might as well fix the hint bits too; usually XMIN_COMMITTED will - * already be set here, but there's a small chance not. - */ - frz->t_infomask |= HEAP_XMIN_COMMITTED; + frz->t_infomask |= HEAP_XMIN_FROZEN; changed = true; } @@ -5882,9 +5873,6 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid, void heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz) { - if (frz->frzflags & XLH_FREEZE_XMIN) - HeapTupleHeaderSetXmin(tuple, FrozenTransactionId); - HeapTupleHeaderSetXmax(tuple, frz->xmax); if (frz->frzflags & XLH_FREEZE_XVAC) @@ -6361,10 +6349,8 @@ 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 && TransactionIdFollows(xmax, *latestRemovedXid)) @@ -6882,7 +6868,7 @@ log_heap_new_cid(Relation relation, HeapTuple tup) if (hdr->t_infomask & HEAP_COMBOCID) { Assert(!(hdr->t_infomask & HEAP_XMAX_INVALID)); - Assert(!(hdr->t_infomask & HEAP_XMIN_INVALID)); + Assert(!HeapTupleHeaderXminInvalid(hdr)); xlrec.cmin = HeapTupleHeaderGetCmin(hdr); xlrec.cmax = HeapTupleHeaderGetCmax(hdr); xlrec.combocid = HeapTupleHeaderGetRawCommandId(hdr); diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index b6fb2e31c5..888a400a03 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -361,10 +361,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/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index 8dd3de5e8e..878f0d28cc 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -823,14 +823,14 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, * NB: Like with per-tuple hint bits, we can't set the * PD_ALL_VISIBLE flag if the inserter committed * asynchronously. See SetHintBits for more info. Check - * that the HEAP_XMIN_COMMITTED hint bit is set because of - * that. + * that the tuple is hinted xmin-committed because + * of that. */ if (all_visible) { TransactionId xmin; - if (!(tuple.t_data->t_infomask & HEAP_XMIN_COMMITTED)) + if (!HeapTupleHeaderXminCommitted(tuple.t_data)) { all_visible = false; break; @@ -1774,7 +1774,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; diff --git a/src/backend/storage/buffer/README b/src/backend/storage/buffer/README index 6fd961206b..80443348c6 100644 --- a/src/backend/storage/buffer/README +++ b/src/backend/storage/buffer/README @@ -65,6 +65,9 @@ manage to be a conflict it would merely mean that one bit-update would be lost and need to be done again later. These four bits are only hints (they cache the results of transaction status lookups in pg_clog), so no great harm is done if they get reset to zero by conflicting updates. +Note, however, that a tuple is frozen by setting both HEAP_XMIN_INVALID +and HEAP_XMIN_COMMITTED; this is a critical update and accordingly requires +an exclusive buffer lock (and it must also be WAL-logged). 5. To physically remove a tuple or compact free space on a page, one must hold a pin and an exclusive lock, *and* observe while holding the diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 9246a00cba..cae1116464 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 923355d3ce..64e68eb3aa 100644 --- a/src/backend/utils/time/combocid.c +++ b/src/backend/utils/time/combocid.c @@ -148,11 +148,11 @@ 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 + * Test for HeapTupleHeaderXminCommitted() first, because it's cheaper than a * TransactionIdIsCurrentTransactionId call. */ - if (!(tup->t_infomask & HEAP_XMIN_COMMITTED) && - TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tup))) + if (!HeapTupleHeaderXminCommitted(tup) && + TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tup))) { CommandId cmin = HeapTupleHeaderGetCmin(tup); diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c index 1ff1da2f07..8bd0ac01f6 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 */ @@ -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; @@ -244,11 +244,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 */ @@ -356,9 +356,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 */ @@ -441,9 +441,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 */ @@ -485,7 +485,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 */ @@ -564,11 +564,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 */ @@ -715,9 +715,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 */ @@ -759,7 +759,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; @@ -793,15 +793,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 */ @@ -909,9 +909,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 */ @@ -953,7 +953,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 */ @@ -995,11 +995,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 */ @@ -1013,7 +1013,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(HeapTupleHeaderGetRawXmin(tuple), snapshot)) return false; /* treat as still in progress */ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */ @@ -1116,9 +1117,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) @@ -1157,7 +1158,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; @@ -1168,9 +1169,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 { /* @@ -1347,8 +1348,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/heapam_xlog.h b/src/include/access/heapam_xlog.h index 4062b422a7..0c37c42be8 100644 --- a/src/include/access/heapam_xlog.h +++ b/src/include/access/heapam_xlog.h @@ -274,7 +274,7 @@ typedef struct xl_heap_inplace * This struct represents a 'freeze plan', which is what we need to know about * a single tuple being frozen during vacuum. */ -#define XLH_FREEZE_XMIN 0x01 +/* 0x01 was XLH_FREEZE_XMIN */ #define XLH_FREEZE_XVAC 0x02 #define XLH_INVALID_XVAC 0x04 diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h index 0a832e9150..c53305ffcd 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" /* @@ -175,6 +176,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 */ @@ -244,16 +246,64 @@ struct HeapTupleHeaderData * macros evaluate their other argument only once. */ -#define HeapTupleHeaderGetXmin(tup) \ +/* + * HeapTupleHeaderGetRawXmin returns the "raw" xmin field, which is the xid + * originally used to insert the tuple. However, the tuple might actually + * be frozen (via HeapTupleHeaderSetXminFrozen) in which case the tuple's xmin + * is visible to every snapshot. Prior to PostgreSQL 9.4, we actually changed + * the xmin to FrozenTransactionId, and that value may still be encountered + * on disk. + */ +#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) \ ) +#define HeapTupleHeaderXminCommitted(tup) \ +( \ + (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_FROZEN)) == 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 +424,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) \ diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index c301d5e5d6..1f791826ae 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -55,7 +55,7 @@ typedef struct BkpBlock /* * Each page of XLOG file has a header like this: */ -#define XLOG_PAGE_MAGIC 0xD07A /* can be used as WAL version indicator */ +#define XLOG_PAGE_MAGIC 0xD07B /* can be used as WAL version indicator */ typedef struct XLogPageHeaderData { diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c index 4f5b92fa3a..a81c18513e 100644 --- a/src/pl/plperl/plperl.c +++ b/src/pl/plperl/plperl.c @@ -2524,7 +2524,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) @@ -2642,7 +2642,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_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 426aeb53f5..9a16b0d4d1 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 d278d6e705..fad80b242e 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 0a2307abdb..7a5e581280 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 9b801b153a..0538038982 100644 --- a/src/pl/tcl/pltcl.c +++ b/src/pl/tcl/pltcl.c @@ -1257,7 +1257,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid, { 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) @@ -1322,7 +1322,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid, 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 */