aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO10
-rw-r--r--bucket.go20
-rw-r--r--cursor.go40
-rw-r--r--db.go29
-rw-r--r--meta.go2
-rw-r--r--page.go1
-rw-r--r--rwtransaction.go20
-rw-r--r--transaction.go70
8 files changed, 90 insertions, 102 deletions
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..ae1dc9f
--- /dev/null
+++ b/TODO
@@ -0,0 +1,10 @@
+TODO
+====
+X Open DB.
+X Initialize transaction.
+- Cursor First, Goto(key), Next
+- RWTransaction.insert()
+ - page split
+ - rebalance
+ - adjust cursors
+- RWTransaction Commmit
diff --git a/bucket.go b/bucket.go
index f9c3566..39ab551 100644
--- a/bucket.go
+++ b/bucket.go
@@ -5,6 +5,8 @@ type bucketid uint32
type Bucket struct {
*bucket
name string
+ transaction Transaction,
+ cursors []*Cursor,
}
type bucket struct {
@@ -15,3 +17,21 @@ type bucket struct {
leafs pgid
entries uint64
}
+
+func (b *Bucket) Close() error {
+ // TODO: Close cursors.
+ return nil
+}
+
+func (b *Bucket) Cursor() (*Cursor, error) {
+ if b.transaction == nil {
+ return nil, InvalidBucketError
+ }
+
+ c := &Cursor{
+ bucket: b,
+ stack: make([]elem, 0),
+ }
+
+ return nil
+}
diff --git a/cursor.go b/cursor.go
index d1990dc..b93178e 100644
--- a/cursor.go
+++ b/cursor.go
@@ -1,29 +1,24 @@
package bolt
type Cursor struct {
- transaction *Transaction
bucket *Bucket
- stack []stackelem
+ stack []elem
}
-type stackelem struct {
+// elem represents a node on a page that's on the cursor's stack.
+type elem struct {
page *page
index int
}
-func (c *Cursor) Transaction() *Transaction {
- return c.transaction
-}
-
func (c *Cursor) Bucket() *Bucket {
return c.bucket
}
-func (c *Cursor) Get(key []byte) ([]byte, error) {
- // TODO: Move to key
- // TODO: If it doesn't exist, return nil, nil
- // TODO: Otherwise return node key+data.
- return nil, nil
+// First moves the cursor to the first item in the bucket and returns its key and data.
+func (c *Cursor) First() ([]byte, []byte, error) {
+ // TODO: Traverse to the first key.
+ return nil, nil, nil
}
// Move the cursor to the next key/value.
@@ -31,27 +26,12 @@ func (c *Cursor) Next() ([]byte, []byte, error) {
return nil, nil, nil
}
-// First moves the cursor to the first item in the bucket and returns its key and data.
-func (c *Cursor) First() ([]byte, []byte, error) {
- // TODO: Traverse to the first key.
- return nil, nil, nil
-}
-
-// Set the cursor on a specific data item.
-// (bool return is whether it is exact).
-func (c *Cursor) set(key []byte, data []byte, op int) (error, bool) {
+// Goto positions the cursor at a specific key.
+func (c *Cursor) Goto(key []byte) ([]byte, error) {
// TODO(benbjohnson): Optimize for specific use cases.
// TODO: Check if len(key) > 0.
// TODO: Start from root page and traverse to correct page.
- return nil, false
-}
-
-func (c *Cursor) insert(key []byte, data []byte) error {
- // TODO: If there is not enough space on page for key+data then split.
- // TODO: Move remaining data on page forward.
- // TODO: Write leaf node to current location.
- // TODO: Adjust available page size.
- return nil
+ return nil, nil
}
diff --git a/db.go b/db.go
index 2a90061..95149a2 100644
--- a/db.go
+++ b/db.go
@@ -217,18 +217,8 @@ func (db *DB) Transaction() (*Transaction, error) {
}
// Create a transaction associated with the database.
- t := &Transaction{
- db: db,
- meta: db.meta(),
- buckets: make(map[string]*Bucket),
- cursors: make(map[uint32]*Cursor),
- }
-
- // Save references to the sys•free and sys•buckets buckets.
- t.sysfree.transaction = t
- t.sysfree.bucket = &t.meta.free
- t.sysbuckets.transaction = t
- t.sysbuckets.bucket = &t.meta.buckets
+ t := &Transaction{}
+ t.init(db, db.meta())
return t, nil
}
@@ -236,16 +226,21 @@ func (db *DB) Transaction() (*Transaction, error) {
// RWTransaction creates a read/write transaction.
// Only one read/write transaction is allowed at a time.
func (db *DB) RWTransaction() (*RWTransaction, error) {
+ db.Lock()
+ defer db.Unlock()
+
// TODO: db.writerMutex.Lock()
// TODO: Add unlock to RWTransaction.Commit() / Abort()
- t := &RWTransaction{}
-
- // Exit if a read-write transaction is currently in progress.
- if db.transaction != nil {
- return nil, TransactionInProgressError
+ // Exit if the database is not open yet.
+ if !db.opened {
+ return nil, DatabaseNotOpenError
}
+ // Create a transaction associated with the database.
+ t := &RWTransaction{}
+ t.init(db, db.meta())
+
return t, nil
}
diff --git a/meta.go b/meta.go
index 881417a..be70993 100644
--- a/meta.go
+++ b/meta.go
@@ -10,7 +10,7 @@ const version uint32 = 1
type meta struct {
magic uint32
version uint32
- buckets bucket
+ sys bucket
pageSize uint32
pgid pgid
txnid txnid
diff --git a/page.go b/page.go
index ba39dc5..c26b957 100644
--- a/page.go
+++ b/page.go
@@ -1,7 +1,6 @@
package bolt
import (
- "bytes"
"unsafe"
)
diff --git a/rwtransaction.go b/rwtransaction.go
index 2c0837f..b0123f5 100644
--- a/rwtransaction.go
+++ b/rwtransaction.go
@@ -5,8 +5,15 @@ package bolt
type RWTransaction struct {
Transaction
- dirtyPages map[int]*page
- freelist []pgno
+ dirtyPages map[pgid]*page
+ freelist []pgid
+}
+
+// init initializes the transaction and associates it with a database.
+func (t *RWTransaction) init(db *DB, meta *meta) {
+ t.dirtyPages = make(map[pgid]*page)
+ t.freelist = make([]pgid)
+ t.Transaction.init(db, meta)
}
// TODO: Allocate scratch meta page.
@@ -232,3 +239,12 @@ func (t *RWTransaction) allocate(count int) (*page, error) {
// TODO: If no free pages are available, resize the mmap to allocate more.
return nil, nil
}
+
+
+func (t *RWTransaction) insert(key []byte, data []byte) error {
+ // TODO: If there is not enough space on page for key+data then split.
+ // TODO: Move remaining data on page forward.
+ // TODO: Write leaf node to current location.
+ // TODO: Adjust available page size.
+ return nil
+}
diff --git a/transaction.go b/transaction.go
index b79f3e7..8371624 100644
--- a/transaction.go
+++ b/transaction.go
@@ -22,31 +22,21 @@ type txnid uint64
type Transaction struct {
id int
db *DB
- dirty bool
- spilled bool
- err error
meta *meta
- sysfree Bucket
- sysbuckets Bucket
+ sys Bucket
buckets map[string]*Bucket
- cursors map[uint32]*Cursor
-
- pgno int
- freePages []pgno
- spillPages []pgno
- dirtyList []pgno
- reader *reader
- // Implicit from slices? TODO: MDB_dbi mt_numdbs;
- dirty_room int
}
// init initializes the transaction and associates it with a database.
-func (t *Transaction) init(db *DB, meta *meta) error {
-
+func (t *Transaction) init(db *DB, meta *meta) {
+ t.db = db
+ t.meta = meta
+ t.buckets = make(map[string]*Bucket)
+ t.sys.transaction = t
+ t.sys.bucket = &t.meta.sys
}
func (t *Transaction) Close() error {
- // TODO: Close cursors.
// TODO: Close buckets.
return nil
}
@@ -57,66 +47,44 @@ func (t *Transaction) DB() *DB {
// Bucket retrieves a bucket by name.
func (t *Transaction) Bucket(name string) (*Bucket, error) {
- if strings.HasPrefix(name, "sys*") {
- return nil, &Error{"system buckets are not available", nil}
- }
-
return t.bucket(name)
}
func (t *Transaction) bucket(name string) (*Bucket, error) {
- // TODO: if ((flags & VALID_FLAGS) != flags) return EINVAL;
- // TODO: if (txn->mt_flags & MDB_TXN_ERROR) return MDB_BAD_TXN;
-
- // Return bucket if it's already been found.
+ // Return bucket if it's already been looked up.
if b := t.buckets[name]; b != nil {
return b, nil
}
- // Open a cursor for the system bucket.
- c, err := t.Cursor(&t.sysbuckets)
- if err != nil {
- return nil, err
- }
-
- // Retrieve bucket data.
- data, err := c.Get([]byte(name))
+ // Retrieve bucket data from the system bucket.
+ data, err := c.get(&t.sys, []byte(name))
if err != nil {
return nil, err
} else if data == nil {
return nil, &Error{"bucket not found", nil}
}
- // TODO: Verify.
- // MDB_node *node = NODEPTR(mc.mc_pg[mc.mc_top], mc.mc_ki[mc.mc_top]);
- // if (!(node->mn_flags & F_SUBDATA))
- // return MDB_INCOMPATIBLE;
+ // Create a bucket that overlays the data.
+ b := &Bucket{
+ bucket: (*bucket)(unsafe.Pointer(&data[0])),
+ name: name,
+ transaction: t,
+ }
+ t.buckets[name] = b
- return nil, nil
+ return b, nil
}
// Cursor creates a cursor associated with a given bucket.
func (t *Transaction) Cursor(b *Bucket) (*Cursor, error) {
if b == nil {
return nil, &Error{"bucket required", nil}
- } else if t.db == nil {
- return nil, InvalidTransactionError
- }
-
- // TODO: if !(txn->mt_dbflags[dbi] & DB_VALID) return InvalidBucketError
- // TODO: if (txn->mt_flags & MDB_TXN_ERROR) return BadTransactionError
-
- // Return existing cursor for the bucket if one exists.
- if c := t.cursors[b.id]; c != nil {
- return c, nil
- }
+ } else
// Create a new cursor and associate it with the transaction and bucket.
c := &Cursor{
transaction: t,
bucket: b,
- top: -1,
- pages: []*page{},
}
// Set the first page if available.