TEST_F(ReqSuite, original) { buildSample(); mark1(); // test visibility with update { MojDbReq req; // start transaction req.begin(&db, false); mark2(req); // visible within transaction checkMarkWithUpdate(50ul, -2, req); } // invisible after aborted transaction checkMarkWithUpdate(0ul, -2); // test visibility with delete mark1(); { MojDbReq req; // start transaction req.begin(&db, false); deleteMark(50ul, -1, req); // visible within transaction { MojDbQuery query; MojAssertNoErr( query.from(_T("Test:1")) ); MojAssertNoErr( query.where(_T("bar"), MojDbQuery::OpLessThan, 2) ); MojObject update; MojUInt32 count = 0xbaddcafe; MojAssertNoErr( db.merge(query, update, count, MojDb::FlagNone, req) ); EXPECT_EQ( 33ul, count); } } // invisible after aborted transaction { MojDbQuery query; MojAssertNoErr( query.from(_T("Test:1")) ); MojAssertNoErr( query.where(_T("bar"), MojDbQuery::OpLessThan, 2) ); MojObject update; // Note: should not set value to something that will introduce double-update MojUInt32 count = 0xbaddcafe; MojAssertNoErr( db.merge(query, update, count) ); EXPECT_EQ( 83ul, count); } }
TEST_F(ReqSuite, deleteUpdateRollback) { buildSample(); mark1(); { MojDbReq req; // start transaction req.begin(&db, false); checkMarkWithUpdate(50ul, -1, req); checkMarkWithUpdate(0ul, -3, req); deleteMark(50ul, -1, req); checkMarkWithUpdate(0ul, -1, req); checkMarkWithUpdate(0ul, -3, req); mark3(req); checkMarkWithUpdate(0ul, -1, req); checkMarkWithUpdate(33ul, -3, req); } checkMarkWithUpdate(50ul, -1); checkMarkWithUpdate(0ul, -3); }
TEST_F(ReqSuite, originalEq) { buildSample(); mark1(); // test visibility with update { MojDbReq req; // start transaction req.begin(&db, false); mark2(req); // visible within transaction checkMarkWithUpdate(50ul, -2, req); } // invisible after aborted transaction checkMarkWithUpdate(0ul, -2); // test visibility with delete mark1(); { MojDbReq req; // start transaction req.begin(&db, false); deleteMark(50ul, -1, req); // visible within transaction checkMarkWithUpdate(0ul, -1, req); } // invisible after aborted transaction checkMarkWithUpdate(50ul, -1); }
TEST_F(ReqSuite, visibility) { buildSample(); mark1(); MojDbReq req; // start transaction req.begin(&db, false); checkMarkWithUpdate(50ul, -1, req); checkMarkWithUpdate(0ul, -2, req); mark2(req); checkMarkWithUpdate(50ul, -2, req); }
TEST_F(ReqSuite, updateRollback) { buildSample(); mark1(); { MojDbReq req; // start transaction req.begin(&db, false); checkMarkWithUpdate(50ul, -1, req); checkMarkWithUpdate(0ul, -2, req); mark2(req); checkMarkWithUpdate(50ul, -2, req); } checkMarkWithUpdate(50ul, -1); checkMarkWithUpdate(0ul, -2); }
MojErr MojDb::drop(const MojChar* path) { MojAssert(path); MojLogTrace(s_log); MojErr err = requireOpen(); MojErrCheck(err); MojDbReq req; err = req.begin(this, true); MojErrCheck(err); err = m_storageEngine->drop(path, req.txn()); MojErrCheck(err); err = req.end(); MojErrCheck(err); err = close(); MojErrCheck(err); return MojErrNone; }
MojErr MojDb::open(const MojChar* path, MojDbStorageEngine* engine) { MojAssert(path); MojLogTrace(s_log); MojErr err = requireNotOpen(); MojErrCheck(err); MojLogInfo(s_log, _T("opening: '%s'..."), path); MojAutoCloser<MojDb> closer(this); m_isOpen = true; // check the database version number and bail if there's a mismatch err = checkDbVersion(path); MojErrCheck(err); // engine if (engine == NULL) { err = createEngine(); MojErrCheck(err); MojAssert(m_storageEngine.get()); err = m_storageEngine->configure(m_conf); MojErrCheck(err); err = m_storageEngine->open(path); MojErrCheck(err); } else { m_storageEngine.reset(engine); } MojDbReq req; err = req.begin(this, true); MojErrCheck(err); // db MojLogInfo(s_log, _T("Open Database: '%s'"), ObjDbName); err = m_storageEngine->openDatabase(ObjDbName, req.txn(), m_objDb); MojErrCheck(err); MojAssert(m_objDb.get()); // seq MojLogInfo(s_log, _T("Open Database: '%s'"), IdSeqName); err = m_storageEngine->openSequence(IdSeqName, req.txn(), m_idSeq); MojErrCheck(err); MojAssert(m_idSeq.get()); // kinds MojLogInfo(s_log, _T("Open Kind Engine")); err = m_kindEngine.open(this, req); MojLogInfo(s_log, _T("Kind Opened...")); MojErrCheck(err); // perms MojLogInfo(s_log, _T("Open Permissions")); err = m_permissionEngine.open(m_conf, this, req); MojErrCheck(err); // quota err = m_quotaEngine.open(m_conf, this, req); MojErrCheck(err); err = req.end(); MojErrCheck(err); // idgen err = m_idGenerator.init(); MojErrCheck(err); closer.release(); MojLogInfo(s_log, _T("open completed")); return MojErrNone; }
MojErr MojDbTxnTest::run() { MojErr err; MojDb db; err = MojDbTestEnv::run(MojDbTestDir); MojTestErrCheck(err); // open err = db.open(MojDbTestDir, env()); MojTestErrCheck(err); // add type MojObject obj; err = obj.fromJson(MojKindStr); MojTestErrCheck(err); err = db.putKind(obj); MojTestErrCheck(err); for (int i = 0; i < 100; ++i) { MojObject obj; MojErr err = obj.putString(MojDb::KindKey, _T("Test:1")); MojTestErrCheck(err); err = obj.put(_T("foo"), (i + 25) % 100); MojTestErrCheck(err); err = obj.put(_T("bar"), i % 3); MojTestErrCheck(err); err = db.put(obj); MojTestErrCheck(err); } // db: x0 = (25, 0), (26, 1), (27, 2), (28, 0) .. x74 = (99,2), x75 = (0,0) .. x99 = (24,0) { MojDbQuery query; err = query.from(_T("Test:1")); MojTestErrCheck(err); err = query.where(_T("foo"), MojDbQuery::OpLessThan, 50); MojTestErrCheck(err); MojObject update; err = update.put(_T("bar"), -1); MojTestErrCheck(err); MojUInt32 count = 0; err = db.merge(query, update, count); MojTestErrCheck(err); MojTestAssert(count == 50); } // db: x0 = (25, -1) .. x24 = (49,-1), x25 = (50,1)i .. x74 = (99,2), x75 = (0,-1) .. x99 = (24, -1) // test visibility with update { MojDbReq req; // start transaction req.begin(&db, false); MojDbQuery query; err = query.from(_T("Test:1")); MojTestErrCheck(err); err = query.where(_T("bar"), MojDbQuery::OpEq, -1); MojTestErrCheck(err); MojObject update; err = update.put(_T("bar"), -2); MojTestErrCheck(err); MojUInt32 count = 0; err = db.merge(query, update, count, MojDb::FlagNone, req); MojTestErrCheck(err); MojTestAssert(count == 50); // txn: x0 = (25, -2) .. x24 = (49,-2), x25 = (50,1) .. x74 = (99,2), x75 = (0,-2) .. x99 = (24, -2) // visible within transaction { MojDbQuery query; err = query.from(_T("Test:1")); MojTestErrCheck(err); err = query.where(_T("bar"), MojDbQuery::OpEq, -2); MojTestErrCheck(err); MojObject update; err = update.put(_T("bar"), -2); MojTestErrCheck(err); MojUInt32 count = 0; err = db.merge(query, update, count, MojDb::FlagNone, req); MojTestErrCheck(err); MojTestAssert(count == 50); } // With BerkeleyDB parallel transaction is locked // invisible outside of transaction if (engineName().compare(_T("leveldb")) == 0) { MojDbQuery query; err = query.from(_T("Test:1")); MojTestErrCheck(err); err = query.where(_T("bar"), MojDbQuery::OpEq, -2); MojTestErrCheck(err); MojObject update; err = update.put(_T("bar"), -2); MojTestErrCheck(err); MojUInt32 count = 0; err = db.merge(query, update, count); MojTestErrCheck(err); MojTestAssert(count == 0); } } // invisible after aborted transaction { MojDbQuery query; err = query.from(_T("Test:1")); MojTestErrCheck(err); err = query.where(_T("bar"), MojDbQuery::OpEq, -2); MojTestErrCheck(err); MojObject update; err = update.put(_T("bar"), -2); MojTestErrCheck(err); MojUInt32 count = 0; err = db.merge(query, update, count); MojTestErrCheck(err); MojTestAssert(count == 0); } // test visibility with delete { MojDbReq req; // start transaction req.begin(&db, false); MojDbQuery query; err = query.from(_T("Test:1")); MojTestErrCheck(err); err = query.where(_T("bar"), MojDbQuery::OpEq, -1); MojTestErrCheck(err); MojUInt32 count = 0; err = db.del(query, count, MojDb::FlagNone, req); MojTestErrCheck(err); MojTestAssert(count == 50); // txn: x25 = (50,1) .. x74 = (99,2) // visible within transaction { MojDbQuery query; err = query.from(_T("Test:1")); MojTestErrCheck(err); err = query.where(_T("bar"), MojDbQuery::OpLessThan, 2); MojTestErrCheck(err); MojObject update; err = update.put(_T("bar"), -3); MojTestErrCheck(err); MojUInt32 count = 0; err = db.merge(query, update, count, MojDb::FlagNone, req); MojTestErrCheck(err); MojTestAssert(count == 33); } // With BerkeleyDB parallel transaction is locked // invisible outside of transaction if (engineName().compare(_T("leveldb")) == 0) { MojDbQuery query; err = query.from(_T("Test:1")); MojTestErrCheck(err); err = query.where(_T("bar"), MojDbQuery::OpLessThan, 2); MojTestErrCheck(err); MojObject update; err = update.put(_T("bar"), -3); MojTestErrCheck(err); MojUInt32 count = 0; err = db.merge(query, update, count); MojTestErrCheck(err); MojTestAssert(count == 83); } } // invisible after aborted transaction { MojDbQuery query; err = query.from(_T("Test:1")); MojTestErrCheck(err); err = query.where(_T("bar"), MojDbQuery::OpLessThan, 2); MojTestErrCheck(err); MojObject update; // Note that if we change bar=1 here we might get double-update when // record we just updated moved into records range ahead of our current // cursor position MojUInt32 count = 0; err = db.merge(query, update, count); MojTestErrCheck(err); MojTestAssert(count == 83); } err = db.close(); MojTestErrCheck(err); return MojErrNone; }