diff --git a/bucket.go b/bucket.go index e2bf60c..57c5441 100644 --- a/bucket.go +++ b/bucket.go @@ -2,6 +2,40 @@ package bolt import ( "bytes" + "errors" +) + +var ( + // ErrBucketNotFound is returned when trying to access a bucket that has + // not been created yet. + ErrBucketNotFound = errors.New("bucket not found") + + // ErrBucketExists is returned when creating a bucket that already exists. + ErrBucketExists = errors.New("bucket already exists") + + // ErrBucketNameRequired is returned when creating a bucket with a blank name. + ErrBucketNameRequired = errors.New("bucket name required") + + // ErrBucketNameTooLarge is returned when creating a bucket with a name + // that is longer than MaxBucketNameSize. + ErrBucketNameTooLarge = errors.New("bucket name too large") + + // ErrBucketNotWritable is returned when changing data on a bucket + // reference that was created from a read-only transaction. + ErrBucketNotWritable = errors.New("bucket not writable") + + // ErrKeyRequired is returned when inserting a zero-length key. + ErrKeyRequired = errors.New("key required") + + // ErrKeyTooLarge is returned when inserting a key that is larger than MaxKeySize. + ErrKeyTooLarge = errors.New("key too large") + + // ErrValueTooLarge is returned when inserting a value that is larger than MaxValueSize. + ErrValueTooLarge = errors.New("value too large") + + // ErrSequenceOverflow is returned when the next sequence number will be + // larger than the maximum integer size. + ErrSequenceOverflow = errors.New("sequence overflow") ) // Bucket represents a collection of key/value pairs inside the database. diff --git a/bucket_test.go b/bucket_test.go index 92d5288..359a98f 100644 --- a/bucket_test.go +++ b/bucket_test.go @@ -2,6 +2,7 @@ package bolt import ( "bytes" + "errors" "fmt" "os" "strconv" @@ -203,12 +204,12 @@ func TestBucketForEachShortCircuit(t *testing.T) { err := tx.Bucket("widgets").ForEach(func(k, v []byte) error { index++ if bytes.Equal(k, []byte("baz")) { - return &Error{"marker", nil} + return errors.New("marker") } return nil }) - assert.Equal(t, err, &Error{"marker", nil}) - assert.Equal(t, index, 2) + assert.Equal(t, errors.New("marker"), err) + assert.Equal(t, 2, index) return nil }) }) diff --git a/db.go b/db.go index 5ec75b0..0d9117f 100644 --- a/db.go +++ b/db.go @@ -1,6 +1,7 @@ package bolt import ( + "errors" "fmt" "io" "os" @@ -15,6 +16,16 @@ const minMmapSize = 1 << 22 // 4MB // The largest step that can be taken when remapping the mmap. const maxMmapStep = 1 << 30 // 1GB +var ( + // ErrDatabaseNotOpen is returned when a DB instance is accessed before it + // is opened or after it is closed. + ErrDatabaseNotOpen = errors.New("database not open") + + // ErrDatabaseOpen is returned when opening a database that is + // already open. + ErrDatabaseOpen = errors.New("database already open") +) + // DB represents a collection of buckets persisted to a file on disk. // All data access is performed through transactions which can be obtained through the DB. // All the functions on DB will return a ErrDatabaseNotOpen if accessed before Open() is called. @@ -89,7 +100,7 @@ func (db *DB) Open(path string, mode os.FileMode) error { // Initialize the database if it doesn't exist. if info, err := db.file.Stat(); err != nil { - return &Error{"stat error", err} + return fmt.Errorf("stat error: %s", err) } else if info.Size() == 0 { // Initialize new files with meta pages. if err := db.init(); err != nil { @@ -101,7 +112,7 @@ func (db *DB) Open(path string, mode os.FileMode) error { if _, err := db.file.ReadAt(buf[:], 0); err == nil { m := db.pageInBuffer(buf[:], 0).meta() if err := m.validate(); err != nil { - return &Error{"meta error", err} + return fmt.Errorf("meta error: %s", err) } db.pageSize = int(m.pageSize) } @@ -140,9 +151,9 @@ func (db *DB) mmap(minsz int) error { info, err := db.file.Stat() if err != nil { - return &Error{"mmap stat error", err} + return fmt.Errorf("mmap stat error: %s", err) } else if int(info.Size()) < db.pageSize*2 { - return &Error{"file size too small", err} + return fmt.Errorf("file size too small") } // Ensure the size is at least the minimum size. @@ -163,10 +174,10 @@ func (db *DB) mmap(minsz int) error { // Validate the meta pages. if err := db.meta0.validate(); err != nil { - return &Error{"meta0 error", err} + return fmt.Errorf("meta0 error: %s", err) } if err := db.meta1.validate(); err != nil { - return &Error{"meta1 error", err} + return fmt.Errorf("meta1 error: %s", err) } return nil @@ -529,7 +540,7 @@ func (db *DB) allocate(count int) (*page, error) { var minsz = int((p.id+pgid(count))+1) * db.pageSize if minsz >= len(db.data) { if err := db.mmap(minsz); err != nil { - return nil, &Error{"mmap allocate error", err} + return nil, fmt.Errorf("mmap allocate error: %s", err) } } diff --git a/db_test.go b/db_test.go index b363486..462821f 100644 --- a/db_test.go +++ b/db_test.go @@ -1,6 +1,7 @@ package bolt import ( + "errors" "io" "io/ioutil" "math/rand" @@ -8,6 +9,7 @@ import ( "strconv" "strings" "testing" + "unsafe" "github.com/stretchr/testify/assert" ) @@ -53,14 +55,12 @@ func TestDBReopen(t *testing.T) { // Ensure that the database returns an error if the file handle cannot be open. func TestDBOpenFileError(t *testing.T) { - withDBFile(func(db *DB, path string) { - exp := &os.PathError{ - Op: "open", - Path: path + "/youre-not-my-real-parent", - Err: syscall.ENOTDIR, - } + withDB(func(db *DB, path string) { err := db.Open(path+"/youre-not-my-real-parent", 0666) - assert.Equal(t, err, exp) + 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) + } }) } @@ -78,13 +78,14 @@ func TestDBMetaInitWriteError(t *testing.T) { // Ensure that a database that is too small returns an error. func TestDBFileTooSmall(t *testing.T) { - withDBFile(func(db *DB, path string) { - // corrupt the database - err := os.Truncate(path, int64(os.Getpagesize())) - assert.NoError(t, err) + withOpenDB(func(db *DB, path string) { + db.Close() - err = db.Open(path, 0666) - assert.Equal(t, err, &Error{"file size too small", nil}) + // corrupt the database + assert.NoError(t, os.Truncate(path, int64(os.Getpagesize()))) + + err := db.Open(path, 0666) + assert.Equal(t, errors.New("file size too small"), err) }) } @@ -108,7 +109,7 @@ func TestDBCorruptMeta0(t *testing.T) { // Open the database. err = db.Open(path, 0666) - assert.Equal(t, err, &Error{"meta error", ErrInvalid}) + assert.Equal(t, err, errors.New("meta error: invalid database")) }) } diff --git a/error.go b/error.go deleted file mode 100644 index f88f47e..0000000 --- a/error.go +++ /dev/null @@ -1,71 +0,0 @@ -package bolt - -var ( - // ErrInvalid is returned when a data file is not a Bolt-formatted database. - ErrInvalid = &Error{"Invalid database", nil} - - // ErrVersionMismatch is returned when the data file was created with a - // different version of Bolt. - ErrVersionMismatch = &Error{"version mismatch", nil} - - // ErrDatabaseNotOpen is returned when a DB instance is accessed before it - // is opened or after it is closed. - ErrDatabaseNotOpen = &Error{"database not open", nil} - - // ErrDatabaseOpen is returned when opening a database that is - // already open. - ErrDatabaseOpen = &Error{"database already open", nil} - - // ErrTxNotWritable is returned when performing a write operation on a - // read-only transaction. - ErrTxNotWritable = &Error{"tx not writable", nil} - - // ErrTxClosed is returned when committing or rolling back a transaction - // that has already been committed or rolled back. - ErrTxClosed = &Error{"tx closed", nil} - - // ErrBucketNotFound is returned when trying to access a bucket that has - // not been created yet. - ErrBucketNotFound = &Error{"bucket not found", nil} - - // ErrBucketExists is returned when creating a bucket that already exists. - ErrBucketExists = &Error{"bucket already exists", nil} - - // ErrBucketNameRequired is returned when creating a bucket with a blank name. - ErrBucketNameRequired = &Error{"bucket name required", nil} - - // ErrBucketNameTooLarge is returned when creating a bucket with a name - // that is longer than MaxBucketNameSize. - ErrBucketNameTooLarge = &Error{"bucket name too large", nil} - - // ErrBucketNotWritable is returned when changing data on a bucket - // reference that was created from a read-only transaction. - ErrBucketNotWritable = &Error{"bucket not writable", nil} - - // ErrKeyRequired is returned when inserting a zero-length key. - ErrKeyRequired = &Error{"key required", nil} - - // ErrKeyTooLarge is returned when inserting a key that is larger than MaxKeySize. - ErrKeyTooLarge = &Error{"key too large", nil} - - // ErrValueTooLarge is returned when inserting a value that is larger than MaxValueSize. - ErrValueTooLarge = &Error{"value too large", nil} - - // ErrSequenceOverflow is returned when the next sequence number will be - // larger than the maximum integer size. - ErrSequenceOverflow = &Error{"sequence overflow", nil} -) - -// Error represents an error condition caused by Bolt. -type Error struct { - message string - cause error -} - -// Error returns a string representation of the error. -func (e *Error) Error() string { - if e.cause != nil { - return e.message + ": " + e.cause.Error() - } - return e.message -} diff --git a/error_test.go b/error_test.go deleted file mode 100644 index 0306985..0000000 --- a/error_test.go +++ /dev/null @@ -1,12 +0,0 @@ -package bolt - -import ( - "github.com/stretchr/testify/assert" - "testing" -) - -// Ensure that nested errors are appropriately formatted. -func TestError(t *testing.T) { - e := &Error{"one error", &Error{"two error", nil}} - assert.Equal(t, e.Error(), "one error: two error") -} diff --git a/meta.go b/meta.go index 0be4e94..cc62637 100644 --- a/meta.go +++ b/meta.go @@ -1,7 +1,20 @@ package bolt +import ( + "errors" +) + const magic uint32 = 0xED0CDAED +var ( + // ErrInvalid is returned when a data file is not a Bolt-formatted database. + ErrInvalid = errors.New("invalid database") + + // ErrVersionMismatch is returned when the data file was created with a + // different version of Bolt. + ErrVersionMismatch = errors.New("version mismatch") +) + type meta struct { magic uint32 version uint32 diff --git a/tx.go b/tx.go index 6464c2b..a3ca825 100644 --- a/tx.go +++ b/tx.go @@ -1,10 +1,21 @@ package bolt import ( + "errors" "sort" "unsafe" ) +var ( + // ErrTxNotWritable is returned when performing a write operation on a + // read-only transaction. + ErrTxNotWritable = errors.New("tx not writable") + + // ErrTxClosed is returned when committing or rolling back a transaction + // that has already been committed or rolled back. + ErrTxClosed = errors.New("tx closed") +) + // txid represents the internal transaction identifier. type txid uint64