Merge branch 'master' of https://github.com/boltdb/bolt into fix-deletion

Conflicts:
	node.go
master
Ben Johnson 2014-05-09 09:38:08 -06:00
commit d1b21e619d
7 changed files with 79 additions and 5 deletions

View File

@ -476,6 +476,7 @@ func (b *Bucket) spill() error {
b.rootNode = b.rootNode.root()
// Update the root node for this bucket.
_assert(b.rootNode.pgid < b.tx.meta.pgid, "pgid (%d) above high water mark (%d)", b.rootNode.pgid, b.tx.meta.pgid)
b.root = b.rootNode.pgid
return nil

26
cmd/bolt/info.go Normal file
View File

@ -0,0 +1,26 @@
package main
import (
"os"
"github.com/boltdb/bolt"
)
// Info prints basic information about a database.
func Info(path string) {
if _, err := os.Stat(path); os.IsNotExist(err) {
fatal(err)
return
}
db, err := bolt.Open(path, 0600)
if err != nil {
fatal(err)
return
}
defer db.Close()
// Print basic database info.
var info = db.Info()
printf("Page Size: %d", info.PageSize)
}

32
cmd/bolt/info_test.go Normal file
View File

@ -0,0 +1,32 @@
package main_test
import (
"testing"
"github.com/boltdb/bolt"
. "github.com/boltdb/bolt/cmd/bolt"
"github.com/stretchr/testify/assert"
)
// Ensure that a database info can be printed.
func TestInfo(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"))
return nil
})
db.Close()
output := run("info", path)
assert.Equal(t, `Page Size: 4096`, output)
})
}
// Ensure that an error is reported if the database is not found.
func TestInfo_NotFound(t *testing.T) {
SetTestMode(true)
output := run("info", "no/such/db")
assert.Equal(t, "stat no/such/db: no such file or directory", output)
}

View File

@ -25,6 +25,14 @@ func NewApp() *cli.App {
app.Usage = "BoltDB toolkit"
app.Version = fmt.Sprintf("0.1.0 (%s %s)", branch, commit)
app.Commands = []cli.Command{
{
Name: "info",
Usage: "Print basic information about a database",
Action: func(c *cli.Context) {
path := c.Args().Get(0)
Info(path)
},
},
{
Name: "get",
Usage: "Retrieve a value for given key in a bucket",

4
db.go
View File

@ -705,6 +705,10 @@ func (m *meta) copy(dest *meta) {
// write writes the meta onto a page.
func (m *meta) write(p *page) {
_assert(m.root.root < m.pgid, "root bucket pgid (%d) above high water mark (%d)", m.root.root, m.pgid)
_assert(m.freelist < m.pgid, "freelist pgid (%d) above high water mark (%d)", m.freelist, m.pgid)
// Page id is either going to be 0 or 1 which we can determine by the transaction ID.
p.id = pgid(m.txid % 2)
p.flags |= metaPageFlag

View File

@ -96,6 +96,7 @@ func (n *node) prevSibling() *node {
// put inserts a key/value.
func (n *node) put(oldKey, newKey, value []byte, pgid pgid, flags uint32) {
_assert(pgid < n.bucket.tx.meta.pgid, "pgid (%d) above high water mark (%d)", pgid, n.bucket.tx.meta.pgid)
_assert(len(oldKey) > 0, "put: zero-length old key")
_assert(len(newKey) > 0, "put: zero-length new key")
@ -284,6 +285,7 @@ func (n *node) spill() error {
}
// Write the node.
_assert(p.id < tx.meta.pgid, "pgid (%d) above high water mark (%d)", p.id, tx.meta.pgid)
node.pgid = p.id
node.write(p)
@ -314,6 +316,7 @@ func (n *node) spill() error {
}
// Write the new root.
_assert(p.id < tx.meta.pgid, "pgid (%d) above high water mark (%d)", p.id, tx.meta.pgid)
parent.pgid = p.id
parent.write(p)
}

View File

@ -9,7 +9,7 @@ import (
// Ensure that a node can insert a key/value.
func TestNode_put(t *testing.T) {
n := &node{inodes: make(inodes, 0)}
n := &node{inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{meta: &meta{pgid: 1}}}}
n.put([]byte("baz"), []byte("baz"), []byte("2"), 0, 0)
n.put([]byte("foo"), []byte("foo"), []byte("0"), 0, 0)
n.put([]byte("bar"), []byte("bar"), []byte("1"), 0, 0)
@ -58,7 +58,7 @@ func TestNode_read_LeafPage(t *testing.T) {
// Ensure that a node can serialize into a leaf page.
func TestNode_write_LeafPage(t *testing.T) {
// Create a node.
n := &node{isLeaf: true, inodes: make(inodes, 0)}
n := &node{isLeaf: true, inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{meta: &meta{pgid: 1}}}}
n.put([]byte("susy"), []byte("susy"), []byte("que"), 0, 0)
n.put([]byte("ricki"), []byte("ricki"), []byte("lake"), 0, 0)
n.put([]byte("john"), []byte("john"), []byte("johnson"), 0, 0)
@ -85,7 +85,7 @@ func TestNode_write_LeafPage(t *testing.T) {
// Ensure that a node can split into appropriate subgroups.
func TestNode_split(t *testing.T) {
// Create a node.
n := &node{inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{}}}
n := &node{inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{meta: &meta{pgid: 1}}}}
n.put([]byte("00000001"), []byte("00000001"), []byte("0123456701234567"), 0, 0)
n.put([]byte("00000002"), []byte("00000002"), []byte("0123456701234567"), 0, 0)
n.put([]byte("00000003"), []byte("00000003"), []byte("0123456701234567"), 0, 0)
@ -104,7 +104,7 @@ func TestNode_split(t *testing.T) {
// Ensure that a page with the minimum number of inodes just returns a single node.
func TestNode_split_MinKeys(t *testing.T) {
// Create a node.
n := &node{inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{}}}
n := &node{inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{meta: &meta{pgid: 1}}}}
n.put([]byte("00000001"), []byte("00000001"), []byte("0123456701234567"), 0, 0)
n.put([]byte("00000002"), []byte("00000002"), []byte("0123456701234567"), 0, 0)
@ -116,7 +116,7 @@ func TestNode_split_MinKeys(t *testing.T) {
// Ensure that a node that has keys that all fit on a page just returns one leaf.
func TestNode_split_SinglePage(t *testing.T) {
// Create a node.
n := &node{inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{}}}
n := &node{inodes: make(inodes, 0), bucket: &Bucket{tx: &Tx{meta: &meta{pgid: 1}}}}
n.put([]byte("00000001"), []byte("00000001"), []byte("0123456701234567"), 0, 0)
n.put([]byte("00000002"), []byte("00000002"), []byte("0123456701234567"), 0, 0)
n.put([]byte("00000003"), []byte("00000003"), []byte("0123456701234567"), 0, 0)