Skip to content

Commit c01262a

Browse files
committed
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.
1 parent 2e1cb73 commit c01262a

File tree

2 files changed

+14
-29
lines changed

2 files changed

+14
-29
lines changed

src/backend/storage/lmgr/predicate.c

+5-9
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,9 @@
156156
* PredicateLockTuple(Relation relation, HeapTuple tuple,
157157
* Snapshot snapshot)
158158
* PredicateLockPageSplit(Relation relation, BlockNumber oldblkno,
159-
* BlockNumber newblkno);
159+
* BlockNumber newblkno)
160160
* PredicateLockPageCombine(Relation relation, BlockNumber oldblkno,
161-
* BlockNumber newblkno);
161+
* BlockNumber newblkno)
162162
* TransferPredicateLocksToHeapRelation(Relation relation)
163163
* ReleasePredicateLocks(bool isCommit)
164164
*
@@ -381,7 +381,7 @@ static SHM_QUEUE *FinishedSerializableTransactions;
381381
* this entry, you can ensure that there's enough scratch space available for
382382
* inserting one entry in the hash table. This is an otherwise-invalid tag.
383383
*/
384-
static const PREDICATELOCKTARGETTAG ScratchTargetTag = {0, 0, 0, 0, 0};
384+
static const PREDICATELOCKTARGETTAG ScratchTargetTag = {0, 0, 0, 0};
385385
static uint32 ScratchTargetTagHash;
386386
static int ScratchPartitionLock;
387387

@@ -2492,8 +2492,6 @@ PredicateLockTuple(Relation relation, HeapTuple tuple, Snapshot snapshot)
24922492
}
24932493
}
24942494
}
2495-
else
2496-
targetxmin = InvalidTransactionId;
24972495

24982496
/*
24992497
* Do quick-but-not-definitive test for a relation lock first. This will
@@ -2512,8 +2510,7 @@ PredicateLockTuple(Relation relation, HeapTuple tuple, Snapshot snapshot)
25122510
relation->rd_node.dbNode,
25132511
relation->rd_id,
25142512
ItemPointerGetBlockNumber(tid),
2515-
ItemPointerGetOffsetNumber(tid),
2516-
targetxmin);
2513+
ItemPointerGetOffsetNumber(tid));
25172514
PredicateLockAcquire(&tag);
25182515
}
25192516

@@ -4283,8 +4280,7 @@ CheckForSerializableConflictIn(Relation relation, HeapTuple tuple,
42834280
relation->rd_node.dbNode,
42844281
relation->rd_id,
42854282
ItemPointerGetBlockNumber(&(tuple->t_data->t_ctid)),
4286-
ItemPointerGetOffsetNumber(&(tuple->t_data->t_ctid)),
4287-
HeapTupleHeaderGetXmin(tuple->t_data));
4283+
ItemPointerGetOffsetNumber(&(tuple->t_data->t_ctid)));
42884284
CheckTargetForConflictsIn(&targettag);
42894285
}
42904286

src/include/storage/predicate_internals.h

+9-20
Original file line numberDiff line numberDiff line change
@@ -254,10 +254,10 @@ typedef struct SERIALIZABLEXID
254254
* be the target of predicate locks.
255255
*
256256
* Note that the hash function being used doesn't properly respect tag
257-
* length -- it will go to a four byte boundary past the end of the tag.
258-
* If you change this struct, make sure any slack space is initialized,
259-
* so that any random bytes in the middle or at the end are not included
260-
* in the hash.
257+
* length -- if the length of the structure isn't a multiple of four bytes it
258+
* will go to a four byte boundary past the end of the tag. If you change
259+
* this struct, make sure any slack space is initialized, so that any random
260+
* bytes in the middle or at the end are not included in the hash.
261261
*
262262
* TODO SSI: If we always use the same fields for the same type of value, we
263263
* should rename these. Holding off until it's clear there are no exceptions.
@@ -272,7 +272,6 @@ typedef struct PREDICATELOCKTARGETTAG
272272
uint32 locktag_field2; /* a 32-bit ID field */
273273
uint32 locktag_field3; /* a 32-bit ID field */
274274
uint32 locktag_field4; /* a 32-bit ID field */
275-
uint32 locktag_field5; /* a 32-bit ID field */
276275
} PREDICATELOCKTARGETTAG;
277276

