Skip to content
This repository has been archived by the owner on Mar 9, 2019. It is now read-only.

Commit

Permalink
Merge pull request #104 from benbjohnson/remove-db-open
Browse files Browse the repository at this point in the history
Remove DB.Open() and only allow bolt.Open().
  • Loading branch information
benbjohnson committed Mar 31, 2014
2 parents b5c1715 + 8dafb23 commit 4870e5f
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 124 deletions.
10 changes: 0 additions & 10 deletions bolt.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,6 @@ import (
"os"
)

// Open creates and opens a database at the given path.
// If the file does not exist then it will be created automatically.
func Open(path string, mode os.FileMode) (*DB, error) {
db := &DB{}
if err := db.Open(path, mode); err != nil {
return nil, err
}
return db, nil
}

// ErrorList represents a slice of errors.
type ErrorList []error

Expand Down
40 changes: 15 additions & 25 deletions db.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,52 +67,43 @@ func (db *DB) String() string {
return fmt.Sprintf("DB<%q>", db.path)
}

// Open opens a data file at the given path and initializes the database.
// Open creates and opens a database at the given path.
// If the file does not exist then it will be created automatically.
func (db *DB) Open(path string, mode os.FileMode) error {
var err error
db.metalock.Lock()
defer db.metalock.Unlock()

// Exit if the database is currently open.
if db.opened {
return ErrDatabaseOpen
}
func Open(path string, mode os.FileMode) (*DB, error) {
var db = &DB{opened: true}

// Open data file and separate sync handler for metadata writes.
db.path = path

var err error
if db.file, err = os.OpenFile(db.path, os.O_RDWR|os.O_CREATE, mode); err != nil {
_ = db.close()
return err
return nil, err
}
if db.metafile, err = os.OpenFile(db.path, os.O_RDWR|os.O_SYNC, mode); err != nil {
_ = db.close()
return err
return nil, err
}

// default values for test hooks
if db.ops.writeAt == nil {
db.ops.writeAt = db.file.WriteAt
}
if db.ops.metaWriteAt == nil {
db.ops.metaWriteAt = db.metafile.WriteAt
}
// Default values for test hooks
db.ops.writeAt = db.file.WriteAt
db.ops.metaWriteAt = db.metafile.WriteAt

// Initialize the database if it doesn't exist.
if info, err := db.file.Stat(); err != nil {
return fmt.Errorf("stat error: %s", err)
return nil, fmt.Errorf("stat error: %s", err)
} else if info.Size() == 0 {
// Initialize new files with meta pages.
if err := db.init(); err != nil {
return err
return nil, err
}
} else {
// Read the first meta page to determine the page size.
var buf [0x1000]byte
if _, err := db.file.ReadAt(buf[:], 0); err == nil {
m := db.pageInBuffer(buf[:], 0).meta()
if err := m.validate(); err != nil {
return fmt.Errorf("meta error: %s", err)
return nil, fmt.Errorf("meta error: %s", err)
}
db.pageSize = int(m.pageSize)
}
Expand All @@ -121,16 +112,15 @@ func (db *DB) Open(path string, mode os.FileMode) error {
// Memory map the data file.
if err := db.mmap(0); err != nil {
_ = db.close()
return err
return nil, err
}

// Read in the freelist.
db.freelist = &freelist{pending: make(map[txid][]pgid)}
db.freelist.read(db.page(db.meta().freelist))

// Mark the database as opened and return.
db.opened = true
return nil
return db, nil
}

// mmap opens the underlying memory-mapped file and initializes the meta references.
Expand Down
126 changes: 55 additions & 71 deletions db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package bolt

import (
"errors"
"io"
"io/ioutil"
"math/rand"
"os"
Expand Down Expand Up @@ -37,38 +36,34 @@ func TestOpenBadPath(t *testing.T) {

// Ensure that a database can be opened without error.
func TestDBOpen(t *testing.T) {
withDB(func(db *DB, path string) {
err := db.Open(path, 0666)
withTempPath(func(path string) {
db, err := Open(path, 0666)
assert.NotNil(t, db)
assert.NoError(t, err)
assert.Equal(t, db.Path(), path)
})
}

// Ensure that the database returns an error if already open.
func TestDBReopen(t *testing.T) {
withDB(func(db *DB, path string) {
db.Open(path, 0666)
err := db.Open(path, 0666)
assert.Equal(t, err, ErrDatabaseOpen)
assert.NoError(t, db.Close())
})
}

// Ensure that a re-opened database is consistent.
func TestOpenCheck(t *testing.T) {
withDB(func(db *DB, path string) {
assert.NoError(t, db.Open(path, 0666))
withTempPath(func(path string) {
db, err := Open(path, 0666)
assert.NoError(t, err)
assert.NoError(t, db.Check())
db.Close()

assert.NoError(t, db.Open(path, 0666))
db, err = Open(path, 0666)
assert.NoError(t, err)
assert.NoError(t, db.Check())
db.Close()
})
}

// Ensure that the database returns an error if the file handle cannot be open.
func TestDBOpenFileError(t *testing.T) {
withDB(func(db *DB, path string) {
err := db.Open(path+"/youre-not-my-real-parent", 0666)
withTempPath(func(path string) {
_, err := Open(path+"/youre-not-my-real-parent", 0666)
if err, _ := err.(*os.PathError); assert.Error(t, err) {
assert.Equal(t, path+"/youre-not-my-real-parent", err.Path)
assert.Equal(t, "open", err.Op)
Expand All @@ -78,33 +73,27 @@ func TestDBOpenFileError(t *testing.T) {

// Ensure that write errors to the meta file handler during initialization are returned.
func TestDBMetaInitWriteError(t *testing.T) {
withDB(func(db *DB, path string) {
// Mock the file system.
db.ops.metaWriteAt = func(p []byte, offset int64) (n int, err error) { return 0, io.ErrShortWrite }

// Open the database.
err := db.Open(path, 0666)
assert.Equal(t, err, io.ErrShortWrite)
})
t.Skip("pending")
}

// Ensure that a database that is too small returns an error.
func TestDBFileTooSmall(t *testing.T) {
withDB(func(db *DB, path string) {
assert.NoError(t, db.Open(path, 0666))
withTempPath(func(path string) {
db, err := Open(path, 0666)
assert.NoError(t, err)
db.Close()

// corrupt the database
assert.NoError(t, os.Truncate(path, int64(os.Getpagesize())))

err := db.Open(path, 0666)
db, err = Open(path, 0666)
assert.Equal(t, errors.New("file size too small"), err)
})
}

// Ensure that corrupt meta0 page errors get returned.
func TestDBCorruptMeta0(t *testing.T) {
withDB(func(db *DB, path string) {
withTempPath(func(path string) {
var m meta
m.magic = magic
m.version = version
Expand All @@ -121,18 +110,17 @@ func TestDBCorruptMeta0(t *testing.T) {
assert.NoError(t, err)

// Open the database.
err = db.Open(path, 0666)
_, err = Open(path, 0666)
assert.Equal(t, err, errors.New("meta error: invalid database"))
})
}

// Ensure that a database cannot open a transaction when it's not open.
func TestDBTxErrDatabaseNotOpen(t *testing.T) {
withDB(func(db *DB, path string) {
tx, err := db.Begin(false)
assert.Nil(t, tx)
assert.Equal(t, err, ErrDatabaseNotOpen)
})
var db DB
tx, err := db.Begin(false)
assert.Nil(t, tx)
assert.Equal(t, err, ErrDatabaseNotOpen)
}

// Ensure that a read-write transaction can be retrieved.
Expand All @@ -149,11 +137,10 @@ func TestDBBeginRW(t *testing.T) {

// Ensure that opening a transaction while the DB is closed returns an error.
func TestDBRWTxOpenWithClosedDB(t *testing.T) {
withDB(func(db *DB, path string) {
tx, err := db.Begin(true)
assert.Equal(t, err, ErrDatabaseNotOpen)
assert.Nil(t, tx)
})
var db DB
tx, err := db.Begin(true)
assert.Equal(t, err, ErrDatabaseNotOpen)
assert.Nil(t, tx)
}

// Ensure a database can provide a transactional block.
Expand All @@ -179,29 +166,27 @@ func TestDBTxBlock(t *testing.T) {

// Ensure a closed database returns an error while running a transaction block
func TestDBTxBlockWhileClosed(t *testing.T) {
withDB(func(db *DB, path string) {
err := db.Update(func(tx *Tx) error {
tx.CreateBucket("widgets")
return nil
})
assert.Equal(t, err, ErrDatabaseNotOpen)
var db DB
err := db.Update(func(tx *Tx) error {
tx.CreateBucket("widgets")
return nil
})
assert.Equal(t, err, ErrDatabaseNotOpen)
}

// Ensure a panic occurs while trying to commit a managed transaction.
func TestDBTxBlockWithManualCommitAndRollback(t *testing.T) {
withOpenDB(func(db *DB, path string) {
db.Update(func(tx *Tx) error {
tx.CreateBucket("widgets")
assert.Panics(t, func() { tx.Commit() })
assert.Panics(t, func() { tx.Rollback() })
return nil
})
db.View(func(tx *Tx) error {
assert.Panics(t, func() { tx.Commit() })
assert.Panics(t, func() { tx.Rollback() })
return nil
})
var db DB
db.Update(func(tx *Tx) error {
tx.CreateBucket("widgets")
assert.Panics(t, func() { tx.Commit() })
assert.Panics(t, func() { tx.Rollback() })
return nil
})
db.View(func(tx *Tx) error {
assert.Panics(t, func() { tx.Commit() })
assert.Panics(t, func() { tx.Rollback() })
return nil
})
}

Expand All @@ -217,8 +202,8 @@ func TestDBCopyFile(t *testing.T) {
assert.NoError(t, os.RemoveAll("/tmp/bolt.copyfile.db"))
assert.NoError(t, db.CopyFile("/tmp/bolt.copyfile.db", 0666))

var db2 DB
assert.NoError(t, db2.Open("/tmp/bolt.copyfile.db", 0666))
db2, err := Open("/tmp/bolt.copyfile.db", 0666)
assert.NoError(t, err)
defer db2.Close()

db2.View(func(tx *Tx) error {
Expand Down Expand Up @@ -272,11 +257,10 @@ func TestDBStat(t *testing.T) {

// Ensure the getting stats on a closed database returns an error.
func TestDBStatWhileClosed(t *testing.T) {
withDB(func(db *DB, path string) {
stat, err := db.Stat()
assert.Equal(t, err, ErrDatabaseNotOpen)
assert.Nil(t, stat)
})
var db DB
stat, err := db.Stat()
assert.Equal(t, err, ErrDatabaseNotOpen)
assert.Nil(t, stat)
}

// Ensure that an error is returned when a database write fails.
Expand Down Expand Up @@ -379,22 +363,22 @@ func BenchmarkDBPutRandom(b *testing.B) {
})
}

// withDB executes a function with a database reference.
func withDB(fn func(*DB, string)) {
// withTempPath executes a function with a database reference.
func withTempPath(fn func(string)) {
f, _ := ioutil.TempFile("", "bolt-")
path := f.Name()
f.Close()
os.Remove(path)
defer os.RemoveAll(path)

var db DB
fn(&db, path)
fn(path)
}

// withOpenDB executes a function with an already opened database.
func withOpenDB(fn func(*DB, string)) {
withDB(func(db *DB, path string) {
if err := db.Open(path, 0666); err != nil {
withTempPath(func(path string) {
db, err := Open(path, 0666)
if err != nil {
panic("cannot open db: " + err.Error())
}
defer db.Close()
Expand Down

0 comments on commit 4870e5f

Please sign in to comment.