mirror of https://github.com/etcd-io/bbolt.git
Error refactoring.
Fixed up a few error issues and refactored out the Error type.pull/34/head
parent
7ea635c8fc
commit
59fde2f664
34
bucket.go
34
bucket.go
|
@ -2,6 +2,40 @@ package bolt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"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.
|
// Bucket represents a collection of key/value pairs inside the database.
|
||||||
|
|
|
@ -2,6 +2,7 @@ package bolt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -203,12 +204,12 @@ func TestBucketForEachShortCircuit(t *testing.T) {
|
||||||
err := tx.Bucket("widgets").ForEach(func(k, v []byte) error {
|
err := tx.Bucket("widgets").ForEach(func(k, v []byte) error {
|
||||||
index++
|
index++
|
||||||
if bytes.Equal(k, []byte("baz")) {
|
if bytes.Equal(k, []byte("baz")) {
|
||||||
return &Error{"marker", nil}
|
return errors.New("marker")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
assert.Equal(t, err, &Error{"marker", nil})
|
assert.Equal(t, errors.New("marker"), err)
|
||||||
assert.Equal(t, index, 2)
|
assert.Equal(t, 2, index)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
25
db.go
25
db.go
|
@ -1,6 +1,7 @@
|
||||||
package bolt
|
package bolt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
@ -15,6 +16,16 @@ const minMmapSize = 1 << 22 // 4MB
|
||||||
// The largest step that can be taken when remapping the mmap.
|
// The largest step that can be taken when remapping the mmap.
|
||||||
const maxMmapStep = 1 << 30 // 1GB
|
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.
|
// 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 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.
|
// 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.
|
// Initialize the database if it doesn't exist.
|
||||||
if info, err := db.file.Stat(); err != nil {
|
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 {
|
} else if info.Size() == 0 {
|
||||||
// Initialize new files with meta pages.
|
// Initialize new files with meta pages.
|
||||||
if err := db.init(); err != nil {
|
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 {
|
if _, err := db.file.ReadAt(buf[:], 0); err == nil {
|
||||||
m := db.pageInBuffer(buf[:], 0).meta()
|
m := db.pageInBuffer(buf[:], 0).meta()
|
||||||
if err := m.validate(); err != nil {
|
if err := m.validate(); err != nil {
|
||||||
return &Error{"meta error", err}
|
return fmt.Errorf("meta error: %s", err)
|
||||||
}
|
}
|
||||||
db.pageSize = int(m.pageSize)
|
db.pageSize = int(m.pageSize)
|
||||||
}
|
}
|
||||||
|
@ -140,9 +151,9 @@ func (db *DB) mmap(minsz int) error {
|
||||||
|
|
||||||
info, err := db.file.Stat()
|
info, err := db.file.Stat()
|
||||||
if err != nil {
|
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 {
|
} 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.
|
// Ensure the size is at least the minimum size.
|
||||||
|
@ -163,10 +174,10 @@ func (db *DB) mmap(minsz int) error {
|
||||||
|
|
||||||
// Validate the meta pages.
|
// Validate the meta pages.
|
||||||
if err := db.meta0.validate(); err != nil {
|
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 {
|
if err := db.meta1.validate(); err != nil {
|
||||||
return &Error{"meta1 error", err}
|
return fmt.Errorf("meta1 error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -529,7 +540,7 @@ func (db *DB) allocate(count int) (*page, error) {
|
||||||
var minsz = int((p.id+pgid(count))+1) * db.pageSize
|
var minsz = int((p.id+pgid(count))+1) * db.pageSize
|
||||||
if minsz >= len(db.data) {
|
if minsz >= len(db.data) {
|
||||||
if err := db.mmap(minsz); err != nil {
|
if err := db.mmap(minsz); err != nil {
|
||||||
return nil, &Error{"mmap allocate error", err}
|
return nil, fmt.Errorf("mmap allocate error: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
29
db_test.go
29
db_test.go
|
@ -1,6 +1,7 @@
|
||||||
package bolt
|
package bolt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
@ -8,6 +9,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"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.
|
// Ensure that the database returns an error if the file handle cannot be open.
|
||||||
func TestDBOpenFileError(t *testing.T) {
|
func TestDBOpenFileError(t *testing.T) {
|
||||||
withDBFile(func(db *DB, path string) {
|
withDB(func(db *DB, path string) {
|
||||||
exp := &os.PathError{
|
|
||||||
Op: "open",
|
|
||||||
Path: path + "/youre-not-my-real-parent",
|
|
||||||
Err: syscall.ENOTDIR,
|
|
||||||
}
|
|
||||||
err := db.Open(path+"/youre-not-my-real-parent", 0666)
|
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.
|
// Ensure that a database that is too small returns an error.
|
||||||
func TestDBFileTooSmall(t *testing.T) {
|
func TestDBFileTooSmall(t *testing.T) {
|
||||||
withDBFile(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
// corrupt the database
|
db.Close()
|
||||||
err := os.Truncate(path, int64(os.Getpagesize()))
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = db.Open(path, 0666)
|
// corrupt the database
|
||||||
assert.Equal(t, err, &Error{"file size too small", nil})
|
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.
|
// Open the database.
|
||||||
err = db.Open(path, 0666)
|
err = db.Open(path, 0666)
|
||||||
assert.Equal(t, err, &Error{"meta error", ErrInvalid})
|
assert.Equal(t, err, errors.New("meta error: invalid database"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
71
error.go
71
error.go
|
@ -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
|
|
||||||
}
|
|
|
@ -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")
|
|
||||||
}
|
|
13
meta.go
13
meta.go
|
@ -1,7 +1,20 @@
|
||||||
package bolt
|
package bolt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
const magic uint32 = 0xED0CDAED
|
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 {
|
type meta struct {
|
||||||
magic uint32
|
magic uint32
|
||||||
version uint32
|
version uint32
|
||||||
|
|
11
tx.go
11
tx.go
|
@ -1,10 +1,21 @@
|
||||||
package bolt
|
package bolt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"sort"
|
"sort"
|
||||||
"unsafe"
|
"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.
|
// txid represents the internal transaction identifier.
|
||||||
type txid uint64
|
type txid uint64
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue