@@ -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
@@ -3184,6 +3186,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3184
3186
bool all_visible_cleared_new = false;
3185
3187
bool checked_lockers ;
3186
3188
bool locker_remains ;
3189
+ bool id_has_external = false;
3187
3190
TransactionId xmax_new_tuple ,
3188
3191
xmax_old_tuple ;
3189
3192
uint16 infomask_old_tuple ,
@@ -3251,7 +3254,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3251
3254
Assert (ItemIdIsNormal (lp ));
3252
3255
3253
3256
/*
3254
- * Fill in enough data in oldtup for HeapDetermineModifiedColumns to work
3257
+ * Fill in enough data in oldtup for HeapDetermineColumnsInfo to work
3255
3258
* properly.
3256
3259
*/
3257
3260
oldtup .t_tableOid = RelationGetRelid (relation );
@@ -3262,9 +3265,17 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3262
3265
/* the new tuple is ready, except for this: */
3263
3266
newtup -> t_tableOid = RelationGetRelid (relation );
3264
3267
3265
- /* Determine columns modified by the update. */
3266
- modified_attrs = HeapDetermineModifiedColumns (relation , interesting_attrs ,
3267
- & oldtup , newtup );
3268
+ /*
3269
+ * Determine columns modified by the update. Additionally, identify
3270
+ * whether any of the unmodified replica identity key attributes in the
3271
+ * old tuple is externally stored or not. This is required because for
3272
+ * such attributes the flattened value won't be WAL logged as part of the
3273
+ * new tuple so we must include it as part of the old_key_tuple. See
3274
+ * ExtractReplicaIdentity.
3275
+ */
3276
+ modified_attrs = HeapDetermineColumnsInfo (relation , interesting_attrs ,
3277
+ id_attrs , & oldtup ,
3278
+ newtup , & id_has_external );
3268
3279
3269
3280
/*
3270
3281
* If we're not updating any "key" column, we can grab a weaker lock type.
@@ -3864,10 +3875,12 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3864
3875
* Compute replica identity tuple before entering the critical section so
3865
3876
* we don't PANIC upon a memory allocation failure.
3866
3877
* ExtractReplicaIdentity() will return NULL if nothing needs to be
3867
- * logged.
3878
+ * logged. Pass old key required as true only if the replica identity key
3879
+ * columns are modified or it has external data.
3868
3880
*/
3869
3881
old_key_tuple = ExtractReplicaIdentity (relation , & oldtup ,
3870
- bms_overlap (modified_attrs , id_attrs ),
3882
+ bms_overlap (modified_attrs , id_attrs ) ||
3883
+ id_has_external ,
3871
3884
& old_key_copied );
3872
3885
3873
3886
/* NO EREPORT(ERROR) from here till changes are logged */
@@ -4023,47 +4036,15 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
4023
4036
}
4024
4037
4025
4038
/*
4026
- * Check if the specified attribute's value is same in both given tuples.
4027
- * Subroutine for HeapDetermineModifiedColumns .
4039
+ * Check if the specified attribute's values are the same. Subroutine for
4040
+ * HeapDetermineColumnsInfo .
4028
4041
*/
4029
4042
static bool
4030
- heap_tuple_attr_equals (TupleDesc tupdesc , int attrnum ,
4031
- HeapTuple tup1 , HeapTuple tup2 )
4043
+ heap_attr_equals (TupleDesc tupdesc , int attrnum , Datum value1 , Datum value2 ,
4044
+ bool isnull1 , bool isnull2 )
4032
4045
{
4033
- Datum value1 ,
4034
- value2 ;
4035
- bool isnull1 ,
4036
- isnull2 ;
4037
4046
Form_pg_attribute att ;
4038
4047
4039
- /*
4040
- * If it's a whole-tuple reference, say "not equal". It's not really
4041
- * worth supporting this case, since it could only succeed after a no-op
4042
- * update, which is hardly a case worth optimizing for.
4043
- */
4044
- if (attrnum == 0 )
4045
- return false;
4046
-
4047
- /*
4048
- * Likewise, automatically say "not equal" for any system attribute other
4049
- * than tableOID; we cannot expect these to be consistent in a HOT chain,
4050
- * or even to be set correctly yet in the new tuple.
4051
- */
4052
- if (attrnum < 0 )
4053
- {
4054
- if (attrnum != TableOidAttributeNumber )
4055
- return false;
4056
- }
4057
-
4058
- /*
4059
- * Extract the corresponding values. XXX this is pretty inefficient if
4060
- * there are many indexed columns. Should HeapDetermineModifiedColumns do
4061
- * a single heap_deform_tuple call on each tuple, instead? But that
4062
- * doesn't work for system columns ...
4063
- */
4064
- value1 = heap_getattr (tup1 , attrnum , tupdesc , & isnull1 );
4065
- value2 = heap_getattr (tup2 , attrnum , tupdesc , & isnull2 );
4066
-
4067
4048
/*
4068
4049
* If one value is NULL and other is not, then they are certainly not
4069
4050
* equal
@@ -4105,24 +4086,96 @@ heap_tuple_attr_equals(TupleDesc tupdesc, int attrnum,
4105
4086
* Given an updated tuple, determine (and return into the output bitmapset),
4106
4087
* from those listed as interesting, the set of columns that changed.
4107
4088
*
4108
- * The input bitmapset is destructively modified; that is OK since this is
4109
- * invoked at most once in heap_update.
4089
+ * has_external indicates if any of the unmodified attributes (from those
4090
+ * listed as interesting) of the old tuple is a member of external_cols and is
4091
+ * stored externally.
4092
+ *
4093
+ * The input interesting_cols bitmapset is destructively modified; that is OK
4094
+ * since this is invoked at most once in heap_update.
4110
4095
*/
4111
4096
static Bitmapset *
4112
- HeapDetermineModifiedColumns (Relation relation , Bitmapset * interesting_cols ,
4113
- HeapTuple oldtup , HeapTuple newtup )
4097
+ HeapDetermineColumnsInfo (Relation relation ,
4098
+ Bitmapset * interesting_cols ,
4099
+ Bitmapset * external_cols ,
4100
+ HeapTuple oldtup , HeapTuple newtup ,
4101
+ bool * has_external )
4114
4102
{
4115
- int attnum ;
4103
+ int attrnum ;
4116
4104
Bitmapset * modified = NULL ;
4105
+ TupleDesc tupdesc = RelationGetDescr (relation );
4117
4106
4118
- while ((attnum = bms_first_member (interesting_cols )) >= 0 )
4107
+ while ((attrnum = bms_first_member (interesting_cols )) >= 0 )
4119
4108
{
4120
- attnum += FirstLowInvalidHeapAttributeNumber ;
4109
+ Datum value1 ,
4110
+ value2 ;
4111
+ bool isnull1 ,
4112
+ isnull2 ;
4113
+
4114
+ attrnum += FirstLowInvalidHeapAttributeNumber ;
4121
4115
4122
- if (!heap_tuple_attr_equals (RelationGetDescr (relation ),
4123
- attnum , oldtup , newtup ))
4116
+ /*
4117
+ * If it's a whole-tuple reference, say "not equal". It's not really
4118
+ * worth supporting this case, since it could only succeed after a
4119
+ * no-op update, which is hardly a case worth optimizing for.
4120
+ */
4121
+ if (attrnum == 0 )
4122
+ {
4124
4123
modified = bms_add_member (modified ,
4125
- attnum - FirstLowInvalidHeapAttributeNumber );
4124
+ attrnum -
4125
+ FirstLowInvalidHeapAttributeNumber );
4126
+ continue ;
4127
+ }
4128
+
4129
+ /*
4130
+ * Likewise, automatically say "not equal" for any system attribute
4131
+ * other than tableOID; we cannot expect these to be consistent in a
4132
+ * HOT chain, or even to be set correctly yet in the new tuple.
4133
+ */
4134
+ if (attrnum < 0 )
4135
+ {
4136
+ if (attrnum != TableOidAttributeNumber )
4137
+ {
4138
+ modified = bms_add_member (modified ,
4139
+ attrnum -
4140
+ FirstLowInvalidHeapAttributeNumber );
4141
+ continue ;
4142
+ }
4143
+ }
4144
+
4145
+ /*
4146
+ * Extract the corresponding values. XXX this is pretty inefficient
4147
+ * if there are many indexed columns. Should we do a single
4148
+ * heap_deform_tuple call on each tuple, instead? But that doesn't
4149
+ * work for system columns ...
4150
+ */
4151
+ value1 = heap_getattr (oldtup , attrnum , tupdesc , & isnull1 );
4152
+ value2 = heap_getattr (newtup , attrnum , tupdesc , & isnull2 );
4153
+
4154
+ if (!heap_attr_equals (tupdesc , attrnum , value1 ,
4155
+ value2 , isnull1 , isnull2 ))
4156
+ {
4157
+ modified = bms_add_member (modified ,
4158
+ attrnum -
4159
+ FirstLowInvalidHeapAttributeNumber );
4160
+ continue ;
4161
+ }
4162
+
4163
+ /*
4164
+ * No need to check attributes that can't be stored externally. Note
4165
+ * that system attributes can't be stored externally.
4166
+ */
4167
+ if (attrnum < 0 || isnull1 ||
4168
+ TupleDescAttr (tupdesc , attrnum - 1 )-> attlen != -1 )
4169
+ continue ;
4170
+
4171
+ /*
4172
+ * Check if the old tuple's attribute is stored externally and is a
4173
+ * member of external_cols.
4174
+ */
4175
+ if (VARATT_IS_EXTERNAL ((struct varlena * ) DatumGetPointer (value1 )) &&
4176
+ bms_is_member (attrnum - FirstLowInvalidHeapAttributeNumber ,
4177
+ external_cols ))
4178
+ * has_external = true;
4126
4179
}
4127
4180
4128
4181
return modified ;
@@ -8359,14 +8412,14 @@ log_heap_new_cid(Relation relation, HeapTuple tup)
8359
8412
* Returns NULL if there's no need to log an identity or if there's no suitable
8360
8413
* key defined.
8361
8414
*
8362
- * key_changed should be false if caller knows that no replica identity
8363
- * columns changed value. It's always true in the DELETE case .
8415
+ * Pass key_required true if any replica identity columns changed value, or if
8416
+ * any of them have any external data. Delete must always pass true .
8364
8417
*
8365
8418
* *copy is set to true if the returned tuple is a modified copy rather than
8366
8419
* the same tuple that was passed in.
8367
8420
*/
8368
8421
static HeapTuple
8369
- ExtractReplicaIdentity (Relation relation , HeapTuple tp , bool key_changed ,
8422
+ ExtractReplicaIdentity (Relation relation , HeapTuple tp , bool key_required ,
8370
8423
bool * copy )
8371
8424
{
8372
8425
TupleDesc desc = RelationGetDescr (relation );
@@ -8398,19 +8451,19 @@ ExtractReplicaIdentity(Relation relation, HeapTuple tp, bool key_changed,
8398
8451
return tp ;
8399
8452
}
8400
8453
8401
- /* if the key hasn 't changed and we're only logging the key, we're done */
8402
- if (!key_changed )
8454
+ /* if the key isn 't required and we're only logging the key, we're done */
8455
+ if (!key_required )
8403
8456
return NULL ;
8404
8457
8405
8458
/* find out the replica identity columns */
8406
8459
idattrs = RelationGetIndexAttrBitmap (relation ,
8407
8460
INDEX_ATTR_BITMAP_IDENTITY_KEY );
8408
8461
8409
8462
/*
8410
- * If there's no defined replica identity columns, treat as !key_changed .
8463
+ * If there's no defined replica identity columns, treat as !key_required .
8411
8464
* (This case should not be reachable from heap_update, since that should
8412
- * calculate key_changed accurately. But heap_delete just passes constant
8413
- * true for key_changed , so we can hit this case in deletes.)
8465
+ * calculate key_required accurately. But heap_delete just passes
8466
+ * constant true for key_required , so we can hit this case in deletes.)
8414
8467
*/
8415
8468
if (bms_is_empty (idattrs ))
8416
8469
return NULL ;
0 commit comments