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
*/
@@ -3250,6 +3260,10 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3250
3260
(errcode (ERRCODE_INVALID_TRANSACTION_STATE ),
3251
3261
errmsg ("cannot update tuples during a parallel operation" )));
3252
3262
3263
+ #ifdef USE_ASSERT_CHECKING
3264
+ check_lock_if_inplace_updateable_rel (relation , otid , newtup );
3265
+ #endif
3266
+
3253
3267
/*
3254
3268
* Fetch the list of attributes to be checked for various operations.
3255
3269
*
@@ -4095,6 +4109,128 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
4095
4109
return TM_Ok ;
4096
4110
}
4097
4111
4112
+ #ifdef USE_ASSERT_CHECKING
4113
+ /*
4114
+ * Confirm adequate lock held during heap_update(), per rules from
4115
+ * README.tuplock section "Locking to write inplace-updated tables".
4116
+ */
4117
+ static void
4118
+ check_lock_if_inplace_updateable_rel (Relation relation ,
4119
+ ItemPointer otid ,
4120
+ HeapTuple newtup )
4121
+ {
4122
+ /* LOCKTAG_TUPLE acceptable for any catalog */
4123
+ switch (RelationGetRelid (relation ))
4124
+ {
4125
+ case RelationRelationId :
4126
+ case DatabaseRelationId :
4127
+ {
4128
+ LOCKTAG tuptag ;
4129
+
4130
+ SET_LOCKTAG_TUPLE (tuptag ,
4131
+ relation -> rd_lockInfo .lockRelId .dbId ,
4132
+ relation -> rd_lockInfo .lockRelId .relId ,
4133
+ ItemPointerGetBlockNumber (otid ),
4134
+ ItemPointerGetOffsetNumber (otid ));
4135
+ if (LockHeldByMe (& tuptag , InplaceUpdateTupleLock ))
4136
+ return ;
4137
+ }
4138
+ break ;
4139
+ default :
4140
+ Assert (!IsInplaceUpdateRelation (relation ));
4141
+ return ;
4142
+ }
4143
+
4144
+ switch (RelationGetRelid (relation ))
4145
+ {
4146
+ case RelationRelationId :
4147
+ {
4148
+ /* LOCKTAG_TUPLE or LOCKTAG_RELATION ok */
4149
+ Form_pg_class classForm = (Form_pg_class ) GETSTRUCT (newtup );
4150
+ Oid relid = classForm -> oid ;
4151
+ Oid dbid ;
4152
+ LOCKTAG tag ;
4153
+
4154
+ if (IsSharedRelation (relid ))
4155
+ dbid = InvalidOid ;
4156
+ else
4157
+ dbid = MyDatabaseId ;
4158
+
4159
+ if (classForm -> relkind == RELKIND_INDEX )
4160
+ {
4161
+ Relation irel = index_open (relid , AccessShareLock );
4162
+
4163
+ SET_LOCKTAG_RELATION (tag , dbid , irel -> rd_index -> indrelid );
4164
+ index_close (irel , AccessShareLock );
4165
+ }
4166
+ else
4167
+ SET_LOCKTAG_RELATION (tag , dbid , relid );
4168
+
4169
+ if (!LockHeldByMe (& tag , ShareUpdateExclusiveLock ) &&
4170
+ !LockOrStrongerHeldByMe (& tag , ShareRowExclusiveLock ))
4171
+ elog (WARNING ,
4172
+ "missing lock for relation \"%s\" (OID %u, relkind %c) @ TID (%u,%u)" ,
4173
+ NameStr (classForm -> relname ),
4174
+ relid ,
4175
+ classForm -> relkind ,
4176
+ ItemPointerGetBlockNumber (otid ),
4177
+ ItemPointerGetOffsetNumber (otid ));
4178
+ }
4179
+ break ;
4180
+ case DatabaseRelationId :
4181
+ {
4182
+ /* LOCKTAG_TUPLE required */
4183
+ Form_pg_database dbForm = (Form_pg_database ) GETSTRUCT (newtup );
4184
+
4185
+ elog (WARNING ,
4186
+ "missing lock on database \"%s\" (OID %u) @ TID (%u,%u)" ,
4187
+ NameStr (dbForm -> datname ),
4188
+ dbForm -> oid ,
4189
+ ItemPointerGetBlockNumber (otid ),
4190
+ ItemPointerGetOffsetNumber (otid ));
4191
+ }
4192
+ break ;
4193
+ }
4194
+ }
4195
+
4196
+ /*
4197
+ * Confirm adequate relation lock held, per rules from README.tuplock section
4198
+ * "Locking to write inplace-updated tables".
4199
+ */
4200
+ static void
4201
+ check_inplace_rel_lock (HeapTuple oldtup )
4202
+ {
4203
+ Form_pg_class classForm = (Form_pg_class ) GETSTRUCT (oldtup );
4204
+ Oid relid = classForm -> oid ;
4205
+ Oid dbid ;
4206
+ LOCKTAG tag ;
4207
+
4208
+ if (IsSharedRelation (relid ))
4209
+ dbid = InvalidOid ;
4210
+ else
4211
+ dbid = MyDatabaseId ;
4212
+
4213
+ if (classForm -> relkind == RELKIND_INDEX )
4214
+ {
4215
+ Relation irel = index_open (relid , AccessShareLock );
4216
+
4217
+ SET_LOCKTAG_RELATION (tag , dbid , irel -> rd_index -> indrelid );
4218
+ index_close (irel , AccessShareLock );
4219
+ }
4220
+ else
4221
+ SET_LOCKTAG_RELATION (tag , dbid , relid );
4222
+
4223
+ if (!LockOrStrongerHeldByMe (& tag , ShareUpdateExclusiveLock ))
4224
+ elog (WARNING ,
4225
+ "missing lock for relation \"%s\" (OID %u, relkind %c) @ TID (%u,%u)" ,
4226
+ NameStr (classForm -> relname ),
4227
+ relid ,
4228
+ classForm -> relkind ,
4229
+ ItemPointerGetBlockNumber (& oldtup -> t_self ),
4230
+ ItemPointerGetOffsetNumber (& oldtup -> t_self ));
4231
+ }
4232
+ #endif
4233
+
4098
4234
/*
4099
4235
* Check if the specified attribute's values are the same. Subroutine for
4100
4236
* HeapDetermineColumnsInfo.
@@ -6120,15 +6256,21 @@ heap_inplace_lock(Relation relation,
6120
6256
TM_Result result ;
6121
6257
bool ret ;
6122
6258
6259
+ #ifdef USE_ASSERT_CHECKING
6260
+ if (RelationGetRelid (relation ) == RelationRelationId )
6261
+ check_inplace_rel_lock (oldtup_ptr );
6262
+ #endif
6263
+
6123
6264
Assert (BufferIsValid (buffer ));
6124
6265
6266
+ LockTuple (relation , & oldtup .t_self , InplaceUpdateTupleLock );
6125
6267
LockBuffer (buffer , BUFFER_LOCK_EXCLUSIVE );
6126
6268
6127
6269
/*----------
6128
6270
* Interpret HeapTupleSatisfiesUpdate() like heap_update() does, except:
6129
6271
*
6130
6272
* - wait unconditionally
6131
- * - no tuple locks
6273
+ * - already locked tuple above, since inplace needs that unconditionally
6132
6274
* - don't recheck header after wait: simpler to defer to next iteration
6133
6275
* - don't try to continue even if the updater aborts: likewise
6134
6276
* - no crosscheck
@@ -6212,7 +6354,10 @@ heap_inplace_lock(Relation relation,
6212
6354
* don't bother optimizing that.
6213
6355
*/
6214
6356
if (!ret )
6357
+ {
6358
+ UnlockTuple (relation , & oldtup .t_self , InplaceUpdateTupleLock );
6215
6359
InvalidateCatalogSnapshot ();
6360
+ }
6216
6361
return ret ;
6217
6362
}
6218
6363
@@ -6221,6 +6366,8 @@ heap_inplace_lock(Relation relation,
6221
6366
*
6222
6367
* The tuple cannot change size, and therefore its header fields and null
6223
6368
* bitmap (if any) don't change either.
6369
+ *
6370
+ * Since we hold LOCKTAG_TUPLE, no updater has a local copy of this tuple.
6224
6371
*/
6225
6372
void
6226
6373
heap_inplace_update_and_unlock (Relation relation ,
@@ -6304,6 +6451,7 @@ heap_inplace_unlock(Relation relation,
6304
6451
HeapTuple oldtup , Buffer buffer )
6305
6452
{
6306
6453
LockBuffer (buffer , BUFFER_LOCK_UNLOCK );
6454
+ UnlockTuple (relation , & oldtup -> t_self , InplaceUpdateTupleLock );
6307
6455
}
6308
6456
6309
6457
/*
0 commit comments