mirror of
https://github.com/etcd-io/bbolt.git
synced 2025-05-31 11:42:30 +00:00
Merge pull request #279 from benbjohnson/remove-import-export
Remove 'import' and 'export' CLI commands.
This commit is contained in:
commit
ee986d7220
@ -1,91 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/boltdb/bolt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Export exports the entire database as a JSON document.
|
|
||||||
func Export(path string) {
|
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
||||||
fatal(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open the database.
|
|
||||||
db, err := bolt.Open(path, 0600, nil)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
err = db.View(func(tx *bolt.Tx) error {
|
|
||||||
// Loop over every bucket and export it as a raw message.
|
|
||||||
var root []*rawMessage
|
|
||||||
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 {
|
|
||||||
return fmt.Errorf("encode: %s", err)
|
|
||||||
}
|
|
||||||
print(string(output))
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportBucket(b *bolt.Bucket) (*rawMessage, error) {
|
|
||||||
// Encode individual key/value pairs into raw messages.
|
|
||||||
var children = make([]*rawMessage, 0)
|
|
||||||
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
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode bucket into a raw message.
|
|
||||||
var root = rawMessage{Type: "bucket"}
|
|
||||||
if root.Value, err = json.Marshal(children); err != nil {
|
|
||||||
return nil, fmt.Errorf("children: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &root, nil
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
package main_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/boltdb/bolt"
|
|
||||||
. "github.com/boltdb/bolt/cmd/bolt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Ensure that a database can be exported.
|
|
||||||
func TestExport(t *testing.T) {
|
|
||||||
SetTestMode(true)
|
|
||||||
open(func(db *bolt.DB, path string) {
|
|
||||||
db.Update(func(tx *bolt.Tx) error {
|
|
||||||
tx.CreateBucket([]byte("widgets"))
|
|
||||||
b := tx.Bucket([]byte("widgets"))
|
|
||||||
b.Put([]byte("foo"), []byte("0000"))
|
|
||||||
b.Put([]byte("bar"), []byte(""))
|
|
||||||
|
|
||||||
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)
|
|
||||||
equals(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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that an error is reported if the database is not found.
|
|
||||||
func TestExport_NotFound(t *testing.T) {
|
|
||||||
SetTestMode(true)
|
|
||||||
output := run("export", "no/such/db")
|
|
||||||
equals(t, "stat no/such/db: no such file or directory", output)
|
|
||||||
}
|
|
@ -1,108 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/boltdb/bolt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Import converts an exported database dump into a new database.
|
|
||||||
func Import(path string, input string) {
|
|
||||||
f, err := os.Open(input)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
// Read in entire dump.
|
|
||||||
var root []*rawMessage
|
|
||||||
if err := json.NewDecoder(f).Decode(&root); err != nil {
|
|
||||||
fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Import all of the buckets.
|
|
||||||
importBuckets(path, root)
|
|
||||||
}
|
|
||||||
|
|
||||||
func importBuckets(path string, root []*rawMessage) {
|
|
||||||
// Open the database.
|
|
||||||
db, err := bolt.Open(path, 0600, nil)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
// Insert entire dump into database.
|
|
||||||
err = db.Update(func(tx *bolt.Tx) error {
|
|
||||||
// Loop over every message and create a bucket.
|
|
||||||
for _, message := range root {
|
|
||||||
// Validate that root messages are buckets.
|
|
||||||
if message.Type != "bucket" {
|
|
||||||
return fmt.Errorf("invalid root type: %q", message.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the bucket if it doesn't exist.
|
|
||||||
b, err := tx.CreateBucketIfNotExists(message.Key)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("create bucket: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode child messages.
|
|
||||||
var children []*rawMessage
|
|
||||||
if err := json.Unmarshal(message.Value, &children); err != nil {
|
|
||||||
return fmt.Errorf("decode children: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Import all the values into the bucket.
|
|
||||||
if err := importBucket(b, children); err != nil {
|
|
||||||
return fmt.Errorf("import bucket: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
fatal("update: ", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func importBucket(b *bolt.Bucket, children []*rawMessage) error {
|
|
||||||
// Decode each message into a key/value pair.
|
|
||||||
for _, child := range children {
|
|
||||||
// Bucket messages are handled recursively.
|
|
||||||
if child.Type == "bucket" {
|
|
||||||
// Create the bucket if it doesn't exist.
|
|
||||||
subbucket, err := b.CreateBucketIfNotExists(child.Key)
|
|
||||||
if 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.
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert key/value into bucket.
|
|
||||||
if err := b.Put(child.Key, value); err != nil {
|
|
||||||
return fmt.Errorf("put: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
package main_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/boltdb/bolt"
|
|
||||||
. "github.com/boltdb/bolt/cmd/bolt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Ensure that a database can be imported.
|
|
||||||
func TestImport(t *testing.T) {
|
|
||||||
SetTestMode(true)
|
|
||||||
|
|
||||||
// Write input file.
|
|
||||||
input := tempfile()
|
|
||||||
ok(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()
|
|
||||||
output := run("import", path, "--input", input)
|
|
||||||
equals(t, ``, output)
|
|
||||||
|
|
||||||
// Open database and verify contents.
|
|
||||||
db, err := bolt.Open(path, 0600, nil)
|
|
||||||
ok(t, err)
|
|
||||||
db.View(func(tx *bolt.Tx) error {
|
|
||||||
assert(t, tx.Bucket([]byte("empty")) != nil, "")
|
|
||||||
|
|
||||||
b := tx.Bucket([]byte("widgets"))
|
|
||||||
assert(t, b != nil, "")
|
|
||||||
equals(t, []byte("0000"), b.Get([]byte("foo")))
|
|
||||||
equals(t, []byte(""), b.Get([]byte("bar")))
|
|
||||||
|
|
||||||
b = tx.Bucket([]byte("woojits"))
|
|
||||||
assert(t, b != nil, "")
|
|
||||||
equals(t, []byte("XXXX"), b.Get([]byte("baz")))
|
|
||||||
|
|
||||||
b = b.Bucket([]byte("woojits/subbucket"))
|
|
||||||
equals(t, []byte("A"), b.Get([]byte("bat")))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
db.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that an error is reported if the database is not found.
|
|
||||||
func TestImport_NotFound(t *testing.T) {
|
|
||||||
SetTestMode(true)
|
|
||||||
output := run("import", "path/to/db", "--input", "no/such/file")
|
|
||||||
equals(t, "open no/such/file: no such file or directory", output)
|
|
||||||
}
|
|
@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
@ -10,13 +9,11 @@ import (
|
|||||||
|
|
||||||
"github.com/boltdb/bolt"
|
"github.com/boltdb/bolt"
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
// "github.com/davecheney/profile"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var branch, commit string
|
var branch, commit string
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// defer profile.Start(&profile.Config{CPUProfile: true, MemProfile: true}).Stop()
|
|
||||||
log.SetFlags(0)
|
log.SetFlags(0)
|
||||||
NewApp().Run(os.Args)
|
NewApp().Run(os.Args)
|
||||||
}
|
}
|
||||||
@ -60,24 +57,6 @@ func NewApp() *cli.App {
|
|||||||
Buckets(path)
|
Buckets(path)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Name: "import",
|
|
||||||
Usage: "Imports from a JSON dump into a database",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
&cli.StringFlag{Name: "input"},
|
|
||||||
},
|
|
||||||
Action: func(c *cli.Context) {
|
|
||||||
Import(c.Args().Get(0), c.String("input"))
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "export",
|
|
||||||
Usage: "Exports a database to JSON",
|
|
||||||
Action: func(c *cli.Context) {
|
|
||||||
path := c.Args().Get(0)
|
|
||||||
Export(path)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Name: "pages",
|
Name: "pages",
|
||||||
Usage: "Dumps page information for a database",
|
Usage: "Dumps page information for a database",
|
||||||
@ -217,10 +196,3 @@ func SetTestMode(value bool) {
|
|||||||
logger = log.New(os.Stderr, "", 0)
|
logger = log.New(os.Stderr, "", 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// rawMessage represents a JSON element in the import/export document.
|
|
||||||
type rawMessage struct {
|
|
||||||
Type string `json:"type,omitempty"`
|
|
||||||
Key []byte `json:"key"`
|
|
||||||
Value json.RawMessage `json:"value"`
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user