diff options
Diffstat (limited to 'src/backend/utils/time/tqual.c')
-rw-r--r-- | src/backend/utils/time/tqual.c | 466 |
1 files changed, 379 insertions, 87 deletions
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c index 51f0afded98..f2c9ff2e1c1 100644 --- a/src/backend/utils/time/tqual.c +++ b/src/backend/utils/time/tqual.c @@ -214,12 +214,25 @@ HeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return true; - if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */ + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */ return true; - Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)); + if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) + { + TransactionId xmax; + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return true; + + /* updating subtransaction must have aborted */ + if (!TransactionIdIsCurrentTransactionId(xmax)) + return true; + else + return false; + } - if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { /* deleting subtransaction must have aborted */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -250,29 +263,41 @@ HeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; return false; /* updated by other */ } if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { - /* MultiXacts are currently only allowed to lock tuples */ - Assert(tuple->t_infomask & HEAP_IS_LOCKED); + TransactionId xmax; + + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) + return true; + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return true; + if (TransactionIdIsCurrentTransactionId(xmax)) + return false; + if (TransactionIdIsInProgress(xmax)) + return true; + if (TransactionIdDidCommit(xmax)) + return false; return true; } - if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; return false; } - if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) return true; - if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) + if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple))) { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -282,7 +307,7 @@ HeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) /* xmax transaction committed */ - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) { SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); @@ -290,7 +315,7 @@ HeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) } SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, - HeapTupleHeaderGetXmax(tuple)); + HeapTupleHeaderGetRawXmax(tuple)); return false; } @@ -380,12 +405,25 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return true; - if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */ + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */ return true; - Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)); + if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) + { + TransactionId xmax; - if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return true; + + /* updating subtransaction must have aborted */ + if (!TransactionIdIsCurrentTransactionId(xmax)) + return true; + else + return false; + } + + if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { /* deleting subtransaction must have aborted */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -419,21 +457,38 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; return false; } if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { - /* MultiXacts are currently only allowed to lock tuples */ - Assert(tuple->t_infomask & HEAP_IS_LOCKED); + TransactionId xmax; + + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) + return true; + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return true; + if (TransactionIdIsCurrentTransactionId(xmax)) + { + if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId(false)) + return true; /* deleted after scan started */ + else + return false; /* deleted before scan started */ + } + if (TransactionIdIsInProgress(xmax)) + return true; + if (TransactionIdDidCommit(xmax)) + return false; return true; } - if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId(false)) return true; /* deleted after scan started */ @@ -441,10 +496,10 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) return false; /* deleted before scan started */ } - if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) return true; - if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) + if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple))) { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -454,7 +509,7 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) /* xmax transaction committed */ - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) { SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); @@ -462,7 +517,7 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) } SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, - HeapTupleHeaderGetXmax(tuple)); + HeapTupleHeaderGetRawXmax(tuple)); return false; } @@ -627,12 +682,30 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return HeapTupleMayBeUpdated; - if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */ + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */ return HeapTupleMayBeUpdated; - Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)); + if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) + { + TransactionId xmax; + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return HeapTupleMayBeUpdated; + + /* updating subtransaction must have aborted */ + if (!TransactionIdIsCurrentTransactionId(xmax)) + return HeapTupleMayBeUpdated; + else + { + if (HeapTupleHeaderGetCmax(tuple) >= curcid) + return HeapTupleSelfUpdated; /* updated after scan started */ + else + return HeapTupleInvisible; /* updated before scan started */ + } + } - if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { /* deleting subtransaction must have aborted */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -666,26 +739,62 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return HeapTupleMayBeUpdated; return HeapTupleUpdated; /* updated by other */ } if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { - /* MultiXacts are currently only allowed to lock tuples */ - Assert(tuple->t_infomask & HEAP_IS_LOCKED); + TransactionId xmax; - if (MultiXactIdIsRunning(HeapTupleHeaderGetXmax(tuple))) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) + { + /* + * If it's only locked but neither EXCL_LOCK nor KEYSHR_LOCK + * is set, it cannot possibly be running. Otherwise need to + * check. + */ + if ((tuple->t_infomask & (HEAP_XMAX_EXCL_LOCK | + HEAP_XMAX_KEYSHR_LOCK)) && + MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple))) + return HeapTupleBeingUpdated; + + SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); + return HeapTupleMayBeUpdated; + } + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + { + if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple))) + return HeapTupleBeingUpdated; + + SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); + return HeapTupleMayBeUpdated; + } + + if (TransactionIdIsCurrentTransactionId(xmax)) + { + if (HeapTupleHeaderGetCmax(tuple) >= curcid) + return HeapTupleSelfUpdated; /* updated after scan started */ + else + return HeapTupleInvisible; /* updated before scan started */ + } + + if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple))) return HeapTupleBeingUpdated; - SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, - InvalidTransactionId); + + if (TransactionIdDidCommit(xmax)) + return HeapTupleUpdated; + /* it must have aborted or crashed */ + SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); return HeapTupleMayBeUpdated; } - if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return HeapTupleMayBeUpdated; if (HeapTupleHeaderGetCmax(tuple) >= curcid) return HeapTupleSelfUpdated; /* updated after scan started */ @@ -693,10 +802,10 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, return HeapTupleInvisible; /* updated before scan started */ } - if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) return HeapTupleBeingUpdated; - if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) + if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple))) { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -706,7 +815,7 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, /* xmax transaction committed */ - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) { SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); @@ -714,7 +823,7 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, } SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, - HeapTupleHeaderGetXmax(tuple)); + HeapTupleHeaderGetRawXmax(tuple)); return HeapTupleUpdated; /* updated by other */ } @@ -793,12 +902,25 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Snapshot snapshot, if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return true; - if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */ + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */ return true; - Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)); + if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) + { + TransactionId xmax; - if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return true; + + /* updating subtransaction must have aborted */ + if (!TransactionIdIsCurrentTransactionId(xmax)) + return true; + else + return false; + } + + if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { /* deleting subtransaction must have aborted */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -833,32 +955,47 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Snapshot snapshot, if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; return false; /* updated by other */ } if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { - /* MultiXacts are currently only allowed to lock tuples */ - Assert(tuple->t_infomask & HEAP_IS_LOCKED); + TransactionId xmax; + + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) + return true; + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return true; + if (TransactionIdIsCurrentTransactionId(xmax)) + return false; + if (TransactionIdIsInProgress(xmax)) + { + snapshot->xmax = xmax; + return true; + } + if (TransactionIdDidCommit(xmax)) + return false; return true; } - if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; return false; } - if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) { - snapshot->xmax = HeapTupleHeaderGetXmax(tuple); + snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple); return true; } - if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) + if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple))) { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -868,7 +1005,7 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Snapshot snapshot, /* xmax transaction committed */ - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) { SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); @@ -876,7 +1013,7 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Snapshot snapshot, } SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, - HeapTupleHeaderGetXmax(tuple)); + HeapTupleHeaderGetRawXmax(tuple)); return false; /* updated by other */ } @@ -957,12 +1094,27 @@ HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot, if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return true; - if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */ + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */ return true; - Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)); + if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) + { + TransactionId xmax; + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return true; + + /* updating subtransaction must have aborted */ + if (!TransactionIdIsCurrentTransactionId(xmax)) + return true; + else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid) + return true; /* updated after scan started */ + else + return false; /* updated before scan started */ + } - if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { /* deleting subtransaction must have aborted */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -999,19 +1151,41 @@ HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot, if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */ return true; - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { - /* MultiXacts are currently only allowed to lock tuples */ - Assert(tuple->t_infomask & HEAP_IS_LOCKED); + TransactionId xmax; + + /* already checked above */ + Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)); + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return true; + if (TransactionIdIsCurrentTransactionId(xmax)) + { + if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid) + return true; /* deleted after scan started */ + else + return false; /* deleted before scan started */ + } + if (TransactionIdIsInProgress(xmax)) + return true; + if (TransactionIdDidCommit(xmax)) + { + /* updating transaction committed, but when? */ + if (XidInMVCCSnapshot(xmax, snapshot)) + return true; /* treat as still in progress */ + return false; + } return true; } if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) { - if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid) return true; /* deleted after scan started */ @@ -1019,10 +1193,10 @@ HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot, return false; /* deleted before scan started */ } - if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) return true; - if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) + if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple))) { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -1032,13 +1206,13 @@ HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot, /* xmax transaction committed */ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, - HeapTupleHeaderGetXmax(tuple)); + HeapTupleHeaderGetRawXmax(tuple)); } /* * OK, the deleting transaction committed too ... but when? */ - if (XidInMVCCSnapshot(HeapTupleHeaderGetXmax(tuple), snapshot)) + if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot)) return true; /* treat as still in progress */ return false; @@ -1112,7 +1286,7 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin, { if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return HEAPTUPLE_INSERT_IN_PROGRESS; - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return HEAPTUPLE_INSERT_IN_PROGRESS; /* inserted and then deleted by same xact */ return HEAPTUPLE_DELETE_IN_PROGRESS; @@ -1144,7 +1318,7 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin, if (tuple->t_infomask & HEAP_XMAX_INVALID) return HEAPTUPLE_LIVE; - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) { /* * "Deleting" xact really only locked it, so the tuple is live in any @@ -1158,40 +1332,96 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin, { if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { - if (MultiXactIdIsRunning(HeapTupleHeaderGetXmax(tuple))) + /* + * If it's only locked but neither EXCL_LOCK nor KEYSHR_LOCK + * are set, it cannot possibly be running; otherwise have to + * check. + */ + if ((tuple->t_infomask & (HEAP_XMAX_EXCL_LOCK | + HEAP_XMAX_KEYSHR_LOCK)) && + MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple))) return HEAPTUPLE_LIVE; + SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); + } else { - if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) return HEAPTUPLE_LIVE; + SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, + InvalidTransactionId); } - - /* - * We don't really care whether xmax did commit, abort or crash. - * We know that xmax did lock the tuple, but it did not and will - * never actually update it. - */ - SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, - InvalidTransactionId); } + + /* + * We don't really care whether xmax did commit, abort or crash. + * We know that xmax did lock the tuple, but it did not and will + * never actually update it. + */ + return HEAPTUPLE_LIVE; } if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { - /* MultiXacts are currently only allowed to lock tuples */ - Assert(tuple->t_infomask & HEAP_IS_LOCKED); - return HEAPTUPLE_LIVE; + TransactionId xmax; + + if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple))) + { + /* already checked above */ + Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)); + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return HEAPTUPLE_LIVE; + if (TransactionIdIsInProgress(xmax)) + return HEAPTUPLE_DELETE_IN_PROGRESS; + else if (TransactionIdDidCommit(xmax)) + /* there are still lockers around -- can't return DEAD here */ + return HEAPTUPLE_RECENTLY_DEAD; + /* updating transaction aborted */ + return HEAPTUPLE_LIVE; + } + + Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED)); + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return HEAPTUPLE_LIVE; + /* multi is not running -- updating xact cannot be */ + Assert(!TransactionIdIsInProgress(xmax)); + if (TransactionIdDidCommit(xmax)) + { + if (!TransactionIdPrecedes(xmax, OldestXmin)) + return HEAPTUPLE_RECENTLY_DEAD; + else + return HEAPTUPLE_DEAD; + } + else + { + /* + * Not in Progress, Not Committed, so either Aborted or crashed. + */ + SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); + return HEAPTUPLE_LIVE; + } + + /* + * Deleter committed, but perhaps it was recent enough that some open + * transactions could still see the tuple. + */ + + /* Otherwise, it's dead and removable */ + return HEAPTUPLE_DEAD; } if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) { - if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) return HEAPTUPLE_DELETE_IN_PROGRESS; - else if (TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) + else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple))) SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, - HeapTupleHeaderGetXmax(tuple)); + HeapTupleHeaderGetRawXmax(tuple)); else { /* @@ -1213,7 +1443,7 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin, * Deleter committed, but perhaps it was recent enough that some open * transactions could still see the tuple. */ - if (!TransactionIdPrecedes(HeapTupleHeaderGetXmax(tuple), OldestXmin)) + if (!TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin)) return HEAPTUPLE_RECENTLY_DEAD; /* Otherwise, it's dead and removable */ @@ -1246,11 +1476,22 @@ HeapTupleIsSurelyDead(HeapTupleHeader tuple, TransactionId OldestXmin) /* * If the inserting transaction committed, but any deleting transaction - * aborted, the tuple is still alive. Likewise, if XMAX is a lock rather - * than a delete, the tuple is still alive. + * aborted, the tuple is still alive. */ - if (tuple->t_infomask & - (HEAP_XMAX_INVALID | HEAP_IS_LOCKED | HEAP_XMAX_IS_MULTI)) + if (tuple->t_infomask & HEAP_XMAX_INVALID) + return false; + + /* + * If the XMAX is just a lock, the tuple is still alive. + */ + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) + return false; + + /* + * If the Xmax is a MultiXact, it might be dead or alive, but we cannot + * know without checking pg_multixact. + */ + if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) return false; /* If deleter isn't known to have committed, assume it's still running. */ @@ -1258,7 +1499,7 @@ HeapTupleIsSurelyDead(HeapTupleHeader tuple, TransactionId OldestXmin) return false; /* Deleter committed, so tuple is dead if the XID is old enough. */ - return TransactionIdPrecedes(HeapTupleHeaderGetXmax(tuple), OldestXmin); + return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin); } /* @@ -1375,3 +1616,54 @@ XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot) return false; } + +/* + * Is the tuple really only locked? That is, is it not updated? + * + * It's easy to check just infomask bits if the locker is not a multi; but + * otherwise we need to verify that the updating transaction has not aborted. + * + * This function is here because it follows the same time qualification rules + * laid out at the top of this file. + */ +bool +HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple) +{ + TransactionId xmax; + + /* if there's no valid Xmax, then there's obviously no update either */ + if (tuple->t_infomask & HEAP_XMAX_INVALID) + return true; + + if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY) + return true; + + /* invalid xmax means no update */ + if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple))) + return true; + + /* + * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this + * must necessarily have been updated + */ + if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)) + return false; + + /* ... but if it's a multi, then perhaps the updating Xid aborted. */ + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) /* shouldn't happen .. */ + return true; + + if (TransactionIdIsCurrentTransactionId(xmax)) + return false; + if (TransactionIdIsInProgress(xmax)) + return false; + if (TransactionIdDidCommit(xmax)) + return false; + + /* + * not current, not in progress, not committed -- must have aborted or + * crashed + */ + return true; +} |