43
43
#include "utils/snapmgr.h"
44
44
#include "utils/typcache.h"
45
45
#include "utils/tqual.h"
46
-
46
+ #include "nodes/bitmapset.h"
47
47
48
48
#undef TOAST_DEBUG
49
49
@@ -69,7 +69,7 @@ typedef struct toast_compress_header
69
69
70
70
static void toast_delete_datum (Relation rel , Datum value , bool is_speculative );
71
71
static Datum toast_save_datum (Relation rel , Datum value ,
72
- struct varlena * oldexternal , int options );
72
+ struct varlena * oldexternal , int options , Bitmapset * update_chunks );
73
73
static bool toastrel_valueid_exists (Relation toastrel , Oid valueid );
74
74
static bool toastid_valueid_exists (Oid toastrelid , Oid valueid );
75
75
static struct varlena * toast_fetch_datum (struct varlena * attr );
@@ -511,6 +511,28 @@ toast_delete(Relation rel, HeapTuple oldtup, bool is_speculative)
511
511
}
512
512
}
513
513
514
+ static Bitmapset *
515
+ build_diff_map (char const * old_value , char const * new_value , size_t old_size , size_t new_size )
516
+ {
517
+ Bitmapset * map = NULL ;
518
+ int i ;
519
+ size_t size = Min (old_size , new_size );
520
+
521
+ for (i = 0 ; size != 0 ; i ++ )
522
+ {
523
+ int chunk_size = size > TOAST_MAX_CHUNK_SIZE ? TOAST_MAX_CHUNK_SIZE : size ;
524
+ if (memcmp (old_value , new_value , chunk_size ) != 0 ) {
525
+ map = bms_add_member (map , i );
526
+ }
527
+ size -= chunk_size ;
528
+ old_value += chunk_size ;
529
+ new_value += chunk_size ;
530
+ }
531
+ if (old_size != new_size ) {
532
+ map = bms_add_member (map , new_size / TOAST_MAX_CHUNK_SIZE );
533
+ }
534
+ return map ;
535
+ }
514
536
515
537
/* ----------
516
538
* toast_insert_or_update -
@@ -542,6 +564,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
542
564
bool need_change = false;
543
565
bool need_free = false;
544
566
bool need_delold = false;
567
+ bool need_delmap = false;
545
568
bool has_nulls = false;
546
569
547
570
Size maxDataLen ;
@@ -556,6 +579,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
556
579
int32 toast_sizes [MaxHeapAttributeNumber ];
557
580
bool toast_free [MaxHeapAttributeNumber ];
558
581
bool toast_delold [MaxHeapAttributeNumber ];
582
+ Bitmapset * * update_chunks ;
559
583
560
584
/*
561
585
* Ignore the INSERT_SPECULATIVE option. Speculative insertions/super
@@ -599,6 +623,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
599
623
memset (toast_oldexternal , 0 , numAttrs * sizeof (struct varlena * ));
600
624
memset (toast_free , 0 , numAttrs * sizeof (bool ));
601
625
memset (toast_delold , 0 , numAttrs * sizeof (bool ));
626
+ update_chunks = palloc0 (numAttrs * sizeof (Bitmapset * ));
602
627
603
628
for (i = 0 ; i < numAttrs ; i ++ )
604
629
{
@@ -630,7 +655,32 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
630
655
* after the update
631
656
*/
632
657
toast_delold [i ] = true;
633
- need_delold = true;
658
+
659
+ if (VARATT_IS_4B (new_value ))
660
+ {
661
+ struct varlena * orig_value = heap_tuple_untoast_attr (old_value );
662
+ size_t old_size = VARSIZE (orig_value ) - VARHDRSZ ;
663
+ size_t new_size = VARSIZE (new_value ) - VARHDRSZ ;
664
+ Bitmapset * map = build_diff_map ((char * ) VARDATA (orig_value ),
665
+ (char * ) VARDATA (new_value ),
666
+ old_size ,
667
+ new_size );
668
+ pfree (orig_value );
669
+ /* If less than half of the chunks are changed
670
+ * and attribute is not compressed then use update rather than insert
671
+ */
672
+ if ((old_size + TOAST_MAX_CHUNK_SIZE - 1 ) / TOAST_MAX_CHUNK_SIZE == (new_size + TOAST_MAX_CHUNK_SIZE - 1 ) / TOAST_MAX_CHUNK_SIZE
673
+ && bms_num_members (map )* 2 < (new_size + TOAST_MAX_CHUNK_SIZE - 1 ) / TOAST_MAX_CHUNK_SIZE
674
+ && TupleDescAttr (tupleDesc , i )-> attstorage == 'e' )
675
+ {
676
+ update_chunks [i ] = map ;
677
+ need_delmap = true;
678
+ toast_delold [i ] = false;
679
+ toast_oldexternal [i ] = old_value ;
680
+ }
681
+ }
682
+ if (toast_delold [i ])
683
+ need_delold = true;
634
684
}
635
685
else
636
686
{
@@ -812,7 +862,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
812
862
old_value = toast_values [i ];
813
863
toast_action [i ] = 'p' ;
814
864
toast_values [i ] = toast_save_datum (rel , toast_values [i ],
815
- toast_oldexternal [i ], options );
865
+ toast_oldexternal [i ], options ,
866
+ update_chunks [i ]);
816
867
if (toast_free [i ])
817
868
pfree (DatumGetPointer (old_value ));
818
869
toast_free [i ] = true;
@@ -865,7 +916,9 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
865
916
old_value = toast_values [i ];
866
917
toast_action [i ] = 'p' ;
867
918
toast_values [i ] = toast_save_datum (rel , toast_values [i ],
868
- toast_oldexternal [i ], options );
919
+ toast_oldexternal [i ],
920
+ options ,
921
+ update_chunks [i ]);
869
922
if (toast_free [i ])
870
923
pfree (DatumGetPointer (old_value ));
871
924
toast_free [i ] = true;
@@ -979,7 +1032,9 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
979
1032
old_value = toast_values [i ];
980
1033
toast_action [i ] = 'p' ;
981
1034
toast_values [i ] = toast_save_datum (rel , toast_values [i ],
982
- toast_oldexternal [i ], options );
1035
+ toast_oldexternal [i ],
1036
+ options ,
1037
+ update_chunks [i ]);
983
1038
if (toast_free [i ])
984
1039
pfree (DatumGetPointer (old_value ));
985
1040
toast_free [i ] = true;
@@ -1067,6 +1122,12 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
1067
1122
if (toast_delold [i ])
1068
1123
toast_delete_datum (rel , toast_oldvalues [i ], false);
1069
1124
1125
+ if (need_delmap )
1126
+ for (i = 0 ; i < numAttrs ; i ++ )
1127
+ bms_free (update_chunks [i ]);
1128
+
1129
+ pfree (update_chunks );
1130
+
1070
1131
return result_tuple ;
1071
1132
}
1072
1133
@@ -1452,6 +1513,61 @@ toast_get_valid_index(Oid toastoid, LOCKMODE lock)
1452
1513
}
1453
1514
1454
1515
1516
+ static void
1517
+ toast_update_chunk (Relation toastrel , struct varatt_external * toast_pointer , int chunk_id , HeapTuple newtoasttup )
1518
+ {
1519
+ Relation * toastidxs ;
1520
+ ScanKeyData toastkey [2 ];
1521
+ SysScanDesc toastscan ;
1522
+ HeapTuple oldtoasttup ;
1523
+ int num_indexes ;
1524
+ int validIndex ;
1525
+ SnapshotData SnapshotToast ;
1526
+
1527
+ /* Fetch valid relation used for process */
1528
+ validIndex = toast_open_indexes (toastrel ,
1529
+ RowExclusiveLock ,
1530
+ & toastidxs ,
1531
+ & num_indexes );
1532
+
1533
+ /*
1534
+ * Setup a scan key to find specified chunk
1535
+ */
1536
+ ScanKeyInit (& toastkey [0 ],
1537
+ (AttrNumber ) 1 ,
1538
+ BTEqualStrategyNumber , F_OIDEQ ,
1539
+ ObjectIdGetDatum (toast_pointer -> va_valueid ));
1540
+ ScanKeyInit (& toastkey [1 ],
1541
+ (AttrNumber ) 2 ,
1542
+ BTEqualStrategyNumber , F_INT4EQ ,
1543
+ Int32GetDatum (chunk_id ));
1544
+
1545
+ /*
1546
+ * Find all the chunks. (We don't actually care whether we see them in
1547
+ * sequence or not, but since we've already locked the index we might as
1548
+ * well use systable_beginscan_ordered.)
1549
+ */
1550
+ init_toast_snapshot (& SnapshotToast );
1551
+ toastscan = systable_beginscan (toastrel , validIndex ,
1552
+ false, & SnapshotToast , 2 , toastkey );
1553
+ oldtoasttup = systable_getnext (toastscan );
1554
+
1555
+ if (oldtoasttup == NULL )
1556
+ elog (ERROR , "Failed to locate TOAST chunk %d.%d" ,
1557
+ toast_pointer -> va_valueid ,
1558
+ chunk_id );
1559
+
1560
+ simple_heap_update (toastrel , & oldtoasttup -> t_self , newtoasttup );
1561
+
1562
+ elog (LOG , "Update toast chunk %d.%d" ,
1563
+ toast_pointer -> va_valueid ,
1564
+ chunk_id );
1565
+
1566
+ systable_endscan (toastscan );
1567
+ toast_close_indexes (toastidxs , num_indexes , RowExclusiveLock );
1568
+ }
1569
+
1570
+
1455
1571
/* ----------
1456
1572
* toast_save_datum -
1457
1573
*
@@ -1466,7 +1582,8 @@ toast_get_valid_index(Oid toastoid, LOCKMODE lock)
1466
1582
*/
1467
1583
static Datum
1468
1584
toast_save_datum (Relation rel , Datum value ,
1469
- struct varlena * oldexternal , int options )
1585
+ struct varlena * oldexternal , int options ,
1586
+ Bitmapset * update_chunks )
1470
1587
{
1471
1588
Relation toastrel ;
1472
1589
Relation * toastidxs ;
@@ -1567,7 +1684,18 @@ toast_save_datum(Relation rel, Datum value,
1567
1684
* options have been changed), we have to pick a value ID that doesn't
1568
1685
* conflict with either new or existing toast value OIDs.
1569
1686
*/
1570
- if (!OidIsValid (rel -> rd_toastoid ))
1687
+ if (update_chunks )
1688
+ {
1689
+ /* If we updte existed toask chaunk, use old toast OID */
1690
+ struct varatt_external old_toast_pointer ;
1691
+ Assert (oldexternal != NULL );
1692
+ Assert (VARATT_IS_EXTERNAL_ONDISK (oldexternal ));
1693
+ /* Must copy to access aligned fields */
1694
+ VARATT_EXTERNAL_GET_POINTER (old_toast_pointer , oldexternal );
1695
+ Assert (old_toast_pointer .va_toastrelid == toast_pointer .va_toastrelid );
1696
+ toast_pointer .va_valueid = old_toast_pointer .va_valueid ;
1697
+ }
1698
+ else if (!OidIsValid (rel -> rd_toastoid ))
1571
1699
{
1572
1700
/* normal case: just choose an unused OID */
1573
1701
toast_pointer .va_valueid =
@@ -1659,12 +1787,20 @@ toast_save_datum(Relation rel, Datum value,
1659
1787
/*
1660
1788
* Build a tuple and store it
1661
1789
*/
1662
- t_values [1 ] = Int32GetDatum (chunk_seq ++ );
1790
+ t_values [1 ] = Int32GetDatum (chunk_seq );
1663
1791
SET_VARSIZE (& chunk_data , chunk_size + VARHDRSZ );
1664
1792
memcpy (VARDATA (& chunk_data ), data_p , chunk_size );
1665
1793
toasttup = heap_form_tuple (toasttupDesc , t_values , t_isnull );
1666
1794
1667
- heap_insert (toastrel , toasttup , mycid , options , NULL );
1795
+ if (update_chunks != NULL )
1796
+ {
1797
+ if (bms_is_member (chunk_seq , update_chunks ))
1798
+ toast_update_chunk (toastrel , & toast_pointer , chunk_seq , toasttup );
1799
+ else
1800
+ goto skip_chunk ;
1801
+ }
1802
+ else
1803
+ heap_insert (toastrel , toasttup , mycid , options , NULL );
1668
1804
1669
1805
/*
1670
1806
* Create the index entry. We cheat a little here by not using
@@ -1677,18 +1813,21 @@ toast_save_datum(Relation rel, Datum value,
1677
1813
* Note also that there had better not be any user-created index on
1678
1814
* the TOAST table, since we don't bother to update anything else.
1679
1815
*/
1680
- for ( i = 0 ; i < num_indexes ; i ++ )
1816
+ if (! HeapTupleIsHeapOnly ( toasttup ) )
1681
1817
{
1682
- /* Only index relations marked as ready can be updated */
1683
- if (IndexIsReady (toastidxs [i ]-> rd_index ))
1684
- index_insert (toastidxs [i ], t_values , t_isnull ,
1685
- & (toasttup -> t_self ),
1686
- toastrel ,
1687
- toastidxs [i ]-> rd_index -> indisunique ?
1688
- UNIQUE_CHECK_YES : UNIQUE_CHECK_NO ,
1689
- NULL );
1818
+ for (i = 0 ; i < num_indexes ; i ++ )
1819
+ {
1820
+ /* Only index relations marked as ready can be updated */
1821
+ if (IndexIsReady (toastidxs [i ]-> rd_index ))
1822
+ index_insert (toastidxs [i ], t_values , t_isnull ,
1823
+ & (toasttup -> t_self ),
1824
+ toastrel ,
1825
+ toastidxs [i ]-> rd_index -> indisunique ?
1826
+ UNIQUE_CHECK_YES : UNIQUE_CHECK_NO ,
1827
+ NULL );
1828
+ }
1690
1829
}
1691
-
1830
+ skip_chunk :
1692
1831
/*
1693
1832
* Free memory
1694
1833
*/
@@ -1699,6 +1838,7 @@ toast_save_datum(Relation rel, Datum value,
1699
1838
*/
1700
1839
data_todo -= chunk_size ;
1701
1840
data_p += chunk_size ;
1841
+ chunk_seq += 1 ;
1702
1842
}
1703
1843
1704
1844
/*
@@ -2387,6 +2527,6 @@ init_toast_snapshot(Snapshot toast_snapshot)
2387
2527
2388
2528
if (snapshot == NULL )
2389
2529
elog (ERROR , "no known snapshots" );
2390
-
2391
- InitToastSnapshot (* toast_snapshot , snapshot -> lsn , snapshot -> whenTaken );
2530
+ * toast_snapshot = * snapshot ;
2531
+ // InitToastSnapshot(*toast_snapshot, snapshot->lsn, snapshot->whenTaken);
2392
2532
}
0 commit comments