@@ -78,9 +78,11 @@ static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
7878 Buffer newbuf , HeapTuple oldtup ,
7979 HeapTuple newtup , HeapTuple old_key_tuple ,
8080 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 );
8486static bool heap_acquire_tuplock (Relation relation , ItemPointer tid ,
8587 LockTupleMode mode , LockWaitPolicy wait_policy ,
8688 bool * have_tuple_lock );
@@ -106,7 +108,7 @@ static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status
106108static void index_delete_sort (TM_IndexDeleteOp * delstate );
107109static int bottomup_sort_and_shrink (TM_IndexDeleteOp * delstate );
108110static 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 ,
110112 bool * copy );
111113
112114
@@ -3184,6 +3186,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
31843186 bool all_visible_cleared_new = false;
31853187 bool checked_lockers ;
31863188 bool locker_remains ;
3189+ bool id_has_external = false;
31873190 TransactionId xmax_new_tuple ,
31883191 xmax_old_tuple ;
31893192 uint16 infomask_old_tuple ,
@@ -3251,7 +3254,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
32513254 Assert (ItemIdIsNormal (lp ));
32523255
32533256 /*
3254- * Fill in enough data in oldtup for HeapDetermineModifiedColumns to work
3257+ * Fill in enough data in oldtup for HeapDetermineColumnsInfo to work
32553258 * properly.
32563259 */
32573260 oldtup .t_tableOid = RelationGetRelid (relation );
@@ -3262,9 +3265,17 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
32623265 /* the new tuple is ready, except for this: */
32633266 newtup -> t_tableOid = RelationGetRelid (relation );
32643267
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 );
32683279
32693280 /*
32703281 * 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,
38643875 * Compute replica identity tuple before entering the critical section so
38653876 * we don't PANIC upon a memory allocation failure.
38663877 * 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.
38683880 */
38693881 old_key_tuple = ExtractReplicaIdentity (relation , & oldtup ,
3870- bms_overlap (modified_attrs , id_attrs ),
3882+ bms_overlap (modified_attrs , id_attrs ) ||
3883+ id_has_external ,
38713884 & old_key_copied );
38723885
38733886 /* NO EREPORT(ERROR) from here till changes are logged */
@@ -4023,47 +4036,15 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
40234036}
40244037
40254038/*
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 .
40284041 */
40294042static 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 )
40324045{
4033- Datum value1 ,
4034- value2 ;
4035- bool isnull1 ,
4036- isnull2 ;
40374046 Form_pg_attribute att ;
40384047
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-
40674048 /*
40684049 * If one value is NULL and other is not, then they are certainly not
40694050 * equal
@@ -4105,24 +4086,96 @@ heap_tuple_attr_equals(TupleDesc tupdesc, int attrnum,
41054086 * Given an updated tuple, determine (and return into the output bitmapset),
41064087 * from those listed as interesting, the set of columns that changed.
41074088 *
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.
41104095 */
41114096static 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 )
41144102{
4115- int attnum ;
4103+ int attrnum ;
41164104 Bitmapset * modified = NULL ;
4105+ TupleDesc tupdesc = RelationGetDescr (relation );
41174106
4118- while ((attnum = bms_first_member (interesting_cols )) >= 0 )
4107+ while ((attrnum = bms_first_member (interesting_cols )) >= 0 )
41194108 {
4120- attnum += FirstLowInvalidHeapAttributeNumber ;
4109+ Datum value1 ,
4110+ value2 ;
4111+ bool isnull1 ,
4112+ isnull2 ;
4113+
4114+ attrnum += FirstLowInvalidHeapAttributeNumber ;
41214115
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+ {
41244123 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;
41264179 }
41274180
41284181 return modified ;
@@ -8359,14 +8412,14 @@ log_heap_new_cid(Relation relation, HeapTuple tup)
83598412 * Returns NULL if there's no need to log an identity or if there's no suitable
83608413 * key defined.
83618414 *
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 .
83648417 *
83658418 * *copy is set to true if the returned tuple is a modified copy rather than
83668419 * the same tuple that was passed in.
83678420 */
83688421static HeapTuple
8369- ExtractReplicaIdentity (Relation relation , HeapTuple tp , bool key_changed ,
8422+ ExtractReplicaIdentity (Relation relation , HeapTuple tp , bool key_required ,
83708423 bool * copy )
83718424{
83728425 TupleDesc desc = RelationGetDescr (relation );
@@ -8398,19 +8451,19 @@ ExtractReplicaIdentity(Relation relation, HeapTuple tp, bool key_changed,
83988451 return tp ;
83998452 }
84008453
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 )
84038456 return NULL ;
84048457
84058458 /* find out the replica identity columns */
84068459 idattrs = RelationGetIndexAttrBitmap (relation ,
84078460 INDEX_ATTR_BITMAP_IDENTITY_KEY );
84088461
84098462 /*
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 .
84118464 * (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.)
84148467 */
84158468 if (bms_is_empty (idattrs ))
84168469 return NULL ;
0 commit comments