Move tests to a test package.

pull/34/head
Ben Johnson 2014-07-26 15:11:47 -06:00
parent ca2339d7cb
commit ba6badc57f
10 changed files with 266 additions and 348 deletions

View File

@ -634,7 +634,7 @@ func (b *Bucket) free() {
var tx = b.tx var tx = b.tx
b.forEachPageNode(func(p *page, n *node, _ int) { b.forEachPageNode(func(p *page, n *node, _ int) {
if p != nil { if p != nil {
tx.db.freelist.free(tx.id(), p) tx.db.freelist.free(tx.meta.txid, p)
} else { } else {
n.free() n.free()
} }

View File

@ -1,4 +1,4 @@
package bolt package bolt_test
import ( import (
"bytes" "bytes"
@ -12,6 +12,7 @@ import (
"testing" "testing"
"testing/quick" "testing/quick"
"github.com/boltdb/bolt"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -19,7 +20,7 @@ import (
func TestBucket_Get_NonExistent(t *testing.T) { func TestBucket_Get_NonExistent(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
assert.Nil(t, value) assert.Nil(t, value)
@ -31,7 +32,7 @@ func TestBucket_Get_NonExistent(t *testing.T) {
func TestBucket_Get_FromNode(t *testing.T) { func TestBucket_Get_FromNode(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
b.Put([]byte("foo"), []byte("bar")) b.Put([]byte("foo"), []byte("bar"))
@ -45,7 +46,7 @@ func TestBucket_Get_FromNode(t *testing.T) {
func TestBucket_Get_IncompatibleValue(t *testing.T) { func TestBucket_Get_IncompatibleValue(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
_, err := tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo")) _, err := tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo"))
assert.NoError(t, err) assert.NoError(t, err)
@ -58,7 +59,7 @@ func TestBucket_Get_IncompatibleValue(t *testing.T) {
func TestBucket_Put(t *testing.T) { func TestBucket_Put(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
err := tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) err := tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))
assert.NoError(t, err) assert.NoError(t, err)
@ -72,7 +73,7 @@ func TestBucket_Put(t *testing.T) {
func TestBucket_Put_Repeat(t *testing.T) { func TestBucket_Put_Repeat(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
assert.NoError(t, b.Put([]byte("foo"), []byte("bar"))) assert.NoError(t, b.Put([]byte("foo"), []byte("bar")))
@ -89,7 +90,7 @@ func TestBucket_Put_Large(t *testing.T) {
defer db.Close() defer db.Close()
count, factor := 100, 200 count, factor := 100, 200
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
for i := 1; i < count; i++ { for i := 1; i < count; i++ {
@ -97,7 +98,7 @@ func TestBucket_Put_Large(t *testing.T) {
} }
return nil return nil
}) })
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
for i := 1; i < count; i++ { for i := 1; i < count; i++ {
value := b.Get([]byte(strings.Repeat("0", i*factor))) value := b.Get([]byte(strings.Repeat("0", i*factor)))
@ -120,7 +121,7 @@ func TestDB_Put_VeryLarge(t *testing.T) {
defer db.Close() defer db.Close()
for i := 0; i < n; i += batchN { for i := 0; i < n; i += batchN {
err := db.Update(func(tx *Tx) error { err := db.Update(func(tx *bolt.Tx) error {
b, _ := tx.CreateBucketIfNotExists([]byte("widgets")) b, _ := tx.CreateBucketIfNotExists([]byte("widgets"))
for j := 0; j < batchN; j++ { for j := 0; j < batchN; j++ {
k, v := make([]byte, ksize), make([]byte, vsize) k, v := make([]byte, ksize), make([]byte, vsize)
@ -137,11 +138,11 @@ func TestDB_Put_VeryLarge(t *testing.T) {
func TestBucket_Put_IncompatibleValue(t *testing.T) { func TestBucket_Put_IncompatibleValue(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
_, err := tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo")) _, err := tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo"))
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, ErrIncompatibleValue, tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))) assert.Equal(t, bolt.ErrIncompatibleValue, tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")))
return nil return nil
}) })
} }
@ -154,22 +155,22 @@ func TestBucket_Put_Closed(t *testing.T) {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
tx.Rollback() tx.Rollback()
assert.Equal(t, ErrTxClosed, b.Put([]byte("foo"), []byte("bar"))) assert.Equal(t, bolt.ErrTxClosed, b.Put([]byte("foo"), []byte("bar")))
} }
// 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 TestBucket_Put_ReadOnly(t *testing.T) { func TestBucket_Put_ReadOnly(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
assert.NoError(t, err) assert.NoError(t, err)
return nil return nil
}) })
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
err := b.Put([]byte("foo"), []byte("bar")) err := b.Put([]byte("foo"), []byte("bar"))
assert.Equal(t, err, ErrTxNotWritable) assert.Equal(t, err, bolt.ErrTxNotWritable)
return nil return nil
}) })
} }
@ -178,7 +179,7 @@ func TestBucket_Put_ReadOnly(t *testing.T) {
func TestBucket_Delete(t *testing.T) { func TestBucket_Delete(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))
err := tx.Bucket([]byte("widgets")).Delete([]byte("foo")) err := tx.Bucket([]byte("widgets")).Delete([]byte("foo"))
@ -193,21 +194,21 @@ func TestBucket_Delete(t *testing.T) {
func TestBucket_Delete_Large(t *testing.T) { func TestBucket_Delete_Large(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
var b, _ = tx.CreateBucket([]byte("widgets")) var b, _ = tx.CreateBucket([]byte("widgets"))
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
assert.NoError(t, b.Put([]byte(strconv.Itoa(i)), []byte(strings.Repeat("*", 1024)))) assert.NoError(t, b.Put([]byte(strconv.Itoa(i)), []byte(strings.Repeat("*", 1024))))
} }
return nil return nil
}) })
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
var b = tx.Bucket([]byte("widgets")) var b = tx.Bucket([]byte("widgets"))
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
assert.NoError(t, b.Delete([]byte(strconv.Itoa(i)))) assert.NoError(t, b.Delete([]byte(strconv.Itoa(i))))
} }
return nil return nil
}) })
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
var b = tx.Bucket([]byte("widgets")) var b = tx.Bucket([]byte("widgets"))
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
assert.Nil(t, b.Get([]byte(strconv.Itoa(i)))) assert.Nil(t, b.Get([]byte(strconv.Itoa(i))))
@ -226,7 +227,7 @@ func TestBucket_Delete_FreelistOverflow(t *testing.T) {
defer db.Close() defer db.Close()
k := make([]byte, 16) k := make([]byte, 16)
for i := uint64(0); i < 10000; i++ { for i := uint64(0); i < 10000; i++ {
err := db.Update(func(tx *Tx) error { err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte("0")) b, err := tx.CreateBucketIfNotExists([]byte("0"))
if err != nil { if err != nil {
t.Fatalf("bucket error: %s", err) t.Fatalf("bucket error: %s", err)
@ -249,7 +250,7 @@ func TestBucket_Delete_FreelistOverflow(t *testing.T) {
} }
// Delete all of them in one large transaction // Delete all of them in one large transaction
err := db.Update(func(tx *Tx) error { err := db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("0")) b := tx.Bucket([]byte("0"))
c := b.Cursor() c := b.Cursor()
for k, _ := c.First(); k != nil; k, _ = c.Next() { for k, _ := c.First(); k != nil; k, _ = c.Next() {
@ -266,7 +267,7 @@ func TestBucket_Delete_FreelistOverflow(t *testing.T) {
func TestBucket_Nested(t *testing.T) { func TestBucket_Nested(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
// Create a widgets bucket. // Create a widgets bucket.
b, err := tx.CreateBucket([]byte("widgets")) b, err := tx.CreateBucket([]byte("widgets"))
assert.NoError(t, err) assert.NoError(t, err)
@ -283,7 +284,7 @@ func TestBucket_Nested(t *testing.T) {
db.MustCheck() db.MustCheck()
// Update widgets/bar. // Update widgets/bar.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
var b = tx.Bucket([]byte("widgets")) var b = tx.Bucket([]byte("widgets"))
assert.NoError(t, b.Put([]byte("bar"), []byte("xxxx"))) assert.NoError(t, b.Put([]byte("bar"), []byte("xxxx")))
return nil return nil
@ -291,7 +292,7 @@ func TestBucket_Nested(t *testing.T) {
db.MustCheck() db.MustCheck()
// Cause a split. // Cause a split.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
var b = tx.Bucket([]byte("widgets")) var b = tx.Bucket([]byte("widgets"))
for i := 0; i < 10000; i++ { for i := 0; i < 10000; i++ {
assert.NoError(t, b.Put([]byte(strconv.Itoa(i)), []byte(strconv.Itoa(i)))) assert.NoError(t, b.Put([]byte(strconv.Itoa(i)), []byte(strconv.Itoa(i))))
@ -301,7 +302,7 @@ func TestBucket_Nested(t *testing.T) {
db.MustCheck() db.MustCheck()
// Insert into widgets/foo/baz. // Insert into widgets/foo/baz.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
var b = tx.Bucket([]byte("widgets")) var b = tx.Bucket([]byte("widgets"))
assert.NoError(t, b.Bucket([]byte("foo")).Put([]byte("baz"), []byte("yyyy"))) assert.NoError(t, b.Bucket([]byte("foo")).Put([]byte("baz"), []byte("yyyy")))
return nil return nil
@ -309,7 +310,7 @@ func TestBucket_Nested(t *testing.T) {
db.MustCheck() db.MustCheck()
// Verify. // Verify.
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
var b = tx.Bucket([]byte("widgets")) var b = tx.Bucket([]byte("widgets"))
assert.Equal(t, []byte("yyyy"), b.Bucket([]byte("foo")).Get([]byte("baz"))) assert.Equal(t, []byte("yyyy"), b.Bucket([]byte("foo")).Get([]byte("baz")))
assert.Equal(t, []byte("xxxx"), b.Get([]byte("bar"))) assert.Equal(t, []byte("xxxx"), b.Get([]byte("bar")))
@ -324,12 +325,12 @@ func TestBucket_Nested(t *testing.T) {
func TestBucket_Delete_Bucket(t *testing.T) { func TestBucket_Delete_Bucket(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
_, err := b.CreateBucket([]byte("foo")) _, err := b.CreateBucket([]byte("foo"))
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, ErrIncompatibleValue, b.Delete([]byte("foo"))) assert.Equal(t, bolt.ErrIncompatibleValue, b.Delete([]byte("foo")))
return nil return nil
}) })
} }
@ -338,14 +339,14 @@ func TestBucket_Delete_Bucket(t *testing.T) {
func TestBucket_Delete_ReadOnly(t *testing.T) { func TestBucket_Delete_ReadOnly(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
return nil return nil
}) })
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
err := b.Delete([]byte("foo")) err := b.Delete([]byte("foo"))
assert.Equal(t, err, ErrTxNotWritable) assert.Equal(t, err, bolt.ErrTxNotWritable)
return nil return nil
}) })
} }
@ -358,14 +359,14 @@ func TestBucket_Delete_Closed(t *testing.T) {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
tx.Rollback() tx.Rollback()
assert.Equal(t, ErrTxClosed, b.Delete([]byte("foo"))) assert.Equal(t, bolt.ErrTxClosed, b.Delete([]byte("foo")))
} }
// Ensure that deleting a bucket causes nested buckets to be deleted. // Ensure that deleting a bucket causes nested buckets to be deleted.
func TestBucket_DeleteBucket_Nested(t *testing.T) { func TestBucket_DeleteBucket_Nested(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
_, err := tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo")) _, err := tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo"))
assert.NoError(t, err) assert.NoError(t, err)
@ -381,7 +382,7 @@ func TestBucket_DeleteBucket_Nested(t *testing.T) {
func TestBucket_DeleteBucket_Nested2(t *testing.T) { func TestBucket_DeleteBucket_Nested2(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
_, err := tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo")) _, err := tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo"))
assert.NoError(t, err) assert.NoError(t, err)
@ -390,7 +391,7 @@ func TestBucket_DeleteBucket_Nested2(t *testing.T) {
assert.NoError(t, tx.Bucket([]byte("widgets")).Bucket([]byte("foo")).Bucket([]byte("bar")).Put([]byte("baz"), []byte("bat"))) assert.NoError(t, tx.Bucket([]byte("widgets")).Bucket([]byte("foo")).Bucket([]byte("bar")).Put([]byte("baz"), []byte("bat")))
return nil return nil
}) })
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
assert.NotNil(t, tx.Bucket([]byte("widgets"))) assert.NotNil(t, tx.Bucket([]byte("widgets")))
assert.NotNil(t, tx.Bucket([]byte("widgets")).Bucket([]byte("foo"))) assert.NotNil(t, tx.Bucket([]byte("widgets")).Bucket([]byte("foo")))
assert.NotNil(t, tx.Bucket([]byte("widgets")).Bucket([]byte("foo")).Bucket([]byte("bar"))) assert.NotNil(t, tx.Bucket([]byte("widgets")).Bucket([]byte("foo")).Bucket([]byte("bar")))
@ -398,7 +399,7 @@ func TestBucket_DeleteBucket_Nested2(t *testing.T) {
assert.NoError(t, tx.DeleteBucket([]byte("widgets"))) assert.NoError(t, tx.DeleteBucket([]byte("widgets")))
return nil return nil
}) })
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
assert.Nil(t, tx.Bucket([]byte("widgets"))) assert.Nil(t, tx.Bucket([]byte("widgets")))
return nil return nil
}) })
@ -408,7 +409,7 @@ func TestBucket_DeleteBucket_Nested2(t *testing.T) {
func TestBucket_DeleteBucket_Large(t *testing.T) { func TestBucket_DeleteBucket_Large(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
assert.NoError(t, err) assert.NoError(t, err)
_, err = tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo")) _, err = tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo"))
@ -419,7 +420,7 @@ func TestBucket_DeleteBucket_Large(t *testing.T) {
} }
return nil return nil
}) })
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
assert.NoError(t, tx.DeleteBucket([]byte("widgets"))) assert.NoError(t, tx.DeleteBucket([]byte("widgets")))
return nil return nil
}) })
@ -431,7 +432,7 @@ func TestBucket_DeleteBucket_Large(t *testing.T) {
func TestBucket_Bucket_IncompatibleValue(t *testing.T) { func TestBucket_Bucket_IncompatibleValue(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
assert.NoError(t, tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))) assert.NoError(t, tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")))
assert.Nil(t, tx.Bucket([]byte("widgets")).Bucket([]byte("foo"))) assert.Nil(t, tx.Bucket([]byte("widgets")).Bucket([]byte("foo")))
@ -443,12 +444,12 @@ func TestBucket_Bucket_IncompatibleValue(t *testing.T) {
func TestBucket_CreateBucket_IncompatibleValue(t *testing.T) { func TestBucket_CreateBucket_IncompatibleValue(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))) assert.NoError(t, tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")))
_, err = tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo")) _, err = tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo"))
assert.Equal(t, ErrIncompatibleValue, err) assert.Equal(t, bolt.ErrIncompatibleValue, err)
return nil return nil
}) })
} }
@ -457,11 +458,11 @@ func TestBucket_CreateBucket_IncompatibleValue(t *testing.T) {
func TestBucket_DeleteBucket_IncompatibleValue(t *testing.T) { func TestBucket_DeleteBucket_IncompatibleValue(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))) assert.NoError(t, tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")))
assert.Equal(t, ErrIncompatibleValue, tx.Bucket([]byte("widgets")).DeleteBucket([]byte("foo"))) assert.Equal(t, bolt.ErrIncompatibleValue, tx.Bucket([]byte("widgets")).DeleteBucket([]byte("foo")))
return nil return nil
}) })
} }
@ -470,7 +471,7 @@ func TestBucket_DeleteBucket_IncompatibleValue(t *testing.T) {
func TestBucket_NextSequence(t *testing.T) { func TestBucket_NextSequence(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
tx.CreateBucket([]byte("woojits")) tx.CreateBucket([]byte("woojits"))
@ -494,15 +495,15 @@ func TestBucket_NextSequence(t *testing.T) {
func TestBucket_NextSequence_ReadOnly(t *testing.T) { func TestBucket_NextSequence_ReadOnly(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
return nil return nil
}) })
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
i, err := b.NextSequence() i, err := b.NextSequence()
assert.Equal(t, i, uint64(0)) assert.Equal(t, i, uint64(0))
assert.Equal(t, err, ErrTxNotWritable) assert.Equal(t, err, bolt.ErrTxNotWritable)
return nil return nil
}) })
} }
@ -516,14 +517,14 @@ func TestBucket_NextSequence_Closed(t *testing.T) {
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
tx.Rollback() tx.Rollback()
_, err := b.NextSequence() _, err := b.NextSequence()
assert.Equal(t, ErrTxClosed, err) assert.Equal(t, bolt.ErrTxClosed, err)
} }
// Ensure a user can loop over all key/value pairs in a bucket. // Ensure a user can loop over all key/value pairs in a bucket.
func TestBucket_ForEach(t *testing.T) { func TestBucket_ForEach(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("0000")) tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("0000"))
tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte("0001")) tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte("0001"))
@ -555,7 +556,7 @@ func TestBucket_ForEach(t *testing.T) {
func TestBucket_ForEach_ShortCircuit(t *testing.T) { func TestBucket_ForEach_ShortCircuit(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
tx.Bucket([]byte("widgets")).Put([]byte("bar"), []byte("0000")) tx.Bucket([]byte("widgets")).Put([]byte("bar"), []byte("0000"))
tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte("0000")) tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte("0000"))
@ -584,19 +585,19 @@ func TestBucket_ForEach_Closed(t *testing.T) {
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
tx.Rollback() tx.Rollback()
err := b.ForEach(func(k, v []byte) error { return nil }) err := b.ForEach(func(k, v []byte) error { return nil })
assert.Equal(t, ErrTxClosed, err) assert.Equal(t, bolt.ErrTxClosed, err)
} }
// 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 TestBucket_Put_EmptyKey(t *testing.T) { func TestBucket_Put_EmptyKey(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
err := tx.Bucket([]byte("widgets")).Put([]byte(""), []byte("bar")) err := tx.Bucket([]byte("widgets")).Put([]byte(""), []byte("bar"))
assert.Equal(t, err, ErrKeyRequired) assert.Equal(t, err, bolt.ErrKeyRequired)
err = tx.Bucket([]byte("widgets")).Put(nil, []byte("bar")) err = tx.Bucket([]byte("widgets")).Put(nil, []byte("bar"))
assert.Equal(t, err, ErrKeyRequired) assert.Equal(t, err, bolt.ErrKeyRequired)
return nil return nil
}) })
} }
@ -605,10 +606,10 @@ func TestBucket_Put_EmptyKey(t *testing.T) {
func TestBucket_Put_KeyTooLarge(t *testing.T) { func TestBucket_Put_KeyTooLarge(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
err := tx.Bucket([]byte("widgets")).Put(make([]byte, 32769), []byte("bar")) err := tx.Bucket([]byte("widgets")).Put(make([]byte, 32769), []byte("bar"))
assert.Equal(t, err, ErrKeyTooLarge) assert.Equal(t, err, bolt.ErrKeyTooLarge)
return nil return nil
}) })
} }
@ -621,18 +622,18 @@ func TestBucket_Stats(t *testing.T) {
// Add bucket with fewer keys but one big value. // Add bucket with fewer keys but one big value.
big_key := []byte("really-big-value") big_key := []byte("really-big-value")
for i := 0; i < 500; i++ { for i := 0; i < 500; i++ {
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
b, _ := tx.CreateBucketIfNotExists([]byte("woojits")) b, _ := tx.CreateBucketIfNotExists([]byte("woojits"))
return b.Put([]byte(fmt.Sprintf("%03d", i)), []byte(strconv.Itoa(i))) return b.Put([]byte(fmt.Sprintf("%03d", i)), []byte(strconv.Itoa(i)))
}) })
} }
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
b, _ := tx.CreateBucketIfNotExists([]byte("woojits")) b, _ := tx.CreateBucketIfNotExists([]byte("woojits"))
return b.Put(big_key, []byte(strings.Repeat("*", 10000))) return b.Put(big_key, []byte(strings.Repeat("*", 10000)))
}) })
db.MustCheck() db.MustCheck()
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("woojits")) b := tx.Bucket([]byte("woojits"))
stats := b.Stats() stats := b.Stats()
assert.Equal(t, 1, stats.BranchPageN, "BranchPageN") assert.Equal(t, 1, stats.BranchPageN, "BranchPageN")
@ -642,13 +643,13 @@ func TestBucket_Stats(t *testing.T) {
assert.Equal(t, 501, stats.KeyN, "KeyN") assert.Equal(t, 501, stats.KeyN, "KeyN")
assert.Equal(t, 2, stats.Depth, "Depth") assert.Equal(t, 2, stats.Depth, "Depth")
branchInuse := pageHeaderSize // branch page header branchInuse := 16 // branch page header
branchInuse += 7 * branchPageElementSize // branch elements branchInuse += 7 * 16 // branch elements
branchInuse += 7 * 3 // branch keys (6 3-byte keys) branchInuse += 7 * 3 // branch keys (6 3-byte keys)
assert.Equal(t, branchInuse, stats.BranchInuse, "BranchInuse") assert.Equal(t, branchInuse, stats.BranchInuse, "BranchInuse")
leafInuse := 7 * pageHeaderSize // leaf page header leafInuse := 7 * 16 // leaf page header
leafInuse += 501 * leafPageElementSize // leaf elements leafInuse += 501 * 16 // leaf elements
leafInuse += 500*3 + len(big_key) // leaf keys leafInuse += 500*3 + len(big_key) // leaf keys
leafInuse += 1*10 + 2*90 + 3*400 + 10000 // leaf values leafInuse += 1*10 + 2*90 + 3*400 + 10000 // leaf values
assert.Equal(t, leafInuse, stats.LeafInuse, "LeafInuse") assert.Equal(t, leafInuse, stats.LeafInuse, "LeafInuse")
@ -682,7 +683,7 @@ func TestBucket_Stats_RandomFill(t *testing.T) {
var count int var count int
r := rand.New(rand.NewSource(42)) r := rand.New(rand.NewSource(42))
for _, i := range r.Perm(1000) { for _, i := range r.Perm(1000) {
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
b, _ := tx.CreateBucketIfNotExists([]byte("woojits")) b, _ := tx.CreateBucketIfNotExists([]byte("woojits"))
b.FillPercent = 0.9 b.FillPercent = 0.9
for _, j := range r.Perm(100) { for _, j := range r.Perm(100) {
@ -695,7 +696,7 @@ func TestBucket_Stats_RandomFill(t *testing.T) {
} }
db.MustCheck() db.MustCheck()
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
s := tx.Bucket([]byte("woojits")).Stats() s := tx.Bucket([]byte("woojits")).Stats()
assert.Equal(t, 100000, s.KeyN, "KeyN") assert.Equal(t, 100000, s.KeyN, "KeyN")
@ -716,7 +717,7 @@ func TestBucket_Stats_RandomFill(t *testing.T) {
func TestBucket_Stats_Small(t *testing.T) { func TestBucket_Stats_Small(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
// Add a bucket that fits on a single root leaf. // Add a bucket that fits on a single root leaf.
b, err := tx.CreateBucket([]byte("whozawhats")) b, err := tx.CreateBucket([]byte("whozawhats"))
assert.NoError(t, err) assert.NoError(t, err)
@ -725,7 +726,7 @@ func TestBucket_Stats_Small(t *testing.T) {
return nil return nil
}) })
db.MustCheck() db.MustCheck()
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("whozawhats")) b := tx.Bucket([]byte("whozawhats"))
stats := b.Stats() stats := b.Stats()
assert.Equal(t, 0, stats.BranchPageN, "BranchPageN") assert.Equal(t, 0, stats.BranchPageN, "BranchPageN")
@ -743,7 +744,7 @@ func TestBucket_Stats_Small(t *testing.T) {
} }
assert.Equal(t, 1, stats.BucketN, "BucketN") assert.Equal(t, 1, stats.BucketN, "BucketN")
assert.Equal(t, 1, stats.InlineBucketN, "InlineBucketN") assert.Equal(t, 1, stats.InlineBucketN, "InlineBucketN")
assert.Equal(t, pageHeaderSize+leafPageElementSize+6, stats.InlineBucketInuse, "InlineBucketInuse") assert.Equal(t, 16+16+6, stats.InlineBucketInuse, "InlineBucketInuse")
return nil return nil
}) })
} }
@ -752,14 +753,14 @@ func TestBucket_Stats_EmptyBucket(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
// Add a bucket that fits on a single root leaf. // Add a bucket that fits on a single root leaf.
_, err := tx.CreateBucket([]byte("whozawhats")) _, err := tx.CreateBucket([]byte("whozawhats"))
assert.NoError(t, err) assert.NoError(t, err)
return nil return nil
}) })
db.MustCheck() db.MustCheck()
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("whozawhats")) b := tx.Bucket([]byte("whozawhats"))
stats := b.Stats() stats := b.Stats()
assert.Equal(t, 0, stats.BranchPageN, "BranchPageN") assert.Equal(t, 0, stats.BranchPageN, "BranchPageN")
@ -777,7 +778,7 @@ func TestBucket_Stats_EmptyBucket(t *testing.T) {
} }
assert.Equal(t, 1, stats.BucketN, "BucketN") assert.Equal(t, 1, stats.BucketN, "BucketN")
assert.Equal(t, 1, stats.InlineBucketN, "InlineBucketN") assert.Equal(t, 1, stats.InlineBucketN, "InlineBucketN")
assert.Equal(t, pageHeaderSize, stats.InlineBucketInuse, "InlineBucketInuse") assert.Equal(t, 16, stats.InlineBucketInuse, "InlineBucketInuse")
return nil return nil
}) })
} }
@ -787,7 +788,7 @@ func TestBucket_Stats_Nested(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("foo")) b, err := tx.CreateBucket([]byte("foo"))
assert.NoError(t, err) assert.NoError(t, err)
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
@ -808,7 +809,7 @@ func TestBucket_Stats_Nested(t *testing.T) {
db.MustCheck() db.MustCheck()
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("foo")) b := tx.Bucket([]byte("foo"))
stats := b.Stats() stats := b.Stats()
assert.Equal(t, 0, stats.BranchPageN, "BranchPageN") assert.Equal(t, 0, stats.BranchPageN, "BranchPageN")
@ -819,19 +820,19 @@ func TestBucket_Stats_Nested(t *testing.T) {
assert.Equal(t, 3, stats.Depth, "Depth") assert.Equal(t, 3, stats.Depth, "Depth")
assert.Equal(t, 0, stats.BranchInuse, "BranchInuse") assert.Equal(t, 0, stats.BranchInuse, "BranchInuse")
foo := pageHeaderSize // foo foo := 16 // foo (pghdr)
foo += 101 * leafPageElementSize // foo leaf elements foo += 101 * 16 // foo leaf elements
foo += 100*2 + 100*2 // foo leaf key/values foo += 100*2 + 100*2 // foo leaf key/values
foo += 3 + bucketHeaderSize // foo -> bar key/value foo += 3 + 16 // foo -> bar key/value
bar := pageHeaderSize // bar bar := 16 // bar (pghdr)
bar += 11 * leafPageElementSize // bar leaf elements bar += 11 * 16 // bar leaf elements
bar += 10 + 10 // bar leaf key/values bar += 10 + 10 // bar leaf key/values
bar += 3 + bucketHeaderSize // bar -> baz key/value bar += 3 + 16 // bar -> baz key/value
baz := pageHeaderSize // baz (inline) baz := 16 // baz (inline) (pghdr)
baz += 10 * leafPageElementSize // baz leaf elements baz += 10 * 16 // baz leaf elements
baz += 10 + 10 // baz leaf key/values baz += 10 + 10 // baz leaf key/values
assert.Equal(t, foo+bar+baz, stats.LeafInuse, "LeafInuse") assert.Equal(t, foo+bar+baz, stats.LeafInuse, "LeafInuse")
if os.Getpagesize() == 4096 { if os.Getpagesize() == 4096 {
@ -857,7 +858,7 @@ func TestBucket_Stats_Large(t *testing.T) {
var index int var index int
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
// Add bucket with lots of keys. // Add bucket with lots of keys.
b, _ := tx.CreateBucketIfNotExists([]byte("widgets")) b, _ := tx.CreateBucketIfNotExists([]byte("widgets"))
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
@ -869,7 +870,7 @@ func TestBucket_Stats_Large(t *testing.T) {
} }
db.MustCheck() db.MustCheck()
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
stats := b.Stats() stats := b.Stats()
assert.Equal(t, 13, stats.BranchPageN, "BranchPageN") assert.Equal(t, 13, stats.BranchPageN, "BranchPageN")
@ -905,12 +906,12 @@ func TestBucket_Put_Single(t *testing.T) {
m := make(map[string][]byte) m := make(map[string][]byte)
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err return err
}) })
for _, item := range items { for _, item := range items {
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
if err := tx.Bucket([]byte("widgets")).Put(item.Key, item.Value); err != nil { if err := tx.Bucket([]byte("widgets")).Put(item.Key, item.Value); err != nil {
panic("put error: " + err.Error()) panic("put error: " + err.Error())
} }
@ -919,7 +920,7 @@ func TestBucket_Put_Single(t *testing.T) {
}) })
// Verify all key/values so far. // Verify all key/values so far.
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
i := 0 i := 0
for k, v := range m { for k, v := range m {
value := tx.Bucket([]byte("widgets")).Get([]byte(k)) value := tx.Bucket([]byte("widgets")).Get([]byte(k))
@ -952,11 +953,11 @@ func TestBucket_Put_Multiple(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
// Bulk insert all values. // Bulk insert all values.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err return err
}) })
err := db.Update(func(tx *Tx) error { err := db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("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))
@ -966,7 +967,7 @@ func TestBucket_Put_Multiple(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
// Verify all items exist. // Verify all items exist.
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
for _, item := range items { for _, item := range items {
value := b.Get(item.Key) value := b.Get(item.Key)
@ -994,11 +995,11 @@ func TestBucket_Delete_Quick(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
// Bulk insert all values. // Bulk insert all values.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err return err
}) })
err := db.Update(func(tx *Tx) error { err := db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("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))
@ -1009,14 +1010,14 @@ func TestBucket_Delete_Quick(t *testing.T) {
// Remove items one at a time and check consistency. // Remove items one at a time and check consistency.
for _, item := range items { for _, item := range items {
err := db.Update(func(tx *Tx) error { err := db.Update(func(tx *bolt.Tx) error {
return tx.Bucket([]byte("widgets")).Delete(item.Key) return tx.Bucket([]byte("widgets")).Delete(item.Key)
}) })
assert.NoError(t, err) assert.NoError(t, err)
} }
// Anything before our deletion index should be nil. // Anything before our deletion index should be nil.
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
tx.Bucket([]byte("widgets")).ForEach(func(k, v []byte) error { tx.Bucket([]byte("widgets")).ForEach(func(k, v []byte) error {
t.Fatalf("bucket should be empty; found: %06x", trunc(k, 3)) t.Fatalf("bucket should be empty; found: %06x", trunc(k, 3))
return nil return nil
@ -1032,12 +1033,12 @@ func TestBucket_Delete_Quick(t *testing.T) {
func ExampleBucket_Put() { func ExampleBucket_Put() {
// Open the database. // Open the database.
db, _ := Open(tempfile(), 0666, nil) db, _ := bolt.Open(tempfile(), 0666, nil)
defer os.Remove(db.Path()) defer os.Remove(db.Path())
defer db.Close() defer db.Close()
// Start a write transaction. // Start a write transaction.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
// Create a bucket. // Create a bucket.
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
@ -1047,7 +1048,7 @@ func ExampleBucket_Put() {
}) })
// Read value back in a different read-only transaction. // Read value back in a different read-only transaction.
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
fmt.Printf("The value of 'foo' is: %s\n", value) fmt.Printf("The value of 'foo' is: %s\n", value)
return nil return nil
@ -1059,12 +1060,12 @@ func ExampleBucket_Put() {
func ExampleBucket_Delete() { func ExampleBucket_Delete() {
// Open the database. // Open the database.
db, _ := Open(tempfile(), 0666, nil) db, _ := bolt.Open(tempfile(), 0666, nil)
defer os.Remove(db.Path()) defer os.Remove(db.Path())
defer db.Close() defer db.Close()
// Start a write transaction. // Start a write transaction.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
// Create a bucket. // Create a bucket.
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
@ -1079,12 +1080,12 @@ func ExampleBucket_Delete() {
}) })
// Delete the key in a different write transaction. // Delete the key in a different write transaction.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
return tx.Bucket([]byte("widgets")).Delete([]byte("foo")) return tx.Bucket([]byte("widgets")).Delete([]byte("foo"))
}) })
// Retrieve the key again. // Retrieve the key again.
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
if value == nil { if value == nil {
fmt.Printf("The value of 'foo' is now: nil\n") fmt.Printf("The value of 'foo' is now: nil\n")
@ -1099,12 +1100,12 @@ func ExampleBucket_Delete() {
func ExampleBucket_ForEach() { func ExampleBucket_ForEach() {
// Open the database. // Open the database.
db, _ := Open(tempfile(), 0666, nil) db, _ := bolt.Open(tempfile(), 0666, nil)
defer os.Remove(db.Path()) defer os.Remove(db.Path())
defer db.Close() defer db.Close()
// Insert data into a bucket. // Insert data into a bucket.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("animals")) tx.CreateBucket([]byte("animals"))
b := tx.Bucket([]byte("animals")) b := tx.Bucket([]byte("animals"))
b.Put([]byte("dog"), []byte("fun")) b.Put([]byte("dog"), []byte("fun"))

View File

@ -1,4 +1,4 @@
package bolt package bolt_test
import ( import (
"bytes" "bytes"
@ -7,6 +7,7 @@ import (
"testing" "testing"
"testing/quick" "testing/quick"
"github.com/boltdb/bolt"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -14,7 +15,7 @@ import (
func TestCursor_Bucket(t *testing.T) { func TestCursor_Bucket(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
b, _ := tx.CreateBucket([]byte("widgets")) b, _ := tx.CreateBucket([]byte("widgets"))
c := b.Cursor() c := b.Cursor()
assert.Equal(t, b, c.Bucket()) assert.Equal(t, b, c.Bucket())
@ -26,7 +27,7 @@ func TestCursor_Bucket(t *testing.T) {
func TestCursor_Seek(t *testing.T) { func TestCursor_Seek(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets")) b, err := tx.CreateBucket([]byte("widgets"))
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, b.Put([]byte("foo"), []byte("0001"))) assert.NoError(t, b.Put([]byte("foo"), []byte("0001")))
@ -36,7 +37,7 @@ func TestCursor_Seek(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
return nil return nil
}) })
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
c := tx.Bucket([]byte("widgets")).Cursor() c := tx.Bucket([]byte("widgets")).Cursor()
// Exact match should go to the key. // Exact match should go to the key.
@ -75,7 +76,7 @@ func TestCursor_Delete(t *testing.T) {
var count = 1000 var count = 1000
// Insert every other key between 0 and $count. // Insert every other key between 0 and $count.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
b, _ := tx.CreateBucket([]byte("widgets")) b, _ := tx.CreateBucket([]byte("widgets"))
for i := 0; i < count; i += 1 { for i := 0; i < count; i += 1 {
k := make([]byte, 8) k := make([]byte, 8)
@ -86,7 +87,7 @@ func TestCursor_Delete(t *testing.T) {
return nil return nil
}) })
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
c := tx.Bucket([]byte("widgets")).Cursor() c := tx.Bucket([]byte("widgets")).Cursor()
bound := make([]byte, 8) bound := make([]byte, 8)
binary.BigEndian.PutUint64(bound, uint64(count/2)) binary.BigEndian.PutUint64(bound, uint64(count/2))
@ -97,11 +98,11 @@ func TestCursor_Delete(t *testing.T) {
} }
c.Seek([]byte("sub")) c.Seek([]byte("sub"))
err := c.Delete() err := c.Delete()
assert.Equal(t, err, ErrIncompatibleValue) assert.Equal(t, err, bolt.ErrIncompatibleValue)
return nil return nil
}) })
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
assert.Equal(t, b.Stats().KeyN, count/2+1) assert.Equal(t, b.Stats().KeyN, count/2+1)
return nil return nil
@ -120,7 +121,7 @@ func TestCursor_Seek_Large(t *testing.T) {
var count = 10000 var count = 10000
// Insert every other key between 0 and $count. // Insert every other key between 0 and $count.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
b, _ := tx.CreateBucket([]byte("widgets")) b, _ := tx.CreateBucket([]byte("widgets"))
for i := 0; i < count; i += 100 { for i := 0; i < count; i += 100 {
for j := i; j < i+100; j += 2 { for j := i; j < i+100; j += 2 {
@ -132,7 +133,7 @@ func TestCursor_Seek_Large(t *testing.T) {
return nil return nil
}) })
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
c := tx.Bucket([]byte("widgets")).Cursor() c := tx.Bucket([]byte("widgets")).Cursor()
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
seek := make([]byte, 8) seek := make([]byte, 8)
@ -164,11 +165,11 @@ func TestCursor_Seek_Large(t *testing.T) {
func TestCursor_EmptyBucket(t *testing.T) { func TestCursor_EmptyBucket(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err return err
}) })
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
c := tx.Bucket([]byte("widgets")).Cursor() c := tx.Bucket([]byte("widgets")).Cursor()
k, v := c.First() k, v := c.First()
assert.Nil(t, k) assert.Nil(t, k)
@ -182,11 +183,11 @@ func TestCursor_EmptyBucketReverse(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err return err
}) })
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
c := tx.Bucket([]byte("widgets")).Cursor() c := tx.Bucket([]byte("widgets")).Cursor()
k, v := c.Last() k, v := c.Last()
assert.Nil(t, k) assert.Nil(t, k)
@ -200,7 +201,7 @@ func TestCursor_Iterate_Leaf(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte{}) tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte{})
tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte{0}) tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte{0})
@ -238,7 +239,7 @@ func TestCursor_LeafRootReverse(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte{}) tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte{})
tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte{0}) tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte{0})
@ -276,7 +277,7 @@ func TestCursor_Restart(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
tx.Bucket([]byte("widgets")).Put([]byte("bar"), []byte{}) tx.Bucket([]byte("widgets")).Put([]byte("bar"), []byte{})
tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte{}) tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte{})
@ -380,7 +381,7 @@ func TestCursor_QuickCheck_BucketsOnly(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets")) b, err := tx.CreateBucket([]byte("widgets"))
assert.NoError(t, err) assert.NoError(t, err)
_, err = b.CreateBucket([]byte("foo")) _, err = b.CreateBucket([]byte("foo"))
@ -391,7 +392,7 @@ func TestCursor_QuickCheck_BucketsOnly(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
return nil return nil
}) })
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
var names []string var names []string
c := tx.Bucket([]byte("widgets")).Cursor() c := tx.Bucket([]byte("widgets")).Cursor()
for k, v := c.First(); k != nil; k, v = c.Next() { for k, v := c.First(); k != nil; k, v = c.Next() {
@ -408,7 +409,7 @@ func TestCursor_QuickCheck_BucketsOnly_Reverse(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets")) b, err := tx.CreateBucket([]byte("widgets"))
assert.NoError(t, err) assert.NoError(t, err)
_, err = b.CreateBucket([]byte("foo")) _, err = b.CreateBucket([]byte("foo"))
@ -419,7 +420,7 @@ func TestCursor_QuickCheck_BucketsOnly_Reverse(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
return nil return nil
}) })
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
var names []string var names []string
c := tx.Bucket([]byte("widgets")).Cursor() c := tx.Bucket([]byte("widgets")).Cursor()
for k, v := c.Last(); k != nil; k, v = c.Prev() { for k, v := c.Last(); k != nil; k, v = c.Prev() {

4
db.go
View File

@ -384,8 +384,8 @@ func (db *DB) beginRWTx() (*Tx, error) {
// Free any pages associated with closed read-only transactions. // Free any pages associated with closed read-only transactions.
var minid txid = 0xFFFFFFFFFFFFFFFF var minid txid = 0xFFFFFFFFFFFFFFFF
for _, t := range db.txs { for _, t := range db.txs {
if t.id() < minid { if t.meta.txid < minid {
minid = t.id() minid = t.meta.txid
} }
} }
if minid > 0 { if minid > 0 {

View File

@ -1,4 +1,4 @@
package bolt package bolt_test
import ( import (
"errors" "errors"
@ -12,8 +12,8 @@ import (
"strings" "strings"
"testing" "testing"
"time" "time"
"unsafe"
"github.com/boltdb/bolt"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -21,7 +21,7 @@ var statsFlag = flag.Bool("stats", false, "show performance stats")
// Ensure that opening a database with a bad path returns an error. // Ensure that opening a database with a bad path returns an error.
func TestOpen_BadPath(t *testing.T) { func TestOpen_BadPath(t *testing.T) {
db, err := Open("", 0666, nil) db, err := bolt.Open("", 0666, nil)
assert.Error(t, err) assert.Error(t, err)
assert.Nil(t, db) assert.Nil(t, db)
} }
@ -30,7 +30,7 @@ func TestOpen_BadPath(t *testing.T) {
func TestOpen(t *testing.T) { func TestOpen(t *testing.T) {
path := tempfile() path := tempfile()
defer os.Remove(path) defer os.Remove(path)
db, err := Open(path, 0666, nil) db, err := bolt.Open(path, 0666, nil)
assert.NotNil(t, db) assert.NotNil(t, db)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, db.Path(), path) assert.Equal(t, db.Path(), path)
@ -47,15 +47,15 @@ func TestOpen_Timeout(t *testing.T) {
defer os.Remove(path) defer os.Remove(path)
// Open a data file. // Open a data file.
db0, err := Open(path, 0666, nil) db0, err := bolt.Open(path, 0666, nil)
assert.NotNil(t, db0) assert.NotNil(t, db0)
assert.NoError(t, err) assert.NoError(t, err)
// Attempt to open the database again. // Attempt to open the database again.
start := time.Now() start := time.Now()
db1, err := Open(path, 0666, &Options{Timeout: 100 * time.Millisecond}) db1, err := bolt.Open(path, 0666, &bolt.Options{Timeout: 100 * time.Millisecond})
assert.Nil(t, db1) assert.Nil(t, db1)
assert.Equal(t, ErrTimeout, err) assert.Equal(t, bolt.ErrTimeout, err)
assert.True(t, time.Since(start) > 100*time.Millisecond) assert.True(t, time.Since(start) > 100*time.Millisecond)
db0.Close() db0.Close()
@ -71,7 +71,7 @@ func TestOpen_Wait(t *testing.T) {
defer os.Remove(path) defer os.Remove(path)
// Open a data file. // Open a data file.
db0, err := Open(path, 0666, nil) db0, err := bolt.Open(path, 0666, nil)
assert.NotNil(t, db0) assert.NotNil(t, db0)
assert.NoError(t, err) assert.NoError(t, err)
@ -80,7 +80,7 @@ func TestOpen_Wait(t *testing.T) {
// Attempt to open the database again. // Attempt to open the database again.
start := time.Now() start := time.Now()
db1, err := Open(path, 0666, &Options{Timeout: 200 * time.Millisecond}) db1, err := bolt.Open(path, 0666, &bolt.Options{Timeout: 200 * time.Millisecond})
assert.NotNil(t, db1) assert.NotNil(t, db1)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, time.Since(start) > 100*time.Millisecond) assert.True(t, time.Since(start) > 100*time.Millisecond)
@ -91,14 +91,14 @@ func TestOpen_Check(t *testing.T) {
path := tempfile() path := tempfile()
defer os.Remove(path) defer os.Remove(path)
db, err := Open(path, 0666, nil) db, err := bolt.Open(path, 0666, nil)
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, db.View(func(tx *Tx) error { return <-tx.Check() })) assert.NoError(t, db.View(func(tx *bolt.Tx) error { return <-tx.Check() }))
db.Close() db.Close()
db, err = Open(path, 0666, nil) db, err = bolt.Open(path, 0666, nil)
assert.NoError(t, err) assert.NoError(t, err)
assert.NoError(t, db.View(func(tx *Tx) error { return <-tx.Check() })) assert.NoError(t, db.View(func(tx *bolt.Tx) error { return <-tx.Check() }))
db.Close() db.Close()
} }
@ -107,7 +107,7 @@ func TestDB_Open_FileError(t *testing.T) {
path := tempfile() path := tempfile()
defer os.Remove(path) defer os.Remove(path)
_, err := Open(path+"/youre-not-my-real-parent", 0666, nil) _, err := bolt.Open(path+"/youre-not-my-real-parent", 0666, nil)
if err, _ := err.(*os.PathError); assert.Error(t, err) { if err, _ := err.(*os.PathError); assert.Error(t, err) {
assert.Equal(t, path+"/youre-not-my-real-parent", err.Path) assert.Equal(t, path+"/youre-not-my-real-parent", err.Path)
assert.Equal(t, "open", err.Op) assert.Equal(t, "open", err.Op)
@ -124,17 +124,20 @@ func TestDB_Open_FileTooSmall(t *testing.T) {
path := tempfile() path := tempfile()
defer os.Remove(path) defer os.Remove(path)
db, err := Open(path, 0666, nil) db, err := bolt.Open(path, 0666, nil)
assert.NoError(t, err) assert.NoError(t, err)
db.Close() db.Close()
// corrupt the database // corrupt the database
assert.NoError(t, os.Truncate(path, int64(os.Getpagesize()))) assert.NoError(t, os.Truncate(path, int64(os.Getpagesize())))
db, err = Open(path, 0666, nil) db, err = bolt.Open(path, 0666, nil)
assert.Equal(t, errors.New("file size too small"), err) assert.Equal(t, errors.New("file size too small"), err)
} }
// TODO(benbjohnson): Test corruption at every byte of the first two pages.
/*
// Ensure that corrupt meta0 page errors get returned. // Ensure that corrupt meta0 page errors get returned.
func TestDB_Open_CorruptMeta0(t *testing.T) { func TestDB_Open_CorruptMeta0(t *testing.T) {
var m meta var m meta
@ -156,7 +159,7 @@ func TestDB_Open_CorruptMeta0(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
// Open the database. // Open the database.
_, err = Open(path, 0666, nil) _, err = bolt.Open(path, 0666, nil)
assert.Equal(t, err, errors.New("meta0 error: invalid database")) assert.Equal(t, err, errors.New("meta0 error: invalid database"))
} }
@ -166,13 +169,13 @@ func TestDB_Open_MetaChecksumError(t *testing.T) {
path := tempfile() path := tempfile()
defer os.Remove(path) defer os.Remove(path)
db, err := Open(path, 0600, nil) db, err := bolt.Open(path, 0600, nil)
pageSize := db.pageSize pageSize := db.pageSize
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err return err
}) })
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("woojits")) _, err := tx.CreateBucket([]byte("woojits"))
return err return err
}) })
@ -185,7 +188,7 @@ func TestDB_Open_MetaChecksumError(t *testing.T) {
f.Close() f.Close()
// Reopen the database. // Reopen the database.
_, err = Open(path, 0600, nil) _, err = bolt.Open(path, 0600, nil)
if assert.Error(t, err) { if assert.Error(t, err) {
if i == 0 { if i == 0 {
assert.Equal(t, "meta0 error: checksum error", err.Error()) assert.Equal(t, "meta0 error: checksum error", err.Error())
@ -195,13 +198,14 @@ func TestDB_Open_MetaChecksumError(t *testing.T) {
} }
} }
} }
*/
// Ensure that a database cannot open a transaction when it's not open. // Ensure that a database cannot open a transaction when it's not open.
func TestDB_Begin_DatabaseNotOpen(t *testing.T) { func TestDB_Begin_DatabaseNotOpen(t *testing.T) {
var db DB var db bolt.DB
tx, err := db.Begin(false) tx, err := db.Begin(false)
assert.Nil(t, tx) assert.Nil(t, tx)
assert.Equal(t, err, ErrDatabaseNotOpen) assert.Equal(t, err, bolt.ErrDatabaseNotOpen)
} }
// Ensure that a read-write transaction can be retrieved. // Ensure that a read-write transaction can be retrieved.
@ -218,9 +222,9 @@ func TestDB_BeginRW(t *testing.T) {
// Ensure that opening a transaction while the DB is closed returns an error. // Ensure that opening a transaction while the DB is closed returns an error.
func TestDB_BeginRW_Closed(t *testing.T) { func TestDB_BeginRW_Closed(t *testing.T) {
var db DB var db bolt.DB
tx, err := db.Begin(true) tx, err := db.Begin(true)
assert.Equal(t, err, ErrDatabaseNotOpen) assert.Equal(t, err, bolt.ErrDatabaseNotOpen)
assert.Nil(t, tx) assert.Nil(t, tx)
} }
@ -228,7 +232,7 @@ func TestDB_BeginRW_Closed(t *testing.T) {
func TestDB_Update(t *testing.T) { func TestDB_Update(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
err := db.Update(func(tx *Tx) error { err := db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
b.Put([]byte("foo"), []byte("bar")) b.Put([]byte("foo"), []byte("bar"))
@ -237,7 +241,7 @@ func TestDB_Update(t *testing.T) {
return nil return nil
}) })
assert.NoError(t, err) assert.NoError(t, err)
err = db.View(func(tx *Tx) error { err = db.View(func(tx *bolt.Tx) error {
assert.Nil(t, tx.Bucket([]byte("widgets")).Get([]byte("foo"))) assert.Nil(t, tx.Bucket([]byte("widgets")).Get([]byte("foo")))
assert.Equal(t, []byte("bat"), tx.Bucket([]byte("widgets")).Get([]byte("baz"))) assert.Equal(t, []byte("bat"), tx.Bucket([]byte("widgets")).Get([]byte("baz")))
return nil return nil
@ -247,24 +251,24 @@ func TestDB_Update(t *testing.T) {
// Ensure a closed database returns an error while running a transaction block // Ensure a closed database returns an error while running a transaction block
func TestDB_Update_Closed(t *testing.T) { func TestDB_Update_Closed(t *testing.T) {
var db DB var db bolt.DB
err := db.Update(func(tx *Tx) error { err := db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
return nil return nil
}) })
assert.Equal(t, err, ErrDatabaseNotOpen) assert.Equal(t, err, bolt.ErrDatabaseNotOpen)
} }
// Ensure a panic occurs while trying to commit a managed transaction. // Ensure a panic occurs while trying to commit a managed transaction.
func TestDB_Update_ManualCommitAndRollback(t *testing.T) { func TestDB_Update_ManualCommitAndRollback(t *testing.T) {
var db DB var db bolt.DB
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
assert.Panics(t, func() { tx.Commit() }) assert.Panics(t, func() { tx.Commit() })
assert.Panics(t, func() { tx.Rollback() }) assert.Panics(t, func() { tx.Rollback() })
return nil return nil
}) })
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
assert.Panics(t, func() { tx.Commit() }) assert.Panics(t, func() { tx.Commit() })
assert.Panics(t, func() { tx.Rollback() }) assert.Panics(t, func() { tx.Rollback() })
return nil return nil
@ -279,24 +283,24 @@ func TestDB_Update_Panic(t *testing.T) {
func() { func() {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
warn("recover: update", r) t.Log("recover: update", r)
} }
}() }()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
panic("omg") panic("omg")
}) })
}() }()
// Verify we can update again. // Verify we can update again.
err := db.Update(func(tx *Tx) error { err := db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err return err
}) })
assert.NoError(t, err) assert.NoError(t, err)
// Verify that our change persisted. // Verify that our change persisted.
err = db.Update(func(tx *Tx) error { err = db.Update(func(tx *bolt.Tx) error {
assert.NotNil(t, tx.Bucket([]byte("widgets"))) assert.NotNil(t, tx.Bucket([]byte("widgets")))
return nil return nil
}) })
@ -306,7 +310,7 @@ func TestDB_Update_Panic(t *testing.T) {
func TestDB_View_Error(t *testing.T) { func TestDB_View_Error(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
err := db.View(func(tx *Tx) error { err := db.View(func(tx *bolt.Tx) error {
return errors.New("xxx") return errors.New("xxx")
}) })
assert.Equal(t, errors.New("xxx"), err) assert.Equal(t, errors.New("xxx"), err)
@ -316,7 +320,7 @@ func TestDB_View_Error(t *testing.T) {
func TestDB_View_Panic(t *testing.T) { func TestDB_View_Panic(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
return nil return nil
}) })
@ -324,17 +328,17 @@ func TestDB_View_Panic(t *testing.T) {
func() { func() {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
warn("recover: view", r) t.Log("recover: view", r)
} }
}() }()
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
assert.NotNil(t, tx.Bucket([]byte("widgets"))) assert.NotNil(t, tx.Bucket([]byte("widgets")))
panic("omg") panic("omg")
}) })
}() }()
// Verify that we can still use read transactions. // Verify that we can still use read transactions.
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
assert.NotNil(t, tx.Bucket([]byte("widgets"))) assert.NotNil(t, tx.Bucket([]byte("widgets")))
return nil return nil
}) })
@ -349,7 +353,7 @@ func TestDB_Commit_WriteFail(t *testing.T) {
func TestDB_Stats(t *testing.T) { func TestDB_Stats(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err return err
}) })
@ -359,35 +363,22 @@ func TestDB_Stats(t *testing.T) {
assert.Equal(t, 2, stats.PendingPageN, "PendingPageN") assert.Equal(t, 2, stats.PendingPageN, "PendingPageN")
} }
// Ensure that the mmap grows appropriately.
func TestDB_mmapSize(t *testing.T) {
db := &DB{pageSize: 4096}
assert.Equal(t, db.mmapSize(0), minMmapSize)
assert.Equal(t, db.mmapSize(16384), minMmapSize)
assert.Equal(t, db.mmapSize(minMmapSize-1), minMmapSize)
assert.Equal(t, db.mmapSize(minMmapSize), minMmapSize)
assert.Equal(t, db.mmapSize(minMmapSize+1), (minMmapSize*2)+4096)
assert.Equal(t, db.mmapSize(10000000), 20000768)
assert.Equal(t, db.mmapSize((1<<30)-1), 2147483648)
assert.Equal(t, db.mmapSize(1<<30), 1<<31)
}
// Ensure that database pages are in expected order and type. // Ensure that database pages are in expected order and type.
func TestDB_Consistency(t *testing.T) { func TestDB_Consistency(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err return err
}) })
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
assert.NoError(t, tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))) assert.NoError(t, tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")))
return nil return nil
}) })
} }
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
if p, _ := tx.Page(0); assert.NotNil(t, p) { if p, _ := tx.Page(0); assert.NotNil(t, p) {
assert.Equal(t, "meta", p.Type) assert.Equal(t, "meta", p.Type)
} }
@ -412,16 +403,9 @@ func TestDB_Consistency(t *testing.T) {
}) })
} }
// Ensure that a database can return a string representation of itself.
func TestDB_String(t *testing.T) {
db := &DB{path: "/foo/bar"}
assert.Equal(t, db.String(), `DB<"/foo/bar">`)
assert.Equal(t, db.GoString(), `bolt.DB{path:"/foo/bar"}`)
}
// Ensure that DB stats can be substracted from one another. // Ensure that DB stats can be substracted from one another.
func TestDBStats_Sub(t *testing.T) { func TestDBStats_Sub(t *testing.T) {
var a, b Stats var a, b bolt.Stats
a.TxStats.PageCount = 3 a.TxStats.PageCount = 3
a.FreePageN = 4 a.FreePageN = 4
b.TxStats.PageCount = 10 b.TxStats.PageCount = 10
@ -432,76 +416,14 @@ func TestDBStats_Sub(t *testing.T) {
assert.Equal(t, 14, diff.FreePageN) assert.Equal(t, 14, diff.FreePageN)
} }
// Ensure that meta with bad magic is invalid.
func TestMeta_validate_magic(t *testing.T) {
m := &meta{magic: 0x01234567}
assert.Equal(t, m.validate(), ErrInvalid)
}
// Ensure that meta with a bad version is invalid.
func TestMeta_validate_version(t *testing.T) {
m := &meta{magic: magic, version: 200}
assert.Equal(t, m.validate(), ErrVersionMismatch)
}
// Ensure that a DB in strict mode will fail when corrupted.
func TestDB_StrictMode(t *testing.T) {
var msg string
func() {
defer func() {
msg = fmt.Sprintf("%s", recover())
}()
db := NewTestDB()
defer db.Close()
db.StrictMode = true
db.Update(func(tx *Tx) error {
tx.CreateBucket([]byte("foo"))
// Corrupt the DB by extending the high water mark.
tx.meta.pgid++
return nil
})
}()
assert.Equal(t, "check fail: page 4: unreachable unfreed", msg)
}
// Ensure that a double freeing a page will result in a panic.
func TestDB_DoubleFree(t *testing.T) {
var msg string
func() {
defer func() {
msg = fmt.Sprintf("%s", recover())
}()
db := NewTestDB()
defer os.Remove(db.DB.Path())
defer db.DB.Close()
db.Update(func(tx *Tx) error {
tx.CreateBucket([]byte("foo"))
// Corrupt the DB by adding a page to the freelist.
db.freelist.free(0, tx.page(3))
return nil
})
}()
assert.Equal(t, "assertion failed: page 3 already freed", msg)
}
func ExampleDB_Update() { func ExampleDB_Update() {
// Open the database. // Open the database.
db, _ := Open(tempfile(), 0666, nil) db, _ := bolt.Open(tempfile(), 0666, nil)
defer os.Remove(db.Path()) defer os.Remove(db.Path())
defer db.Close() defer db.Close()
// Execute several commands within a write transaction. // Execute several commands within a write transaction.
err := db.Update(func(tx *Tx) error { err := db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets")) b, err := tx.CreateBucket([]byte("widgets"))
if err != nil { if err != nil {
return err return err
@ -514,7 +436,7 @@ func ExampleDB_Update() {
// 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 {
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
fmt.Printf("The value of 'foo' is: %s\n", value) fmt.Printf("The value of 'foo' is: %s\n", value)
return nil return nil
@ -527,12 +449,12 @@ func ExampleDB_Update() {
func ExampleDB_View() { func ExampleDB_View() {
// Open the database. // Open the database.
db, _ := Open(tempfile(), 0666, nil) db, _ := bolt.Open(tempfile(), 0666, nil)
defer os.Remove(db.Path()) defer os.Remove(db.Path())
defer db.Close() defer db.Close()
// Insert data into a bucket. // Insert data into a bucket.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("people")) tx.CreateBucket([]byte("people"))
b := tx.Bucket([]byte("people")) b := tx.Bucket([]byte("people"))
b.Put([]byte("john"), []byte("doe")) b.Put([]byte("john"), []byte("doe"))
@ -541,7 +463,7 @@ func ExampleDB_View() {
}) })
// Access data from within a read-only transactional block. // Access data from within a read-only transactional block.
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
v := tx.Bucket([]byte("people")).Get([]byte("john")) v := tx.Bucket([]byte("people")).Get([]byte("john"))
fmt.Printf("John's last name is %s.\n", v) fmt.Printf("John's last name is %s.\n", v)
return nil return nil
@ -553,12 +475,12 @@ func ExampleDB_View() {
func ExampleDB_Begin_ReadOnly() { func ExampleDB_Begin_ReadOnly() {
// Open the database. // Open the database.
db, _ := Open(tempfile(), 0666, nil) db, _ := bolt.Open(tempfile(), 0666, nil)
defer os.Remove(db.Path()) defer os.Remove(db.Path())
defer db.Close() defer db.Close()
// Create a bucket. // Create a bucket.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err return err
}) })
@ -588,12 +510,12 @@ func ExampleDB_Begin_ReadOnly() {
// TestDB represents a wrapper around a Bolt DB to handle temporary file // TestDB represents a wrapper around a Bolt DB to handle temporary file
// creation and automatic cleanup on close. // creation and automatic cleanup on close.
type TestDB struct { type TestDB struct {
*DB *bolt.DB
} }
// NewTestDB returns a new instance of TestDB. // NewTestDB returns a new instance of TestDB.
func NewTestDB() *TestDB { func NewTestDB() *TestDB {
db, err := Open(tempfile(), 0666, nil) db, err := bolt.Open(tempfile(), 0666, nil)
if err != nil { if err != nil {
panic("cannot open db: " + err.Error()) panic("cannot open db: " + err.Error())
} }
@ -632,7 +554,7 @@ func (db *TestDB) PrintStats() {
// MustCheck runs a consistency check on the database and panics if any errors are found. // MustCheck runs a consistency check on the database and panics if any errors are found.
func (db *TestDB) MustCheck() { func (db *TestDB) MustCheck() {
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
// Collect all the errors. // Collect all the errors.
var errors []error var errors []error
for err := range tx.Check() { for err := range tx.Check() {
@ -667,7 +589,7 @@ func (db *TestDB) MustCheck() {
// CopyTempFile copies a database to a temporary file. // CopyTempFile copies a database to a temporary file.
func (db *TestDB) CopyTempFile() { func (db *TestDB) CopyTempFile() {
path := tempfile() path := tempfile()
db.View(func(tx *Tx) error { return tx.CopyFile(path, 0600) }) db.View(func(tx *bolt.Tx) error { return tx.CopyFile(path, 0600) })
fmt.Println("db copied to: ", path) fmt.Println("db copied to: ", path)
} }
@ -680,7 +602,7 @@ func tempfile() string {
} }
// mustContainKeys checks that a bucket contains a given set of keys. // mustContainKeys checks that a bucket contains a given set of keys.
func mustContainKeys(b *Bucket, m map[string]string) { func mustContainKeys(b *bolt.Bucket, m map[string]string) {
found := make(map[string]string) found := make(map[string]string)
b.ForEach(func(k, _ []byte) error { b.ForEach(func(k, _ []byte) error {
found[string(k)] = "" found[string(k)] = ""

View File

@ -337,7 +337,7 @@ func (n *node) spill() error {
for _, node := range nodes { for _, node := range nodes {
// Add node's page to the freelist if it's not new. // Add node's page to the freelist if it's not new.
if node.pgid > 0 { if node.pgid > 0 {
tx.db.freelist.free(tx.id(), tx.page(node.pgid)) tx.db.freelist.free(tx.meta.txid, tx.page(node.pgid))
node.pgid = 0 node.pgid = 0
} }
@ -565,7 +565,7 @@ func (n *node) dereference() {
// free adds the node's underlying page to the freelist. // free adds the node's underlying page to the freelist.
func (n *node) free() { func (n *node) free() {
if n.pgid != 0 { if n.pgid != 0 {
n.bucket.tx.db.freelist.free(n.bucket.tx.id(), n.bucket.tx.page(n.pgid)) n.bucket.tx.db.freelist.free(n.bucket.tx.meta.txid, n.bucket.tx.page(n.pgid))
n.pgid = 0 n.pgid = 0
} }
} }

View File

@ -1,9 +1,11 @@
package bolt package bolt_test
import ( import (
"bytes" "bytes"
"flag" "flag"
"fmt"
"math/rand" "math/rand"
"os"
"reflect" "reflect"
"testing/quick" "testing/quick"
"time" "time"
@ -28,8 +30,8 @@ func init() {
flag.IntVar(&qmaxksize, "quick.maxksize", 1024, "") flag.IntVar(&qmaxksize, "quick.maxksize", 1024, "")
flag.IntVar(&qmaxvsize, "quick.maxvsize", 1024, "") flag.IntVar(&qmaxvsize, "quick.maxvsize", 1024, "")
flag.Parse() flag.Parse()
warn("seed:", qseed) fmt.Fprintln(os.Stderr, "seed:", qseed)
warnf("quick settings: count=%v, items=%v, ksize=%v, vsize=%v", qcount, qmaxitems, qmaxksize, qmaxvsize) fmt.Fprintf(os.Stderr, "quick settings: count=%v, items=%v, ksize=%v, vsize=%v\n", qcount, qmaxitems, qmaxksize, qmaxvsize)
} }
func qconfig() *quick.Config { func qconfig() *quick.Config {

View File

@ -1,4 +1,4 @@
package bolt package bolt_test
import ( import (
"bytes" "bytes"
@ -7,6 +7,7 @@ import (
"sync" "sync"
"testing" "testing"
"github.com/boltdb/bolt"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -39,7 +40,7 @@ func testSimulate(t *testing.T, threadCount, parallelism int) {
var readerHandlers = []simulateHandler{simulateGetHandler} var readerHandlers = []simulateHandler{simulateGetHandler}
var writerHandlers = []simulateHandler{simulateGetHandler, simulatePutHandler} var writerHandlers = []simulateHandler{simulateGetHandler, simulatePutHandler}
var versions = make(map[txid]*QuickDB) var versions = make(map[int]*QuickDB)
versions[1] = NewQuickDB() versions[1] = NewQuickDB()
db := NewTestDB() db := NewTestDB()
@ -76,9 +77,9 @@ func testSimulate(t *testing.T, threadCount, parallelism int) {
// Obtain current state of the dataset. // Obtain current state of the dataset.
mutex.Lock() mutex.Lock()
var qdb = versions[tx.id()] var qdb = versions[tx.ID()]
if writable { if writable {
qdb = versions[tx.id()-1].Copy() qdb = versions[tx.ID()-1].Copy()
} }
mutex.Unlock() mutex.Unlock()
@ -86,7 +87,7 @@ func testSimulate(t *testing.T, threadCount, parallelism int) {
if writable { if writable {
defer func() { defer func() {
mutex.Lock() mutex.Lock()
versions[tx.id()] = qdb versions[tx.ID()] = qdb
mutex.Unlock() mutex.Unlock()
assert.NoError(t, tx.Commit()) assert.NoError(t, tx.Commit())
@ -117,10 +118,10 @@ func testSimulate(t *testing.T, threadCount, parallelism int) {
wg.Wait() wg.Wait()
} }
type simulateHandler func(tx *Tx, qdb *QuickDB) type simulateHandler func(tx *bolt.Tx, qdb *QuickDB)
// Retrieves a key from the database and verifies that it is what is expected. // Retrieves a key from the database and verifies that it is what is expected.
func simulateGetHandler(tx *Tx, qdb *QuickDB) { func simulateGetHandler(tx *bolt.Tx, qdb *QuickDB) {
// Randomly retrieve an existing exist. // Randomly retrieve an existing exist.
keys := qdb.Rand() keys := qdb.Rand()
if len(keys) == 0 { if len(keys) == 0 {
@ -155,7 +156,7 @@ func simulateGetHandler(tx *Tx, qdb *QuickDB) {
} }
// Inserts a key into the database. // Inserts a key into the database.
func simulatePutHandler(tx *Tx, qdb *QuickDB) { func simulatePutHandler(tx *bolt.Tx, qdb *QuickDB) {
var err error var err error
keys, value := randKeys(), randValue() keys, value := randKeys(), randValue()

10
tx.go
View File

@ -52,9 +52,9 @@ func (tx *Tx) init(db *DB) {
} }
} }
// id returns the transaction id. // ID returns the transaction id.
func (tx *Tx) id() txid { func (tx *Tx) ID() int {
return tx.meta.txid return int(tx.meta.txid)
} }
// DB returns a reference to the database that created the transaction. // DB returns a reference to the database that created the transaction.
@ -158,7 +158,7 @@ func (tx *Tx) Commit() error {
// Free the freelist and allocate new pages for it. This will overestimate // Free the freelist and allocate new pages for it. This will overestimate
// the size of the freelist but not underestimate the size (which would be bad). // the size of the freelist but not underestimate the size (which would be bad).
tx.db.freelist.free(tx.id(), tx.db.page(tx.meta.freelist)) tx.db.freelist.free(tx.meta.txid, tx.db.page(tx.meta.freelist))
p, err := tx.allocate((tx.db.freelist.size() / tx.db.pageSize) + 1) p, err := tx.allocate((tx.db.freelist.size() / tx.db.pageSize) + 1)
if err != nil { if err != nil {
tx.rollback() tx.rollback()
@ -218,7 +218,7 @@ func (tx *Tx) rollback() {
return return
} }
if tx.writable { if tx.writable {
tx.db.freelist.rollback(tx.id()) tx.db.freelist.rollback(tx.meta.txid)
tx.db.freelist.reload(tx.db.page(tx.db.meta().freelist)) tx.db.freelist.reload(tx.db.page(tx.db.meta().freelist))
} }
tx.close() tx.close()

View File

@ -1,4 +1,4 @@
package bolt package bolt_test
import ( import (
"errors" "errors"
@ -6,6 +6,7 @@ import (
"os" "os"
"testing" "testing"
"github.com/boltdb/bolt"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -16,7 +17,7 @@ func TestTx_Commit_Closed(t *testing.T) {
tx, _ := db.Begin(true) tx, _ := db.Begin(true)
tx.CreateBucket([]byte("foo")) tx.CreateBucket([]byte("foo"))
assert.NoError(t, tx.Commit()) assert.NoError(t, tx.Commit())
assert.Equal(t, tx.Commit(), ErrTxClosed) assert.Equal(t, tx.Commit(), bolt.ErrTxClosed)
} }
// Ensure that rolling back a closed transaction returns an error. // Ensure that rolling back a closed transaction returns an error.
@ -25,7 +26,7 @@ func TestTx_Rollback_Closed(t *testing.T) {
defer db.Close() defer db.Close()
tx, _ := db.Begin(true) tx, _ := db.Begin(true)
assert.NoError(t, tx.Rollback()) assert.NoError(t, tx.Rollback())
assert.Equal(t, tx.Rollback(), ErrTxClosed) assert.Equal(t, tx.Rollback(), bolt.ErrTxClosed)
} }
// Ensure that committing a read-only transaction returns an error. // Ensure that committing a read-only transaction returns an error.
@ -33,14 +34,14 @@ func TestTx_Commit_ReadOnly(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
tx, _ := db.Begin(false) tx, _ := db.Begin(false)
assert.Equal(t, tx.Commit(), ErrTxNotWritable) assert.Equal(t, tx.Commit(), bolt.ErrTxNotWritable)
} }
// Ensure that a transaction can retrieve a cursor on the root bucket. // Ensure that a transaction can retrieve a cursor on the root bucket.
func TestTx_Cursor(t *testing.T) { func TestTx_Cursor(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
tx.CreateBucket([]byte("woojits")) tx.CreateBucket([]byte("woojits"))
c := tx.Cursor() c := tx.Cursor()
@ -65,10 +66,10 @@ func TestTx_Cursor(t *testing.T) {
func TestTx_CreateBucket_ReadOnly(t *testing.T) { func TestTx_CreateBucket_ReadOnly(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("foo")) b, err := tx.CreateBucket([]byte("foo"))
assert.Nil(t, b) assert.Nil(t, b)
assert.Equal(t, ErrTxNotWritable, err) assert.Equal(t, bolt.ErrTxNotWritable, err)
return nil return nil
}) })
} }
@ -81,14 +82,14 @@ func TestTx_CreateBucket_Closed(t *testing.T) {
tx.Commit() tx.Commit()
b, err := tx.CreateBucket([]byte("foo")) b, err := tx.CreateBucket([]byte("foo"))
assert.Nil(t, b) assert.Nil(t, b)
assert.Equal(t, ErrTxClosed, err) assert.Equal(t, bolt.ErrTxClosed, err)
} }
// Ensure that a Tx can retrieve a bucket. // Ensure that a Tx can retrieve a bucket.
func TestTx_Bucket(t *testing.T) { func TestTx_Bucket(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
assert.NotNil(t, b) assert.NotNil(t, b)
@ -100,7 +101,7 @@ func TestTx_Bucket(t *testing.T) {
func TestTx_Get_Missing(t *testing.T) { func TestTx_Get_Missing(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))
value := tx.Bucket([]byte("widgets")).Get([]byte("no_such_key")) value := tx.Bucket([]byte("widgets")).Get([]byte("no_such_key"))
@ -115,7 +116,7 @@ func TestTx_CreateBucket(t *testing.T) {
defer db.Close() defer db.Close()
// Create a bucket. // Create a bucket.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets")) b, err := tx.CreateBucket([]byte("widgets"))
assert.NotNil(t, b) assert.NotNil(t, b)
assert.NoError(t, err) assert.NoError(t, err)
@ -123,7 +124,7 @@ func TestTx_CreateBucket(t *testing.T) {
}) })
// Read the bucket through a separate transaction. // Read the bucket through a separate transaction.
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
assert.NotNil(t, b) assert.NotNil(t, b)
return nil return nil
@ -134,7 +135,7 @@ func TestTx_CreateBucket(t *testing.T) {
func TestTx_CreateBucketIfNotExists(t *testing.T) { func TestTx_CreateBucketIfNotExists(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte("widgets")) b, err := tx.CreateBucketIfNotExists([]byte("widgets"))
assert.NotNil(t, b) assert.NotNil(t, b)
assert.NoError(t, err) assert.NoError(t, err)
@ -145,16 +146,16 @@ func TestTx_CreateBucketIfNotExists(t *testing.T) {
b, err = tx.CreateBucketIfNotExists([]byte{}) b, err = tx.CreateBucketIfNotExists([]byte{})
assert.Nil(t, b) assert.Nil(t, b)
assert.Equal(t, ErrBucketNameRequired, err) assert.Equal(t, bolt.ErrBucketNameRequired, err)
b, err = tx.CreateBucketIfNotExists(nil) b, err = tx.CreateBucketIfNotExists(nil)
assert.Nil(t, b) assert.Nil(t, b)
assert.Equal(t, ErrBucketNameRequired, err) assert.Equal(t, bolt.ErrBucketNameRequired, err)
return nil return nil
}) })
// Read the bucket through a separate transaction. // Read the bucket through a separate transaction.
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
assert.NotNil(t, b) assert.NotNil(t, b)
return nil return nil
@ -166,7 +167,7 @@ func TestTx_CreateBucket_Exists(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
// Create a bucket. // Create a bucket.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets")) b, err := tx.CreateBucket([]byte("widgets"))
assert.NotNil(t, b) assert.NotNil(t, b)
assert.NoError(t, err) assert.NoError(t, err)
@ -174,10 +175,10 @@ func TestTx_CreateBucket_Exists(t *testing.T) {
}) })
// Create the same bucket again. // Create the same bucket again.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("widgets")) b, err := tx.CreateBucket([]byte("widgets"))
assert.Nil(t, b) assert.Nil(t, b)
assert.Equal(t, ErrBucketExists, err) assert.Equal(t, bolt.ErrBucketExists, err)
return nil return nil
}) })
} }
@ -186,10 +187,10 @@ func TestTx_CreateBucket_Exists(t *testing.T) {
func TestTx_CreateBucket_NameRequired(t *testing.T) { func TestTx_CreateBucket_NameRequired(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket(nil) b, err := tx.CreateBucket(nil)
assert.Nil(t, b) assert.Nil(t, b)
assert.Equal(t, ErrBucketNameRequired, err) assert.Equal(t, bolt.ErrBucketNameRequired, err)
return nil return nil
}) })
} }
@ -200,30 +201,20 @@ func TestTx_DeleteBucket(t *testing.T) {
defer db.Close() defer db.Close()
// Create a bucket and add a value. // Create a bucket and add a value.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))
return nil return nil
}) })
// Save root page id.
var root pgid
db.View(func(tx *Tx) error {
root = tx.Bucket([]byte("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.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
assert.NoError(t, tx.DeleteBucket([]byte("widgets"))) assert.NoError(t, tx.DeleteBucket([]byte("widgets")))
assert.Nil(t, tx.Bucket([]byte("widgets"))) assert.Nil(t, tx.Bucket([]byte("widgets")))
return nil return nil
}) })
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
// Verify that the bucket's page is free.
assert.Equal(t, []pgid{4, 5}, 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.
b, err := tx.CreateBucket([]byte("widgets")) b, err := tx.CreateBucket([]byte("widgets"))
assert.NotNil(t, b) assert.NotNil(t, b)
@ -239,15 +230,15 @@ func TestTx_DeleteBucket_Closed(t *testing.T) {
defer db.Close() defer db.Close()
tx, _ := db.Begin(true) tx, _ := db.Begin(true)
tx.Commit() tx.Commit()
assert.Equal(t, tx.DeleteBucket([]byte("foo")), ErrTxClosed) assert.Equal(t, tx.DeleteBucket([]byte("foo")), bolt.ErrTxClosed)
} }
// Ensure that deleting a bucket with a read-only transaction returns an error. // Ensure that deleting a bucket with a read-only transaction returns an error.
func TestTx_DeleteBucket_ReadOnly(t *testing.T) { func TestTx_DeleteBucket_ReadOnly(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
assert.Equal(t, tx.DeleteBucket([]byte("foo")), ErrTxNotWritable) assert.Equal(t, tx.DeleteBucket([]byte("foo")), bolt.ErrTxNotWritable)
return nil return nil
}) })
} }
@ -256,8 +247,8 @@ func TestTx_DeleteBucket_ReadOnly(t *testing.T) {
func TestTx_DeleteBucket_NotFound(t *testing.T) { func TestTx_DeleteBucket_NotFound(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
assert.Equal(t, ErrBucketNotFound, tx.DeleteBucket([]byte("widgets"))) assert.Equal(t, bolt.ErrBucketNotFound, tx.DeleteBucket([]byte("widgets")))
return nil return nil
}) })
} }
@ -267,7 +258,7 @@ func TestTx_OnCommit(t *testing.T) {
var x int var x int
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.OnCommit(func() { x += 1 }) tx.OnCommit(func() { x += 1 })
tx.OnCommit(func() { x += 2 }) tx.OnCommit(func() { x += 2 })
_, err := tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
@ -281,7 +272,7 @@ func TestTx_OnCommit_Rollback(t *testing.T) {
var x int var x int
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.OnCommit(func() { x += 1 }) tx.OnCommit(func() { x += 1 })
tx.OnCommit(func() { x += 2 }) tx.OnCommit(func() { x += 2 })
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
@ -295,20 +286,20 @@ func TestTx_CopyFile(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
var dest = tempfile() var dest = tempfile()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))
tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte("bat")) tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte("bat"))
return nil return nil
}) })
assert.NoError(t, db.View(func(tx *Tx) error { return tx.CopyFile(dest, 0600) })) assert.NoError(t, db.View(func(tx *bolt.Tx) error { return tx.CopyFile(dest, 0600) }))
db2, err := Open(dest, 0600, nil) db2, err := bolt.Open(dest, 0600, nil)
assert.NoError(t, err) assert.NoError(t, err)
defer db2.Close() defer db2.Close()
db2.View(func(tx *Tx) error { db2.View(func(tx *bolt.Tx) error {
assert.Equal(t, []byte("bar"), tx.Bucket([]byte("widgets")).Get([]byte("foo"))) assert.Equal(t, []byte("bar"), tx.Bucket([]byte("widgets")).Get([]byte("foo")))
assert.Equal(t, []byte("bat"), tx.Bucket([]byte("widgets")).Get([]byte("baz"))) assert.Equal(t, []byte("bat"), tx.Bucket([]byte("widgets")).Get([]byte("baz")))
return nil return nil
@ -340,14 +331,14 @@ func (f *failWriter) Write(p []byte) (n int, err error) {
func TestTx_CopyFile_Error_Meta(t *testing.T) { func TestTx_CopyFile_Error_Meta(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))
tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte("bat")) tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte("bat"))
return nil return nil
}) })
err := db.View(func(tx *Tx) error { return tx.Copy(&failWriter{}) }) err := db.View(func(tx *bolt.Tx) error { return tx.Copy(&failWriter{}) })
assert.EqualError(t, err, "meta copy: error injected for tests") assert.EqualError(t, err, "meta copy: error injected for tests")
} }
@ -355,31 +346,31 @@ func TestTx_CopyFile_Error_Meta(t *testing.T) {
func TestTx_CopyFile_Error_Normal(t *testing.T) { func TestTx_CopyFile_Error_Normal(t *testing.T) {
db := NewTestDB() db := NewTestDB()
defer db.Close() defer db.Close()
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))
tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte("bat")) tx.Bucket([]byte("widgets")).Put([]byte("baz"), []byte("bat"))
return nil return nil
}) })
err := db.View(func(tx *Tx) error { return tx.Copy(&failWriter{3 * db.pageSize}) }) err := db.View(func(tx *bolt.Tx) error { return tx.Copy(&failWriter{3 * db.Info().PageSize}) })
assert.EqualError(t, err, "error injected for tests") assert.EqualError(t, err, "error injected for tests")
} }
func ExampleTx_Rollback() { func ExampleTx_Rollback() {
// Open the database. // Open the database.
db, _ := Open(tempfile(), 0666, nil) db, _ := bolt.Open(tempfile(), 0666, nil)
defer os.Remove(db.Path()) defer os.Remove(db.Path())
defer db.Close() defer db.Close()
// Create a bucket. // Create a bucket.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err return err
}) })
// Set a value for a key. // Set a value for a key.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
return tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) return tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))
}) })
@ -390,7 +381,7 @@ func ExampleTx_Rollback() {
tx.Rollback() tx.Rollback()
// Ensure that our original value is still set. // Ensure that our original value is still set.
db.View(func(tx *Tx) error { db.View(func(tx *bolt.Tx) error {
value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
fmt.Printf("The value for 'foo' is still: %s\n", value) fmt.Printf("The value for 'foo' is still: %s\n", value)
return nil return nil
@ -402,12 +393,12 @@ func ExampleTx_Rollback() {
func ExampleTx_CopyFile() { func ExampleTx_CopyFile() {
// Open the database. // Open the database.
db, _ := Open(tempfile(), 0666, nil) db, _ := bolt.Open(tempfile(), 0666, nil)
defer os.Remove(db.Path()) defer os.Remove(db.Path())
defer db.Close() defer db.Close()
// Create a bucket and a key. // Create a bucket and a key.
db.Update(func(tx *Tx) error { db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))
return nil return nil
@ -415,15 +406,15 @@ func ExampleTx_CopyFile() {
// Copy the database to another file. // Copy the database to another file.
toFile := tempfile() toFile := tempfile()
db.View(func(tx *Tx) error { return tx.CopyFile(toFile, 0666) }) db.View(func(tx *bolt.Tx) error { return tx.CopyFile(toFile, 0666) })
defer os.Remove(toFile) defer os.Remove(toFile)
// Open the cloned database. // Open the cloned database.
db2, _ := Open(toFile, 0666, nil) db2, _ := bolt.Open(toFile, 0666, nil)
defer db2.Close() defer db2.Close()
// Ensure that the key exists in the copy. // Ensure that the key exists in the copy.
db2.View(func(tx *Tx) error { db2.View(func(tx *bolt.Tx) error {
value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
fmt.Printf("The value for 'foo' in the clone is: %s\n", value) fmt.Printf("The value for 'foo' in the clone is: %s\n", value)
return nil return nil