From c6f007b74a8438fa135f1a3521f59425edc3a720 Mon Sep 17 00:00:00 2001 From: Andras Banki-Horvath Date: Tue, 27 Apr 2021 11:14:43 +0200 Subject: [PATCH] walletdb: add ForEachBucket to the ReadTx with bbolt implementation This commit extends the ReadTx (and ReadWriteTx) interface with ForEachBucket which can be used to iterate through all top level buckets. This is a missing piece from the walletdb abstraction which will allow us to iterate all keys in a walletdb opening the possibility to build generic tools to browse and edit walletdb files regardless of the underlying driver. --- walletdb/bdb/db.go | 9 +++++++++ walletdb/interface.go | 3 +++ walletdb/walletdbtest/interface.go | 25 +++++++++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/walletdb/bdb/db.go b/walletdb/bdb/db.go index e6a782f..288ffe1 100644 --- a/walletdb/bdb/db.go +++ b/walletdb/bdb/db.go @@ -60,6 +60,15 @@ func (tx *transaction) ReadBucket(key []byte) walletdb.ReadBucket { return tx.ReadWriteBucket(key) } +// ForEachBucket will iterate through all top level buckets. +func (tx *transaction) ForEachBucket(fn func(key []byte) error) error { + return convertErr(tx.boltTx.ForEach( + func(name []byte, _ *bbolt.Bucket) error { + return fn(name) + }, + )) +} + func (tx *transaction) ReadWriteBucket(key []byte) walletdb.ReadWriteBucket { boltBucket := tx.boltTx.Bucket(key) if boltBucket == nil { diff --git a/walletdb/interface.go b/walletdb/interface.go index 40fb19e..b6abe66 100644 --- a/walletdb/interface.go +++ b/walletdb/interface.go @@ -19,6 +19,9 @@ type ReadTx interface { // described by the key does not exist, nil is returned. ReadBucket(key []byte) ReadBucket + // ForEachBucket will iterate through all top level buckets. + ForEachBucket(func(key []byte) error) error + // Rollback closes the transaction, discarding changes (if any) if the // database was modified by a write transaction. Rollback() error diff --git a/walletdb/walletdbtest/interface.go b/walletdb/walletdbtest/interface.go index 037de1c..da17f12 100644 --- a/walletdb/walletdbtest/interface.go +++ b/walletdb/walletdbtest/interface.go @@ -540,6 +540,31 @@ func testNamespaceAndTxInterfaces(tc *testContext, namespaceKey string) bool { return false } + // Test that we can read the top level buckets. + var topLevelBuckets []string + walletdb.View(tc.db, func(tx walletdb.ReadTx) error { + return tx.ForEachBucket(func(key []byte) error { + topLevelBuckets = append(topLevelBuckets, string(key)) + return nil + }) + }) + if err != nil { + if err != errSubTestFail { + tc.t.Errorf("%v", err) + } + return false + } + + if len(topLevelBuckets) != 1 { + tc.t.Errorf("ForEachBucket: expected only one top level bucket") + return false + } + if topLevelBuckets[0] != namespaceKey { + tc.t.Errorf("ForEachBucket: expected %v, got %v", namespaceKey, + topLevelBuckets[0]) + return false + } + // Test the bucket interface via a managed read-write transaction. // Also, put a series of values and force a rollback so the following // code can ensure the values were not stored.