@@ -78,9 +78,11 @@ static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
7878Buffer newbuf ,HeapTuple oldtup ,
7979HeapTuple newtup ,HeapTuple old_key_tuple ,
8080bool 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 ,
8587LockTupleMode mode ,LockWaitPolicy wait_policy ,
8688bool * 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 ,
110112bool * copy );
111113
112114
@@ -3185,6 +3187,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
31853187bool all_visible_cleared_new = false;
31863188bool checked_lockers ;
31873189bool locker_remains ;
3190+ bool id_has_external = false;
31883191TransactionId xmax_new_tuple ,
31893192xmax_old_tuple ;
31903193uint16 infomask_old_tuple ,
@@ -3269,7 +3272,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
32693272Assert (ItemIdIsNormal (lp ));
32703273
32713274/*
3272- * Fill in enough data in oldtup forHeapDetermineModifiedColumns to work
3275+ * Fill in enough data in oldtup forHeapDetermineColumnsInfo to work
32733276 * properly.
32743277 */
32753278oldtup .t_tableOid = RelationGetRelid (relation );
@@ -3280,9 +3283,17 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
32803283/* the new tuple is ready, except for this: */
32813284newtup -> t_tableOid = RelationGetRelid (relation );
32823285
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 );
32863297
32873298/*
32883299 * 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,
38833894 * Compute replica identity tuple before entering the critical section so
38843895 * we don't PANIC upon a memory allocation failure.
38853896 * 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.
38873899 */
38883900old_key_tuple = ExtractReplicaIdentity (relation ,& oldtup ,
3889- bms_overlap (modified_attrs ,id_attrs ),
3901+ bms_overlap (modified_attrs ,id_attrs )||
3902+ id_has_external ,
38903903& old_key_copied );
38913904
38923905/* NO EREPORT(ERROR) from here till changes are logged */
@@ -4042,47 +4055,15 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
40424055}
40434056
40444057/*
4045- * Check if the specified attribute'svalue is same in both given tuples.
4046- *Subroutine for HeapDetermineModifiedColumns .
4058+ * Check if the specified attribute'svalues are the same. Subroutine for
4059+ *HeapDetermineColumnsInfo .
40474060 */
40484061static 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 )
40514064{
4052- Datum value1 ,
4053- value2 ;
4054- bool isnull1 ,
4055- isnull2 ;
40564065Form_pg_attribute att ;
40574066
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-
40864067/*
40874068 * If one value is NULL and other is not, then they are certainly not
40884069 * equal
@@ -4124,24 +4105,96 @@ heap_tuple_attr_equals(TupleDesc tupdesc, int attrnum,
41244105 * Given an updated tuple, determine (and return into the output bitmapset),
41254106 * from those listed as interesting, the set of columns that changed.
41264107 *
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.
41294114 */
41304115static 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 )
41334121{
4134- int attnum ;
4122+ int attrnum ;
41354123Bitmapset * modified = NULL ;
4124+ TupleDesc tupdesc = RelationGetDescr (relation );
41364125
4137- while ((attnum = bms_first_member (interesting_cols )) >=0 )
4126+ while ((attrnum = bms_first_member (interesting_cols )) >=0 )
41384127{
4139- attnum += FirstLowInvalidHeapAttributeNumber ;
4128+ Datum value1 ,
4129+ value2 ;
4130+ bool isnull1 ,
4131+ isnull2 ;
4132+
4133+ attrnum += FirstLowInvalidHeapAttributeNumber ;
41404134
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+ {
41434142modified = 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;
41454198}
41464199
41474200return modified ;
@@ -8306,14 +8359,14 @@ log_heap_new_cid(Relation relation, HeapTuple tup)
83068359 * Returns NULL if there's no need to log an identity or if there's no suitable
83078360 * key defined.
83088361 *
8309- *key_changed should be false ifcaller knows that no replica identity
8310- *columns changed value. It's always true in the DELETE case .
8362+ *Pass key_required true ifany replica identity columns changed value, or if
8363+ *any of them have any external data. Delete must always pass true .
83118364 *
83128365 * *copy is set to true if the returned tuple is a modified copy rather than
83138366 * the same tuple that was passed in.
83148367 */
83158368static HeapTuple
8316- ExtractReplicaIdentity (Relation relation ,HeapTuple tp ,bool key_changed ,
8369+ ExtractReplicaIdentity (Relation relation ,HeapTuple tp ,bool key_required ,
83178370bool * copy )
83188371{
83198372TupleDesc desc = RelationGetDescr (relation );
@@ -8345,19 +8398,19 @@ ExtractReplicaIdentity(Relation relation, HeapTuple tp, bool key_changed,
83458398return tp ;
83468399}
83478400
8348- /* if the keyhasn 'tchanged and we're only logging the key, we're done */
8349- if (!key_changed )
8401+ /* if the keyisn 'trequired and we're only logging the key, we're done */
8402+ if (!key_required )
83508403return NULL ;
83518404
83528405/* find out the replica identity columns */
83538406idattrs = RelationGetIndexAttrBitmap (relation ,
83548407INDEX_ATTR_BITMAP_IDENTITY_KEY );
83558408
83568409/*
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 .
83588411 * (This case should not be reachable from heap_update, since that should
8359- * calculatekey_changed accurately. But heap_delete just passes constant
8360- * true forkey_changed , so we can hit this case in deletes.)
8412+ * calculatekey_required accurately. But heap_delete just passes
8413+ *constant true forkey_required , so we can hit this case in deletes.)
83618414 */
83628415if (bms_is_empty (idattrs ))
83638416return NULL ;