TEST_F(ReqSuite, verifySample) { buildSample(); mark1(); checkMarkWithUpdate(50ul, -1); mark2(); checkMarkWithUpdate(50ul, -2); }
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, 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); }
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); }
void c4_SaveContext::SaveIt(c4_HandlerSeq &root_, c4_Allocator **spacePtr_, c4_Bytes &rootWalk_) { d4_assert(_space != 0); const t4_i32 size = _strategy.FileSize(); if (_strategy._failure != 0) return ; const t4_i32 end = _fullScan ? 0 : size - _strategy._baseOffset; if (_differ == 0) { if (_mode != 1) _space->Initialize(); // don't allocate anything inside the file in extend mode if (_mode == 2 && end > 0) { _space->Occupy(1, end - 1); _nextSpace->Occupy(1, end - 1); } // the header is always reserved _space->Occupy(1, 7); _nextSpace->Occupy(1, 7); if (end > 0) { d4_assert(end >= 16); _space->Occupy(end - 16, 16); _nextSpace->Occupy(end - 16, 16); _space->Occupy(end, 8); _nextSpace->Occupy(end, 8); } } //AllocDump("a1", false); //AllocDump("a2", true); // first pass allocates columns and constructs shallow walks c4_Column walk(root_.Persist()); SetWalkBuffer(&walk); CommitSequence(root_, true); SetWalkBuffer(0); CommitColumn(walk); c4_Bytes tempWalk; walk.FetchBytes(0, walk.ColSize(), tempWalk, true); t4_i32 limit = _nextSpace->AllocationLimit(); d4_assert(limit >= 8 || _differ != 0); if (limit < 0) { // 2006-01-12 #2: catch file size exceeding 2 Gb _strategy._failure = - 1; // unusual non-zero value flags this case return ; } bool changed = _fullScan || tempWalk != rootWalk_; rootWalk_ = c4_Bytes(tempWalk.Contents(), tempWalk.Size(), true); _preflight = false; // special-case to avoid saving data if file is logically empty // in that case, the data is 0x80 0x81 0x80 (plus the header) if (!_fullScan && limit <= 11 && _differ == 0) { _space->Initialize(); _nextSpace->Initialize(); changed = false; } if (!changed) return ; //AllocDump("b1", false); //AllocDump("b2", true); if (_differ != 0) { int n = _differ->NewDiffID(); _differ->CreateDiff(n, walk); return ; } d4_assert(_mode != 0 || _fullScan); // this is the place where writing may start // figure out where the new file ends and write a skip tail there t4_i32 end0 = end; // true if the file need not be extended due to internal free space bool inPlace = end0 == limit - 8; if (inPlace) { d4_assert(!_fullScan); _space->Release(end0, 8); _nextSpace->Release(end0, 8); end0 -= 16; // overwrite existing tail markers } else { /* 18-11-2005 write new end marker and flush it before *anything* else! */ if (!_fullScan && end0 < limit) { c4_FileMark mark1(limit, 0); _strategy.DataWrite(limit, &mark1, sizeof mark1); _strategy.DataCommit(0); if (_strategy._failure != 0) return ; } c4_FileMark head(limit + 16-end, _strategy._bytesFlipped, end > 0); _strategy.DataWrite(end, &head, sizeof head); if (end0 < limit) end0 = limit; // create a gap } t4_i32 end1 = end0 + 8; t4_i32 end2 = end1 + 8; if (!_fullScan && !inPlace) { c4_FileMark mark1(end0, 0); _strategy.DataWrite(end0, &mark1, sizeof mark1); #if q4_WIN32 /* March 8, 2002 * On at least NT4 with NTFS, extending a file can cause it to be * rounded up further than expected. To prevent creating a bad * file (since the file does then not end with a marker), the * workaround it so simply accept the new end instead and rewrite. * Note that between these two writes, the file is in a bad state. */ t4_i32 realend = _strategy.FileSize() - _strategy._baseOffset; if (realend > end1) { end0 = limit = realend - 8; end1 = realend; end2 = realend + 8; c4_FileMark mark1a(end0, 0); _strategy.DataWrite(end0, &mark1a, sizeof mark1a); } #endif d4_assert(_strategy.FileSize() == _strategy._baseOffset + end1); } _space->Occupy(end0, 16); _nextSpace->Occupy(end0, 16); // strategy.DataCommit(0); // may be needed, need more info on how FS's work // but this would need more work, since we can't adjust file-mapping here // second pass saves the columns and structure to disk CommitSequence(root_, true); // writes changed columns CommitColumn(walk); //! d4_assert(_curr == 0); d4_assert(_nextPosIndex == _newPositions.GetSize()); if (_fullScan) { c4_FileMark mark1(limit, 0); _strategy.DataWrite(_strategy.FileSize() - _strategy._baseOffset, &mark1, sizeof mark1); c4_FileMark mark2(limit - walk.ColSize(), walk.ColSize()); _strategy.DataWrite(_strategy.FileSize() - _strategy._baseOffset, &mark2, sizeof mark2); return ; } if (inPlace) d4_assert(_strategy.FileSize() == _strategy._baseOffset + end2); else { // make sure the allocated size hasn't changed d4_assert(_nextSpace->AllocationLimit() == limit + 16); d4_assert(end0 >= limit); d4_assert(_strategy.FileSize() - _strategy._baseOffset == end1); } if (walk.Position() == 0 || _strategy._failure != 0) return ; _strategy.DataCommit(0); c4_FileMark mark2(walk.Position(), walk.ColSize()); _strategy.DataWrite(end1, &mark2, sizeof mark2); d4_assert(_strategy.FileSize() - _strategy._baseOffset == end2); // do not alter the file header in extend mode, unless it is new if (!_fullScan && (_mode == 1 || end == 0)) { _strategy.DataCommit(0); c4_FileMark head(end2, _strategy._bytesFlipped, false); d4_assert(head.IsHeader()); _strategy.DataWrite(0, &head, sizeof head); // if the file became smaller, we could shrink it if (limit + 16 < end0) { /* Not yet, this depends on the strategy class being able to truncate, but there is no way to find out whether it does (the solution is to write tail markers in such a way that the file won't grow unnecessarily if it doesn't). The logic will probably be: * write new skip + commit "tails" at limit (no visible effect on file) * overwrite commit tail at end with a skip to this new one (equivalent) * replace header with one pointing to that internal new one (equivalent) * flush (now the file is valid both truncated and not-yet-truncated end = limit; */ } } // if using memory mapped files, make sure the map is no longer in use if (_strategy._mapStart != 0) root_.UnmappedAll(); // commit and tell strategy object what the new file size is, this // may be smaller now, if old data at the end is no longer referenced _strategy.DataCommit(end2); d4_assert(_strategy.FileSize() - _strategy._baseOffset == end2); if (spacePtr_ != 0 && _space != _nextSpace) { d4_assert(*spacePtr_ == _space); delete *spacePtr_; *spacePtr_ = _nextSpace; _nextSpace = 0; } }