*** a/src/backend/storage/lmgr/predicate.c
--- b/src/backend/storage/lmgr/predicate.c
***************
*** 156,164 ****
   *		PredicateLockTuple(Relation relation, HeapTuple tuple,
   *						Snapshot snapshot)
   *		PredicateLockPageSplit(Relation relation, BlockNumber oldblkno,
!  *							   BlockNumber newblkno);
   *		PredicateLockPageCombine(Relation relation, BlockNumber oldblkno,
!  *								 BlockNumber newblkno);
   *		TransferPredicateLocksToHeapRelation(Relation relation)
   *		ReleasePredicateLocks(bool isCommit)
   *
--- 156,164 ----
   *		PredicateLockTuple(Relation relation, HeapTuple tuple,
   *						Snapshot snapshot)
   *		PredicateLockPageSplit(Relation relation, BlockNumber oldblkno,
!  *							   BlockNumber newblkno)
   *		PredicateLockPageCombine(Relation relation, BlockNumber oldblkno,
!  *								 BlockNumber newblkno)
   *		TransferPredicateLocksToHeapRelation(Relation relation)
   *		ReleasePredicateLocks(bool isCommit)
   *
***************
*** 381,387 **** static SHM_QUEUE *FinishedSerializableTransactions;
   * 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.
   */
! static const PREDICATELOCKTARGETTAG ScratchTargetTag = {0, 0, 0, 0, 0};
  static uint32 ScratchTargetTagHash;
  static int	ScratchPartitionLock;
  
--- 381,387 ----
   * 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.
   */
! static const PREDICATELOCKTARGETTAG ScratchTargetTag = {0, 0, 0, 0};
  static uint32 ScratchTargetTagHash;
  static int	ScratchPartitionLock;
  
***************
*** 2492,2499 **** PredicateLockTuple(Relation relation, HeapTuple tuple, Snapshot snapshot)
  			}
  		}
  	}
- 	else
- 		targetxmin = InvalidTransactionId;
  
  	/*
  	 * Do quick-but-not-definitive test for a relation lock first.	This will
--- 2492,2497 ----
***************
*** 2512,2519 **** PredicateLockTuple(Relation relation, HeapTuple tuple, Snapshot snapshot)
  									 relation->rd_node.dbNode,
  									 relation->rd_id,
  									 ItemPointerGetBlockNumber(tid),
! 									 ItemPointerGetOffsetNumber(tid),
! 									 targetxmin);
  	PredicateLockAcquire(&tag);
  }
  
--- 2510,2516 ----
  									 relation->rd_node.dbNode,
  									 relation->rd_id,
  									 ItemPointerGetBlockNumber(tid),
! 									 ItemPointerGetOffsetNumber(tid));
  	PredicateLockAcquire(&tag);
  }
  
***************
*** 4283,4290 **** CheckForSerializableConflictIn(Relation relation, HeapTuple tuple,
  										 relation->rd_node.dbNode,
  										 relation->rd_id,
  						 ItemPointerGetBlockNumber(&(tuple->t_data->t_ctid)),
! 						ItemPointerGetOffsetNumber(&(tuple->t_data->t_ctid)),
! 									  HeapTupleHeaderGetXmin(tuple->t_data));
  		CheckTargetForConflictsIn(&targettag);
  	}
  
--- 4280,4286 ----
  										 relation->rd_node.dbNode,
  										 relation->rd_id,
  						 ItemPointerGetBlockNumber(&(tuple->t_data->t_ctid)),
! 						ItemPointerGetOffsetNumber(&(tuple->t_data->t_ctid)));
  		CheckTargetForConflictsIn(&targettag);
  	}
  
*** a/src/include/storage/predicate.h
--- b/src/include/storage/predicate.h
***************
*** 52,57 **** extern void PredicateLockTuple(Relation relation, HeapTuple tuple, Snapshot snap
--- 52,58 ----
  extern void PredicateLockPageSplit(Relation relation, BlockNumber oldblkno, BlockNumber newblkno);
  extern void PredicateLockPageCombine(Relation relation, BlockNumber oldblkno, BlockNumber newblkno);
  extern void TransferPredicateLocksToHeapRelation(Relation relation);
+ extern void ReleasePredicateTupleLocks(Relation relation, BlockNumber blkno, OffsetNumber offset);
  extern void ReleasePredicateLocks(bool isCommit);
  
  /* conflict detection (may also trigger rollback) */
*** a/src/include/storage/predicate_internals.h
--- b/src/include/storage/predicate_internals.h
***************
*** 254,263 **** typedef struct SERIALIZABLEXID
   * be the target of predicate locks.
   *
   * 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.
