@@ -4794,6 +4794,83 @@ compute_new_xmax_infomask(TransactionId xmax, uint16 old_infomask,
4794
4794
* result_xmax = new_xmax ;
4795
4795
}
4796
4796
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
+
4797
4874
4798
4875
/*
4799
4876
* Recursive part of heap_lock_updated_tuple
@@ -4811,7 +4888,8 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
4811
4888
Buffer buf ;
4812
4889
uint16 new_infomask ,
4813
4890
new_infomask2 ,
4814
- old_infomask ;
4891
+ old_infomask ,
4892
+ old_infomask2 ;
4815
4893
TransactionId xmax ,
4816
4894
new_xmax ;
4817
4895
TransactionId priorXmax = InvalidTransactionId ;
@@ -4853,45 +4931,107 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid,
4853
4931
}
4854
4932
4855
4933
old_infomask = mytup .t_data -> t_infomask ;
4934
+ old_infomask2 = mytup .t_data -> t_infomask2 ;
4856
4935
xmax = HeapTupleHeaderGetRawXmax (mytup .t_data );
4857
4936
4858
4937
/*
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.
4865
4942
*/
4866
- if (!(old_infomask & HEAP_XMAX_INVALID ) &&
4867
- (mytup .t_data -> t_infomask2 & HEAP_KEYS_UPDATED ))
4943
+ if (!(old_infomask & HEAP_XMAX_INVALID ))
4868
4944
{
4869
- TransactionId update_xid ;
4945
+ TransactionId rawxmax ;
4946
+ bool needwait ;
4870
4947
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 )
4878
4950
{
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 );
4888
4980
}
4889
- else if (TransactionIdDidAbort (update_xid ))
4890
- ; /* okay to proceed */
4891
- else if (TransactionIdDidCommit (update_xid ))
4981
+ else
4892
4982
{
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
+ }
4895
5035
}
4896
5036
}
4897
5037
0 commit comments