*** a/src/backend/access/heap/heapam.c
--- b/src/backend/access/heap/heapam.c
***************
*** 1529,1535 **** heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
  	OffsetNumber offnum;
  	bool		at_chain_start;
  	bool		valid;
- 	bool		match_found;
  
  	if (all_dead)
  		*all_dead = true;
--- 1529,1534 ----
***************
*** 1539,1545 **** heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
  	Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
  	offnum = ItemPointerGetOffsetNumber(tid);
  	at_chain_start = true;
- 	match_found = false;
  
  	/* Scan through possible multiple members of HOT-chain */
  	for (;;)
--- 1538,1543 ----
***************
*** 1597,1606 **** heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
  			PredicateLockTuple(relation, &heapTuple);
  			if (all_dead)
  				*all_dead = false;
! 			if (IsolationIsSerializable())
! 				match_found = true;
! 			else
! 				return true;
  		}
  
  		/*
--- 1595,1601 ----
  			PredicateLockTuple(relation, &heapTuple);
  			if (all_dead)
  				*all_dead = false;
! 			return true;
  		}
  
  		/*
***************
*** 1629,1635 **** heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
  			break;				/* end of chain */
  	}
  
! 	return match_found;
  }
  
  /*
--- 1624,1630 ----
  			break;				/* end of chain */
  	}
  
! 	return false;
  }
  
  /*
***************
*** 2855,2866 **** l2:
  
  	END_CRIT_SECTION();
  
- 	/*
- 	 * Any existing SIREAD locks on the old tuple must be linked to the new
- 	 * tuple for conflict detection purposes.
- 	 */
- 	PredicateLockTupleRowVersionLink(relation, &oldtup, heaptup);
- 
  	if (newbuf != buffer)
  		LockBuffer(newbuf, BUFFER_LOCK_UNLOCK);
  	LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
--- 2850,2855 ----
*** a/src/backend/access/index/indexam.c
--- b/src/backend/access/index/indexam.c
***************
*** 612,619 **** index_getnext(IndexScanDesc scan, ScanDirection direction)
  				 * any more members.  Otherwise, check for continuation of the
  				 * HOT-chain, and set state for next time.
  				 */
! 				if (IsMVCCSnapshot(scan->xs_snapshot)
! 					&& !IsolationIsSerializable())
  					scan->xs_next_hot = InvalidOffsetNumber;
  				else if (HeapTupleIsHotUpdated(heapTuple))
  				{
--- 612,618 ----
  				 * any more members.  Otherwise, check for continuation of the
  				 * HOT-chain, and set state for next time.
  				 */
! 				if (IsMVCCSnapshot(scan->xs_snapshot))
  					scan->xs_next_hot = InvalidOffsetNumber;
  				else if (HeapTupleIsHotUpdated(heapTuple))
  				{
*** a/src/backend/storage/lmgr/predicate.c
--- b/src/backend/storage/lmgr/predicate.c
***************
*** 155,163 ****
   *							   BlockNumber newblkno);
   *		PredicateLockPageCombine(Relation relation, BlockNumber oldblkno,
   *								 BlockNumber newblkno);
-  *		PredicateLockTupleRowVersionLink(const Relation relation,
-  *										 const HeapTuple oldTuple,
-  *										 const HeapTuple newTuple)
   *		ReleasePredicateLocks(bool isCommit)
   *
   * conflict detection (may also trigger rollback)
--- 155,160 ----
***************
*** 2252,2341 **** PredicateLockTuple(const Relation relation, const HeapTuple tuple)
  	PredicateLockAcquire(&tag);
  }
  