278277
/*
@@ -283,12 +282,6 @@ typedef struct PREDICATELOCKTARGETTAG
283282
* added when a predicate lock is requested on an object which doesn't
284283
* already have one. An entry is removed when the last lock is removed from
285284
* its list.
286-
*
287-
* Because a particular target might become obsolete, due to update to a new
288-
* version, before the reading transaction is obsolete, we need some way to
289-
* prevent errors from reuse of a tuple ID. Rather than attempting to clean
290-
* up the targets as the related tuples are pruned or vacuumed, we check the
291-
* xmin on access. This should be far less costly.
292285
*/
293286
typedef struct PREDICATELOCKTARGET
294287
{
@@ -398,22 +391,19 @@ typedef struct PredicateLockData
398391
((locktag).locktag_field1 = (dboid), \
399392
(locktag).locktag_field2 = (reloid), \
400393
(locktag).locktag_field3 = InvalidBlockNumber, \
401-
(locktag).locktag_field4 = InvalidOffsetNumber, \
402-
(locktag).locktag_field5 = InvalidTransactionId)
394+
(locktag).locktag_field4 = InvalidOffsetNumber)
403395

404396
#define SET_PREDICATELOCKTARGETTAG_PAGE(locktag,dboid,reloid,blocknum) \
405397
((locktag).locktag_field1 = (dboid), \
406398
(locktag).locktag_field2 = (reloid), \
407399
(locktag).locktag_field3 = (blocknum), \
408-
(locktag).locktag_field4 = InvalidOffsetNumber, \
409-
(locktag).locktag_field5 = InvalidTransactionId)
400+
(locktag).locktag_field4 = InvalidOffsetNumber)
410401

411-
#define SET_PREDICATELOCKTARGETTAG_TUPLE(locktag,dboid,reloid,blocknum,offnum,xmin) \
402+
#define SET_PREDICATELOCKTARGETTAG_TUPLE(locktag,dboid,reloid,blocknum,offnum) \
412403
((locktag).locktag_field1 = (dboid), \
413404
(locktag).locktag_field2 = (reloid), \
414405
(locktag).locktag_field3 = (blocknum), \
415-
(locktag).locktag_field4 = (offnum), \
416-
(locktag).locktag_field5 = (xmin))
406+
(locktag).locktag_field4 = (offnum))
417407

418408
#define GET_PREDICATELOCKTARGETTAG_DB(locktag) \
419409
((Oid) (locktag).locktag_field1)
@@ -423,8 +413,6 @@ typedef struct PredicateLockData
423413
((BlockNumber) (locktag).locktag_field3)
424414
#define GET_PREDICATELOCKTARGETTAG_OFFSET(locktag) \
425415
((OffsetNumber) (locktag).locktag_field4)
426-
#define GET_PREDICATELOCKTARGETTAG_XMIN(locktag) \
427-
((TransactionId) (locktag).locktag_field5)
428416
#define GET_PREDICATELOCKTARGETTAG_TYPE(locktag) \
429417
(((locktag).locktag_field4 != InvalidOffsetNumber) ? PREDLOCKTAG_TUPLE : \
430418
(((locktag).locktag_field3 != InvalidBlockNumber) ? PREDLOCKTAG_PAGE : \
@@ -462,6 +450,7 @@ typedef struct TwoPhasePredicateXactRecord
462450
typedef struct TwoPhasePredicateLockRecord
463451
{
464452
PREDICATELOCKTARGETTAG target;
453+
uint32 filler; /* to avoid length change in back-patched fix */
465454
} TwoPhasePredicateLockRecord;
466455

467456
typedef struct TwoPhasePredicateRecord

0 commit comments

Comments
 (0)