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
This commit is contained in:
Robert Haas 2013-12-22 15:49:09 -05:00
parent 961bf59fb7
commit 37484ad2aa
17 changed files with 125 additions and 84 deletions

View File

@ -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);

View File

@ -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));

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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) \

View File

@ -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
{

View File

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

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

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