Index: src/backend/access/heap/heapam.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v
retrieving revision 1.200.2.2
diff -c -r1.200.2.2 heapam.c
*** src/backend/access/heap/heapam.c	22 Nov 2005 18:23:03 -0000	1.200.2.2
--- src/backend/access/heap/heapam.c	17 Nov 2006 17:37:29 -0000
***************
*** 2080,2085 ****
--- 2080,2087 ----
  	ItemId		lp;
  	PageHeader	dp;
  	TransactionId xid;
+ 	TransactionId xmax;
+ 	uint16		old_infomask;
  	uint16		new_infomask;
  	LOCKMODE	tuple_lock_type;
  	bool		have_tuple_lock = false;
***************
*** 2119,2124 ****
--- 2121,2145 ----
  		LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
  
  		/*
+ 		 * If we wish to acquire share lock, and the tuple is already
+ 		 * share-locked by a multixact that includes any subtransaction of the
+ 		 * current top transaction, then we effectively hold the desired lock
+ 		 * already.  We *must* succeed without trying to take the tuple lock,
+ 		 * else we will deadlock against anyone waiting to acquire exclusive
+ 		 * lock.  We don't need to make any state changes in this case.
+ 		 */
+ 		if (mode == LockTupleShared &&
+ 			(infomask & HEAP_XMAX_IS_MULTI) &&
+ 			MultiXactIdIsCurrent((MultiXactId) xwait))
+ 		{
+ 			Assert(infomask & HEAP_XMAX_SHARED_LOCK);
+ 			/* Probably can't hold tuple lock here, but may as well check */
+ 			if (have_tuple_lock)
+ 				UnlockTuple(relation, tid, tuple_lock_type);
+ 			return HeapTupleMayBeUpdated;
+ 		}
+ 
+ 		/*
  		 * Acquire tuple lock to establish our priority for the tuple.
  		 * LockTuple will release us when we are next-in-line for the tuple.
  		 * We must do this even if we are share-locking.
***************
*** 2256,2280 ****
  	}
  
  	/*
  	 * Compute the new xmax and infomask to store into the tuple.  Note we do
  	 * not modify the tuple just yet, because that would leave it in the wrong
  	 * state if multixact.c elogs.
  	 */
  	xid = GetCurrentTransactionId();
  