!  * If you change this struct, make sure any slack space is initialized,
!  * so that any random 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
   * should rename these.  Holding off until it's clear there are no exceptions.
--- 254,263 ----
   * be the target of predicate locks.
   *
   * Note that the hash function being used doesn't properly respect tag
!  * length -- if the length of the structure isn't a multiple of four bytes it
!  * will go to a four byte boundary past the end of the tag.  If you change
!  * this struct, make sure any slack space is initialized, so that any random
!  * 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
   * should rename these.  Holding off until it's clear there are no exceptions.
***************
*** 272,278 **** typedef struct PREDICATELOCKTARGETTAG
  	uint32		locktag_field2; /* a 32-bit ID field */
  	uint32		locktag_field3; /* a 32-bit ID field */
  	uint32		locktag_field4; /* a 32-bit ID field */
- 	uint32		locktag_field5; /* a 32-bit ID field */
  } PREDICATELOCKTARGETTAG;
  
  /*
--- 272,277 ----
***************
*** 283,294 **** typedef struct PREDICATELOCKTARGETTAG
   * 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
   * 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
  {
--- 282,287 ----
***************
*** 398,419 **** typedef struct PredicateLockData
  	((locktag).locktag_field1 = (dboid), \
  	 (locktag).locktag_field2 = (reloid), \
  	 (locktag).locktag_field3 = InvalidBlockNumber, \
! 	 (locktag).locktag_field4 = InvalidOffsetNumber, \
! 	 (locktag).locktag_field5 = InvalidTransactionId)
  
  #define SET_PREDICATELOCKTARGETTAG_PAGE(locktag,dboid,reloid,blocknum) \
  	((locktag).locktag_field1 = (dboid), \
  	 (locktag).locktag_field2 = (reloid), \
  	 (locktag).locktag_field3 = (blocknum), \
! 	 (locktag).locktag_field4 = InvalidOffsetNumber, \
! 	 (locktag).locktag_field5 = InvalidTransactionId)
  
! #define SET_PREDICATELOCKTARGETTAG_TUPLE(locktag,dboid,reloid,blocknum,offnum,xmin) \
  	((locktag).locktag_field1 = (dboid), \
  	 (locktag).locktag_field2 = (reloid), \
  	 (locktag).locktag_field3 = (blocknum), \
! 	 (locktag).locktag_field4 = (offnum), \
! 	 (locktag).locktag_field5 = (xmin))
  
  #define GET_PREDICATELOCKTARGETTAG_DB(locktag) \
  	((Oid) (locktag).locktag_field1)
--- 391,409 ----
  	((locktag).locktag_field1 = (dboid), \
  	 (locktag).locktag_field2 = (reloid), \
  	 (locktag).locktag_field3 = InvalidBlockNumber, \
! 	 (locktag).locktag_field4 = InvalidOffsetNumber)
  
  #define SET_PREDICATELOCKTARGETTAG_PAGE(locktag,dboid,reloid,blocknum) \
  	((locktag).locktag_field1 = (dboid), \
  	 (locktag).locktag_field2 = (reloid), \
  	 (locktag).locktag_field3 = (blocknum), \
! 	 (locktag).locktag_field4 = InvalidOffsetNumber)
  
! #define SET_PREDICATELOCKTARGETTAG_TUPLE(locktag,dboid,reloid,blocknum,offnum) \
  	((locktag).locktag_field1 = (dboid), \
  	 (locktag).locktag_field2 = (reloid), \
  	 (locktag).locktag_field3 = (blocknum), \
! 	 (locktag).locktag_field4 = (offnum))
  
  #define GET_PREDICATELOCKTARGETTAG_DB(locktag) \
  	((Oid) (locktag).locktag_field1)
***************
*** 423,430 **** typedef struct PredicateLockData
  	((BlockNumber) (locktag).locktag_field3)
  #define GET_PREDICATELOCKTARGETTAG_OFFSET(locktag) \
  	((OffsetNumber) (locktag).locktag_field4)
- #define GET_PREDICATELOCKTARGETTAG_XMIN(locktag) \
- 	((TransactionId) (locktag).locktag_field5)
  #define GET_PREDICATELOCKTARGETTAG_TYPE(locktag)							 \
  	(((locktag).locktag_field4 != InvalidOffsetNumber) ? PREDLOCKTAG_TUPLE : \
  	 (((locktag).locktag_field3 != InvalidBlockNumber) ? PREDLOCKTAG_PAGE :   \
--- 413,418 ----
