diff --git a/db.go b/db.go index 88c3065..bb9bbf6 100644 --- a/db.go +++ b/db.go @@ -37,11 +37,6 @@ type DB struct { mmaplock sync.RWMutex // Protects mmap access during remapping. } -// NewDB creates a new DB instance. -func NewDB() *DB { - return &DB{} -} - // Path returns the path to currently open database file. func (db *DB) Path() string { return db.path @@ -460,8 +455,8 @@ func (db *DB) Copy(w io.Writer) error { // CopyFile copies the entire database to file at the given path. // A reader transaction is maintained during the copy so it is safe to continue // using the database while a copy is in progress. -func (db *DB) CopyFile(path string) error { - f, err := os.Create(path) +func (db *DB) CopyFile(path string, mode os.FileMode) error { + f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode) if err != nil { return err } diff --git a/db_test.go b/db_test.go index 51b76d5..2682f55 100644 --- a/db_test.go +++ b/db_test.go @@ -140,6 +140,17 @@ func TestDBTransactionDatabaseNotOpenError(t *testing.T) { }) } +// Ensure that a bucket that gets a non-existent key returns nil. +func TestDBGetNonExistent(t *testing.T) { + withOpenDB(func(db *DB, path string) { + db.CreateBucket("widgets") + value, err := db.Get("widgets", []byte("foo")) + if assert.NoError(t, err) { + assert.Nil(t, value) + } + }) +} + // Ensure that a bucket can write a key/value. func TestDBPut(t *testing.T) { withOpenDB(func(db *DB, path string) { @@ -207,17 +218,17 @@ func withDB(fn func(*DB, string)) { os.Remove(path) defer os.RemoveAll(path) - db := NewDB() - fn(db, path) + var db DB + fn(&db, path) } // withMockDB executes a function with a database reference and a mock filesystem. func withMockDB(fn func(*DB, *mockos, *mocksyscall, string)) { os, syscall := &mockos{}, &mocksyscall{} - db := NewDB() + var db DB db.os = os db.syscall = syscall - fn(db, os, syscall, "/mock/db") + fn(&db, os, syscall, "/mock/db") } // withOpenDB executes a function with an already opened database. diff --git a/example_test.go b/example_test.go new file mode 100644 index 0000000..892807e --- /dev/null +++ b/example_test.go @@ -0,0 +1,142 @@ +package bolt + +import ( + "fmt" + "os" +) + +func init() { + os.RemoveAll("/tmp/bolt") + os.MkdirAll("/tmp/bolt", 0777) +} + +func ExampleDB_Put() { + // Open the database. + var db DB + db.Open("/tmp/bolt/db_put.db", 0666) + defer db.Close() + + // Create a bucket. + db.CreateBucket("widgets") + + // Set the value "bar" for the key "foo". + db.Put("widgets", []byte("foo"), []byte("bar")) + + // Retrieve the key back from the database and verify it. + value, _ := db.Get("widgets", []byte("foo")) + fmt.Printf("The value of 'foo' is: %s\n", string(value)) + + // Output: + // The value of 'foo' is: bar +} + +func ExampleDB_Delete() { + // Open the database. + var db DB + db.Open("/tmp/bolt/db_delete.db", 0666) + defer db.Close() + + // Create a bucket. + db.CreateBucket("widgets") + + // Set the value "bar" for the key "foo". + db.Put("widgets", []byte("foo"), []byte("bar")) + + // Retrieve the key back from the database and verify it. + value, _ := db.Get("widgets", []byte("foo")) + fmt.Printf("The value of 'foo' was: %s\n", string(value)) + + // Delete the "foo" key. + db.Delete("widgets", []byte("foo")) + + // Retrieve the key again. + value, _ = db.Get("widgets", []byte("foo")) + if value == nil { + fmt.Printf("The value of 'foo' is now: nil\n") + } + + // Output: + // The value of 'foo' was: bar + // The value of 'foo' is now: nil +} + +func ExampleRWTransaction() { + // Open the database. + var db DB + db.Open("/tmp/bolt/rwtransaction.db", 0666) + defer db.Close() + + // Create a bucket. + db.CreateBucket("widgets") + + // Create several keys in a transaction. + rwtxn, _ := db.RWTransaction() + rwtxn.Put("widgets", []byte("john"), []byte("blue")) + rwtxn.Put("widgets", []byte("abby"), []byte("red")) + rwtxn.Put("widgets", []byte("zephyr"), []byte("purple")) + rwtxn.Commit() + + // Iterate over the values in sorted key order. + txn, _ := db.Transaction() + c, _ := txn.Cursor("widgets") + for k, v := c.First(); k != nil; k, v = c.Next() { + fmt.Printf("%s likes %s\n", string(k), string(v)) + } + txn.Close() + + // Output: + // abby likes red + // john likes blue + // zephyr likes purple +} + +func ExampleRWTransaction_rollback() { + // Open the database. + var db DB + db.Open("/tmp/bolt/rwtransaction_rollback.db", 0666) + defer db.Close() + + // Create a bucket. + db.CreateBucket("widgets") + + // Set a value for a key. + db.Put("widgets", []byte("foo"), []byte("bar")) + + // Update the key but rollback the transaction so it never saves. + rwtxn, _ := db.RWTransaction() + rwtxn.Put("widgets", []byte("foo"), []byte("baz")) + rwtxn.Rollback() + + // Ensure that our original value is still set. + value, _ := db.Get("widgets", []byte("foo")) + fmt.Printf("The value for 'foo' is still: %s\n", string(value)) + + // Output: + // The value for 'foo' is still: bar +} + +func ExampleDB_CopyFile() { + // Open the database. + var db DB + db.Open("/tmp/bolt/db_copy.db", 0666) + defer db.Close() + + // Create a bucket and a key. + db.CreateBucket("widgets") + db.Put("widgets", []byte("foo"), []byte("bar")) + + // Copy the database to another file. + db.CopyFile("/tmp/bolt/db_copy_2.db", 0666) + + // Open the cloned database. + var db2 DB + db2.Open("/tmp/bolt/db_copy_2.db", 0666) + defer db2.Close() + + // Ensure that the key exists in the copy. + value, _ := db2.Get("widgets", []byte("foo")) + fmt.Printf("The value for 'foo' in the clone is: %s\n", string(value)) + + // Output: + // The value for 'foo' in the clone is: bar +} diff --git a/rwtransaction_test.go b/rwtransaction_test.go index 67855be..1ce3f70 100644 --- a/rwtransaction_test.go +++ b/rwtransaction_test.go @@ -119,7 +119,7 @@ func TestRWTransactionPutSingle(t *testing.T) { panic("get error: " + err.Error()) } if !bytes.Equal(value, v) { - db.CopyFile("/tmp/bolt.put.single.db") + db.CopyFile("/tmp/bolt.put.single.db", 0666) t.Fatalf("value mismatch [run %d] (%d of %d):\nkey: %x\ngot: %x\nexp: %x", index, i, len(m), []byte(k), value, v) } i++ @@ -155,7 +155,7 @@ func TestRWTransactionPutMultiple(t *testing.T) { value, err := txn.Get("widgets", item.Key) assert.NoError(t, err) if !assert.Equal(t, item.Value, value) { - db.CopyFile("/tmp/bolt.put.multiple.db") + db.CopyFile("/tmp/bolt.put.multiple.db", 0666) t.FailNow() } }