func scopedUpdate(ns walletdb.Namespace, f func(walletdb.Bucket) error) error { tx, err := ns.Begin(true) if err != nil { str := "cannot begin update" return storeError(ErrDatabase, str, err) } err = f(tx.RootBucket()) if err != nil { rbErr := tx.Rollback() if rbErr != nil { const desc = "rollback failed" serr, ok := err.(Error) if !ok { // This really shouldn't happen. return storeError(ErrDatabase, desc, rbErr) } serr.Desc = desc + ": " + serr.Desc return serr } return err } err = tx.Commit() if err != nil { str := "commit failed" return storeError(ErrDatabase, str, err) } return nil }
func scopedView(ns walletdb.Namespace, f func(walletdb.Bucket) error) error { tx, err := ns.Begin(false) if err != nil { str := "cannot begin view" return storeError(ErrDatabase, str, err) } err = f(tx.RootBucket()) rbErr := tx.Rollback() if err != nil { return err } if rbErr != nil { str := "cannot close view" return storeError(ErrDatabase, str, rbErr) } return nil }
// testManualTxInterface ensures that manual transactions work as expected. func testManualTxInterface(tc *testContext, namespace walletdb.Namespace) bool { // populateValues tests that populating values works as expected. // // When the writable flag is false, a read-only tranasction is created, // standard bucket tests for read-only transactions are performed, and // the Commit function is checked to ensure it fails as expected. // // Otherwise, a read-write transaction is created, the values are // written, standard bucket tests for read-write transactions are // performed, and then the transaction is either commited or rolled // back depending on the flag. populateValues := func(writable, rollback bool, putValues map[string]string) bool { tx, err := namespace.Begin(writable) if err != nil { tc.t.Errorf("Begin: unexpected error %v", err) return false } rootBucket := tx.RootBucket() if rootBucket == nil { tc.t.Errorf("RootBucket: unexpected nil root bucket") _ = tx.Rollback() return false } tc.isWritable = writable if !testBucketInterface(tc, rootBucket) { _ = tx.Rollback() return false } if !writable { // The transaction is not writable, so it should fail // the commit. if err := tx.Commit(); err != walletdb.ErrTxNotWritable { tc.t.Errorf("Commit: unexpected error %v, "+ "want %v", err, walletdb.ErrTxNotWritable) _ = tx.Rollback() return false } // Rollback the transaction. if err := tx.Rollback(); err != nil { tc.t.Errorf("Commit: unexpected error %v", err) return false } } else { if !testPutValues(tc, rootBucket, putValues) { return false } if rollback { // Rollback the transaction. if err := tx.Rollback(); err != nil { tc.t.Errorf("Rollback: unexpected "+ "error %v", err) return false } } else { // The commit should succeed. if err := tx.Commit(); err != nil { tc.t.Errorf("Commit: unexpected error "+ "%v", err) return false } } } return true } // checkValues starts a read-only transaction and checks that all of // the key/value pairs specified in the expectedValues parameter match // what's in the database. checkValues := func(expectedValues map[string]string) bool { // Begin another read-only transaction to ensure... tx, err := namespace.Begin(false) if err != nil { tc.t.Errorf("Begin: unexpected error %v", err) return false } rootBucket := tx.RootBucket() if rootBucket == nil { tc.t.Errorf("RootBucket: unexpected nil root bucket") _ = tx.Rollback() return false } if !testGetValues(tc, rootBucket, expectedValues) { _ = tx.Rollback() return false } // Rollback the read-only transaction. if err := tx.Rollback(); err != nil { tc.t.Errorf("Commit: unexpected error %v", err) return false } return true } // deleteValues starts a read-write transaction and deletes the keys // in the passed key/value pairs. deleteValues := func(values map[string]string) bool { tx, err := namespace.Begin(true) if err != nil { } rootBucket := tx.RootBucket() if rootBucket == nil { tc.t.Errorf("RootBucket: unexpected nil root bucket") _ = tx.Rollback() return false } // Delete the keys and ensure they were deleted. if !testDeleteValues(tc, rootBucket, values) { _ = tx.Rollback() return false } if !testGetValues(tc, rootBucket, rollbackValues(values)) { _ = tx.Rollback() return false } // Commit the changes and ensure it was successful. if err := tx.Commit(); err != nil { tc.t.Errorf("Commit: unexpected error %v", err) return false } return true } // keyValues holds the keys and values to use when putting values // into a bucket. var keyValues = map[string]string{ "umtxkey1": "foo1", "umtxkey2": "foo2", "umtxkey3": "foo3", } // Ensure that attempting populating the values using a read-only // transaction fails as expected. if !populateValues(false, true, keyValues) { return false } if !checkValues(rollbackValues(keyValues)) { return false } // Ensure that attempting populating the values using a read-write // transaction and then rolling it back yields the expected values. if !populateValues(true, true, keyValues) { return false } if !checkValues(rollbackValues(keyValues)) { return false } // Ensure that attempting populating the values using a read-write // transaction and then committing it stores the expected values. if !populateValues(true, false, keyValues) { return false } if !checkValues(keyValues) { return false } // Clean up the keys. if !deleteValues(keyValues) { return false } return true }