- /*
-  * If the old tuple has any predicate locks, copy them to the new target.
-  *
-  * This is called at an UPDATE, where any predicate locks held on the old
-  * tuple need to be copied to the new tuple, because logically they both
-  * represent the same row. A lock taken before the update must conflict
-  * with anyone locking the same row after the update.
-  */
- void
- PredicateLockTupleRowVersionLink(const Relation relation,
- 								 const HeapTuple oldTuple,
- 								 const HeapTuple newTuple)
- {
- 	PREDICATELOCKTARGETTAG oldtupletag;
- 	PREDICATELOCKTARGETTAG oldpagetag;
- 	PREDICATELOCKTARGETTAG newtupletag;
- 	BlockNumber oldblk,
- 				newblk;
- 	OffsetNumber oldoff,
- 				newoff;
- 	TransactionId oldxmin,
- 				newxmin;
- 
- 	/*
- 	 * Bail out quickly if there are no serializable transactions
- 	 * running.
- 	 *
- 	 * It's safe to do this check without taking any additional
- 	 * locks. Even if a serializable transaction starts concurrently,
- 	 * we know it can't take any SIREAD locks on the modified tuple
- 	 * because the caller is holding the associated buffer page lock.
- 	 * Memory reordering isn't an issue; the memory barrier in the
- 	 * LWLock acquisition guarantees that this read occurs while the
- 	 * buffer page lock is held.
- 	 */
- 	if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
- 		return;
- 
- 	oldblk = ItemPointerGetBlockNumber(&(oldTuple->t_self));
- 	oldoff = ItemPointerGetOffsetNumber(&(oldTuple->t_self));
- 	oldxmin = HeapTupleHeaderGetXmin(oldTuple->t_data);
- 
- 	newblk = ItemPointerGetBlockNumber(&(newTuple->t_self));
- 	newoff = ItemPointerGetOffsetNumber(&(newTuple->t_self));
- 	newxmin = HeapTupleHeaderGetXmin(newTuple->t_data);
- 
- 	SET_PREDICATELOCKTARGETTAG_TUPLE(oldtupletag,
- 									 relation->rd_node.dbNode,
- 									 relation->rd_id,
- 									 oldblk,
- 									 oldoff,
- 									 oldxmin);
- 
- 	SET_PREDICATELOCKTARGETTAG_PAGE(oldpagetag,
- 									relation->rd_node.dbNode,
- 									relation->rd_id,
- 									oldblk);
- 
- 	SET_PREDICATELOCKTARGETTAG_TUPLE(newtupletag,
- 									 relation->rd_node.dbNode,
- 									 relation->rd_id,
- 									 newblk,
- 									 newoff,
- 									 newxmin);
- 
- 	/*
- 	 * A page-level lock on the page containing the old tuple counts too.
- 	 * Anyone holding a lock on the page is logically holding a lock on the
- 	 * old tuple, so we need to acquire a lock on his behalf on the new tuple
- 	 * too. However, if the new tuple is on the same page as the old one, the
- 	 * old page-level lock already covers the new tuple.
- 	 *
- 	 * A relation-level lock always covers both tuple versions, so we don't
- 	 * need to worry about those here.
- 	 */
- 	LWLockAcquire(SerializablePredicateLockListLock, LW_EXCLUSIVE);
- 
- 	TransferPredicateLocksToNewTarget(oldtupletag, newtupletag, false);
- 	if (newblk != oldblk)
- 		TransferPredicateLocksToNewTarget(oldpagetag, newtupletag, false);
- 
- 	LWLockRelease(SerializablePredicateLockListLock);
- }
- 
  
  /*
   *		DeleteLockTarget
--- 2249,2254 ----
***************
*** 2650,2658 **** PredicateLockPageSplit(const Relation relation, const BlockNumber oldblkno,
  
  	/*
  	 * Bail out quickly if there are no serializable transactions
! 	 * running. As with PredicateLockTupleRowVersionLink, it's safe to
! 	 * check this without taking locks because the caller is holding
! 	 * the buffer page lock.
  	 */
  	if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
  		return;
--- 2563,2577 ----
  
  	/*
  	 * Bail out quickly if there are no serializable transactions
! 	 * running.
! 	 *
! 	 * It's safe to do this check without taking any additional
! 	 * locks. Even if a serializable transaction starts concurrently,
! 	 * we know it can't take any SIREAD locks on the page being split
! 	 * because the caller is holding the associated buffer page lock.
! 	 * Memory reordering isn't an issue; the memory barrier in the
! 	 * LWLock acquisition guarantees that this read occurs while the
! 	 * buffer page lock is held.
  	 */
  	if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
  		return;
