diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 8975191..9086c60 100644
*** a/src/backend/access/heap/heapam.c
--- b/src/backend/access/heap/heapam.c
*************** static XLogRecPtr log_heap_update(Relati
*** 83,88 ****
--- 83,89 ----
  				bool all_visible_cleared, bool new_all_visible_cleared);
  static bool HeapSatisfiesHOTUpdate(Relation relation, Bitmapset *hot_attrs,
  					   HeapTuple oldtup, HeapTuple newtup);
+ static bool HeapSatisfiesLockersVisible(HeapTupleHeader tuple, Snapshot snapshot);
  
  
  /* ----------------------------------------------------------------
*************** simple_heap_insert(Relation relation, He
*** 2033,2040 ****
   *	update_xmax - output parameter, used only for failure case (see below)
   *	cid - delete command ID (used for visibility test, and stored into
   *		cmax if successful)
-  *	crosscheck - if not InvalidSnapshot, also check tuple against this
   *	wait - true if should wait for any conflicting update to commit/abort
   *
   * Normal, successful return value is HeapTupleMayBeUpdated, which
   * actually means we did delete it.  Failure return codes are
--- 2034,2042 ----
   *	update_xmax - output parameter, used only for failure case (see below)
   *	cid - delete command ID (used for visibility test, and stored into
   *		cmax if successful)
   *	wait - true if should wait for any conflicting update to commit/abort
+  *	lockcheck_snapshot - if not NULL, report the tuple as updated if it
+  *		was locked by a transaction not visible under this snapshot
   *
   * Normal, successful return value is HeapTupleMayBeUpdated, which
   * actually means we did delete it.  Failure return codes are
*************** simple_heap_insert(Relation relation, He
*** 2049,2055 ****
  HTSU_Result
  heap_delete(Relation relation, ItemPointer tid,
  			ItemPointer ctid, TransactionId *update_xmax,
! 			CommandId cid, Snapshot crosscheck, bool wait)
  {
  	HTSU_Result result;
  	TransactionId xid = GetCurrentTransactionId();
--- 2051,2057 ----
  HTSU_Result
  heap_delete(Relation relation, ItemPointer tid,
  			ItemPointer ctid, TransactionId *update_xmax,
! 			CommandId cid, bool wait, Snapshot lockcheck_snapshot)
  {
  	HTSU_Result result;
  	TransactionId xid = GetCurrentTransactionId();
*************** l1:
*** 2170,2181 ****
  		else
  			result = HeapTupleUpdated;
  	}
! 
! 	if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
  	{
! 		/* Perform additional check for serializable RI updates */
! 		if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer))
! 			result = HeapTupleUpdated;
  	}
  
  	if (result != HeapTupleMayBeUpdated)
--- 2172,2183 ----
  		else
  			result = HeapTupleUpdated;
  	}
! 	
! 	/* Verify visibility of locking transactions. */
! 	if ((result == HeapTupleMayBeUpdated) &&
! 		!HeapSatisfiesLockersVisible(tp.t_data, lockcheck_snapshot))
  	{
! 		result = HeapTupleUpdated;
  	}
  
  	if (result != HeapTupleMayBeUpdated)
*************** l1:
*** 2183,2189 ****
  		Assert(result == HeapTupleSelfUpdated ||
  			   result == HeapTupleUpdated ||
  			   result == HeapTupleBeingUpdated);
! 		Assert(!(tp.t_data->t_infomask & HEAP_XMAX_INVALID));
  		*ctid = tp.t_data->t_ctid;
  		*update_xmax = HeapTupleHeaderGetXmax(tp.t_data);
  		UnlockReleaseBuffer(buffer);
--- 2185,2192 ----
  		Assert(result == HeapTupleSelfUpdated ||
  			   result == HeapTupleUpdated ||
  			   result == HeapTupleBeingUpdated);
