51
51
#include "access/xloginsert.h"
52
52
#include "access/xlogutils.h"
53
53
#include "catalog/catalog.h"
54
+ #include "catalog/pg_database.h"
55
+ #include "catalog/pg_database_d.h"
54
56
#include "miscadmin.h"
55
57
#include "pgstat.h"
56
58
#include "port/atomics.h"
@@ -76,6 +78,12 @@ static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
76
78
Buffer newbuf , HeapTuple oldtup ,
77
79
HeapTuple newtup , HeapTuple old_key_tuple ,
78
80
bool all_visible_cleared , bool new_all_visible_cleared );
81
+ #ifdef USE_ASSERT_CHECKING
82
+ static void check_lock_if_inplace_updateable_rel (Relation relation ,
83
+ ItemPointer otid ,
84
+ HeapTuple newtup );
85
+ static void check_inplace_rel_lock (HeapTuple oldtup );
86
+ #endif
79
87
static Bitmapset * HeapDetermineColumnsInfo (Relation relation ,
80
88
Bitmapset * interesting_cols ,
81
89
Bitmapset * external_cols ,
@@ -115,6 +123,8 @@ static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_re
115
123
* heavyweight lock mode and MultiXactStatus values to use for any particular
116
124
* tuple lock strength.
117
125
*
126
+ * These interact with InplaceUpdateTupleLock, an alias for ExclusiveLock.
127
+ *
118
128
* Don't look at lockstatus/updstatus directly! Use get_mxact_status_for_lock
119
129
* instead.
120
130
*/
@@ -2975,6 +2985,10 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
2975
2985
(errcode (ERRCODE_INVALID_TRANSACTION_STATE ),
2976
2986
errmsg ("cannot update tuples during a parallel operation" )));
2977
2987
2988
+ #ifdef USE_ASSERT_CHECKING
2989
+ check_lock_if_inplace_updateable_rel (relation , otid , newtup );
2990
+ #endif
2991
+
2978
2992
/*
2979
2993
* Fetch the list of attributes to be checked for various operations.
2980
2994
*
@@ -3821,6 +3835,128 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3821
3835
return TM_Ok ;
3822
3836
}
3823
3837
3838
+ #ifdef USE_ASSERT_CHECKING
3839
+ /*
3840
+ * Confirm adequate lock held during heap_update(), per rules from
3841
+ * README.tuplock section "Locking to write inplace-updated tables".
3842
+ */
3843
+ static void
3844
+ check_lock_if_inplace_updateable_rel (Relation relation ,
3845
+ ItemPointer otid ,
3846
+ HeapTuple newtup )
3847
+ {
3848
+ /* LOCKTAG_TUPLE acceptable for any catalog */
3849
+ switch (RelationGetRelid (relation ))
3850
+ {
3851
+ case RelationRelationId :
3852
+ case DatabaseRelationId :
3853
+ {
3854
+ LOCKTAG tuptag ;
3855
+
3856
+ SET_LOCKTAG_TUPLE (tuptag ,
3857
+ relation -> rd_lockInfo .lockRelId .dbId ,
3858
+ relation -> rd_lockInfo .lockRelId .relId ,
3859
+ ItemPointerGetBlockNumber (otid ),
3860
+ ItemPointerGetOffsetNumber (otid ));
3861
+ if (LockHeldByMe (& tuptag , InplaceUpdateTupleLock ))
3862
+ return ;
3863
+ }
3864
+ break ;
3865
+ default :
3866
+ Assert (!IsInplaceUpdateRelation (relation ));
3867
+ return ;
3868
+ }
3869
+
3870
+ switch (RelationGetRelid (relation ))
3871
+ {
3872
+ case RelationRelationId :
3873
+ {
3874
+ /* LOCKTAG_TUPLE or LOCKTAG_RELATION ok */
3875
+ Form_pg_class classForm = (Form_pg_class ) GETSTRUCT (newtup );
3876
+ Oid relid = classForm -> oid ;
3877
+ Oid dbid ;
3878
+ LOCKTAG tag ;
3879
+
3880
+ if (IsSharedRelation (relid ))
3881
+ dbid = InvalidOid ;
3882
+ else
3883
+ dbid = MyDatabaseId ;
3884
+
3885
+ if (classForm -> relkind == RELKIND_INDEX )
3886
+ {
3887
+ Relation irel = index_open (relid , AccessShareLock );
3888
+
3889
+ SET_LOCKTAG_RELATION (tag , dbid , irel -> rd_index -> indrelid );
3890
+ index_close (irel , AccessShareLock );
3891
+ }
3892
+ else
3893
+ SET_LOCKTAG_RELATION (tag , dbid , relid );
3894
+
3895
+ if (!LockHeldByMe (& tag , ShareUpdateExclusiveLock ) &&
3896
+ !LockOrStrongerHeldByMe (& tag , ShareRowExclusiveLock ))
3897
+ elog (WARNING ,
3898
+ "missing lock for relation \"%s\" (OID %u, relkind %c) @ TID (%u,%u)" ,
3899
+ NameStr (classForm -> relname ),
3900
+ relid ,
3901
+ classForm -> relkind ,
3902
+ ItemPointerGetBlockNumber (otid ),
3903
+ ItemPointerGetOffsetNumber (otid ));
3904
+ }
3905
+ break ;
3906
+ case DatabaseRelationId :
3907
+ {
3908
+ /* LOCKTAG_TUPLE required */
3909
+ Form_pg_database dbForm = (Form_pg_database ) GETSTRUCT (newtup );
3910
+
3911
+ elog (WARNING ,
3912
+ "missing lock on database \"%s\" (OID %u) @ TID (%u,%u)" ,
3913
+ NameStr (dbForm -> datname ),
3914
+ dbForm -> oid ,
3915
+ ItemPointerGetBlockNumber (otid ),
3916
+ ItemPointerGetOffsetNumber (otid ));
3917
+ }
3918
+ break ;
3919
+ }
3920
+ }
3921
+
3922
+ /*
3923
+ * Confirm adequate relation lock held, per rules from README.tuplock section
3924
+ * "Locking to write inplace-updated tables".
3925
+ */
3926
+ static void
3927
+ check_inplace_rel_lock (HeapTuple oldtup )
3928
+ {
3929
+ Form_pg_class classForm = (Form_pg_class ) GETSTRUCT (oldtup );
3930
+ Oid relid = classForm -> oid ;
3931
+ Oid dbid ;
3932
+ LOCKTAG tag ;
3933
+
3934
+ if (IsSharedRelation (relid ))
3935
+ dbid = InvalidOid ;
3936
+ else
3937
+ dbid = MyDatabaseId ;
3938
+
3939
+ if (classForm -> relkind == RELKIND_INDEX )
3940
+ {
3941
+ Relation irel = index_open (relid , AccessShareLock );
3942
+
3943
+ SET_LOCKTAG_RELATION (tag , dbid , irel -> rd_index -> indrelid );
3944
+ index_close (irel , AccessShareLock );
3945
+ }
3946
+ else
3947
+ SET_LOCKTAG_RELATION (tag , dbid , relid );
3948
+
3949
+ if (!LockOrStrongerHeldByMe (& tag , ShareUpdateExclusiveLock ))
3950
+ elog (WARNING ,
3951
+ "missing lock for relation \"%s\" (OID %u, relkind %c) @ TID (%u,%u)" ,
3952
+ NameStr (classForm -> relname ),
3953
+ relid ,
3954
+ classForm -> relkind ,
3955
+ ItemPointerGetBlockNumber (& oldtup -> t_self ),
3956
+ ItemPointerGetOffsetNumber (& oldtup -> t_self ));
3957
+ }
3958
+ #endif
3959
+
3824
3960
/*
3825
3961
* Check if the specified attribute's values are the same. Subroutine for
3826
3962
* HeapDetermineColumnsInfo.
@@ -5848,15 +5984,21 @@ heap_inplace_lock(Relation relation,
5848
5984
TM_Result result ;
5849
5985
bool ret ;
5850
5986
5987
+ #ifdef USE_ASSERT_CHECKING
5988
+ if (RelationGetRelid (relation ) == RelationRelationId )
5989
+ check_inplace_rel_lock (oldtup_ptr );
5990
+ #endif
5991
+
5851
5992
Assert (BufferIsValid (buffer ));
5852
5993
5994
+ LockTuple (relation , & oldtup .t_self , InplaceUpdateTupleLock );
5853
5995
LockBuffer (buffer , BUFFER_LOCK_EXCLUSIVE );
5854
5996
5855
5997
/*----------
5856
5998
* Interpret HeapTupleSatisfiesUpdate() like heap_update() does, except:
5857
5999
*
5858
6000
* - wait unconditionally
5859
- * - no tuple locks
6001
+ * - already locked tuple above, since inplace needs that unconditionally
5860
6002
* - don't recheck header after wait: simpler to defer to next iteration
5861
6003
* - don't try to continue even if the updater aborts: likewise
5862
6004
* - no crosscheck
@@ -5940,7 +6082,10 @@ heap_inplace_lock(Relation relation,
5940
6082
* don't bother optimizing that.
5941
6083
*/
5942
6084
if (!ret )
6085
+ {
6086
+ UnlockTuple (relation , & oldtup .t_self , InplaceUpdateTupleLock );
5943
6087
InvalidateCatalogSnapshot ();
6088
+ }
5944
6089
return ret ;
5945
6090
}
5946
6091
@@ -5949,6 +6094,8 @@ heap_inplace_lock(Relation relation,
5949
6094
*
5950
6095
* The tuple cannot change size, and therefore its header fields and null
5951
6096
* bitmap (if any) don't change either.
6097
+ *
6098
+ * Since we hold LOCKTAG_TUPLE, no updater has a local copy of this tuple.
5952
6099
*/
5953
6100
void
5954
6101
heap_inplace_update_and_unlock (Relation relation ,
@@ -6032,6 +6179,7 @@ heap_inplace_unlock(Relation relation,
6032
6179
HeapTuple oldtup , Buffer buffer )
6033
6180
{
6034
6181
LockBuffer (buffer , BUFFER_LOCK_UNLOCK );
6182
+ UnlockTuple (relation , & oldtup -> t_self , InplaceUpdateTupleLock );
6035
6183
}
6036
6184
6037
6185
/*
0 commit comments