diff --git a/walletdb/bdb/db.go b/walletdb/bdb/db.go index 046c6ac..9d700bf 100644 --- a/walletdb/bdb/db.go +++ b/walletdb/bdb/db.go @@ -68,7 +68,7 @@ func (tx *transaction) ReadWriteBucket(key []byte) walletdb.ReadWriteBucket { } func (tx *transaction) CreateTopLevelBucket(key []byte) (walletdb.ReadWriteBucket, error) { - boltBucket, err := tx.boltTx.CreateBucket(key) + boltBucket, err := tx.boltTx.CreateBucketIfNotExists(key) if err != nil { return nil, convertErr(err) } @@ -231,6 +231,21 @@ func (b *bucket) Tx() walletdb.ReadWriteTx { } } +// NextSequence returns an autoincrementing integer for the bucket. +func (b *bucket) NextSequence() (uint64, error) { + return (*bbolt.Bucket)(b).NextSequence() +} + +// SetSequence updates the sequence number for the bucket. +func (b *bucket) SetSequence(v uint64) error { + return (*bbolt.Bucket)(b).SetSequence(v) +} + +// Sequence returns the current integer for the bucket without incrementing it. +func (b *bucket) Sequence() uint64 { + return (*bbolt.Bucket)(b).Sequence() +} + // cursor represents a cursor over key/value pairs and nested buckets of a // bucket. // diff --git a/walletdb/bdb/driver_test.go b/walletdb/bdb/driver_test.go index c233abe..8fdcab1 100644 --- a/walletdb/bdb/driver_test.go +++ b/walletdb/bdb/driver_test.go @@ -23,7 +23,7 @@ func TestCreateOpenFail(t *testing.T) { // Ensure that attempting to open a database that doesn't exist returns // the expected error. wantErr := walletdb.ErrDbDoesNotExist - if _, err := walletdb.Open(dbType, "noexist.db"); err != wantErr { + if _, err := walletdb.Open(dbType, "noexist.db", true); err != wantErr { t.Errorf("Open: did not receive expected error - got %v, "+ "want %v", err, wantErr) return @@ -32,7 +32,7 @@ func TestCreateOpenFail(t *testing.T) { // Ensure that attempting to open a database with the wrong number of // parameters returns the expected error. wantErr = fmt.Errorf("invalid arguments to %s.Open -- expected "+ - "database path", dbType) + "database path and no-freelist-sync option", dbType) if _, err := walletdb.Open(dbType, 1, 2, 3); err.Error() != wantErr.Error() { t.Errorf("Open: did not receive expected error - got %v, "+ "want %v", err, wantErr) @@ -43,7 +43,7 @@ func TestCreateOpenFail(t *testing.T) { // the first parameter returns the expected error. wantErr = fmt.Errorf("first argument to %s.Open is invalid -- "+ "expected database path string", dbType) - if _, err := walletdb.Open(dbType, 1); err.Error() != wantErr.Error() { + if _, err := walletdb.Open(dbType, 1, true); err.Error() != wantErr.Error() { t.Errorf("Open: did not receive expected error - got %v, "+ "want %v", err, wantErr) return @@ -52,7 +52,7 @@ func TestCreateOpenFail(t *testing.T) { // Ensure that attempting to create a database with the wrong number of // parameters returns the expected error. wantErr = fmt.Errorf("invalid arguments to %s.Create -- expected "+ - "database path", dbType) + "database path and no-freelist-sync option", dbType) if _, err := walletdb.Create(dbType, 1, 2, 3); err.Error() != wantErr.Error() { t.Errorf("Create: did not receive expected error - got %v, "+ "want %v", err, wantErr) @@ -63,7 +63,7 @@ func TestCreateOpenFail(t *testing.T) { // the first parameter returns the expected error. wantErr = fmt.Errorf("first argument to %s.Create is invalid -- "+ "expected database path string", dbType) - if _, err := walletdb.Create(dbType, 1); err.Error() != wantErr.Error() { + if _, err := walletdb.Create(dbType, 1, true); err.Error() != wantErr.Error() { t.Errorf("Create: did not receive expected error - got %v, "+ "want %v", err, wantErr) return @@ -72,7 +72,7 @@ func TestCreateOpenFail(t *testing.T) { // Ensure operations against a closed database return the expected // error. dbPath := "createfail.db" - db, err := walletdb.Create(dbType, dbPath) + db, err := walletdb.Create(dbType, dbPath, true) if err != nil { t.Errorf("Create: unexpected error: %v", err) return @@ -93,7 +93,7 @@ func TestCreateOpenFail(t *testing.T) { func TestPersistence(t *testing.T) { // Create a new database to run tests against. dbPath := "persistencetest.db" - db, err := walletdb.Create(dbType, dbPath) + db, err := walletdb.Create(dbType, dbPath, true) if err != nil { t.Errorf("Failed to create test database (%s) %v", dbType, err) return diff --git a/walletdb/interface.go b/walletdb/interface.go index f89fa91..f120e71 100644 --- a/walletdb/interface.go +++ b/walletdb/interface.go @@ -126,6 +126,16 @@ type ReadWriteBucket interface { // Tx returns the bucket's transaction. Tx() ReadWriteTx + + // NextSequence returns an autoincrementing integer for the bucket. + NextSequence() (uint64, error) + + // SetSequence updates the sequence number for the bucket. + SetSequence(v uint64) error + + // Sequence returns the current integer for the bucket without + // incrementing it. + Sequence() uint64 } // ReadCursor represents a bucket cursor that can be positioned at the start or diff --git a/walletdb/walletdbtest/interface.go b/walletdb/walletdbtest/interface.go index 8a2beb1..663a0fa 100644 --- a/walletdb/walletdbtest/interface.go +++ b/walletdb/walletdbtest/interface.go @@ -104,6 +104,51 @@ func testNestedReadWriteBucket(tc *testContext, testBucket walletdb.ReadWriteBuc return true } +// testSequence tests that the sequence related methods work as expected. +func testSequence(tc *testContext, testBucket walletdb.ReadWriteBucket) bool { + // Obtaining the current sequence twice should give us the same value. + seqNo1 := testBucket.Sequence() + seqNo2 := testBucket.Sequence() + if seqNo1 != seqNo2 { + tc.t.Errorf("Sequence: seq has incremented") + return false + } + + // Incrementing to the next sequence should give us a value one larger + // than the prior number. + seqNo3, err := testBucket.NextSequence() + if err != nil { + tc.t.Errorf("Sequence: unexpected error: %v", err) + return false + } + if seqNo3 != seqNo2+1 { + tc.t.Errorf("Sequence: expected seq no of %v, instead got %v", + seqNo2+1, seqNo3) + return false + } + + // We should be able to modify the sequence base number. + newBase := uint64(100) + if err := testBucket.SetSequence(newBase); err != nil { + tc.t.Errorf("Sequence: unexpected error: %v", err) + return false + } + + // Any offset from this new sequence should now be properly reflected. + seqNo4, err := testBucket.NextSequence() + if err != nil { + tc.t.Errorf("Sequence: unexpected error: %v", err) + return false + } + if seqNo4 != newBase+1 { + tc.t.Errorf("Sequence: expected seq no of %v, instead got %v", + newBase+1, seqNo4) + return false + } + + return false +} + // testReadWriteBucketInterface ensures the bucket interface is working properly by // exercising all of its functions. func testReadWriteBucketInterface(tc *testContext, bucket walletdb.ReadWriteBucket) bool { @@ -164,6 +209,11 @@ func testReadWriteBucketInterface(tc *testContext, bucket walletdb.ReadWriteBuck return false } + // Test that the sequence methods work as expected. + if !testSequence(tc, bucket) { + return false + } + // Ensure creating a new bucket works as expected. testBucketName := []byte("testbucket") testBucket, err := bucket.CreateBucket(testBucketName) @@ -678,7 +728,7 @@ func testAdditionalErrors(tc *testContext) bool { // TestInterface performs all interfaces tests for this database driver. func TestInterface(t Tester, dbType, dbPath string) { - db, err := walletdb.Create(dbType, dbPath) + db, err := walletdb.Create(dbType, dbPath, true) if err != nil { t.Errorf("Failed to create test database (%s) %v", dbType, err) return