mirror of https://github.com/etcd-io/bbolt.git
Remove ease-of-use functions from the DB type.
Functions such as DB.Put(), DB.Get(), and DB.Delete() were originally added to be easy to use, however, after implementing Bolt in multiple projects I have found these ease-of-use functions useless. Nearly every use case requires multiple calls in a single transaction. Using the DB ease of use functions turned out to be an antipattern.pull/34/head
parent
1c7b59a4c2
commit
3cc959fb1a
315
bucket_test.go
315
bucket_test.go
|
@ -15,19 +15,20 @@ import (
|
||||||
// Ensure that a bucket that gets a non-existent key returns nil.
|
// Ensure that a bucket that gets a non-existent key returns nil.
|
||||||
func TestBucketGetNonExistent(t *testing.T) {
|
func TestBucketGetNonExistent(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
value, err := db.Get("widgets", []byte("foo"))
|
tx.CreateBucket("widgets")
|
||||||
if assert.NoError(t, err) {
|
value := tx.Bucket("widgets").Get([]byte("foo"))
|
||||||
assert.Nil(t, value)
|
assert.Nil(t, value)
|
||||||
}
|
return nil
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that a bucket can read a value that is not flushed yet.
|
// Ensure that a bucket can read a value that is not flushed yet.
|
||||||
func TestBucketGetFromNode(t *testing.T) {
|
func TestBucketGetFromNode(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
|
||||||
db.Do(func(tx *Tx) error {
|
db.Do(func(tx *Tx) error {
|
||||||
|
tx.CreateBucket("widgets")
|
||||||
b := tx.Bucket("widgets")
|
b := tx.Bucket("widgets")
|
||||||
b.Put([]byte("foo"), []byte("bar"))
|
b.Put([]byte("foo"), []byte("bar"))
|
||||||
value := b.Get([]byte("foo"))
|
value := b.Get([]byte("foo"))
|
||||||
|
@ -40,20 +41,24 @@ func TestBucketGetFromNode(t *testing.T) {
|
||||||
// Ensure that a bucket can write a key/value.
|
// Ensure that a bucket can write a key/value.
|
||||||
func TestBucketPut(t *testing.T) {
|
func TestBucketPut(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
err := db.Put("widgets", []byte("foo"), []byte("bar"))
|
tx.CreateBucket("widgets")
|
||||||
assert.NoError(t, err)
|
err := tx.Bucket("widgets").Put([]byte("foo"), []byte("bar"))
|
||||||
value, err := db.Get("widgets", []byte("foo"))
|
assert.NoError(t, err)
|
||||||
if assert.NoError(t, err) {
|
value := tx.Bucket("widgets").Get([]byte("foo"))
|
||||||
assert.Equal(t, value, []byte("bar"))
|
assert.Equal(t, value, []byte("bar"))
|
||||||
}
|
return nil
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that setting a value on a read-only bucket returns an error.
|
// Ensure that setting a value on a read-only bucket returns an error.
|
||||||
func TestBucketPutReadOnly(t *testing.T) {
|
func TestBucketPutReadOnly(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
|
tx.CreateBucket("widgets")
|
||||||
|
return nil
|
||||||
|
})
|
||||||
db.With(func(tx *Tx) error {
|
db.With(func(tx *Tx) error {
|
||||||
b := tx.Bucket("widgets")
|
b := tx.Bucket("widgets")
|
||||||
err := b.Put([]byte("foo"), []byte("bar"))
|
err := b.Put([]byte("foo"), []byte("bar"))
|
||||||
|
@ -66,21 +71,25 @@ func TestBucketPutReadOnly(t *testing.T) {
|
||||||
// Ensure that a bucket can delete an existing key.
|
// Ensure that a bucket can delete an existing key.
|
||||||
func TestBucketDelete(t *testing.T) {
|
func TestBucketDelete(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
db.Put("widgets", []byte("foo"), []byte("bar"))
|
tx.CreateBucket("widgets")
|
||||||
err := db.Delete("widgets", []byte("foo"))
|
tx.Bucket("widgets").Put([]byte("foo"), []byte("bar"))
|
||||||
assert.NoError(t, err)
|
err := tx.Bucket("widgets").Delete([]byte("foo"))
|
||||||
value, err := db.Get("widgets", []byte("foo"))
|
assert.NoError(t, err)
|
||||||
if assert.NoError(t, err) {
|
value := tx.Bucket("widgets").Get([]byte("foo"))
|
||||||
assert.Nil(t, value)
|
assert.Nil(t, value)
|
||||||
}
|
return nil
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that deleting a key on a read-only bucket returns an error.
|
// Ensure that deleting a key on a read-only bucket returns an error.
|
||||||
func TestBucketDeleteReadOnly(t *testing.T) {
|
func TestBucketDeleteReadOnly(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
|
tx.CreateBucket("widgets")
|
||||||
|
return nil
|
||||||
|
})
|
||||||
db.With(func(tx *Tx) error {
|
db.With(func(tx *Tx) error {
|
||||||
b := tx.Bucket("widgets")
|
b := tx.Bucket("widgets")
|
||||||
err := b.Delete([]byte("foo"))
|
err := b.Delete([]byte("foo"))
|
||||||
|
@ -93,33 +102,34 @@ func TestBucketDeleteReadOnly(t *testing.T) {
|
||||||
// Ensure that a bucket can return an autoincrementing sequence.
|
// Ensure that a bucket can return an autoincrementing sequence.
|
||||||
func TestBucketNextSequence(t *testing.T) {
|
func TestBucketNextSequence(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
db.CreateBucket("woojits")
|
tx.CreateBucket("widgets")
|
||||||
|
tx.CreateBucket("woojits")
|
||||||
|
|
||||||
// Make sure sequence increments.
|
// Make sure sequence increments.
|
||||||
seq, err := db.NextSequence("widgets")
|
seq, err := tx.Bucket("widgets").NextSequence()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, seq, 1)
|
assert.Equal(t, seq, 1)
|
||||||
seq, err = db.NextSequence("widgets")
|
seq, err = tx.Bucket("widgets").NextSequence()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, seq, 2)
|
assert.Equal(t, seq, 2)
|
||||||
|
|
||||||
// Buckets should be separate.
|
// Buckets should be separate.
|
||||||
seq, err = db.NextSequence("woojits")
|
seq, err = tx.Bucket("woojits").NextSequence()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, seq, 1)
|
assert.Equal(t, seq, 1)
|
||||||
|
return nil
|
||||||
// Missing buckets return an error.
|
})
|
||||||
seq, err = db.NextSequence("no_such_bucket")
|
|
||||||
assert.Equal(t, err, ErrBucketNotFound)
|
|
||||||
assert.Equal(t, seq, 0)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that retrieving the next sequence on a read-only bucket returns an error.
|
// Ensure that retrieving the next sequence on a read-only bucket returns an error.
|
||||||
func TestBucketNextSequenceReadOnly(t *testing.T) {
|
func TestBucketNextSequenceReadOnly(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
|
tx.CreateBucket("widgets")
|
||||||
|
return nil
|
||||||
|
})
|
||||||
db.With(func(tx *Tx) error {
|
db.With(func(tx *Tx) error {
|
||||||
b := tx.Bucket("widgets")
|
b := tx.Bucket("widgets")
|
||||||
i, err := b.NextSequence()
|
i, err := b.NextSequence()
|
||||||
|
@ -133,7 +143,10 @@ func TestBucketNextSequenceReadOnly(t *testing.T) {
|
||||||
// Ensure that incrementing past the maximum sequence number will return an error.
|
// Ensure that incrementing past the maximum sequence number will return an error.
|
||||||
func TestBucketNextSequenceOverflow(t *testing.T) {
|
func TestBucketNextSequenceOverflow(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
|
tx.CreateBucket("widgets")
|
||||||
|
return nil
|
||||||
|
})
|
||||||
db.Do(func(tx *Tx) error {
|
db.Do(func(tx *Tx) error {
|
||||||
b := tx.Bucket("widgets")
|
b := tx.Bucket("widgets")
|
||||||
b.bucket.sequence = uint64(maxInt)
|
b.bucket.sequence = uint64(maxInt)
|
||||||
|
@ -148,70 +161,82 @@ func TestBucketNextSequenceOverflow(t *testing.T) {
|
||||||
// Ensure a database can loop over all key/value pairs in a bucket.
|
// Ensure a database can loop over all key/value pairs in a bucket.
|
||||||
func TestBucketForEach(t *testing.T) {
|
func TestBucketForEach(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
db.Put("widgets", []byte("foo"), []byte("0000"))
|
tx.CreateBucket("widgets")
|
||||||
db.Put("widgets", []byte("baz"), []byte("0001"))
|
tx.Bucket("widgets").Put([]byte("foo"), []byte("0000"))
|
||||||
db.Put("widgets", []byte("bar"), []byte("0002"))
|
tx.Bucket("widgets").Put([]byte("baz"), []byte("0001"))
|
||||||
|
tx.Bucket("widgets").Put([]byte("bar"), []byte("0002"))
|
||||||
|
|
||||||
var index int
|
var index int
|
||||||
err := db.ForEach("widgets", func(k, v []byte) error {
|
err := tx.Bucket("widgets").ForEach(func(k, v []byte) error {
|
||||||
switch index {
|
switch index {
|
||||||
case 0:
|
case 0:
|
||||||
assert.Equal(t, k, []byte("bar"))
|
assert.Equal(t, k, []byte("bar"))
|
||||||
assert.Equal(t, v, []byte("0002"))
|
assert.Equal(t, v, []byte("0002"))
|
||||||
case 1:
|
case 1:
|
||||||
assert.Equal(t, k, []byte("baz"))
|
assert.Equal(t, k, []byte("baz"))
|
||||||
assert.Equal(t, v, []byte("0001"))
|
assert.Equal(t, v, []byte("0001"))
|
||||||
case 2:
|
case 2:
|
||||||
assert.Equal(t, k, []byte("foo"))
|
assert.Equal(t, k, []byte("foo"))
|
||||||
assert.Equal(t, v, []byte("0000"))
|
assert.Equal(t, v, []byte("0000"))
|
||||||
}
|
}
|
||||||
index++
|
index++
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, index, 3)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, index, 3)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure a database can stop iteration early.
|
// Ensure a database can stop iteration early.
|
||||||
func TestBucketForEachShortCircuit(t *testing.T) {
|
func TestBucketForEachShortCircuit(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
db.Put("widgets", []byte("bar"), []byte("0000"))
|
tx.CreateBucket("widgets")
|
||||||
db.Put("widgets", []byte("baz"), []byte("0000"))
|
tx.Bucket("widgets").Put([]byte("bar"), []byte("0000"))
|
||||||
db.Put("widgets", []byte("foo"), []byte("0000"))
|
tx.Bucket("widgets").Put([]byte("baz"), []byte("0000"))
|
||||||
|
tx.Bucket("widgets").Put([]byte("foo"), []byte("0000"))
|
||||||
|
|
||||||
var index int
|
var index int
|
||||||
err := db.ForEach("widgets", func(k, v []byte) error {
|
err := tx.Bucket("widgets").ForEach(func(k, v []byte) error {
|
||||||
index++
|
index++
|
||||||
if bytes.Equal(k, []byte("baz")) {
|
if bytes.Equal(k, []byte("baz")) {
|
||||||
return &Error{"marker", nil}
|
return &Error{"marker", nil}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
assert.Equal(t, err, &Error{"marker", nil})
|
||||||
|
assert.Equal(t, index, 2)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
assert.Equal(t, err, &Error{"marker", nil})
|
|
||||||
assert.Equal(t, index, 2)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that an error is returned when inserting with an empty key.
|
// Ensure that an error is returned when inserting with an empty key.
|
||||||
func TestBucketPutEmptyKey(t *testing.T) {
|
func TestBucketPutEmptyKey(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
err := db.Put("widgets", []byte(""), []byte("bar"))
|
tx.CreateBucket("widgets")
|
||||||
assert.Equal(t, err, ErrKeyRequired)
|
err := tx.Bucket("widgets").Put([]byte(""), []byte("bar"))
|
||||||
err = db.Put("widgets", nil, []byte("bar"))
|
assert.Equal(t, err, ErrKeyRequired)
|
||||||
assert.Equal(t, err, ErrKeyRequired)
|
err = tx.Bucket("widgets").Put(nil, []byte("bar"))
|
||||||
|
assert.Equal(t, err, ErrKeyRequired)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that an error is returned when inserting with a key that's too large.
|
// Ensure that an error is returned when inserting with a key that's too large.
|
||||||
func TestBucketPutKeyTooLarge(t *testing.T) {
|
func TestBucketPutKeyTooLarge(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
err := db.Put("widgets", make([]byte, 32769), []byte("bar"))
|
tx.CreateBucket("widgets")
|
||||||
assert.Equal(t, err, ErrKeyTooLarge)
|
err := tx.Bucket("widgets").Put(make([]byte, 32769), []byte("bar"))
|
||||||
|
assert.Equal(t, err, ErrKeyTooLarge)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,30 +311,35 @@ func TestBucketPutSingle(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
m := make(map[string][]byte)
|
m := make(map[string][]byte)
|
||||||
|
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
|
return tx.CreateBucket("widgets")
|
||||||
|
})
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
if err := db.Put("widgets", item.Key, item.Value); err != nil {
|
db.Do(func(tx *Tx) error {
|
||||||
panic("put error: " + err.Error())
|
if err := tx.Bucket("widgets").Put(item.Key, item.Value); err != nil {
|
||||||
}
|
panic("put error: " + err.Error())
|
||||||
m[string(item.Key)] = item.Value
|
}
|
||||||
|
m[string(item.Key)] = item.Value
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
// Verify all key/values so far.
|
// Verify all key/values so far.
|
||||||
i := 0
|
db.With(func(tx *Tx) error {
|
||||||
for k, v := range m {
|
i := 0
|
||||||
value, err := db.Get("widgets", []byte(k))
|
for k, v := range m {
|
||||||
if err != nil {
|
value := tx.Bucket("widgets").Get([]byte(k))
|
||||||
panic("get error: " + err.Error())
|
if !bytes.Equal(value, v) {
|
||||||
|
db.CopyFile("/tmp/bolt.put.single.db", 0666)
|
||||||
|
t.Fatalf("value mismatch [run %d] (%d of %d):\nkey: %x\ngot: %x\nexp: %x", index, i, len(m), []byte(k), value, v)
|
||||||
|
}
|
||||||
|
i++
|
||||||
}
|
}
|
||||||
if !bytes.Equal(value, v) {
|
return nil
|
||||||
db.CopyFile("/tmp/bolt.put.single.db", 0666)
|
})
|
||||||
t.Fatalf("value mismatch [run %d] (%d of %d):\nkey: %x\ngot: %x\nexp: %x", index, i, len(m), []byte(k), value, v)
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprint(os.Stderr, ".")
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
fmt.Fprint(os.Stderr, ".")
|
||||||
index++
|
index++
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -328,25 +358,30 @@ func TestBucketPutMultiple(t *testing.T) {
|
||||||
f := func(items testdata) bool {
|
f := func(items testdata) bool {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
// Bulk insert all values.
|
// Bulk insert all values.
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
tx, _ := db.RWTx()
|
return tx.CreateBucket("widgets")
|
||||||
b := tx.Bucket("widgets")
|
})
|
||||||
for _, item := range items {
|
err := db.Do(func(tx *Tx) error {
|
||||||
assert.NoError(t, b.Put(item.Key, item.Value))
|
b := tx.Bucket("widgets")
|
||||||
}
|
for _, item := range items {
|
||||||
assert.NoError(t, tx.Commit())
|
assert.NoError(t, b.Put(item.Key, item.Value))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Verify all items exist.
|
// Verify all items exist.
|
||||||
tx, _ = db.Tx()
|
db.With(func(tx *Tx) error {
|
||||||
b = tx.Bucket("widgets")
|
b := tx.Bucket("widgets")
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
value := b.Get(item.Key)
|
value := b.Get(item.Key)
|
||||||
if !assert.Equal(t, item.Value, value) {
|
if !assert.Equal(t, item.Value, value) {
|
||||||
db.CopyFile("/tmp/bolt.put.multiple.db", 0666)
|
db.CopyFile("/tmp/bolt.put.multiple.db", 0666)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
tx.Rollback()
|
})
|
||||||
})
|
})
|
||||||
fmt.Fprint(os.Stderr, ".")
|
fmt.Fprint(os.Stderr, ".")
|
||||||
return true
|
return true
|
||||||
|
@ -366,35 +401,43 @@ func TestBucketDeleteQuick(t *testing.T) {
|
||||||
f := func(items testdata) bool {
|
f := func(items testdata) bool {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
// Bulk insert all values.
|
// Bulk insert all values.
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
tx, _ := db.RWTx()
|
return tx.CreateBucket("widgets")
|
||||||
b := tx.Bucket("widgets")
|
})
|
||||||
for _, item := range items {
|
err := db.Do(func(tx *Tx) error {
|
||||||
assert.NoError(t, b.Put(item.Key, item.Value))
|
b := tx.Bucket("widgets")
|
||||||
}
|
for _, item := range items {
|
||||||
assert.NoError(t, tx.Commit())
|
assert.NoError(t, b.Put(item.Key, item.Value))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Remove items one at a time and check consistency.
|
// Remove items one at a time and check consistency.
|
||||||
for i, item := range items {
|
for i, item := range items {
|
||||||
assert.NoError(t, db.Delete("widgets", item.Key))
|
err := db.Do(func(tx *Tx) error {
|
||||||
|
return tx.Bucket("widgets").Delete(item.Key)
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Anything before our deletion index should be nil.
|
// Anything before our deletion index should be nil.
|
||||||
tx, _ := db.Tx()
|
db.With(func(tx *Tx) error {
|
||||||
b := tx.Bucket("widgets")
|
b := tx.Bucket("widgets")
|
||||||
for j, exp := range items {
|
for j, exp := range items {
|
||||||
if j > i {
|
if j > i {
|
||||||
value := b.Get(exp.Key)
|
value := b.Get(exp.Key)
|
||||||
if !assert.Equal(t, exp.Value, value) {
|
if !assert.Equal(t, exp.Value, value) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
value := b.Get(exp.Key)
|
value := b.Get(exp.Key)
|
||||||
if !assert.Nil(t, value) {
|
if !assert.Nil(t, value) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
tx.Rollback()
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
fmt.Fprint(os.Stderr, ".")
|
fmt.Fprint(os.Stderr, ".")
|
||||||
|
|
129
db.go
129
db.go
|
@ -372,135 +372,6 @@ func (db *DB) With(fn func(*Tx) error) error {
|
||||||
return fn(t)
|
return fn(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForEach executes a function for each key/value pair in a bucket.
|
|
||||||
// An error is returned if the bucket cannot be found.
|
|
||||||
func (db *DB) ForEach(name string, fn func(k, v []byte) error) error {
|
|
||||||
return db.With(func(t *Tx) error {
|
|
||||||
b := t.Bucket(name)
|
|
||||||
if b == nil {
|
|
||||||
return ErrBucketNotFound
|
|
||||||
}
|
|
||||||
return b.ForEach(fn)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bucket retrieves a reference to a bucket.
|
|
||||||
// This is typically useful for checking the existence of a bucket.
|
|
||||||
func (db *DB) Bucket(name string) (*Bucket, error) {
|
|
||||||
t, err := db.Tx()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer t.Rollback()
|
|
||||||
return t.Bucket(name), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Buckets retrieves a list of all buckets in the database.
|
|
||||||
func (db *DB) Buckets() ([]*Bucket, error) {
|
|
||||||
t, err := db.Tx()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer t.Rollback()
|
|
||||||
return t.Buckets(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateBucket creates a new bucket with the given name.
|
|
||||||
// This function can return an error if the bucket already exists, if the name
|
|
||||||
// is blank, or the bucket name is too long.
|
|
||||||
func (db *DB) CreateBucket(name string) error {
|
|
||||||
return db.Do(func(t *Tx) error {
|
|
||||||
return t.CreateBucket(name)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateBucketIfNotExists creates a new bucket with the given name if it doesn't already exist.
|
|
||||||
// This function can return an error if the name is blank, or the bucket name is too long.
|
|
||||||
func (db *DB) CreateBucketIfNotExists(name string) error {
|
|
||||||
return db.Do(func(t *Tx) error {
|
|
||||||
return t.CreateBucketIfNotExists(name)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteBucket removes a bucket from the database.
|
|
||||||
// Returns an error if the bucket does not exist.
|
|
||||||
func (db *DB) DeleteBucket(name string) error {
|
|
||||||
return db.Do(func(t *Tx) error {
|
|
||||||
return t.DeleteBucket(name)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextSequence returns an autoincrementing integer for the bucket.
|
|
||||||
// This function can return an error if the bucket does not exist.
|
|
||||||
func (db *DB) NextSequence(name string) (int, error) {
|
|
||||||
var seq int
|
|
||||||
err := db.Do(func(t *Tx) error {
|
|
||||||
b := t.Bucket(name)
|
|
||||||
if b == nil {
|
|
||||||
return ErrBucketNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
seq, err = b.NextSequence()
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return seq, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get retrieves the value for a key in a bucket.
|
|
||||||
// Returns an error if the key does not exist.
|
|
||||||
func (db *DB) Get(name string, key []byte) ([]byte, error) {
|
|
||||||
t, err := db.Tx()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer t.Rollback()
|
|
||||||
|
|
||||||
// Open bucket and retrieve value for key.
|
|
||||||
b := t.Bucket(name)
|
|
||||||
if b == nil {
|
|
||||||
return nil, ErrBucketNotFound
|
|
||||||
}
|
|
||||||
value := b.Get(key)
|
|
||||||
if value == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the value out since the transaction will be closed after this
|
|
||||||
// function ends. The data can get reclaimed between now and when the
|
|
||||||
// value is used.
|
|
||||||
tmp := make([]byte, len(value))
|
|
||||||
copy(tmp, value)
|
|
||||||
return tmp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put sets the value for a key in a bucket.
|
|
||||||
// Returns an error if the bucket is not found, if key is blank, if the key is too large, or if the value is too large.
|
|
||||||
func (db *DB) Put(name string, key []byte, value []byte) error {
|
|
||||||
return db.Do(func(t *Tx) error {
|
|
||||||
b := t.Bucket(name)
|
|
||||||
if b == nil {
|
|
||||||
return ErrBucketNotFound
|
|
||||||
}
|
|
||||||
return b.Put(key, value)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete removes a key from a bucket.
|
|
||||||
// Returns an error if the bucket cannot be found.
|
|
||||||
func (db *DB) Delete(name string, key []byte) error {
|
|
||||||
return db.Do(func(t *Tx) error {
|
|
||||||
b := t.Bucket(name)
|
|
||||||
if b == nil {
|
|
||||||
return ErrBucketNotFound
|
|
||||||
}
|
|
||||||
return b.Delete(key)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy writes the entire database to a writer.
|
// Copy writes the entire database to a writer.
|
||||||
// A reader transaction is maintained during the copy so it is safe to continue
|
// A reader transaction is maintained during the copy so it is safe to continue
|
||||||
// using the database while a copy is in progress.
|
// using the database while a copy is in progress.
|
||||||
|
|
111
db_test.go
111
db_test.go
|
@ -164,14 +164,6 @@ func TestDBTxErrDatabaseNotOpen(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that a delete on a missing bucket returns an error.
|
|
||||||
func TestDBDeleteFromMissingBucket(t *testing.T) {
|
|
||||||
withOpenDB(func(db *DB, path string) {
|
|
||||||
err := db.Delete("widgets", []byte("foo"))
|
|
||||||
assert.Equal(t, err, ErrBucketNotFound)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that a read-write transaction can be retrieved.
|
// Ensure that a read-write transaction can be retrieved.
|
||||||
func TestDBRWTx(t *testing.T) {
|
func TestDBRWTx(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
|
@ -204,10 +196,12 @@ func TestDBTxBlock(t *testing.T) {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
value, _ := db.Get("widgets", []byte("foo"))
|
err = db.With(func(tx *Tx) error {
|
||||||
assert.Nil(t, value)
|
assert.Nil(t, tx.Bucket("widgets").Get([]byte("foo")))
|
||||||
value, _ = db.Get("widgets", []byte("baz"))
|
assert.Equal(t, []byte("bat"), tx.Bucket("widgets").Get([]byte("baz")))
|
||||||
assert.Equal(t, value, []byte("bat"))
|
return nil
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,63 +216,15 @@ func TestDBTxBlockWhileClosed(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure a database returns an error when trying to attempt a for each on a missing bucket.
|
|
||||||
func TestDBForEachBucketNotFound(t *testing.T) {
|
|
||||||
withOpenDB(func(db *DB, path string) {
|
|
||||||
err := db.ForEach("widgets", func(k, v []byte) error { return nil })
|
|
||||||
assert.Equal(t, err, ErrBucketNotFound)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure a closed database returns an error when executing a for each.
|
|
||||||
func TestDBForEachWhileClosed(t *testing.T) {
|
|
||||||
withDB(func(db *DB, path string) {
|
|
||||||
err := db.ForEach("widgets", func(k, v []byte) error { return nil })
|
|
||||||
assert.Equal(t, err, ErrDatabaseNotOpen)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure a closed database returns an error when finding a bucket.
|
|
||||||
func TestDBBucketWhileClosed(t *testing.T) {
|
|
||||||
withDB(func(db *DB, path string) {
|
|
||||||
b, err := db.Bucket("widgets")
|
|
||||||
assert.Equal(t, err, ErrDatabaseNotOpen)
|
|
||||||
assert.Nil(t, b)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure a closed database returns an error when finding all buckets.
|
|
||||||
func TestDBBucketsWhileClosed(t *testing.T) {
|
|
||||||
withDB(func(db *DB, path string) {
|
|
||||||
b, err := db.Buckets()
|
|
||||||
assert.Equal(t, err, ErrDatabaseNotOpen)
|
|
||||||
assert.Nil(t, b)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure a closed database returns an error when getting a key.
|
|
||||||
func TestDBGetWhileClosed(t *testing.T) {
|
|
||||||
withDB(func(db *DB, path string) {
|
|
||||||
value, err := db.Get("widgets", []byte("foo"))
|
|
||||||
assert.Equal(t, err, ErrDatabaseNotOpen)
|
|
||||||
assert.Nil(t, value)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that an error is returned when inserting into a bucket that doesn't exist.
|
|
||||||
func TestDBPutBucketNotFound(t *testing.T) {
|
|
||||||
withOpenDB(func(db *DB, path string) {
|
|
||||||
err := db.Put("widgets", []byte("foo"), []byte("bar"))
|
|
||||||
assert.Equal(t, err, ErrBucketNotFound)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that the database can be copied to a file path.
|
// Ensure that the database can be copied to a file path.
|
||||||
func TestDBCopyFile(t *testing.T) {
|
func TestDBCopyFile(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
db.Put("widgets", []byte("foo"), []byte("bar"))
|
tx.CreateBucket("widgets")
|
||||||
db.Put("widgets", []byte("baz"), []byte("bat"))
|
tx.Bucket("widgets").Put([]byte("foo"), []byte("bar"))
|
||||||
|
tx.Bucket("widgets").Put([]byte("baz"), []byte("bat"))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
assert.NoError(t, os.RemoveAll("/tmp/bolt.copyfile.db"))
|
assert.NoError(t, os.RemoveAll("/tmp/bolt.copyfile.db"))
|
||||||
assert.NoError(t, db.CopyFile("/tmp/bolt.copyfile.db", 0666))
|
assert.NoError(t, db.CopyFile("/tmp/bolt.copyfile.db", 0666))
|
||||||
|
|
||||||
|
@ -286,10 +232,11 @@ func TestDBCopyFile(t *testing.T) {
|
||||||
assert.NoError(t, db2.Open("/tmp/bolt.copyfile.db", 0666))
|
assert.NoError(t, db2.Open("/tmp/bolt.copyfile.db", 0666))
|
||||||
defer db2.Close()
|
defer db2.Close()
|
||||||
|
|
||||||
value, _ := db2.Get("widgets", []byte("foo"))
|
db2.With(func(tx *Tx) error {
|
||||||
assert.Equal(t, value, []byte("bar"))
|
assert.Equal(t, []byte("bar"), tx.Bucket("widgets").Get([]byte("foo")))
|
||||||
value, _ = db2.Get("widgets", []byte("baz"))
|
assert.Equal(t, []byte("bat"), tx.Bucket("widgets").Get([]byte("baz")))
|
||||||
assert.Equal(t, value, []byte("bat"))
|
return nil
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,8 +253,12 @@ func TestDBStat(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Delete some keys.
|
// Delete some keys.
|
||||||
db.Delete("widgets", []byte("10"))
|
db.Do(func(tx *Tx) error {
|
||||||
db.Delete("widgets", []byte("1000"))
|
return tx.Bucket("widgets").Delete([]byte("10"))
|
||||||
|
})
|
||||||
|
db.Do(func(tx *Tx) error {
|
||||||
|
return tx.Bucket("widgets").Delete([]byte("1000"))
|
||||||
|
})
|
||||||
|
|
||||||
// Open some readers.
|
// Open some readers.
|
||||||
t0, _ := db.Tx()
|
t0, _ := db.Tx()
|
||||||
|
@ -367,9 +318,13 @@ func TestDBString(t *testing.T) {
|
||||||
func BenchmarkDBPutSequential(b *testing.B) {
|
func BenchmarkDBPutSequential(b *testing.B) {
|
||||||
value := []byte(strings.Repeat("0", 64))
|
value := []byte(strings.Repeat("0", 64))
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
|
return tx.CreateBucket("widgets")
|
||||||
|
})
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
db.Put("widgets", []byte(strconv.Itoa(i)), value)
|
db.Do(func(tx *Tx) error {
|
||||||
|
return tx.Bucket("widgets").Put([]byte(strconv.Itoa(i)), value)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -379,9 +334,13 @@ func BenchmarkDBPutRandom(b *testing.B) {
|
||||||
indexes := rand.Perm(b.N)
|
indexes := rand.Perm(b.N)
|
||||||
value := []byte(strings.Repeat("0", 64))
|
value := []byte(strings.Repeat("0", 64))
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
|
return tx.CreateBucket("widgets")
|
||||||
|
})
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
db.Put("widgets", []byte(strconv.Itoa(indexes[i])), value)
|
db.Do(func(tx *Tx) error {
|
||||||
|
return tx.Bucket("widgets").Put([]byte(strconv.Itoa(indexes[i])), value)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
196
example_test.go
196
example_test.go
|
@ -10,56 +10,6 @@ func init() {
|
||||||
os.MkdirAll("/tmp/bolt", 0777)
|
os.MkdirAll("/tmp/bolt", 0777)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleDB_Put() {
|
|
||||||
// Open the database.
|
|
||||||
var db DB
|
|
||||||
db.Open("/tmp/bolt/db_put.db", 0666)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
// Create a bucket.
|
|
||||||
db.CreateBucket("widgets")
|
|
||||||
|
|
||||||
// Set the value "bar" for the key "foo".
|
|
||||||
db.Put("widgets", []byte("foo"), []byte("bar"))
|
|
||||||
|
|
||||||
// Retrieve the key back from the database and verify it.
|
|
||||||
value, _ := db.Get("widgets", []byte("foo"))
|
|
||||||
fmt.Printf("The value of 'foo' is: %s\n", string(value))
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// The value of 'foo' is: bar
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleDB_Delete() {
|
|
||||||
// Open the database.
|
|
||||||
var db DB
|
|
||||||
db.Open("/tmp/bolt/db_delete.db", 0666)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
// Create a bucket.
|
|
||||||
db.CreateBucket("widgets")
|
|
||||||
|
|
||||||
// Set the value "bar" for the key "foo".
|
|
||||||
db.Put("widgets", []byte("foo"), []byte("bar"))
|
|
||||||
|
|
||||||
// Retrieve the key back from the database and verify it.
|
|
||||||
value, _ := db.Get("widgets", []byte("foo"))
|
|
||||||
fmt.Printf("The value of 'foo' was: %s\n", string(value))
|
|
||||||
|
|
||||||
// Delete the "foo" key.
|
|
||||||
db.Delete("widgets", []byte("foo"))
|
|
||||||
|
|
||||||
// Retrieve the key again.
|
|
||||||
value, _ = db.Get("widgets", []byte("foo"))
|
|
||||||
if value == nil {
|
|
||||||
fmt.Printf("The value of 'foo' is now: nil\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// The value of 'foo' was: bar
|
|
||||||
// The value of 'foo' is now: nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleDB_Do() {
|
func ExampleDB_Do() {
|
||||||
// Open the database.
|
// Open the database.
|
||||||
var db DB
|
var db DB
|
||||||
|
@ -67,11 +17,11 @@ func ExampleDB_Do() {
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
// Execute several commands within a write transaction.
|
// Execute several commands within a write transaction.
|
||||||
err := db.Do(func(t *Tx) error {
|
err := db.Do(func(tx *Tx) error {
|
||||||
if err := t.CreateBucket("widgets"); err != nil {
|
if err := tx.CreateBucket("widgets"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
b := t.Bucket("widgets")
|
b := tx.Bucket("widgets")
|
||||||
if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
|
if err := b.Put([]byte("foo"), []byte("bar")); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -80,8 +30,11 @@ func ExampleDB_Do() {
|
||||||
|
|
||||||
// If our transactional block didn't return an error then our data is saved.
|
// If our transactional block didn't return an error then our data is saved.
|
||||||
if err == nil {
|
if err == nil {
|
||||||
value, _ := db.Get("widgets", []byte("foo"))
|
db.With(func(tx *Tx) error {
|
||||||
fmt.Printf("The value of 'foo' is: %s\n", string(value))
|
value := tx.Bucket("widgets").Get([]byte("foo"))
|
||||||
|
fmt.Printf("The value of 'foo' is: %s\n", string(value))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
|
@ -91,13 +44,16 @@ func ExampleDB_Do() {
|
||||||
func ExampleDB_With() {
|
func ExampleDB_With() {
|
||||||
// Open the database.
|
// Open the database.
|
||||||
var db DB
|
var db DB
|
||||||
db.Open("/tmp/bolt/db_foreach.db", 0666)
|
db.Open("/tmp/bolt/db_with.db", 0666)
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
// Insert data into a bucket.
|
// Insert data into a bucket.
|
||||||
db.CreateBucket("people")
|
db.Do(func(tx *Tx) error {
|
||||||
db.Put("people", []byte("john"), []byte("doe"))
|
tx.CreateBucket("people")
|
||||||
db.Put("people", []byte("susy"), []byte("que"))
|
tx.Bucket("people").Put([]byte("john"), []byte("doe"))
|
||||||
|
tx.Bucket("people").Put([]byte("susy"), []byte("que"))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
// Access data from within a read-only transactional block.
|
// Access data from within a read-only transactional block.
|
||||||
db.With(func(t *Tx) error {
|
db.With(func(t *Tx) error {
|
||||||
|
@ -110,21 +66,92 @@ func ExampleDB_With() {
|
||||||
// John's last name is doe.
|
// John's last name is doe.
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleDB_ForEach() {
|
func ExampleTx_Put() {
|
||||||
// Open the database.
|
// Open the database.
|
||||||
var db DB
|
var db DB
|
||||||
db.Open("/tmp/bolt/db_foreach.db", 0666)
|
db.Open("/tmp/bolt/db_put.db", 0666)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
// Start a write transaction.
|
||||||
|
db.Do(func(tx *Tx) error {
|
||||||
|
// Create a bucket.
|
||||||
|
tx.CreateBucket("widgets")
|
||||||
|
|
||||||
|
// Set the value "bar" for the key "foo".
|
||||||
|
tx.Bucket("widgets").Put([]byte("foo"), []byte("bar"))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// Read value back in a different read-only transaction.
|
||||||
|
db.Do(func(tx *Tx) error {
|
||||||
|
value := tx.Bucket("widgets").Get([]byte("foo"))
|
||||||
|
fmt.Printf("The value of 'foo' is: %s\n", string(value))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// The value of 'foo' is: bar
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleTx_Delete() {
|
||||||
|
// Open the database.
|
||||||
|
var db DB
|
||||||
|
db.Open("/tmp/bolt/db_delete.db", 0666)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
// Start a write transaction.
|
||||||
|
db.Do(func(tx *Tx) error {
|
||||||
|
// Create a bucket.
|
||||||
|
tx.CreateBucket("widgets")
|
||||||
|
b := tx.Bucket("widgets")
|
||||||
|
|
||||||
|
// Set the value "bar" for the key "foo".
|
||||||
|
b.Put([]byte("foo"), []byte("bar"))
|
||||||
|
|
||||||
|
// Retrieve the key back from the database and verify it.
|
||||||
|
value := b.Get([]byte("foo"))
|
||||||
|
fmt.Printf("The value of 'foo' was: %s\n", string(value))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// Delete the key in a different write transaction.
|
||||||
|
db.Do(func(tx *Tx) error {
|
||||||
|
return tx.Bucket("widgets").Delete([]byte("foo"))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Retrieve the key again.
|
||||||
|
db.With(func(tx *Tx) error {
|
||||||
|
value := tx.Bucket("widgets").Get([]byte("foo"))
|
||||||
|
if value == nil {
|
||||||
|
fmt.Printf("The value of 'foo' is now: nil\n")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// The value of 'foo' was: bar
|
||||||
|
// The value of 'foo' is now: nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleTx_ForEach() {
|
||||||
|
// Open the database.
|
||||||
|
var db DB
|
||||||
|
db.Open("/tmp/bolt/tx_foreach.db", 0666)
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
// Insert data into a bucket.
|
// Insert data into a bucket.
|
||||||
db.CreateBucket("animals")
|
db.Do(func(tx *Tx) error {
|
||||||
db.Put("animals", []byte("dog"), []byte("fun"))
|
tx.CreateBucket("animals")
|
||||||
db.Put("animals", []byte("cat"), []byte("lame"))
|
b := tx.Bucket("animals")
|
||||||
db.Put("animals", []byte("liger"), []byte("awesome"))
|
b.Put([]byte("dog"), []byte("fun"))
|
||||||
|
b.Put([]byte("cat"), []byte("lame"))
|
||||||
|
b.Put([]byte("liger"), []byte("awesome"))
|
||||||
|
|
||||||
// Iterate over items in sorted key order.
|
// Iterate over items in sorted key order.
|
||||||
db.ForEach("animals", func(k, v []byte) error {
|
b.ForEach(func(k, v []byte) error {
|
||||||
fmt.Printf("A %s is %s.\n", string(k), string(v))
|
fmt.Printf("A %s is %s.\n", string(k), string(v))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -141,7 +168,9 @@ func ExampleTx() {
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
// Create a bucket.
|
// Create a bucket.
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
|
return tx.CreateBucket("widgets")
|
||||||
|
})
|
||||||
|
|
||||||
// Create several keys in a transaction.
|
// Create several keys in a transaction.
|
||||||
tx, _ := db.RWTx()
|
tx, _ := db.RWTx()
|
||||||
|
@ -172,10 +201,14 @@ func ExampleTx_rollback() {
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
// Create a bucket.
|
// Create a bucket.
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
|
return tx.CreateBucket("widgets")
|
||||||
|
})
|
||||||
|
|
||||||
// Set a value for a key.
|
// Set a value for a key.
|
||||||
db.Put("widgets", []byte("foo"), []byte("bar"))
|
db.Do(func(tx *Tx) error {
|
||||||
|
return tx.Bucket("widgets").Put([]byte("foo"), []byte("bar"))
|
||||||
|
})
|
||||||
|
|
||||||
// Update the key but rollback the transaction so it never saves.
|
// Update the key but rollback the transaction so it never saves.
|
||||||
tx, _ := db.RWTx()
|
tx, _ := db.RWTx()
|
||||||
|
@ -184,8 +217,11 @@ func ExampleTx_rollback() {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
|
|
||||||
// Ensure that our original value is still set.
|
// Ensure that our original value is still set.
|
||||||
value, _ := db.Get("widgets", []byte("foo"))
|
db.With(func(tx *Tx) error {
|
||||||
fmt.Printf("The value for 'foo' is still: %s\n", string(value))
|
value := tx.Bucket("widgets").Get([]byte("foo"))
|
||||||
|
fmt.Printf("The value for 'foo' is still: %s\n", string(value))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// The value for 'foo' is still: bar
|
// The value for 'foo' is still: bar
|
||||||
|
@ -198,8 +234,11 @@ func ExampleDB_CopyFile() {
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
// Create a bucket and a key.
|
// Create a bucket and a key.
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
db.Put("widgets", []byte("foo"), []byte("bar"))
|
tx.CreateBucket("widgets")
|
||||||
|
tx.Bucket("widgets").Put([]byte("foo"), []byte("bar"))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
// Copy the database to another file.
|
// Copy the database to another file.
|
||||||
db.CopyFile("/tmp/bolt/db_copy_2.db", 0666)
|
db.CopyFile("/tmp/bolt/db_copy_2.db", 0666)
|
||||||
|
@ -210,8 +249,11 @@ func ExampleDB_CopyFile() {
|
||||||
defer db2.Close()
|
defer db2.Close()
|
||||||
|
|
||||||
// Ensure that the key exists in the copy.
|
// Ensure that the key exists in the copy.
|
||||||
value, _ := db2.Get("widgets", []byte("foo"))
|
db2.With(func(tx *Tx) error {
|
||||||
fmt.Printf("The value for 'foo' in the clone is: %s\n", string(value))
|
value := tx.Bucket("widgets").Get([]byte("foo"))
|
||||||
|
fmt.Printf("The value for 'foo' in the clone is: %s\n", string(value))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// The value for 'foo' in the clone is: bar
|
// The value for 'foo' in the clone is: bar
|
||||||
|
|
|
@ -28,7 +28,9 @@ func TestParallelTxs(t *testing.T) {
|
||||||
var current testdata
|
var current testdata
|
||||||
|
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
|
return tx.CreateBucket("widgets")
|
||||||
|
})
|
||||||
|
|
||||||
// Maintain a set of concurrent readers.
|
// Maintain a set of concurrent readers.
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
221
tx_test.go
221
tx_test.go
|
@ -16,15 +16,18 @@ import (
|
||||||
// Ensure that the database can retrieve a list of buckets.
|
// Ensure that the database can retrieve a list of buckets.
|
||||||
func TestTxBuckets(t *testing.T) {
|
func TestTxBuckets(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("foo")
|
db.Do(func(tx *Tx) error {
|
||||||
db.CreateBucket("bar")
|
tx.CreateBucket("foo")
|
||||||
db.CreateBucket("baz")
|
tx.CreateBucket("bar")
|
||||||
buckets, err := db.Buckets()
|
tx.CreateBucket("baz")
|
||||||
if assert.NoError(t, err) && assert.Equal(t, len(buckets), 3) {
|
buckets := tx.Buckets()
|
||||||
assert.Equal(t, buckets[0].Name(), "bar")
|
if assert.Equal(t, len(buckets), 3) {
|
||||||
assert.Equal(t, buckets[1].Name(), "baz")
|
assert.Equal(t, buckets[0].Name(), "bar")
|
||||||
assert.Equal(t, buckets[2].Name(), "foo")
|
assert.Equal(t, buckets[1].Name(), "baz")
|
||||||
}
|
assert.Equal(t, buckets[2].Name(), "foo")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,33 +42,40 @@ func TestTxCreateBucketReadOnly(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that a Tx can retrieve a bucket.
|
// Ensure that a Tx can retrieve a bucket.
|
||||||
func TestTxBucketMissing(t *testing.T) {
|
func TestTxBucket(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
b, err := db.Bucket("widgets")
|
tx.CreateBucket("widgets")
|
||||||
assert.NoError(t, err)
|
b := tx.Bucket("widgets")
|
||||||
if assert.NotNil(t, b) {
|
if assert.NotNil(t, b) {
|
||||||
assert.Equal(t, "widgets", b.Name())
|
assert.Equal(t, "widgets", b.Name())
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that a Tx retrieving a non-existent key returns nil.
|
// Ensure that a Tx retrieving a non-existent key returns nil.
|
||||||
func TestTxGetMissing(t *testing.T) {
|
func TestTxGetMissing(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
db.Put("widgets", []byte("foo"), []byte("bar"))
|
tx.CreateBucket("widgets")
|
||||||
value, err := db.Get("widgets", []byte("no_such_key"))
|
tx.Bucket("widgets").Put([]byte("foo"), []byte("bar"))
|
||||||
assert.NoError(t, err)
|
value := tx.Bucket("widgets").Get([]byte("no_such_key"))
|
||||||
assert.Nil(t, value)
|
assert.Nil(t, value)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that retrieving all buckets returns writable buckets.
|
// Ensure that retrieving all buckets returns writable buckets.
|
||||||
func TestTxWritableBuckets(t *testing.T) {
|
func TestTxWritableBuckets(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
db.CreateBucket("woojits")
|
tx.CreateBucket("widgets")
|
||||||
|
tx.CreateBucket("woojits")
|
||||||
|
return nil
|
||||||
|
})
|
||||||
db.Do(func(tx *Tx) error {
|
db.Do(func(tx *Tx) error {
|
||||||
buckets := tx.Buckets()
|
buckets := tx.Buckets()
|
||||||
assert.Equal(t, len(buckets), 2)
|
assert.Equal(t, len(buckets), 2)
|
||||||
|
@ -75,10 +85,11 @@ func TestTxWritableBuckets(t *testing.T) {
|
||||||
buckets[1].Put([]byte("bar"), []byte("0001"))
|
buckets[1].Put([]byte("bar"), []byte("0001"))
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
v, _ := db.Get("widgets", []byte("foo"))
|
db.With(func(tx *Tx) error {
|
||||||
assert.Equal(t, v, []byte("0000"))
|
assert.Equal(t, []byte("0000"), tx.Bucket("widgets").Get([]byte("foo")))
|
||||||
v, _ = db.Get("woojits", []byte("bar"))
|
assert.Equal(t, []byte("0001"), tx.Bucket("woojits").Get([]byte("bar")))
|
||||||
assert.Equal(t, v, []byte("0001"))
|
return nil
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,27 +97,36 @@ func TestTxWritableBuckets(t *testing.T) {
|
||||||
func TestTxCreateBucket(t *testing.T) {
|
func TestTxCreateBucket(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
// Create a bucket.
|
// Create a bucket.
|
||||||
err := db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, tx.CreateBucket("widgets"))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
// Read the bucket through a separate transaction.
|
// Read the bucket through a separate transaction.
|
||||||
b, err := db.Bucket("widgets")
|
db.With(func(tx *Tx) error {
|
||||||
assert.NotNil(t, b)
|
b := tx.Bucket("widgets")
|
||||||
assert.NoError(t, err)
|
assert.NotNil(t, b)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that a bucket can be created if it doesn't already exist.
|
// Ensure that a bucket can be created if it doesn't already exist.
|
||||||
func TestTxCreateBucketIfNotExists(t *testing.T) {
|
func TestTxCreateBucketIfNotExists(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
assert.NoError(t, db.CreateBucketIfNotExists("widgets"))
|
db.Do(func(tx *Tx) error {
|
||||||
assert.NoError(t, db.CreateBucketIfNotExists("widgets"))
|
assert.NoError(t, tx.CreateBucketIfNotExists("widgets"))
|
||||||
assert.Equal(t, db.CreateBucketIfNotExists(""), ErrBucketNameRequired)
|
assert.NoError(t, tx.CreateBucketIfNotExists("widgets"))
|
||||||
|
assert.Equal(t, tx.CreateBucketIfNotExists(""), ErrBucketNameRequired)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
// Read the bucket through a separate transaction.
|
// Read the bucket through a separate transaction.
|
||||||
b, err := db.Bucket("widgets")
|
db.With(func(tx *Tx) error {
|
||||||
assert.NotNil(t, b)
|
b := tx.Bucket("widgets")
|
||||||
assert.NoError(t, err)
|
assert.NotNil(t, b)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,31 +134,37 @@ func TestTxCreateBucketIfNotExists(t *testing.T) {
|
||||||
func TestTxRecreateBucket(t *testing.T) {
|
func TestTxRecreateBucket(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
// Create a bucket.
|
// Create a bucket.
|
||||||
err := db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, tx.CreateBucket("widgets"))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
// Create the same bucket again.
|
// Create the same bucket again.
|
||||||
err = db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
assert.Equal(t, err, ErrBucketExists)
|
assert.Equal(t, ErrBucketExists, tx.CreateBucket("widgets"))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that a bucket is created with a non-blank name.
|
// Ensure that a bucket is created with a non-blank name.
|
||||||
func TestTxCreateBucketWithoutName(t *testing.T) {
|
func TestTxCreateBucketWithoutName(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
err := db.CreateBucket("")
|
db.Do(func(tx *Tx) error {
|
||||||
assert.Equal(t, err, ErrBucketNameRequired)
|
assert.Equal(t, ErrBucketNameRequired, tx.CreateBucket(""))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that a bucket name is not too long.
|
// Ensure that a bucket name is not too long.
|
||||||
func TestTxCreateBucketWithLongName(t *testing.T) {
|
func TestTxCreateBucketWithLongName(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
err := db.CreateBucket(strings.Repeat("X", 255))
|
db.Do(func(tx *Tx) error {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, tx.CreateBucket(strings.Repeat("X", 255)))
|
||||||
|
assert.Equal(t, ErrBucketNameTooLarge, tx.CreateBucket(strings.Repeat("X", 256)))
|
||||||
err = db.CreateBucket(strings.Repeat("X", 256))
|
return nil
|
||||||
assert.Equal(t, err, ErrBucketNameTooLarge)
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,25 +172,35 @@ func TestTxCreateBucketWithLongName(t *testing.T) {
|
||||||
func TestTxDeleteBucket(t *testing.T) {
|
func TestTxDeleteBucket(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
// Create a bucket and add a value.
|
// Create a bucket and add a value.
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
db.Put("widgets", []byte("foo"), []byte("bar"))
|
tx.CreateBucket("widgets")
|
||||||
|
tx.Bucket("widgets").Put([]byte("foo"), []byte("bar"))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
b, _ := db.Bucket("widgets")
|
// Save root page id.
|
||||||
|
var root pgid
|
||||||
|
db.With(func(tx *Tx) error {
|
||||||
|
root = tx.Bucket("widgets").root
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
// Delete the bucket and make sure we can't get the value.
|
// Delete the bucket and make sure we can't get the value.
|
||||||
assert.NoError(t, db.DeleteBucket("widgets"))
|
db.Do(func(tx *Tx) error {
|
||||||
value, err := db.Get("widgets", []byte("foo"))
|
assert.NoError(t, tx.DeleteBucket("widgets"))
|
||||||
assert.Equal(t, err, ErrBucketNotFound)
|
assert.Nil(t, tx.Bucket("widgets"))
|
||||||
assert.Nil(t, value)
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
// Verify that the bucket's page is free.
|
db.Do(func(tx *Tx) error {
|
||||||
assert.Equal(t, db.freelist.all(), []pgid{b.root})
|
// Verify that the bucket's page is free.
|
||||||
|
assert.Equal(t, []pgid{root}, db.freelist.all())
|
||||||
|
|
||||||
// Create the bucket again and make sure there's not a phantom value.
|
// Create the bucket again and make sure there's not a phantom value.
|
||||||
assert.NoError(t, db.CreateBucket("widgets"))
|
assert.NoError(t, tx.CreateBucket("widgets"))
|
||||||
value, err = db.Get("widgets", []byte("foo"))
|
assert.Nil(t, tx.Bucket("widgets").Get([]byte("foo")))
|
||||||
assert.NoError(t, err)
|
return nil
|
||||||
assert.Nil(t, value)
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,15 +217,19 @@ func TestTxDeleteBucketReadOnly(t *testing.T) {
|
||||||
// Ensure that an error is returned when deleting from a bucket that doesn't exist.
|
// Ensure that an error is returned when deleting from a bucket that doesn't exist.
|
||||||
func TestTxDeleteBucketNotFound(t *testing.T) {
|
func TestTxDeleteBucketNotFound(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
err := db.DeleteBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
assert.Equal(t, err, ErrBucketNotFound)
|
assert.Equal(t, ErrBucketNotFound, tx.DeleteBucket("widgets"))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that a Tx cursor can iterate over an empty bucket without error.
|
// Ensure that a Tx cursor can iterate over an empty bucket without error.
|
||||||
func TestTxCursorEmptyBucket(t *testing.T) {
|
func TestTxCursorEmptyBucket(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
|
return tx.CreateBucket("widgets")
|
||||||
|
})
|
||||||
db.With(func(tx *Tx) error {
|
db.With(func(tx *Tx) error {
|
||||||
c := tx.Bucket("widgets").Cursor()
|
c := tx.Bucket("widgets").Cursor()
|
||||||
k, v := c.First()
|
k, v := c.First()
|
||||||
|
@ -203,7 +243,9 @@ func TestTxCursorEmptyBucket(t *testing.T) {
|
||||||
// Ensure that a Tx cursor can reverse iterate over an empty bucket without error.
|
// Ensure that a Tx cursor can reverse iterate over an empty bucket without error.
|
||||||
func TestCursorEmptyBucketReverse(t *testing.T) {
|
func TestCursorEmptyBucketReverse(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
|
return tx.CreateBucket("widgets")
|
||||||
|
})
|
||||||
db.With(func(tx *Tx) error {
|
db.With(func(tx *Tx) error {
|
||||||
c := tx.Bucket("widgets").Cursor()
|
c := tx.Bucket("widgets").Cursor()
|
||||||
k, v := c.Last()
|
k, v := c.Last()
|
||||||
|
@ -217,10 +259,13 @@ func TestCursorEmptyBucketReverse(t *testing.T) {
|
||||||
// Ensure that a Tx cursor can iterate over a single root with a couple elements.
|
// Ensure that a Tx cursor can iterate over a single root with a couple elements.
|
||||||
func TestTxCursorLeafRoot(t *testing.T) {
|
func TestTxCursorLeafRoot(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
db.Put("widgets", []byte("baz"), []byte{})
|
tx.CreateBucket("widgets")
|
||||||
db.Put("widgets", []byte("foo"), []byte{0})
|
tx.Bucket("widgets").Put([]byte("baz"), []byte{})
|
||||||
db.Put("widgets", []byte("bar"), []byte{1})
|
tx.Bucket("widgets").Put([]byte("foo"), []byte{0})
|
||||||
|
tx.Bucket("widgets").Put([]byte("bar"), []byte{1})
|
||||||
|
return nil
|
||||||
|
})
|
||||||
tx, _ := db.Tx()
|
tx, _ := db.Tx()
|
||||||
c := tx.Bucket("widgets").Cursor()
|
c := tx.Bucket("widgets").Cursor()
|
||||||
|
|
||||||
|
@ -251,10 +296,13 @@ func TestTxCursorLeafRoot(t *testing.T) {
|
||||||
// Ensure that a Tx cursor can iterate in reverse over a single root with a couple elements.
|
// Ensure that a Tx cursor can iterate in reverse over a single root with a couple elements.
|
||||||
func TestTxCursorLeafRootReverse(t *testing.T) {
|
func TestTxCursorLeafRootReverse(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
db.Put("widgets", []byte("baz"), []byte{})
|
tx.CreateBucket("widgets")
|
||||||
db.Put("widgets", []byte("foo"), []byte{0})
|
tx.Bucket("widgets").Put([]byte("baz"), []byte{})
|
||||||
db.Put("widgets", []byte("bar"), []byte{1})
|
tx.Bucket("widgets").Put([]byte("foo"), []byte{0})
|
||||||
|
tx.Bucket("widgets").Put([]byte("bar"), []byte{1})
|
||||||
|
return nil
|
||||||
|
})
|
||||||
tx, _ := db.Tx()
|
tx, _ := db.Tx()
|
||||||
c := tx.Bucket("widgets").Cursor()
|
c := tx.Bucket("widgets").Cursor()
|
||||||
|
|
||||||
|
@ -285,9 +333,12 @@ func TestTxCursorLeafRootReverse(t *testing.T) {
|
||||||
// Ensure that a Tx cursor can restart from the beginning.
|
// Ensure that a Tx cursor can restart from the beginning.
|
||||||
func TestTxCursorRestart(t *testing.T) {
|
func TestTxCursorRestart(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
db.Put("widgets", []byte("bar"), []byte{})
|
tx.CreateBucket("widgets")
|
||||||
db.Put("widgets", []byte("foo"), []byte{})
|
tx.Bucket("widgets").Put([]byte("bar"), []byte{})
|
||||||
|
tx.Bucket("widgets").Put([]byte("foo"), []byte{})
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
tx, _ := db.Tx()
|
tx, _ := db.Tx()
|
||||||
c := tx.Bucket("widgets").Cursor()
|
c := tx.Bucket("widgets").Cursor()
|
||||||
|
@ -317,8 +368,8 @@ func TestTxCursorIterate(t *testing.T) {
|
||||||
f := func(items testdata) bool {
|
f := func(items testdata) bool {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
// Bulk insert all values.
|
// Bulk insert all values.
|
||||||
db.CreateBucket("widgets")
|
|
||||||
tx, _ := db.RWTx()
|
tx, _ := db.RWTx()
|
||||||
|
tx.CreateBucket("widgets")
|
||||||
b := tx.Bucket("widgets")
|
b := tx.Bucket("widgets")
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
assert.NoError(t, b.Put(item.Key, item.Value))
|
assert.NoError(t, b.Put(item.Key, item.Value))
|
||||||
|
@ -358,8 +409,8 @@ func TestTxCursorIterateReverse(t *testing.T) {
|
||||||
f := func(items testdata) bool {
|
f := func(items testdata) bool {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
// Bulk insert all values.
|
// Bulk insert all values.
|
||||||
db.CreateBucket("widgets")
|
|
||||||
tx, _ := db.RWTx()
|
tx, _ := db.RWTx()
|
||||||
|
tx.CreateBucket("widgets")
|
||||||
b := tx.Bucket("widgets")
|
b := tx.Bucket("widgets")
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
assert.NoError(t, b.Put(item.Key, item.Value))
|
assert.NoError(t, b.Put(item.Key, item.Value))
|
||||||
|
@ -397,8 +448,8 @@ func BenchmarkTxCursor(b *testing.B) {
|
||||||
|
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
// Write data to bucket.
|
// Write data to bucket.
|
||||||
db.CreateBucket("widgets")
|
|
||||||
db.Do(func(tx *Tx) error {
|
db.Do(func(tx *Tx) error {
|
||||||
|
tx.CreateBucket("widgets")
|
||||||
bucket := tx.Bucket("widgets")
|
bucket := tx.Bucket("widgets")
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
bucket.Put([]byte(strconv.Itoa(indexes[i])), value)
|
bucket.Put([]byte(strconv.Itoa(indexes[i])), value)
|
||||||
|
@ -427,7 +478,9 @@ func BenchmarkTxPutRandom(b *testing.B) {
|
||||||
indexes := rand.Perm(b.N)
|
indexes := rand.Perm(b.N)
|
||||||
value := []byte(strings.Repeat("0", 64))
|
value := []byte(strings.Repeat("0", 64))
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
|
return tx.CreateBucket("widgets")
|
||||||
|
})
|
||||||
var tx *Tx
|
var tx *Tx
|
||||||
var bucket *Bucket
|
var bucket *Bucket
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
|
@ -448,7 +501,9 @@ func BenchmarkTxPutRandom(b *testing.B) {
|
||||||
func BenchmarkTxPutSequential(b *testing.B) {
|
func BenchmarkTxPutSequential(b *testing.B) {
|
||||||
value := []byte(strings.Repeat("0", 64))
|
value := []byte(strings.Repeat("0", 64))
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
db.CreateBucket("widgets")
|
db.Do(func(tx *Tx) error {
|
||||||
|
return tx.CreateBucket("widgets")
|
||||||
|
})
|
||||||
db.Do(func(tx *Tx) error {
|
db.Do(func(tx *Tx) error {
|
||||||
bucket := tx.Bucket("widgets")
|
bucket := tx.Bucket("widgets")
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
|
|
Loading…
Reference in New Issue