***************
*** 3955,3963 **** OnConflict_CheckForSerializationFailure(const SERIALIZABLEXACT *reader,
  	 * before the reader acquired its snapshot.  (That is, the reader must not
  	 * have been concurrent with the out-conflict transaction.)
  	 */
! 	if (!failure && !SxactIsCommitted(writer))
  	{
! 		if (SxactHasSummaryConflictOut(reader))
  		{
  			failure = true;
  			conflict = NULL;
--- 3874,3882 ----
  	 * before the reader acquired its snapshot.  (That is, the reader must not
  	 * have been concurrent with the out-conflict transaction.)
  	 */
! 	if (!failure)
  	{
! 		if (SxactHasSummaryConflictOut(writer))
  		{
  			failure = true;
  			conflict = NULL;
***************
*** 3969,3979 **** OnConflict_CheckForSerializationFailure(const SERIALIZABLEXACT *reader,
  							 offsetof(RWConflictData, outLink));
  		while (conflict)
  		{
! 			if ((reader == conflict->sxactIn && SxactIsCommitted(reader))
! 				|| (SxactIsCommitted(conflict->sxactIn)
! 					&& !SxactIsCommitted(reader)
! 					&& (!SxactIsReadOnly(reader)
! 						|| conflict->sxactIn->commitSeqNo <= reader->SeqNo.lastCommitBeforeSnapshot)))
  			{
  				failure = true;
  				break;
--- 3888,3900 ----
  							 offsetof(RWConflictData, outLink));
  		while (conflict)
  		{
! 			if (SxactIsCommitted(conflict->sxactIn)
! 				&& (!SxactIsCommitted(reader)
! 					|| conflict->sxactIn->commitSeqNo <= reader->commitSeqNo)
! 				&& (!SxactIsCommitted(writer)
! 					|| conflict->sxactIn->commitSeqNo <= writer->commitSeqNo)
! 				&& (!SxactIsReadOnly(reader)
! 					|| conflict->sxactIn->commitSeqNo <= reader->SeqNo.lastCommitBeforeSnapshot))
  			{
  				failure = true;
  				break;
*** a/src/include/storage/predicate.h
--- b/src/include/storage/predicate.h
***************
*** 47,53 **** extern void RegisterPredicateLockingXid(const TransactionId xid);
  extern void PredicateLockRelation(const Relation relation);
  extern void PredicateLockPage(const Relation relation, const BlockNumber blkno);
  extern void PredicateLockTuple(const Relation relation, const HeapTuple tuple);
- extern void PredicateLockTupleRowVersionLink(const Relation relation, const HeapTuple oldTuple, const HeapTuple newTuple);
  extern void PredicateLockPageSplit(const Relation relation, const BlockNumber oldblkno, const BlockNumber newblkno);
  extern void PredicateLockPageCombine(const Relation relation, const BlockNumber oldblkno, const BlockNumber newblkno);
  extern void ReleasePredicateLocks(const bool isCommit);
--- 47,52 ----
*** a/src/test/isolation/expected/multiple-row-versions.out
--- b/src/test/isolation/expected/multiple-row-versions.out
***************
*** 19,24 **** id             txt
  1                             
  step c4:  COMMIT; 
  step c3:  COMMIT; 
- ERROR:  could not serialize access due to read/write dependencies among transactions
  step wz1:  UPDATE t SET txt = 'a' WHERE id = 1; 
  step c1:  COMMIT; 
--- 19,24 ----
  1                             
  step c4:  COMMIT; 
  step c3:  COMMIT; 
  step wz1:  UPDATE t SET txt = 'a' WHERE id = 1; 
+ ERROR:  could not serialize access due to read/write dependencies among transactions
  step c1:  COMMIT; 
*** a/src/test/isolation/specs/multiple-row-versions.spec
--- b/src/test/isolation/specs/multiple-row-versions.spec
***************
*** 1,8 ****
  # Multiple Row Versions test
  #
! # This test is designed to ensure that predicate locks taken on one version
! # of a row are detected as conflicts when a later version of the row is
! # updated or deleted by a transaction concurrent to the reader.
  #
  # Due to long permutation setup time, we are only testing one specific
  # permutation, which should get a serialization error.
--- 1,7 ----
  # Multiple Row Versions test
  #
! # This test is designed to cover some code paths which only occur with
! # four or more transactions interacting with particular timings.
  #
  # Due to long permutation setup time, we are only testing one specific
  # permutation, which should get a serialization error.
