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);
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));
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);
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;
}
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)
* 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))
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);
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);
* 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;
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;
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
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 */
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;
/*
* 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);
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 */
}
}
}
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
{
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
return true;
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 */
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 */
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 */
}
}
}
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
{
if (HeapTupleHeaderGetCmin(tuple) >= curcid)
return HeapTupleInvisible; /* inserted after scan started */
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 */
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 */
}
}
}
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
{
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
return true;
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 */
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 */
}
}
}
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
{
if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
return false; /* inserted after scan started */
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 */
* 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 */
* 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)
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;
/* 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
{
/*
* 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
* 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
#include "access/htup.h"
#include "access/tupdesc.h"
#include "access/tupmacs.h"
+#include "access/transam.h"
#include "storage/bufpage.h"
/*
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 */
* 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
#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) \
/*
* 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
{
* 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)
(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 */
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
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;
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 =
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);
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;
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);
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);
{
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)
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 */