Return bucket from CreateBucket() functions.

This commit changes the API for:

    Tx.CreateBucket()
    Tx.CreateBucketIfNotExists()
    Bucket.CreateBucket()
    Bucket.CreateBucketIfNotExists()

These functions now return the *Bucket and error instead of just the error.
pull/34/head
Ben Johnson 2014-04-15 23:45:06 -04:00
parent 02cd971daa
commit 2505b9a7dc
9 changed files with 150 additions and 87 deletions

View File

@ -154,18 +154,16 @@ 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 *Tx, qdb *QuickDB) {
var err error
keys, value := randKeys(), randValue() keys, value := randKeys(), randValue()
// Retrieve root bucket. // Retrieve root bucket.
b := tx.Bucket(keys[0]) b := tx.Bucket(keys[0])
if b == nil { if b == nil {
if err := tx.CreateBucket(keys[0]); err != nil { b, err = tx.CreateBucket(keys[0])
if err != nil {
panic("create bucket: " + err.Error()) panic("create bucket: " + err.Error())
} }
b = tx.Bucket(keys[0])
if b == nil {
panic(fmt.Sprintf("bucket[0] nil: %v", keys[0]))
}
} }
// Create nested buckets, if necessary. // Create nested buckets, if necessary.
@ -174,10 +172,10 @@ func simulatePutHandler(tx *Tx, qdb *QuickDB) {
if child != nil { if child != nil {
b = child b = child
} else { } else {
if err := b.CreateBucket(key); err != nil { b, err = b.CreateBucket(key)
if err != nil {
panic("create bucket: " + err.Error()) panic("create bucket: " + err.Error())
} }
b = b.Bucket(key)
} }
} }

View File

