Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 6a6de84

Browse files
committed
Incremental toast update
1 parent 29d7ebf commit 6a6de84

File tree

1 file changed

+162
-22
lines changed

1 file changed

+162
-22
lines changed

src/backend/access/heap/tuptoaster.c

Lines changed: 162 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
#include "utils/snapmgr.h"
4444
#include "utils/typcache.h"
4545
#include "utils/tqual.h"
46-
46+
#include "nodes/bitmapset.h"
4747

4848
#undef TOAST_DEBUG
4949

@@ -69,7 +69,7 @@ typedef struct toast_compress_header
6969

7070
static void toast_delete_datum(Relation rel, Datum value, bool is_speculative);
7171
static Datum toast_save_datum(Relation rel, Datum value,
72-
struct varlena *oldexternal, int options);
72+
struct varlena *oldexternal, int options, Bitmapset* update_chunks);
7373
static bool toastrel_valueid_exists(Relation toastrel, Oid valueid);
7474
static bool toastid_valueid_exists(Oid toastrelid, Oid valueid);
7575
static struct varlena *toast_fetch_datum(struct varlena *attr);
@@ -511,6 +511,28 @@ toast_delete(Relation rel, HeapTuple oldtup, bool is_speculative)
511511
}
512512
}
513513

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+
}
514536

515537
/* ----------
516538
* toast_insert_or_update -
@@ -542,6 +564,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
542564
bool need_change = false;
543565
bool need_free = false;
544566
bool need_delold = false;
567+
bool need_delmap = false;
545568
bool has_nulls = false;
546569

547570
Size maxDataLen;
@@ -556,6 +579,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
556579
int32 toast_sizes[MaxHeapAttributeNumber];
557580
bool toast_free[MaxHeapAttributeNumber];
558581
bool toast_delold[MaxHeapAttributeNumber];
582+
Bitmapset** update_chunks;
559583

560584
/*
561585
* Ignore the INSERT_SPECULATIVE option. Speculative insertions/super
@@ -599,6 +623,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
599623
memset(toast_oldexternal, 0, numAttrs * sizeof(struct varlena *));
600624
memset(toast_free, 0, numAttrs * sizeof(bool));
601625
memset(toast_delold, 0, numAttrs * sizeof(bool));
626+
update_chunks = palloc0(numAttrs*sizeof(Bitmapset*));
602627

603628
for (i = 0; i < numAttrs; i++)
604629
{
@@ -630,7 +655,32 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
630655
* after the update
631656
*/
632657
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;
634684
}
635685
else
636686
{
@@ -812,7 +862,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
812862
old_value = toast_values[i];
813863
toast_action[i] = 'p';
814864
toast_values[i] = toast_save_datum(rel, toast_values[i],
815-
toast_oldexternal[i], options);
865+
toast_oldexternal[i], options,
866+
update_chunks[i]);
816867
if (toast_free[i])
817868
pfree(DatumGetPointer(old_value));
818869
toast_free[i] = true;
@@ -865,7 +916,9 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
865916
old_value = toast_values[i];
866917
toast_action[i] = 'p';
867918
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]);
869922
if (toast_free[i])
870923
pfree(DatumGetPointer(old_value));
871924
toast_free[i] = true;
@@ -979,7 +1032,9 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
9791032
old_value = toast_values[i];
9801033
toast_action[i] = 'p';
9811034
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]);
9831038
if (toast_free[i])
9841039
pfree(DatumGetPointer(old_value));
9851040
toast_free[i] = true;
@@ -1067,6 +1122,12 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
10671122
if (toast_delold[i])
10681123
toast_delete_datum(rel, toast_oldvalues[i], false);
10691124

1125+
if (need_delmap)
1126+
for (i = 0; i < numAttrs; i++)
1127+
bms_free(update_chunks[i]);
1128+
1129+
pfree(update_chunks);
1130+
10701131
return result_tuple;
10711132
}
10721133

@@ -1452,6 +1513,61 @@ toast_get_valid_index(Oid toastoid, LOCKMODE lock)
14521513
}
14531514

14541515

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+
14551571
/* ----------
14561572
* toast_save_datum -
14571573
*
@@ -1466,7 +1582,8 @@ toast_get_valid_index(Oid toastoid, LOCKMODE lock)
14661582
*/
14671583
static Datum
14681584
toast_save_datum(Relation rel, Datum value,
1469-
struct varlena *oldexternal, int options)
1585+
struct varlena *oldexternal, int options,
1586+
Bitmapset* update_chunks)
14701587
{
14711588
Relation toastrel;
14721589
Relation *toastidxs;
@@ -1567,7 +1684,18 @@ toast_save_datum(Relation rel, Datum value,
15671684
* options have been changed), we have to pick a value ID that doesn't
15681685
* conflict with either new or existing toast value OIDs.
15691686
*/
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))
15711699
{
15721700
/* normal case: just choose an unused OID */
15731701
toast_pointer.va_valueid =
@@ -1659,12 +1787,20 @@ toast_save_datum(Relation rel, Datum value,
16591787
/*
16601788
* Build a tuple and store it
16611789
*/
1662-
t_values[1] = Int32GetDatum(chunk_seq++);
1790+
t_values[1] = Int32GetDatum(chunk_seq);
16631791
SET_VARSIZE(&chunk_data, chunk_size + VARHDRSZ);
16641792
memcpy(VARDATA(&chunk_data), data_p, chunk_size);
16651793
toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull);
16661794

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);
16681804

16691805
/*
16701806
* Create the index entry. We cheat a little here by not using
@@ -1677,18 +1813,21 @@ toast_save_datum(Relation rel, Datum value,
16771813
* Note also that there had better not be any user-created index on
16781814
* the TOAST table, since we don't bother to update anything else.
16791815
*/
1680-
for (i = 0; i < num_indexes; i++)
1816+
if (!HeapTupleIsHeapOnly(toasttup))
16811817
{
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+
}
16901829
}
1691-
1830+
skip_chunk:
16921831
/*
16931832
* Free memory
16941833
*/
@@ -1699,6 +1838,7 @@ toast_save_datum(Relation rel, Datum value,
16991838
*/
17001839
data_todo -= chunk_size;
17011840
data_p += chunk_size;
1841+
chunk_seq += 1;
17021842
}
17031843

17041844
/*
@@ -2387,6 +2527,6 @@ init_toast_snapshot(Snapshot toast_snapshot)
23872527

23882528
if (snapshot == NULL)
23892529
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);
23922532
}

0 commit comments

Comments
 (0)