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