aboutsummaryrefslogtreecommitdiff
path: root/transaction.go
diff options
context:
space:
mode:
Diffstat (limited to 'transaction.go')
-rw-r--r--transaction.go262
1 files changed, 104 insertions, 158 deletions
diff --git a/transaction.go b/transaction.go
index 4525a31..384249c 100644
--- a/transaction.go
+++ b/transaction.go
@@ -1,5 +1,9 @@
package bolt
+import (
+ "strings"
+)
+
var TransactionExistingChildError = &Error{"txn already has a child", nil}
var TransactionReadOnlyChildError = &Error{"read-only txn cannot create a child", nil}
@@ -18,87 +22,133 @@ const (
)
type Transaction struct {
- id int
- db *DB
- writable bool
- dirty bool
- spilled bool
- err error
- parent *Transaction
- child *Transaction
- buckets []*txnbucket
+ id int
+ db *DB
+ writable bool
+ dirty bool
+ spilled bool
+ err error
+ meta *meta
+ sysfree Bucket
+ sysbuckets Bucket
+ buckets map[string]*Bucket
+ cursors map[uint32]*Cursor
pgno int
freePages []pgno
spillPages []pgno
dirtyList []pgno
reader *reader
- // TODO: bucketxs []*bucketx
// Implicit from slices? TODO: MDB_dbi mt_numdbs;
dirty_room int
pagestate pagestate
}
-type txnbucket struct {
- bucket *Bucket
- cursor *Cursor
- flags int
+// CreateBucket creates a new bucket.
+func (t *Transaction) CreateBucket(name string, dupsort bool) (*Bucket, error) {
+ // TODO: Check if bucket already exists.
+ // TODO: Put new entry into system bucket.
+
+ /*
+ MDB_db dummy;
+ data.mv_size = sizeof(MDB_db);
+ data.mv_data = &dummy;
+ memset(&dummy, 0, sizeof(dummy));
+ dummy.md_root = P_INVALID;
+ dummy.md_flags = flags & PERSISTENT_FLAGS;
+ rc = mdb_cursor_put(&mc, &key, &data, F_SUBDATA);
+ dbflag |= DB_DIRTY;
+ */
+ return nil, nil
}
-// Transaction begins a nested child transaction. Child transactions are only
-// available to writable transactions and a transaction can only have one child
-// at a time.
-func (t *Transaction) Transaction() (*Transaction, error) {
- // Exit if parent already has a child transaction.
- if t.child != nil {
- return nil, TransactionExistingChildError
- }
- // Exit if using parent for read-only transaction.
- if !t.writable {
- return nil, TransactionReadOnlyChildError
+// DropBucket deletes a bucket.
+func (t *Transaction) DeleteBucket(b *Bucket) error {
+ // TODO: Find bucket.
+ // TODO: Remove entry from system bucket.
+ return nil
+}
+
+// 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}
}
- // TODO: Exit if parent is in an error state.
- // Create the child transaction and attach the parent.
- child := &Transaction{
- id: t.id,
- db: t.db,
- parent: t,
- writable: true,
- pgno: t.pgno,
- pagestate: t.db.pagestate,
+ 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.
+ if b := t.buckets[name]; b != nil {
+ return b, nil
}
- copy(child.buckets, t.buckets)
- // TODO: Remove DB_NEW flag.
- t.child = child
+ // Open a cursor for the system bucket.
+ c, err := t.Cursor(&t.sysbuckets)
+ if err != nil {
+ return nil, err
+ }
- // TODO: wtf?
- // if (env->me_pghead) {
- // size = MDB_IDL_SIZEOF(env->me_pghead);
- // env->me_pghead = mdb_midl_alloc(env->me_pghead[0]);
- // if (env->me_pghead)
- // memcpy(env->me_pghead, ntxn->mnt_pgstate.mf_pghead, size);
- // else
- // rc = ENOMEM;
- // }
+ // Retrieve bucket data.
+ data, err := c.Get([]byte(name))
+ if err != nil {
+ return nil, err
+ } else if data == nil {
+ return nil, &Error{"bucket not found", nil}
+ }
- // TODO: Back up parent transaction's cursors.
- // if t.shadow(child); err != nil {
- // child.reset0()
- // return err
- // }
+ // 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;
- return child, nil
+ return nil, nil
}
+// Cursor creates a cursor associated with a given bucket.
func (t *Transaction) Cursor(b *Bucket) (*Cursor, error) {
- // TODO: if !(txn->mt_dbflags[dbi] & DB_VALID) return InvalidBucketError
- // TODO: if (txn->mt_flags & MDB_TXN_ERROR) return BadTransactionError
+ if b == nil {
+ return nil, &Error{"bucket required", nil}
+ }
// Allow read access to the freelist
// TODO: if (!dbi && !F_ISSET(txn->mt_flags, MDB_TXN_RDONLY))
+ return t.cursor(b)
+}
+
+func (t *Transaction) cursor(b *Bucket) (*Cursor, error) {
+ // 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 b != nil {
+ if c := t.cursors[b.id]; c != nil {
+ return c, nil
+ }
+ }
+
+ // Create a new cursor and associate it with the transaction and bucket.
+ c := &Cursor{
+ transaction: t,
+ bucket: b,
+ }
+ if (b.flags & MDB_DUPSORT) != 0 {
+ c.subcursor = &Cursor{
+ transaction: t,
+ bucket: b,
+ }
+ }
+
+ // Find the root page if the bucket is stale.
+ if (c.bucket.flags & txnb_stale) != 0 {
+ c.findPage(nil, ps_rootonly)
+ }
+
/*
MDB_cursor *mc;
size_t size = sizeof(MDB_cursor);
@@ -1426,110 +1476,6 @@ func (t *Transaction) Put(b Bucket, key []byte, data []byte, flags int) error {
return nil
}
-func (t *Transaction) Bucket(name string, flags int) (*Bucket, error) {
- /*
- MDB_val key, data;
- MDB_dbi i;
- MDB_cursor mc;
- int rc, dbflag, exact;
- unsigned int unused = 0;
- size_t len;
-
- if (txn->mt_dbxs[FREE_DBI].md_cmp == NULL) {
- mdb_default_cmp(txn, FREE_DBI);
- }
-
- if ((flags & VALID_FLAGS) != flags)
- return EINVAL;
- if (txn->mt_flags & MDB_TXN_ERROR)
- return MDB_BAD_TXN;
-
- // main DB?
- if (!name) {
- *dbi = MAIN_DBI;
- if (flags & PERSISTENT_FLAGS) {
- uint16_t f2 = flags & PERSISTENT_FLAGS;
- // make sure flag changes get committed
- if ((txn->mt_dbs[MAIN_DBI].md_flags | f2) != txn->mt_dbs[MAIN_DBI].md_flags) {
- txn->mt_dbs[MAIN_DBI].md_flags |= f2;
- txn->mt_flags |= MDB_TXN_DIRTY;
- }
- }
- mdb_default_cmp(txn, MAIN_DBI);
- return MDB_SUCCESS;
- }
-
- if (txn->mt_dbxs[MAIN_DBI].md_cmp == NULL) {
- mdb_default_cmp(txn, MAIN_DBI);
- }
-
- // Is the DB already open?
- len = strlen(name);
- for (i=2; i<txn->mt_numdbs; i++) {
- if (!txn->mt_dbxs[i].md_name.mv_size) {
- // Remember this free slot
- if (!unused) unused = i;
- continue;
- }
- if (len == txn->mt_dbxs[i].md_name.mv_size &&
- !strncmp(name, txn->mt_dbxs[i].md_name.mv_data, len)) {
- *dbi = i;
- return MDB_SUCCESS;
- }
- }
-
- // If no free slot and max hit, fail
- if (!unused && txn->mt_numdbs >= txn->mt_env->me_maxdbs)
- return MDB_DBS_FULL;
-
- // Cannot mix named databases with some mainDB flags
- if (txn->mt_dbs[MAIN_DBI].md_flags & (MDB_DUPSORT|MDB_INTEGERKEY))
- return (flags & MDB_CREATE) ? MDB_INCOMPATIBLE : MDB_NOTFOUND;
-
- // Find the DB info
- dbflag = DB_NEW|DB_VALID;
- exact = 0;
- key.mv_size = len;
- key.mv_data = (void *)name;
- mdb_cursor_init(&mc, txn, MAIN_DBI, NULL);
- rc = mdb_cursor_set(&mc, &key, &data, MDB_SET, &exact);
- if (rc == MDB_SUCCESS) {
- // make sure this is actually a DB
- 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;
- } else if (rc == MDB_NOTFOUND && (flags & MDB_CREATE)) {
- // Create if requested
- MDB_db dummy;
- data.mv_size = sizeof(MDB_db);
- data.mv_data = &dummy;
- memset(&dummy, 0, sizeof(dummy));
- dummy.md_root = P_INVALID;
- dummy.md_flags = flags & PERSISTENT_FLAGS;
- rc = mdb_cursor_put(&mc, &key, &data, F_SUBDATA);
- dbflag |= DB_DIRTY;
- }
-
- // OK, got info, add to table
- if (rc == MDB_SUCCESS) {
- unsigned int slot = unused ? unused : txn->mt_numdbs;
- txn->mt_dbxs[slot].md_name.mv_data = strdup(name);
- txn->mt_dbxs[slot].md_name.mv_size = len;
- txn->mt_dbxs[slot].md_rel = NULL;
- txn->mt_dbflags[slot] = dbflag;
- memcpy(&txn->mt_dbs[slot], data.mv_data, sizeof(MDB_db));
- *dbi = slot;
- mdb_default_cmp(txn, slot);
- if (!unused) {
- txn->mt_numdbs++;
- }
- }
-
- return rc;
- */
- return nil, nil
-}
-
func (t *Transaction) Stat(b Bucket) *stat {
/*
if (txn == NULL || arg == NULL || dbi >= txn->mt_numdbs)