]> granicus.if.org Git - postgresql/commitdiff
Change the way we mark tuples as frozen.
authorRobert Haas <rhaas@postgresql.org>
Sun, 22 Dec 2013 20:49:09 +0000 (15:49 -0500)
committerRobert Haas <rhaas@postgresql.org>
Sun, 22 Dec 2013 20:49:09 +0000 (15:49 -0500)
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

17 files changed:
contrib/pageinspect/heapfuncs.c
src/backend/access/common/heaptuple.c
src/backend/access/heap/heapam.c
src/backend/commands/sequence.c
src/backend/commands/vacuumlazy.c
src/backend/storage/buffer/README
src/backend/utils/fmgr/fmgr.c
src/backend/utils/time/combocid.c
src/backend/utils/time/tqual.c
src/include/access/heapam_xlog.h
src/include/access/htup_details.h
src/include/access/xlog_internal.h
src/pl/plperl/plperl.c
src/pl/plpgsql/src/pl_comp.c
src/pl/plpython/plpy_procedure.c
src/pl/plpython/plpy_typeio.c
src/pl/tcl/pltcl.c

index 6d8f6f1c74064b0b9b47e17d295833e1a5f1b11b..a78cff3179b4f4b249d3d5e92d038190cfde84ff 100644 (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);
index e39b9770cbf519cd65630f0332e92c004d4d4f7d..347d6163d3e5a5a3e5fd459ca03e9c38d0a9c648 100644 (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));
index 4dfc8b1de50fa938586e6003948d2cb9ae7403f3..f8545c1eb702de90df52e9cd4245440ad4a4e204 100644 (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);
index b6fb2e31c5954bbfc1043c624962e6f2482983e0..888a400a03f6b3659bcf3cfb629645a6b5a6aecb 100644 (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);
index 8dd3de5e8e2984e09b9f025ce3612df60bcb50a5..878f0d28ccbda6110532e76a79b2ee293f8f5e6f 100644 (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;
index 6fd961206b5026d0d6c60eaf0652895b33b63068..80443348c6062ec6b38391b1ffd7eaf4f7b39c08 100644 (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
index 9246a00cbae192a760e44ba5ad9b8e1f7f50a482..cae111646450764fa6f13a2b0ccea9674d77faa6 100644 (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;
index 923355d3ceb4f3e49a584ab3c420c7b31b0272ae..64e68eb3aae6d9b3301d404d75cb131d2c8a6313 100644 (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);
 
index 1ff1da2f07607a35920877aec2281c8a5cfa8ed0..8bd0ac01f68e285790b1583c090c0cf72a8681d7 100644 (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
index 4062b422a7d77dff6e1c616eb5b2a9a649c5d92f..0c37c42be8df8fefc26fb2eb4ed43e0166c9c99e 100644 (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
 
index 0a832e915078a35296210cdf24cab4303b711877..c53305ffcda6344d5242b1696d4bde21cf30974d 100644 (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) \
index c301d5e5d65b8d5bcda90b45a394add8e26d05d8..1f791826ae8793798c771c87840bc4d93eb8a796 100644 (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
 {
index 4f5b92fa3affbf77b7308dc8ea88cfbb1ca84631..a81c18513e506b326e1aa36fb384149cee7b80b7 100644 (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 */
index 426aeb53f5888c1c1f7348ca4aaabda1925dc18d..9a16b0d4d19401abbf03cb719a29de96f9953d6e 100644 (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;
index d278d6e7058c41ec49dffd4a57135ebb8dd895b6..fad80b242e75a9a11a2e8214d6b08695bfc30216 100644 (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;
 
index 0a2307abdb98306a703b9b343f52e08126c87de7..7a5e581280d9b39cb1fb3515ece0fb0722cd1ff1 100644 (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);
index 9b801b153a193a08ad8cc0f8641bf8c5ebd7b8e7..053803898215e6740c39b6342363f9e8399807cd 100644 (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 */