@@ -78,9 +78,11 @@ static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
78
78
Buffer newbuf , HeapTuple oldtup ,
79
79
HeapTuple newtup , HeapTuple old_key_tuple ,
80
80
bool all_visible_cleared , bool new_all_visible_cleared );
81
- static Bitmapset * HeapDetermineModifiedColumns (Relation relation ,
82
- Bitmapset * interesting_cols ,
83
- HeapTuple oldtup , HeapTuple newtup );
81
+ static Bitmapset * HeapDetermineColumnsInfo (Relation relation ,
82
+ Bitmapset * interesting_cols ,
83
+ Bitmapset * external_cols ,
84
+ HeapTuple oldtup , HeapTuple newtup ,
85
+ bool * has_external );
84
86
static bool heap_acquire_tuplock (Relation relation , ItemPointer tid ,
85
87
LockTupleMode mode , LockWaitPolicy wait_policy ,
86
88
bool * have_tuple_lock );
@@ -106,7 +108,7 @@ static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status
106
108
static void index_delete_sort (TM_IndexDeleteOp * delstate );
107
109
static int bottomup_sort_and_shrink (TM_IndexDeleteOp * delstate );
108
110
static XLogRecPtr log_heap_new_cid (Relation relation , HeapTuple tup );
109
- static HeapTuple ExtractReplicaIdentity (Relation rel , HeapTuple tup , bool key_changed ,
111
+ static HeapTuple ExtractReplicaIdentity (Relation rel , HeapTuple tup , bool key_required ,
110
112
bool * copy );
111
113
112
114
@@ -3185,6 +3187,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3185
3187
bool all_visible_cleared_new = false;
3186
3188
bool checked_lockers ;
3187
3189
bool locker_remains ;
3190
+ bool id_has_external = false;
3188
3191
TransactionId xmax_new_tuple ,
3189
3192
xmax_old_tuple ;
3190
3193
uint16 infomask_old_tuple ,
@@ -3269,7 +3272,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3269
3272
Assert (ItemIdIsNormal (lp ));
3270
3273
3271
3274
/*
3272
- * Fill in enough data in oldtup for HeapDetermineModifiedColumns to work
3275
+ * Fill in enough data in oldtup for HeapDetermineColumnsInfo to work
3273
3276
* properly.
3274
3277
*/
3275
3278
oldtup .t_tableOid = RelationGetRelid (relation );
@@ -3280,9 +3283,17 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3280
3283
/* the new tuple is ready, except for this: */
3281
3284
newtup -> t_tableOid = RelationGetRelid (relation );
3282
3285
3283
- /* Determine columns modified by the update. */
3284
- modified_attrs = HeapDetermineModifiedColumns (relation , interesting_attrs ,
3285
- & oldtup , newtup );
3286
+ /*
3287
+ * Determine columns modified by the update. Additionally, identify
3288
+ * whether any of the unmodified replica identity key attributes in the
3289
+ * old tuple is externally stored or not. This is required because for
3290
+ * such attributes the flattened value won't be WAL logged as part of the
3291
+ * new tuple so we must include it as part of the old_key_tuple. See
3292
+ * ExtractReplicaIdentity.
3293
+ */
3294
+ modified_attrs = HeapDetermineColumnsInfo (relation , interesting_attrs ,
3295
+ id_attrs , & oldtup ,
3296
+ newtup , & id_has_external );
3286
3297
3287
3298
/*
3288
3299
* If we're not updating any "key" column, we can grab a weaker lock type.
@@ -3883,10 +3894,12 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3883
3894
* Compute replica identity tuple before entering the critical section so
3884
3895
* we don't PANIC upon a memory allocation failure.
3885
3896
* ExtractReplicaIdentity() will return NULL if nothing needs to be
3886
- * logged.
3897
+ * logged. Pass old key required as true only if the replica identity key
3898
+ * columns are modified or it has external data.
3887
3899
*/
3888
3900
old_key_tuple = ExtractReplicaIdentity (relation , & oldtup ,
3889
- bms_overlap (modified_attrs , id_attrs ),
3901
+ bms_overlap (modified_attrs , id_attrs ) ||
3902
+ id_has_external ,
3890
3903
& old_key_copied );
3891
3904
3892
3905
/* NO EREPORT(ERROR) from here till changes are logged */
@@ -4042,47 +4055,15 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
4042
4055
}
4043
4056
4044
4057
/*
4045
- * Check if the specified attribute's value is same in both given tuples.
4046
- * Subroutine for HeapDetermineModifiedColumns .
4058
+ * Check if the specified attribute's values are the same. Subroutine for
4059
+ * HeapDetermineColumnsInfo .
4047
4060
*/
4048
4061
static bool
4049
- heap_tuple_attr_equals (TupleDesc tupdesc , int attrnum ,
4050
- HeapTuple tup1 , HeapTuple tup2 )
4062
+ heap_attr_equals (TupleDesc tupdesc , int attrnum , Datum value1 , Datum value2 ,
4063
+ bool isnull1 , bool isnull2 )
4051
4064
{
4052
- Datum value1 ,
4053
- value2 ;
4054
- bool isnull1 ,
4055
- isnull2 ;
4056
4065
Form_pg_attribute att ;
4057
4066
4058
- /*
4059
- * If it's a whole-tuple reference, say "not equal". It's not really
4060
- * worth supporting this case, since it could only succeed after a no-op
4061
- * update, which is hardly a case worth optimizing for.
4062
- */
4063
- if (attrnum == 0 )
4064
- return false;
4065
-
4066
- /*
4067
- * Likewise, automatically say "not equal" for any system attribute other
4068
- * than tableOID; we cannot expect these to be consistent in a HOT chain,
4069
- * or even to be set correctly yet in the new tuple.
4070
- */
4071
- if (attrnum < 0 )
4072
- {
4073
- if (attrnum != TableOidAttributeNumber )
4074
- return false;
4075
- }
4076
-
4077
- /*
4078
- * Extract the corresponding values. XXX this is pretty inefficient if
4079
- * there are many indexed columns. Should HeapDetermineModifiedColumns do
4080
- * a single heap_deform_tuple call on each tuple, instead? But that
4081
- * doesn't work for system columns ...
4082
- */
4083
- value1 = heap_getattr (tup1 , attrnum , tupdesc , & isnull1 );
4084
- value2 = heap_getattr (tup2 , attrnum , tupdesc , & isnull2 );
4085
-
4086
4067
/*
4087
4068
* If one value is NULL and other is not, then they are certainly not
4088
4069
* equal
@@ -4124,24 +4105,96 @@ heap_tuple_attr_equals(TupleDesc tupdesc, int attrnum,
4124
4105
* Given an updated tuple, determine (and return into the output bitmapset),
4125
4106
* from those listed as interesting, the set of columns that changed.
4126
4107
*
4127
- * The input bitmapset is destructively modified; that is OK since this is
4128
- * invoked at most once in heap_update.
4108
+ * has_external indicates if any of the unmodified attributes (from those
4109
+ * listed as interesting) of the old tuple is a member of external_cols and is
4110
+ * stored externally.
4111
+ *
4112
+ * The input interesting_cols bitmapset is destructively modified; that is OK
4113
+ * since this is invoked at most once in heap_update.
4129
4114
*/
4130
4115
static Bitmapset *
4131
- HeapDetermineModifiedColumns (Relation relation , Bitmapset * interesting_cols ,
4132
- HeapTuple oldtup , HeapTuple newtup )
4116
+ HeapDetermineColumnsInfo (Relation relation ,
4117
+ Bitmapset * interesting_cols ,
4118
+ Bitmapset * external_cols ,
4119
+ HeapTuple oldtup , HeapTuple newtup ,
4120
+ bool * has_external )
4133
4121
{
4134
- int attnum ;
4122
+ int attrnum ;
4135
4123
Bitmapset * modified = NULL ;
4124
+ TupleDesc tupdesc = RelationGetDescr (relation );
4136
4125
4137
- while ((attnum = bms_first_member (interesting_cols )) >= 0 )
4126
+ while ((attrnum = bms_first_member (interesting_cols )) >= 0 )
4138
4127
{
4139
- attnum += FirstLowInvalidHeapAttributeNumber ;
4128
+ Datum value1 ,
4129
+ value2 ;
4130
+ bool isnull1 ,
4131
+ isnull2 ;
4132
+
4133
+ attrnum += FirstLowInvalidHeapAttributeNumber ;
4140
4134
4141
- if (!heap_tuple_attr_equals (RelationGetDescr (relation ),
4142
- attnum , oldtup , newtup ))
4135
+ /*
4136
+ * If it's a whole-tuple reference, say "not equal". It's not really
4137
+ * worth supporting this case, since it could only succeed after a
4138
+ * no-op update, which is hardly a case worth optimizing for.
4139
+ */
4140
+ if (attrnum == 0 )
4141
+ {
4143
4142
modified = bms_add_member (modified ,
4144
- attnum - FirstLowInvalidHeapAttributeNumber );
4143
+ attrnum -
4144
+ FirstLowInvalidHeapAttributeNumber );
4145
+ continue ;
4146
+ }
4147
+
4148
+ /*
4149
+ * Likewise, automatically say "not equal" for any system attribute
4150
+ * other than tableOID; we cannot expect these to be consistent in a
4151
+ * HOT chain, or even to be set correctly yet in the new tuple.
4152
+ */
4153
+ if (attrnum < 0 )
4154
+ {
4155
+ if (attrnum != TableOidAttributeNumber )
4156
+ {
4157
+ modified = bms_add_member (modified ,
4158
+ attrnum -
4159
+ FirstLowInvalidHeapAttributeNumber );
4160
+ continue ;
4161
+ }
4162
+ }
4163
+
4164
+ /*
4165
+ * Extract the corresponding values. XXX this is pretty inefficient
4166
+ * if there are many indexed columns. Should we do a single
4167
+ * heap_deform_tuple call on each tuple, instead? But that doesn't
4168
+ * work for system columns ...
4169
+ */
4170
+ value1 = heap_getattr (oldtup , attrnum , tupdesc , & isnull1 );
4171
+ value2 = heap_getattr (newtup , attrnum , tupdesc , & isnull2 );
4172
+
4173
+ if (!heap_attr_equals (tupdesc , attrnum , value1 ,
4174
+ value2 , isnull1 , isnull2 ))
4175
+ {
4176
+ modified = bms_add_member (modified ,
4177
+ attrnum -
4178
+ FirstLowInvalidHeapAttributeNumber );
4179
+ continue ;
4180
+ }
4181
+
4182
+ /*
4183
+ * No need to check attributes that can't be stored externally. Note
4184
+ * that system attributes can't be stored externally.
4185
+ */
4186
+ if (attrnum < 0 || isnull1 ||
4187
+ TupleDescAttr (tupdesc , attrnum - 1 )-> attlen != -1 )
4188
+ continue ;
4189
+
4190
+ /*
4191
+ * Check if the old tuple's attribute is stored externally and is a
4192
+ * member of external_cols.
4193
+ */
4194
+ if (VARATT_IS_EXTERNAL ((struct varlena * ) DatumGetPointer (value1 )) &&
4195
+ bms_is_member (attrnum - FirstLowInvalidHeapAttributeNumber ,
4196
+ external_cols ))
4197
+ * has_external = true;
4145
4198
}
4146
4199
4147
4200
return modified ;
@@ -8306,14 +8359,14 @@ log_heap_new_cid(Relation relation, HeapTuple tup)
8306
8359
* Returns NULL if there's no need to log an identity or if there's no suitable
8307
8360
* key defined.
8308
8361
*
8309
- * key_changed should be false if caller knows that no replica identity
8310
- * columns changed value. It's always true in the DELETE case .
8362
+ * Pass key_required true if any replica identity columns changed value, or if
8363
+ * any of them have any external data. Delete must always pass true .
8311
8364
*
8312
8365
* *copy is set to true if the returned tuple is a modified copy rather than
8313
8366
* the same tuple that was passed in.
8314
8367
*/
8315
8368
static HeapTuple
8316
- ExtractReplicaIdentity (Relation relation , HeapTuple tp , bool key_changed ,
8369
+ ExtractReplicaIdentity (Relation relation , HeapTuple tp , bool key_required ,
8317
8370
bool * copy )
8318
8371
{
8319
8372
TupleDesc desc = RelationGetDescr (relation );
@@ -8345,19 +8398,19 @@ ExtractReplicaIdentity(Relation relation, HeapTuple tp, bool key_changed,
8345
8398
return tp ;
8346
8399
}
8347
8400
8348
- /* if the key hasn 't changed and we're only logging the key, we're done */
8349
- if (!key_changed )
8401
+ /* if the key isn 't required and we're only logging the key, we're done */
8402
+ if (!key_required )
8350
8403
return NULL ;
8351
8404
8352
8405
/* find out the replica identity columns */
8353
8406
idattrs = RelationGetIndexAttrBitmap (relation ,
8354
8407
INDEX_ATTR_BITMAP_IDENTITY_KEY );
8355
8408
8356
8409
/*
8357
- * If there's no defined replica identity columns, treat as !key_changed .
8410
+ * If there's no defined replica identity columns, treat as !key_required .
8358
8411
* (This case should not be reachable from heap_update, since that should
8359
- * calculate key_changed accurately. But heap_delete just passes constant
8360
- * true for key_changed , so we can hit this case in deletes.)
8412
+ * calculate key_required accurately. But heap_delete just passes
8413
+ * constant true for key_required , so we can hit this case in deletes.)
8361
8414
*/
8362
8415
if (bms_is_empty (idattrs ))
8363
8416
return NULL ;
0 commit comments