mirror of
https://github.com/etcd-io/bbolt.git
synced 2025-05-31 11:42:30 +00:00
Upgrade import/export to use nested buckets.
This commit is contained in:
parent
714436100a
commit
10fed5f74d
@ -13,9 +13,9 @@ func TestBuckets(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 {
|
||||||
tx.CreateBucket("woojits")
|
tx.CreateBucket([]byte("woojits"))
|
||||||
tx.CreateBucket("widgets")
|
tx.CreateBucket([]byte("widgets"))
|
||||||
tx.CreateBucket("whatchits")
|
tx.CreateBucket([]byte("whatchits"))
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
db.Close()
|
db.Close()
|
||||||
|
@ -23,25 +23,33 @@ func Export(path string) {
|
|||||||
}
|
}
|
||||||
defer db.Close()
|
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.
|
// Loop over every bucket and export it as a raw message.
|
||||||
var root []*rawMessage
|
var root []*rawMessage
|
||||||
for _, b := range tx.Buckets() {
|
err := tx.ForEach(func(name []byte, b *bolt.Bucket) error {
|
||||||
message, err := exportBucket(b)
|
message, err := exportBucket(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal(err)
|
fatal(err)
|
||||||
}
|
}
|
||||||
|
message.Key = name
|
||||||
root = append(root, message)
|
root = append(root, message)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode all buckets into JSON.
|
// Encode all buckets into JSON.
|
||||||
output, err := json.Marshal(root)
|
output, err := json.Marshal(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal("encode: ", err)
|
return fmt.Errorf("encode: ", err)
|
||||||
}
|
}
|
||||||
print(string(output))
|
print(string(output))
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func exportBucket(b *bolt.Bucket) (*rawMessage, error) {
|
func exportBucket(b *bolt.Bucket) (*rawMessage, error) {
|
||||||
@ -50,11 +58,22 @@ func exportBucket(b *bolt.Bucket) (*rawMessage, error) {
|
|||||||
err := b.ForEach(func(k, v []byte) error {
|
err := b.ForEach(func(k, v []byte) error {
|
||||||
var err 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}
|
var child = &rawMessage{Key: k}
|
||||||
if child.Value, err = json.Marshal(v); err != nil {
|
if child.Value, err = json.Marshal(v); err != nil {
|
||||||
return fmt.Errorf("value: %s", err)
|
return fmt.Errorf("value: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
children = append(children, child)
|
children = append(children, child)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -64,7 +83,6 @@ func exportBucket(b *bolt.Bucket) (*rawMessage, error) {
|
|||||||
|
|
||||||
// Encode bucket into a raw message.
|
// Encode bucket into a raw message.
|
||||||
var root = rawMessage{Type: "bucket"}
|
var root = rawMessage{Type: "bucket"}
|
||||||
root.Key = []byte(b.Name())
|
|
||||||
if root.Value, err = json.Marshal(children); err != nil {
|
if root.Value, err = json.Marshal(children); err != nil {
|
||||||
return nil, fmt.Errorf("children: %s", err)
|
return nil, fmt.Errorf("children: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -13,19 +13,26 @@ func TestExport(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 {
|
||||||
tx.CreateBucket("widgets")
|
tx.CreateBucket([]byte("widgets"))
|
||||||
b := tx.Bucket("widgets")
|
b := tx.Bucket([]byte("widgets"))
|
||||||
b.Put([]byte("foo"), []byte("0000"))
|
b.Put([]byte("foo"), []byte("0000"))
|
||||||
b.Put([]byte("bar"), []byte(""))
|
b.Put([]byte("bar"), []byte(""))
|
||||||
|
|
||||||
tx.CreateBucket("woojits")
|
tx.CreateBucket([]byte("woojits"))
|
||||||
b = tx.Bucket("woojits")
|
b = tx.Bucket([]byte("woojits"))
|
||||||
b.Put([]byte("baz"), []byte("XXXX"))
|
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
|
return nil
|
||||||
})
|
})
|
||||||
db.Close()
|
db.Close()
|
||||||
output := run("export", path)
|
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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@ func TestGet(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 {
|
||||||
tx.CreateBucket("widgets")
|
tx.CreateBucket([]byte("widgets"))
|
||||||
tx.Bucket("widgets").Put([]byte("foo"), []byte("bar"))
|
tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar"))
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
db.Close()
|
db.Close()
|
||||||
@ -45,7 +45,7 @@ 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("widgets")
|
return tx.CreateBucket([]byte("widgets"))
|
||||||
})
|
})
|
||||||
db.Close()
|
db.Close()
|
||||||
output := run("get", path, "widgets", "foo")
|
output := run("get", path, "widgets", "foo")
|
||||||
|
@ -41,7 +41,7 @@ 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(string(message.Key)); err != nil {
|
if err := tx.CreateBucketIfNotExists(message.Key); err != nil {
|
||||||
return fmt.Errorf("create bucket: %s", err)
|
return fmt.Errorf("create bucket: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ func Import(path string, input string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Import all the values into the bucket.
|
// 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 {
|
if err := importBucket(b, children); err != nil {
|
||||||
return fmt.Errorf("import bucket: %s", err)
|
return fmt.Errorf("import bucket: %s", err)
|
||||||
}
|
}
|
||||||
@ -67,7 +67,28 @@ func Import(path string, input string) {
|
|||||||
func importBucket(b *bolt.Bucket, children []*rawMessage) error {
|
func importBucket(b *bolt.Bucket, children []*rawMessage) error {
|
||||||
// Decode each message into a key/value pair.
|
// Decode each message into a key/value pair.
|
||||||
for _, child := range children {
|
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
|
var value []byte
|
||||||
if err := json.Unmarshal(child.Value, &value); err != nil {
|
if err := json.Unmarshal(child.Value, &value); err != nil {
|
||||||
return fmt.Errorf("decode value: %s", err)
|
return fmt.Errorf("decode value: %s", err)
|
||||||
|
@ -15,7 +15,7 @@ func TestImport(t *testing.T) {
|
|||||||
|
|
||||||
// Write input file.
|
// Write input file.
|
||||||
input := tempfile()
|
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.
|
// Import database.
|
||||||
path := tempfile()
|
path := tempfile()
|
||||||
@ -26,15 +26,20 @@ func TestImport(t *testing.T) {
|
|||||||
db, err := bolt.Open(path, 0600)
|
db, err := bolt.Open(path, 0600)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
db.View(func(tx *bolt.Tx) error {
|
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) {
|
if assert.NotNil(t, b) {
|
||||||
assert.Equal(t, []byte("0000"), b.Get([]byte("foo")))
|
assert.Equal(t, []byte("0000"), b.Get([]byte("foo")))
|
||||||
assert.Equal(t, []byte(""), b.Get([]byte("bar")))
|
assert.Equal(t, []byte(""), b.Get([]byte("bar")))
|
||||||
}
|
}
|
||||||
|
|
||||||
b = tx.Bucket("woojits")
|
b = tx.Bucket([]byte("woojits"))
|
||||||
if assert.NotNil(t, b) {
|
if assert.NotNil(t, b) {
|
||||||
assert.Equal(t, []byte("XXXX"), b.Get([]byte("baz")))
|
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
|
return nil
|
||||||
|
@ -13,10 +13,10 @@ func TestKeys(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 {
|
||||||
tx.CreateBucket("widgets")
|
tx.CreateBucket([]byte("widgets"))
|
||||||
tx.Bucket("widgets").Put([]byte("0002"), []byte(""))
|
tx.Bucket([]byte("widgets")).Put([]byte("0002"), []byte(""))
|
||||||
tx.Bucket("widgets").Put([]byte("0001"), []byte(""))
|
tx.Bucket([]byte("widgets")).Put([]byte("0001"), []byte(""))
|
||||||
tx.Bucket("widgets").Put([]byte("0003"), []byte(""))
|
tx.Bucket([]byte("widgets")).Put([]byte("0003"), []byte(""))
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
db.Close()
|
db.Close()
|
||||||
|
@ -13,7 +13,7 @@ func TestSet(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 {
|
||||||
tx.CreateBucket("widgets")
|
tx.CreateBucket([]byte("widgets"))
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
db.Close()
|
db.Close()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user