mirror of https://github.com/etcd-io/bbolt.git
commit
b17d078d4f
|
@ -1,7 +1,5 @@
|
||||||
package bolt
|
package bolt
|
||||||
|
|
||||||
const MaxBucketNameSize = 255
|
|
||||||
|
|
||||||
type Bucket struct {
|
type Bucket struct {
|
||||||
*bucket
|
*bucket
|
||||||
name string
|
name string
|
||||||
|
|
7
const.go
7
const.go
|
@ -1,8 +1,9 @@
|
||||||
package bolt
|
package bolt
|
||||||
|
|
||||||
const Version = 1
|
const version = 1
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MaxKeySize = 0x8000
|
MaxBucketNameSize = 255
|
||||||
MaxDataSize = 0xffffffff
|
MaxKeySize = 32768
|
||||||
|
MaxDataSize = 4294967295
|
||||||
)
|
)
|
||||||
|
|
96
db.go
96
db.go
|
@ -14,12 +14,6 @@ const (
|
||||||
|
|
||||||
const minPageSize = 0x1000
|
const minPageSize = 0x1000
|
||||||
|
|
||||||
var (
|
|
||||||
DatabaseNotOpenError = &Error{"db is not open", nil}
|
|
||||||
DatabaseAlreadyOpenedError = &Error{"db already open", nil}
|
|
||||||
TransactionInProgressError = &Error{"writable transaction is already in progress", nil}
|
|
||||||
)
|
|
||||||
|
|
||||||
type DB struct {
|
type DB struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
opened bool
|
opened bool
|
||||||
|
@ -157,9 +151,9 @@ func (db *DB) init() error {
|
||||||
// Initialize the meta page.
|
// Initialize the meta page.
|
||||||
m := p.meta()
|
m := p.meta()
|
||||||
m.magic = magic
|
m.magic = magic
|
||||||
m.version = Version
|
m.version = version
|
||||||
m.pageSize = uint32(db.pageSize)
|
m.pageSize = uint32(db.pageSize)
|
||||||
m.version = Version
|
m.version = version
|
||||||
m.free = 2
|
m.free = 2
|
||||||
m.sys = 3
|
m.sys = 3
|
||||||
m.pgid = 4
|
m.pgid = 4
|
||||||
|
@ -239,6 +233,92 @@ func (db *DB) RWTransaction() (*RWTransaction, error) {
|
||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bucket retrieves a reference to a bucket.
|
||||||
|
func (db *DB) Bucket(name string) (*Bucket, error) {
|
||||||
|
t, err := db.Transaction()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer t.Close()
|
||||||
|
return t.Bucket(name), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buckets retrieves a list of all buckets in the database.
|
||||||
|
func (db *DB) Buckets() ([]*Bucket, error) {
|
||||||
|
t, err := db.Transaction()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer t.Close()
|
||||||
|
return t.Buckets(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateBucket creates a new bucket in the database.
|
||||||
|
func (db *DB) CreateBucket(name string) error {
|
||||||
|
t, err := db.RWTransaction()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := t.CreateBucket(name); err != nil {
|
||||||
|
t.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteBucket removes a bucket from the database.
|
||||||
|
func (db *DB) DeleteBucket(name string) error {
|
||||||
|
t, err := db.RWTransaction()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := t.DeleteBucket(name); err != nil {
|
||||||
|
t.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves the value for a key in a bucket.
|
||||||
|
func (db *DB) Get(name string, key []byte) ([]byte, error) {
|
||||||
|
t, err := db.Transaction()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer t.Close()
|
||||||
|
return t.Get(name, key), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put sets the value for a key in a bucket.
|
||||||
|
func (db *DB) Put(name string, key []byte, value []byte) error {
|
||||||
|
t, err := db.RWTransaction()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := t.Put(name, key, value); err != nil {
|
||||||
|
t.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return t.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removes a key from a bucket.
|
||||||
|
func (db *DB) Delete(name string, key []byte) error {
|
||||||
|
t, err := db.RWTransaction()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := t.Delete(name, key); err != nil {
|
||||||
|
t.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return t.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
// page retrieves a page reference from the mmap based on the current page size.
|
// page retrieves a page reference from the mmap based on the current page size.
|
||||||
func (db *DB) page(id pgid) *page {
|
func (db *DB) page(id pgid) *page {
|
||||||
return (*page)(unsafe.Pointer(&db.data[id*pgid(db.pageSize)]))
|
return (*page)(unsafe.Pointer(&db.data[id*pgid(db.pageSize)]))
|
||||||
|
|
|
@ -123,16 +123,16 @@ func TestDBCorruptMeta0(t *testing.T) {
|
||||||
withMockDB(func(db *DB, mockos *mockos, mocksyscall *mocksyscall, path string) {
|
withMockDB(func(db *DB, mockos *mockos, mocksyscall *mocksyscall, path string) {
|
||||||
var m meta
|
var m meta
|
||||||
m.magic = magic
|
m.magic = magic
|
||||||
m.version = Version
|
m.version = version
|
||||||
m.pageSize = 0x8000
|
m.pageSize = 0x8000
|
||||||
|
|
||||||
// Create a file with bad magic.
|
// Create a file with bad magic.
|
||||||
b := make([]byte, 0x10000)
|
b := make([]byte, 0x10000)
|
||||||
p0, p1 := (*page)(unsafe.Pointer(&b[0x0000])), (*page)(unsafe.Pointer(&b[0x8000]))
|
p0, p1 := (*page)(unsafe.Pointer(&b[0x0000])), (*page)(unsafe.Pointer(&b[0x8000]))
|
||||||
p0.meta().magic = 0
|
p0.meta().magic = 0
|
||||||
p0.meta().version = Version
|
p0.meta().version = version
|
||||||
p1.meta().magic = magic
|
p1.meta().magic = magic
|
||||||
p1.meta().version = Version
|
p1.meta().version = version
|
||||||
|
|
||||||
// Mock file access.
|
// Mock file access.
|
||||||
file, metafile := &mockfile{}, &mockfile{}
|
file, metafile := &mockfile{}, &mockfile{}
|
||||||
|
|
10
error.go
10
error.go
|
@ -1,5 +1,15 @@
|
||||||
package bolt
|
package bolt
|
||||||
|
|
||||||
|
var (
|
||||||
|
InvalidError = &Error{"Invalid database", nil}
|
||||||
|
VersionMismatchError = &Error{"version mismatch", nil}
|
||||||
|
DatabaseNotOpenError = &Error{"db is not open", nil}
|
||||||
|
DatabaseAlreadyOpenedError = &Error{"db already open", nil}
|
||||||
|
TransactionInProgressError = &Error{"writable transaction is already in progress", nil}
|
||||||
|
InvalidTransactionError = &Error{"txn is invalid", nil}
|
||||||
|
BucketAlreadyExistsError = &Error{"bucket already exists", nil}
|
||||||
|
)
|
||||||
|
|
||||||
type Error struct {
|
type Error struct {
|
||||||
message string
|
message string
|
||||||
cause error
|
cause error
|
||||||
|
|
8
meta.go
8
meta.go
|
@ -1,12 +1,6 @@
|
||||||
package bolt
|
package bolt
|
||||||
|
|
||||||
var (
|
|
||||||
InvalidError = &Error{"Invalid database", nil}
|
|
||||||
VersionMismatchError = &Error{"version mismatch", nil}
|
|
||||||
)
|
|
||||||
|
|
||||||
const magic uint32 = 0xDEADC0DE
|
const magic uint32 = 0xDEADC0DE
|
||||||
const version uint32 = 1
|
|
||||||
|
|
||||||
type meta struct {
|
type meta struct {
|
||||||
magic uint32
|
magic uint32
|
||||||
|
@ -22,7 +16,7 @@ type meta struct {
|
||||||
func (m *meta) validate() error {
|
func (m *meta) validate() error {
|
||||||
if m.magic != magic {
|
if m.magic != magic {
|
||||||
return InvalidError
|
return InvalidError
|
||||||
} else if m.version != Version {
|
} else if m.version != version {
|
||||||
return VersionMismatchError
|
return VersionMismatchError
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -79,7 +79,7 @@ func (t *RWTransaction) Put(name string, key []byte, value []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RWTransaction) Delete(key []byte) error {
|
func (t *RWTransaction) Delete(name string, key []byte) error {
|
||||||
// TODO: Traverse to the correct node.
|
// TODO: Traverse to the correct node.
|
||||||
// TODO: If missing, exit.
|
// TODO: If missing, exit.
|
||||||
// TODO: Remove node from page.
|
// TODO: Remove node from page.
|
||||||
|
@ -116,17 +116,15 @@ func (t *RWTransaction) Commit() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RWTransaction) Rollback() error {
|
func (t *RWTransaction) Rollback() {
|
||||||
return t.close()
|
t.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RWTransaction) close() error {
|
func (t *RWTransaction) close() {
|
||||||
// Clear temporary pages.
|
// Clear temporary pages.
|
||||||
t.leafs = nil
|
t.leafs = nil
|
||||||
|
|
||||||
// TODO: Release writer lock.
|
// TODO: Release writer lock.
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// allocate returns a contiguous block of memory starting at a given page.
|
// allocate returns a contiguous block of memory starting at a given page.
|
||||||
|
|
|
@ -19,28 +19,12 @@ func TestRWTransaction(t *testing.T) {
|
||||||
func TestTransactionCreateBucket(t *testing.T) {
|
func TestTransactionCreateBucket(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
// Create a bucket.
|
// Create a bucket.
|
||||||
txn, _ := db.RWTransaction()
|
err := db.CreateBucket("widgets")
|
||||||
err := txn.CreateBucket("widgets")
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Commit the transaction.
|
// Read the bucket through a separate transaction.
|
||||||
err = txn.Commit()
|
b, err := db.Bucket("widgets")
|
||||||
|
assert.NotNil(t, b)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Open a separate read-only transaction.
|
|
||||||
rtxn, err := db.Transaction()
|
|
||||||
assert.NotNil(t, txn)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
b := rtxn.Bucket("widgets")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
if assert.NotNil(t, b) {
|
|
||||||
assert.Equal(t, b.Name(), "widgets")
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that an existing bucket cannot be created.
|
|
||||||
func TestTransactionCreateExistingBucket(t *testing.T) {
|
|
||||||
t.Skip("pending")
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
package bolt
|
package bolt
|
||||||
|
|
||||||
var (
|
|
||||||
InvalidTransactionError = &Error{"txn is invalid", nil}
|
|
||||||
BucketAlreadyExistsError = &Error{"bucket already exists", nil}
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ps_modify = 1
|
ps_modify = 1
|
||||||
ps_rootonly = 2
|
ps_rootonly = 2
|
||||||
|
@ -32,9 +27,8 @@ func (t *Transaction) init(db *DB) {
|
||||||
t.sys.read(t.page(t.meta.sys))
|
t.sys.read(t.page(t.meta.sys))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transaction) Close() error {
|
func (t *Transaction) Close() {
|
||||||
// TODO: Close buckets.
|
// TODO: Close buckets.
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transaction) DB() *DB {
|
func (t *Transaction) DB() *DB {
|
||||||
|
@ -56,6 +50,12 @@ func (t *Transaction) Bucket(name string) *Bucket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Buckets retrieves a list of all buckets.
|
||||||
|
func (t *Transaction) Buckets() []*Bucket {
|
||||||
|
warn("[pending] Transaction.Buckets()") // TODO
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Cursor creates a cursor associated with a given bucket.
|
// Cursor creates a cursor associated with a given bucket.
|
||||||
func (t *Transaction) Cursor(name string) *Cursor {
|
func (t *Transaction) Cursor(name string) *Cursor {
|
||||||
b := t.Bucket(name)
|
b := t.Bucket(name)
|
||||||
|
@ -74,8 +74,8 @@ func (t *Transaction) Get(name string, key []byte) []byte {
|
||||||
return c.Get(key)
|
return c.Get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stat returns information about a bucket's internal structure.
|
// stat returns information about a bucket's internal structure.
|
||||||
func (t *Transaction) Stat(name string) *Stat {
|
func (t *Transaction) stat(name string) *Stat {
|
||||||
// TODO
|
// TODO
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue