MojErr MojDbPutHandler::open(const MojObject& conf, MojDb* db, MojDbReq& req) { LOG_TRACE("Entering function %s", __FUNCTION__); MojAssertWriteLocked(db->schemaLock()); m_db = db; // configure MojErr err = configure(conf, req); MojErrCheck(err); // load permissions from db MojDbQuery query; err = query.from(m_kindId); MojErrCheck(err); MojDbCursor cursor; err = db->find(query, cursor, req); MojErrCheck(err); for (;;) { bool found = false; MojObject perm; err = cursor.get(perm, found); MojErrCheck(err); if (!found) break; err = put(perm, req, false); MojErrCheck(err); } return MojErrNone; }
MojErr MojDb::watch(const MojDbQuery& query, MojDbCursor& cursor, WatchSignal::SlotRef watchHandler, bool& firedOut, MojDbReqRef req) { MojLogTrace(s_log); firedOut = false; MojErr err = beginReq(req); MojErrCheck(err); MojRefCountedPtr<MojDbWatcher> watcher(new MojDbWatcher(watchHandler)); MojAllocCheck(watcher.get()); MojDbQuery limitedQuery = query; limitedQuery.limit(1); err = findImpl(limitedQuery, cursor, watcher.get(), req, OpRead); MojErrCheck(err); MojDbStorageItem* item = NULL; bool found = false; cursor.verifymode(false); err = cursor.get(item, found); MojErrCheck(err); if (found) { const MojDbKey& key = cursor.storageQuery()->endKey(); err = watcher->fire(key); MojErrCheck(err); firedOut = true; } err = req->end(false); MojErrCheck(err); return MojErrNone; }
/** * verifyRecords */ MojErr MojDbShardManagerTest::verifyRecords (const MojChar* strKind, MojDb& db, const MojDbShardInfo&, MojUInt32& count) { MojDbQuery query; MojDbCursor cursor; count = 0; MojErr err = query.from(strKind); MojErrCheck(err); err = db.find(query, cursor); MojErrCheck(err); while (true) { bool found; MojObject dbObj; err = cursor.get(dbObj, found); MojErrCheck(err); if (!found) break; ++count; } return MojErrNone; }
MojErr MojDb::merge(const MojDbQuery& query, const MojObject& props, MojUInt32& countOut, MojUInt32 flags, MojDbReqRef req) { MojLogTrace(s_log); countOut = 0; MojErr err = beginReq(req); MojErrCheck(err); MojDbCursor cursor; err = findImpl(query, cursor, NULL, req, OpUpdate); MojErrCheck(err); MojAssert(cursor.txn()); MojUInt32 count = 0; MojUInt32 warns = 0; bool found = false; MojObject prev; for (;;) { // get prev rev from cursor MojDbStorageItem* prevItem = NULL; err = cursor.get(prevItem, found); if (err == MojErrInternalIndexOnFind) { warns++; continue; } MojErrCheck(err); if (!found) break; err = prevItem->toObject(prev, m_kindEngine); MojErrCheck(err); // merge obj into prev MojObject merged; err = mergeInto(merged, props, prev); MojErrCheck(err); // and update the db const MojObject& id = prevItem->id(); err = putObj(id, merged, &prev, prevItem, req, OpUpdate); MojErrCheck(err); ++count; } if (warns > 0) MojLogWarning(s_log, _T("Merge index_warnings: %s; count: %d\n"), query.from().data(), warns); err = cursor.close(); MojErrCheck(err); err = req->end(); MojErrCheck(err); countOut = count; return MojErrNone; }
MojErr MojDbIndex::build(MojDbStorageTxn* txn) { LOG_TRACE("Entering function %s", __FUNCTION__); MojAssert(isOpen()); MojAssert(m_kind && m_kindEngine); MojAssert(m_props.size() > 1); // query for all existing objects of this type and add them to the index. MojDbQuery query; MojErr err = query.from(m_kind->id()); MojErrCheck(err); if (m_includeDeleted) { err = query.includeDeleted(); MojErrCheck(err); } MojDbCursor cursor; MojDbReq adminRequest(true); adminRequest.txn(txn); err = m_kindEngine->find(query, cursor, NULL, adminRequest, OpRead); MojErrCheck(err); for (;;) { MojObject obj; bool found = false; err = cursor.get(obj, found); MojErrCheck(err); if (!found) break; // add this object to the index err = update(&obj, NULL, txn, false); MojErrCheck(err); } err = cursor.close(); MojErrCheck(err); return MojErrNone; }
MojErr MojDbKindEngine::loadKinds(MojDbReq& req) { MojAssert(isOpen()); MojAssertWriteLocked(m_db->m_schemaLock); MojLogTrace(s_log); MojDbQuery query; MojErr err = query.from(KindKindId); MojErrCheck(err); MojDbCursor cursor; err = m_db->find(query, cursor, req); MojErrCheck(err); for (;;) { MojObject obj; bool found = false; err = cursor.get(obj, found); MojErrCheck(err); if (!found) break; // load kind MojErr loadErr = err = putKind(obj, req); MojErrCatchAll(err) { MojString id; bool found = false; MojErr err = obj.get(MojDbServiceDefs::IdKey, id, found); MojErrCheck(err); MojString errStr; MojErrToString(loadErr, errStr); MojLogError(s_log, _T("error loading kind '%s' - %s"), id.data(), errStr.data()); } } err = cursor.close(); MojErrCheck(err); return MojErrNone; }
MojErr MojDbWatchTest::pageTest(MojDb& db) { MojObject id; MojObject idFirst; MojObject idFourth; MojObject idLast; MojInt64 rev; for (int i = 100; i < 150; ++i) { MojErr err = put(db, 100, i, id, rev); MojTestErrCheck(err); if (i == 100) { idFirst = id; } else if (i == 103) { idFourth = id; } else if (i == 149) { idLast = id; } } MojDbQuery query; MojErr err = query.from(_T("WatchTest:1")); MojTestErrCheck(err); err = query.where(_T("foo"), MojDbQuery::OpGreaterThanEq, 100); MojTestErrCheck(err); query.limit(3); MojRefCountedPtr<TestWatcher> watcher(new TestWatcher); MojTestAssert(watcher.get()); MojDbCursor cursor; err = db.find(query, cursor, watcher->m_slot); MojTestErrCheck(err); bool found = false; MojUInt32 count = 0; do { MojObject obj; err = cursor.get(obj, found); MojTestErrCheck(err); if (found) ++count; } while (found); MojTestAssert(count == 3); MojDbQuery::Page page; err = cursor.nextPage(page); MojTestErrCheck(err); err = cursor.close(); MojTestErrCheck(err); err = merge(db, idFourth, 53); MojTestErrCheck(err); MojTestAssert(watcher->m_count == 0); query.page(page); MojRefCountedPtr<TestWatcher> watcher2(new TestWatcher); MojTestAssert(watcher2.get()); err = db.find(query, cursor, watcher2->m_slot); MojTestErrCheck(err); found = false; count = 0; do { MojObject obj; err = cursor.get(obj, found); MojTestErrCheck(err); if (found) ++count; } while (found); MojTestAssert(count == 3); err = cursor.close(); MojTestErrCheck(err); err = db.del(idFirst, found); MojTestErrCheck(err); MojTestAssert(found); MojTestAssert(watcher->m_count == 1); MojTestAssert(watcher2->m_count == 0); err = db.del(idFourth, found); MojTestErrCheck(err); MojTestAssert(found); MojTestAssert(watcher->m_count == 1); MojTestAssert(watcher2->m_count == 1); // desc order query.page(MojDbQuery::Page()); query.desc(true); MojRefCountedPtr<TestWatcher> watcher3(new TestWatcher); MojTestAssert(watcher3.get()); err = db.find(query, cursor, watcher3->m_slot); MojTestErrCheck(err); found = false; count = 0; do { MojObject obj; err = cursor.get(obj, found); MojTestErrCheck(err); if (found) ++count; } while (found); MojTestAssert(count == 3); err = cursor.close(); MojTestErrCheck(err); err = merge(db, idLast, 53); MojTestErrCheck(err); MojTestAssert(watcher3->m_count == 1); MojRefCountedPtr<TestWatcher> watcher4(new TestWatcher); MojTestAssert(watcher4.get()); err = db.find(query, cursor, watcher4->m_slot); MojTestErrCheck(err); found = false; count = 0; do { MojObject obj; err = cursor.get(obj, found); MojTestErrCheck(err); if (found) ++count; } while (found); MojTestAssert(count == 3); err = cursor.close(); MojTestErrCheck(err); err = merge(db, idLast, 54); MojTestErrCheck(err); MojTestAssert(watcher4->m_count == 1); return MojErrNone; }
MojErr MojDbPurgeTest::run() { MojDb db; MojErr err = db.open(MojDbTestDir); MojTestErrCheck(err); // put type MojObject obj; err = obj.fromJson(MojKindStr); MojTestErrCheck(err); err = db.putKind(obj); MojTestErrCheck(err); MojObject revNums[10]; MojObject ids[10]; //put 10 objects in the db for(int i = 0; i < 10; i++) { err = obj.fromJson(MojTestObjStr1); MojTestErrCheck(err); err = db.put(obj); MojTestErrCheck(err); // get _rev and id MojObject rev; err = obj.getRequired(MojDb::RevKey, rev); MojTestErrCheck(err); revNums[i] = rev; MojObject id; err = obj.getRequired(MojDb::IdKey, id); MojTestErrCheck(err); ids[i] = id; } //purge now, there are no RevTimestamp entries MojUInt32 count = 0; err = db.purge(count, 30); MojTestErrCheck(err); err = checkObjectsPurged(db, count, 0, 10, 1, -1); MojTestErrCheck(err); //create a RevTimestamp entry - that's not more than PurgeNumDays days ago MojTime time; err = MojGetCurrentTime(time); MojTestErrCheck(err); err = createRevTimestamp(db, revNums[0], time.microsecs()); MojTestErrCheck(err); //purge now, there are no RevTimestamp entries that are more than //PurgeNumDays ago, so nothing should be purged count = 0; err = db.purge(count, 30); MojTestErrCheck(err); err = checkObjectsPurged(db, count, 0, 10, 3, -1); MojTestErrCheck(err); //create a RevTimestamp entry for more than PurgeNumDays days ago err = MojGetCurrentTime(time); MojTestErrCheck(err); err = createRevTimestamp(db, revNums[9], time.microsecs() - (((MojInt64)40) * MojTime::UnitsPerDay)); MojTestErrCheck(err); //purge now, since nothing has been deleted, nothing should be purged, //but the RevTimestamp object should be deleted count = 0; err = db.purge(count, 30); MojTestErrCheck(err); err = checkObjectsPurged(db, count, 0, 10, 4, -1); MojTestErrCheck(err); //delete something - this will set its revision number higher bool found; err = db.del(ids[0], found); MojTestErrCheck(err); MojTestAssert(found == true); //purge now, since nothing has been deleted prior to the revision //number, nothing should be purged count = 0; err = db.purge(count, 30); MojTestErrCheck(err); err = checkObjectsPurged(db, count, 0, 9, 5, -1); MojTestErrCheck(err); //delete another object err = db.del(ids[1], found); MojTestErrCheck(err); MojTestAssert(found == true); //create a RevTimestamp entry for more than PurgeNumDays days ago, //with the rev number of the 1st obj we deleted MojDbQuery query; err = query.from(_T("PurgeTest:1")); MojTestErrCheck(err); err = query.where(MojDb::IdKey, MojDbQuery::OpEq, ids[0]); MojTestErrCheck(err); err = query.includeDeleted(); MojTestErrCheck(err); MojDbCursor cursor; err = db.find(query, cursor); MojTestErrCheck(err); MojObject objFromDb; err = cursor.get(objFromDb, found); MojTestErrCheck(err); err = cursor.close(); MojTestErrCheck(err); MojTestAssert(found == true); MojObject revFromDb; err = objFromDb.getRequired(MojDb::RevKey, revFromDb); MojTestErrCheck(err); err = MojGetCurrentTime(time); MojTestErrCheck(err); err = createRevTimestamp(db, revFromDb, time.microsecs() - (((MojInt64)35) * MojTime::UnitsPerDay)); MojTestErrCheck(err); //now purge, only id[0] should be purged count = 0; err = db.purge(count, 30); MojTestErrCheck(err); err = checkObjectsPurged(db, count, 1, 8, 6, revFromDb); MojTestErrCheck(err); //TODO 2.12.10 - this test does not pass yet, we need to fix calling delKind after a purge //err = delKindTest(db); //MojTestErrCheck(err); err = db.close(); MojTestErrCheck(err); return MojErrNone; }
MojErr MojDb::delImpl(const MojDbQuery& quer, MojUInt32& countOut, MojDbReq& req, MojUInt32 flags) { MojLogTrace(s_log); countOut = 0; MojInt32 warns = 0; MojDbQuery newQuery = quer; MojUInt32 queryLimit = newQuery.limit(); if(newQuery.limit() == MojDbQuery::LimitDefault) newQuery.limit(AutoBatchSize); while(queryLimit > 0) { MojDbCursor cursor; MojErr err = findImpl(newQuery, cursor, NULL, req, OpDelete); MojErrCheck(err); MojAssert(cursor.txn()); MojUInt32 count = 0; MojUInt32 numberInBatch = 0; bool found = false; MojObject obj; for (;;) { MojDbStorageItem* item = NULL; err = cursor.get(item, found); // We simply skip ghost keys and continue; A warning is already written to the system log earlier if (err == MojErrInternalIndexOnFind) { warns++; numberInBatch++; continue; } MojErrCheck(err); if (!found) break; err = item->toObject(obj, m_kindEngine); MojErrCheck(err); const MojObject& id = item->id(); MojObject deleted; err = delObj(id, obj, item, deleted, req, flags); MojErrCheck(err); ++count; numberInBatch++; } if (warns > 0) MojLogInfo(s_log, _T("delquery index_warnings: %s, count: %d\n"), newQuery.from().data(), warns); countOut += count; err = cursor.close(); MojErrCheck(err); if(numberInBatch >= AutoBatchSize) // sing > - just in case something messed up { err = commitBatch(req); MojErrCheck(err); } if(count == 0) break; queryLimit -= newQuery.limit(); if(queryLimit > AutoBatchSize) newQuery.limit(AutoBatchSize); else newQuery.limit(queryLimit); } return MojErrNone; }
MojErr MojDbKind::stats(MojObject& objOut, MojSize& usageOut, MojDbReq& req, bool verify) { MojLogTrace(s_log); #if defined(TESTDBKIND) MojLogInfo(s_log, _T("Subkinds for - %s ; count = %d\n"), m_id.data(), m_subs.size()); int n = 0; for (KindVec::ConstIterator i = m_subs.begin(); i != m_subs.end(); ++i) { MojLogInfo(s_log, _T("SubKind %d: %s"), n++, (*i)->id().data()); } MojLogInfo(s_log, _T("Supers for - %s ; count = %d\n"), m_id.data(), m_supers.size()); n = 0; for (KindVec::ConstIterator i = m_supers.begin(); i != m_supers.end(); ++i) { MojLogInfo(s_log, _T("Super %d: %s"), n++, (*i)->id().data()); } #endif // analyze objects MojDbQuery query; MojErr err = query.from(m_id); MojErrCheck(err); err = query.includeDeleted(true); MojErrCheck(err); MojDbCursor cursor; err = m_kindEngine->find(query, cursor, NULL, req, OpRead); MojLogInfo(s_log, _T("KindStats start: %s ; Indexes = %zu; Using Index: %s; \n"), m_id.data(), m_indexes.size(), cursor.m_dbIndex->name().data()); MojErrCheck(err); MojSize count = 0; MojSize size = 0; MojSize delCount = 0; MojSize delSize = 0; MojSize warnings = 0; for (;;) { MojDbStorageItem* item = NULL; bool found = false; cursor.verifymode(true); err = cursor.get(item, found); if (err == MojErrInternalIndexOnFind) { warnings++; continue; } if (err != MojErrNone) // for all other errors break and dump current stats break; if (!found) break; MojObject obj; err = item->toObject(obj, *m_kindEngine, true); if (err != MojErrNone) break; bool deleted = false; if (obj.get(MojDb::DelKey, deleted) && deleted) { delSize += item->size(); delCount++; } else { size += item->size(); count++; } } MojLogInfo(s_log, _T("KindStats Summary: %s : Count: %zu; delCount: %zu; warnings: %zu \n"), m_id.data(), count, delCount, warnings); usageOut += size + delSize; MojObject info; err = info.put(SizeKey, (MojInt64) size); MojErrCheck(err); err = info.put(CountKey, (MojInt64) count); MojErrCheck(err); if (delCount > 0) { err = info.put(DelSizeKey, (MojInt64) delSize); MojErrCheck(err); err = info.put(DelCountKey, (MojInt64) delCount); MojErrCheck(err); } if (warnings > 0) { err = info.put(WarnKey, (MojInt64) warnings); MojErrCheck(err); } err = objOut.put(ObjectsKey, info); MojErrCheck(err); // and indexes MojObject indexes; for (IndexVec::ConstIterator i = m_indexes.begin(); i != m_indexes.end(); ++i) { MojObject indexInfo; err = (*i)->stats(indexInfo, usageOut, req); MojErrCheck(err); if (verify) { MojDbIndex *pi = i->get(); MojErr err2 = verifyIndex(pi, indexInfo, req); MojErrCheck(err2); } err = indexes.put((*i)->name(), indexInfo); MojErrCheck(err); } err = objOut.put(IndexesKey, indexes); MojErrCheck(err); return MojErrNone; }
MojErr MojDbKind::updateSupers(const KindMap& map, const StringVec& superIds, bool updating, MojDbReq& req) { MojLogTrace(s_log); MojInt32 indexes = 0; if (updating) { KindVec addedSupers; MojErr err = diffSupers(map, superIds, m_superIds, addedSupers); MojErrCheck(err); KindVec removedSupers; err = diffSupers(map, m_superIds, superIds, removedSupers); MojErrCheck(err); // remove/add our objects from new/removed supers if (!addedSupers.empty() || !removedSupers.empty()) { MojDbQuery query; err = query.from(m_id); MojErrCheck(err); err = query.includeDeleted(true); MojErrCheck(err); MojDbCursor cursor; cursor.kindEngine(m_kindEngine); err = find(cursor, NULL, req, OpKindUpdate); MojErrCheck(err); for (;;) { MojObject obj; bool found = false; err = cursor.get(obj, found); MojErrCheck(err); if (!found) break; for (KindVec::ConstIterator i = addedSupers.begin(); i != addedSupers.end(); ++i) { err = (*i)->updateOwnIndexes(&obj, NULL, req, indexes); MojErrCheck(err); } for (KindVec::ConstIterator i = removedSupers.begin(); i != removedSupers.end(); ++i) { err = (*i)->updateOwnIndexes(NULL, &obj, req, indexes); MojErrCheck(err); } } } } // remove old supers m_superIds = superIds; MojErr err = clearSupers(); MojErrCheck(err); // look for new supers for (StringVec::ConstIterator i = m_superIds.begin(); i != m_superIds.end(); ++i) { KindMap::ConstIterator mapIter = map.find(*i); if (mapIter != map.end()) { err = addSuper(mapIter->get()); MojErrCheck(err); } } // look for kinds that extend us for (KindMap::ConstIterator i = map.begin(); i != map.end(); ++i) { if ((*i)->m_superIds.find(m_id) != MojInvalidIndex) { err = (*i)->addSuper(this); MojErrCheck(err); } } // add root kind if we have no supers if (m_supers.empty()) { KindMap::ConstIterator mapIter = map.find(MojDbKindEngine::RootKindId); if (mapIter != map.end()) { err = addSuper(mapIter->get()); MojErrCheck(err); } } return MojErrNone; }
MojErr MojDbKind::verifyIndex(MojDbIndex *pIndex, MojObject &iinfo, MojDbReq& req) { // Goes throudh each index entry and verifies that it points to a valid object // For debugging purposes as stats for indexes does not access the target objects // Index->stats function does not have enough context to find the object // db/stats usage '{"verify":true,"kind":"xyz"}' - each optional MojDbQuery query; MojErr err = query.from(m_id); MojErrCheck(err); err = query.includeDeleted(true); MojErrCheck(err); MojDbCursor cursor; query.m_forceIndex = pIndex; // Important: Otherwise, it will pick the default index cursor.verifymode(true); // to get the errors err = m_kindEngine->find(query, cursor, NULL, req, OpRead); MojLogInfo(s_log, _T("Kind_verifyIndex: Kind: %s; Index: %s; idIndex: %zX; size: %zu; CursorIndex: %s \n"), m_name.data(), pIndex->name().data(), pIndex->idIndex(), pIndex->size(), cursor.m_dbIndex->name().data()); MojErrCheck(err); MojSize count = 0; MojSize delCount = 0; MojSize warnCount = 0; char s[1024]; for (;;) { MojDbStorageItem* item = NULL; bool found = false; err = cursor.get(item, found); if (err == MojErrInternalIndexOnFind) { warnCount++; MojDbIsamQuery *iquery = (MojDbIsamQuery *)cursor.m_storageQuery.get(); MojErr err2 = MojByteArrayToHex(iquery->m_keyData, iquery->m_keySize, s); MojErrCheck(err2); MojChar *ids = (iquery->m_keySize > 18) ? (MojChar *)(iquery->m_keyData + iquery->m_keySize - 17) : NULL; MojLogInfo(s_log, _T("VerifyIndex Warning: %s; KeySize: %zu; %s ;id: %s \n"), cursor.m_dbIndex->name().data(), iquery->m_keySize, s, ids); continue; } MojErrCheck(err); if (!found) break; MojObject obj; err = item->toObject(obj, *m_kindEngine, true); MojErrCheck(err); bool deleted = false; if (obj.get(MojDb::DelKey, deleted) && deleted) { delCount++; } else { count++; } } MojLogInfo(s_log, _T("Kind_verifyIndex Counts: Kind: %s; Index: %s; count: %zu; delcount: %zu; warnings: %zu \n"), m_name.data(), pIndex->name().data(), count, delCount, warnCount); err = iinfo.put(VerifyCountKey, (MojInt64)count); MojErrCheck(err); err = iinfo.put(VerifyWarnCountKey, (MojInt64) warnCount); MojErrCheck(err); err = iinfo.put(VerifyDelCountKey, (MojInt64) delCount); MojErrCheck(err); return MojErrNone; }
MojErr MojDb::dumpImpl(MojFile& file, bool backup, bool incDel, const MojObject& revParam, const MojObject& delRevParam, bool skipKinds, MojUInt32& countOut, MojDbReq& req, MojObject* response, const MojChar* keyName, MojSize& bytesWritten, MojSize& warns, MojUInt32 maxBytes) { // query for objects, adding the backup key and rev key if necessary MojDbQuery query; MojErr err = query.from(MojDbKindEngine::RootKindId); MojErrCheck(err); err = query.where(MojDb::DelKey, MojDbQuery::OpEq, incDel); MojErrCheck(err); if (backup) { err = query.where(MojDb::SyncKey, MojDbQuery::OpEq, true); MojErrCheck(err); if (incDel) { err = query.where(MojDb::RevKey, MojDbQuery::OpGreaterThan, delRevParam); MojErrCheck(err); } else { err = query.where(MojDb::RevKey, MojDbQuery::OpGreaterThan, revParam); MojErrCheck(err); } } MojDbCursor cursor; err = findImpl(query, cursor, NULL, req, OpRead); MojErrCheck(err); warns = 0; MojObject curRev; for(;;) { bool found = false; MojObject obj; err = cursor.get(obj, found); // So that we can get as much data as possible from a corrupt database // We simply skip ghost keys and continue if (err == MojErrInternalIndexOnFind) { warns++; continue; } MojErrCheck(err); if (!found) break; if (skipKinds) { MojString kind; err = obj.getRequired(KindKey, kind); MojErrCheck(err); if (kind == MojDbKindEngine::KindKindId) { continue; } } // write out each object, if the backup is full, insert the appropriate incremental key err = dumpObj(file, obj, bytesWritten, maxBytes); MojErrCatch(err, MojErrDbBackupFull) { if (response) { MojErr errBackup = MojErrNone; if (!curRev.undefined()) { errBackup = insertIncrementalKey(*response, keyName, curRev); MojErrCheck(errBackup); } else { errBackup = insertIncrementalKey(*response, keyName, incDel ? delRevParam: revParam); MojErrCheck(errBackup); } errBackup = handleBackupFull(revParam, delRevParam, *response, keyName); MojErrCheck(errBackup); } return MojErrNone; } MojErrCheck(err); err = obj.getRequired(MojDb::RevKey, curRev); MojErrCheck(err); countOut++; } err = cursor.close(); MojErrCheck(err); if (warns > 0) { MojLogWarning(s_log, _T("Finished Backup with %d warnings \n"), (int)warns); } else { MojLogDebug(s_log, _T("Finished Backup with no warnings \n")); } // construct the next incremental key if (response && !curRev.undefined()) { err = insertIncrementalKey(*response, keyName, curRev); MojErrCheck(err); } return MojErrNone; }
MojErr MojDb::purge(MojUInt32& countOut, MojInt64 numDays, MojDbReqRef req) { MojLogTrace(s_log); countOut = 0; if (numDays <= -1) { numDays = m_purgeWindow; } MojErr err = beginReq(req); MojErrCheck(err); MojLogDebug(s_log, _T("purging objects deleted more than %lld days ago..."), numDays); MojTime time; err = MojGetCurrentTime(time); MojErrCheck(err); // store the revision number to current timestamp mapping MojObject revTimeMapping; MojInt64 rev; err = nextId(rev); MojErrCheck(err); err = revTimeMapping.put(RevNumKey, rev); MojErrCheck(err); err = revTimeMapping.put(TimestampKey, time.microsecs()); MojErrCheck(err); err = revTimeMapping.putString(KindKey, MojDbKindEngine::RevTimestampId); MojErrCheck(err); err = putImpl(revTimeMapping, MojDb::FlagNone, req); MojErrCheck(err); // find the revision number for numDays prior to now MojInt64 purgeTime = time.microsecs() - (MojTime::UnitsPerDay * numDays); MojDbQuery query; err = query.from(MojDbKindEngine::RevTimestampId); MojErrCheck(err); query.limit(1); err = query.where(TimestampKey, MojDbQuery::OpLessThanEq, purgeTime); MojErrCheck(err); err = query.order(TimestampKey); MojErrCheck(err); query.desc(true); MojDbCursor cursor; err = findImpl(query, cursor, NULL, req, OpDelete); MojErrCheck(err); bool found = false; MojObject obj; err = cursor.get(obj, found); MojErrCheck(err); err = cursor.close(); MojErrCheck(err); MojUInt32 batchCount = 0; MojUInt32 totalCount = 0; while ((found)) { // Do it in AutoBatchSize batches batchCount = 0; req->fixmode(true); // purge even if index mis-matches err = purgeImpl(obj, batchCount, req); MojLogDebug(s_log, _T("purge batch processed: batch: %d; total: %d; err = %d\n"), batchCount, (totalCount + batchCount), err); MojErrCheck(err); totalCount += batchCount; countOut = totalCount; if (batchCount < AutoBatchSize) // last batch break; err = commitBatch(req); MojErrCheck(err); continue; } // end request err = req->end(); MojErrCheck(err); MojLogDebug(s_log, _T("purged %d objects"), countOut); return MojErrNone; }