@ -107,15 +107,15 @@ func (b *Bucket) Bucket(name []byte) *Bucket {
return &child return &child
} }
// CreateBucket creates a new bucket at the given key. // CreateBucket creates a new bucket at the given key and returns the new bucket.
// Returns an error if the key already exists, if the bucket name is blank, or if the bucket name is too long. // Returns an error if the key already exists, if the bucket name is blank, or if the bucket name is too long.
func (b *Bucket) CreateBucket(key []byte) error { func (b *Bucket) CreateBucket(key []byte) (*Bucket, error) {
if b.tx.db == nil { if b.tx.db == nil {
return ErrTxClosed return nil, ErrTxClosed
} else if !b.tx.writable { } else if !b.tx.writable {
return ErrTxNotWritable return nil, ErrTxNotWritable
} else if len(key) == 0 { } else if len(key) == 0 {
return ErrBucketNameRequired return nil, ErrBucketNameRequired
} }
// Move cursor to correct position. // Move cursor to correct position.
@ -125,16 +125,16 @@ func (b *Bucket) CreateBucket(key []byte) error {
// Return an error if there is an existing key. // Return an error if there is an existing key.
if bytes.Equal(key, k) { if bytes.Equal(key, k) {
if (flags & bucketLeafFlag) != 0 { if (flags & bucketLeafFlag) != 0 {
return ErrBucketExists return nil, ErrBucketExists
} else { } else {
return ErrIncompatibleValue return nil, ErrIncompatibleValue
} }
} }
// Create a blank root leaf page. // Create a blank root leaf page.
p, err := b.tx.allocate(1) p, err := b.tx.allocate(1)
if err != nil { if err != nil {
return err return nil, err
} }
p.flags = leafPageFlag p.flags = leafPageFlag
@ -146,17 +146,19 @@ func (b *Bucket) CreateBucket(key []byte) error {
// Insert into node. // Insert into node.
c.node().put(key, key, value, 0, bucketLeafFlag) c.node().put(key, key, value, 0, bucketLeafFlag)
return nil return b.Bucket(key), nil
} }
// CreateBucketIfNotExists creates a new bucket if it doesn't already exist. // CreateBucketIfNotExists creates a new bucket if it doesn't already exist and returns a reference to it.
// Returns an error if the bucket name is blank, or if the bucket name is too long. // Returns an error if the bucket name is blank, or if the bucket name is too long.
func (b *Bucket) CreateBucketIfNotExists(key []byte) error { func (b *Bucket) CreateBucketIfNotExists(key []byte) (*Bucket, error) {
err := b.CreateBucket(key) child, err := b.CreateBucket(key)
if err != nil && err != ErrBucketExists { if err == ErrBucketExists {
return err return b.Bucket(key), nil
} else if err != nil {
return nil, err
} }
return nil return child, nil
} }
// DeleteBucket deletes a bucket at the given key. // DeleteBucket deletes a bucket at the given key.

View File

@ -44,7 +44,8 @@ func TestBucket_Get_IncompatibleValue(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
assert.NoError(t, tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo"))) _, err := tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo"))
assert.NoError(t, err)
assert.Nil(t, tx.Bucket([]byte("widgets")).Get([]byte("foo"))) assert.Nil(t, tx.Bucket([]byte("widgets")).Get([]byte("foo")))
return nil return nil
}) })
@ -109,7 +110,8 @@ func TestBucket_Put_IncompatibleValue(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
assert.NoError(t, tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo"))) _, err := tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo"))
assert.NoError(t, err)
assert.Equal(t, ErrIncompatibleValue, tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))) assert.Equal(t, ErrIncompatibleValue, tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")))
return nil return nil
}) })
@ -131,7 +133,8 @@ func TestBucket_Put_Closed(t *testing.T) {
func TestBucket_Put_ReadOnly(t *testing.T) { func TestBucket_Put_ReadOnly(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
assert.NoError(t, tx.CreateBucket([]byte("widgets"))) _, err := tx.CreateBucket([]byte("widgets"))
assert.NoError(t, err)
return nil return nil
}) })
db.View(func(tx *Tx) error { db.View(func(tx *Tx) error {
@ -164,7 +167,8 @@ func TestBucket_Delete_Bucket(t *testing.T) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
assert.NoError(t, b.CreateBucket([]byte("foo"))) _, err := b.CreateBucket([]byte("foo"))
assert.NoError(t, err)
assert.Equal(t, ErrIncompatibleValue, b.Delete([]byte("foo"))) assert.Equal(t, ErrIncompatibleValue, b.Delete([]byte("foo")))
return nil return nil
}) })
@ -203,8 +207,10 @@ func TestBucket_DeleteBucket_Nested(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
assert.NoError(t, tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo"))) _, err := tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo"))
assert.NoError(t, tx.Bucket([]byte("widgets")).Bucket([]byte("foo")).CreateBucket([]byte("bar"))) assert.NoError(t, err)
_, err = tx.Bucket([]byte("widgets")).Bucket([]byte("foo")).CreateBucket([]byte("bar"))
assert.NoError(t, err)
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")))
assert.NoError(t, tx.Bucket([]byte("widgets")).DeleteBucket([]byte("foo"))) assert.NoError(t, tx.Bucket([]byte("widgets")).DeleteBucket([]byte("foo")))
return nil return nil
@ -217,8 +223,10 @@ func TestBucket_DeleteBucket_Nested2(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
tx.CreateBucket([]byte("widgets")) tx.CreateBucket([]byte("widgets"))
assert.NoError(t, tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo"))) _, err := tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo"))
assert.NoError(t, tx.Bucket([]byte("widgets")).Bucket([]byte("foo")).CreateBucket([]byte("bar"))) assert.NoError(t, err)
_, err = tx.Bucket([]byte("widgets")).Bucket([]byte("foo")).CreateBucket([]byte("bar"))
assert.NoError(t, err)
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
}) })
@ -241,8 +249,10 @@ func TestBucket_DeleteBucket_Nested2(t *testing.T) {
func TestBucket_DeleteBucket_Large(t *testing.T) { func TestBucket_DeleteBucket_Large(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
assert.NoError(t, tx.CreateBucket([]byte("widgets"))) _, err := tx.CreateBucket([]byte("widgets"))
assert.NoError(t, tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo"))) assert.NoError(t, err)
_, err = tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo"))
assert.NoError(t, err)
b := tx.Bucket([]byte("widgets")).Bucket([]byte("foo")) b := tx.Bucket([]byte("widgets")).Bucket([]byte("foo"))
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
assert.NoError(t, b.Put([]byte(fmt.Sprintf("%d", i)), []byte(fmt.Sprintf("%0100d", i)))) assert.NoError(t, b.Put([]byte(fmt.Sprintf("%d", i)), []byte(fmt.Sprintf("%0100d", i))))
@ -274,9 +284,11 @@ func TestBucket_Bucket_IncompatibleValue(t *testing.T) {
func TestBucket_CreateBucket_IncompatibleValue(t *testing.T) { func TestBucket_CreateBucket_IncompatibleValue(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
assert.NoError(t, tx.CreateBucket([]byte("widgets"))) _, err := tx.CreateBucket([]byte("widgets"))
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")).CreateBucket([]byte("foo"))) _, err = tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo"))
assert.Equal(t, ErrIncompatibleValue, err)
return nil return nil
}) })
}) })
@ -286,7 +298,8 @@ func TestBucket_CreateBucket_IncompatibleValue(t *testing.T) {
func TestBucket_DeleteBucket_IncompatibleValue(t *testing.T) { func TestBucket_DeleteBucket_IncompatibleValue(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
assert.NoError(t, tx.CreateBucket([]byte("widgets"))) _, err := tx.CreateBucket([]byte("widgets"))
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, ErrIncompatibleValue, tx.Bucket([]byte("widgets")).DeleteBucket([]byte("foo")))
return nil return nil
@ -464,7 +477,8 @@ func TestBucket_Stat(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
// Add bucket with fewer keys but one big value. // Add bucket with fewer keys but one big value.
assert.NoError(t, tx.CreateBucket([]byte("woojits"))) _, err := tx.CreateBucket([]byte("woojits"))
assert.NoError(t, err)
b := tx.Bucket([]byte("woojits")) b := tx.Bucket([]byte("woojits"))
for i := 0; i < 500; i++ { for i := 0; i < 500; i++ {
b.Put([]byte(strconv.Itoa(i)), []byte(strconv.Itoa(i))) b.Put([]byte(strconv.Itoa(i)), []byte(strconv.Itoa(i)))
@ -472,8 +486,8 @@ func TestBucket_Stat(t *testing.T) {
b.Put([]byte("really-big-value"), []byte(strings.Repeat("*", 10000))) b.Put([]byte("really-big-value"), []byte(strings.Repeat("*", 10000)))
// Add a bucket that fits on a single root leaf. // Add a bucket that fits on a single root leaf.
assert.NoError(t, tx.CreateBucket([]byte("whozawhats"))) b, err = tx.CreateBucket([]byte("whozawhats"))
b = tx.Bucket([]byte("whozawhats")) assert.NoError(t, err)
b.Put([]byte("foo"), []byte("bar")) b.Put([]byte("foo"), []byte("bar"))
return nil return nil
@ -543,7 +557,8 @@ 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 *Tx) error {
return tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err
}) })
for _, item := range items { for _, item := range items {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
@ -588,7 +603,8 @@ func TestBucket_Put_Multiple(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
// Bulk insert all values. // Bulk insert all values.
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
return tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err
}) })
err := db.Update(func(tx *Tx) error { err := db.Update(func(tx *Tx) error {
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))
@ -628,7 +644,8 @@ func TestBucket_Delete_Quick(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
// Bulk insert all values. // Bulk insert all values.
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
return tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err
}) })
err := db.Update(func(tx *Tx) error { err := db.Update(func(tx *Tx) error {
b := tx.Bucket([]byte("widgets")) b := tx.Bucket([]byte("widgets"))

View File

@ -45,7 +45,8 @@ func TestGetKeyNotFound(t *testing.T) {
SetTestMode(true) SetTestMode(true)
open(func(db *bolt.DB, path string) { open(func(db *bolt.DB, path string) {
db.Update(func(tx *bolt.Tx) error { db.Update(func(tx *bolt.Tx) error {
return tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err
}) })
db.Close() db.Close()
output := run("get", path, "widgets", "foo") output := run("get", path, "widgets", "foo")

View File

@ -41,7 +41,8 @@ func Import(path string, input string) {
} }
// Create the bucket if it doesn't exist. // Create the bucket if it doesn't exist.
if err := tx.CreateBucketIfNotExists(message.Key); err != nil { b, err := tx.CreateBucketIfNotExists(message.Key)
if err != nil {
return fmt.Errorf("create bucket: %s", err) return fmt.Errorf("create bucket: %s", err)
} }
@ -52,7 +53,6 @@ func Import(path string, input string) {
} }
// Import all the values into the bucket. // Import all the values into the bucket.
b := tx.Bucket(message.Key)
if err := importBucket(b, children); err != nil { if err := importBucket(b, children); err != nil {
return fmt.Errorf("import bucket: %s", err) return fmt.Errorf("import bucket: %s", err)
} }
@ -70,7 +70,8 @@ func importBucket(b *bolt.Bucket, children []*rawMessage) error {
// Bucket messages are handled recursively. // Bucket messages are handled recursively.
if child.Type == "bucket" { if child.Type == "bucket" {
// Create the bucket if it doesn't exist. // Create the bucket if it doesn't exist.
if err := b.CreateBucketIfNotExists(child.Key); err != nil { subbucket, err := b.CreateBucketIfNotExists(child.Key)
if err != nil {
return fmt.Errorf("create bucket: %s", err) return fmt.Errorf("create bucket: %s", err)
} }
@ -81,7 +82,6 @@ func importBucket(b *bolt.Bucket, children []*rawMessage) error {
} }
// Import subbucket. // Import subbucket.
subbucket := b.Bucket(child.Key)
if err := importBucket(subbucket, subchildren); err != nil { if err := importBucket(subbucket, subchildren); err != nil {
return fmt.Errorf("import bucket: %s", err) return fmt.Errorf("import bucket: %s", err)
} }

View File

@ -12,12 +12,13 @@ import (
func TestCursor_Seek(t *testing.T) { func TestCursor_Seek(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
assert.NoError(t, tx.CreateBucket([]byte("widgets"))) b, err := tx.CreateBucket([]byte("widgets"))
b := tx.Bucket([]byte("widgets")) assert.NoError(t, err)
assert.NoError(t, b.Put([]byte("foo"), []byte("0001"))) assert.NoError(t, b.Put([]byte("foo"), []byte("0001")))
assert.NoError(t, b.Put([]byte("bar"), []byte("0002"))) assert.NoError(t, b.Put([]byte("bar"), []byte("0002")))
assert.NoError(t, b.Put([]byte("baz"), []byte("0003"))) assert.NoError(t, b.Put([]byte("baz"), []byte("0003")))
assert.NoError(t, b.CreateBucket([]byte("bkt"))) _, err = b.CreateBucket([]byte("bkt"))
assert.NoError(t, err)
return nil return nil
}) })
db.View(func(tx *Tx) error { db.View(func(tx *Tx) error {
@ -57,7 +58,8 @@ func TestCursor_Seek(t *testing.T) {
func TestCursor_EmptyBucket(t *testing.T) { func TestCursor_EmptyBucket(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
return tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err
}) })
db.View(func(tx *Tx) error { db.View(func(tx *Tx) error {
c := tx.Bucket([]byte("widgets")).Cursor() c := tx.Bucket([]byte("widgets")).Cursor()
@ -73,7 +75,8 @@ func TestCursor_EmptyBucket(t *testing.T) {
func TestCursor_EmptyBucketReverse(t *testing.T) { func TestCursor_EmptyBucketReverse(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
return tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err
}) })
db.View(func(tx *Tx) error { db.View(func(tx *Tx) error {
c := tx.Bucket([]byte("widgets")).Cursor() c := tx.Bucket([]byte("widgets")).Cursor()
@ -262,11 +265,14 @@ func TestCursor_Iterate_Reverse(t *testing.T) {
func TestCursor_Iterate_BucketsOnly(t *testing.T) { func TestCursor_Iterate_BucketsOnly(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
assert.NoError(t, tx.CreateBucket([]byte("widgets"))) b, err := tx.CreateBucket([]byte("widgets"))
b := tx.Bucket([]byte("widgets")) assert.NoError(t, err)
assert.NoError(t, b.CreateBucket([]byte("foo"))) _, err = b.CreateBucket([]byte("foo"))
assert.NoError(t, b.CreateBucket([]byte("bar"))) assert.NoError(t, err)
assert.NoError(t, b.CreateBucket([]byte("baz"))) _, err = b.CreateBucket([]byte("bar"))
assert.NoError(t, err)
_, err = b.CreateBucket([]byte("baz"))
assert.NoError(t, err)
return nil return nil
}) })
db.View(func(tx *Tx) error { db.View(func(tx *Tx) error {
@ -286,11 +292,14 @@ func TestCursor_Iterate_BucketsOnly(t *testing.T) {
func TestCursor_Iterate_BucketsOnly_Reverse(t *testing.T) { func TestCursor_Iterate_BucketsOnly_Reverse(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
assert.NoError(t, tx.CreateBucket([]byte("widgets"))) b, err := tx.CreateBucket([]byte("widgets"))
b := tx.Bucket([]byte("widgets")) assert.NoError(t, err)
assert.NoError(t, b.CreateBucket([]byte("foo"))) _, err = b.CreateBucket([]byte("foo"))
assert.NoError(t, b.CreateBucket([]byte("bar"))) assert.NoError(t, err)
assert.NoError(t, b.CreateBucket([]byte("baz"))) _, err = b.CreateBucket([]byte("bar"))
assert.NoError(t, err)
_, err = b.CreateBucket([]byte("baz"))
assert.NoError(t, err)
return nil return nil
}) })
db.View(func(tx *Tx) error { db.View(func(tx *Tx) error {

View File

@ -128,10 +128,12 @@ func TestDB_Open_MetaChecksumError(t *testing.T) {
db, err := Open(path, 0600) db, err := Open(path, 0600)
pageSize := db.pageSize pageSize := db.pageSize
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
return tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err
}) })
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
return tx.CreateBucket([]byte("woojits")) _, err := tx.CreateBucket([]byte("woojits"))
return err
}) })
db.Close() db.Close()
@ -272,7 +274,8 @@ func TestDB_Commit_WriteFail(t *testing.T) {
func TestDB_Stats(t *testing.T) { func TestDB_Stats(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
return tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err
}) })
stats := db.Stats() stats := db.Stats()
assert.Equal(t, 3, stats.TxStats.PageCount) assert.Equal(t, 3, stats.TxStats.PageCount)
@ -295,7 +298,8 @@ func TestDB_mmapSize(t *testing.T) {
func TestDB_Consistency(t *testing.T) { func TestDB_Consistency(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
return tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err
}) })
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
@ -357,7 +361,8 @@ func BenchmarkDB_Put_Sequential(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.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
return tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err
}) })
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
@ -373,7 +378,8 @@ func BenchmarkDB_Put_Random(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.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
return tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err
}) })
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
@ -391,10 +397,10 @@ func ExampleDB_Update() {
// 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 *Tx) error {
if err := tx.CreateBucket([]byte("widgets")); err != nil { b, err := tx.CreateBucket([]byte("widgets"))
if err != nil {
return err return err
} }
b := tx.Bucket([]byte("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
} }
@ -448,7 +454,8 @@ func ExampleDB_Begin_ReadOnly() {
// Create a bucket. // Create a bucket.
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
return tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err
}) })
// Create several keys in a transaction. // Create several keys in a transaction.

4
tx.go
View File

@ -88,13 +88,13 @@ func (tx *Tx) Bucket(name []byte) *Bucket {
// CreateBucket creates a new bucket. // CreateBucket creates a new bucket.
// Returns an error if the bucket already exists, if the bucket name is blank, or if the bucket name is too long. // Returns an error if the bucket already exists, if the bucket name is blank, or if the bucket name is too long.
func (tx *Tx) CreateBucket(name []byte) error { func (tx *Tx) CreateBucket(name []byte) (*Bucket, error) {
return tx.root.CreateBucket(name) return tx.root.CreateBucket(name)
} }
// CreateBucketIfNotExists creates a new bucket if it doesn't already exist. // CreateBucketIfNotExists creates a new bucket if it doesn't already exist.
// Returns an error if the bucket name is blank, or if the bucket name is too long. // Returns an error if the bucket name is blank, or if the bucket name is too long.
func (tx *Tx) CreateBucketIfNotExists(name []byte) error { func (tx *Tx) CreateBucketIfNotExists(name []byte) (*Bucket, error) {
return tx.root.CreateBucketIfNotExists(name) return tx.root.CreateBucketIfNotExists(name)
} }

View File

@ -43,7 +43,9 @@ func TestTx_Commit_ReadOnly(t *testing.T) {
func TestTx_CreateBucket_ReadOnly(t *testing.T) { func TestTx_CreateBucket_ReadOnly(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
db.View(func(tx *Tx) error { db.View(func(tx *Tx) error {
assert.Equal(t, tx.CreateBucket([]byte("foo")), ErrTxNotWritable) b, err := tx.CreateBucket([]byte("foo"))
assert.Nil(t, b)
assert.Equal(t, ErrTxNotWritable, err)
return nil return nil
}) })
}) })
@ -54,7 +56,9 @@ func TestTx_CreateBucket_Closed(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
tx, _ := db.Begin(true) tx, _ := db.Begin(true)
tx.Commit() tx.Commit()
assert.Equal(t, tx.CreateBucket([]byte("foo")), ErrTxClosed) b, err := tx.CreateBucket([]byte("foo"))
assert.Nil(t, b)
assert.Equal(t, ErrTxClosed, err)
}) })
} }
@ -88,7 +92,9 @@ func TestTx_CreateBucket(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
// Create a bucket. // Create a bucket.
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
assert.NoError(t, tx.CreateBucket([]byte("widgets"))) b, err := tx.CreateBucket([]byte("widgets"))
assert.NotNil(t, b)
assert.NoError(t, err)
return nil return nil
}) })
@ -105,10 +111,21 @@ func TestTx_CreateBucket(t *testing.T) {
func TestTx_CreateBucketIfNotExists(t *testing.T) { func TestTx_CreateBucketIfNotExists(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
assert.NoError(t, tx.CreateBucketIfNotExists([]byte("widgets"))) b, err := tx.CreateBucketIfNotExists([]byte("widgets"))
assert.NoError(t, tx.CreateBucketIfNotExists([]byte("widgets"))) assert.NotNil(t, b)
assert.Equal(t, ErrBucketNameRequired, tx.CreateBucketIfNotExists([]byte{})) assert.NoError(t, err)
assert.Equal(t, ErrBucketNameRequired, tx.CreateBucketIfNotExists(nil))
b, err = tx.CreateBucketIfNotExists([]byte("widgets"))
assert.NotNil(t, b)
assert.NoError(t, err)
b, err = tx.CreateBucketIfNotExists([]byte{})
assert.Nil(t, b)
assert.Equal(t, ErrBucketNameRequired, err)
b, err = tx.CreateBucketIfNotExists(nil)
assert.Nil(t, b)
assert.Equal(t, ErrBucketNameRequired, err)
return nil return nil
}) })
@ -126,13 +143,17 @@ func TestTx_CreateBucket_Exists(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
// Create a bucket. // Create a bucket.
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
assert.NoError(t, tx.CreateBucket([]byte("widgets"))) b, err := tx.CreateBucket([]byte("widgets"))
assert.NotNil(t, b)
assert.NoError(t, err)
return nil return nil
}) })
// Create the same bucket again. // Create the same bucket again.
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
assert.Equal(t, ErrBucketExists, tx.CreateBucket([]byte("widgets"))) b, err := tx.CreateBucket([]byte("widgets"))
assert.Nil(t, b)
assert.Equal(t, ErrBucketExists, err)
return nil return nil
}) })
}) })
@ -142,7 +163,9 @@ func TestTx_CreateBucket_Exists(t *testing.T) {
func TestTx_CreateBucket_NameRequired(t *testing.T) { func TestTx_CreateBucket_NameRequired(t *testing.T) {
withOpenDB(func(db *DB, path string) { withOpenDB(func(db *DB, path string) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
assert.Equal(t, ErrBucketNameRequired, tx.CreateBucket(nil)) b, err := tx.CreateBucket(nil)
assert.Nil(t, b)
assert.Equal(t, ErrBucketNameRequired, err)
return nil return nil
}) })
}) })
@ -177,7 +200,9 @@ func TestTx_DeleteBucket(t *testing.T) {
assert.Equal(t, []pgid{7, 6, root, 2}, db.freelist.all()) assert.Equal(t, []pgid{7, 6, root, 2}, 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, tx.CreateBucket([]byte("widgets"))) b, err := tx.CreateBucket([]byte("widgets"))
assert.NotNil(t, b)
assert.NoError(t, err)
assert.Nil(t, tx.Bucket([]byte("widgets")).Get([]byte("foo"))) assert.Nil(t, tx.Bucket([]byte("widgets")).Get([]byte("foo")))
return nil return nil
}) })
@ -220,7 +245,8 @@ func TestTx_OnCommit(t *testing.T) {
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
tx.OnCommit(func() { x += 1 }) tx.OnCommit(func() { x += 1 })
tx.OnCommit(func() { x += 2 }) tx.OnCommit(func() { x += 2 })
return tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err
}) })
}) })
assert.Equal(t, 3, x) assert.Equal(t, 3, x)
@ -292,7 +318,8 @@ func benchmarkTxPutRandom(b *testing.B, total int) {
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.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
return tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err
}) })
var tx *Tx var tx *Tx
var bucket *Bucket var bucket *Bucket
@ -323,7 +350,8 @@ func benchmarkTxPutSequential(b *testing.B, total int) {
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.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
return tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err
}) })
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
bucket := tx.Bucket([]byte("widgets")) bucket := tx.Bucket([]byte("widgets"))
@ -345,7 +373,8 @@ func ExampleTx_Rollback() {
// Create a bucket. // Create a bucket.
db.Update(func(tx *Tx) error { db.Update(func(tx *Tx) error {
return tx.CreateBucket([]byte("widgets")) _, err := tx.CreateBucket([]byte("widgets"))
return err
}) })
// Set a value for a key. // Set a value for a key.