From cb06b56d7cef72ad5842a11adaa5dd28918af39a Mon Sep 17 00:00:00 2001 From: sashass1315 Date: Fri, 6 Feb 2026 19:55:44 +0200 Subject: [PATCH 1/2] core/rawdb: add DeleteRange to tableReplayer --- core/rawdb/table.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/rawdb/table.go b/core/rawdb/table.go index d38afdaa35..92f62232a6 100644 --- a/core/rawdb/table.go +++ b/core/rawdb/table.go @@ -17,6 +17,8 @@ package rawdb import ( + "errors" + "github.com/ethereum/go-ethereum/ethdb" ) @@ -272,6 +274,16 @@ func (r *tableReplayer) Delete(key []byte) error { return r.w.Delete(trimmed) } +// DeleteRange implements the interface KeyValueRangeDeleter. +func (r *tableReplayer) DeleteRange(start, end []byte) error { + trimmedStart := start[len(r.prefix):] + trimmedEnd := end[len(r.prefix):] + if rangeDeleter, ok := r.w.(ethdb.KeyValueRangeDeleter); ok { + return rangeDeleter.DeleteRange(trimmedStart, trimmedEnd) + } + return errors.New("ethdb.KeyValueWriter does not implement DeleteRange") +} + // Replay replays the batch contents. func (b *tableBatch) Replay(w ethdb.KeyValueWriter) error { return b.batch.Replay(&tableReplayer{w: w, prefix: b.prefix}) From 8f4deb06366b48f890f4aa6fb5316c518906ec86 Mon Sep 17 00:00:00 2001 From: sashass1315 Date: Fri, 6 Feb 2026 19:56:26 +0200 Subject: [PATCH 2/2] add test --- core/rawdb/table_test.go | 60 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/core/rawdb/table_test.go b/core/rawdb/table_test.go index 36fd331059..d3982bf06e 100644 --- a/core/rawdb/table_test.go +++ b/core/rawdb/table_test.go @@ -27,8 +27,9 @@ func TestTableDatabase(t *testing.T) { testTableDatabase(t, "prefix") func TestEmptyPrefixTableDatabase(t *testing.T) { testTableDatabase(t, "") } type testReplayer struct { - puts [][]byte - dels [][]byte + puts [][]byte + dels [][]byte + delRanges [][2][]byte } func (r *testReplayer) Put(key []byte, value []byte) error { @@ -41,6 +42,11 @@ func (r *testReplayer) Delete(key []byte) error { return nil } +func (r *testReplayer) DeleteRange(start, end []byte) error { + r.delRanges = append(r.delRanges, [2][]byte{start, end}) + return nil +} + func testTableDatabase(t *testing.T, prefix string) { db := NewTable(NewMemoryDatabase(), prefix) @@ -126,6 +132,56 @@ func testTableDatabase(t *testing.T, prefix string) { check(db.NewIterator([]byte{0xee}, nil), 0, 0) check(db.NewIterator(nil, []byte{0x00}), 6, 0) + // Test batch replayer with DeleteRange + db2 := NewTable(NewMemoryDatabase(), prefix) + for _, entry := range entries { + db2.Put(entry.key, entry.value) + } + batch2 := db2.NewBatch() + batch2.Put([]byte{0x07, 0x08}, []byte{0x10, 0x11}) + batch2.DeleteRange([]byte{0x01, 0x02}, []byte{0x05, 0x06}) + batch2.Delete([]byte{0xff, 0xff, 0x03}) + + // Replay into another batch (tests tableReplayer.DeleteRange via batch-to-batch) + batch3 := db2.NewBatch() + if err := batch2.Replay(batch3); err != nil { + t.Fatalf("Failed to replay batch with DeleteRange: %v", err) + } + if err := batch3.Write(); err != nil { + t.Fatalf("Failed to write replayed batch: %v", err) + } + // Keys in range [0x01,0x02 .. 0x05,0x06) should be deleted + for _, key := range [][]byte{{0x01, 0x02}, {0x03, 0x04}} { + if _, err := db2.Get(key); err == nil { + t.Fatalf("Key %x should be deleted after replayed DeleteRange", key) + } + } + // Key 0x05,0x06 should still exist (exclusive end) + if _, err := db2.Get([]byte{0x05, 0x06}); err != nil { + t.Fatalf("Key 0x0506 should exist (exclusive end): %v", err) + } + // New key should exist + if _, err := db2.Get([]byte{0x07, 0x08}); err != nil { + t.Fatalf("Key 0x0708 should exist after replay: %v", err) + } + // Deleted single key should be gone + if _, err := db2.Get([]byte{0xff, 0xff, 0x03}); err == nil { + t.Fatal("Key 0xffff03 should be deleted after replay") + } + + // Replay into a testReplayer to verify prefix stripping + r2 := &testReplayer{} + batch2.Replay(r2) + if len(r2.delRanges) != 1 { + t.Fatalf("Expected 1 DeleteRange in replay, got %d", len(r2.delRanges)) + } + if !bytes.Equal(r2.delRanges[0][0], []byte{0x01, 0x02}) { + t.Fatalf("DeleteRange start mismatch: want=%x, got=%x", []byte{0x01, 0x02}, r2.delRanges[0][0]) + } + if !bytes.Equal(r2.delRanges[0][1], []byte{0x05, 0x06}) { + t.Fatalf("DeleteRange end mismatch: want=%x, got=%x", []byte{0x05, 0x06}, r2.delRanges[0][1]) + } + // Test range deletion db.DeleteRange(nil, nil) for _, entry := range entries {