! 		Assert(!(tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
! 			   (tp.t_data->t_infomask & HEAP_IS_LOCKED));
  		*ctid = tp.t_data->t_ctid;
  		*update_xmax = HeapTupleHeaderGetXmax(tp.t_data);
  		UnlockReleaseBuffer(buffer);
*************** simple_heap_delete(Relation relation, It
*** 2313,2320 ****
  
  	result = heap_delete(relation, tid,
  						 &update_ctid, &update_xmax,
! 						 GetCurrentCommandId(true), InvalidSnapshot,
! 						 true /* wait for commit */ );
  	switch (result)
  	{
  		case HeapTupleSelfUpdated:
--- 2316,2324 ----
  
  	result = heap_delete(relation, tid,
  						 &update_ctid, &update_xmax,
! 						 GetCurrentCommandId(true),
! 						 true /* wait for commit */ ,
! 						 InvalidSnapshot);
  	switch (result)
  	{
  		case HeapTupleSelfUpdated:
*************** simple_heap_delete(Relation relation, It
*** 2349,2355 ****
   *	update_xmax - output parameter, used only for failure case (see below)
   *	cid - update command ID (used for visibility test, and stored into
   *		cmax/cmin if successful)
!  *	crosscheck - if not InvalidSnapshot, also check old tuple against this
   *	wait - true if should wait for any conflicting update to commit/abort
   *
   * Normal, successful return value is HeapTupleMayBeUpdated, which
--- 2353,2360 ----
   *	update_xmax - output parameter, used only for failure case (see below)
   *	cid - update command ID (used for visibility test, and stored into
   *		cmax/cmin if successful)
!  *	lockcheck_snapshot - if not NULL, report the tuple as updated if it
!  *		was locked by a transaction not visible under this snapshot
   *	wait - true if should wait for any conflicting update to commit/abort
   *
   * Normal, successful return value is HeapTupleMayBeUpdated, which
*************** simple_heap_delete(Relation relation, It
*** 2371,2377 ****
  HTSU_Result
  heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
  			ItemPointer ctid, TransactionId *update_xmax,
! 			CommandId cid, Snapshot crosscheck, bool wait)
  {
  	HTSU_Result result;
  	TransactionId xid = GetCurrentTransactionId();
--- 2376,2382 ----
  HTSU_Result
  heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
  			ItemPointer ctid, TransactionId *update_xmax,
! 			CommandId cid, bool wait, Snapshot lockcheck_snapshot)
  {
  	HTSU_Result result;
  	TransactionId xid = GetCurrentTransactionId();
*************** l2:
*** 2523,2541 ****
  			result = HeapTupleUpdated;
  	}
  
! 	if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated)
  	{
! 		/* Perform additional check for serializable RI updates */
! 		if (!HeapTupleSatisfiesVisibility(&oldtup, crosscheck, buffer))
! 			result = HeapTupleUpdated;
  	}
  
  	if (result != HeapTupleMayBeUpdated)
  	{
  		Assert(result == HeapTupleSelfUpdated ||
  			   result == HeapTupleUpdated ||
  			   result == HeapTupleBeingUpdated);
! 		Assert(!(oldtup.t_data->t_infomask & HEAP_XMAX_INVALID));
  		*ctid = oldtup.t_data->t_ctid;
  		*update_xmax = HeapTupleHeaderGetXmax(oldtup.t_data);
  		UnlockReleaseBuffer(buffer);
--- 2528,2548 ----
  			result = HeapTupleUpdated;
  	}
  
! 	/* Verify visibility of locking transactions. */
! 	if ((result == HeapTupleMayBeUpdated) &&
! 		!HeapSatisfiesLockersVisible(oldtup.t_data, lockcheck_snapshot))
  	{
! 		result = HeapTupleUpdated;
  	}
  
+ 
  	if (result != HeapTupleMayBeUpdated)
  	{
  		Assert(result == HeapTupleSelfUpdated ||
  			   result == HeapTupleUpdated ||
  			   result == HeapTupleBeingUpdated);
! 		Assert(!(oldtup.t_data->t_infomask & HEAP_XMAX_INVALID) ||
! 			   (oldtup.t_data->t_infomask & HEAP_IS_LOCKED));
  		*ctid = oldtup.t_data->t_ctid;
  		*update_xmax = HeapTupleHeaderGetXmax(oldtup.t_data);
  		UnlockReleaseBuffer(buffer);
*************** HeapSatisfiesHOTUpdate(Relation relation
*** 2961,2966 ****
--- 2968,3045 ----
  	return true;
  }
  
+ 
+ /*
+  * Returns false if one of the tuple's lockers committed but
+  * aren't visible according to lockcheck_snapshot.
+  * Assumes that all locking transaction either committed or aborted,
+  * but aren't still in progress.
+  */
+ static bool
+ HeapSatisfiesLockersVisible(HeapTupleHeader tuple, Snapshot lockcheck_snapshot)
+ {
+ 	if (lockcheck_snapshot == InvalidSnapshot)
+ 		return true;
+ 	
+ 	if (tuple->t_infomask & HEAP_IS_LOCKED)
+ 	{
+ 		/* If the tuple was locked, we now check whether the locking
+ 		 * transaction(s) are visible under lockcheck_snapshot. If
+ 		 * they aren't, we pretent the tuple was updated.
+ 		 */
+ 
+ 		if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
+ 		{
+ 			TransactionId* xids;
+ 			int xids_l = GetMultiXactIdMembers(HeapTupleHeaderGetXmax(tuple), &xids);
+ 
+ 			if (xids_l < 1) {
+ 				/* The multi xact either is too old to be inspected or doesn't contain members.
+ 				 * The second case is probably impossible, but even if not it doesn't pose
+ 				 * any problem.
+ 				 * In the first case, we have to trust that all xids that were contained in
+ 				 * the xact are in fact visible under lockcheck_snapshot. Currently this
+ 				 * is always the case, since lockcheck_snapshot is always the transaction's
+ 				 * serializable snapshot, and we call MultiXactIdSetOldestVisible() before
+ 				 * acquireing that snapshot.
+ 				 */
+ 				return true;
+ 			}
+ 			else
+ 			{
+ 				int i;
+ 				for (i = 0; i < xids_l; ++i)
+ 				{
+ 					/* We expect to be called after the locking transactions' fates have been decided. */
+ 					Assert(!TransactionIdIsInProgress(xids[i]));
+ 					
+ 					if (!TransactionIdDidAbort(xids[i]) &&
+ 					    XidInMVCCSnapshot(xids[i], lockcheck_snapshot))
+ 					{
+ 						/* Ups. Non-aborted, invisible locker */
+ 						return false;
+ 					}
+ 				}
+ 				return true;
+ 			}
+ 		}
+ 		else
+ 		{
+ 			/* We expect to be called after the locking transaction's fate has been decided. */
+ 			Assert(!TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)));
+ 			
+ 			/* Locker must either be visible or have aborted */
+ 			return TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)) ||
+ 				   !XidInMVCCSnapshot(HeapTupleHeaderGetXmax(tuple), lockcheck_snapshot);
+ 		}
+ 	}
+ 	else
+ 	{
+ 		/* Tuple wasn't locked */
+ 		return true;
+ 	}
+ }
+ 
  /*
   *	simple_heap_update - replace a tuple
   *
*************** simple_heap_update(Relation relation, It
*** 2978,2985 ****
  
  	result = heap_update(relation, otid, tup,
  						 &update_ctid, &update_xmax,
! 						 GetCurrentCommandId(true), InvalidSnapshot,
! 						 true /* wait for commit */ );
  	switch (result)
  	{
  		case HeapTupleSelfUpdated:
--- 3057,3064 ----
  
  	result = heap_update(relation, otid, tup,
  						 &update_ctid, &update_xmax,
! 						 GetCurrentCommandId(true),
! 						 true /* wait for commit */, InvalidSnapshot);
  	switch (result)
  	{
  		case HeapTupleSelfUpdated:
*************** simple_heap_update(Relation relation, It
*** 3013,3018 ****
--- 3092,3100 ----
   *		tuple's cmax if lock is successful)
   *	mode: indicates if shared or exclusive tuple lock is desired
   *	nowait: if true, ereport rather than blocking if lock not available
+  *	lockcheck_snapshot: if not NULL, report the tuple as updated if it
+  *						was locked by a transaction not visible under
+  *						this snapshot
   *
   * Output parameters:
   *	*tuple: all fields filled in
*************** simple_heap_update(Relation relation, It
*** 3066,3072 ****
  HTSU_Result
  heap_lock_tuple(Relation relation, HeapTuple tuple, Buffer *buffer,
  				ItemPointer ctid, TransactionId *update_xmax,
! 				CommandId cid, LockTupleMode mode, bool nowait)
  {
  	HTSU_Result result;
  	ItemPointer tid = &(tuple->t_self);
--- 3148,3155 ----
  HTSU_Result
  heap_lock_tuple(Relation relation, HeapTuple tuple, Buffer *buffer,
  				ItemPointer ctid, TransactionId *update_xmax,
! 				CommandId cid, LockTupleMode mode, bool nowait,
! 				Snapshot lockcheck_snapshot)
  {
  	HTSU_Result result;
  	ItemPointer tid = &(tuple->t_self);
*************** l3:
*** 3246,3256 ****
  		else
  			result = HeapTupleUpdated;
  	}
  
  	if (result != HeapTupleMayBeUpdated)
  	{
  		Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated);
! 		Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID));
  		*ctid = tuple->t_data->t_ctid;
  		*update_xmax = HeapTupleHeaderGetXmax(tuple->t_data);
  		LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
--- 3329,3347 ----
  		else
  			result = HeapTupleUpdated;
  	}
+ 	
+ 	/* Verify visibility of locking transactions */
+ 	if ((result == HeapTupleMayBeUpdated) &&
+ 		!HeapSatisfiesLockersVisible(tuple->t_data, lockcheck_snapshot))
+ 	{
+ 		result = HeapTupleUpdated;
+ 	}
  
  	if (result != HeapTupleMayBeUpdated)
  	{
  		Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated);
! 		Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
! 			   (tuple->t_data->t_infomask & HEAP_IS_LOCKED));
  		*ctid = tuple->t_data->t_ctid;
  		*update_xmax = HeapTupleHeaderGetXmax(tuple->t_data);
  		LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index b5bb311..47ff5de 100644
*** a/src/backend/access/transam/multixact.c
--- b/src/backend/access/transam/multixact.c
*************** static MemoryContext MXactContext = NULL
*** 211,217 ****
  #endif
  
  /* internal MultiXactId management */
- static void MultiXactIdSetOldestVisible(void);
  static MultiXactId CreateMultiXactId(int nxids, TransactionId *xids);
  static void RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
  				   int nxids, TransactionId *xids);
