@@ -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_tuple ,
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_changed ,
107
+ static HeapTuple ExtractReplicaIdentity (Relation rel , HeapTuple tup , bool key_required ,
106
108
bool * copy );
107
109
108
110
@@ -2910,6 +2912,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
2910
2912
bool all_visible_cleared_new = false;
2911
2913
bool checked_lockers ;
2912
2914
bool locker_remains ;
2915
+ bool id_has_external = false;
2913
2916
TransactionId xmax_new_tuple ,
2914
2917
xmax_old_tuple ;
2915
2918
uint16 infomask_old_tuple ,
@@ -2994,7 +2997,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
2994
2997
Assert (ItemIdIsNormal (lp ));
2995
2998
2996
2999
/*
2997
- * Fill in enough data in oldtup for HeapDetermineModifiedColumns to work
3000
+ * Fill in enough data in oldtup for HeapDetermineColumnsInfo to work
2998
3001
* properly.
2999
3002
*/
3000
3003
oldtup .t_tableOid = RelationGetRelid (relation );
@@ -3005,9 +3008,17 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3005
3008
/* the new tuple is ready, except for this: */
3006
3009
newtup -> t_tableOid = RelationGetRelid (relation );
3007
3010
3008
- /* Determine columns modified by the update. */
3009
- modified_attrs = HeapDetermineModifiedColumns (relation , interesting_attrs ,
3010
- & oldtup , newtup );
3011
+ /*
3012
+ * Determine columns modified by the update. Additionally, identify
3013
+ * whether any of the unmodified replica identity key attributes in the
3014
+ * old tuple is externally stored or not. This is required because for
3015
+ * such attributes the flattened value won't be WAL logged as part of the
3016
+ * new tuple so we must include it as part of the old_key_tuple. See
3017
+ * ExtractReplicaIdentity.
3018
+ */
3019
+ modified_attrs = HeapDetermineColumnsInfo (relation , interesting_attrs ,
3020
+ id_attrs , & oldtup ,
3021
+ newtup , & id_has_external );
3011
3022
3012
3023
/*
3013
3024
* If we're not updating any "key" column, we can grab a weaker lock type.
@@ -3609,10 +3620,12 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3609
3620
* Compute replica identity tuple before entering the critical section so
3610
3621
* we don't PANIC upon a memory allocation failure.
3611
3622
* ExtractReplicaIdentity() will return NULL if nothing needs to be
3612
- * logged.
3623
+ * logged. Pass old key required as true only if the replica identity key
3624
+ * columns are modified or it has external data.
3613
3625
*/
3614
3626
old_key_tuple = ExtractReplicaIdentity (relation , & oldtup ,
3615
- bms_overlap (modified_attrs , id_attrs ),
3627
+ bms_overlap (modified_attrs , id_attrs ) ||
3628
+ id_has_external ,
3616
3629
& old_key_copied );
3617
3630
3618
3631
/* NO EREPORT(ERROR) from here till changes are logged */
@@ -3768,47 +3781,15 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
3768
3781
}
3769
3782
3770
3783
/*
3771
- * Check if the specified attribute's value is same in both given tuples.
3772
- * Subroutine for HeapDetermineModifiedColumns .
3784
+ * Check if the specified attribute's values are the same. Subroutine for
3785
+ * HeapDetermineColumnsInfo .
3773
3786
*/
3774
3787
static bool
3775
- heap_tuple_attr_equals (TupleDesc tupdesc , int attrnum ,
3776
- HeapTuple tup1 , HeapTuple tup2 )
3788
+ heap_attr_equals (TupleDesc tupdesc , int attrnum , Datum value1 , Datum value2 ,
3789
+ bool isnull1 , bool isnull2 )
3777
3790
{
3778
- Datum value1 ,
3779
- value2 ;
3780
- bool isnull1 ,
3781
- isnull2 ;
3782
3791
Form_pg_attribute att ;
3783
3792
3784
- /*
3785
- * If it's a whole-tuple reference, say "not equal". It's not really
3786
- * worth supporting this case, since it could only succeed after a no-op
3787
- * update, which is hardly a case worth optimizing for.
3788
- */
3789
- if (attrnum == 0 )
3790
- return false;
3791
-
3792
- /*
3793
- * Likewise, automatically say "not equal" for any system attribute other
3794
- * than tableOID; we cannot expect these to be consistent in a HOT chain,
3795
- * or even to be set correctly yet in the new tuple.
3796
- */
3797
- if (attrnum < 0 )
3798
- {
3799
- if (attrnum != TableOidAttributeNumber )
3800
- return false;
3801
- }
3802
-
3803
- /*
3804
- * Extract the corresponding values. XXX this is pretty inefficient if
3805
- * there are many indexed columns. Should HeapDetermineModifiedColumns do
3806
- * a single heap_deform_tuple call on each tuple, instead? But that
3807
- * doesn't work for system columns ...
3808
- */
3809
- value1 = heap_getattr (tup1 , attrnum , tupdesc , & isnull1 );
3810
- value2 = heap_getattr (tup2 , attrnum , tupdesc , & isnull2 );
3811
-
3812
3793
/*
3813
3794
* If one value is NULL and other is not, then they are certainly not
3814
3795
* equal
@@ -3850,24 +3831,96 @@ heap_tuple_attr_equals(TupleDesc tupdesc, int attrnum,
3850
3831
* Given an updated tuple, determine (and return into the output bitmapset),
3851
3832
* from those listed as interesting, the set of columns that changed.
3852
3833
*
3853
- * The input bitmapset is destructively modified; that is OK since this is
3854
- * invoked at most once in heap_update.
3834
+ * has_external indicates if any of the unmodified attributes (from those
3835
+ * listed as interesting) of the old tuple is a member of external_cols and is
3836
+ * stored externally.
3837
+ *
3838
+ * The input interesting_cols bitmapset is destructively modified; that is OK
3839
+ * since this is invoked at most once in heap_update.
3855
3840
*/
3856
3841
static Bitmapset *
3857
- HeapDetermineModifiedColumns (Relation relation , Bitmapset * interesting_cols ,
3858
- HeapTuple oldtup , HeapTuple newtup )
3842
+ HeapDetermineColumnsInfo (Relation relation ,
3843
+ Bitmapset * interesting_cols ,
3844
+ Bitmapset * external_cols ,
3845
+ HeapTuple oldtup , HeapTuple newtup ,
3846
+ bool * has_external )
3859
3847
{
3860
- int attnum ;
3848
+ int attrnum ;
3861
3849
Bitmapset * modified = NULL ;
3850
+ TupleDesc tupdesc = RelationGetDescr (relation );
3862
3851
3863
- while ((attnum = bms_first_member (interesting_cols )) >= 0 )
3852
+ while ((attrnum = bms_first_member (interesting_cols )) >= 0 )
3864
3853
{
3865
- attnum += FirstLowInvalidHeapAttributeNumber ;
3854
+ Datum value1 ,
3855
+ value2 ;
3856
+ bool isnull1 ,
3857
+ isnull2 ;
3858
+
3859
+ attrnum += FirstLowInvalidHeapAttributeNumber ;
3866
3860
3867
- if (!heap_tuple_attr_equals (RelationGetDescr (relation ),
3868
- attnum , oldtup , newtup ))
3861
+ /*
3862
+ * If it's a whole-tuple reference, say "not equal". It's not really
3863
+ * worth supporting this case, since it could only succeed after a
3864
+ * no-op update, which is hardly a case worth optimizing for.
3865
+ */
3866
+ if (attrnum == 0 )
3867
+ {
3869
3868
modified = bms_add_member (modified ,
3870
- attnum - FirstLowInvalidHeapAttributeNumber );
3869
+ attrnum -
3870
+ FirstLowInvalidHeapAttributeNumber );
3871
+ continue ;
3872
+ }
3873
+
3874
+ /*
3875
+ * Likewise, automatically say "not equal" for any system attribute
3876
+ * other than tableOID; we cannot expect these to be consistent in a
3877
+ * HOT chain, or even to be set correctly yet in the new tuple.
3878
+ */
3879
+ if (attrnum < 0 )
3880
+ {
3881
+ if (attrnum != TableOidAttributeNumber )
3882
+ {
3883
+ modified = bms_add_member (modified ,
3884
+ attrnum -
3885
+ FirstLowInvalidHeapAttributeNumber );
3886
+ continue ;
3887
+ }
3888
+ }
3889
+
3890
+ /*
3891
+ * Extract the corresponding values. XXX this is pretty inefficient
3892
+ * if there are many indexed columns. Should we do a single
3893
+ * heap_deform_tuple call on each tuple, instead? But that doesn't
3894
+ * work for system columns ...
3895
+ */
3896
+ value1 = heap_getattr (oldtup , attrnum , tupdesc , & isnull1 );
3897
+ value2 = heap_getattr (newtup , attrnum , tupdesc , & isnull2 );
3898
+
3899
+ if (!heap_attr_equals (tupdesc , attrnum , value1 ,
3900
+ value2 , isnull1 , isnull2 ))
3901
+ {
3902
+ modified = bms_add_member (modified ,
3903
+ attrnum -
3904
+ FirstLowInvalidHeapAttributeNumber );
3905
+ continue ;
3906
+ }
3907
+
3908
+ /*
3909
+ * No need to check attributes that can't be stored externally. Note
3910
+ * that system attributes can't be stored externally.
3911
+ */
3912
+ if (attrnum < 0 || isnull1 ||
3913
+ TupleDescAttr (tupdesc , attrnum - 1 )-> attlen != -1 )
3914
+ continue ;
3915
+
3916
+ /*
3917
+ * Check if the old tuple's attribute is stored externally and is a
3918
+ * member of external_cols.
3919
+ */
3920
+ if (VARATT_IS_EXTERNAL ((struct varlena * ) DatumGetPointer (value1 )) &&
3921
+ bms_is_member (attrnum - FirstLowInvalidHeapAttributeNumber ,
3922
+ external_cols ))
3923
+ * has_external = true;
3871
3924
}
3872
3925
3873
3926
return modified ;
@@ -7620,14 +7673,14 @@ log_heap_new_cid(Relation relation, HeapTuple tup)
7620
7673
* Returns NULL if there's no need to log an identity or if there's no suitable
7621
7674
* key defined.
7622
7675
*
7623
- * key_changed should be false if caller knows that no replica identity
7624
- * columns changed value. It's always true in the DELETE case .
7676
+ * Pass key_required true if any replica identity columns changed value, or if
7677
+ * any of them have any external data. Delete must always pass true .
7625
7678
*
7626
7679
* *copy is set to true if the returned tuple is a modified copy rather than
7627
7680
* the same tuple that was passed in.
7628
7681
*/
7629
7682
static HeapTuple
7630
- ExtractReplicaIdentity (Relation relation , HeapTuple tp , bool key_changed ,
7683
+ ExtractReplicaIdentity (Relation relation , HeapTuple tp , bool key_required ,
7631
7684
bool * copy )
7632
7685
{
7633
7686
TupleDesc desc = RelationGetDescr (relation );
@@ -7659,19 +7712,19 @@ ExtractReplicaIdentity(Relation relation, HeapTuple tp, bool key_changed,
7659
7712
return tp ;
7660
7713
}
7661
7714
7662
- /* if the key hasn 't changed and we're only logging the key, we're done */
7663
- if (!key_changed )
7715
+ /* if the key isn 't required and we're only logging the key, we're done */
7716
+ if (!key_required )
7664
7717
return NULL ;
7665
7718
7666
7719
/* find out the replica identity columns */
7667
7720
idattrs = RelationGetIndexAttrBitmap (relation ,
7668
7721
INDEX_ATTR_BITMAP_IDENTITY_KEY );
7669
7722
7670
7723
/*
7671
- * If there's no defined replica identity columns, treat as !key_changed .
7724
+ * If there's no defined replica identity columns, treat as !key_required .
7672
7725
* (This case should not be reachable from heap_update, since that should
7673
- * calculate key_changed accurately. But heap_delete just passes constant
7674
- * true for key_changed , so we can hit this case in deletes.)
7726
+ * calculate key_required accurately. But heap_delete just passes
7727
+ * constant true for key_required , so we can hit this case in deletes.)
7675
7728
*/
7676
7729
if (bms_is_empty (idattrs ))
7677
7730
return NULL ;
0 commit comments