Commit b72e5fa1 authored by Tom Lane's avatar Tom Lane

Adjust time qual checking code so that we always check TransactionIdIsInProgress

before we check commit/abort status.  Formerly this was done in some paths
but not all, with the result that a transaction might be considered
committed for some purposes before it became committed for others.
Per example found by Jan Wieck.
parent 4cd4ed0c
...@@ -11,12 +11,28 @@ ...@@ -11,12 +11,28 @@
* "hint" status bits if we see that the inserting or deleting transaction * "hint" status bits if we see that the inserting or deleting transaction
* has now committed or aborted. * 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) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.87 2005/04/28 21:47:16 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.88 2005/05/07 21:22:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -147,19 +163,19 @@ HeapTupleSatisfiesItself(HeapTupleHeader tuple, Buffer buffer) ...@@ -147,19 +163,19 @@ HeapTupleSatisfiesItself(HeapTupleHeader tuple, Buffer buffer)
return false; return false;
} }
else if (!TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple))) else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
{
if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(tuple)))
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
SetBufferCommitInfoNeedsSave(buffer);
}
return false; return false;
else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
{
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
SetBufferCommitInfoNeedsSave(buffer);
} }
else else
{ {
tuple->t_infomask |= HEAP_XMIN_COMMITTED; /* it must have aborted or crashed */
tuple->t_infomask |= HEAP_XMIN_INVALID;
SetBufferCommitInfoNeedsSave(buffer); SetBufferCommitInfoNeedsSave(buffer);
return false;
} }
} }
...@@ -189,13 +205,14 @@ HeapTupleSatisfiesItself(HeapTupleHeader tuple, Buffer buffer) ...@@ -189,13 +205,14 @@ HeapTupleSatisfiesItself(HeapTupleHeader tuple, Buffer buffer)
return false; return false;
} }
if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
return true;
if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
{ {
if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple))) /* it must have aborted or crashed */
{ tuple->t_infomask |= HEAP_XMAX_INVALID;
tuple->t_infomask |= HEAP_XMAX_INVALID; SetBufferCommitInfoNeedsSave(buffer);
SetBufferCommitInfoNeedsSave(buffer);
}
return true; return true;
} }
...@@ -330,19 +347,19 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Buffer buffer) ...@@ -330,19 +347,19 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Buffer buffer)
else else
return false; /* deleted before scan started */ return false; /* deleted before scan started */
} }
else if (!TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple))) else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
{
if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(tuple)))
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
SetBufferCommitInfoNeedsSave(buffer);
}
return false; return false;
else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
{
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
SetBufferCommitInfoNeedsSave(buffer);
} }
else else
{ {
tuple->t_infomask |= HEAP_XMIN_COMMITTED; /* it must have aborted or crashed */
tuple->t_infomask |= HEAP_XMIN_INVALID;
SetBufferCommitInfoNeedsSave(buffer); SetBufferCommitInfoNeedsSave(buffer);
return false;
} }
} }
...@@ -375,13 +392,14 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Buffer buffer) ...@@ -375,13 +392,14 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Buffer buffer)
return false; /* deleted before scan started */ return false; /* deleted before scan started */
} }
if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
return true;
if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
{ {
if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple))) /* it must have aborted or crashed */
{ tuple->t_infomask |= HEAP_XMAX_INVALID;
tuple->t_infomask |= HEAP_XMAX_INVALID; SetBufferCommitInfoNeedsSave(buffer);
SetBufferCommitInfoNeedsSave(buffer);
}
return true; return true;
} }
...@@ -569,19 +587,19 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, ...@@ -569,19 +587,19 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid,
return HeapTupleInvisible; /* updated before scan return HeapTupleInvisible; /* updated before scan
* started */ * started */
} }
else if (!TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple))) else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
{
if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(tuple)))
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
SetBufferCommitInfoNeedsSave(buffer);
}
return HeapTupleInvisible; return HeapTupleInvisible;
else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
{
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
SetBufferCommitInfoNeedsSave(buffer);
} }
else else
{ {
tuple->t_infomask |= HEAP_XMIN_COMMITTED; /* it must have aborted or crashed */
tuple->t_infomask |= HEAP_XMIN_INVALID;
SetBufferCommitInfoNeedsSave(buffer); SetBufferCommitInfoNeedsSave(buffer);
return HeapTupleInvisible;
} }
} }
...@@ -620,16 +638,15 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, ...@@ -620,16 +638,15 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid,
return HeapTupleInvisible; /* updated before scan started */ return HeapTupleInvisible; /* updated before scan started */
} }
if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
return HeapTupleBeingUpdated;
if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
{ {
if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple))) /* it must have aborted or crashed */
{ tuple->t_infomask |= HEAP_XMAX_INVALID;
tuple->t_infomask |= HEAP_XMAX_INVALID; SetBufferCommitInfoNeedsSave(buffer);
SetBufferCommitInfoNeedsSave(buffer); return HeapTupleMayBeUpdated;
return HeapTupleMayBeUpdated;
}
/* running xact */
return HeapTupleBeingUpdated; /* in updation by other */
} }
/* xmax transaction committed */ /* xmax transaction committed */
...@@ -735,23 +752,24 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Buffer buffer) ...@@ -735,23 +752,24 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Buffer buffer)
return false; return false;
} }
else if (!TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple))) else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
{ {
if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(tuple)))
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
SetBufferCommitInfoNeedsSave(buffer);
return false;
}
SnapshotDirty->xmin = HeapTupleHeaderGetXmin(tuple); SnapshotDirty->xmin = HeapTupleHeaderGetXmin(tuple);
/* XXX shouldn't we fall through to look at xmax? */ /* XXX shouldn't we fall through to look at xmax? */
return true; /* in insertion by other */ return true; /* in insertion by other */
} }
else else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
{ {
tuple->t_infomask |= HEAP_XMIN_COMMITTED; tuple->t_infomask |= HEAP_XMIN_COMMITTED;
SetBufferCommitInfoNeedsSave(buffer); 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 */ /* by here, the inserting transaction has committed */
...@@ -781,17 +799,18 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Buffer buffer) ...@@ -781,17 +799,18 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Buffer buffer)
return false; return false;
} }
if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
{ {
if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
{
tuple->t_infomask |= HEAP_XMAX_INVALID;
SetBufferCommitInfoNeedsSave(buffer);
return true;
}
/* running xact */
SnapshotDirty->xmax = HeapTupleHeaderGetXmax(tuple); SnapshotDirty->xmax = HeapTupleHeaderGetXmax(tuple);
return true; /* in updation by other */ 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 */ /* xmax transaction committed */
...@@ -907,19 +926,19 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot, ...@@ -907,19 +926,19 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot,
else else
return false; /* deleted before scan started */ return false; /* deleted before scan started */
} }
else if (!TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple))) else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
{
if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(tuple)))
{
tuple->t_infomask |= HEAP_XMIN_INVALID;
SetBufferCommitInfoNeedsSave(buffer);
}
return false; return false;
else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
{
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
SetBufferCommitInfoNeedsSave(buffer);
} }
else else
{ {
tuple->t_infomask |= HEAP_XMIN_COMMITTED; /* it must have aborted or crashed */
tuple->t_infomask |= HEAP_XMIN_INVALID;
SetBufferCommitInfoNeedsSave(buffer); SetBufferCommitInfoNeedsSave(buffer);
return false;
} }
} }
...@@ -982,13 +1001,14 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot, ...@@ -982,13 +1001,14 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot,
return false; /* deleted before scan started */ return false; /* deleted before scan started */
} }
if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))
return true;
if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))
{ {
if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple))) /* it must have aborted or crashed */
{ tuple->t_infomask |= HEAP_XMAX_INVALID;
tuple->t_infomask |= HEAP_XMAX_INVALID; SetBufferCommitInfoNeedsSave(buffer);
SetBufferCommitInfoNeedsSave(buffer);
}
return true; return true;
} }
...@@ -1057,13 +1077,6 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin, ...@@ -1057,13 +1077,6 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin,
* *
* If the inserting transaction aborted, then the tuple was never visible * If the inserting transaction aborted, then the tuple was never visible
* to any other transaction, so we can delete it immediately. * to any other transaction, so we can delete it immediately.
*
* NOTE: must check TransactionIdIsInProgress (which looks in PROC array)
* before TransactionIdDidCommit/TransactionIdDidAbort (which look in
* pg_clog). Otherwise we have a race condition where 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 PROC array.
*/ */
if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment