Commit c01262a8 authored by Kevin Grittner's avatar Kevin Grittner

Eliminate xmin from hash tag for predicate locks on heap tuples.

If a tuple was frozen while its predicate locks mattered,
read-write dependencies could be missed, resulting in failure to
detect conflicts which could lead to anomalies in committed
serializable transactions.

This field was added to the tag when we still thought that it was
necessary to carry locks forward to a new version of an updated
row.  That was later proven to be unnecessary, which allowed
simplification of the code, but elimination of xmin from the tag
was missed at the time.

Per report and analysis by Heikki Linnakangas.
Backpatch to 9.1.
parent 2e1cb733
...@@ -156,9 +156,9 @@ ...@@ -156,9 +156,9 @@
* PredicateLockTuple(Relation relation, HeapTuple tuple, * PredicateLockTuple(Relation relation, HeapTuple tuple,
* Snapshot snapshot) * Snapshot snapshot)
* PredicateLockPageSplit(Relation relation, BlockNumber oldblkno, * PredicateLockPageSplit(Relation relation, BlockNumber oldblkno,
* BlockNumber newblkno); * BlockNumber newblkno)
* PredicateLockPageCombine(Relation relation, BlockNumber oldblkno, * PredicateLockPageCombine(Relation relation, BlockNumber oldblkno,
* BlockNumber newblkno); * BlockNumber newblkno)
* TransferPredicateLocksToHeapRelation(Relation relation) * TransferPredicateLocksToHeapRelation(Relation relation)
* ReleasePredicateLocks(bool isCommit) * ReleasePredicateLocks(bool isCommit)
* *
...@@ -381,7 +381,7 @@ static SHM_QUEUE *FinishedSerializableTransactions; ...@@ -381,7 +381,7 @@ static SHM_QUEUE *FinishedSerializableTransactions;
* this entry, you can ensure that there's enough scratch space available for * this entry, you can ensure that there's enough scratch space available for
* inserting one entry in the hash table. This is an otherwise-invalid tag. * inserting one entry in the hash table. This is an otherwise-invalid tag.
*/ */
static const PREDICATELOCKTARGETTAG ScratchTargetTag = {0, 0, 0, 0, 0}; static const PREDICATELOCKTARGETTAG ScratchTargetTag = {0, 0, 0, 0};
static uint32 ScratchTargetTagHash; static uint32 ScratchTargetTagHash;
static int ScratchPartitionLock; static int ScratchPartitionLock;
...@@ -2492,8 +2492,6 @@ PredicateLockTuple(Relation relation, HeapTuple tuple, Snapshot snapshot) ...@@ -2492,8 +2492,6 @@ PredicateLockTuple(Relation relation, HeapTuple tuple, Snapshot snapshot)
} }
} }
} }
else
targetxmin = InvalidTransactionId;
/* /*
* Do quick-but-not-definitive test for a relation lock first. This will * Do quick-but-not-definitive test for a relation lock first. This will
...@@ -2512,8 +2510,7 @@ PredicateLockTuple(Relation relation, HeapTuple tuple, Snapshot snapshot) ...@@ -2512,8 +2510,7 @@ PredicateLockTuple(Relation relation, HeapTuple tuple, Snapshot snapshot)
relation->rd_node.dbNode, relation->rd_node.dbNode,
relation->rd_id, relation->rd_id,
ItemPointerGetBlockNumber(tid), ItemPointerGetBlockNumber(tid),
ItemPointerGetOffsetNumber(tid), ItemPointerGetOffsetNumber(tid));
targetxmin);
PredicateLockAcquire(&tag); PredicateLockAcquire(&tag);
} }
...@@ -4283,8 +4280,7 @@ CheckForSerializableConflictIn(Relation relation, HeapTuple tuple, ...@@ -4283,8 +4280,7 @@ CheckForSerializableConflictIn(Relation relation, HeapTuple tuple,
relation->rd_node.dbNode, relation->rd_node.dbNode,
relation->rd_id, relation->rd_id,
ItemPointerGetBlockNumber(&(tuple->t_data->t_ctid)), ItemPointerGetBlockNumber(&(tuple->t_data->t_ctid)),
ItemPointerGetOffsetNumber(&(tuple->t_data->t_ctid)), ItemPointerGetOffsetNumber(&(tuple->t_data->t_ctid)));
HeapTupleHeaderGetXmin(tuple->t_data));
CheckTargetForConflictsIn(&targettag); CheckTargetForConflictsIn(&targettag);
} }
......
...@@ -254,10 +254,10 @@ typedef struct SERIALIZABLEXID ...@@ -254,10 +254,10 @@ typedef struct SERIALIZABLEXID
* be the target of predicate locks. * be the target of predicate locks.
* *
* Note that the hash function being used doesn't properly respect tag * Note that the hash function being used doesn't properly respect tag
* length -- it will go to a four byte boundary past the end of the tag. * length -- if the length of the structure isn't a multiple of four bytes it
* If you change this struct, make sure any slack space is initialized, * will go to a four byte boundary past the end of the tag. If you change
* so that any random bytes in the middle or at the end are not included * this struct, make sure any slack space is initialized, so that any random
* in the hash. * bytes in the middle or at the end are not included in the hash.
* *
* TODO SSI: If we always use the same fields for the same type of value, we * TODO SSI: If we always use the same fields for the same type of value, we
* should rename these. Holding off until it's clear there are no exceptions. * should rename these. Holding off until it's clear there are no exceptions.
...@@ -272,7 +272,6 @@ typedef struct PREDICATELOCKTARGETTAG ...@@ -272,7 +272,6 @@ typedef struct PREDICATELOCKTARGETTAG
uint32 locktag_field2; /* a 32-bit ID field */ uint32 locktag_field2; /* a 32-bit ID field */
uint32 locktag_field3; /* a 32-bit ID field */ uint32 locktag_field3; /* a 32-bit ID field */
uint32 locktag_field4; /* a 32-bit ID field */ uint32 locktag_field4; /* a 32-bit ID field */
uint32 locktag_field5; /* a 32-bit ID field */
} PREDICATELOCKTARGETTAG; } PREDICATELOCKTARGETTAG;
/* /*
...@@ -283,12 +282,6 @@ typedef struct PREDICATELOCKTARGETTAG ...@@ -283,12 +282,6 @@ typedef struct PREDICATELOCKTARGETTAG
* added when a predicate lock is requested on an object which doesn't * added when a predicate lock is requested on an object which doesn't
* already have one. An entry is removed when the last lock is removed from * already have one. An entry is removed when the last lock is removed from
* its list. * its list.
*
* Because a particular target might become obsolete, due to update to a new
* version, before the reading transaction is obsolete, we need some way to
* prevent errors from reuse of a tuple ID. Rather than attempting to clean
* up the targets as the related tuples are pruned or vacuumed, we check the
* xmin on access. This should be far less costly.
*/ */
typedef struct PREDICATELOCKTARGET typedef struct PREDICATELOCKTARGET
{ {
...@@ -398,22 +391,19 @@ typedef struct PredicateLockData ...@@ -398,22 +391,19 @@ typedef struct PredicateLockData
((locktag).locktag_field1 = (dboid), \ ((locktag).locktag_field1 = (dboid), \
(locktag).locktag_field2 = (reloid), \ (locktag).locktag_field2 = (reloid), \
(locktag).locktag_field3 = InvalidBlockNumber, \ (locktag).locktag_field3 = InvalidBlockNumber, \
(locktag).locktag_field4 = InvalidOffsetNumber, \ (locktag).locktag_field4 = InvalidOffsetNumber)
(locktag).locktag_field5 = InvalidTransactionId)
#define SET_PREDICATELOCKTARGETTAG_PAGE(locktag,dboid,reloid,blocknum) \ #define SET_PREDICATELOCKTARGETTAG_PAGE(locktag,dboid,reloid,blocknum) \
((locktag).locktag_field1 = (dboid), \ ((locktag).locktag_field1 = (dboid), \
(locktag).locktag_field2 = (reloid), \ (locktag).locktag_field2 = (reloid), \
(locktag).locktag_field3 = (blocknum), \ (locktag).locktag_field3 = (blocknum), \
(locktag).locktag_field4 = InvalidOffsetNumber, \ (locktag).locktag_field4 = InvalidOffsetNumber)
(locktag).locktag_field5 = InvalidTransactionId)
#define SET_PREDICATELOCKTARGETTAG_TUPLE(locktag,dboid,reloid,blocknum,offnum,xmin) \ #define SET_PREDICATELOCKTARGETTAG_TUPLE(locktag,dboid,reloid,blocknum,offnum) \
((locktag).locktag_field1 = (dboid), \ ((locktag).locktag_field1 = (dboid), \
(locktag).locktag_field2 = (reloid), \ (locktag).locktag_field2 = (reloid), \
(locktag).locktag_field3 = (blocknum), \ (locktag).locktag_field3 = (blocknum), \
(locktag).locktag_field4 = (offnum), \ (locktag).locktag_field4 = (offnum))
(locktag).locktag_field5 = (xmin))
#define GET_PREDICATELOCKTARGETTAG_DB(locktag) \ #define GET_PREDICATELOCKTARGETTAG_DB(locktag) \
((Oid) (locktag).locktag_field1) ((Oid) (locktag).locktag_field1)
...@@ -423,8 +413,6 @@ typedef struct PredicateLockData ...@@ -423,8 +413,6 @@ typedef struct PredicateLockData
((BlockNumber) (locktag).locktag_field3) ((BlockNumber) (locktag).locktag_field3)
#define GET_PREDICATELOCKTARGETTAG_OFFSET(locktag) \ #define GET_PREDICATELOCKTARGETTAG_OFFSET(locktag) \
((OffsetNumber) (locktag).locktag_field4) ((OffsetNumber) (locktag).locktag_field4)
#define GET_PREDICATELOCKTARGETTAG_XMIN(locktag) \
((TransactionId) (locktag).locktag_field5)
#define GET_PREDICATELOCKTARGETTAG_TYPE(locktag) \ #define GET_PREDICATELOCKTARGETTAG_TYPE(locktag) \
(((locktag).locktag_field4 != InvalidOffsetNumber) ? PREDLOCKTAG_TUPLE : \ (((locktag).locktag_field4 != InvalidOffsetNumber) ? PREDLOCKTAG_TUPLE : \
(((locktag).locktag_field3 != InvalidBlockNumber) ? PREDLOCKTAG_PAGE : \ (((locktag).locktag_field3 != InvalidBlockNumber) ? PREDLOCKTAG_PAGE : \
...@@ -462,6 +450,7 @@ typedef struct TwoPhasePredicateXactRecord ...@@ -462,6 +450,7 @@ typedef struct TwoPhasePredicateXactRecord
typedef struct TwoPhasePredicateLockRecord typedef struct TwoPhasePredicateLockRecord
{ {
PREDICATELOCKTARGETTAG target; PREDICATELOCKTARGETTAG target;
uint32 filler; /* to avoid length change in back-patched fix */
} TwoPhasePredicateLockRecord; } TwoPhasePredicateLockRecord;
typedef struct TwoPhasePredicateRecord typedef struct TwoPhasePredicateRecord
......
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