! 	new_infomask = tuple->t_data->t_infomask;
! 
! 	new_infomask &= ~(HEAP_XMAX_COMMITTED |
! 					  HEAP_XMAX_INVALID |
! 					  HEAP_XMAX_IS_MULTI |
! 					  HEAP_IS_LOCKED |
! 					  HEAP_MOVED);
  
  	if (mode == LockTupleShared)
  	{
- 		TransactionId xmax = HeapTupleHeaderGetXmax(tuple->t_data);
- 		uint16		old_infomask = tuple->t_data->t_infomask;
- 
  		/*
  		 * If this is the first acquisition of a shared lock in the current
  		 * transaction, set my per-backend OldestMemberMXactId setting. We can
--- 2277,2325 ----
  	}
  
  	/*
+ 	 * We might already hold the desired lock (or stronger), possibly under
+ 	 * a different subtransaction of the current top transaction.  If so,
+ 	 * there is no need to change state or issue a WAL record.  We already
+ 	 * handled the case where this is true for xmax being a MultiXactId,
+ 	 * so now check for cases where it is a plain TransactionId.
+ 	 *
+ 	 * Note in particular that this covers the case where we already hold
+ 	 * exclusive lock on the tuple and the caller only wants shared lock.
+ 	 * It would certainly not do to give up the exclusive lock.
+ 	 */
+ 	xmax = HeapTupleHeaderGetXmax(tuple->t_data);
+ 	old_infomask = tuple->t_data->t_infomask;
+ 
+ 	if (!(old_infomask & (HEAP_XMAX_INVALID |
+ 						  HEAP_XMAX_COMMITTED |
+ 						  HEAP_XMAX_IS_MULTI)) &&
+ 		(mode == LockTupleShared ?
+ 		 (old_infomask & HEAP_IS_LOCKED) :
+ 		 (old_infomask & HEAP_XMAX_EXCL_LOCK)) &&
+ 		TransactionIdIsCurrentTransactionId(xmax))
+ 	{
+ 		LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
+ 		/* Probably can't hold tuple lock here, but may as well check */
+ 		if (have_tuple_lock)
+ 			UnlockTuple(relation, tid, tuple_lock_type);
+ 		return HeapTupleMayBeUpdated;
+ 	}
+ 
+ 	/*
  	 * Compute the new xmax and infomask to store into the tuple.  Note we do
  	 * not modify the tuple just yet, because that would leave it in the wrong
  	 * state if multixact.c elogs.
  	 */
  	xid = GetCurrentTransactionId();
  
! 	new_infomask = old_infomask & ~(HEAP_XMAX_COMMITTED |
! 									HEAP_XMAX_INVALID |
! 									HEAP_XMAX_IS_MULTI |
! 									HEAP_IS_LOCKED |
! 									HEAP_MOVED);
  
  	if (mode == LockTupleShared)
  	{
  		/*
  		 * If this is the first acquisition of a shared lock in the current
  		 * transaction, set my per-backend OldestMemberMXactId setting. We can
***************
*** 2315,2346 ****
  			}
  			else if (TransactionIdIsInProgress(xmax))
  			{
! 				if (TransactionIdEquals(xmax, xid))
! 				{
! 					/*
! 					 * If the old locker is ourselves, we'll just mark the
! 					 * tuple again with our own TransactionId.	However we
! 					 * have to consider the possibility that we had exclusive
! 					 * rather than shared lock before --- if so, be careful to
! 					 * preserve the exclusivity of the lock.
! 					 */
! 					if (!(old_infomask & HEAP_XMAX_SHARED_LOCK))
! 					{
! 						new_infomask &= ~HEAP_XMAX_SHARED_LOCK;
! 						new_infomask |= HEAP_XMAX_EXCL_LOCK;
! 						mode = LockTupleExclusive;
! 					}
! 				}
! 				else
! 				{
! 					/*
! 					 * If the Xmax is a valid TransactionId, then we need to
! 					 * create a new MultiXactId that includes both the old
! 					 * locker and our own TransactionId.
! 					 */
! 					xid = MultiXactIdCreate(xmax, xid);
! 					new_infomask |= HEAP_XMAX_IS_MULTI;
! 				}
  			}
  			else
  			{
--- 2360,2372 ----
  			}
  			else if (TransactionIdIsInProgress(xmax))
  			{
! 				/*
! 				 * If the XMAX is a valid TransactionId, then we need to
! 				 * create a new MultiXactId that includes both the old
! 				 * locker and our own TransactionId.
! 				 */
! 				xid = MultiXactIdCreate(xmax, xid);
! 				new_infomask |= HEAP_XMAX_IS_MULTI;
  			}
  			else
  			{
Index: src/backend/access/transam/multixact.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/access/transam/multixact.c,v
retrieving revision 1.11.2.2
diff -c -r1.11.2.2 multixact.c
*** src/backend/access/transam/multixact.c	20 Jul 2006 00:46:56 -0000	1.11.2.2
--- src/backend/access/transam/multixact.c	17 Nov 2006 17:37:29 -0000
***************
*** 365,371 ****
  MultiXactIdIsRunning(MultiXactId multi)
  {
  	TransactionId *members;
- 	TransactionId myXid;
  	int			nmembers;
  	int			i;
  
--- 365,370 ----
***************
*** 379,390 ****
  		return false;
  	}
  
! 	/* checking for myself is cheap */
! 	myXid = GetTopTransactionId();
! 
  	for (i = 0; i < nmembers; i++)
  	{
! 		if (TransactionIdEquals(members[i], myXid))
  		{
  			debug_elog3(DEBUG2, "IsRunning: I (%d) am running!", i);
  			pfree(members);
--- 378,391 ----
  		return false;
  	}
  
! 	/*
! 	 * Checking for myself is cheap compared to looking in shared memory,
! 	 * so first do the equivalent of MultiXactIdIsCurrent().  This is not
! 	 * needed for correctness, it's just a fast path.
! 	 */
  	for (i = 0; i < nmembers; i++)
  	{
! 		if (TransactionIdIsCurrentTransactionId(members[i]))
  		{
  			debug_elog3(DEBUG2, "IsRunning: I (%d) am running!", i);
  			pfree(members);
***************
*** 416,421 ****
--- 417,460 ----
  }
  
  /*
+  * MultiXactIdIsCurrent
+  *		Returns true if the current transaction is a member of the MultiXactId.
+  *
+  * We return true if any live subtransaction of the current top-level
+  * transaction is a member.  This is appropriate for the same reason that a
+  * lock held by any such subtransaction is globally equivalent to a lock
+  * held by the current subtransaction: no such lock could be released without
+  * aborting this subtransaction, and hence releasing its locks.  So it's not
+  * necessary to add the current subxact to the MultiXact separately.
+  */
+ bool
+ MultiXactIdIsCurrent(MultiXactId multi)
+ {
+ 	bool		result = false;
+ 	TransactionId *members;
+ 	int			nmembers;
+ 	int			i;
+ 
+ 	nmembers = GetMultiXactIdMembers(multi, &members);
+ 
+ 	if (nmembers < 0)
+ 		return false;
+ 
+ 	for (i = 0; i < nmembers; i++)
+ 	{
+ 		if (TransactionIdIsCurrentTransactionId(members[i]))
+ 		{
+ 			result = true;
+ 			break;
+ 		}
+ 	}
+ 
+ 	pfree(members);
+ 
+ 	return result;
+ }
+ 
+ /*
   * MultiXactIdSetOldestMember
   *		Save the oldest MultiXactId this transaction could be a member of.
   *
Index: src/backend/utils/time/tqual.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/time/tqual.c,v
retrieving revision 1.91.2.1
diff -c -r1.91.2.1 tqual.c
*** src/backend/utils/time/tqual.c	22 Nov 2005 18:23:25 -0000	1.91.2.1
--- src/backend/utils/time/tqual.c	17 Nov 2006 17:37:29 -0000
***************
*** 506,512 ****
   *	HeapTupleUpdated: The tuple was updated by a committed transaction.
   *
   *	HeapTupleBeingUpdated: The tuple is being updated by an in-progress
!  *	transaction other than the current transaction.
   */
  HTSU_Result
  HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid,
--- 506,515 ----
   *	HeapTupleUpdated: The tuple was updated by a committed transaction.
   *
   *	HeapTupleBeingUpdated: The tuple is being updated by an in-progress
!  *	transaction other than the current transaction.  (Note: this includes
!  *	the case where the tuple is share-locked by a MultiXact, even if the
!  *	MultiXact includes the current transaction.  Callers that want to
!  *	distinguish that case must test for it themselves.)
   */
  HTSU_Result
  HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid,
Index: src/include/access/multixact.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/access/multixact.h,v
retrieving revision 1.7
diff -c -r1.7 multixact.h
*** src/include/access/multixact.h	15 Oct 2005 02:49:42 -0000	1.7
--- src/include/access/multixact.h	17 Nov 2006 17:37:29 -0000
***************
*** 41,46 ****
--- 41,47 ----
  extern MultiXactId MultiXactIdCreate(TransactionId xid1, TransactionId xid2);
  extern MultiXactId MultiXactIdExpand(MultiXactId multi, TransactionId xid);
  extern bool MultiXactIdIsRunning(MultiXactId multi);
+ extern bool MultiXactIdIsCurrent(MultiXactId multi);
  extern void MultiXactIdWait(MultiXactId multi);
  extern bool ConditionalMultiXactIdWait(MultiXactId multi);
  extern void MultiXactIdSetOldestMember(void);