--- 211,216 ----
*************** MultiXactIdSetOldestMember(void)
*** 531,537 ****
   * there is no live transaction, now or later, that can be a member of any
   * MultiXactId older than the OldestVisibleMXactId we compute here.
   */
! static void
  MultiXactIdSetOldestVisible(void)
  {
  	if (!MultiXactIdIsValid(OldestVisibleMXactId[MyBackendId]))
--- 530,536 ----
   * there is no live transaction, now or later, that can be a member of any
   * MultiXactId older than the OldestVisibleMXactId we compute here.
   */
! void
  MultiXactIdSetOldestVisible(void)
  {
  	if (!MultiXactIdIsValid(OldestVisibleMXactId[MyBackendId]))
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 2cbc192..120ea60 100644
*** a/src/backend/commands/trigger.c
--- b/src/backend/commands/trigger.c
*************** ltrmark:;
*** 2347,2353 ****
  		test = heap_lock_tuple(relation, &tuple, &buffer,
  							   &update_ctid, &update_xmax,
  							   estate->es_output_cid,
! 							   LockTupleExclusive, false);
  		switch (test)
  		{
  			case HeapTupleSelfUpdated:
--- 2347,2355 ----
  		test = heap_lock_tuple(relation, &tuple, &buffer,
  							   &update_ctid, &update_xmax,
  							   estate->es_output_cid,
! 							   LockTupleExclusive, false,
! 							   IsXactIsoLevelSerializable ? estate->es_snapshot :
! 															InvalidSnapshot);
  		switch (test)
  		{
  			case HeapTupleSelfUpdated:
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index d299310..fd5ed3c 100644
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
*************** EvalPlanQualFetch(EState *estate, Relati
*** 1518,1527 ****
  			/*
  			 * This is a live tuple, so now try to lock it.
  			 */
  			test = heap_lock_tuple(relation, &tuple, &buffer,
  								   &update_ctid, &update_xmax,
  								   estate->es_output_cid,
! 								   lockmode, false);
  			/* We now have two pins on the buffer, get rid of one */
  			ReleaseBuffer(buffer);
  
--- 1518,1530 ----
  			/*
  			 * This is a live tuple, so now try to lock it.
  			 */
+ 			Assert(!IsXactIsoLevelSerializable || (estate->es_snapshot != InvalidSnapshot));
  			test = heap_lock_tuple(relation, &tuple, &buffer,
  								   &update_ctid, &update_xmax,
  								   estate->es_output_cid,
! 								   lockmode, false,
! 								   IsXactIsoLevelSerializable ? estate->es_snapshot :
! 																InvalidSnapshot);
  			/* We now have two pins on the buffer, get rid of one */
  			ReleaseBuffer(buffer);
  
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 00f0562..b771b19 100644
*** a/src/backend/executor/nodeLockRows.c
--- b/src/backend/executor/nodeLockRows.c
*************** lnext:
*** 71,76 ****
--- 71,77 ----
  		ItemPointerData update_ctid;
  		TransactionId update_xmax;
  		LockTupleMode lockmode;
+ 		Snapshot lockcheck_snapshot = InvalidSnapshot;
  		HTSU_Result test;
  		HeapTuple	copyTuple;
  
*************** lnext:
*** 110,123 ****
  
  		/* okay, try to lock the tuple */
  		if (erm->markType == ROW_MARK_EXCLUSIVE)
  			lockmode = LockTupleExclusive;
  		else
  			lockmode = LockTupleShared;
  
  		test = heap_lock_tuple(erm->relation, &tuple, &buffer,
  							   &update_ctid, &update_xmax,
  							   estate->es_output_cid,
! 							   lockmode, erm->noWait);
  		ReleaseBuffer(buffer);
  		switch (test)
  		{
--- 111,134 ----
  
  		/* okay, try to lock the tuple */
  		if (erm->markType == ROW_MARK_EXCLUSIVE)
+ 		{
  			lockmode = LockTupleExclusive;
+ 			/* In serializable mode, acquireing an exclusive lock
+ 			 * requires seeing all other lockers.
+ 			 */
+ 			if (IsXactIsoLevelSerializable)
+ 				lockcheck_snapshot = estate->es_snapshot;
+ 		}
  		else
+ 		{
  			lockmode = LockTupleShared;
+ 		}
  
  		test = heap_lock_tuple(erm->relation, &tuple, &buffer,
  							   &update_ctid, &update_xmax,
  							   estate->es_output_cid,
! 							   lockmode, erm->noWait,
! 							   lockcheck_snapshot);
  		ReleaseBuffer(buffer);
  		switch (test)
  		{
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 7856b66..ecc75f9 100644
*** a/src/backend/executor/nodeModifyTable.c
--- b/src/backend/executor/nodeModifyTable.c
*************** ldelete:;
*** 316,323 ****
  	result = heap_delete(resultRelationDesc, tupleid,
  						 &update_ctid, &update_xmax,
  						 estate->es_output_cid,
! 						 estate->es_crosscheck_snapshot,
! 						 true /* wait for commit */ );
  	switch (result)
  	{
  		case HeapTupleSelfUpdated:
--- 316,324 ----
  	result = heap_delete(resultRelationDesc, tupleid,
  						 &update_ctid, &update_xmax,
  						 estate->es_output_cid,
! 						 true, /* wait for commit */
! 						 IsXactIsoLevelSerializable ? estate->es_snapshot :
! 													  InvalidSnapshot);
  	switch (result)
  	{
  		case HeapTupleSelfUpdated:
*************** lreplace:;
*** 504,511 ****
  	result = heap_update(resultRelationDesc, tupleid, tuple,
  						 &update_ctid, &update_xmax,
  						 estate->es_output_cid,
! 						 estate->es_crosscheck_snapshot,
! 						 true /* wait for commit */ );
  	switch (result)
  	{
  		case HeapTupleSelfUpdated:
--- 505,513 ----
  	result = heap_update(resultRelationDesc, tupleid, tuple,
  						 &update_ctid, &update_xmax,
  						 estate->es_output_cid,
! 						 true, /* wait for commit */ 
! 						 IsXactIsoLevelSerializable ? estate->es_snapshot :
! 													  InvalidSnapshot);
  	switch (result)
  	{
  		case HeapTupleSelfUpdated:
diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c
index 59a27de..d4dc5ca 100644
*** a/src/backend/utils/time/snapmgr.c
--- b/src/backend/utils/time/snapmgr.c
***************
*** 27,32 ****
--- 27,33 ----
  
  #include "access/transam.h"
  #include "access/xact.h"
+ #include "access/multixact.h"
  #include "storage/proc.h"
  #include "storage/procarray.h"
  #include "utils/memutils.h"
*************** GetTransactionSnapshot(void)
*** 125,130 ****
--- 126,139 ----
  	if (!FirstSnapshotSet)
  	{
  		Assert(RegisteredSnapshots == 0);
+ 		
+ 		/* Must store the oldest visible multi xact *before* taking the
+ 		 * serializable snapshot. Otherwise HeapSatisfiesLockersVisible in
+ 		 * heapam.c will fail, since it'll be unable to inspect a multi xact
+ 		 * that might contains xids invisible to the serializable snapshot.
+ 		 */
+ 		if (IsXactIsoLevelSerializable)
+ 			MultiXactIdSetOldestVisible();
  
  		CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData);
  		FirstSnapshotSet = true;
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
index 4f3630c..302a571 100644
*** a/src/backend/utils/time/tqual.c
--- b/src/backend/utils/time/tqual.c
*************** SnapshotData SnapshotSelfData = {HeapTup
*** 72,80 ****
  SnapshotData SnapshotAnyData = {HeapTupleSatisfiesAny};
  SnapshotData SnapshotToastData = {HeapTupleSatisfiesToast};
  
- /* local functions */
- static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
- 
  
  /*
   * SetHintBits()
--- 72,77 ----
*************** HeapTupleSatisfiesVacuum(HeapTupleHeader
*** 1253,1259 ****
   * by this function.  This is OK for current uses, because we actually only
   * apply this for known-committed XIDs.
   */
! static bool
  XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
  {
  	uint32		i;
--- 1250,1256 ----
   * by this function.  This is OK for current uses, because we actually only
   * apply this for known-committed XIDs.
   */
! bool
  XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
  {
  	uint32		i;
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index f963146..a17d62c 100644
*** a/src/include/access/heapam.h
--- b/src/include/access/heapam.h
*************** extern Oid heap_insert(Relation relation
*** 98,112 ****
  			int options, BulkInsertState bistate);
  extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
  			ItemPointer ctid, TransactionId *update_xmax,
! 			CommandId cid, Snapshot crosscheck, bool wait);
  extern HTSU_Result heap_update(Relation relation, ItemPointer otid,
  			HeapTuple newtup,
  			ItemPointer ctid, TransactionId *update_xmax,
! 			CommandId cid, Snapshot crosscheck, bool wait);
  extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
  				Buffer *buffer, ItemPointer ctid,
  				TransactionId *update_xmax, CommandId cid,
! 				LockTupleMode mode, bool nowait);
  extern void heap_inplace_update(Relation relation, HeapTuple tuple);
  extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
  				  Buffer buf);
--- 98,113 ----
  			int options, BulkInsertState bistate);
  extern HTSU_Result heap_delete(Relation relation, ItemPointer tid,
  			ItemPointer ctid, TransactionId *update_xmax,
! 			CommandId cid, bool wait, Snapshot crosscheck);
  extern HTSU_Result heap_update(Relation relation, ItemPointer otid,
  			HeapTuple newtup,
  			ItemPointer ctid, TransactionId *update_xmax,
! 			CommandId cid, bool wait, Snapshot crosscheck);
  extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
  				Buffer *buffer, ItemPointer ctid,
  				TransactionId *update_xmax, CommandId cid,
! 				LockTupleMode mode, bool nowait,
! 				Snapshot lockcheck_snapshot);
  extern void heap_inplace_update(Relation relation, HeapTuple tuple);
  extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
  				  Buffer buf);
diff --git a/src/include/access/multixact.h b/src/include/access/multixact.h
index 5910465..4769bca 100644
*** a/src/include/access/multixact.h
--- b/src/include/access/multixact.h
*************** extern bool MultiXactIdIsCurrent(MultiXa
*** 49,54 ****
--- 49,55 ----
  extern void MultiXactIdWait(MultiXactId multi);
  extern bool ConditionalMultiXactIdWait(MultiXactId multi);
  extern void MultiXactIdSetOldestMember(void);
+ extern void MultiXactIdSetOldestVisible(void);
  extern int	GetMultiXactIdMembers(MultiXactId multi, TransactionId **xids);
  
  extern void AtEOXact_MultiXact(void);
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index e85e820..fa386e7 100644
*** a/src/include/utils/tqual.h
--- b/src/include/utils/tqual.h
*************** extern PGDLLIMPORT SnapshotData Snapshot
*** 41,46 ****
--- 41,48 ----
  #define IsMVCCSnapshot(snapshot)  \
  	((snapshot)->satisfies == HeapTupleSatisfiesMVCC)
  
+ bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
+ 
  /*
   * HeapTupleSatisfiesVisibility
   *		True iff heap tuple satisfies a time qual.
