mirror of
https://github.com/postgres/postgres.git
synced 2025-11-06 07:49:08 +03:00
TOAST needs to do at least minimal time-qual checking in order not to
mess up after an aborted VACUUM FULL, per today's pghackers discussion. Add a suitable HeapTupleSatisfiesToast routine. Remove useless special- case test in HeapTupleSatisfiesVisibility macro for xmax = BootstrapTransactionId; perhaps that was needed at one time, but it's a waste of cycles now, not to mention actively wrong for SnapshotAny. Along the way, add some much-needed comments to tqual.c, and simplify toast_fetch_datum, which no longer needs to assume it may see chunks out-of-order.
This commit is contained in:
@@ -1,14 +1,22 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* tqual.c
|
||||
* POSTGRES "time" qualification code.
|
||||
* POSTGRES "time" qualification code, ie, tuple visibility rules.
|
||||
*
|
||||
* 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. The caller is responsible for noticing any
|
||||
* change in t_infomask and scheduling a disk write if so. Note that 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.)
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/time/tqual.c,v 1.46 2002/01/11 20:07:03 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/time/tqual.c,v 1.47 2002/01/16 20:29:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -36,8 +44,7 @@ bool ReferentialIntegritySnapshotOverride = false;
|
||||
*
|
||||
* 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
|
||||
@@ -153,8 +160,7 @@ HeapTupleSatisfiesItself(HeapTupleHeader tuple)
|
||||
*
|
||||
* Note:
|
||||
* Assumes heap tuple is valid.
|
||||
*/
|
||||
/*
|
||||
*
|
||||
* The satisfaction of "now" requires the following:
|
||||
*
|
||||
* ((Xmin == my-transaction && changed by the current transaction
|
||||
@@ -288,6 +294,71 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* HeapTupleSatisfiesToast
|
||||
* True iff heap tuple is valid for TOAST usage.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
bool
|
||||
HeapTupleSatisfiesToast(HeapTupleHeader tuple)
|
||||
{
|
||||
if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
|
||||
{
|
||||
if (tuple->t_infomask & HEAP_XMIN_INVALID)
|
||||
return false;
|
||||
|
||||
if (tuple->t_infomask & HEAP_MOVED_OFF)
|
||||
{
|
||||
if (TransactionIdIsCurrentTransactionId((TransactionId) tuple->t_cmin))
|
||||
return false;
|
||||
if (!TransactionIdIsInProgress((TransactionId) tuple->t_cmin))
|
||||
{
|
||||
if (TransactionIdDidCommit((TransactionId) tuple->t_cmin))
|
||||
{
|
||||
tuple->t_infomask |= HEAP_XMIN_INVALID;
|
||||
return false;
|
||||
}
|
||||
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
|
||||
}
|
||||
}
|
||||
else if (tuple->t_infomask & HEAP_MOVED_IN)
|
||||
{
|
||||
if (!TransactionIdIsCurrentTransactionId((TransactionId) tuple->t_cmin))
|
||||
{
|
||||
if (TransactionIdIsInProgress((TransactionId) tuple->t_cmin))
|
||||
return false;
|
||||
if (TransactionIdDidCommit((TransactionId) tuple->t_cmin))
|
||||
tuple->t_infomask |= HEAP_XMIN_COMMITTED;
|
||||
else
|
||||
{
|
||||
tuple->t_infomask |= HEAP_XMIN_INVALID;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* otherwise assume the tuple is valid for TOAST. */
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* HeapTupleSatisfiesUpdate
|
||||
* Check whether a tuple can be updated.
|
||||
*
|
||||
* This applies exactly the same checks as HeapTupleSatisfiesNow,
|
||||
* but returns a more-detailed result code, since UPDATE needs to know
|
||||
* more than "is it visible?"
|
||||
*/
|
||||
int
|
||||
HeapTupleSatisfiesUpdate(HeapTuple htuple)
|
||||
{
|
||||
@@ -404,6 +475,18 @@ HeapTupleSatisfiesUpdate(HeapTuple htuple)
|
||||
return HeapTupleUpdated; /* updated by other */
|
||||
}
|
||||
|
||||
/*
|
||||
* HeapTupleSatisfiesDirty
|
||||
* True iff heap tuple is valid, including effects of concurrent xacts.
|
||||
*
|
||||
* This is essentially like HeapTupleSatisfiesItself as far as effects of
|
||||
* the current transaction and committed/aborted xacts are concerned.
|
||||
* However, we also include the effects of other xacts still in progress.
|
||||
*
|
||||
* Returns extra information in the global variable SnapshotDirty, namely
|
||||
* xids of concurrent xacts that affected the tuple. Also, the tuple's
|
||||
* t_ctid (forward link) is returned if it's being updated.
|
||||
*/
|
||||
bool
|
||||
HeapTupleSatisfiesDirty(HeapTupleHeader tuple)
|
||||
{
|
||||
@@ -516,6 +599,18 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple)
|
||||
return false; /* updated by other */
|
||||
}
|
||||
|
||||
/*
|
||||
* HeapTupleSatisfiesSnapshot
|
||||
* True iff heap tuple is valid for the given snapshot.
|
||||
*
|
||||
* This is the same as HeapTupleSatisfiesNow, except that transactions that
|
||||
* were in progress or as yet unstarted when the snapshot was taken will
|
||||
* be treated as uncommitted, even if they really have committed by now.
|
||||
*
|
||||
* (Notice, however, that the tuple status hint bits will be updated on the
|
||||
* basis of the true state of the transaction, even if we then pretend we
|
||||
* can't see it.)
|
||||
*/
|
||||
bool
|
||||
HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot)
|
||||
{
|
||||
@@ -658,11 +753,6 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot)
|
||||
* deleted by XIDs >= OldestXmin are deemed "recently dead"; they might
|
||||
* still be visible to some open transaction, so we can't remove them,
|
||||
* even if we see that the deleting transaction has committed.
|
||||
*
|
||||
* As with the other HeapTupleSatisfies routines, we may update the tuple's
|
||||
* "hint" status bits if we see that the inserting or deleting transaction
|
||||
* has now committed or aborted. The caller is responsible for noticing any
|
||||
* change in t_infomask and scheduling a disk write if so.
|
||||
*/
|
||||
HTSV_Result
|
||||
HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin)
|
||||
@@ -808,13 +898,21 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* SetQuerySnapshot
|
||||
* Initialize query snapshot for a new query
|
||||
*
|
||||
* The SerializableSnapshot is the first one taken in a transaction.
|
||||
* In serializable mode we just use that one throughout the transaction.
|
||||
* In read-committed mode, we take a new snapshot at the start of each query.
|
||||
*/
|
||||
void
|
||||
SetQuerySnapshot(void)
|
||||
{
|
||||
/* Initialize snapshot overriding to false */
|
||||
ReferentialIntegritySnapshotOverride = false;
|
||||
|
||||
/* 1st call in xaction */
|
||||
/* 1st call in xaction? */
|
||||
if (SerializableSnapshot == NULL)
|
||||
{
|
||||
SerializableSnapshot = GetSnapshotData(true);
|
||||
@@ -837,6 +935,10 @@ SetQuerySnapshot(void)
|
||||
Assert(QuerySnapshot != NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* FreeXactSnapshot
|
||||
* Free snapshot(s) at end of transaction.
|
||||
*/
|
||||
void
|
||||
FreeXactSnapshot(void)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user