52
52
#include "access/xloginsert.h"
53
53
#include "access/xlogutils.h"
54
54
#include "catalog/catalog.h"
55
+ #include "catalog/pg_database.h"
56
+ #include "catalog/pg_database_d.h"
55
57
#include "miscadmin.h"
56
58
#include "pgstat.h"
57
59
#include "port/atomics.h"
@@ -78,6 +80,12 @@ static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
78
80
Buffer newbuf , HeapTuple oldtup ,
79
81
HeapTuple newtup , HeapTuple old_key_tuple ,
80
82
bool all_visible_cleared , bool new_all_visible_cleared );
83
+ #ifdef USE_ASSERT_CHECKING
84
+ static void check_lock_if_inplace_updateable_rel (Relation relation ,
85
+ ItemPointer otid ,
86
+ HeapTuple newtup );
87
+ static void check_inplace_rel_lock (HeapTuple oldtup );
88
+ #endif
81
89
static Bitmapset * HeapDetermineColumnsInfo (Relation relation ,
82
90
Bitmapset * interesting_cols ,
83
91
Bitmapset * external_cols ,
@@ -119,6 +127,8 @@ static HeapTuple ExtractReplicaIdentity(Relation rel, HeapTuple tup, bool key_re
119
127
* heavyweight lock mode and MultiXactStatus values to use for any particular
120
128
* tuple lock strength.
121
129
*
130
+ * These interact with InplaceUpdateTupleLock, an alias for ExclusiveLock.
131
+ *
122
132
* Don't look at lockstatus/updstatus directly! Use get_mxact_status_for_lock
123
133
* instead.
124
134
*/
@@ -3187,6 +3197,10 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3187
3197
(errcode (ERRCODE_INVALID_TRANSACTION_STATE ),
3188
3198
errmsg ("cannot update tuples during a parallel operation" )));
3189
3199
3200
+ #ifdef USE_ASSERT_CHECKING
3201
+ check_lock_if_inplace_updateable_rel (relation , otid , newtup );
3202
+ #endif
3203
+
3190
3204
/*
3191
3205
* Fetch the list of attributes to be checked for various operations.
3192
3206
*
@@ -4014,6 +4028,128 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
4014
4028
return TM_Ok ;
4015
4029
}
4016
4030
4031
+ #ifdef USE_ASSERT_CHECKING
4032
+ /*
4033
+ * Confirm adequate lock held during heap_update(), per rules from
4034
+ * README.tuplock section "Locking to write inplace-updated tables".
4035
+ */
4036
+ static void
4037
+ check_lock_if_inplace_updateable_rel (Relation relation ,
4038
+ ItemPointer otid ,
4039
+ HeapTuple newtup )
4040
+ {
4041
+ /* LOCKTAG_TUPLE acceptable for any catalog */
4042
+ switch (RelationGetRelid (relation ))
4043
+ {
4044
+ case RelationRelationId :
4045
+ case DatabaseRelationId :
4046
+ {
4047
+ LOCKTAG tuptag ;
4048
+
4049
+ SET_LOCKTAG_TUPLE (tuptag ,
4050
+ relation -> rd_lockInfo .lockRelId .dbId ,
4051
+ relation -> rd_lockInfo .lockRelId .relId ,
4052
+ ItemPointerGetBlockNumber (otid ),
4053
+ ItemPointerGetOffsetNumber (otid ));
4054
+ if (LockHeldByMe (& tuptag , InplaceUpdateTupleLock ))
4055
+ return ;
4056
+ }
4057
+ break ;
4058
+ default :
4059
+ Assert (!IsInplaceUpdateRelation (relation ));
4060
+ return ;
4061
+ }
4062
+
4063
+ switch (RelationGetRelid (relation ))
4064
+ {
4065
+ case RelationRelationId :
4066
+ {
4067
+ /* LOCKTAG_TUPLE or LOCKTAG_RELATION ok */
4068
+ Form_pg_class classForm = (Form_pg_class ) GETSTRUCT (newtup );
4069
+ Oid relid = classForm -> oid ;
4070
+ Oid dbid ;
4071
+ LOCKTAG tag ;
4072
+
4073
+ if (IsSharedRelation (relid ))
4074
+ dbid = InvalidOid ;
4075
+ else
4076
+ dbid = MyDatabaseId ;
4077
+
4078
+ if (classForm -> relkind == RELKIND_INDEX )
4079
+ {
4080
+ Relation irel = index_open (relid , AccessShareLock );
4081
+
4082
+ SET_LOCKTAG_RELATION (tag , dbid , irel -> rd_index -> indrelid );
4083
+ index_close (irel , AccessShareLock );
4084
+ }
4085
+ else
4086
+ SET_LOCKTAG_RELATION (tag , dbid , relid );
4087
+
4088
+ if (!LockHeldByMe (& tag , ShareUpdateExclusiveLock ) &&
4089
+ !LockOrStrongerHeldByMe (& tag , ShareRowExclusiveLock ))
4090
+ elog (WARNING ,
4091
+ "missing lock for relation \"%s\" (OID %u, relkind %c) @ TID (%u,%u)" ,
4092
+ NameStr (classForm -> relname ),
4093
+ relid ,
4094
+ classForm -> relkind ,
4095
+ ItemPointerGetBlockNumber (otid ),
4096
+ ItemPointerGetOffsetNumber (otid ));
4097
+ }
4098
+ break ;
4099
+ case DatabaseRelationId :
4100
+ {
4101
+ /* LOCKTAG_TUPLE required */
4102
+ Form_pg_database dbForm = (Form_pg_database ) GETSTRUCT (newtup );
4103
+
4104
+ elog (WARNING ,
4105
+ "missing lock on database \"%s\" (OID %u) @ TID (%u,%u)" ,
4106
+ NameStr (dbForm -> datname ),
4107
+ dbForm -> oid ,
4108
+ ItemPointerGetBlockNumber (otid ),
4109
+ ItemPointerGetOffsetNumber (otid ));
4110
+ }
4111
+ break ;
4112
+ }
4113
+ }
4114
+
4115
+ /*
4116
+ * Confirm adequate relation lock held, per rules from README.tuplock section
4117
+ * "Locking to write inplace-updated tables".
4118
+ */
4119
+ static void
4120
+ check_inplace_rel_lock (HeapTuple oldtup )
4121
+ {
4122
+ Form_pg_class classForm = (Form_pg_class ) GETSTRUCT (oldtup );
4123
+ Oid relid = classForm -> oid ;
4124
+ Oid dbid ;
4125
+ LOCKTAG tag ;
4126
+
4127
+ if (IsSharedRelation (relid ))
4128
+ dbid = InvalidOid ;
4129
+ else
4130
+ dbid = MyDatabaseId ;
4131
+
4132
+ if (classForm -> relkind == RELKIND_INDEX )
4133
+ {
4134
+ Relation irel = index_open (relid , AccessShareLock );
4135
+
4136
+ SET_LOCKTAG_RELATION (tag , dbid , irel -> rd_index -> indrelid );
4137
+ index_close (irel , AccessShareLock );
4138
+ }
4139
+ else
4140
+ SET_LOCKTAG_RELATION (tag , dbid , relid );
4141
+
4142
+ if (!LockOrStrongerHeldByMe (& tag , ShareUpdateExclusiveLock ))
4143
+ elog (WARNING ,
4144
+ "missing lock for relation \"%s\" (OID %u, relkind %c) @ TID (%u,%u)" ,
4145
+ NameStr (classForm -> relname ),
4146
+ relid ,
4147
+ classForm -> relkind ,
4148
+ ItemPointerGetBlockNumber (& oldtup -> t_self ),
4149
+ ItemPointerGetOffsetNumber (& oldtup -> t_self ));
4150
+ }
4151
+ #endif
4152
+
4017
4153
/*
4018
4154
* Check if the specified attribute's values are the same. Subroutine for
4019
4155
* HeapDetermineColumnsInfo.
@@ -6039,15 +6175,21 @@ heap_inplace_lock(Relation relation,
6039
6175
TM_Result result ;
6040
6176
bool ret ;
6041
6177
6178
+ #ifdef USE_ASSERT_CHECKING
6179
+ if (RelationGetRelid (relation ) == RelationRelationId )
6180
+ check_inplace_rel_lock (oldtup_ptr );
6181
+ #endif
6182
+
6042
6183
Assert (BufferIsValid (buffer ));
6043
6184
6185
+ LockTuple (relation , & oldtup .t_self , InplaceUpdateTupleLock );
6044
6186
LockBuffer (buffer , BUFFER_LOCK_EXCLUSIVE );
6045
6187
6046
6188
/*----------
6047
6189
* Interpret HeapTupleSatisfiesUpdate() like heap_update() does, except:
6048
6190
*
6049
6191
* - wait unconditionally
6050
- * - no tuple locks
6192
+ * - already locked tuple above, since inplace needs that unconditionally
6051
6193
* - don't recheck header after wait: simpler to defer to next iteration
6052
6194
* - don't try to continue even if the updater aborts: likewise
6053
6195
* - no crosscheck
@@ -6131,7 +6273,10 @@ heap_inplace_lock(Relation relation,
6131
6273
* don't bother optimizing that.
6132
6274
*/
6133
6275
if (!ret )
6276
+ {
6277
+ UnlockTuple (relation , & oldtup .t_self , InplaceUpdateTupleLock );
6134
6278
InvalidateCatalogSnapshot ();
6279
+ }
6135
6280
return ret ;
6136
6281
}
6137
6282
@@ -6140,6 +6285,8 @@ heap_inplace_lock(Relation relation,
6140
6285
*
6141
6286
* The tuple cannot change size, and therefore its header fields and null
6142
6287
* bitmap (if any) don't change either.
6288
+ *
6289
+ * Since we hold LOCKTAG_TUPLE, no updater has a local copy of this tuple.
6143
6290
*/
6144
6291
void
6145
6292
heap_inplace_update_and_unlock (Relation relation ,
@@ -6223,6 +6370,7 @@ heap_inplace_unlock(Relation relation,
6223
6370
HeapTuple oldtup , Buffer buffer )
6224
6371
{
6225
6372
LockBuffer (buffer , BUFFER_LOCK_UNLOCK );
6373
+ UnlockTuple (relation , & oldtup -> t_self , InplaceUpdateTupleLock );
6226
6374
}
6227
6375
6228
6376
/*
0 commit comments