📄 tqual.c
字号:
/*------------------------------------------------------------------------- * * tqual.c * POSTGRES "time" qualification code, ie, tuple visibility rules. * * The caller must hold at least a shared buffer context lock on the buffer * containing the tuple. (VACUUM FULL assumes it's sufficient to have * exclusive lock on the containing relation, instead.) * * NOTE: all the HeapTupleSatisfies routines will update the tuple's * "hint" status bits if we see that the inserting or deleting transaction * has now committed or aborted. * * NOTE: must check TransactionIdIsInProgress (which looks in PGPROC array) * before TransactionIdDidCommit/TransactionIdDidAbort (which look in * pg_clog). Otherwise we have a race condition: we might decide that a * just-committed transaction crashed, because none of the tests succeed. * xact.c is careful to record commit/abort in pg_clog before it unsets * MyProc->xid in PGPROC array. That fixes that problem, but it also * means there is a window where TransactionIdIsInProgress and * TransactionIdDidCommit will both return true. If we check only * TransactionIdDidCommit, we could consider a tuple committed when a * later GetSnapshotData call will still think the originating transaction * is in progress, which leads to application-level inconsistency. The * upshot is that we gotta check TransactionIdIsInProgress first in all * code paths, except for a few cases where we are looking at * subtransactions of our own main transaction and so there can't be any * race condition. * * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.91.2.1 2005/11/22 18:23:25 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/multixact.h"#include "access/subtrans.h"#include "storage/procarray.h"#include "utils/tqual.h"/* * These SnapshotData structs are static to simplify memory allocation * (see the hack in GetSnapshotData to avoid repeated malloc/free). */static SnapshotData SnapshotDirtyData;static SnapshotData SerializableSnapshotData;static SnapshotData LatestSnapshotData;/* Externally visible pointers to valid snapshots: */Snapshot SnapshotDirty = &SnapshotDirtyData;Snapshot SerializableSnapshot = NULL;Snapshot LatestSnapshot = NULL;/* * This pointer is not maintained by this module, but it's convenient * to declare it here anyway. Callers typically assign a copy of * GetTransactionSnapshot's result to ActiveSnapshot. */Snapshot ActiveSnapshot = NULL;/* These are updated by GetSnapshotData: */TransactionId TransactionXmin = InvalidTransactionId;TransactionId RecentXmin = InvalidTransactionId;TransactionId RecentGlobalXmin = InvalidTransactionId;/* * HeapTupleSatisfiesItself * True iff heap tuple is valid "for itself". * * Here, we consider the effects of: * all committed transactions (as of the current instant) * previous commands of this transaction * changes made by 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 */boolHeapTupleSatisfiesItself(HeapTupleHeader tuple, Buffer buffer){ if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) { if (tuple->t_infomask & HEAP_XMIN_INVALID) return false; if (tuple->t_infomask & HEAP_MOVED_OFF) { TransactionId xvac = HeapTupleHeaderGetXvac(tuple); if (TransactionIdIsCurrentTransactionId(xvac)) return false; if (!TransactionIdIsInProgress(xvac)) { if (TransactionIdDidCommit(xvac)) { tuple->t_infomask |= HEAP_XMIN_INVALID; SetBufferCommitInfoNeedsSave(buffer); return false; } tuple->t_infomask |= HEAP_XMIN_COMMITTED; SetBufferCommitInfoNeedsSave(buffer); } } else if (tuple->t_infomask & HEAP_MOVED_IN) { TransactionId xvac = HeapTupleHeaderGetXvac(tuple); if (!TransactionIdIsCurrentTransactionId(xvac)) { if (TransactionIdIsInProgress(xvac)) return false; if (TransactionIdDidCommit(xvac)) { tuple->t_infomask |= HEAP_XMIN_COMMITTED; SetBufferCommitInfoNeedsSave(buffer); } else { tuple->t_infomask |= HEAP_XMIN_INVALID; SetBufferCommitInfoNeedsSave(buffer); return false; } } } else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple))) { if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return true; if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */ return true; Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)); /* deleting subtransaction aborted? */ if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple))) { tuple->t_infomask |= HEAP_XMAX_INVALID; SetBufferCommitInfoNeedsSave(buffer); return true; } Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))); return false; } else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple))) return false; else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple))) { tuple->t_infomask |= HEAP_XMIN_COMMITTED; SetBufferCommitInfoNeedsSave(buffer); } else { /* it must have aborted or crashed */ tuple->t_infomask |= HEAP_XMIN_INVALID; SetBufferCommitInfoNeedsSave(buffer); 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 (tuple->t_infomask & HEAP_IS_LOCKED) 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); return true; } if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) { if (tuple->t_infomask & HEAP_IS_LOCKED) return true; return false; } if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple))) return true; if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) { /* it must have aborted or crashed */ tuple->t_infomask |= HEAP_XMAX_INVALID; SetBufferCommitInfoNeedsSave(buffer); return true; } /* xmax transaction committed */ if (tuple->t_infomask & HEAP_IS_LOCKED) { tuple->t_infomask |= HEAP_XMAX_INVALID; SetBufferCommitInfoNeedsSave(buffer); return true; } tuple->t_infomask |= HEAP_XMAX_COMMITTED; SetBufferCommitInfoNeedsSave(buffer); return false;}/* * 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. * * 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. */boolHeapTupleSatisfiesNow(HeapTupleHeader tuple, Buffer buffer){ if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) { if (tuple->t_infomask & HEAP_XMIN_INVALID) return false; if (tuple->t_infomask & HEAP_MOVED_OFF) { TransactionId xvac = HeapTupleHeaderGetXvac(tuple); if (TransactionIdIsCurrentTransactionId(xvac)) return false; if (!TransactionIdIsInProgress(xvac)) { if (TransactionIdDidCommit(xvac)) { tuple->t_infomask |= HEAP_XMIN_INVALID; SetBufferCommitInfoNeedsSave(buffer); return false; } tuple->t_infomask |= HEAP_XMIN_COMMITTED; SetBufferCommitInfoNeedsSave(buffer); } } else if (tuple->t_infomask & HEAP_MOVED_IN) { TransactionId xvac = HeapTupleHeaderGetXvac(tuple); if (!TransactionIdIsCurrentTransactionId(xvac)) { if (TransactionIdIsInProgress(xvac)) return false; if (TransactionIdDidCommit(xvac)) { tuple->t_infomask |= HEAP_XMIN_COMMITTED; SetBufferCommitInfoNeedsSave(buffer); } else { tuple->t_infomask |= HEAP_XMIN_INVALID; SetBufferCommitInfoNeedsSave(buffer); return false; } } } else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple))) { if (HeapTupleHeaderGetCmin(tuple) >= GetCurrentCommandId()) return false; /* inserted after scan started */ if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return true; if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */ return true; Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)); /* deleting subtransaction aborted? */ if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple))) { tuple->t_infomask |= HEAP_XMAX_INVALID; SetBufferCommitInfoNeedsSave(buffer); return true; } Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))); if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId()) 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))) { tuple->t_infomask |= HEAP_XMIN_COMMITTED; SetBufferCommitInfoNeedsSave(buffer); } else { /* it must have aborted or crashed */ tuple->t_infomask |= HEAP_XMIN_INVALID; SetBufferCommitInfoNeedsSave(buffer); 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 (tuple->t_infomask & HEAP_IS_LOCKED) 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); return true; } if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) { if (tuple->t_infomask & HEAP_IS_LOCKED) return true; if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId()) return true; /* deleted after scan started */ else return false; /* deleted before scan started */ } if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple))) return true; if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) { /* it must have aborted or crashed */ tuple->t_infomask |= HEAP_XMAX_INVALID; SetBufferCommitInfoNeedsSave(buffer); return true; } /* xmax transaction committed */ if (tuple->t_infomask & HEAP_IS_LOCKED) { tuple->t_infomask |= HEAP_XMAX_INVALID; SetBufferCommitInfoNeedsSave(buffer); return true; } tuple->t_infomask |= HEAP_XMAX_COMMITTED; SetBufferCommitInfoNeedsSave(buffer); return false;}/* * HeapTupleSatisfiesToast * True iff heap tuple is valid as a TOAST row. * * This is a simplified version that only checks for VACUUM moving conditions. * It's appropriate for TOAST usage because TOAST really doesn't want to do * its own time qual checks; if you can see the main table row that contains * a TOAST reference, you should be able to see the TOASTed value. However, * vacuuming a TOAST table is independent of the main table, and in case such * a vacuum fails partway through, we'd better do this much checking. * * Among other things, this means you can't do UPDATEs of rows in a TOAST * table. */boolHeapTupleSatisfiesToast(HeapTupleHeader tuple, Buffer buffer){ if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) { if (tuple->t_infomask & HEAP_XMIN_INVALID) return false; if (tuple->t_infomask & HEAP_MOVED_OFF) { TransactionId xvac = HeapTupleHeaderGetXvac(tuple); if (TransactionIdIsCurrentTransactionId(xvac)) return false; if (!TransactionIdIsInProgress(xvac)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -