// Ensure that a re-opened database is consistent. func TestOpen_Check(t *testing.T) { path := tempfile() db, err := bolt.Open(path, 0666, nil) if err != nil { t.Fatal(err) } if err := db.View(func(tx *bolt.Tx) error { return <-tx.Check() }); err != nil { t.Fatal(err) } if err := db.Close(); err != nil { t.Fatal(err) } db, err = bolt.Open(path, 0666, nil) if err != nil { t.Fatal(err) } if err := db.View(func(tx *bolt.Tx) error { return <-tx.Check() }); err != nil { t.Fatal(err) } if err := db.Close(); err != nil { t.Fatal(err) } }
// Ensure that opening an already open database file will wait until its closed. func TestOpen_Wait(t *testing.T) { if runtime.GOOS == "solaris" { t.Skip("solaris fcntl locks don't support intra-process locking") } path := tempfile() // Open a data file. db0, err := bolt.Open(path, 0666, nil) if err != nil { t.Fatal(err) } // Close it in just a bit. time.AfterFunc(100*time.Millisecond, func() { _ = db0.Close() }) // Attempt to open the database again. start := time.Now() db1, err := bolt.Open(path, 0666, &bolt.Options{Timeout: 200 * time.Millisecond}) if err != nil { t.Fatal(err) } else if time.Since(start) <= 100*time.Millisecond { t.Fatal("expected to wait at least timeout duration") } if err := db1.Close(); err != nil { t.Fatal(err) } }
func ExampleTx_CopyFile() { // Open the database. db, err := bolt.Open(tempfile(), 0666, nil) if err != nil { log.Fatal(err) } defer os.Remove(db.Path()) // Create a bucket and a key. if err := db.Update(func(tx *bolt.Tx) error { b, err := tx.CreateBucket([]byte("widgets")) if err != nil { return err } if err := b.Put([]byte("foo"), []byte("bar")); err != nil { return err } return nil }); err != nil { log.Fatal(err) } // Copy the database to another file. toFile := tempfile() if err := db.View(func(tx *bolt.Tx) error { return tx.CopyFile(toFile, 0666) }); err != nil { log.Fatal(err) } defer os.Remove(toFile) // Open the cloned database. db2, err := bolt.Open(toFile, 0666, nil) if err != nil { log.Fatal(err) } // Ensure that the key exists in the copy. if err := db2.View(func(tx *bolt.Tx) error { value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) fmt.Printf("The value for 'foo' in the clone is: %s\n", value) return nil }); err != nil { log.Fatal(err) } // Close database to release file lock. if err := db.Close(); err != nil { log.Fatal(err) } if err := db2.Close(); err != nil { log.Fatal(err) } // Output: // The value for 'foo' in the clone is: bar }
// MustOpenDB returns a new, open DB at a temporary location. func MustOpenDB() *DB { db, err := bolt.Open(tempfile(), 0666, nil) if err != nil { panic(err) } return &DB{db} }
// Run executes the command. func (cmd *InfoCommand) Run(args ...string) error { // Parse flags. fs := flag.NewFlagSet("", flag.ContinueOnError) help := fs.Bool("h", false, "") if err := fs.Parse(args); err != nil { return err } else if *help { fmt.Fprintln(cmd.Stderr, cmd.Usage()) return ErrUsage } // Require database path. path := fs.Arg(0) if path == "" { return ErrPathRequired } else if _, err := os.Stat(path); os.IsNotExist(err) { return ErrFileNotFound } // Open the database. db, err := bolt.Open(path, 0666, nil) if err != nil { return err } defer db.Close() // Print basic database info. info := db.Info() fmt.Fprintf(cmd.Stdout, "Page Size: %d\n", info.PageSize) return nil }
func ExampleDB_Begin_ReadOnly() { // Open the database. db, err := bolt.Open(tempfile(), 0666, nil) if err != nil { log.Fatal(err) } defer os.Remove(db.Path()) // Create a bucket using a read-write transaction. if err := db.Update(func(tx *bolt.Tx) error { _, err := tx.CreateBucket([]byte("widgets")) return err }); err != nil { log.Fatal(err) } // Create several keys in a transaction. tx, err := db.Begin(true) if err != nil { log.Fatal(err) } b := tx.Bucket([]byte("widgets")) if err := b.Put([]byte("john"), []byte("blue")); err != nil { log.Fatal(err) } if err := b.Put([]byte("abby"), []byte("red")); err != nil { log.Fatal(err) } if err := b.Put([]byte("zephyr"), []byte("purple")); err != nil { log.Fatal(err) } if err := tx.Commit(); err != nil { log.Fatal(err) } // Iterate over the values in sorted key order. tx, err = db.Begin(false) if err != nil { log.Fatal(err) } c := tx.Bucket([]byte("widgets")).Cursor() for k, v := c.First(); k != nil; k, v = c.Next() { fmt.Printf("%s likes %s\n", k, v) } if err := tx.Rollback(); err != nil { log.Fatal(err) } if err := db.Close(); err != nil { log.Fatal(err) } // Output: // abby likes red // john likes blue // zephyr likes purple }
// Ensure that opening a database does not increase its size. // https://github.com/boltdb/bolt/issues/291 func TestOpen_Size(t *testing.T) { // Open a data file. db := MustOpenDB() path := db.Path() defer db.MustClose() pagesize := db.Info().PageSize // Insert until we get above the minimum 4MB size. if err := db.Update(func(tx *bolt.Tx) error { b, _ := tx.CreateBucketIfNotExists([]byte("data")) for i := 0; i < 10000; i++ { if err := b.Put([]byte(fmt.Sprintf("%04d", i)), make([]byte, 1000)); err != nil { t.Fatal(err) } } return nil }); err != nil { t.Fatal(err) } // Close database and grab the size. if err := db.DB.Close(); err != nil { t.Fatal(err) } sz := fileSize(path) if sz == 0 { t.Fatalf("unexpected new file size: %d", sz) } // Reopen database, update, and check size again. db0, err := bolt.Open(path, 0666, nil) if err != nil { t.Fatal(err) } if err := db0.Update(func(tx *bolt.Tx) error { if err := tx.Bucket([]byte("data")).Put([]byte{0}, []byte{0}); err != nil { t.Fatal(err) } return nil }); err != nil { t.Fatal(err) } if err := db0.Close(); err != nil { t.Fatal(err) } newSz := fileSize(path) if newSz == 0 { t.Fatalf("unexpected new file size: %d", newSz) } // Compare the original size with the new size. // db size might increase by a few page sizes due to the new small update. if sz < newSz-5*int64(pagesize) { t.Fatalf("unexpected file growth: %d => %d", sz, newSz) } }
func ExampleBucket_Delete() { // Open the database. db, err := bolt.Open(tempfile(), 0666, nil) if err != nil { log.Fatal(err) } defer os.Remove(db.Path()) // Start a write transaction. if err := db.Update(func(tx *bolt.Tx) error { // Create a bucket. b, err := tx.CreateBucket([]byte("widgets")) if err != nil { return err } // Set the value "bar" for the key "foo". if err := b.Put([]byte("foo"), []byte("bar")); err != nil { return err } // Retrieve the key back from the database and verify it. value := b.Get([]byte("foo")) fmt.Printf("The value of 'foo' was: %s\n", value) return nil }); err != nil { log.Fatal(err) } // Delete the key in a different write transaction. if err := db.Update(func(tx *bolt.Tx) error { return tx.Bucket([]byte("widgets")).Delete([]byte("foo")) }); err != nil { log.Fatal(err) } // Retrieve the key again. if err := db.View(func(tx *bolt.Tx) error { value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) if value == nil { fmt.Printf("The value of 'foo' is now: nil\n") } return nil }); err != nil { log.Fatal(err) } // Close database to release file lock. if err := db.Close(); err != nil { log.Fatal(err) } // Output: // The value of 'foo' was: bar // The value of 'foo' is now: nil }
// TestDB_Open_InitialMmapSize tests if having InitialMmapSize large enough // to hold data from concurrent write transaction resolves the issue that // read transaction blocks the write transaction and causes deadlock. // This is a very hacky test since the mmap size is not exposed. func TestDB_Open_InitialMmapSize(t *testing.T) { path := tempfile() defer os.Remove(path) initMmapSize := 1 << 31 // 2GB testWriteSize := 1 << 27 // 134MB db, err := bolt.Open(path, 0666, &bolt.Options{InitialMmapSize: initMmapSize}) if err != nil { t.Fatal(err) } // create a long-running read transaction // that never gets closed while writing rtx, err := db.Begin(false) if err != nil { t.Fatal(err) } // create a write transaction wtx, err := db.Begin(true) if err != nil { t.Fatal(err) } b, err := wtx.CreateBucket([]byte("test")) if err != nil { t.Fatal(err) } // and commit a large write err = b.Put([]byte("foo"), make([]byte, testWriteSize)) if err != nil { t.Fatal(err) } done := make(chan struct{}) go func() { if err := wtx.Commit(); err != nil { t.Fatal(err) } done <- struct{}{} }() select { case <-time.After(5 * time.Second): t.Errorf("unexpected that the reader blocks writer") case <-done: } if err := rtx.Rollback(); err != nil { t.Fatal(err) } }
func ExampleCursor_reverse() { // Open the database. db, err := bolt.Open(tempfile(), 0666, nil) if err != nil { log.Fatal(err) } defer os.Remove(db.Path()) // Start a read-write transaction. if err := db.Update(func(tx *bolt.Tx) error { // Create a new bucket. b, err := tx.CreateBucket([]byte("animals")) if err != nil { return err } // Insert data into a bucket. if err := b.Put([]byte("dog"), []byte("fun")); err != nil { log.Fatal(err) } if err := b.Put([]byte("cat"), []byte("lame")); err != nil { log.Fatal(err) } if err := b.Put([]byte("liger"), []byte("awesome")); err != nil { log.Fatal(err) } // Create a cursor for iteration. c := b.Cursor() // Iterate over items in reverse sorted key order. This starts // from the last key/value pair and updates the k/v variables to // the previous key/value on each iteration. // // The loop finishes at the beginning of the cursor when a nil key // is returned. for k, v := c.Last(); k != nil; k, v = c.Prev() { fmt.Printf("A %s is %s.\n", k, v) } return nil }); err != nil { log.Fatal(err) } // Close the database to release the file lock. if err := db.Close(); err != nil { log.Fatal(err) } // Output: // A liger is awesome. // A dog is fun. // A cat is lame. }
// Ensure that a database that is too small returns an error. func TestOpen_FileTooSmall(t *testing.T) { path := tempfile() db, err := bolt.Open(path, 0666, nil) if err != nil { t.Fatal(err) } if err := db.Close(); err != nil { t.Fatal(err) } // corrupt the database if err := os.Truncate(path, int64(os.Getpagesize())); err != nil { t.Fatal(err) } db, err = bolt.Open(path, 0666, nil) if err == nil || err.Error() != "file size too small" { t.Fatalf("unexpected error: %s", err) } }
// MustOpen creates a Bolt database in a temporary location. func MustOpen(mode os.FileMode, options *bolt.Options) *DB { // Create temporary path. f, _ := ioutil.TempFile("", "bolt-") f.Close() os.Remove(f.Name()) db, err := bolt.Open(f.Name(), mode, options) if err != nil { panic(err.Error()) } return &DB{DB: db, Path: f.Name()} }
// Run executes the command. func (cmd *CheckCommand) Run(args ...string) error { // Parse flags. fs := flag.NewFlagSet("", flag.ContinueOnError) help := fs.Bool("h", false, "") if err := fs.Parse(args); err != nil { return err } else if *help { fmt.Fprintln(cmd.Stderr, cmd.Usage()) return ErrUsage } // Require database path. path := fs.Arg(0) if path == "" { return ErrPathRequired } else if _, err := os.Stat(path); os.IsNotExist(err) { return ErrFileNotFound } // Open database. db, err := bolt.Open(path, 0666, nil) if err != nil { return err } defer db.Close() // Perform consistency check. return db.View(func(tx *bolt.Tx) error { var count int ch := tx.Check() loop: for { select { case err, ok := <-ch: if !ok { break loop } fmt.Fprintln(cmd.Stdout, err) count++ } } // Print summary of errors. if count > 0 { fmt.Fprintf(cmd.Stdout, "%d errors found\n", count) return ErrCorrupt } // Notify user that database is valid. fmt.Fprintln(cmd.Stdout, "OK") return nil }) }
func ExampleTx_Rollback() { // Open the database. db, err := bolt.Open(tempfile(), 0666, nil) if err != nil { log.Fatal(err) } defer os.Remove(db.Path()) // Create a bucket. if err := db.Update(func(tx *bolt.Tx) error { _, err := tx.CreateBucket([]byte("widgets")) return err }); err != nil { log.Fatal(err) } // Set a value for a key. if err := db.Update(func(tx *bolt.Tx) error { return tx.Bucket([]byte("widgets")).Put([]byte("foo"), []byte("bar")) }); err != nil { log.Fatal(err) } // Update the key but rollback the transaction so it never saves. tx, err := db.Begin(true) if err != nil { log.Fatal(err) } b := tx.Bucket([]byte("widgets")) if err := b.Put([]byte("foo"), []byte("baz")); err != nil { log.Fatal(err) } if err := tx.Rollback(); err != nil { log.Fatal(err) } // Ensure that our original value is still set. if err := db.View(func(tx *bolt.Tx) error { value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) fmt.Printf("The value for 'foo' is still: %s\n", value) return nil }); err != nil { log.Fatal(err) } // Close database to release file lock. if err := db.Close(); err != nil { log.Fatal(err) } // Output: // The value for 'foo' is still: bar }
func ExampleBucket_ForEach() { // Open the database. db, err := bolt.Open(tempfile(), 0666, nil) if err != nil { log.Fatal(err) } defer os.Remove(db.Path()) // Insert data into a bucket. if err := db.Update(func(tx *bolt.Tx) error { b, err := tx.CreateBucket([]byte("animals")) if err != nil { return err } if err := b.Put([]byte("dog"), []byte("fun")); err != nil { return err } if err := b.Put([]byte("cat"), []byte("lame")); err != nil { return err } if err := b.Put([]byte("liger"), []byte("awesome")); err != nil { return err } // Iterate over items in sorted key order. if err := b.ForEach(func(k, v []byte) error { fmt.Printf("A %s is %s.\n", k, v) return nil }); err != nil { return err } return nil }); err != nil { log.Fatal(err) } // Close database to release file lock. if err := db.Close(); err != nil { log.Fatal(err) } // Output: // A cat is lame. // A dog is fun. // A liger is awesome. }
// Ensure that the database can be copied to a file path. func TestTx_CopyFile(t *testing.T) { db := MustOpenDB() defer db.MustClose() path := tempfile() if err := db.Update(func(tx *bolt.Tx) error { b, err := tx.CreateBucket([]byte("widgets")) if err != nil { t.Fatal(err) } if err := b.Put([]byte("foo"), []byte("bar")); err != nil { t.Fatal(err) } if err := b.Put([]byte("baz"), []byte("bat")); err != nil { t.Fatal(err) } return nil }); err != nil { t.Fatal(err) } if err := db.View(func(tx *bolt.Tx) error { return tx.CopyFile(path, 0600) }); err != nil { t.Fatal(err) } db2, err := bolt.Open(path, 0600, nil) if err != nil { t.Fatal(err) } if err := db2.View(func(tx *bolt.Tx) error { if v := tx.Bucket([]byte("widgets")).Get([]byte("foo")); !bytes.Equal(v, []byte("bar")) { t.Fatalf("unexpected value: %v", v) } if v := tx.Bucket([]byte("widgets")).Get([]byte("baz")); !bytes.Equal(v, []byte("bat")) { t.Fatalf("unexpected value: %v", v) } return nil }); err != nil { t.Fatal(err) } if err := db2.Close(); err != nil { t.Fatal(err) } }
// Ensure that a database can be opened without error. func TestOpen(t *testing.T) { path := tempfile() db, err := bolt.Open(path, 0666, nil) if err != nil { t.Fatal(err) } else if db == nil { t.Fatal("expected db") } if s := db.Path(); s != path { t.Fatalf("unexpected path: %s", s) } if err := db.Close(); err != nil { t.Fatal(err) } }
// Ensure that opening a file that is not a Bolt database returns ErrInvalid. func TestOpen_ErrInvalid(t *testing.T) { path := tempfile() f, err := os.Create(path) if err != nil { t.Fatal(err) } if _, err := fmt.Fprintln(f, "this is not a bolt database"); err != nil { t.Fatal(err) } if err := f.Close(); err != nil { t.Fatal(err) } defer os.Remove(path) if _, err := bolt.Open(path, 0666, nil); err != bolt.ErrInvalid { t.Fatalf("unexpected error: %s", err) } }
func ExampleBucket_Put() { // Open the database. db, err := bolt.Open(tempfile(), 0666, nil) if err != nil { log.Fatal(err) } defer os.Remove(db.Path()) // Start a write transaction. if err := db.Update(func(tx *bolt.Tx) error { // Create a bucket. b, err := tx.CreateBucket([]byte("widgets")) if err != nil { return err } // Set the value "bar" for the key "foo". if err := b.Put([]byte("foo"), []byte("bar")); err != nil { return err } return nil }); err != nil { log.Fatal(err) } // Read value back in a different read-only transaction. if err := db.View(func(tx *bolt.Tx) error { value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) fmt.Printf("The value of 'foo' is: %s\n", value) return nil }); err != nil { log.Fatal(err) } // Close database to release file lock. if err := db.Close(); err != nil { log.Fatal(err) } // Output: // The value of 'foo' is: bar }
func ExampleDB_View() { // Open the database. db, err := bolt.Open(tempfile(), 0666, nil) if err != nil { log.Fatal(err) } defer os.Remove(db.Path()) // Insert data into a bucket. if err := db.Update(func(tx *bolt.Tx) error { b, err := tx.CreateBucket([]byte("people")) if err != nil { return err } if err := b.Put([]byte("john"), []byte("doe")); err != nil { return err } if err := b.Put([]byte("susy"), []byte("que")); err != nil { return err } return nil }); err != nil { log.Fatal(err) } // Access data from within a read-only transactional block. if err := db.View(func(tx *bolt.Tx) error { v := tx.Bucket([]byte("people")).Get([]byte("john")) fmt.Printf("John's last name is %s.\n", v) return nil }); err != nil { log.Fatal(err) } // Close database to release the file lock. if err := db.Close(); err != nil { log.Fatal(err) } // Output: // John's last name is doe. }
// Run executes the "bench" command. func (cmd *BenchCommand) Run(args ...string) error { // Parse CLI arguments. options, err := cmd.ParseFlags(args) if err != nil { return err } // Remove path if "-work" is not set. Otherwise keep path. if options.Work { fmt.Fprintf(cmd.Stdout, "work: %s\n", options.Path) } else { defer os.Remove(options.Path) } // Create database. db, err := bolt.Open(options.Path, 0666, nil) if err != nil { return err } db.NoSync = options.NoSync defer db.Close() // Write to the database. var results BenchResults if err := cmd.runWrites(db, options, &results); err != nil { return fmt.Errorf("write: %v", err) } // Read from the database. if err := cmd.runReads(db, options, &results); err != nil { return fmt.Errorf("bench: read: %s", err) } // Print results. fmt.Fprintf(os.Stderr, "# Write\t%v\t(%v/op)\t(%v op/sec)\n", results.WriteDuration, results.WriteOpDuration(), results.WriteOpsPerSecond()) fmt.Fprintf(os.Stderr, "# Read\t%v\t(%v/op)\t(%v op/sec)\n", results.ReadDuration, results.ReadOpDuration(), results.ReadOpsPerSecond()) fmt.Fprintln(os.Stderr, "") return nil }
func ExampleDB_Update() { // Open the database. db, err := bolt.Open(tempfile(), 0666, nil) if err != nil { log.Fatal(err) } defer os.Remove(db.Path()) // Execute several commands within a read-write transaction. if err := db.Update(func(tx *bolt.Tx) error { b, err := tx.CreateBucket([]byte("widgets")) if err != nil { return err } if err := b.Put([]byte("foo"), []byte("bar")); err != nil { return err } return nil }); err != nil { log.Fatal(err) } // Read the value back from a separate read-only transaction. if err := db.View(func(tx *bolt.Tx) error { value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) fmt.Printf("The value of 'foo' is: %s\n", value) return nil }); err != nil { log.Fatal(err) } // Close database to release the file lock. if err := db.Close(); err != nil { log.Fatal(err) } // Output: // The value of 'foo' is: bar }
// Ensure that opening a file created with a different version of Bolt returns // ErrVersionMismatch. func TestOpen_ErrVersionMismatch(t *testing.T) { buf := make([]byte, pageSize) meta := (*meta)(unsafe.Pointer(&buf[0])) meta.magic = magic meta.version = version + 100 path := tempfile() f, err := os.Create(path) if err != nil { t.Fatal(err) } if _, err := f.WriteAt(buf, pageHeaderSize); err != nil { t.Fatal(err) } if err := f.Close(); err != nil { t.Fatal(err) } defer os.Remove(path) if _, err := bolt.Open(path, 0666, nil); err != bolt.ErrVersionMismatch { t.Fatalf("unexpected error: %s", err) } }
// Run executes the command. func (cmd *PagesCommand) Run(args ...string) error { // Parse flags. fs := flag.NewFlagSet("", flag.ContinueOnError) help := fs.Bool("h", false, "") if err := fs.Parse(args); err != nil { return err } else if *help { fmt.Fprintln(cmd.Stderr, cmd.Usage()) return ErrUsage } // Require database path. path := fs.Arg(0) if path == "" { return ErrPathRequired } else if _, err := os.Stat(path); os.IsNotExist(err) { return ErrFileNotFound } // Open database. db, err := bolt.Open(path, 0666, nil) if err != nil { return err } defer func() { _ = db.Close() }() // Write header. fmt.Fprintln(cmd.Stdout, "ID TYPE ITEMS OVRFLW") fmt.Fprintln(cmd.Stdout, "======== ========== ====== ======") return db.Update(func(tx *bolt.Tx) error { var id int for { p, err := tx.Page(id) if err != nil { return &PageError{ID: id, Err: err} } else if p == nil { break } // Only display count and overflow if this is a non-free page. var count, overflow string if p.Type != "free" { count = strconv.Itoa(p.Count) if p.OverflowCount > 0 { overflow = strconv.Itoa(p.OverflowCount) } } // Print table row. fmt.Fprintf(cmd.Stdout, "%-8d %-10s %-6s %-6s\n", p.ID, p.Type, count, overflow) // Move to the next non-overflow page. id += 1 if p.Type != "free" { id += p.OverflowCount } } return nil }) }
// Run executes the command. func (cmd *StatsCommand) Run(args ...string) error { // Parse flags. fs := flag.NewFlagSet("", flag.ContinueOnError) help := fs.Bool("h", false, "") if err := fs.Parse(args); err != nil { return err } else if *help { fmt.Fprintln(cmd.Stderr, cmd.Usage()) return ErrUsage } // Require database path. path, prefix := fs.Arg(0), fs.Arg(1) if path == "" { return ErrPathRequired } else if _, err := os.Stat(path); os.IsNotExist(err) { return ErrFileNotFound } // Open database. db, err := bolt.Open(path, 0666, nil) if err != nil { return err } defer db.Close() return db.View(func(tx *bolt.Tx) error { var s bolt.BucketStats var count int if err := tx.ForEach(func(name []byte, b *bolt.Bucket) error { if bytes.HasPrefix(name, []byte(prefix)) { s.Add(b.Stats()) count += 1 } return nil }); err != nil { return err } fmt.Fprintf(cmd.Stdout, "Aggregate statistics for %d buckets\n\n", count) fmt.Fprintln(cmd.Stdout, "Page count statistics") fmt.Fprintf(cmd.Stdout, "\tNumber of logical branch pages: %d\n", s.BranchPageN) fmt.Fprintf(cmd.Stdout, "\tNumber of physical branch overflow pages: %d\n", s.BranchOverflowN) fmt.Fprintf(cmd.Stdout, "\tNumber of logical leaf pages: %d\n", s.LeafPageN) fmt.Fprintf(cmd.Stdout, "\tNumber of physical leaf overflow pages: %d\n", s.LeafOverflowN) fmt.Fprintln(cmd.Stdout, "Tree statistics") fmt.Fprintf(cmd.Stdout, "\tNumber of keys/value pairs: %d\n", s.KeyN) fmt.Fprintf(cmd.Stdout, "\tNumber of levels in B+tree: %d\n", s.Depth) fmt.Fprintln(cmd.Stdout, "Page size utilization") fmt.Fprintf(cmd.Stdout, "\tBytes allocated for physical branch pages: %d\n", s.BranchAlloc) var percentage int if s.BranchAlloc != 0 { percentage = int(float32(s.BranchInuse) * 100.0 / float32(s.BranchAlloc)) } fmt.Fprintf(cmd.Stdout, "\tBytes actually used for branch data: %d (%d%%)\n", s.BranchInuse, percentage) fmt.Fprintf(cmd.Stdout, "\tBytes allocated for physical leaf pages: %d\n", s.LeafAlloc) percentage = 0 if s.LeafAlloc != 0 { percentage = int(float32(s.LeafInuse) * 100.0 / float32(s.LeafAlloc)) } fmt.Fprintf(cmd.Stdout, "\tBytes actually used for leaf data: %d (%d%%)\n", s.LeafInuse, percentage) fmt.Fprintln(cmd.Stdout, "Bucket statistics") fmt.Fprintf(cmd.Stdout, "\tTotal number of buckets: %d\n", s.BucketN) percentage = 0 if s.BucketN != 0 { percentage = int(float32(s.InlineBucketN) * 100.0 / float32(s.BucketN)) } fmt.Fprintf(cmd.Stdout, "\tTotal number on inlined buckets: %d (%d%%)\n", s.InlineBucketN, percentage) percentage = 0 if s.LeafInuse != 0 { percentage = int(float32(s.InlineBucketInuse) * 100.0 / float32(s.LeafInuse)) } fmt.Fprintf(cmd.Stdout, "\tBytes used for inlined buckets: %d (%d%%)\n", s.InlineBucketInuse, percentage) return nil }) }
// Ensure that opening a database with a bad path returns an error. func TestOpen_ErrNotExists(t *testing.T) { _, err := bolt.Open(filepath.Join(tempfile(), "bad-path"), 0666, nil) if err == nil { t.Fatal("expected error") } }
// Ensure that opening a database with a blank path returns an error. func TestOpen_ErrPathRequired(t *testing.T) { _, err := bolt.Open("", 0666, nil) if err == nil { t.Fatalf("expected error") } }
// Ensure that a database can be opened in read-only mode by multiple processes // and that a database can not be opened in read-write mode and in read-only // mode at the same time. func TestOpen_ReadOnly(t *testing.T) { if runtime.GOOS == "solaris" { t.Skip("solaris fcntl locks don't support intra-process locking") } bucket, key, value := []byte(`bucket`), []byte(`key`), []byte(`value`) path := tempfile() // Open in read-write mode. db, err := bolt.Open(path, 0666, nil) if err != nil { t.Fatal(err) } else if db.IsReadOnly() { t.Fatal("db should not be in read only mode") } if err := db.Update(func(tx *bolt.Tx) error { b, err := tx.CreateBucket(bucket) if err != nil { return err } if err := b.Put(key, value); err != nil { t.Fatal(err) } return nil }); err != nil { t.Fatal(err) } if err := db.Close(); err != nil { t.Fatal(err) } // Open in read-only mode. db0, err := bolt.Open(path, 0666, &bolt.Options{ReadOnly: true}) if err != nil { t.Fatal(err) } // Opening in read-write mode should return an error. if _, err = bolt.Open(path, 0666, &bolt.Options{Timeout: time.Millisecond * 100}); err == nil { t.Fatal("expected error") } // And again (in read-only mode). db1, err := bolt.Open(path, 0666, &bolt.Options{ReadOnly: true}) if err != nil { t.Fatal(err) } // Verify both read-only databases are accessible. for _, db := range []*bolt.DB{db0, db1} { // Verify is is in read only mode indeed. if !db.IsReadOnly() { t.Fatal("expected read only mode") } // Read-only databases should not allow updates. if err := db.Update(func(*bolt.Tx) error { panic(`should never get here`) }); err != bolt.ErrDatabaseReadOnly { t.Fatalf("unexpected error: %s", err) } // Read-only databases should not allow beginning writable txns. if _, err := db.Begin(true); err != bolt.ErrDatabaseReadOnly { t.Fatalf("unexpected error: %s", err) } // Verify the data. if err := db.View(func(tx *bolt.Tx) error { b := tx.Bucket(bucket) if b == nil { return fmt.Errorf("expected bucket `%s`", string(bucket)) } got := string(b.Get(key)) expected := string(value) if got != expected { return fmt.Errorf("expected `%s`, got `%s`", expected, got) } return nil }); err != nil { t.Fatal(err) } } if err := db0.Close(); err != nil { t.Fatal(err) } if err := db1.Close(); err != nil { t.Fatal(err) } }