@@ -76,9 +76,11 @@ static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
76
76
Buffer newbuf ,HeapTuple oldtup ,
77
77
HeapTuple newtup ,HeapTuple old_key_tup ,
78
78
bool all_visible_cleared ,bool new_all_visible_cleared );
79
- static Bitmapset * HeapDetermineModifiedColumns (Relation relation ,
80
- Bitmapset * interesting_cols ,
81
- HeapTuple oldtup ,HeapTuple newtup );
79
+ static Bitmapset * HeapDetermineColumnsInfo (Relation relation ,
80
+ Bitmapset * interesting_cols ,
81
+ Bitmapset * external_cols ,
82
+ HeapTuple oldtup ,HeapTuple newtup ,
83
+ bool * has_external );
82
84
static bool heap_acquire_tuplock (Relation relation ,ItemPointer tid ,
83
85
LockTupleMode mode ,LockWaitPolicy wait_policy ,
84
86
bool * have_tuple_lock );
@@ -102,7 +104,7 @@ static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 in
102
104
static bool ConditionalMultiXactIdWait (MultiXactId multi ,MultiXactStatus status ,
103
105
uint16 infomask ,Relation rel ,int * remaining );
104
106
static XLogRecPtr log_heap_new_cid (Relation relation ,HeapTuple tup );
105
- static HeapTuple ExtractReplicaIdentity (Relation rel ,HeapTuple tup ,bool key_modified ,
107
+ static HeapTuple ExtractReplicaIdentity (Relation rel ,HeapTuple tup ,bool key_required ,
106
108
bool * copy );
107
109
108
110
@@ -2941,6 +2943,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
2941
2943
bool all_visible_cleared_new = false;
2942
2944
bool checked_lockers ;
2943
2945
bool locker_remains ;
2946
+ bool id_has_external = false;
2944
2947
TransactionId xmax_new_tuple ,
2945
2948
xmax_old_tuple ;
2946
2949
uint16 infomask_old_tuple ,
@@ -3025,7 +3028,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3025
3028
Assert (ItemIdIsNormal (lp ));
3026
3029
3027
3030
/*
3028
- * Fill in enough data in oldtup forHeapDetermineModifiedColumns to work
3031
+ * Fill in enough data in oldtup forHeapDetermineColumnsInfo to work
3029
3032
* properly.
3030
3033
*/
3031
3034
oldtup .t_tableOid = RelationGetRelid (relation );
@@ -3036,9 +3039,17 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3036
3039
/* the new tuple is ready, except for this: */
3037
3040
newtup -> t_tableOid = RelationGetRelid (relation );
3038
3041
3039
- /* Determine columns modified by the update. */
3040
- modified_attrs = HeapDetermineModifiedColumns (relation ,interesting_attrs ,
3041
- & oldtup ,newtup );
3042
+ /*
3043
+ * Determine columns modified by the update. Additionally, identify
3044
+ * whether any of the unmodified replica identity key attributes in the
3045
+ * old tuple is externally stored or not. This is required because for
3046
+ * such attributes the flattened value won't be WAL logged as part of the
3047
+ * new tuple so we must include it as part of the old_key_tuple. See
3048
+ * ExtractReplicaIdentity.
3049
+ */
3050
+ modified_attrs = HeapDetermineColumnsInfo (relation ,interesting_attrs ,
3051
+ id_attrs ,& oldtup ,
3052
+ newtup ,& id_has_external );
3042
3053
3043
3054
/*
3044
3055
* If we're not updating any "key" column, we can grab a weaker lock type.
@@ -3639,10 +3650,12 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3639
3650
* Compute replica identity tuple before entering the critical section so
3640
3651
* we don't PANIC upon a memory allocation failure.
3641
3652
* ExtractReplicaIdentity() will return NULL if nothing needs to be
3642
- * logged.
3653
+ * logged. Pass old key required as true only if the replica identity key
3654
+ * columns are modified or it has external data.
3643
3655
*/
3644
3656
old_key_tuple = ExtractReplicaIdentity (relation ,& oldtup ,
3645
- bms_overlap (modified_attrs ,id_attrs ),
3657
+ bms_overlap (modified_attrs ,id_attrs )||
3658
+ id_has_external ,
3646
3659
& old_key_copied );
3647
3660
3648
3661
/* NO EREPORT(ERROR) from here till changes are logged */
@@ -3798,47 +3811,15 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3798
3811
}
3799
3812
3800
3813
/*
3801
- * Check if the specified attribute'svalue is same in both given tuples.
3802
- *Subroutine for HeapDetermineModifiedColumns .
3814
+ * Check if the specified attribute'svalues are the same. Subroutine for
3815
+ *HeapDetermineColumnsInfo .
3803
3816
*/
3804
3817
static bool
3805
- heap_tuple_attr_equals (TupleDesc tupdesc ,int attrnum ,
3806
- HeapTuple tup1 , HeapTuple tup2 )
3818
+ heap_attr_equals (TupleDesc tupdesc ,int attrnum , Datum value1 , Datum value2 ,
3819
+ bool isnull1 , bool isnull2 )
3807
3820
{
3808
- Datum value1 ,
3809
- value2 ;
3810
- bool isnull1 ,
3811
- isnull2 ;
3812
3821
Form_pg_attribute att ;
3813
3822
3814
- /*
3815
- * If it's a whole-tuple reference, say "not equal". It's not really
3816
- * worth supporting this case, since it could only succeed after a no-op
3817
- * update, which is hardly a case worth optimizing for.
3818
- */
3819
- if (attrnum == 0 )
3820
- return false;
3821
-
3822
- /*
3823
- * Likewise, automatically say "not equal" for any system attribute other
3824
- * than tableOID; we cannot expect these to be consistent in a HOT chain,
3825
- * or even to be set correctly yet in the new tuple.
3826
- */
3827
- if (attrnum < 0 )
3828
- {
3829
- if (attrnum != TableOidAttributeNumber )
3830
- return false;
3831
- }
3832
-
3833
- /*
3834
- * Extract the corresponding values. XXX this is pretty inefficient if
3835
- * there are many indexed columns. Should HeapDetermineModifiedColumns do
3836
- * a single heap_deform_tuple call on each tuple, instead?But that
3837
- * doesn't work for system columns ...
3838
- */
3839
- value1 = heap_getattr (tup1 ,attrnum ,tupdesc ,& isnull1 );
3840
- value2 = heap_getattr (tup2 ,attrnum ,tupdesc ,& isnull2 );
3841
-
3842
3823
/*
3843
3824
* If one value is NULL and other is not, then they are certainly not
3844
3825
* equal
@@ -3880,24 +3861,96 @@ heap_tuple_attr_equals(TupleDesc tupdesc, int attrnum,
3880
3861
* Given an updated tuple, determine (and return into the output bitmapset),
3881
3862
* from those listed as interesting, the set of columns that changed.
3882
3863
*
3883
- * The input bitmapset is destructively modified; that is OK since this is
3884
- * invoked at most once in heap_update.
3864
+ * has_external indicates if any of the unmodified attributes (from those
3865
+ * listed as interesting) of the old tuple is a member of external_cols and is
3866
+ * stored externally.
3867
+ *
3868
+ * The input interesting_cols bitmapset is destructively modified; that is OK
3869
+ * since this is invoked at most once in heap_update.
3885
3870
*/
3886
3871
static Bitmapset *
3887
- HeapDetermineModifiedColumns (Relation relation ,Bitmapset * interesting_cols ,
3888
- HeapTuple oldtup ,HeapTuple newtup )
3872
+ HeapDetermineColumnsInfo (Relation relation ,
3873
+ Bitmapset * interesting_cols ,
3874
+ Bitmapset * external_cols ,
3875
+ HeapTuple oldtup ,HeapTuple newtup ,
3876
+ bool * has_external )
3889
3877
{
3890
- int attnum ;
3878
+ int attrnum ;
3891
3879
Bitmapset * modified = NULL ;
3880
+ TupleDesc tupdesc = RelationGetDescr (relation );
3892
3881
3893
- while ((attnum = bms_first_member (interesting_cols )) >=0 )
3882
+ while ((attrnum = bms_first_member (interesting_cols )) >=0 )
3894
3883
{
3895
- attnum += FirstLowInvalidHeapAttributeNumber ;
3884
+ Datum value1 ,
3885
+ value2 ;
3886
+ bool isnull1 ,
3887
+ isnull2 ;
3888
+
3889
+ attrnum += FirstLowInvalidHeapAttributeNumber ;
3896
3890
3897
- if (!heap_tuple_attr_equals (RelationGetDescr (relation ),
3898
- attnum ,oldtup ,newtup ))
3891
+ /*
3892
+ * If it's a whole-tuple reference, say "not equal". It's not really
3893
+ * worth supporting this case, since it could only succeed after a
3894
+ * no-op update, which is hardly a case worth optimizing for.
3895
+ */
3896
+ if (attrnum == 0 )
3897
+ {
3899
3898
modified = bms_add_member (modified ,
3900
- attnum - FirstLowInvalidHeapAttributeNumber );
3899
+ attrnum -
3900
+ FirstLowInvalidHeapAttributeNumber );
3901
+ continue ;
3902
+ }
3903
+
3904
+ /*
3905
+ * Likewise, automatically say "not equal" for any system attribute
3906
+ * other than tableOID; we cannot expect these to be consistent in a
3907
+ * HOT chain, or even to be set correctly yet in the new tuple.
3908
+ */
3909
+ if (attrnum < 0 )
3910
+ {
3911
+ if (attrnum != TableOidAttributeNumber )
3912
+ {
3913
+ modified = bms_add_member (modified ,
3914
+ attrnum -
3915
+ FirstLowInvalidHeapAttributeNumber );
3916
+ continue ;
3917
+ }
3918
+ }
3919
+
3920
+ /*
3921
+ * Extract the corresponding values. XXX this is pretty inefficient
3922
+ * if there are many indexed columns. Should we do a single
3923
+ * heap_deform_tuple call on each tuple, instead?But that doesn't
3924
+ * work for system columns ...
3925
+ */
3926
+ value1 = heap_getattr (oldtup ,attrnum ,tupdesc ,& isnull1 );
3927
+ value2 = heap_getattr (newtup ,attrnum ,tupdesc ,& isnull2 );
3928
+
3929
+ if (!heap_attr_equals (tupdesc ,attrnum ,value1 ,
3930
+ value2 ,isnull1 ,isnull2 ))
3931
+ {
3932
+ modified = bms_add_member (modified ,
3933
+ attrnum -
3934
+ FirstLowInvalidHeapAttributeNumber );
3935
+ continue ;
3936
+ }
3937
+
3938
+ /*
3939
+ * No need to check attributes that can't be stored externally. Note
3940
+ * that system attributes can't be stored externally.
3941
+ */
3942
+ if (attrnum < 0 || isnull1 ||
3943
+ TupleDescAttr (tupdesc ,attrnum - 1 )-> attlen != -1 )
3944
+ continue ;
3945
+
3946
+ /*
3947
+ * Check if the old tuple's attribute is stored externally and is a
3948
+ * member of external_cols.
3949
+ */
3950
+ if (VARATT_IS_EXTERNAL ((struct varlena * )DatumGetPointer (value1 ))&&
3951
+ bms_is_member (attrnum - FirstLowInvalidHeapAttributeNumber ,
3952
+ external_cols ))
3953
+ * has_external = true;
3901
3954
}
3902
3955
3903
3956
return modified ;
@@ -7661,14 +7714,14 @@ log_heap_new_cid(Relation relation, HeapTuple tup)
7661
7714
* Returns NULL if there's no need to log an identity or if there's no suitable
7662
7715
* key defined.
7663
7716
*
7664
- *key_changed should be false ifcaller knows that no replica identity
7665
- *columns changed value. It's always true in the DELETE case .
7717
+ *Pass key_required true ifany replica identity columns changed value, or if
7718
+ *any of them have any external data. Delete must always pass true .
7666
7719
*
7667
7720
* *copy is set to true if the returned tuple is a modified copy rather than
7668
7721
* the same tuple that was passed in.
7669
7722
*/
7670
7723
static HeapTuple
7671
- ExtractReplicaIdentity (Relation relation ,HeapTuple tp ,bool key_changed ,
7724
+ ExtractReplicaIdentity (Relation relation ,HeapTuple tp ,bool key_required ,
7672
7725
bool * copy )
7673
7726
{
7674
7727
TupleDesc desc = RelationGetDescr (relation );
@@ -7700,19 +7753,19 @@ ExtractReplicaIdentity(Relation relation, HeapTuple tp, bool key_changed,
7700
7753
return tp ;
7701
7754
}
7702
7755
7703
- /* if the keyhasn 'tchanged and we're only logging the key, we're done */
7704
- if (!key_changed )
7756
+ /* if the keyisn 'trequired and we're only logging the key, we're done */
7757
+ if (!key_required )
7705
7758
return NULL ;
7706
7759
7707
7760
/* find out the replica identity columns */
7708
7761
idattrs = RelationGetIndexAttrBitmap (relation ,
7709
7762
INDEX_ATTR_BITMAP_IDENTITY_KEY );
7710
7763
7711
7764
/*
7712
- * If there's no defined replica identity columns, treat as !key_changed .
7765
+ * If there's no defined replica identity columns, treat as !key_required .
7713
7766
* (This case should not be reachable from heap_update, since that should
7714
- * calculatekey_changed accurately. But heap_delete just passes constant
7715
- * true forkey_changed , so we can hit this case in deletes.)
7767
+ * calculatekey_required accurately. But heap_delete just passes
7768
+ *constant true forkey_required , so we can hit this case in deletes.)
7716
7769
*/
7717
7770
if (bms_is_empty (idattrs ))
7718
7771
return NULL ;