@@ -4794,6 +4794,83 @@ compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
47944794 * result_xmax = new_xmax ;
47954795}
47964796
4797+ /*
4798+ * Subroutine for heap_lock_updated_tuple_rec.
4799+ *
4800+ * Given an hypothetical multixact status held by the transaction identified
4801+ * with the given xid, does the current transaction need to wait, fail, or can
4802+ * it continue if it wanted to acquire a lock of the given mode? "needwait"
4803+ * is set to true if waiting is necessary; if it can continue, then
4804+ * HeapTupleMayBeUpdated is returned. In case of a conflict, a different
4805+ * HeapTupleSatisfiesUpdate return code is returned.
4806+ *
4807+ * The held status is said to be hypothetical because it might correspond to a
4808+ * lock held by a single Xid, i.e. not a real MultiXactId; we express it this
4809+ * way for simplicity of API.
4810+ */
4811+ static HTSU_Result
4812+ test_lockmode_for_conflict (MultiXactStatus status , TransactionId xid ,
4813+ LockTupleMode mode , bool * needwait )
4814+ {
4815+ MultiXactStatus wantedstatus ;
4816+
4817+ * needwait = false;
4818+ wantedstatus = get_mxact_status_for_lock (mode , false);
4819+
4820+ /*
4821+ * Note: we *must* check TransactionIdIsInProgress before
4822+ * TransactionIdDidAbort/Commit; see comment at top of tqual.c for an
4823+ * explanation.
4824+ */
4825+ if (TransactionIdIsCurrentTransactionId (xid ))
4826+ {
4827+ /*
4828+ * Updated by our own transaction? Just return failure. This shouldn't
4829+ * normally happen.
4830+ */
4831+ return HeapTupleSelfUpdated ;
4832+ }
4833+ else if (TransactionIdIsInProgress (xid ))
4834+ {
4835+ /*
4836+ * If the locking transaction is running, what we do depends on whether
4837+ * the lock modes conflict: if they do, then we must wait for it to
4838+ * finish; otherwise we can fall through to lock this tuple version
4839+ * without waiting.
4840+ */
4841+ if (DoLockModesConflict (LOCKMODE_from_mxstatus (status ),
4842+ LOCKMODE_from_mxstatus (wantedstatus )))
4843+ {
4844+ * needwait = true;
4845+ }
4846+
4847+ /*
4848+ * If we set needwait above, then this value doesn't matter; otherwise,
4849+ * this value signals to caller that it's okay to proceed.
4850+ */
4851+ return HeapTupleMayBeUpdated ;
4852+ }
4853+ else if (TransactionIdDidAbort (xid ))
4854+ return HeapTupleMayBeUpdated ;
4855+ else if (TransactionIdDidCommit (xid ))
4856+ {
4857+ /*
4858+ * If the updating transaction committed, what we do depends on whether
4859+ * the lock modes conflict: if they do, then we must report error to
4860+ * caller. But if they don't, we can fall through to lock it.
4861+ */
4862+ if (DoLockModesConflict (LOCKMODE_from_mxstatus (status ),
4863+ LOCKMODE_from_mxstatus (wantedstatus )))
4864+ /* bummer */
4865+ return HeapTupleUpdated ;
4866+
4867+ return HeapTupleMayBeUpdated ;
4868+ }
4869+
4870+ /* Not in progress, not aborted, not committed -- must have crashed */
4871+ return HeapTupleMayBeUpdated ;
4872+ }
4873+
47974874
47984875/*
47994876 * Recursive part of heap_lock_updated_tuple
@@ -4811,7 +4888,8 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
48114888 Buffer buf ;
48124889 uint16 new_infomask ,
48134890 new_infomask2 ,
4814- old_infomask ;
4891+ old_infomask ,
4892+ old_infomask2 ;
48154893 TransactionId xmax ,
48164894 new_xmax ;
48174895 TransactionId priorXmax = InvalidTransactionId ;
@@ -4853,45 +4931,107 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
48534931 }
48544932
48554933 old_infomask = mytup .t_data -> t_infomask ;
4934+ old_infomask2 = mytup .t_data -> t_infomask2 ;
48564935 xmax = HeapTupleHeaderGetRawXmax (mytup .t_data );
48574936
48584937 /*
4859- * If this tuple is updated and the key has been modified (or
4860- * deleted), what we do depends on the status of the updating
4861- * transaction: if it's live, we sleep until it finishes; if it has
4862- * committed, we have to fail (i.e. return HeapTupleUpdated); if it
4863- * aborted, we ignore it. For updates that didn't touch the key, we
4864- * can just plough ahead.
4938+ * If this tuple version has been updated or locked by some concurrent
4939+ * transaction(s), what we do depends on whether our lock mode
4940+ * conflicts with what those other transactions hold, and also on the
4941+ * status of them.
48654942 */
4866- if (!(old_infomask & HEAP_XMAX_INVALID ) &&
4867- (mytup .t_data -> t_infomask2 & HEAP_KEYS_UPDATED ))
4943+ if (!(old_infomask & HEAP_XMAX_INVALID ))
48684944 {
4869- TransactionId update_xid ;
4945+ TransactionId rawxmax ;
4946+ bool needwait ;
48704947
4871- /*
4872- * Note: we *must* check TransactionIdIsInProgress before
4873- * TransactionIdDidAbort/Commit; see comment at top of tqual.c for
4874- * an explanation.
4875- */
4876- update_xid = HeapTupleHeaderGetUpdateXid (mytup .t_data );
4877- if (TransactionIdIsCurrentTransactionId (update_xid ))
4948+ rawxmax = HeapTupleHeaderGetRawXmax (mytup .t_data );
4949+ if (old_infomask & HEAP_XMAX_IS_MULTI )
48784950 {
4879- UnlockReleaseBuffer (buf );
4880- return HeapTupleSelfUpdated ;
4881- }
4882- else if (TransactionIdIsInProgress (update_xid ))
4883- {
4884- LockBuffer (buf , BUFFER_LOCK_UNLOCK );
4885- /* No LockTupleTuplock here -- see heap_lock_updated_tuple */
4886- XactLockTableWait (update_xid );
4887- goto l4 ;
4951+ int nmembers ;
4952+ int i ;
4953+ MultiXactMember * members ;
4954+
4955+ nmembers = GetMultiXactIdMembers (rawxmax , & members , false, false);
4956+ for (i = 0 ; i < nmembers ; i ++ )
4957+ {
4958+ HTSU_Result res ;
4959+
4960+ res = test_lockmode_for_conflict (members [i ].status ,
4961+ members [i ].xid ,
4962+ mode , & needwait );
4963+
4964+ if (needwait )
4965+ {
4966+ LockBuffer (buf , BUFFER_LOCK_UNLOCK );
4967+ XactLockTableWait (members [i ].xid );
4968+ pfree (members );
4969+ goto l4 ;
4970+ }
4971+ if (res != HeapTupleMayBeUpdated )
4972+ {
4973+ UnlockReleaseBuffer (buf );
4974+ pfree (members );
4975+ return res ;
4976+ }
4977+ }
4978+ if (members )
4979+ pfree (members );
48884980 }
4889- else if (TransactionIdDidAbort (update_xid ))
4890- ; /* okay to proceed */
4891- else if (TransactionIdDidCommit (update_xid ))
4981+ else
48924982 {
4893- UnlockReleaseBuffer (buf );
4894- return HeapTupleUpdated ;
4983+ HTSU_Result res ;
4984+ MultiXactStatus status ;
4985+
4986+ /*
4987+ * For a non-multi Xmax, we first need to compute the
4988+ * corresponding MultiXactStatus by using the infomask bits.
4989+ */
4990+ if (HEAP_XMAX_IS_LOCKED_ONLY (old_infomask ))
4991+ {
4992+ if (HEAP_XMAX_IS_KEYSHR_LOCKED (old_infomask ))
4993+ status = MultiXactStatusForKeyShare ;
4994+ else if (HEAP_XMAX_IS_SHR_LOCKED (old_infomask ))
4995+ status = MultiXactStatusForShare ;
4996+ else if (HEAP_XMAX_IS_EXCL_LOCKED (old_infomask ))
4997+ {
4998+ if (old_infomask2 & HEAP_KEYS_UPDATED )
4999+ status = MultiXactStatusForUpdate ;
5000+ else
5001+ status = MultiXactStatusForNoKeyUpdate ;
5002+ }
5003+ else
5004+ {
5005+ /*
5006+ * LOCK_ONLY present alone (a pg_upgraded tuple
5007+ * marked as share-locked in the old cluster) shouldn't
5008+ * be seen in the middle of an update chain.
5009+ */
5010+ elog (ERROR , "invalid lock status in tuple" );
5011+ }
5012+ }
5013+ else
5014+ {
5015+ /* it's an update, but which kind? */
5016+ if (old_infomask2 & HEAP_KEYS_UPDATED )
5017+ status = MultiXactStatusUpdate ;
5018+ else
5019+ status = MultiXactStatusNoKeyUpdate ;
5020+ }
5021+
5022+ res = test_lockmode_for_conflict (status , rawxmax , mode ,
5023+ & needwait );
5024+ if (needwait )
5025+ {
5026+ LockBuffer (buf , BUFFER_LOCK_UNLOCK );
5027+ XactLockTableWait (rawxmax );
5028+ goto l4 ;
5029+ }
5030+ if (res != HeapTupleMayBeUpdated )
5031+ {
5032+ UnlockReleaseBuffer (buf );
5033+ return res ;
5034+ }
48955035 }
48965036 }
48975037
0 commit comments