/*------------------------------------------------------------------------- * * tqual.c * POSTGRES "time" qualification code. * * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/utils/time/tqual.c,v 1.36 2000/07/03 04:45:09 inoue Exp $ * *------------------------------------------------------------------------- */ /* #define TQUALDEBUG 1 */ #include "postgres.h" #include "utils/tqual.h" SnapshotData SnapshotDirtyData; Snapshot SnapshotDirty = &SnapshotDirtyData; Snapshot QuerySnapshot = NULL; Snapshot SerializableSnapshot = NULL; bool ReferentialIntegritySnapshotOverride = false; /* * HeapTupleSatisfiesItself * True iff heap tuple is valid for "itself." * "{it}self" means valid as of everything that's happened * in the current transaction, _including_ the current command. * * Note: * Assumes heap tuple is valid. */ /* * The satisfaction of "itself" requires the following: * * ((Xmin == my-transaction && the row was updated by the current transaction, and * (Xmax is null it was not deleted * [|| Xmax != my-transaction)]) [or it was deleted by another transaction] * || * * (Xmin is committed && the row was modified by a committed transaction, and * (Xmax is null || the row has not been deleted, or * (Xmax != my-transaction && the row was deleted by another transaction * Xmax is not committed))) that has not been committed */ bool HeapTupleSatisfiesItself(HeapTupleHeader tuple) { if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) { if (tuple->t_infomask & HEAP_XMIN_INVALID) return false; if (tuple->t_infomask & HEAP_MOVED_OFF) { if (TransactionIdDidCommit((TransactionId) tuple->t_cmin)) { tuple->t_infomask |= HEAP_XMIN_INVALID; return false; } } else if (tuple->t_infomask & HEAP_MOVED_IN) { if (!TransactionIdDidCommit((TransactionId) tuple->t_cmin)) { tuple->t_infomask |= HEAP_XMIN_INVALID; return false; } } else if (TransactionIdIsCurrentTransactionId(tuple->t_xmin)) { if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return true; if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; return false; } else if (!TransactionIdDidCommit(tuple->t_xmin)) { if (TransactionIdDidAbort(tuple->t_xmin)) tuple->t_infomask |= HEAP_XMIN_INVALID; /* aborted */ return false; } tuple->t_infomask |= HEAP_XMIN_COMMITTED; } /* the tuple was inserted validly */ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */ return true; if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; return false; /* updated by other */ } if (TransactionIdIsCurrentTransactionId(tuple->t_xmax)) { if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; return false; } if (!TransactionIdDidCommit(tuple->t_xmax)) { if (TransactionIdDidAbort(tuple->t_xmax)) tuple->t_infomask |= HEAP_XMAX_INVALID; /* aborted */ return true; } /* by here, deleting transaction has committed */ tuple->t_infomask |= HEAP_XMAX_COMMITTED; if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; return false; } /* * HeapTupleSatisfiesNow * True iff heap tuple is valid "now." * "now" means valid including everything that's happened * in the current transaction _up to, but not including,_ * the current command. * * Note: * Assumes heap tuple is valid. */ /* * The satisfaction of "now" requires the following: * * ((Xmin == my-transaction && changed by the current transaction * Cmin != my-command && but not by 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 by this command, * || or * * (Xmin is committed && the row was modified 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 command, or * Cmax == my-command) || * (Xmax is not committed && the row was deleted by another transaction * Xmax != my-transaction)))) that has not been committed * * mao says 17 march 1993: the tests in this routine are correct; * if you think they're not, you're wrong, and you should think * about it again. i know, it happened to me. we don't need to * check commit time against the start time of this transaction * because 2ph locking protects us from doing the wrong thing. * if you mess around here, you'll break serializability. the only * problem with this code is that it does the wrong thing for system * catalog updates, because the catalogs aren't subject to 2ph, so * the serializability guarantees we provide don't extend to xacts * that do catalog accesses. this is unfortunate, but not critical. */ bool HeapTupleSatisfiesNow(HeapTupleHeader tuple) { if (AMI_OVERRIDE) return true; if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) { if (tuple->t_infomask & HEAP_XMIN_INVALID) return false; if (tuple->t_infomask & HEAP_MOVED_OFF) { if (TransactionIdDidCommit((TransactionId) tuple->t_cmin)) { tuple->t_infomask |= HEAP_XMIN_INVALID; return false; } } else if (tuple->t_infomask & HEAP_MOVED_IN) { if (!TransactionIdDidCommit((TransactionId) tuple->t_cmin)) { tuple->t_infomask |= HEAP_XMIN_INVALID; return false; } } else if (TransactionIdIsCurrentTransactionId(tuple->t_xmin)) { if (CommandIdGEScanCommandId(tuple->t_cmin)) return false; /* inserted after scan started */ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return true; Assert(TransactionIdIsCurrentTransactionId(tuple->t_xmax)); if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; if (CommandIdGEScanCommandId(tuple->t_cmax)) return true; /* deleted after scan started */ else return false; /* deleted before scan started */ } else if (!TransactionIdDidCommit(tuple->t_xmin)) { if (TransactionIdDidAbort(tuple->t_xmin)) tuple->t_infomask |= HEAP_XMIN_INVALID; /* aborted */ return false; } tuple->t_infomask |= HEAP_XMIN_COMMITTED; } /* 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 (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; return false; } if (TransactionIdIsCurrentTransactionId(tuple->t_xmax)) { if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; if (CommandIdGEScanCommandId(tuple->t_cmax)) return true; /* deleted after scan started */ else return false; /* deleted before scan started */ } if (!TransactionIdDidCommit(tuple->t_xmax)) { if (TransactionIdDidAbort(tuple->t_xmax)) tuple->t_infomask |= HEAP_XMAX_INVALID; /* aborted */ return true; } /* xmax transaction committed */ tuple->t_infomask |= HEAP_XMAX_COMMITTED; if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; return false; } int HeapTupleSatisfiesUpdate(HeapTuple tuple) { HeapTupleHeader th = tuple->t_data; if (AMI_OVERRIDE) return HeapTupleMayBeUpdated; if (!(th->t_infomask & HEAP_XMIN_COMMITTED)) { if (th->t_infomask & HEAP_XMIN_INVALID) /* xid invalid or aborted */ return HeapTupleInvisible; if (th->t_infomask & HEAP_MOVED_OFF) { if (TransactionIdDidCommit((TransactionId) th->t_cmin)) { th->t_infomask |= HEAP_XMIN_INVALID; return HeapTupleInvisible; } } else if (th->t_infomask & HEAP_MOVED_IN) { if (!TransactionIdDidCommit((TransactionId) th->t_cmin)) { th->t_infomask |= HEAP_XMIN_INVALID; return HeapTupleInvisible; } } else if (TransactionIdIsCurrentTransactionId(th->t_xmin)) { if (CommandIdGEScanCommandId(th->t_cmin)) return HeapTupleInvisible; /* inserted after scan * started */ if (th->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return HeapTupleMayBeUpdated; Assert(TransactionIdIsCurrentTransactionId(th->t_xmax)); if (th->t_infomask & HEAP_MARKED_FOR_UPDATE) return HeapTupleMayBeUpdated; if (CommandIdGEScanCommandId(th->t_cmax)) return HeapTupleSelfUpdated; /* updated after scan * started */ else return HeapTupleInvisible; /* updated before scan * started */ } else if (!TransactionIdDidCommit(th->t_xmin)) { if (TransactionIdDidAbort(th->t_xmin)) th->t_infomask |= HEAP_XMIN_INVALID; /* aborted */ return HeapTupleInvisible; } th->t_infomask |= HEAP_XMIN_COMMITTED; } /* by here, the inserting transaction has committed */ if (th->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */ return HeapTupleMayBeUpdated; if (th->t_infomask & HEAP_XMAX_COMMITTED) { if (th->t_infomask & HEAP_MARKED_FOR_UPDATE) return HeapTupleMayBeUpdated; return HeapTupleUpdated;/* updated by other */ } if (TransactionIdIsCurrentTransactionId(th->t_xmax)) { if (th->t_infomask & HEAP_MARKED_FOR_UPDATE) return HeapTupleMayBeUpdated; if (CommandIdGEScanCommandId(th->t_cmax)) return HeapTupleSelfUpdated; /* updated after scan * started */ else return HeapTupleInvisible; /* updated before scan started */ } if (!TransactionIdDidCommit(th->t_xmax)) { if (TransactionIdDidAbort(th->t_xmax)) { th->t_infomask |= HEAP_XMAX_INVALID; /* aborted */ return HeapTupleMayBeUpdated; } /* running xact */ return HeapTupleBeingUpdated; /* in updation by other */ } /* xmax transaction committed */ th->t_infomask |= HEAP_XMAX_COMMITTED; if (th->t_infomask & HEAP_MARKED_FOR_UPDATE) return HeapTupleMayBeUpdated; return HeapTupleUpdated; /* updated by other */ } bool HeapTupleSatisfiesDirty(HeapTupleHeader tuple) { SnapshotDirty->xmin = SnapshotDirty->xmax = InvalidTransactionId; ItemPointerSetInvalid(&(SnapshotDirty->tid)); if (AMI_OVERRIDE) return true; if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) { if (tuple->t_infomask & HEAP_XMIN_INVALID) return false; if (tuple->t_infomask & HEAP_MOVED_OFF) { /* * HeapTupleSatisfiesDirty is used by unique btree-s and so * may be used while vacuuming. */ if (TransactionIdIsCurrentTransactionId((TransactionId) tuple->t_cmin)) return false; if (TransactionIdDidCommit((TransactionId) tuple->t_cmin)) { tuple->t_infomask |= HEAP_XMIN_INVALID; return false; } tuple->t_infomask |= HEAP_XMIN_COMMITTED; } else if (tuple->t_infomask & HEAP_MOVED_IN) { if (!TransactionIdIsCurrentTransactionId((TransactionId) tuple->t_cmin)) { if (TransactionIdDidCommit((TransactionId) tuple->t_cmin)) tuple->t_infomask |= HEAP_XMIN_COMMITTED; else { tuple->t_infomask |= HEAP_XMIN_INVALID; return false; } } } else if (TransactionIdIsCurrentTransactionId(tuple->t_xmin)) { if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return true; Assert(TransactionIdIsCurrentTransactionId(tuple->t_xmax)); if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; return false; } else if (!TransactionIdDidCommit(tuple->t_xmin)) { if (TransactionIdDidAbort(tuple->t_xmin)) { tuple->t_infomask |= HEAP_XMIN_INVALID; /* aborted */ return false; } SnapshotDirty->xmin = tuple->t_xmin; return true; /* in insertion by other */ } else tuple->t_infomask |= HEAP_XMIN_COMMITTED; } /* 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 (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; SnapshotDirty->tid = tuple->t_ctid; return false; /* updated by other */ } if (TransactionIdIsCurrentTransactionId(tuple->t_xmax)) { if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; return false; } if (!TransactionIdDidCommit(tuple->t_xmax)) { if (TransactionIdDidAbort(tuple->t_xmax)) { tuple->t_infomask |= HEAP_XMAX_INVALID; /* aborted */ return true; } /* running xact */ SnapshotDirty->xmax = tuple->t_xmax; return true; /* in updation by other */ } /* xmax transaction committed */ tuple->t_infomask |= HEAP_XMAX_COMMITTED; if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; SnapshotDirty->tid = tuple->t_ctid; return false; /* updated by other */ } bool HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot) { if (AMI_OVERRIDE) return true; if (ReferentialIntegritySnapshotOverride) return HeapTupleSatisfiesNow(tuple); if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) { if (tuple->t_infomask & HEAP_XMIN_INVALID) return false; if (tuple->t_infomask & HEAP_MOVED_OFF) { if (TransactionIdDidCommit((TransactionId) tuple->t_cmin)) { tuple->t_infomask |= HEAP_XMIN_INVALID; return false; } } else if (tuple->t_infomask & HEAP_MOVED_IN) { if (!TransactionIdDidCommit((TransactionId) tuple->t_cmin)) { tuple->t_infomask |= HEAP_XMIN_INVALID; return false; } } else if (TransactionIdIsCurrentTransactionId(tuple->t_xmin)) { if (CommandIdGEScanCommandId(tuple->t_cmin)) return false; /* inserted after scan started */ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return true; Assert(TransactionIdIsCurrentTransactionId(tuple->t_xmax)); if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; if (CommandIdGEScanCommandId(tuple->t_cmax)) return true; /* deleted after scan started */ else return false; /* deleted before scan started */ } else if (!TransactionIdDidCommit(tuple->t_xmin)) { if (TransactionIdDidAbort(tuple->t_xmin)) tuple->t_infomask |= HEAP_XMIN_INVALID; /* aborted */ return false; } tuple->t_infomask |= HEAP_XMIN_COMMITTED; } /* * By here, the inserting transaction has committed - have to check * when... */ if (tuple->t_xmin >= snapshot->xmax) return false; if (tuple->t_xmin >= snapshot->xmin) { uint32 i; for (i = 0; i < snapshot->xcnt; i++) { if (tuple->t_xmin == snapshot->xip[i]) return false; } } if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */ return true; if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return true; if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) { if (TransactionIdIsCurrentTransactionId(tuple->t_xmax)) { if (CommandIdGEScanCommandId(tuple->t_cmax)) return true; /* deleted after scan started */ else return false; /* deleted before scan started */ } if (!TransactionIdDidCommit(tuple->t_xmax)) { if (TransactionIdDidAbort(tuple->t_xmax)) tuple->t_infomask |= HEAP_XMAX_INVALID; /* aborted */ return true; } /* xmax transaction committed */ tuple->t_infomask |= HEAP_XMAX_COMMITTED; } if (tuple->t_xmax >= snapshot->xmax) return true; if (tuple->t_xmax >= snapshot->xmin) { uint32 i; for (i = 0; i < snapshot->xcnt; i++) { if (tuple->t_xmax == snapshot->xip[i]) return true; } } return false; } void SetQuerySnapshot(void) { /* Initialize snapshot overriding to false */ ReferentialIntegritySnapshotOverride = false; /* 1st call in xaction */ if (SerializableSnapshot == NULL) { SerializableSnapshot = GetSnapshotData(true); QuerySnapshot = SerializableSnapshot; Assert(QuerySnapshot != NULL); return; } if (QuerySnapshot != SerializableSnapshot) { free(QuerySnapshot->xip); free(QuerySnapshot); } if (XactIsoLevel == XACT_SERIALIZABLE) QuerySnapshot = SerializableSnapshot; else QuerySnapshot = GetSnapshotData(false); Assert(QuerySnapshot != NULL); } void FreeXactSnapshot(void) { if (QuerySnapshot != NULL && QuerySnapshot != SerializableSnapshot) { free(QuerySnapshot->xip); free(QuerySnapshot); } QuerySnapshot = NULL; if (SerializableSnapshot != NULL) { free(SerializableSnapshot->xip); free(SerializableSnapshot); } SerializableSnapshot = NULL; }