Skip to content
This repository has been archived by the owner on Mar 9, 2019. It is now read-only.

Commit

Permalink
Upgrade import/export to use nested buckets.
Browse files Browse the repository at this point in the history
  • Loading branch information
benbjohnson committed Apr 11, 2014
1 parent 7144361 commit 10fed5f
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 27 deletions.
6 changes: 3 additions & 3 deletions cmd/bolt/buckets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ func TestBuckets(t *testing.T) {
SetTestMode(true)
open(func(db *bolt.DB, path string) {
db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket("woojits")
tx.CreateBucket("widgets")
tx.CreateBucket("whatchits")
tx.CreateBucket([]byte("woojits"))
tx.CreateBucket([]byte("widgets"))
tx.CreateBucket([]byte("whatchits"))
return nil
})
db.Close()
Expand Down
28 changes: 23 additions & 5 deletions cmd/bolt/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,33 @@ func Export(path string) {
}
defer db.Close()

db.View(func(tx *bolt.Tx) error {
err = db.View(func(tx *bolt.Tx) error {
// Loop over every bucket and export it as a raw message.
var root []*rawMessage
for _, b := range tx.Buckets() {
err := tx.ForEach(func(name []byte, b *bolt.Bucket) error {
message, err := exportBucket(b)
if err != nil {
fatal(err)
}
message.Key = name
root = append(root, message)
return nil
})
if err != nil {
return err
}

// Encode all buckets into JSON.
output, err := json.Marshal(root)
if err != nil {
fatal("encode: ", err)
return fmt.Errorf("encode: ", err)
}
print(string(output))
return nil
})
if err != nil {
fatal(err)
}
}

func exportBucket(b *bolt.Bucket) (*rawMessage, error) {
Expand All @@ -50,11 +58,22 @@ func exportBucket(b *bolt.Bucket) (*rawMessage, error) {
err := b.ForEach(func(k, v []byte) error {
var err error

// If there is no value then it is a bucket.
if v == nil {
child, err := exportBucket(b.Bucket(k))
if err != nil {
return fmt.Errorf("bucket: %s", err)
}
child.Key = k
children = append(children, child)
return nil
}

// Otherwise it's a regular key.
var child = &rawMessage{Key: k}
if child.Value, err = json.Marshal(v); err != nil {
return fmt.Errorf("value: %s", err)
}

children = append(children, child)
return nil
})
Expand All @@ -64,7 +83,6 @@ func exportBucket(b *bolt.Bucket) (*rawMessage, error) {

// Encode bucket into a raw message.
var root = rawMessage{Type: "bucket"}
root.Key = []byte(b.Name())
if root.Value, err = json.Marshal(children); err != nil {
return nil, fmt.Errorf("children: %s", err)
}
Expand Down
17 changes: 12 additions & 5 deletions cmd/bolt/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,26 @@ func TestExport(t *testing.T) {
SetTestMode(true)
open(func(db *bolt.DB, path string) {
db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket("widgets")
b := tx.Bucket("widgets")
tx.CreateBucket([]byte("widgets"))
b := tx.Bucket([]byte("widgets"))
b.Put([]byte("foo"), []byte("0000"))
b.Put([]byte("bar"), []byte(""))

tx.CreateBucket("woojits")
b = tx.Bucket("woojits")
tx.CreateBucket([]byte("woojits"))
b = tx.Bucket([]byte("woojits"))
b.Put([]byte("baz"), []byte("XXXX"))

b.CreateBucket([]byte("woojits/subbucket"))
b = b.Bucket([]byte("woojits/subbucket"))
b.Put([]byte("bat"), []byte("A"))

tx.CreateBucket([]byte("empty"))

return nil
})
db.Close()
output := run("export", path)
assert.Equal(t, `[{"type":"bucket","key":"d2lkZ2V0cw==","value":[{"key":"YmFy","value":""},{"key":"Zm9v","value":"MDAwMA=="}]},{"type":"bucket","key":"d29vaml0cw==","value":[{"key":"YmF6","value":"WFhYWA=="}]}]`, output)
assert.Equal(t, `[{"type":"bucket","key":"ZW1wdHk=","value":[]},{"type":"bucket","key":"d2lkZ2V0cw==","value":[{"key":"YmFy","value":""},{"key":"Zm9v","value":"MDAwMA=="}]},{"type":"bucket","key":"d29vaml0cw==","value":[{"key":"YmF6","value":"WFhYWA=="},{"type":"bucket","key":"d29vaml0cy9zdWJidWNrZXQ=","value":[{"key":"YmF0","value":"QQ=="}]}]}]`, output)
})
}

Expand Down
6 changes: 3 additions & 3 deletions cmd/bolt/get_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ func TestGet(t *testing.T) {
SetTestMode(true)
open(func(db *bolt.DB, path string) {
db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket("widgets")
tx.Bucket("widgets").Put([]byte("foo"), []byte("bar"))
tx.CreateBucket([]byte("widgets"))
tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))
return nil
})
db.Close()
Expand Down Expand Up @@ -45,7 +45,7 @@ func TestGetKeyNotFound(t *testing.T) {
SetTestMode(true)
open(func(db *bolt.DB, path string) {
db.Update(func(tx *bolt.Tx) error {
return tx.CreateBucket("widgets")
return tx.CreateBucket([]byte("widgets"))
})
db.Close()
output := run("get", path, "widgets", "foo")
Expand Down
27 changes: 24 additions & 3 deletions cmd/bolt/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func Import(path string, input string) {
}

// Create the bucket if it doesn't exist.
if err := tx.CreateBucketIfNotExists(string(message.Key)); err != nil {
if err := tx.CreateBucketIfNotExists(message.Key); err != nil {
return fmt.Errorf("create bucket: %s", err)
}

Expand All @@ -52,7 +52,7 @@ func Import(path string, input string) {
}

// Import all the values into the bucket.
b := tx.Bucket(string(message.Key))
b := tx.Bucket(message.Key)
if err := importBucket(b, children); err != nil {
return fmt.Errorf("import bucket: %s", err)
}
Expand All @@ -67,7 +67,28 @@ func Import(path string, input string) {
func importBucket(b *bolt.Bucket, children []*rawMessage) error {
// Decode each message into a key/value pair.
for _, child := range children {
// Decode the base64 value.
// Bucket messages are handled recursively.
if child.Type == "bucket" {
// Create the bucket if it doesn't exist.
if err := b.CreateBucketIfNotExists(child.Key); err != nil {
return fmt.Errorf("create bucket: %s", err)
}

// Decode child messages.
var subchildren []*rawMessage
if err := json.Unmarshal(child.Value, &subchildren); err != nil {
return fmt.Errorf("decode children: %s", err)
}

// Import subbucket.
subbucket := b.Bucket(child.Key)
if err := importBucket(subbucket, subchildren); err != nil {
return fmt.Errorf("import bucket: %s", err)
}
continue
}

// Non-bucket values are decoded from base64.
var value []byte
if err := json.Unmarshal(child.Value, &value); err != nil {
return fmt.Errorf("decode value: %s", err)
Expand Down
11 changes: 8 additions & 3 deletions cmd/bolt/import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func TestImport(t *testing.T) {

// Write input file.
input := tempfile()
assert.NoError(t, ioutil.WriteFile(input, []byte(`[{"type":"bucket","key":"d2lkZ2V0cw==","value":[{"key":"YmFy","value":""},{"key":"Zm9v","value":"MDAwMA=="}]},{"type":"bucket","key":"d29vaml0cw==","value":[{"key":"YmF6","value":"WFhYWA=="}]}]`), 0600))
assert.NoError(t, ioutil.WriteFile(input, []byte(`[{"type":"bucket","key":"ZW1wdHk=","value":[]},{"type":"bucket","key":"d2lkZ2V0cw==","value":[{"key":"YmFy","value":""},{"key":"Zm9v","value":"MDAwMA=="}]},{"type":"bucket","key":"d29vaml0cw==","value":[{"key":"YmF6","value":"WFhYWA=="},{"type":"bucket","key":"d29vaml0cy9zdWJidWNrZXQ=","value":[{"key":"YmF0","value":"QQ=="}]}]}]`), 0600))

// Import database.
path := tempfile()
Expand All @@ -26,15 +26,20 @@ func TestImport(t *testing.T) {
db, err := bolt.Open(path, 0600)
assert.NoError(t, err)
db.View(func(tx *bolt.Tx) error {
b := tx.Bucket("widgets")
assert.NotNil(t, tx.Bucket([]byte("empty")))

b := tx.Bucket([]byte("widgets"))
if assert.NotNil(t, b) {
assert.Equal(t, []byte("0000"), b.Get([]byte("foo")))
assert.Equal(t, []byte(""), b.Get([]byte("bar")))
}

b = tx.Bucket("woojits")
b = tx.Bucket([]byte("woojits"))
if assert.NotNil(t, b) {
assert.Equal(t, []byte("XXXX"), b.Get([]byte("baz")))

b = b.Bucket([]byte("woojits/subbucket"))
assert.Equal(t, []byte("A"), b.Get([]byte("bat")))
}

return nil
Expand Down
8 changes: 4 additions & 4 deletions cmd/bolt/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ func TestKeys(t *testing.T) {
SetTestMode(true)
open(func(db *bolt.DB, path string) {
db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket("widgets")
tx.Bucket("widgets").Put([]byte("0002"), []byte(""))
tx.Bucket("widgets").Put([]byte("0001"), []byte(""))
tx.Bucket("widgets").Put([]byte("0003"), []byte(""))
tx.CreateBucket([]byte("widgets"))
tx.Bucket([]byte("widgets")).Put([]byte("0002"), []byte(""))
tx.Bucket([]byte("widgets")).Put([]byte("0001"), []byte(""))
tx.Bucket([]byte("widgets")).Put([]byte("0003"), []byte(""))
return nil
})
db.Close()
Expand Down
2 changes: 1 addition & 1 deletion cmd/bolt/set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func TestSet(t *testing.T) {
SetTestMode(true)
open(func(db *bolt.DB, path string) {
db.Update(func(tx *bolt.Tx) error {
tx.CreateBucket("widgets")
tx.CreateBucket([]byte("widgets"))
return nil
})
db.Close()
Expand Down

0 comments on commit 10fed5f

Please sign in to comment.