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 */