mirror of https://github.com/hak5/bolt.git
Initialize transaction/rwtransaction.
parent
2c6b8e0ebf
commit
1baa6d576a
|
@ -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
|
20
bucket.go
20
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
|
||||
}
|
||||
|
|
42
cursor.go
42
cursor.go
|
@ -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
29
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
|
||||
}
|
||||
|
||||
|
|
2
meta.go
2
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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue