/* * Given an opened sequence relation, lock the page buffer and find the tuple * * *buf receives the reference to the pinned-and-ex-locked buffer * *seqtuple receives the reference to the sequence tuple proper * (this arg should point to a local variable of type HeapTupleData) * * Function's return value points to the data payload of the tuple */ static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple) { Page page; ItemId lp; sequence_magic *sm; Form_pg_sequence seq; *buf = ReadBuffer(rel, 0); LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE); page = BufferGetPage(*buf); sm = (sequence_magic *) PageGetSpecialPointer(page); if (sm->magic != SEQ_MAGIC) elog(ERROR, "bad magic number in sequence \"%s\": %08X", RelationGetRelationName(rel), sm->magic); lp = PageGetItemId(page, FirstOffsetNumber); Assert(ItemIdIsNormal(lp)); /* Note we currently only bother to set these two fields of *seqtuple */ seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp); seqtuple->t_len = ItemIdGetLength(lp); /* * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on * a sequence, which would leave a non-frozen XID in the sequence tuple's * xmax, which eventually leads to clog access failures or worse. If we * see this has happened, clean up after it. We treat this like a hint * bit update, ie, don't bother to WAL-log it, since we can certainly do * this again if the update gets lost. */ Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI)); if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId) { HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId); seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED; seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID; MarkBufferDirtyHint(*buf, true); } seq = (Form_pg_sequence) GETSTRUCT(seqtuple); /* this is a handy place to update our copy of the increment */ elm->increment = seq->increment_by; return seq; }
/* * HeapTupleIsSurelyDead * * Determine whether a tuple is surely dead. We sometimes use this * in lieu of HeapTupleSatisifesVacuum when the tuple has just been * tested by HeapTupleSatisfiesMVCC and, therefore, any hint bits that * can be set should already be set. We assume that if no hint bits * either for xmin or xmax, the transaction is still running. This is * therefore faster than HeapTupleSatisfiesVacuum, because we don't * consult CLOG (and also because we don't need to give an exact answer, * just whether or not the tuple is surely dead). */ bool HeapTupleIsSurelyDead(HeapTuple htup, TransactionId OldestXmin) { HeapTupleHeader tuple = htup->t_data; Assert(ItemPointerIsValid(&htup->t_self)); Assert(htup->t_tableOid != InvalidOid); /* * If the inserting transaction is marked invalid, then it aborted, and * the tuple is definitely dead. If it's marked neither committed nor * invalid, then we assume it's still alive (since the presumption is that * all relevant hint bits were just set moments ago). */ if (!HeapTupleHeaderXminCommitted(tuple)) return HeapTupleHeaderXminInvalid(tuple) ? true : false; /* * If the inserting transaction committed, but any deleting transaction * aborted, the tuple is still alive. */ 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. */ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) return false; /* Deleter committed, so tuple is dead if the XID is old enough. */ return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin); }
/* * 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); /* not LOCKED_ONLY, so it has to have an xmax */ Assert(TransactionIdIsValid(xmax)); 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; }
/* * HeapTupleSatisfiesMVCC * True iff heap tuple is valid for the given MVCC snapshot. * * Here, we consider the effects of: * all transactions committed as of the time of the given snapshot * previous commands of this transaction * * Does _not_ include: * transactions shown as in-progress by the snapshot * transactions started after the snapshot was taken * changes made by the current command * * (Notice, however, that the tuple status hint bits will be updated on the * basis of the true state of the transaction, even if we then pretend we * can't see it.) */ bool HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot, Buffer buffer) { HeapTupleHeader tuple = htup->t_data; Assert(ItemPointerIsValid(&htup->t_self)); Assert(htup->t_tableOid != InvalidOid); if (!HeapTupleHeaderXminCommitted(tuple)) { if (HeapTupleHeaderXminInvalid(tuple)) return false; /* Used by pre-9.0 binary upgrades */ if (tuple->t_infomask & HEAP_MOVED_OFF) { TransactionId xvac = HeapTupleHeaderGetXvac(tuple); if (TransactionIdIsCurrentTransactionId(xvac)) return false; if (!TransactionIdIsInProgress(xvac)) { if (TransactionIdDidCommit(xvac)) { SetHintBits(tuple, buffer, HEAP_XMIN_INVALID, InvalidTransactionId); return false; } SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, InvalidTransactionId); } } /* Used by pre-9.0 binary upgrades */ else if (tuple->t_infomask & HEAP_MOVED_IN) { TransactionId xvac = HeapTupleHeaderGetXvac(tuple); if (!TransactionIdIsCurrentTransactionId(xvac)) { if (TransactionIdIsInProgress(xvac)) return false; if (TransactionIdDidCommit(xvac)) SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, InvalidTransactionId); else { SetHintBits(tuple, buffer, HEAP_XMIN_INVALID, InvalidTransactionId); return false; } } } else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple))) { if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid) return false; /* inserted after scan started */ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return true; if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */ return true; if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { TransactionId xmax; xmax = HeapTupleGetUpdateXid(tuple); /* not LOCKED_ONLY, so it has to have an xmax */ Assert(TransactionIdIsValid(xmax)); /* 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(HeapTupleHeaderGetRawXmax(tuple))) { /* deleting subtransaction must have aborted */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); return true; } if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid) return true; /* deleted after scan started */ else return false; /* deleted before scan started */ } else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple))) return false; else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple))) SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, HeapTupleHeaderGetRawXmin(tuple)); else { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID, InvalidTransactionId); return false; } } /* * By here, the inserting transaction has committed - have to check * when... */ 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 */ return true; if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { TransactionId xmax; /* already checked above */ Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)); xmax = HeapTupleGetUpdateXid(tuple); /* not LOCKED_ONLY, so it has to have an xmax */ Assert(TransactionIdIsValid(xmax)); 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; } /* it must have aborted or crashed */ return true; } if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) { if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid) return true; /* deleted after scan started */ else return false; /* deleted before scan started */ } if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) return true; if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple))) { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); return true; } /* xmax transaction committed */ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, HeapTupleHeaderGetRawXmax(tuple)); } /* * OK, the deleting transaction committed too ... but when? */ if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot)) return true; /* treat as still in progress */ return false; }
/* * HeapTupleSatisfiesDirty * True iff heap tuple is valid including effects of open transactions. * * Here, we consider the effects of: * all committed and in-progress transactions (as of the current instant) * previous commands of this transaction * changes made by the current command * * This is essentially like HeapTupleSatisfiesSelf as far as effects of * the current transaction and committed/aborted xacts are concerned. * However, we also include the effects of other xacts still in progress. * * A special hack is that the passed-in snapshot struct is used as an * output argument to return the xids of concurrent xacts that affected the * tuple. snapshot->xmin is set to the tuple's xmin if that is another * transaction that's still in progress; or to InvalidTransactionId if the * tuple's xmin is committed good, committed dead, or my own xact. Similarly * for snapshot->xmax and the tuple's xmax. */ bool HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot, Buffer buffer) { HeapTupleHeader tuple = htup->t_data; Assert(ItemPointerIsValid(&htup->t_self)); Assert(htup->t_tableOid != InvalidOid); snapshot->xmin = snapshot->xmax = InvalidTransactionId; if (!HeapTupleHeaderXminCommitted(tuple)) { if (HeapTupleHeaderXminInvalid(tuple)) return false; /* Used by pre-9.0 binary upgrades */ if (tuple->t_infomask & HEAP_MOVED_OFF) { TransactionId xvac = HeapTupleHeaderGetXvac(tuple); if (TransactionIdIsCurrentTransactionId(xvac)) return false; if (!TransactionIdIsInProgress(xvac)) { if (TransactionIdDidCommit(xvac)) { SetHintBits(tuple, buffer, HEAP_XMIN_INVALID, InvalidTransactionId); return false; } SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, InvalidTransactionId); } } /* Used by pre-9.0 binary upgrades */ else if (tuple->t_infomask & HEAP_MOVED_IN) { TransactionId xvac = HeapTupleHeaderGetXvac(tuple); if (!TransactionIdIsCurrentTransactionId(xvac)) { if (TransactionIdIsInProgress(xvac)) return false; if (TransactionIdDidCommit(xvac)) SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, InvalidTransactionId); else { SetHintBits(tuple, buffer, HEAP_XMIN_INVALID, InvalidTransactionId); return false; } } } else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple))) { if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return true; if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */ return true; if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { TransactionId xmax; xmax = HeapTupleGetUpdateXid(tuple); /* not LOCKED_ONLY, so it has to have an xmax */ Assert(TransactionIdIsValid(xmax)); /* 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, InvalidTransactionId); return true; } return false; } else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple))) { snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple); /* XXX shouldn't we fall through to look at xmax? */ return true; /* in insertion by other */ } else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple))) SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, HeapTupleHeaderGetRawXmin(tuple)); else { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID, InvalidTransactionId); return false; } } /* by here, the inserting transaction has committed */ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */ return true; if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; return false; /* updated by other */ } if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { TransactionId xmax; if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; xmax = HeapTupleGetUpdateXid(tuple); /* not LOCKED_ONLY, so it has to have an xmax */ Assert(TransactionIdIsValid(xmax)); if (TransactionIdIsCurrentTransactionId(xmax)) return false; if (TransactionIdIsInProgress(xmax)) { snapshot->xmax = xmax; return true; } if (TransactionIdDidCommit(xmax)) return false; /* it must have aborted or crashed */ return true; } if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; return false; } if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) { if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple); return true; } if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple))) { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); return true; } /* xmax transaction committed */ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) { SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); return true; } SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, HeapTupleHeaderGetRawXmax(tuple)); return false; /* updated by other */ }
/* * HeapTupleSatisfiesUpdate * * This function returns a more detailed result code than most of the * functions in this file, since UPDATE needs to know more than "is it * visible?". It also allows for user-supplied CommandId rather than * relying on CurrentCommandId. * * The possible return codes are: * * HeapTupleInvisible: the tuple didn't exist at all when the scan started, * e.g. it was created by a later CommandId. * * HeapTupleMayBeUpdated: The tuple is valid and visible, so it may be * updated. * * HeapTupleSelfUpdated: The tuple was updated by the current transaction, * after the current scan started. * * HeapTupleUpdated: The tuple was updated by a committed transaction. * * HeapTupleBeingUpdated: The tuple is being updated by an in-progress * transaction other than the current transaction. (Note: this includes * the case where the tuple is share-locked by a MultiXact, even if the * MultiXact includes the current transaction. Callers that want to * distinguish that case must test for it themselves.) */ HTSU_Result HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid, Buffer buffer) { HeapTupleHeader tuple = htup->t_data; Assert(ItemPointerIsValid(&htup->t_self)); Assert(htup->t_tableOid != InvalidOid); if (!HeapTupleHeaderXminCommitted(tuple)) { if (HeapTupleHeaderXminInvalid(tuple)) return HeapTupleInvisible; /* Used by pre-9.0 binary upgrades */ if (tuple->t_infomask & HEAP_MOVED_OFF) { TransactionId xvac = HeapTupleHeaderGetXvac(tuple); if (TransactionIdIsCurrentTransactionId(xvac)) return HeapTupleInvisible; if (!TransactionIdIsInProgress(xvac)) { if (TransactionIdDidCommit(xvac)) { SetHintBits(tuple, buffer, HEAP_XMIN_INVALID, InvalidTransactionId); return HeapTupleInvisible; } SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, InvalidTransactionId); } } /* Used by pre-9.0 binary upgrades */ else if (tuple->t_infomask & HEAP_MOVED_IN) { TransactionId xvac = HeapTupleHeaderGetXvac(tuple); if (!TransactionIdIsCurrentTransactionId(xvac)) { if (TransactionIdIsInProgress(xvac)) return HeapTupleInvisible; if (TransactionIdDidCommit(xvac)) SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, InvalidTransactionId); else { SetHintBits(tuple, buffer, HEAP_XMIN_INVALID, InvalidTransactionId); return HeapTupleInvisible; } } } else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple))) { if (HeapTupleHeaderGetCmin(tuple) >= curcid) return HeapTupleInvisible; /* inserted after scan started */ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return HeapTupleMayBeUpdated; if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) { TransactionId xmax; xmax = HeapTupleHeaderGetRawXmax(tuple); /* * Careful here: even though this tuple was created by our own * transaction, it might be locked by other transactions, if * the original version was key-share locked when we updated * it. */ if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { if (MultiXactHasRunningRemoteMembers(xmax)) return HeapTupleBeingUpdated; else return HeapTupleMayBeUpdated; } /* if locker is gone, all's well */ if (!TransactionIdIsInProgress(xmax)) return HeapTupleMayBeUpdated; if (!TransactionIdIsCurrentTransactionId(xmax)) return HeapTupleBeingUpdated; else return HeapTupleMayBeUpdated; } if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { TransactionId xmax; xmax = HeapTupleGetUpdateXid(tuple); /* not LOCKED_ONLY, so it has to have an xmax */ Assert(TransactionIdIsValid(xmax)); /* updating subtransaction must have aborted */ if (!TransactionIdIsCurrentTransactionId(xmax)) { if (MultiXactHasRunningRemoteMembers(HeapTupleHeaderGetRawXmax(tuple))) return HeapTupleBeingUpdated; return HeapTupleMayBeUpdated; } else { if (HeapTupleHeaderGetCmax(tuple) >= curcid) return HeapTupleSelfUpdated; /* updated after scan * started */ else return HeapTupleInvisible; /* updated before scan * started */ } } if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { /* deleting subtransaction must have aborted */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); return HeapTupleMayBeUpdated; } if (HeapTupleHeaderGetCmax(tuple) >= curcid) return HeapTupleSelfUpdated; /* updated after scan started */ else return HeapTupleInvisible; /* updated before scan started */ } else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple))) return HeapTupleInvisible; else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple))) SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, HeapTupleHeaderGetRawXmin(tuple)); else { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID, InvalidTransactionId); return HeapTupleInvisible; } } /* by here, the inserting transaction has committed */ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */ return HeapTupleMayBeUpdated; if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return HeapTupleMayBeUpdated; return HeapTupleUpdated; /* updated by other */ } if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { TransactionId xmax; 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); /* not LOCKED_ONLY, so it has to have an xmax */ Assert(TransactionIdIsValid(xmax)); if (TransactionIdIsCurrentTransactionId(xmax)) { if (HeapTupleHeaderGetCmax(tuple) >= curcid) return HeapTupleSelfUpdated; /* updated after scan started */ else return HeapTupleInvisible; /* updated before scan started */ } if (TransactionIdIsInProgress(xmax)) return HeapTupleBeingUpdated; if (TransactionIdDidCommit(xmax)) return HeapTupleUpdated; /* * By here, the update in the Xmax is either aborted or crashed, but * what about the other members? */ if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple))) { /* * There's no member, even just a locker, alive anymore, so we can * mark the Xmax as invalid. */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); return HeapTupleMayBeUpdated; } else { /* There are lockers running */ return HeapTupleBeingUpdated; } } if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return HeapTupleMayBeUpdated; if (HeapTupleHeaderGetCmax(tuple) >= curcid) return HeapTupleSelfUpdated; /* updated after scan started */ else return HeapTupleInvisible; /* updated before scan started */ } if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) return HeapTupleBeingUpdated; if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple))) { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); return HeapTupleMayBeUpdated; } /* xmax transaction committed */ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) { SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); return HeapTupleMayBeUpdated; } SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, HeapTupleHeaderGetRawXmax(tuple)); return HeapTupleUpdated; /* updated by other */ }
/* * See the comments for HeapTupleSatisfiesMVCC for the semantics this function * obeys. * * Only usable on tuples from catalog tables! * * We don't need to support HEAP_MOVED_(IN|OFF) for now because we only support * reading catalog pages which couldn't have been created in an older version. * * We don't set any hint bits in here as it seems unlikely to be beneficial as * those should already be set by normal access and it seems to be too * dangerous to do so as the semantics of doing so during timetravel are more * complicated than when dealing "only" with the present. */ bool HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot, Buffer buffer) { HeapTupleHeader tuple = htup->t_data; TransactionId xmin = HeapTupleHeaderGetXmin(tuple); TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple); Assert(ItemPointerIsValid(&htup->t_self)); Assert(htup->t_tableOid != InvalidOid); /* inserting transaction aborted */ if (HeapTupleHeaderXminInvalid(tuple)) { Assert(!TransactionIdDidCommit(xmin)); return false; } /* check if it's one of our txids, toplevel is also in there */ else if (TransactionIdInArray(xmin, snapshot->subxip, snapshot->subxcnt)) { bool resolved; CommandId cmin = HeapTupleHeaderGetRawCommandId(tuple); CommandId cmax = InvalidCommandId; /* * another transaction might have (tried to) delete this tuple or * cmin/cmax was stored in a combocid. So we need to lookup the actual * values externally. */ resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot, htup, buffer, &cmin, &cmax); if (!resolved) elog(ERROR, "could not resolve cmin/cmax of catalog tuple"); Assert(cmin != InvalidCommandId); if (cmin >= snapshot->curcid) return false; /* inserted after scan started */ /* fall through */ } /* committed before our xmin horizon. Do a normal visibility check. */ else if (TransactionIdPrecedes(xmin, snapshot->xmin)) { Assert(!(HeapTupleHeaderXminCommitted(tuple) && !TransactionIdDidCommit(xmin))); /* check for hint bit first, consult clog afterwards */ if (!HeapTupleHeaderXminCommitted(tuple) && !TransactionIdDidCommit(xmin)) return false; /* fall through */ } /* beyond our xmax horizon, i.e. invisible */ else if (TransactionIdFollowsOrEquals(xmin, snapshot->xmax)) { return false; } /* check if it's a committed transaction in [xmin, xmax) */ else if (TransactionIdInArray(xmin, snapshot->xip, snapshot->xcnt)) { /* fall through */ } /* * none of the above, i.e. between [xmin, xmax) but hasn't committed. I.e. * invisible. */ else { return false; } /* at this point we know xmin is visible, go on to check xmax */ /* xid invalid or aborted */ if (tuple->t_infomask & HEAP_XMAX_INVALID) return true; /* locked tuples are always visible */ else if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; /* * We can see multis here if we're looking at user tables or if somebody * SELECT ... FOR SHARE/UPDATE a system table. */ else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { xmax = HeapTupleGetUpdateXid(tuple); } /* check if it's one of our txids, toplevel is also in there */ if (TransactionIdInArray(xmax, snapshot->subxip, snapshot->subxcnt)) { bool resolved; CommandId cmin; CommandId cmax = HeapTupleHeaderGetRawCommandId(tuple); /* Lookup actual cmin/cmax values */ resolved = ResolveCminCmaxDuringDecoding(HistoricSnapshotGetTupleCids(), snapshot, htup, buffer, &cmin, &cmax); if (!resolved) elog(ERROR, "could not resolve combocid to cmax"); Assert(cmax != InvalidCommandId); if (cmax >= snapshot->curcid) return true; /* deleted after scan started */ else return false; /* deleted before scan started */ } /* below xmin horizon, normal transaction state is valid */ else if (TransactionIdPrecedes(xmax, snapshot->xmin)) { Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED && !TransactionIdDidCommit(xmax))); /* check hint bit first */ if (tuple->t_infomask & HEAP_XMAX_COMMITTED) return false; /* check clog */ return !TransactionIdDidCommit(xmax); } /* above xmax horizon, we cannot possibly see the deleting transaction */ else if (TransactionIdFollowsOrEquals(xmax, snapshot->xmax)) return true; /* xmax is between [xmin, xmax), check known committed array */ else if (TransactionIdInArray(xmax, snapshot->xip, snapshot->xcnt)) return false; /* xmax is between [xmin, xmax), but known not to have committed yet */ else return true; }
/* * HeapTupleSatisfiesVacuum * * Determine the status of tuples for VACUUM purposes. Here, what * we mainly want to know is if a tuple is potentially visible to *any* * running transaction. If so, it can't be removed yet by VACUUM. * * OldestXmin is a cutoff XID (obtained from GetOldestXmin()). Tuples * deleted by XIDs >= OldestXmin are deemed "recently dead"; they might * still be visible to some open transaction, so we can't remove them, * even if we see that the deleting transaction has committed. */ HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, Buffer buffer) { HeapTupleHeader tuple = htup->t_data; Assert(ItemPointerIsValid(&htup->t_self)); Assert(htup->t_tableOid != InvalidOid); /* * Has inserting transaction committed? * * If the inserting transaction aborted, then the tuple was never visible * to any other transaction, so we can delete it immediately. */ if (!HeapTupleHeaderXminCommitted(tuple)) { if (HeapTupleHeaderXminInvalid(tuple)) return HEAPTUPLE_DEAD; /* Used by pre-9.0 binary upgrades */ else if (tuple->t_infomask & HEAP_MOVED_OFF) { TransactionId xvac = HeapTupleHeaderGetXvac(tuple); if (TransactionIdIsCurrentTransactionId(xvac)) return HEAPTUPLE_DELETE_IN_PROGRESS; if (TransactionIdIsInProgress(xvac)) return HEAPTUPLE_DELETE_IN_PROGRESS; if (TransactionIdDidCommit(xvac)) { SetHintBits(tuple, buffer, HEAP_XMIN_INVALID, InvalidTransactionId); return HEAPTUPLE_DEAD; } SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, InvalidTransactionId); } /* Used by pre-9.0 binary upgrades */ else if (tuple->t_infomask & HEAP_MOVED_IN) { TransactionId xvac = HeapTupleHeaderGetXvac(tuple); if (TransactionIdIsCurrentTransactionId(xvac)) return HEAPTUPLE_INSERT_IN_PROGRESS; if (TransactionIdIsInProgress(xvac)) return HEAPTUPLE_INSERT_IN_PROGRESS; if (TransactionIdDidCommit(xvac)) SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, InvalidTransactionId); else { SetHintBits(tuple, buffer, HEAP_XMIN_INVALID, InvalidTransactionId); return HEAPTUPLE_DEAD; } } else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple))) { if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return HEAPTUPLE_INSERT_IN_PROGRESS; /* only locked? run infomask-only check first, for performance */ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) || HeapTupleHeaderIsOnlyLocked(tuple)) return HEAPTUPLE_INSERT_IN_PROGRESS; /* inserted and then deleted by same xact */ return HEAPTUPLE_DELETE_IN_PROGRESS; } else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple))) SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, HeapTupleHeaderGetRawXmin(tuple)); else { /* * Not in Progress, Not Committed, so either Aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID, InvalidTransactionId); return HEAPTUPLE_DEAD; } /* * At this point the xmin is known committed, but we might not have * been able to set the hint bit yet; so we can no longer Assert that * it's set. */ } /* * Okay, the inserter committed, so it was good at some point. Now what * about the deleting transaction? */ if (tuple->t_infomask & HEAP_XMAX_INVALID) return HEAPTUPLE_LIVE; if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) { /* * "Deleting" xact really only locked it, so the tuple is live in any * case. However, we should make sure that either XMAX_COMMITTED or * XMAX_INVALID gets set once the xact is gone, to reduce the costs of * examining the tuple for future xacts. Also, marking dead * MultiXacts as invalid here provides defense against MultiXactId * wraparound (see also comments in heap_freeze_tuple()). */ if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) { if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { /* * 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(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. */ return HEAPTUPLE_LIVE; } if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { TransactionId xmax; if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple))) { /* already checked above */ Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)); xmax = HeapTupleGetUpdateXid(tuple); /* not LOCKED_ONLY, so it has to have an xmax */ Assert(TransactionIdIsValid(xmax)); 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); /* not LOCKED_ONLY, so it has to have an xmax */ Assert(TransactionIdIsValid(xmax)); /* 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; } /* * Not in Progress, Not Committed, so either Aborted or crashed. * Remove the Xmax. */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); return HEAPTUPLE_LIVE; } if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) { if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) return HEAPTUPLE_DELETE_IN_PROGRESS; else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple))) SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, HeapTupleHeaderGetRawXmax(tuple)); else { /* * Not in Progress, Not Committed, so either Aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); return HEAPTUPLE_LIVE; } /* * At this point the xmax is known committed, but we might not have * been able to set the hint bit yet; so we can no longer Assert that * it's set. */ } /* * Deleter committed, but perhaps it was recent enough that some open * transactions could still see the tuple. */ if (!TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin)) return HEAPTUPLE_RECENTLY_DEAD; /* Otherwise, it's dead and removable */ return HEAPTUPLE_DEAD; }
Datum heap_page_items(PG_FUNCTION_ARGS) { bytea *raw_page = PG_GETARG_BYTEA_P(0); heap_page_items_state *inter_call_data = NULL; FuncCallContext *fctx; int raw_page_size; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use raw page functions")))); raw_page_size = VARSIZE(raw_page) - VARHDRSZ; if (SRF_IS_FIRSTCALL()) { TupleDesc tupdesc; MemoryContext mctx; if (raw_page_size < SizeOfPageHeaderData) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("input page too small (%d bytes)", raw_page_size))); fctx = SRF_FIRSTCALL_INIT(); mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); inter_call_data = palloc(sizeof(heap_page_items_state)); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); inter_call_data->tupd = tupdesc; inter_call_data->offset = FirstOffsetNumber; inter_call_data->page = VARDATA(raw_page); fctx->max_calls = PageGetMaxOffsetNumber(inter_call_data->page); fctx->user_fctx = inter_call_data; MemoryContextSwitchTo(mctx); } fctx = SRF_PERCALL_SETUP(); inter_call_data = fctx->user_fctx; if (fctx->call_cntr < fctx->max_calls) { Page page = inter_call_data->page; HeapTuple resultTuple; Datum result; ItemId id; Datum values[13]; bool nulls[13]; uint16 lp_offset; uint16 lp_flags; uint16 lp_len; memset(nulls, 0, sizeof(nulls)); /* Extract information from the line pointer */ id = PageGetItemId(page, inter_call_data->offset); lp_offset = ItemIdGetOffset(id); lp_flags = ItemIdGetFlags(id); lp_len = ItemIdGetLength(id); values[0] = UInt16GetDatum(inter_call_data->offset); values[1] = UInt16GetDatum(lp_offset); values[2] = UInt16GetDatum(lp_flags); values[3] = UInt16GetDatum(lp_len); /* * We do just enough validity checking to make sure we don't reference * data outside the page passed to us. The page could be corrupt in * many other ways, but at least we won't crash. */ if (ItemIdHasStorage(id) && lp_len >= sizeof(HeapTupleHeader) && lp_offset == MAXALIGN(lp_offset) && lp_offset + lp_len <= raw_page_size) { HeapTupleHeader tuphdr; int bits_len; /* Extract information from the tuple header */ tuphdr = (HeapTupleHeader) PageGetItem(page, id); values[4] = UInt32GetDatum(HeapTupleHeaderGetXmin(tuphdr)); values[5] = UInt32GetDatum(HeapTupleHeaderGetRawXmax(tuphdr)); values[6] = UInt32GetDatum(HeapTupleHeaderGetRawCommandId(tuphdr)); /* shared with xvac */ values[7] = PointerGetDatum(&tuphdr->t_ctid); values[8] = UInt32GetDatum(tuphdr->t_infomask2); values[9] = UInt32GetDatum(tuphdr->t_infomask); values[10] = UInt8GetDatum(tuphdr->t_hoff); /* * We already checked that the item as is completely within the * raw page passed to us, with the length given in the line * pointer.. Let's check that t_hoff doesn't point over lp_len, * before using it to access t_bits and oid. */ if (tuphdr->t_hoff >= sizeof(HeapTupleHeader) && tuphdr->t_hoff <= lp_len) { if (tuphdr->t_infomask & HEAP_HASNULL) { bits_len = tuphdr->t_hoff - (((char *) tuphdr->t_bits) -((char *) tuphdr)); values[11] = CStringGetTextDatum( bits_to_text(tuphdr->t_bits, bits_len * 8)); } else nulls[11] = true; if (tuphdr->t_infomask & HEAP_HASOID) values[12] = HeapTupleHeaderGetOid(tuphdr); else nulls[12] = true; } else { nulls[11] = true; nulls[12] = true; } } else { /* * The line pointer is not used, or it's invalid. Set the rest of * the fields to NULL */ int i; for (i = 4; i <= 12; i++) nulls[i] = true; } /* Build and return the result tuple. */ resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls); result = HeapTupleGetDatum(resultTuple); inter_call_data->offset++; SRF_RETURN_NEXT(fctx, result); } else SRF_RETURN_DONE(fctx); }
/* * HeapTupleSatisfiesUpdate * * Same logic as HeapTupleSatisfiesNow, but returns a more detailed result * code, since UPDATE needs to know more than "is it visible?". Also, * tuples of my own xact are tested against the passed CommandId not * CurrentCommandId. * * The possible return codes are: * * HeapTupleInvisible: the tuple didn't exist at all when the scan started, * e.g. it was created by a later CommandId. * * HeapTupleMayBeUpdated: The tuple is valid and visible, so it may be * updated. * * HeapTupleSelfUpdated: The tuple was updated by the current transaction, * after the current scan started. * * HeapTupleUpdated: The tuple was updated by a committed transaction. * * HeapTupleBeingUpdated: The tuple is being updated by an in-progress * transaction other than the current transaction. (Note: this includes * the case where the tuple is share-locked by a MultiXact, even if the * MultiXact includes the current transaction. Callers that want to * distinguish that case must test for it themselves.) */ HTSU_Result HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, Buffer buffer) { if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) { if (tuple->t_infomask & HEAP_XMIN_INVALID) return HeapTupleInvisible; /* Used by pre-9.0 binary upgrades */ if (tuple->t_infomask & HEAP_MOVED_OFF) { TransactionId xvac = HeapTupleHeaderGetXvac(tuple); if (TransactionIdIsCurrentTransactionId(xvac)) return HeapTupleInvisible; if (!TransactionIdIsInProgress(xvac)) { if (TransactionIdDidCommit(xvac)) { SetHintBits(tuple, buffer, HEAP_XMIN_INVALID, InvalidTransactionId); return HeapTupleInvisible; } SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, InvalidTransactionId); } } /* Used by pre-9.0 binary upgrades */ else if (tuple->t_infomask & HEAP_MOVED_IN) { TransactionId xvac = HeapTupleHeaderGetXvac(tuple); if (!TransactionIdIsCurrentTransactionId(xvac)) { if (TransactionIdIsInProgress(xvac)) return HeapTupleInvisible; if (TransactionIdDidCommit(xvac)) SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, InvalidTransactionId); else { SetHintBits(tuple, buffer, HEAP_XMIN_INVALID, InvalidTransactionId); return HeapTupleInvisible; } } } else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple))) { if (HeapTupleHeaderGetCmin(tuple) >= curcid) return HeapTupleInvisible; /* inserted after scan started */ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return HeapTupleMayBeUpdated; if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */ return HeapTupleMayBeUpdated; 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(HeapTupleHeaderGetRawXmax(tuple))) { /* deleting subtransaction must have aborted */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); return HeapTupleMayBeUpdated; } if (HeapTupleHeaderGetCmax(tuple) >= curcid) return HeapTupleSelfUpdated; /* updated after scan started */ else return HeapTupleInvisible; /* updated before scan started */ } else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple))) return HeapTupleInvisible; else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple))) SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, HeapTupleHeaderGetXmin(tuple)); else { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID, InvalidTransactionId); return HeapTupleInvisible; } } /* by here, the inserting transaction has committed */ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */ return HeapTupleMayBeUpdated; if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return HeapTupleMayBeUpdated; return HeapTupleUpdated; /* updated by other */ } if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { TransactionId xmax; 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; if (TransactionIdDidCommit(xmax)) return HeapTupleUpdated; /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); return HeapTupleMayBeUpdated; } if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return HeapTupleMayBeUpdated; if (HeapTupleHeaderGetCmax(tuple) >= curcid) return HeapTupleSelfUpdated; /* updated after scan started */ else return HeapTupleInvisible; /* updated before scan started */ } if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) return HeapTupleBeingUpdated; if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple))) { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); return HeapTupleMayBeUpdated; } /* xmax transaction committed */ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) { SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); return HeapTupleMayBeUpdated; } SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, HeapTupleHeaderGetRawXmax(tuple)); return HeapTupleUpdated; /* updated by other */ }
/* * HeapTupleSatisfiesNow * True iff heap tuple is valid "now". * * Here, we consider the effects of: * all committed transactions (as of the current instant) * previous commands of this transaction * * Note we do _not_ include changes made by the current command. This * solves the "Halloween problem" wherein an UPDATE might try to re-update * its own output tuples, http://en.wikipedia.org/wiki/Halloween_Problem. * * Note: * Assumes heap tuple is valid. * * The satisfaction of "now" requires the following: * * ((Xmin == my-transaction && inserted by the current transaction * Cmin < my-command && before this command, and * (Xmax is null || the row has not been deleted, or * (Xmax == my-transaction && it was deleted by the current transaction * Cmax >= my-command))) but not before this command, * || or * (Xmin is committed && the row was inserted by a committed transaction, and * (Xmax is null || the row has not been deleted, or * (Xmax == my-transaction && the row is being deleted by this transaction * Cmax >= my-command) || but it's not deleted "yet", or * (Xmax != my-transaction && the row was deleted by another transaction * Xmax is not committed)))) that has not been committed * */ bool HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) { if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) { if (tuple->t_infomask & HEAP_XMIN_INVALID) return false; /* Used by pre-9.0 binary upgrades */ if (tuple->t_infomask & HEAP_MOVED_OFF) { TransactionId xvac = HeapTupleHeaderGetXvac(tuple); if (TransactionIdIsCurrentTransactionId(xvac)) return false; if (!TransactionIdIsInProgress(xvac)) { if (TransactionIdDidCommit(xvac)) { SetHintBits(tuple, buffer, HEAP_XMIN_INVALID, InvalidTransactionId); return false; } SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, InvalidTransactionId); } } /* Used by pre-9.0 binary upgrades */ else if (tuple->t_infomask & HEAP_MOVED_IN) { TransactionId xvac = HeapTupleHeaderGetXvac(tuple); if (!TransactionIdIsCurrentTransactionId(xvac)) { if (TransactionIdIsInProgress(xvac)) return false; if (TransactionIdDidCommit(xvac)) SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, InvalidTransactionId); else { SetHintBits(tuple, buffer, HEAP_XMIN_INVALID, InvalidTransactionId); return false; } } } else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple))) { if (HeapTupleHeaderGetCmin(tuple) >= GetCurrentCommandId(false)) return false; /* inserted after scan started */ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return true; if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */ return true; 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(HeapTupleHeaderGetRawXmax(tuple))) { /* deleting subtransaction must have aborted */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); return true; } if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId(false)) return true; /* deleted after scan started */ else return false; /* deleted before scan started */ } else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple))) return false; else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple))) SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, HeapTupleHeaderGetXmin(tuple)); else { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMIN_INVALID, InvalidTransactionId); return false; } } /* by here, the inserting transaction has committed */ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */ return true; if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; return false; } if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { 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(HeapTupleHeaderGetRawXmax(tuple))) { if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId(false)) return true; /* deleted after scan started */ else return false; /* deleted before scan started */ } if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) return true; if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple))) { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); return true; } /* xmax transaction committed */ if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) { SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); return true; } SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, HeapTupleHeaderGetRawXmax(tuple)); return false; }
Datum pgrowlocks(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; HeapScanDesc scan; HeapTuple tuple; TupleDesc tupdesc; AttInMetadata *attinmeta; Datum result; MyData *mydata; Relation rel; if (SRF_IS_FIRSTCALL()) { text *relname; RangeVar *relrv; MemoryContext oldcontext; AclResult aclresult; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); attinmeta = TupleDescGetAttInMetadata(tupdesc); funcctx->attinmeta = attinmeta; relname = PG_GETARG_TEXT_PP(0); relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is a partitioned table", RelationGetRelationName(rel)), errdetail("Partitioned tables do not contain rows."))); else if (rel->rd_rel->relkind != RELKIND_RELATION) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table", RelationGetRelationName(rel)))); /* * check permissions: must have SELECT on table or be in * pg_stat_scan_tables */ aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), ACL_SELECT); if (aclresult != ACLCHECK_OK) aclresult = is_member_of_role(GetUserId(), DEFAULT_ROLE_STAT_SCAN_TABLES) ? ACLCHECK_OK : ACLCHECK_NO_PRIV; if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind), RelationGetRelationName(rel)); scan = heap_beginscan(rel, GetActiveSnapshot(), 0, NULL); mydata = palloc(sizeof(*mydata)); mydata->rel = rel; mydata->scan = scan; mydata->ncolumns = tupdesc->natts; funcctx->user_fctx = mydata; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); attinmeta = funcctx->attinmeta; mydata = (MyData *) funcctx->user_fctx; scan = mydata->scan; /* scan the relation */ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { HTSU_Result htsu; TransactionId xmax; uint16 infomask; /* must hold a buffer lock to call HeapTupleSatisfiesUpdate */ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE); htsu = HeapTupleSatisfiesUpdate(tuple, GetCurrentCommandId(false), scan->rs_cbuf); xmax = HeapTupleHeaderGetRawXmax(tuple->t_data); infomask = tuple->t_data->t_infomask; /* * A tuple is locked if HTSU returns BeingUpdated. */ if (htsu == HeapTupleBeingUpdated) { char **values; values = (char **) palloc(mydata->ncolumns * sizeof(char *)); values[Atnum_tid] = (char *) DirectFunctionCall1(tidout, PointerGetDatum(&tuple->t_self)); values[Atnum_xmax] = palloc(NCHARS * sizeof(char)); snprintf(values[Atnum_xmax], NCHARS, "%d", xmax); if (infomask & HEAP_XMAX_IS_MULTI) { MultiXactMember *members; int nmembers; bool first = true; bool allow_old; values[Atnum_ismulti] = pstrdup("true"); allow_old = HEAP_LOCKED_UPGRADED(infomask); nmembers = GetMultiXactIdMembers(xmax, &members, allow_old, false); if (nmembers == -1) { values[Atnum_xids] = "{0}"; values[Atnum_modes] = "{transient upgrade status}"; values[Atnum_pids] = "{0}"; } else { int j; values[Atnum_xids] = palloc(NCHARS * nmembers); values[Atnum_modes] = palloc(NCHARS * nmembers); values[Atnum_pids] = palloc(NCHARS * nmembers); strcpy(values[Atnum_xids], "{"); strcpy(values[Atnum_modes], "{"); strcpy(values[Atnum_pids], "{"); for (j = 0; j < nmembers; j++) { char buf[NCHARS]; if (!first) { strcat(values[Atnum_xids], ","); strcat(values[Atnum_modes], ","); strcat(values[Atnum_pids], ","); } snprintf(buf, NCHARS, "%d", members[j].xid); strcat(values[Atnum_xids], buf); switch (members[j].status) { case MultiXactStatusUpdate: snprintf(buf, NCHARS, "Update"); break; case MultiXactStatusNoKeyUpdate: snprintf(buf, NCHARS, "No Key Update"); break; case MultiXactStatusForUpdate: snprintf(buf, NCHARS, "For Update"); break; case MultiXactStatusForNoKeyUpdate: snprintf(buf, NCHARS, "For No Key Update"); break; case MultiXactStatusForShare: snprintf(buf, NCHARS, "Share"); break; case MultiXactStatusForKeyShare: snprintf(buf, NCHARS, "Key Share"); break; } strcat(values[Atnum_modes], buf); snprintf(buf, NCHARS, "%d", BackendXidGetPid(members[j].xid)); strcat(values[Atnum_pids], buf); first = false; } strcat(values[Atnum_xids], "}"); strcat(values[Atnum_modes], "}"); strcat(values[Atnum_pids], "}"); } } else { values[Atnum_ismulti] = pstrdup("false"); values[Atnum_xids] = palloc(NCHARS * sizeof(char)); snprintf(values[Atnum_xids], NCHARS, "{%d}", xmax); values[Atnum_modes] = palloc(NCHARS); if (infomask & HEAP_XMAX_LOCK_ONLY) { if (HEAP_XMAX_IS_SHR_LOCKED(infomask)) snprintf(values[Atnum_modes], NCHARS, "{For Share}"); else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask)) snprintf(values[Atnum_modes], NCHARS, "{For Key Share}"); else if (HEAP_XMAX_IS_EXCL_LOCKED(infomask)) { if (tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) snprintf(values[Atnum_modes], NCHARS, "{For Update}"); else snprintf(values[Atnum_modes], NCHARS, "{For No Key Update}"); } else /* neither keyshare nor exclusive bit it set */ snprintf(values[Atnum_modes], NCHARS, "{transient upgrade status}"); } else { if (tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) snprintf(values[Atnum_modes], NCHARS, "{Update}"); else snprintf(values[Atnum_modes], NCHARS, "{No Key Update}"); } values[Atnum_pids] = palloc(NCHARS * sizeof(char)); snprintf(values[Atnum_pids], NCHARS, "{%d}", BackendXidGetPid(xmax)); } LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); /* build a tuple */ tuple = BuildTupleFromCStrings(attinmeta, values); /* make the tuple into a datum */ result = HeapTupleGetDatum(tuple); /* * no need to pfree what we allocated; it's on a short-lived * memory context anyway */ SRF_RETURN_NEXT(funcctx, result); } else { LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); } } heap_endscan(scan); table_close(mydata->rel, AccessShareLock); SRF_RETURN_DONE(funcctx); }