On 2018-May-24, Andres Freund wrote: > On 2018-05-24 13:08:53 -0400, Alvaro Herrera wrote: > > Hmm .. surely > > xid = HeapTupleHeaderGetXmin(tuple); > > xmin_frozen = ((xid == FrozenTransactionId) || > > HeapTupleHeaderXminFrozen(tuple)); > > - if (TransactionIdIsNormal(xid)) > > + if (!xmin_frozen && TransactionIdIsNormal(xid)) > I don't think that's necesary - HeapTupleHeaderGetXmin() returns > FrozenTransactionId if the tuple is frozen (note the > HeapTupleHeaderXminFrozen() within). Ah, yeah ... I probably thought about this when writing it and removed it for that reason. BTW I think the definition of HeapTupleHeaderXminFrozen is seriously confusing, by failing to return true if the xmin is numerically FrozenXid (which it'll be if the database was pg_upgraded). I wonder about this one in HeapTupleSatisfiesMVCC: else { /* xmin is committed, but maybe not according to our snapshot */ if (!HeapTupleHeaderXminFrozen(tuple) && XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot)) return false; /* treat as still in progress */ } I think this is not a bug only because XidInMVCCSnapshot does this /* Any xid < xmin is not in-progress */ if (TransactionIdPrecedes(xid, snapshot->xmin)) return false; which makes it return false for FrozenXid, but seems more of an accident than explicitly designed. -- Álvaro Herrera https://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services