Initialize transaction/rwtransaction.

master
Ben Johnson 2014-01-26 15:29:06 -07:00
parent 2c6b8e0ebf
commit 1baa6d576a
8 changed files with 91 additions and 103 deletions

10
TODO Normal file
View File

@ -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

View File

@ -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
}

View File

@ -1,57 +1,37 @@
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
}
// Move the cursor to the next key/value.
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) {
// Move the cursor to the next key/value.
func (c *Cursor) Next() ([]byte, []byte, error) {
return nil, nil, nil
}
// 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
}

29
db.go
View File

@ -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
}

View File

@ -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

View File

@ -1,7 +1,6 @@
package bolt
import (
"bytes"
"unsafe"
)

View File

@ -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
}

View File

@ -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.