1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* tqual.c
|
2007-03-25 21:45:14 +02:00
|
|
|
* POSTGRES "time qualification" code, ie, tuple visibility rules.
|
2002-01-16 21:29:02 +01:00
|
|
|
*
|
2004-10-16 00:40:29 +02:00
|
|
|
* NOTE: all the HeapTupleSatisfies routines will update the tuple's
|
|
|
|
* "hint" status bits if we see that the inserting or deleting transaction
|
2007-08-14 19:35:18 +02:00
|
|
|
* has now committed or aborted (and it is safe to set the hint bits).
|
|
|
|
* If the hint bits are changed, SetBufferCommitInfoNeedsSave is called on
|
|
|
|
* the passed-in buffer. The caller must hold not only a pin, but at least
|
|
|
|
* shared buffer content lock on the buffer containing the tuple.
|
2004-10-16 00:40:29 +02:00
|
|
|
*
|
2005-05-07 23:22:01 +02:00
|
|
|
* 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
|
2005-10-15 04:49:52 +02:00
|
|
|
* is in progress, which leads to application-level inconsistency. The
|
2005-05-07 23:22:01 +02:00
|
|
|
* 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.
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2007-01-05 23:20:05 +01:00
|
|
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2007-11-15 22:14:46 +01:00
|
|
|
* $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.107 2007/11/15 21:14:41 momjian Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2005-04-28 23:47:18 +02:00
|
|
|
#include "access/multixact.h"
|
2004-07-01 02:52:04 +02:00
|
|
|
#include "access/subtrans.h"
|
2006-07-13 18:49:20 +02:00
|
|
|
#include "access/transam.h"
|
|
|
|
#include "access/xact.h"
|
2006-07-13 19:47:02 +02:00
|
|
|
#include "storage/bufmgr.h"
|
2005-05-19 23:35:48 +02:00
|
|
|
#include "storage/procarray.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "utils/tqual.h"
|
|
|
|
|
2007-03-25 21:45:14 +02:00
|
|
|
|
|
|
|
/* Static variables representing various special snapshot semantics */
|
|
|
|
SnapshotData SnapshotNowData = {HeapTupleSatisfiesNow};
|
|
|
|
SnapshotData SnapshotSelfData = {HeapTupleSatisfiesSelf};
|
|
|
|
SnapshotData SnapshotAnyData = {HeapTupleSatisfiesAny};
|
|
|
|
SnapshotData SnapshotToastData = {HeapTupleSatisfiesToast};
|
|
|
|
|
2003-10-01 23:30:53 +02:00
|
|
|
/*
|
2004-09-13 22:10:13 +02:00
|
|
|
* These SnapshotData structs are static to simplify memory allocation
|
2003-10-01 23:30:53 +02:00
|
|
|
* (see the hack in GetSnapshotData to avoid repeated malloc/free).
|
|
|
|
*/
|
2007-03-25 21:45:14 +02:00
|
|
|
static SnapshotData SerializableSnapshotData = {HeapTupleSatisfiesMVCC};
|
|
|
|
static SnapshotData LatestSnapshotData = {HeapTupleSatisfiesMVCC};
|
2003-10-01 23:30:53 +02:00
|
|
|
|
|
|
|
/* Externally visible pointers to valid snapshots: */
|
2004-09-13 22:10:13 +02:00
|
|
|
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;
|
1998-12-16 12:53:55 +01:00
|
|
|
|
2007-09-08 22:31:15 +02:00
|
|
|
/*
|
|
|
|
* These are updated by GetSnapshotData. We initialize them this way
|
|
|
|
* for the convenience of TransactionIdIsInProgress: even in bootstrap
|
|
|
|
* mode, we don't want it to say that BootstrapTransactionId is in progress.
|
|
|
|
*/
|
|
|
|
TransactionId TransactionXmin = FirstNormalTransactionId;
|
|
|
|
TransactionId RecentXmin = FirstNormalTransactionId;
|
|
|
|
TransactionId RecentGlobalXmin = FirstNormalTransactionId;
|
2002-05-24 20:57:57 +02:00
|
|
|
|
2006-09-03 17:59:39 +02:00
|
|
|
/* local functions */
|
2007-03-25 21:45:14 +02:00
|
|
|
static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
|
2006-09-03 17:59:39 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2007-08-02 00:45:09 +02:00
|
|
|
/*
|
2007-08-14 19:35:18 +02:00
|
|
|
* SetHintBits()
|
2007-08-02 00:45:09 +02:00
|
|
|
*
|
|
|
|
* Set commit/abort hint bits on a tuple, if appropriate at this time.
|
|
|
|
*
|
2007-08-14 19:35:18 +02:00
|
|
|
* It is only safe to set a transaction-committed hint bit if we know the
|
|
|
|
* transaction's commit record has been flushed to disk. We cannot change
|
|
|
|
* the LSN of the page here because we may hold only a share lock on the
|
|
|
|
* buffer, so we can't use the LSN to interlock this; we have to just refrain
|
|
|
|
* from setting the hint bit until some future re-examination of the tuple.
|
2007-08-02 00:45:09 +02:00
|
|
|
*
|
2007-11-15 22:14:46 +01:00
|
|
|
* We can always set hint bits when marking a transaction aborted. (Some
|
2007-08-14 19:35:18 +02:00
|
|
|
* code in heapam.c relies on that!)
|
|
|
|
*
|
|
|
|
* Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
|
2007-08-02 00:45:09 +02:00
|
|
|
* we can always set the hint bits, since VACUUM FULL always uses synchronous
|
2007-11-15 22:14:46 +01:00
|
|
|
* commits and doesn't move tuples that weren't previously hinted. (This is
|
2007-08-14 19:35:18 +02:00
|
|
|
* not known by this subroutine, but is applied by its callers.)
|
2007-08-02 00:45:09 +02:00
|
|
|
*
|
|
|
|
* Normal commits may be asynchronous, so for those we need to get the LSN
|
|
|
|
* of the transaction and then check whether this is flushed.
|
|
|
|
*
|
|
|
|
* The caller should pass xid as the XID of the transaction to check, or
|
|
|
|
* InvalidTransactionId if no check is needed.
|
|
|
|
*/
|
|
|
|
static inline void
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(HeapTupleHeader tuple, Buffer buffer,
|
|
|
|
uint16 infomask, TransactionId xid)
|
2007-08-02 00:45:09 +02:00
|
|
|
{
|
|
|
|
if (TransactionIdIsValid(xid))
|
|
|
|
{
|
|
|
|
/* NB: xid must be known committed here! */
|
2007-11-15 22:14:46 +01:00
|
|
|
XLogRecPtr commitLSN = TransactionIdGetCommitLSN(xid);
|
2007-08-02 00:45:09 +02:00
|
|
|
|
|
|
|
if (XLogNeedsFlush(commitLSN))
|
|
|
|
return; /* not flushed yet, so don't set hint */
|
|
|
|
}
|
|
|
|
|
|
|
|
tuple->t_infomask |= infomask;
|
|
|
|
SetBufferCommitInfoNeedsSave(buffer);
|
|
|
|
}
|
|
|
|
|
2007-08-14 19:35:18 +02:00
|
|
|
/*
|
|
|
|
* HeapTupleSetHintBits --- exported version of SetHintBits()
|
|
|
|
*
|
|
|
|
* This must be separate because of C99's brain-dead notions about how to
|
|
|
|
* implement inline functions.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
|
|
|
|
uint16 infomask, TransactionId xid)
|
|
|
|
{
|
|
|
|
SetHintBits(tuple, buffer, infomask, xid);
|
|
|
|
}
|
|
|
|
|
2007-08-02 00:45:09 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
2007-03-25 21:45:14 +02:00
|
|
|
* HeapTupleSatisfiesSelf
|
2002-01-17 00:51:56 +01:00
|
|
|
* True iff heap tuple is valid "for itself".
|
2002-01-17 00:09:09 +01:00
|
|
|
*
|
2002-01-17 00:51:56 +01:00
|
|
|
* Here, we consider the effects of:
|
|
|
|
* all committed transactions (as of the current instant)
|
2002-01-17 00:09:09 +01:00
|
|
|
* previous commands of this transaction
|
|
|
|
* changes made by the current command
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* Note:
|
1997-09-07 07:04:48 +02:00
|
|
|
* Assumes heap tuple is valid.
|
2002-01-16 21:29:02 +01:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* The satisfaction of "itself" requires the following:
|
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* ((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]
|
1996-07-09 08:22:35 +02:00
|
|
|
* ||
|
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* (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
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1998-04-24 16:43:33 +02:00
|
|
|
bool
|
2007-03-25 21:45:14 +02:00
|
|
|
HeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-11-02 16:27:14 +01:00
|
|
|
if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-03-28 22:32:42 +02:00
|
|
|
if (tuple->t_infomask & HEAP_XMIN_INVALID)
|
1998-09-01 05:29:17 +02:00
|
|
|
return false;
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1999-03-28 22:32:42 +02:00
|
|
|
if (tuple->t_infomask & HEAP_MOVED_OFF)
|
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdIsCurrentTransactionId(xvac))
|
1999-03-28 22:32:42 +02:00
|
|
|
return false;
|
2003-06-25 03:08:13 +02:00
|
|
|
if (!TransactionIdIsInProgress(xvac))
|
2001-12-19 18:18:39 +01:00
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdDidCommit(xvac))
|
2001-12-19 18:18:39 +01:00
|
|
|
{
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
2001-12-19 18:18:39 +01:00
|
|
|
return false;
|
|
|
|
}
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
InvalidTransactionId);
|
1999-03-28 22:32:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tuple->t_infomask & HEAP_MOVED_IN)
|
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-06-25 03:08:13 +02:00
|
|
|
if (!TransactionIdIsCurrentTransactionId(xvac))
|
1999-03-28 22:32:42 +02:00
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdIsInProgress(xvac))
|
2001-12-19 18:18:39 +01:00
|
|
|
return false;
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdDidCommit(xvac))
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
InvalidTransactionId);
|
2001-12-19 18:18:39 +01:00
|
|
|
else
|
|
|
|
{
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
2001-12-19 18:18:39 +01:00
|
|
|
return false;
|
|
|
|
}
|
1999-03-28 22:32:42 +02:00
|
|
|
}
|
|
|
|
}
|
2002-06-15 21:54:24 +02:00
|
|
|
else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1997-11-02 16:27:14 +01:00
|
|
|
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
|
1998-09-01 05:29:17 +02:00
|
|
|
return true;
|
2001-12-19 18:18:39 +01:00
|
|
|
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */
|
|
|
|
return true;
|
|
|
|
|
|
|
|
Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));
|
|
|
|
|
2007-09-21 20:24:28 +02:00
|
|
|
if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
|
2004-07-28 16:23:31 +02:00
|
|
|
{
|
2007-09-21 20:24:28 +02:00
|
|
|
/* deleting subtransaction must have aborted */
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
|
|
|
|
InvalidTransactionId);
|
2004-07-01 02:52:04 +02:00
|
|
|
return true;
|
2004-07-28 16:23:31 +02:00
|
|
|
}
|
2004-07-01 02:52:04 +02:00
|
|
|
|
1998-12-15 13:47:01 +01:00
|
|
|
return false;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2005-05-07 23:22:01 +02:00
|
|
|
else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
|
1998-09-01 05:29:17 +02:00
|
|
|
return false;
|
2005-05-07 23:22:01 +02:00
|
|
|
else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
HeapTupleHeaderGetXmin(tuple));
|
2001-12-19 18:18:39 +01:00
|
|
|
else
|
2004-10-16 00:40:29 +02:00
|
|
|
{
|
2005-05-07 23:22:01 +02:00
|
|
|
/* it must have aborted or crashed */
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
2005-05-07 23:22:01 +02:00
|
|
|
return false;
|
2004-10-16 00:40:29 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2001-12-19 18:18:39 +01:00
|
|
|
|
|
|
|
/* by here, the inserting transaction has committed */
|
From: Dan McGuirk <mcguirk@indirect.com>
Reply-To: hackers@hub.org, Dan McGuirk <mcguirk@indirect.com>
To: hackers@hub.org
Subject: [HACKERS] tmin writeback optimization
I was doing some profiling of the backend, and noticed that during a certain
benchmark I was running somewhere between 30% and 75% of the backend's CPU
time was being spent in calls to TransactionIdDidCommit() from
HeapTupleSatisfiesNow() or HeapTupleSatisfiesItself() to determine that
changed rows' transactions had in fact been committed even though the rows'
tmin values had not yet been set.
When a query looks at a given row, it needs to figure out whether the
transaction that changed the row has been committed and hence it should pay
attention to the row, or whether on the other hand the transaction is still
in progress or has been aborted and hence the row should be ignored. If
a tmin value is set, it is known definitively that the row's transaction
has been committed. However, if tmin is not set, the transaction
referred to in xmin must be looked up in pg_log, and this is what the
backend was spending a lot of time doing during my benchmark.
So, implementing a method suggested by Vadim, I created the following
patch that, the first time a query finds a committed row whose tmin value
is not set, sets it, and marks the buffer where the row is stored as
dirty. (It works for tmax, too.) This doesn't result in the boost in
real time performance I was hoping for, however it does decrease backend
CPU usage by up to two-thirds in certain situations, so it could be
rather beneficial in high-concurrency settings.
1997-03-28 08:06:53 +01:00
|
|
|
|
1997-11-02 16:27:14 +01:00
|
|
|
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
|
1998-09-01 05:29:17 +02:00
|
|
|
return true;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-11-02 16:27:14 +01:00
|
|
|
if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
|
1998-12-15 13:47:01 +01:00
|
|
|
{
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
1998-12-15 13:47:01 +01:00
|
|
|
return true;
|
1999-05-25 18:15:34 +02:00
|
|
|
return false; /* updated by other */
|
1998-12-15 13:47:01 +01:00
|
|
|
}
|
1997-11-02 16:27:14 +01:00
|
|
|
|
2005-04-28 23:47:18 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2002-06-15 21:54:24 +02:00
|
|
|
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
|
1998-12-15 13:47:01 +01:00
|
|
|
{
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
1998-12-15 13:47:01 +01:00
|
|
|
return true;
|
1998-09-01 05:29:17 +02:00
|
|
|
return false;
|
1998-12-15 13:47:01 +01:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-05-07 23:22:01 +02:00
|
|
|
if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
|
|
|
|
return true;
|
|
|
|
|
2002-06-15 21:54:24 +02:00
|
|
|
if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2005-05-07 23:22:01 +02:00
|
|
|
/* it must have aborted or crashed */
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
|
|
|
|
InvalidTransactionId);
|
1998-09-01 05:29:17 +02:00
|
|
|
return true;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2001-12-19 18:18:39 +01:00
|
|
|
/* xmax transaction committed */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
2003-06-25 03:08:13 +02:00
|
|
|
{
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
|
|
|
|
InvalidTransactionId);
|
1998-12-15 13:47:01 +01:00
|
|
|
return true;
|
2003-06-25 03:08:13 +02:00
|
|
|
}
|
1998-12-15 13:47:01 +01:00
|
|
|
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
|
|
|
|
HeapTupleHeaderGetXmax(tuple));
|
1998-09-01 05:29:17 +02:00
|
|
|
return false;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1999-05-25 18:15:34 +02:00
|
|
|
* HeapTupleSatisfiesNow
|
2002-01-17 00:51:56 +01:00
|
|
|
* True iff heap tuple is valid "now".
|
2002-01-17 00:09:09 +01:00
|
|
|
*
|
2002-01-17 00:51:56 +01:00
|
|
|
* Here, we consider the effects of:
|
|
|
|
* all committed transactions (as of the current instant)
|
2002-01-17 00:09:09 +01:00
|
|
|
* previous commands of this transaction
|
|
|
|
*
|
2002-01-17 00:51:56 +01:00
|
|
|
* 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.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* Note:
|
1997-09-07 07:04:48 +02:00
|
|
|
* Assumes heap tuple is valid.
|
2002-01-16 21:29:02 +01:00
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* The satisfaction of "now" requires the following:
|
|
|
|
*
|
2006-09-15 18:39:32 +02:00
|
|
|
* ((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,
|
1997-09-07 07:04:48 +02:00
|
|
|
* || or
|
2006-09-15 18:39:32 +02:00
|
|
|
* (Xmin is committed && the row was inserted by a committed transaction, and
|
1997-09-07 07:04:48 +02:00
|
|
|
* (Xmax is null || the row has not been deleted, or
|
2006-09-15 18:39:32 +02:00
|
|
|
* (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
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* 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.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1998-04-24 16:43:33 +02:00
|
|
|
bool
|
2007-03-25 21:45:14 +02:00
|
|
|
HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-11-02 16:27:14 +01:00
|
|
|
if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-03-28 22:32:42 +02:00
|
|
|
if (tuple->t_infomask & HEAP_XMIN_INVALID)
|
1998-09-01 05:29:17 +02:00
|
|
|
return false;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-03-28 22:32:42 +02:00
|
|
|
if (tuple->t_infomask & HEAP_MOVED_OFF)
|
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdIsCurrentTransactionId(xvac))
|
1999-03-28 22:32:42 +02:00
|
|
|
return false;
|
2003-06-25 03:08:13 +02:00
|
|
|
if (!TransactionIdIsInProgress(xvac))
|
2001-12-19 18:18:39 +01:00
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdDidCommit(xvac))
|
2001-12-19 18:18:39 +01:00
|
|
|
{
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
2001-12-19 18:18:39 +01:00
|
|
|
return false;
|
|
|
|
}
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
InvalidTransactionId);
|
1999-03-28 22:32:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tuple->t_infomask & HEAP_MOVED_IN)
|
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-06-25 03:08:13 +02:00
|
|
|
if (!TransactionIdIsCurrentTransactionId(xvac))
|
1999-03-28 22:32:42 +02:00
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdIsInProgress(xvac))
|
2001-12-19 18:18:39 +01:00
|
|
|
return false;
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdDidCommit(xvac))
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
InvalidTransactionId);
|
2001-12-19 18:18:39 +01:00
|
|
|
else
|
|
|
|
{
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
2001-12-19 18:18:39 +01:00
|
|
|
return false;
|
|
|
|
}
|
1999-03-28 22:32:42 +02:00
|
|
|
}
|
|
|
|
}
|
2002-06-15 21:54:24 +02:00
|
|
|
else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2002-06-15 21:54:24 +02:00
|
|
|
if (HeapTupleHeaderGetCmin(tuple) >= GetCurrentCommandId())
|
1998-09-01 06:40:42 +02:00
|
|
|
return false; /* inserted after scan started */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-11-02 16:27:14 +01:00
|
|
|
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
|
1998-09-01 05:29:17 +02:00
|
|
|
return true;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */
|
|
|
|
return true;
|
|
|
|
|
|
|
|
Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));
|
|
|
|
|
2007-09-21 20:24:28 +02:00
|
|
|
if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
|
2004-07-28 16:23:31 +02:00
|
|
|
{
|
2007-09-21 20:24:28 +02:00
|
|
|
/* deleting subtransaction must have aborted */
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
|
|
|
|
InvalidTransactionId);
|
2004-07-01 02:52:04 +02:00
|
|
|
return true;
|
2004-07-28 16:23:31 +02:00
|
|
|
}
|
2004-07-01 02:52:04 +02:00
|
|
|
|
2002-06-15 21:54:24 +02:00
|
|
|
if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId())
|
1998-09-01 05:29:17 +02:00
|
|
|
return true; /* deleted after scan started */
|
1997-11-02 16:27:14 +01:00
|
|
|
else
|
1998-09-01 06:40:42 +02:00
|
|
|
return false; /* deleted before scan started */
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2005-05-07 23:22:01 +02:00
|
|
|
else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
|
1998-09-01 05:29:17 +02:00
|
|
|
return false;
|
2005-05-07 23:22:01 +02:00
|
|
|
else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
HeapTupleHeaderGetXmin(tuple));
|
2001-12-19 18:18:39 +01:00
|
|
|
else
|
2004-10-16 00:40:29 +02:00
|
|
|
{
|
2005-05-07 23:22:01 +02:00
|
|
|
/* it must have aborted or crashed */
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
2005-05-07 23:22:01 +02:00
|
|
|
return false;
|
2004-10-16 00:40:29 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
From: Dan McGuirk <mcguirk@indirect.com>
Reply-To: hackers@hub.org, Dan McGuirk <mcguirk@indirect.com>
To: hackers@hub.org
Subject: [HACKERS] tmin writeback optimization
I was doing some profiling of the backend, and noticed that during a certain
benchmark I was running somewhere between 30% and 75% of the backend's CPU
time was being spent in calls to TransactionIdDidCommit() from
HeapTupleSatisfiesNow() or HeapTupleSatisfiesItself() to determine that
changed rows' transactions had in fact been committed even though the rows'
tmin values had not yet been set.
When a query looks at a given row, it needs to figure out whether the
transaction that changed the row has been committed and hence it should pay
attention to the row, or whether on the other hand the transaction is still
in progress or has been aborted and hence the row should be ignored. If
a tmin value is set, it is known definitively that the row's transaction
has been committed. However, if tmin is not set, the transaction
referred to in xmin must be looked up in pg_log, and this is what the
backend was spending a lot of time doing during my benchmark.
So, implementing a method suggested by Vadim, I created the following
patch that, the first time a query finds a committed row whose tmin value
is not set, sets it, and marks the buffer where the row is stored as
dirty. (It works for tmax, too.) This doesn't result in the boost in
real time performance I was hoping for, however it does decrease backend
CPU usage by up to two-thirds in certain situations, so it could be
rather beneficial in high-concurrency settings.
1997-03-28 08:06:53 +01:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* by here, the inserting transaction has committed */
|
From: Dan McGuirk <mcguirk@indirect.com>
Reply-To: hackers@hub.org, Dan McGuirk <mcguirk@indirect.com>
To: hackers@hub.org
Subject: [HACKERS] tmin writeback optimization
I was doing some profiling of the backend, and noticed that during a certain
benchmark I was running somewhere between 30% and 75% of the backend's CPU
time was being spent in calls to TransactionIdDidCommit() from
HeapTupleSatisfiesNow() or HeapTupleSatisfiesItself() to determine that
changed rows' transactions had in fact been committed even though the rows'
tmin values had not yet been set.
When a query looks at a given row, it needs to figure out whether the
transaction that changed the row has been committed and hence it should pay
attention to the row, or whether on the other hand the transaction is still
in progress or has been aborted and hence the row should be ignored. If
a tmin value is set, it is known definitively that the row's transaction
has been committed. However, if tmin is not set, the transaction
referred to in xmin must be looked up in pg_log, and this is what the
backend was spending a lot of time doing during my benchmark.
So, implementing a method suggested by Vadim, I created the following
patch that, the first time a query finds a committed row whose tmin value
is not set, sets it, and marks the buffer where the row is stored as
dirty. (It works for tmax, too.) This doesn't result in the boost in
real time performance I was hoping for, however it does decrease backend
CPU usage by up to two-thirds in certain situations, so it could be
rather beneficial in high-concurrency settings.
1997-03-28 08:06:53 +01:00
|
|
|
|
1997-11-02 16:27:14 +01:00
|
|
|
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
|
1998-09-01 05:29:17 +02:00
|
|
|
return true;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-11-02 16:27:14 +01:00
|
|
|
if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
|
1998-12-15 13:47:01 +01:00
|
|
|
{
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
1998-12-15 13:47:01 +01:00
|
|
|
return true;
|
1998-09-01 05:29:17 +02:00
|
|
|
return false;
|
1998-12-15 13:47:01 +01:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-04-28 23:47:18 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2002-06-15 21:54:24 +02:00
|
|
|
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
1998-12-15 13:47:01 +01:00
|
|
|
return true;
|
2002-06-15 21:54:24 +02:00
|
|
|
if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId())
|
1998-09-01 05:29:17 +02:00
|
|
|
return true; /* deleted after scan started */
|
1997-11-02 16:27:14 +01:00
|
|
|
else
|
1998-09-01 05:29:17 +02:00
|
|
|
return false; /* deleted before scan started */
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2005-05-07 23:22:01 +02:00
|
|
|
if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
|
|
|
|
return true;
|
|
|
|
|
2002-06-15 21:54:24 +02:00
|
|
|
if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2005-05-07 23:22:01 +02:00
|
|
|
/* it must have aborted or crashed */
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
|
|
|
|
InvalidTransactionId);
|
1998-09-01 05:29:17 +02:00
|
|
|
return true;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
1997-11-02 16:27:14 +01:00
|
|
|
/* xmax transaction committed */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
2003-06-25 03:08:13 +02:00
|
|
|
{
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
|
|
|
|
InvalidTransactionId);
|
1998-12-15 13:47:01 +01:00
|
|
|
return true;
|
2003-06-25 03:08:13 +02:00
|
|
|
}
|
1998-12-15 13:47:01 +01:00
|
|
|
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
|
|
|
|
HeapTupleHeaderGetXmax(tuple));
|
1998-09-01 05:29:17 +02:00
|
|
|
return false;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1998-12-15 13:47:01 +01:00
|
|
|
|
2007-03-25 21:45:14 +02:00
|
|
|
/*
|
|
|
|
* HeapTupleSatisfiesAny
|
|
|
|
* Dummy "satisfies" routine: any tuple satisfies SnapshotAny.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
HeapTupleSatisfiesAny(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2002-01-16 21:29:02 +01:00
|
|
|
/*
|
|
|
|
* HeapTupleSatisfiesToast
|
2002-01-17 00:51:56 +01:00
|
|
|
* True iff heap tuple is valid as a TOAST row.
|
2002-01-16 21:29:02 +01:00
|
|
|
*
|
|
|
|
* 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
|
2002-01-17 00:09:09 +01:00
|
|
|
* its own time qual checks; if you can see the main table row that contains
|
2002-09-04 22:31:48 +02:00
|
|
|
* a TOAST reference, you should be able to see the TOASTed value. However,
|
2002-01-16 21:29:02 +01:00
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
bool
|
2007-03-25 21:45:14 +02:00
|
|
|
HeapTupleSatisfiesToast(HeapTupleHeader tuple, Snapshot snapshot,
|
|
|
|
Buffer buffer)
|
2002-01-16 21:29:02 +01:00
|
|
|
{
|
|
|
|
if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
|
|
|
|
{
|
|
|
|
if (tuple->t_infomask & HEAP_XMIN_INVALID)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (tuple->t_infomask & HEAP_MOVED_OFF)
|
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdIsCurrentTransactionId(xvac))
|
2002-01-16 21:29:02 +01:00
|
|
|
return false;
|
2003-06-25 03:08:13 +02:00
|
|
|
if (!TransactionIdIsInProgress(xvac))
|
2002-01-16 21:29:02 +01:00
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdDidCommit(xvac))
|
2002-01-16 21:29:02 +01:00
|
|
|
{
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
2002-01-16 21:29:02 +01:00
|
|
|
return false;
|
|
|
|
}
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
InvalidTransactionId);
|
2002-01-16 21:29:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tuple->t_infomask & HEAP_MOVED_IN)
|
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-06-25 03:08:13 +02:00
|
|
|
if (!TransactionIdIsCurrentTransactionId(xvac))
|
2002-01-16 21:29:02 +01:00
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdIsInProgress(xvac))
|
2002-01-16 21:29:02 +01:00
|
|
|
return false;
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdDidCommit(xvac))
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
InvalidTransactionId);
|
2002-01-16 21:29:02 +01:00
|
|
|
else
|
|
|
|
{
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
2002-01-16 21:29:02 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* otherwise assume the tuple is valid for TOAST. */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* HeapTupleSatisfiesUpdate
|
|
|
|
*
|
2002-01-17 00:51:56 +01:00
|
|
|
* Same logic as HeapTupleSatisfiesNow, but returns a more detailed result
|
2002-05-22 00:05:55 +02:00
|
|
|
* 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.
|
2005-04-28 23:47:18 +02:00
|
|
|
*
|
|
|
|
* 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
|
2006-11-17 19:00:15 +01:00
|
|
|
* 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.)
|
2002-01-16 21:29:02 +01:00
|
|
|
*/
|
2005-03-21 00:40:34 +01:00
|
|
|
HTSU_Result
|
2004-10-16 00:40:29 +02:00
|
|
|
HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid,
|
|
|
|
Buffer buffer)
|
1998-12-15 13:47:01 +01:00
|
|
|
{
|
2001-12-19 18:18:39 +01:00
|
|
|
if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
|
1998-12-15 13:47:01 +01:00
|
|
|
{
|
2001-12-19 18:18:39 +01:00
|
|
|
if (tuple->t_infomask & HEAP_XMIN_INVALID)
|
1998-12-15 13:47:01 +01:00
|
|
|
return HeapTupleInvisible;
|
|
|
|
|
2001-12-19 18:18:39 +01:00
|
|
|
if (tuple->t_infomask & HEAP_MOVED_OFF)
|
1999-03-28 22:32:42 +02:00
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdIsCurrentTransactionId(xvac))
|
1999-03-28 22:32:42 +02:00
|
|
|
return HeapTupleInvisible;
|
2003-06-25 03:08:13 +02:00
|
|
|
if (!TransactionIdIsInProgress(xvac))
|
2001-12-19 18:18:39 +01:00
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdDidCommit(xvac))
|
2001-12-19 18:18:39 +01:00
|
|
|
{
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
2001-12-19 18:18:39 +01:00
|
|
|
return HeapTupleInvisible;
|
|
|
|
}
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
InvalidTransactionId);
|
1999-03-28 22:32:42 +02:00
|
|
|
}
|
|
|
|
}
|
2001-12-19 18:18:39 +01:00
|
|
|
else if (tuple->t_infomask & HEAP_MOVED_IN)
|
1999-03-28 22:32:42 +02:00
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-06-25 03:08:13 +02:00
|
|
|
if (!TransactionIdIsCurrentTransactionId(xvac))
|
1999-03-28 22:32:42 +02:00
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdIsInProgress(xvac))
|
2001-12-19 18:18:39 +01:00
|
|
|
return HeapTupleInvisible;
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdDidCommit(xvac))
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
InvalidTransactionId);
|
2001-12-19 18:18:39 +01:00
|
|
|
else
|
|
|
|
{
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
2001-12-19 18:18:39 +01:00
|
|
|
return HeapTupleInvisible;
|
|
|
|
}
|
1999-03-28 22:32:42 +02:00
|
|
|
}
|
|
|
|
}
|
2002-06-15 21:54:24 +02:00
|
|
|
else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
|
1998-12-15 13:47:01 +01:00
|
|
|
{
|
2002-06-15 21:54:24 +02:00
|
|
|
if (HeapTupleHeaderGetCmin(tuple) >= curcid)
|
2005-10-15 04:49:52 +02:00
|
|
|
return HeapTupleInvisible; /* inserted after scan started */
|
1998-12-15 13:47:01 +01:00
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
|
1998-12-15 13:47:01 +01:00
|
|
|
return HeapTupleMayBeUpdated;
|
|
|
|
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */
|
|
|
|
return HeapTupleMayBeUpdated;
|
|
|
|
|
|
|
|
Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));
|
|
|
|
|
2007-09-21 20:24:28 +02:00
|
|
|
if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
|
2004-07-28 16:23:31 +02:00
|
|
|
{
|
2007-09-21 20:24:28 +02:00
|
|
|
/* deleting subtransaction must have aborted */
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
|
|
|
|
InvalidTransactionId);
|
2004-07-01 02:52:04 +02:00
|
|
|
return HeapTupleMayBeUpdated;
|
2004-07-28 16:23:31 +02:00
|
|
|
}
|
2004-07-01 02:52:04 +02:00
|
|
|
|
2002-06-15 21:54:24 +02:00
|
|
|
if (HeapTupleHeaderGetCmax(tuple) >= curcid)
|
2005-10-15 04:49:52 +02:00
|
|
|
return HeapTupleSelfUpdated; /* updated after scan started */
|
1998-12-15 13:47:01 +01:00
|
|
|
else
|
2005-10-15 04:49:52 +02:00
|
|
|
return HeapTupleInvisible; /* updated before scan started */
|
1998-12-15 13:47:01 +01:00
|
|
|
}
|
2005-05-07 23:22:01 +02:00
|
|
|
else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
|
1998-12-15 13:47:01 +01:00
|
|
|
return HeapTupleInvisible;
|
2005-05-07 23:22:01 +02:00
|
|
|
else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
HeapTupleHeaderGetXmin(tuple));
|
2001-12-19 18:18:39 +01:00
|
|
|
else
|
2004-10-16 00:40:29 +02:00
|
|
|
{
|
2005-05-07 23:22:01 +02:00
|
|
|
/* it must have aborted or crashed */
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
2005-05-07 23:22:01 +02:00
|
|
|
return HeapTupleInvisible;
|
2004-10-16 00:40:29 +02:00
|
|
|
}
|
1998-12-15 13:47:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* by here, the inserting transaction has committed */
|
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
|
1998-12-15 13:47:01 +01:00
|
|
|
return HeapTupleMayBeUpdated;
|
|
|
|
|
2001-12-19 18:18:39 +01:00
|
|
|
if (tuple->t_infomask & HEAP_XMAX_COMMITTED)
|
1998-12-15 13:47:01 +01:00
|
|
|
{
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
1998-12-15 13:47:01 +01:00
|
|
|
return HeapTupleMayBeUpdated;
|
2001-10-28 07:26:15 +01:00
|
|
|
return HeapTupleUpdated; /* updated by other */
|
1998-12-15 13:47:01 +01:00
|
|
|
}
|
|
|
|
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
|
|
|
|
{
|
|
|
|
/* MultiXacts are currently only allowed to lock tuples */
|
|
|
|
Assert(tuple->t_infomask & HEAP_IS_LOCKED);
|
|
|
|
|
|
|
|
if (MultiXactIdIsRunning(HeapTupleHeaderGetXmax(tuple)))
|
|
|
|
return HeapTupleBeingUpdated;
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
|
|
|
|
InvalidTransactionId);
|
2005-04-28 23:47:18 +02:00
|
|
|
return HeapTupleMayBeUpdated;
|
|
|
|
}
|
|
|
|
|
2002-06-15 21:54:24 +02:00
|
|
|
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
|
1998-12-15 13:47:01 +01:00
|
|
|
{
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
1998-12-15 13:47:01 +01:00
|
|
|
return HeapTupleMayBeUpdated;
|
2002-06-15 21:54:24 +02:00
|
|
|
if (HeapTupleHeaderGetCmax(tuple) >= curcid)
|
2005-10-15 04:49:52 +02:00
|
|
|
return HeapTupleSelfUpdated; /* updated after scan started */
|
1998-12-15 13:47:01 +01:00
|
|
|
else
|
|
|
|
return HeapTupleInvisible; /* updated before scan started */
|
|
|
|
}
|
|
|
|
|
2005-05-07 23:22:01 +02:00
|
|
|
if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
|
|
|
|
return HeapTupleBeingUpdated;
|
|
|
|
|
2002-06-15 21:54:24 +02:00
|
|
|
if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
|
1998-12-15 13:47:01 +01:00
|
|
|
{
|
2005-05-07 23:22:01 +02:00
|
|
|
/* it must have aborted or crashed */
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
|
|
|
|
InvalidTransactionId);
|
2005-05-07 23:22:01 +02:00
|
|
|
return HeapTupleMayBeUpdated;
|
1998-12-15 13:47:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* xmax transaction committed */
|
|
|
|
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
2003-06-25 03:08:13 +02:00
|
|
|
{
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
|
|
|
|
InvalidTransactionId);
|
1998-12-15 13:47:01 +01:00
|
|
|
return HeapTupleMayBeUpdated;
|
2003-06-25 03:08:13 +02:00
|
|
|
}
|
1998-12-15 13:47:01 +01:00
|
|
|
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
|
|
|
|
HeapTupleHeaderGetXmax(tuple));
|
1999-05-25 18:15:34 +02:00
|
|
|
return HeapTupleUpdated; /* updated by other */
|
1998-12-15 13:47:01 +01:00
|
|
|
}
|
|
|
|
|
2002-01-17 00:51:56 +01:00
|
|
|
/*
|
|
|
|
* HeapTupleSatisfiesDirty
|
|
|
|
* True iff heap tuple is valid including effects of open transactions.
|
2002-01-17 00:09:09 +01:00
|
|
|
*
|
2002-01-17 00:51:56 +01:00
|
|
|
* Here, we consider the effects of:
|
|
|
|
* all committed and in-progress transactions (as of the current instant)
|
2002-01-17 00:09:09 +01:00
|
|
|
* previous commands of this transaction
|
2002-01-17 00:51:56 +01:00
|
|
|
* changes made by the current command
|
2002-01-16 21:29:02 +01:00
|
|
|
*
|
2007-03-25 21:45:14 +02:00
|
|
|
* This is essentially like HeapTupleSatisfiesSelf as far as effects of
|
2002-01-16 21:29:02 +01:00
|
|
|
* the current transaction and committed/aborted xacts are concerned.
|
|
|
|
* However, we also include the effects of other xacts still in progress.
|
|
|
|
*
|
2007-03-25 21:45:14 +02:00
|
|
|
* 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.
|
2002-01-16 21:29:02 +01:00
|
|
|
*/
|
1998-12-15 13:47:01 +01:00
|
|
|
bool
|
2007-03-25 21:45:14 +02:00
|
|
|
HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Snapshot snapshot,
|
|
|
|
Buffer buffer)
|
1998-12-15 13:47:01 +01:00
|
|
|
{
|
2007-03-25 21:45:14 +02:00
|
|
|
snapshot->xmin = snapshot->xmax = InvalidTransactionId;
|
1998-12-15 13:47:01 +01:00
|
|
|
|
|
|
|
if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
|
|
|
|
{
|
1999-03-28 22:32:42 +02:00
|
|
|
if (tuple->t_infomask & HEAP_XMIN_INVALID)
|
1998-12-15 13:47:01 +01:00
|
|
|
return false;
|
|
|
|
|
1999-03-28 22:32:42 +02:00
|
|
|
if (tuple->t_infomask & HEAP_MOVED_OFF)
|
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdIsCurrentTransactionId(xvac))
|
1999-04-05 12:55:49 +02:00
|
|
|
return false;
|
2003-06-25 03:08:13 +02:00
|
|
|
if (!TransactionIdIsInProgress(xvac))
|
1999-03-28 22:32:42 +02:00
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdDidCommit(xvac))
|
2001-12-19 18:18:39 +01:00
|
|
|
{
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
2001-12-19 18:18:39 +01:00
|
|
|
return false;
|
|
|
|
}
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
InvalidTransactionId);
|
1999-03-28 22:32:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tuple->t_infomask & HEAP_MOVED_IN)
|
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-06-25 03:08:13 +02:00
|
|
|
if (!TransactionIdIsCurrentTransactionId(xvac))
|
1999-03-28 22:32:42 +02:00
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdIsInProgress(xvac))
|
2001-12-19 18:18:39 +01:00
|
|
|
return false;
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdDidCommit(xvac))
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
InvalidTransactionId);
|
1999-04-12 18:57:27 +02:00
|
|
|
else
|
|
|
|
{
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
1999-04-12 18:57:27 +02:00
|
|
|
return false;
|
|
|
|
}
|
1999-03-28 22:32:42 +02:00
|
|
|
}
|
|
|
|
}
|
2002-06-15 21:54:24 +02:00
|
|
|
else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
|
1998-12-15 13:47:01 +01:00
|
|
|
{
|
|
|
|
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
|
|
|
|
return true;
|
|
|
|
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */
|
|
|
|
return true;
|
|
|
|
|
|
|
|
Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));
|
|
|
|
|
2007-09-21 20:24:28 +02:00
|
|
|
if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
|
2004-07-28 16:23:31 +02:00
|
|
|
{
|
2007-09-21 20:24:28 +02:00
|
|
|
/* deleting subtransaction must have aborted */
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
|
|
|
|
InvalidTransactionId);
|
2004-07-01 02:52:04 +02:00
|
|
|
return true;
|
2004-07-28 16:23:31 +02:00
|
|
|
}
|
2004-07-01 02:52:04 +02:00
|
|
|
|
1998-12-15 13:47:01 +01:00
|
|
|
return false;
|
|
|
|
}
|
2005-05-07 23:22:01 +02:00
|
|
|
else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
|
1998-12-15 13:47:01 +01:00
|
|
|
{
|
2007-03-25 21:45:14 +02:00
|
|
|
snapshot->xmin = HeapTupleHeaderGetXmin(tuple);
|
2001-12-19 18:18:39 +01:00
|
|
|
/* XXX shouldn't we fall through to look at xmax? */
|
1999-05-25 18:15:34 +02:00
|
|
|
return true; /* in insertion by other */
|
1998-12-15 13:47:01 +01:00
|
|
|
}
|
2005-05-07 23:22:01 +02:00
|
|
|
else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
HeapTupleHeaderGetXmin(tuple));
|
2005-05-07 23:22:01 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* it must have aborted or crashed */
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
2005-05-07 23:22:01 +02:00
|
|
|
return false;
|
|
|
|
}
|
1998-12-15 13:47:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
{
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
1998-12-15 13:47:01 +01:00
|
|
|
return true;
|
1999-05-25 18:15:34 +02:00
|
|
|
return false; /* updated by other */
|
1998-12-15 13:47:01 +01:00
|
|
|
}
|
|
|
|
|
2005-04-28 23:47:18 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2002-06-15 21:54:24 +02:00
|
|
|
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
|
2000-07-03 06:45:09 +02:00
|
|
|
{
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
2000-07-03 06:45:09 +02:00
|
|
|
return true;
|
1998-12-15 13:47:01 +01:00
|
|
|
return false;
|
2000-07-03 06:45:09 +02:00
|
|
|
}
|
1998-12-15 13:47:01 +01:00
|
|
|
|
2005-05-07 23:22:01 +02:00
|
|
|
if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
|
1998-12-15 13:47:01 +01:00
|
|
|
{
|
2007-03-25 21:45:14 +02:00
|
|
|
snapshot->xmax = HeapTupleHeaderGetXmax(tuple);
|
2005-05-07 23:22:01 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
|
|
|
|
{
|
|
|
|
/* it must have aborted or crashed */
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
|
|
|
|
InvalidTransactionId);
|
2005-05-07 23:22:01 +02:00
|
|
|
return true;
|
1998-12-15 13:47:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* xmax transaction committed */
|
|
|
|
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
2003-06-25 03:08:13 +02:00
|
|
|
{
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
|
|
|
|
InvalidTransactionId);
|
1998-12-15 13:47:01 +01:00
|
|
|
return true;
|
2003-06-25 03:08:13 +02:00
|
|
|
}
|
1998-12-15 13:47:01 +01:00
|
|
|
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
|
|
|
|
HeapTupleHeaderGetXmax(tuple));
|
1999-05-25 18:15:34 +02:00
|
|
|
return false; /* updated by other */
|
1998-12-15 13:47:01 +01:00
|
|
|
}
|
1998-12-16 12:53:55 +01:00
|
|
|
|
2002-01-16 21:29:02 +01:00
|
|
|
/*
|
2007-03-25 21:45:14 +02:00
|
|
|
* HeapTupleSatisfiesMVCC
|
|
|
|
* True iff heap tuple is valid for the given MVCC snapshot.
|
2002-01-17 00:09:09 +01:00
|
|
|
*
|
2002-01-17 00:51:56 +01:00
|
|
|
* Here, we consider the effects of:
|
|
|
|
* all transactions committed as of the time of the given snapshot
|
2002-01-17 00:09:09 +01:00
|
|
|
* previous commands of this transaction
|
|
|
|
*
|
|
|
|
* Does _not_ include:
|
2002-01-17 00:51:56 +01:00
|
|
|
* transactions shown as in-progress by the snapshot
|
|
|
|
* transactions started after the snapshot was taken
|
2002-01-17 00:09:09 +01:00
|
|
|
* changes made by the current command
|
2002-01-16 21:29:02 +01:00
|
|
|
*
|
|
|
|
* This is the same as HeapTupleSatisfiesNow, except that transactions that
|
|
|
|
* were in progress or as yet unstarted when the snapshot was taken will
|
2002-01-17 00:09:09 +01:00
|
|
|
* be treated as uncommitted, even if they have committed by now.
|
2002-01-16 21:29:02 +01:00
|
|
|
*
|
|
|
|
* (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.)
|
|
|
|
*/
|
1998-12-16 12:53:55 +01:00
|
|
|
bool
|
2007-03-25 21:45:14 +02:00
|
|
|
HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot,
|
|
|
|
Buffer buffer)
|
1998-12-16 12:53:55 +01:00
|
|
|
{
|
|
|
|
if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
|
|
|
|
{
|
1999-03-28 22:32:42 +02:00
|
|
|
if (tuple->t_infomask & HEAP_XMIN_INVALID)
|
1998-12-16 12:53:55 +01:00
|
|
|
return false;
|
|
|
|
|
1999-03-28 22:32:42 +02:00
|
|
|
if (tuple->t_infomask & HEAP_MOVED_OFF)
|
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdIsCurrentTransactionId(xvac))
|
1999-03-28 22:32:42 +02:00
|
|
|
return false;
|
2003-06-25 03:08:13 +02:00
|
|
|
if (!TransactionIdIsInProgress(xvac))
|
2001-12-19 18:18:39 +01:00
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdDidCommit(xvac))
|
2001-12-19 18:18:39 +01:00
|
|
|
{
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
2001-12-19 18:18:39 +01:00
|
|
|
return false;
|
|
|
|
}
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
InvalidTransactionId);
|
1999-03-28 22:32:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tuple->t_infomask & HEAP_MOVED_IN)
|
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-06-25 03:08:13 +02:00
|
|
|
if (!TransactionIdIsCurrentTransactionId(xvac))
|
1999-03-28 22:32:42 +02:00
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdIsInProgress(xvac))
|
2001-12-19 18:18:39 +01:00
|
|
|
return false;
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdDidCommit(xvac))
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
InvalidTransactionId);
|
2001-12-19 18:18:39 +01:00
|
|
|
else
|
|
|
|
{
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
2001-12-19 18:18:39 +01:00
|
|
|
return false;
|
|
|
|
}
|
1999-03-28 22:32:42 +02:00
|
|
|
}
|
|
|
|
}
|
2002-06-15 21:54:24 +02:00
|
|
|
else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
|
1998-12-16 12:53:55 +01:00
|
|
|
{
|
2002-06-15 21:54:24 +02:00
|
|
|
if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
|
1998-12-16 12:53:55 +01:00
|
|
|
return false; /* inserted after scan started */
|
|
|
|
|
|
|
|
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
|
|
|
|
return true;
|
|
|
|
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */
|
|
|
|
return true;
|
|
|
|
|
|
|
|
Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));
|
|
|
|
|
2007-09-21 20:24:28 +02:00
|
|
|
if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
|
2004-07-28 16:23:31 +02:00
|
|
|
{
|
2007-09-21 20:24:28 +02:00
|
|
|
/* deleting subtransaction must have aborted */
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
|
|
|
|
InvalidTransactionId);
|
2004-07-01 02:52:04 +02:00
|
|
|
return true;
|
2004-07-28 16:23:31 +02:00
|
|
|
}
|
2004-07-01 02:52:04 +02:00
|
|
|
|
2002-06-15 21:54:24 +02:00
|
|
|
if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
|
1998-12-16 12:53:55 +01:00
|
|
|
return true; /* deleted after scan started */
|
|
|
|
else
|
|
|
|
return false; /* deleted before scan started */
|
|
|
|
}
|
2005-05-07 23:22:01 +02:00
|
|
|
else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
|
1998-12-16 12:53:55 +01:00
|
|
|
return false;
|
2005-05-07 23:22:01 +02:00
|
|
|
else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
HeapTupleHeaderGetXmin(tuple));
|
2001-12-19 18:18:39 +01:00
|
|
|
else
|
2004-10-16 00:40:29 +02:00
|
|
|
{
|
2005-05-07 23:22:01 +02:00
|
|
|
/* it must have aborted or crashed */
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
2005-05-07 23:22:01 +02:00
|
|
|
return false;
|
2004-10-16 00:40:29 +02:00
|
|
|
}
|
1998-12-16 12:53:55 +01:00
|
|
|
}
|
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
/*
|
|
|
|
* By here, the inserting transaction has committed - have to check
|
|
|
|
* when...
|
1998-12-16 12:53:55 +01:00
|
|
|
*/
|
2007-03-25 21:45:14 +02:00
|
|
|
if (XidInMVCCSnapshot(HeapTupleHeaderGetXmin(tuple), snapshot))
|
2006-09-03 17:59:39 +02:00
|
|
|
return false; /* treat as still in progress */
|
1998-12-16 12:53:55 +01:00
|
|
|
|
|
|
|
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */
|
|
|
|
return true;
|
|
|
|
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
|
|
|
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);
|
1998-12-16 12:53:55 +01:00
|
|
|
return true;
|
2005-04-28 23:47:18 +02:00
|
|
|
}
|
1998-12-16 12:53:55 +01:00
|
|
|
|
|
|
|
if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
|
|
|
|
{
|
2002-06-15 21:54:24 +02:00
|
|
|
if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
|
1998-12-16 12:53:55 +01:00
|
|
|
{
|
2002-06-15 21:54:24 +02:00
|
|
|
if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
|
1999-05-25 18:15:34 +02:00
|
|
|
return true; /* deleted after scan started */
|
1998-12-16 12:53:55 +01:00
|
|
|
else
|
1999-05-25 18:15:34 +02:00
|
|
|
return false; /* deleted before scan started */
|
1998-12-16 12:53:55 +01:00
|
|
|
}
|
|
|
|
|
2005-05-07 23:22:01 +02:00
|
|
|
if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
|
|
|
|
return true;
|
|
|
|
|
2002-06-15 21:54:24 +02:00
|
|
|
if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
|
1998-12-16 12:53:55 +01:00
|
|
|
{
|
2005-05-07 23:22:01 +02:00
|
|
|
/* it must have aborted or crashed */
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
|
|
|
|
InvalidTransactionId);
|
1998-12-16 12:53:55 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* xmax transaction committed */
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
|
|
|
|
HeapTupleHeaderGetXmax(tuple));
|
1998-12-16 12:53:55 +01:00
|
|
|
}
|
|
|
|
|
2002-05-06 04:39:01 +02:00
|
|
|
/*
|
|
|
|
* OK, the deleting transaction committed too ... but when?
|
|
|
|
*/
|
2007-03-25 21:45:14 +02:00
|
|
|
if (XidInMVCCSnapshot(HeapTupleHeaderGetXmax(tuple), snapshot))
|
2006-09-03 17:59:39 +02:00
|
|
|
return true; /* treat as still in progress */
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1998-12-16 12:53:55 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2001-07-12 06:11:13 +02:00
|
|
|
|
|
|
|
/*
|
2002-01-17 00:09:09 +01:00
|
|
|
* HeapTupleSatisfiesVacuum
|
|
|
|
*
|
2002-01-17 00:51:56 +01:00
|
|
|
* 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.
|
2001-07-12 06:11:13 +02:00
|
|
|
*
|
2001-10-25 07:50:21 +02:00
|
|
|
* OldestXmin is a cutoff XID (obtained from GetOldestXmin()). Tuples
|
2001-08-26 18:56:03 +02:00
|
|
|
* deleted by XIDs >= OldestXmin are deemed "recently dead"; they might
|
2001-07-12 06:11:13 +02:00
|
|
|
* 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
|
2004-10-16 00:40:29 +02:00
|
|
|
HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin,
|
|
|
|
Buffer buffer)
|
2001-07-12 06:11:13 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Has inserting transaction committed?
|
|
|
|
*
|
2005-11-22 19:17:34 +01:00
|
|
|
* If the inserting transaction aborted, then the tuple was never visible
|
|
|
|
* to any other transaction, so we can delete it immediately.
|
2001-07-12 06:11:13 +02:00
|
|
|
*/
|
|
|
|
if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
|
|
|
|
{
|
|
|
|
if (tuple->t_infomask & HEAP_XMIN_INVALID)
|
|
|
|
return HEAPTUPLE_DEAD;
|
|
|
|
else if (tuple->t_infomask & HEAP_MOVED_OFF)
|
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdIsCurrentTransactionId(xvac))
|
2001-12-19 18:18:39 +01:00
|
|
|
return HEAPTUPLE_DELETE_IN_PROGRESS;
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdIsInProgress(xvac))
|
2001-12-19 18:18:39 +01:00
|
|
|
return HEAPTUPLE_DELETE_IN_PROGRESS;
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdDidCommit(xvac))
|
2001-07-12 06:11:13 +02:00
|
|
|
{
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
2001-07-12 06:11:13 +02:00
|
|
|
return HEAPTUPLE_DEAD;
|
|
|
|
}
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
InvalidTransactionId);
|
2001-07-12 06:11:13 +02:00
|
|
|
}
|
|
|
|
else if (tuple->t_infomask & HEAP_MOVED_IN)
|
|
|
|
{
|
2003-06-25 03:08:13 +02:00
|
|
|
TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdIsCurrentTransactionId(xvac))
|
2001-12-19 18:18:39 +01:00
|
|
|
return HEAPTUPLE_INSERT_IN_PROGRESS;
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdIsInProgress(xvac))
|
2001-12-19 18:18:39 +01:00
|
|
|
return HEAPTUPLE_INSERT_IN_PROGRESS;
|
2003-06-25 03:08:13 +02:00
|
|
|
if (TransactionIdDidCommit(xvac))
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
InvalidTransactionId);
|
2001-12-19 18:18:39 +01:00
|
|
|
else
|
2001-07-12 06:11:13 +02:00
|
|
|
{
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
2001-07-12 06:11:13 +02:00
|
|
|
return HEAPTUPLE_DEAD;
|
|
|
|
}
|
|
|
|
}
|
2002-06-15 21:54:24 +02:00
|
|
|
else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
|
2003-09-22 02:47:23 +02:00
|
|
|
{
|
|
|
|
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
|
|
|
|
return HEAPTUPLE_INSERT_IN_PROGRESS;
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
2003-09-22 02:47:23 +02:00
|
|
|
return HEAPTUPLE_INSERT_IN_PROGRESS;
|
|
|
|
/* inserted and then deleted by same xact */
|
|
|
|
return HEAPTUPLE_DELETE_IN_PROGRESS;
|
|
|
|
}
|
2002-06-15 21:54:24 +02:00
|
|
|
else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
|
|
|
|
HeapTupleHeaderGetXmin(tuple));
|
2001-07-12 06:11:13 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Not in Progress, Not Committed, so either Aborted or crashed
|
2001-07-12 06:11:13 +02:00
|
|
|
*/
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
|
|
|
|
InvalidTransactionId);
|
2001-07-12 06:11:13 +02:00
|
|
|
return HEAPTUPLE_DEAD;
|
|
|
|
}
|
2007-11-15 22:14:46 +01:00
|
|
|
|
2007-08-02 00:45:09 +02:00
|
|
|
/*
|
|
|
|
* At this point the xmin is known committed, but we might not have
|
2007-11-15 22:14:46 +01:00
|
|
|
* been able to set the hint bit yet; so we can no longer Assert that
|
|
|
|
* it's set.
|
2007-08-02 00:45:09 +02:00
|
|
|
*/
|
2001-07-12 06:11:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Okay, the inserter committed, so it was good at some point. Now what
|
|
|
|
* about the deleting transaction?
|
2001-07-12 06:11:13 +02:00
|
|
|
*/
|
|
|
|
if (tuple->t_infomask & HEAP_XMAX_INVALID)
|
|
|
|
return HEAPTUPLE_LIVE;
|
|
|
|
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_IS_LOCKED)
|
2001-12-19 18:18:39 +01:00
|
|
|
{
|
2002-01-11 21:07:03 +01:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* "Deleting" xact really only locked it, so the tuple is live in any
|
Fix recently-understood problems with handling of XID freezing, particularly
in PITR scenarios. We now WAL-log the replacement of old XIDs with
FrozenTransactionId, so that such replacement is guaranteed to propagate to
PITR slave databases. Also, rather than relying on hint-bit updates to be
preserved, pg_clog is not truncated until all instances of an XID are known to
have been replaced by FrozenTransactionId. Add new GUC variables and
pg_autovacuum columns to allow management of the freezing policy, so that
users can trade off the size of pg_clog against the amount of freezing work
done. Revise the already-existing code that forces autovacuum of tables
approaching the wraparound point to make it more bulletproof; also, revise the
autovacuum logic so that anti-wraparound vacuuming is done per-table rather
than per-database. initdb forced because of changes in pg_class, pg_database,
and pg_autovacuum catalogs. Heikki Linnakangas, Simon Riggs, and Tom Lane.
2006-11-05 23:42:10 +01:00
|
|
|
* case. However, we should make sure that either XMAX_COMMITTED or
|
2007-11-15 22:14:46 +01:00
|
|
|
* XMAX_INVALID gets set once the xact is gone, to reduce the costs of
|
|
|
|
* examining the tuple for future xacts. Also, marking dead
|
Fix recently-understood problems with handling of XID freezing, particularly
in PITR scenarios. We now WAL-log the replacement of old XIDs with
FrozenTransactionId, so that such replacement is guaranteed to propagate to
PITR slave databases. Also, rather than relying on hint-bit updates to be
preserved, pg_clog is not truncated until all instances of an XID are known to
have been replaced by FrozenTransactionId. Add new GUC variables and
pg_autovacuum columns to allow management of the freezing policy, so that
users can trade off the size of pg_clog against the amount of freezing work
done. Revise the already-existing code that forces autovacuum of tables
approaching the wraparound point to make it more bulletproof; also, revise the
autovacuum logic so that anti-wraparound vacuuming is done per-table rather
than per-database. initdb forced because of changes in pg_class, pg_database,
and pg_autovacuum catalogs. Heikki Linnakangas, Simon Riggs, and Tom Lane.
2006-11-05 23:42:10 +01:00
|
|
|
* MultiXacts as invalid here provides defense against MultiXactId
|
|
|
|
* wraparound (see also comments in heap_freeze_tuple()).
|
2002-01-11 21:07:03 +01:00
|
|
|
*/
|
|
|
|
if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
|
|
|
|
{
|
2005-04-28 23:47:18 +02:00
|
|
|
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
|
|
|
|
{
|
|
|
|
if (MultiXactIdIsRunning(HeapTupleHeaderGetXmax(tuple)))
|
|
|
|
return HEAPTUPLE_LIVE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
|
|
|
|
return HEAPTUPLE_LIVE;
|
|
|
|
}
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-06-25 03:08:13 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* 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.
|
2003-06-25 03:08:13 +02:00
|
|
|
*/
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
|
|
|
|
InvalidTransactionId);
|
2002-01-11 21:07:03 +01:00
|
|
|
}
|
2001-12-19 18:18:39 +01:00
|
|
|
return HEAPTUPLE_LIVE;
|
|
|
|
}
|
|
|
|
|
2005-04-28 23:47:18 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2001-07-12 06:11:13 +02:00
|
|
|
if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
|
|
|
|
{
|
2002-06-15 21:54:24 +02:00
|
|
|
if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
|
2001-07-17 00:43:34 +02:00
|
|
|
return HEAPTUPLE_DELETE_IN_PROGRESS;
|
2002-06-15 21:54:24 +02:00
|
|
|
else if (TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
|
|
|
|
HeapTupleHeaderGetXmax(tuple));
|
2001-07-12 06:11:13 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Not in Progress, Not Committed, so either Aborted or crashed
|
2001-07-12 06:11:13 +02:00
|
|
|
*/
|
2007-08-14 19:35:18 +02:00
|
|
|
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
|
|
|
|
InvalidTransactionId);
|
2001-07-12 06:11:13 +02:00
|
|
|
return HEAPTUPLE_LIVE;
|
|
|
|
}
|
2007-11-15 22:14:46 +01:00
|
|
|
|
2007-08-02 00:45:09 +02:00
|
|
|
/*
|
|
|
|
* At this point the xmax is known committed, but we might not have
|
2007-11-15 22:14:46 +01:00
|
|
|
* been able to set the hint bit yet; so we can no longer Assert that
|
|
|
|
* it's set.
|
2007-08-02 00:45:09 +02:00
|
|
|
*/
|
2001-07-12 06:11:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Deleter committed, but check special cases.
|
|
|
|
*/
|
|
|
|
|
2004-08-22 04:41:58 +02:00
|
|
|
if (TransactionIdEquals(HeapTupleHeaderGetXmin(tuple),
|
2002-09-04 22:31:48 +02:00
|
|
|
HeapTupleHeaderGetXmax(tuple)))
|
2001-07-12 06:11:13 +02:00
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Inserter also deleted it, so it was never visible to anyone else.
|
|
|
|
* However, we can only remove it early if it's not an updated tuple;
|
|
|
|
* else its parent tuple is linking to it via t_ctid, and this tuple
|
|
|
|
* mustn't go away before the parent does.
|
2001-10-25 07:50:21 +02:00
|
|
|
*/
|
2005-08-20 02:40:32 +02:00
|
|
|
if (!(tuple->t_infomask & HEAP_UPDATED))
|
|
|
|
return HEAPTUPLE_DEAD;
|
2001-07-12 06:11:13 +02:00
|
|
|
}
|
|
|
|
|
2002-06-15 21:54:24 +02:00
|
|
|
if (!TransactionIdPrecedes(HeapTupleHeaderGetXmax(tuple), OldestXmin))
|
2001-07-12 06:11:13 +02:00
|
|
|
{
|
|
|
|
/* deleting xact is too recent, tuple could still be visible */
|
|
|
|
return HEAPTUPLE_RECENTLY_DEAD;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Otherwise, it's dead and removable */
|
|
|
|
return HEAPTUPLE_DEAD;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-01-16 21:29:02 +01:00
|
|
|
/*
|
2004-09-13 22:10:13 +02:00
|
|
|
* GetTransactionSnapshot
|
|
|
|
* Get the appropriate snapshot for a new query in a transaction.
|
2002-01-16 21:29:02 +01:00
|
|
|
*
|
|
|
|
* The SerializableSnapshot is the first one taken in a transaction.
|
|
|
|
* In serializable mode we just use that one throughout the transaction.
|
2004-09-13 22:10:13 +02:00
|
|
|
* In read-committed mode, we take a new snapshot each time we are called.
|
|
|
|
*
|
|
|
|
* Note that the return value points at static storage that will be modified
|
|
|
|
* by future calls and by CommandCounterIncrement(). Callers should copy
|
|
|
|
* the result with CopySnapshot() if it is to be used very long.
|
2002-01-16 21:29:02 +01:00
|
|
|
*/
|
2004-09-13 22:10:13 +02:00
|
|
|
Snapshot
|
|
|
|
GetTransactionSnapshot(void)
|
1998-12-16 12:53:55 +01:00
|
|
|
{
|
2004-09-13 22:10:13 +02:00
|
|
|
/* First call in transaction? */
|
1998-12-18 10:10:39 +01:00
|
|
|
if (SerializableSnapshot == NULL)
|
1998-12-16 12:53:55 +01:00
|
|
|
{
|
2003-06-12 03:42:21 +02:00
|
|
|
SerializableSnapshot = GetSnapshotData(&SerializableSnapshotData, true);
|
2004-09-13 22:10:13 +02:00
|
|
|
return SerializableSnapshot;
|
1998-12-16 12:53:55 +01:00
|
|
|
}
|
|
|
|
|
2003-11-06 23:08:15 +01:00
|
|
|
if (IsXactIsoLevelSerializable)
|
2004-09-13 22:10:13 +02:00
|
|
|
return SerializableSnapshot;
|
1998-12-16 12:53:55 +01:00
|
|
|
|
2004-09-13 22:10:13 +02:00
|
|
|
LatestSnapshot = GetSnapshotData(&LatestSnapshotData, false);
|
|
|
|
|
|
|
|
return LatestSnapshot;
|
1998-12-16 12:53:55 +01:00
|
|
|
}
|
|
|
|
|
2002-05-22 00:59:01 +02:00
|
|
|
/*
|
2004-09-13 22:10:13 +02:00
|
|
|
* GetLatestSnapshot
|
|
|
|
* Get a snapshot that is up-to-date as of the current instant,
|
|
|
|
* even if we are executing in SERIALIZABLE mode.
|
2002-05-22 00:59:01 +02:00
|
|
|
*/
|
|
|
|
Snapshot
|
2004-09-13 22:10:13 +02:00
|
|
|
GetLatestSnapshot(void)
|
2002-05-22 00:59:01 +02:00
|
|
|
{
|
2004-09-13 22:10:13 +02:00
|
|
|
/* Should not be first call in transaction */
|
|
|
|
if (SerializableSnapshot == NULL)
|
2003-07-25 22:18:01 +02:00
|
|
|
elog(ERROR, "no snapshot has been set");
|
2002-05-22 00:59:01 +02:00
|
|
|
|
2004-09-13 22:10:13 +02:00
|
|
|
LatestSnapshot = GetSnapshotData(&LatestSnapshotData, false);
|
2002-05-22 00:59:01 +02:00
|
|
|
|
2004-09-13 22:10:13 +02:00
|
|
|
return LatestSnapshot;
|
2002-05-22 00:59:01 +02:00
|
|
|
}
|
|
|
|
|
2003-10-01 23:30:53 +02:00
|
|
|
/*
|
2004-09-13 22:10:13 +02:00
|
|
|
* CopySnapshot
|
|
|
|
* Copy the given snapshot.
|
2003-10-01 23:30:53 +02:00
|
|
|
*
|
|
|
|
* The copy is palloc'd in the current memory context.
|
|
|
|
*/
|
|
|
|
Snapshot
|
2004-09-13 22:10:13 +02:00
|
|
|
CopySnapshot(Snapshot snapshot)
|
2003-10-01 23:30:53 +02:00
|
|
|
{
|
2004-09-13 22:10:13 +02:00
|
|
|
Snapshot newsnap;
|
2006-09-03 17:59:39 +02:00
|
|
|
Size subxipoff;
|
|
|
|
Size size;
|
2003-10-01 23:30:53 +02:00
|
|
|
|
2006-09-03 17:59:39 +02:00
|
|
|
/* We allocate any XID arrays needed in the same palloc block. */
|
|
|
|
size = subxipoff = sizeof(SnapshotData) +
|
|
|
|
snapshot->xcnt * sizeof(TransactionId);
|
|
|
|
if (snapshot->subxcnt > 0)
|
|
|
|
size += snapshot->subxcnt * sizeof(TransactionId);
|
|
|
|
|
|
|
|
newsnap = (Snapshot) palloc(size);
|
2004-09-13 22:10:13 +02:00
|
|
|
memcpy(newsnap, snapshot, sizeof(SnapshotData));
|
2006-09-03 17:59:39 +02:00
|
|
|
|
|
|
|
/* setup XID array */
|
2003-10-01 23:30:53 +02:00
|
|
|
if (snapshot->xcnt > 0)
|
|
|
|
{
|
2004-09-13 22:10:13 +02:00
|
|
|
newsnap->xip = (TransactionId *) (newsnap + 1);
|
|
|
|
memcpy(newsnap->xip, snapshot->xip,
|
2003-10-01 23:30:53 +02:00
|
|
|
snapshot->xcnt * sizeof(TransactionId));
|
|
|
|
}
|
|
|
|
else
|
2004-09-13 22:10:13 +02:00
|
|
|
newsnap->xip = NULL;
|
2003-10-01 23:30:53 +02:00
|
|
|
|
2006-09-03 17:59:39 +02:00
|
|
|
/* setup subXID array */
|
|
|
|
if (snapshot->subxcnt > 0)
|
|
|
|
{
|
|
|
|
newsnap->subxip = (TransactionId *) ((char *) newsnap + subxipoff);
|
|
|
|
memcpy(newsnap->subxip, snapshot->subxip,
|
|
|
|
snapshot->subxcnt * sizeof(TransactionId));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
newsnap->subxip = NULL;
|
|
|
|
|
2004-09-13 22:10:13 +02:00
|
|
|
return newsnap;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FreeSnapshot
|
|
|
|
* Free a snapshot previously copied with CopySnapshot.
|
|
|
|
*
|
|
|
|
* This is currently identical to pfree, but is provided for cleanliness.
|
|
|
|
*
|
|
|
|
* Do *not* apply this to the results of GetTransactionSnapshot or
|
2007-03-25 21:45:14 +02:00
|
|
|
* GetLatestSnapshot, since those are just static structs.
|
2004-09-13 22:10:13 +02:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
FreeSnapshot(Snapshot snapshot)
|
|
|
|
{
|
|
|
|
pfree(snapshot);
|
2003-10-01 23:30:53 +02:00
|
|
|
}
|
|
|
|
|
2002-01-16 21:29:02 +01:00
|
|
|
/*
|
|
|
|
* FreeXactSnapshot
|
|
|
|
* Free snapshot(s) at end of transaction.
|
|
|
|
*/
|
1998-12-16 12:53:55 +01:00
|
|
|
void
|
|
|
|
FreeXactSnapshot(void)
|
|
|
|
{
|
2003-06-12 03:42:21 +02:00
|
|
|
/*
|
2004-09-13 22:10:13 +02:00
|
|
|
* We do not free the xip arrays for the static snapshot structs; they
|
|
|
|
* will be reused soon. So this is now just a state change to prevent
|
2004-08-29 07:07:03 +02:00
|
|
|
* outside callers from accessing the snapshots.
|
2003-06-12 03:42:21 +02:00
|
|
|
*/
|
1998-12-18 10:10:39 +01:00
|
|
|
SerializableSnapshot = NULL;
|
2004-09-13 22:10:13 +02:00
|
|
|
LatestSnapshot = NULL;
|
|
|
|
ActiveSnapshot = NULL; /* just for cleanliness */
|
1998-12-16 12:53:55 +01:00
|
|
|
}
|
2006-09-03 17:59:39 +02:00
|
|
|
|
|
|
|
/*
|
2007-03-25 21:45:14 +02:00
|
|
|
* XidInMVCCSnapshot
|
2006-09-03 17:59:39 +02:00
|
|
|
* Is the given XID still-in-progress according to the snapshot?
|
|
|
|
*
|
|
|
|
* Note: GetSnapshotData never stores either top xid or subxids of our own
|
|
|
|
* backend into a snapshot, so these xids will not be reported as "running"
|
|
|
|
* by this function. This is OK for current uses, because we actually only
|
|
|
|
* apply this for known-committed XIDs.
|
|
|
|
*/
|
|
|
|
static bool
|
2007-03-25 21:45:14 +02:00
|
|
|
XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
|
2006-09-03 17:59:39 +02:00
|
|
|
{
|
|
|
|
uint32 i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make a quick range check to eliminate most XIDs without looking at the
|
2006-10-04 02:30:14 +02:00
|
|
|
* xip arrays. Note that this is OK even if we convert a subxact XID to
|
2006-09-03 17:59:39 +02:00
|
|
|
* its parent below, because a subxact with XID < xmin has surely also got
|
|
|
|
* a parent with XID < xmin, while one with XID >= xmax must belong to a
|
|
|
|
* parent that was not yet committed at the time of this snapshot.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Any xid < xmin is not in-progress */
|
|
|
|
if (TransactionIdPrecedes(xid, snapshot->xmin))
|
|
|
|
return false;
|
|
|
|
/* Any xid >= xmax is in-progress */
|
|
|
|
if (TransactionIdFollowsOrEquals(xid, snapshot->xmax))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the snapshot contains full subxact data, the fastest way to check
|
|
|
|
* things is just to compare the given XID against both subxact XIDs and
|
2006-10-04 02:30:14 +02:00
|
|
|
* top-level XIDs. If the snapshot overflowed, we have to use pg_subtrans
|
2006-09-03 17:59:39 +02:00
|
|
|
* to convert a subxact XID to its parent XID, but then we need only look
|
|
|
|
* at top-level XIDs not subxacts.
|
|
|
|
*/
|
|
|
|
if (snapshot->subxcnt >= 0)
|
|
|
|
{
|
|
|
|
/* full data, so search subxip */
|
|
|
|
int32 j;
|
|
|
|
|
|
|
|
for (j = 0; j < snapshot->subxcnt; j++)
|
|
|
|
{
|
|
|
|
if (TransactionIdEquals(xid, snapshot->subxip[j]))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* not there, fall through to search xip[] */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* overflowed, so convert xid to top-level */
|
|
|
|
xid = SubTransGetTopmostTransaction(xid);
|
|
|
|
|
|
|
|
/*
|
2006-10-04 02:30:14 +02:00
|
|
|
* If xid was indeed a subxact, we might now have an xid < xmin, so
|
|
|
|
* recheck to avoid an array scan. No point in rechecking xmax.
|
2006-09-03 17:59:39 +02:00
|
|
|
*/
|
|
|
|
if (TransactionIdPrecedes(xid, snapshot->xmin))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < snapshot->xcnt; i++)
|
|
|
|
{
|
|
|
|
if (TransactionIdEquals(xid, snapshot->xip[i]))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|