MojErr MojDb::delObj(const MojObject& id, const MojObject& obj, MojDbStorageItem* item, MojObject& foundObjOut, MojDbReq& req, MojUInt32 flags) { MojAssert(item); MojLogTrace(s_log); if (MojFlagGet(flags, FlagPurge)) { // update indexes MojTokenSet tokenSet; // we want purge to force delete req.fixmode(true); MojErr err = m_kindEngine.update(NULL, &obj, req, OpDelete, tokenSet); MojErrCheck(err); // gross layering violation err = req.txn()->offsetQuota(-(MojInt64) item->size()); MojErrCheck(err); // permanently delete bool found = false; err = m_objDb->del(id, req.txn(), found); MojErrCheck(err); if (!found) MojErrThrow(MojErrDbCorruptDatabase); err = foundObjOut.put(IdKey, id); MojErrCheck(err); } else { // set deleted flag and put if we are not purging MojObject newObj = obj; MojErr err = newObj.putBool(DelKey, true); MojErrCheck(err); err = putObj(id, newObj, &obj, item, req, OpDelete); MojErrCheck(err); foundObjOut = newObj; } return MojErrNone; }
MojErr MojDbIndex::find(MojDbCursor& cursor, MojDbWatcher* watcher, MojDbReq& req) { LOG_TRACE("Entering function %s", __FUNCTION__); MojAssert(isOpen()); MojAutoPtr<MojDbQueryPlan> plan(new MojDbQueryPlan(*m_kindEngine)); MojAllocCheck(plan.get()); MojErr err = plan->init(cursor.query(), *this); MojErrCheck(err); if (watcher) { // we have to add the watch before beginning the txn or we may miss events MojAssert(cursor.txn() == NULL); err = addWatch(*plan, cursor, watcher, req); MojErrCheck(err); } if (!cursor.txn()) { MojDbStorageTxn* txn = req.txn(); bool cursorOwnsTxn = !(req.batch() || txn); if (txn) { cursor.txn(txn, cursorOwnsTxn); } else { MojRefCountedPtr<MojDbStorageTxn> localTxn; err = m_collection->beginTxn(localTxn); MojErrCheck(err); cursor.txn(localTxn.get(), cursorOwnsTxn); req.txn(localTxn.get()); } } cursor.m_dbIndex = this; // for debugging err = m_collection->find(plan, cursor.txn(), cursor.m_storageQuery); MojErrCheck(err); cursor.m_watcher = watcher; return MojErrNone; }
MojErr MojDbIndex::stats(MojObject& objOut, MojSize& usageOut, MojDbReq& req) { LOG_TRACE("Entering function %s", __FUNCTION__); MojAssert(isOpen()); MojSize count = 0; MojSize size = 0; MojErr err = m_index->stats(req.txn(), count, size); LOG_DEBUG("[db_mojodb] IndexStats: Kind: %s;Index: %s; Id: %zX; count= %zu; size= %zu; delMisses = %d, err= %d \n", m_kind->name().data(), m_name.data(), idIndex(), count, size, m_delMisses, err); MojErrCheck(err); usageOut += size; err = objOut.put(SizeKey, (MojInt64) size); MojErrCheck(err); err = objOut.put(CountKey, (MojInt64) count); MojErrCheck(err); err = objOut.put(DelMissesKey, (MojInt64) m_delMisses); // cumulative since start MojErrCheck(err); MojThreadReadGuard guard(m_lock); if (!m_watcherMap.empty()) { MojObject watcherInfo; for (WatcherMap::ConstIterator i = m_watcherMap.begin(); i != m_watcherMap.end(); ++i) { err = watcherInfo.put(i.key(), (MojInt64) i.value()); MojErrCheck(err); } err = objOut.put(WatchesKey, watcherInfo); MojErrCheck(err); } return MojErrNone; }
MojErr MojDbIndex::updateLocale(const MojChar* locale, MojDbReq& req) { LOG_TRACE("Entering function %s", __FUNCTION__); MojAssert(isOpen()); MojAssert(locale); bool haveCollate = false; for (PropVec::ConstIterator i = m_props.begin(); i != m_props.end(); ++i) { if ((*i)->collation() != MojDbCollationInvalid) { haveCollate = true; MojErr err = (*i)->updateLocale(locale); MojErrCheck(err); } } if (haveCollate) { // drop and reindex MojErr err = drop(req); MojErrCheck(err); err = build(req.txn()); MojErrCheck(err); } m_locale.assign(locale); return MojErrNone; }
MojErr MojDbKindState::readIds(const MojChar* key, MojDbReq& req, MojObject& objOut, MojRefCountedPtr<MojDbStorageItem>& itemOut) { MojErr err = readObj(key, objOut, m_kindEngine->indexIdDb(), req.txn(), itemOut); MojErrCheck(err); return MojErrNone; }
MojErr MojDbKindState::writeIds(const MojChar* key, const MojObject& obj, MojDbReq& req, MojRefCountedPtr<MojDbStorageItem>& oldItem) { MojErr err = writeObj(key, obj, m_kindEngine->indexIdDb(), req.txn(), oldItem); MojErrCheck(err); return MojErrNone; }
MojErr MojDbIndex::open(MojDbStorageIndex* index, const MojObject& id, MojDbReq& req, bool created) { LOG_TRACE("Entering function %s", __FUNCTION__); MojAssert(!isOpen() && !m_props.empty()); MojAssert(index); // we don't want to take built-in props into account when sorting indexes, m_sortKey = m_propNames; m_id = id; MojDbKey idKey; MojErr err = idKey.assign(id); MojErrCheck(err); err = m_idSet.put(idKey); MojErrCheck(err); err = addBuiltinProps(); MojErrCheck(err); if (created && !isIdIndex()) { // if this index was just created, we need to re-index before committing the transaction MojDbStorageTxn* txn = req.txn(); txn->notifyPreCommit(m_preCommitSlot); txn->notifyPostCommit(m_postCommitSlot); } else { // otherwise it's ready m_ready = true; } // and we're open m_index.reset(index); m_collection = m_index.get(); return MojErrNone; }
MojErr MojDbQuotaEngine::initUsage(MojDbKind* kind, MojDbReq& req) { MojAssert(kind); MojRefCountedPtr<MojDbStorageItem> item; MojErr err = m_usageDb->get(kind->id(), req.txn(), false, item); MojErrCheck(err); if (!item.get()) { MojObject stats; MojSize usage = 0; err = kind->stats(stats, usage, req, false); MojErrCheck(err); err = insertUsage(kind->id(), (MojInt64) usage, req.txn()); MojErrCheck(err); } return MojErrNone; }
MojErr MojDbIndex::drop(MojDbReq& req) { LOG_TRACE("Entering function %s", __FUNCTION__); MojAssert(isOpen()); MojErr err = m_index->drop(req.txn()); MojErrCheck(err); return MojErrNone; }
MojErr MojDbIndex::drop(MojDbReq& req) { MojAssert(isOpen()); MojLogTrace(s_log); MojErr err = m_index->drop(req.txn()); MojErrCheck(err); return MojErrNone; }
MojErr MojDbQuotaEngine::open(const MojObject& conf, MojDb* db, MojDbReq& req) { LOG_TRACE("Entering function %s", __FUNCTION__); MojAssert(db); MojErr err = db->storageEngine()->openDatabase(_T("UsageDbName"), req.txn(), m_usageDb); MojErrCheck(err); err = MojDbPutHandler::open(conf, db, req); MojErrCheck(err); MojDbKindEngine::KindMap& kinds = db->kindEngine()->kindMap(); for (MojDbKindEngine::KindMap::ConstIterator i = kinds.begin(); i != kinds.end(); ++i) { err = initUsage(i.value().get(), req); MojErrCheck(err); } err = refreshImpl(req.txn()); MojErrCheck(err); m_isOpen = true; return MojErrNone; }
MojErr MojDbKindState::initTokens(MojDbReq& req, const StringSet& strings) { // TODO: bug inside this function. (latest strace step) MojAssertMutexLocked(m_lock); // TODO: filing load tokens. Go inside readObj // load tokens MojErr err = readObj(TokensKey, m_tokensObj, m_kindEngine->kindDb(), req.txn(), m_oldTokensItem); MojErrCheck(err); // populate token vec MojUInt8 maxToken = 0; err = m_tokenVec.resize(m_tokensObj.size()); MojErrCheck(err); for (MojObject::ConstIterator i = m_tokensObj.begin(); i != m_tokensObj.end(); ++i) { MojString key = i.key(); MojInt64 value = i.value().intValue(); MojSize idx = (MojSize) (value - MojObjectWriter::TokenStartMarker); if (value < MojObjectWriter::TokenStartMarker || value >= MojUInt8Max || idx >= m_tokenVec.size()) { MojErrThrow(MojErrDbInvalidToken); } if (value > maxToken) { maxToken = (MojUInt8) value; } err = m_tokenVec.setAt(idx, key); MojErrCheck(err); } if (maxToken > 0) { m_nextToken = (MojUInt8) (maxToken + 1); } // add strings bool updated = false; for (StringSet::ConstIterator i = strings.begin(); i != strings.end(); ++i) { if (!m_tokensObj.contains(*i)) { updated = true; MojUInt8 token = 0; TokenVec tokenVec; MojObject tokenObj; err = addPropImpl(*i, false, token, tokenVec, tokenObj); MojErrCheck(err); } } if (updated) { err = writeTokens(m_tokensObj); MojErrCheck(err); } return MojErrNone; }
MojErr MojDbKind::updateOwnIndexes(const MojObject* newObj, const MojObject* oldObj, const MojDbReq& req, MojInt32& idxcount) { MojInt32 count = 0; for (IndexVec::ConstIterator i = m_indexes.begin(); i != m_indexes.end(); ++i) { count++; MojErr err = (*i)->update(newObj, oldObj, req.txn(), req.fixmode()); MojErrCheck(err); } MojLogInfo(s_log, _T("Kind_UpdateOwnIndexes: %s; count: %d \n"), this->id().data(), count); idxcount += count; return MojErrNone; }
MojErr MojDb::getImpl(const MojObject& id, MojObjectVisitor& visitor, MojDbOp op, MojDbReq& req) { MojRefCountedPtr<MojDbStorageItem> item; MojErr err = m_objDb->get(id, req.txn(), false, item); MojErrCheck(err); if (item.get()) { MojString kindId; err = item->kindId(kindId, m_kindEngine); MojErrCheck(err); err = m_kindEngine.checkPermission(kindId, op, req); MojErrCheck(err); err = item->visit(visitor, m_kindEngine); MojErrCheck(err); err = item->close(); MojErrCheck(err); } return MojErrNone; }
MojErr MojDbKindEngine::open(MojDb* db, MojDbReq& req) { MojAssert(db); MojAssertWriteLocked(db->m_schemaLock); MojLogTrace(s_log); // open kind db and index seq m_db = db; MojDbStorageEngine* engine = db->storageEngine(); MojDbStorageTxn* txn = req.txn(); MojAssert(engine); MojErr err = engine->openDatabase(KindsDbName, txn, m_kindDb); MojErrCheck(err); err = engine->openDatabase(IndexIdsDbName, txn, m_indexIdDb); MojErrCheck(err); err = engine->openSequence(IndexIdsSeqName, txn, m_indexIdSeq); MojErrCheck(err); // built-in kinds err = addBuiltin(RootKindJson, req); MojErrCheck(err); err = addBuiltin(KindKindJson, req); MojErrCheck(err); err = addBuiltin(RevTimestampJson, req); MojErrCheck(err); err = addBuiltin(DbStateJson, req); MojErrCheck(err); err = addBuiltin(PermissionJson, req); MojErrCheck(err); err = addBuiltin(QuotaJson, req); MojErrCheck(err); // built-in indexes err = setupRootKind(); MojErrCheck(err); // locale err = db->getLocale(m_locale, req); MojErrCheck(err); // load kinds from obj db err = loadKinds(req); MojErrCheck(err); return MojErrNone; }
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::delImpl(const MojObject& id, bool& foundOut, MojObject& foundObjOut, MojDbReq& req, MojUInt32 flags) { MojLogTrace(s_log); foundObjOut.clear(); // get object, so we can find the type MojRefCountedPtr<MojDbStorageItem> item; MojErr err = m_objDb->get(id, req.txn(), true, item); MojErrCheck(err); if (item.get()) { // and delete it MojObject obj; err = item->toObject(obj, m_kindEngine); MojErrCheck(err); err = delObj(id, obj, item.get(), foundObjOut, req, flags); MojErrCheck(err); foundOut = true; } return MojErrNone; }
MojErr MojDb::getState(const MojChar* key, MojObject& valOut, MojDbReq& req) { MojLogTrace(s_log); MojString idStr; MojErr err = idStr.assign(DbStateObjId); MojErrCheck(err); MojObject id(idStr); MojRefCountedPtr<MojDbStorageItem> item; err = m_objDb->get(id, req.txn(), false, item); MojErrCheck(err); if (item.get()) { MojObject obj; err = item->toObject(obj, m_kindEngine, true); MojErrCheck(err); MojObject val; if (obj.get(key, val)) { valOut = val; } } return MojErrNone; }
MojErr MojDbKind::openIndex(MojDbIndex* index, MojDbReq& req) { MojAssert(index); MojAssert(m_db); MojLogTrace(s_log); // construct storage index name MojString name; MojErr err = name.format(_T("%s-%d-%s"), m_name.data(), m_version, index->name().data()); MojErrCheck(err); // get id MojObject id; bool created = false; err = m_state->indexId(index->name(), req, id, created); MojErrCheck(err); // open MojRefCountedPtr<MojDbStorageIndex> storageIndex; err = m_db->openIndex(id, req.txn(), storageIndex); MojErrCheck(err); err = index->open(storageIndex.get(), id, req, created); MojErrCheck(err); return MojErrNone; }
MojErr MojDbQuotaEngine::put(MojObject& obj, MojDbReq& req, bool putObj) { MojLogTrace(s_log); MojAssertWriteLocked(m_db->schemaLock()); // check for admin permission if (!req.admin()) { MojErrThrow(MojErrDbPermissionDenied); } // pull params out of object MojString owner; MojErr err = obj.getRequired(MojDbServiceDefs::OwnerKey, owner); MojErrCheck(err); MojInt64 size = 0; err = obj.getRequired(MojDbServiceDefs::SizeKey, size); MojErrCheck(err); // validate owner err = validateWildcard(owner, MojErrDbInvalidOwner); MojErrCheck(err); // put object if (putObj) { MojString id; err = id.format(_T("_quotas/%s"), owner.data()); MojErrCheck(err); err = obj.put(MojDb::IdKey, id); MojErrCheck(err); err = obj.putString(MojDb::KindKey, MojDbKindEngine::QuotaId); MojErrCheck(err); MojDbAdminGuard adminGuard(req); err = m_db->put(obj, MojDb::FlagForce, req); MojErrCheck(err); // defer commit of quota until txn commit MojRefCountedPtr<MojDbQuotaCommitHandler> handler(new MojDbQuotaCommitHandler(this, owner, size, req.txn())); MojAllocCheck(handler.get()); } else { err = commitQuota(owner, size); 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 MojDb::putObj(const MojObject& id, MojObject& obj, const MojObject* oldObj, MojDbStorageItem* oldItem, MojDbReq& req, MojDbOp op, bool checkSchema) { MojLogTrace(s_log); // if nothing changed, don't do the update if (oldObj != NULL && obj == *oldObj) return MojErrNone; // update revision MojInt64 rev; MojErr err = nextId(rev); MojErrCheck(err); err = obj.put(RevKey, rev); MojErrCheck(err); // assign id MojObject putId = id; if (putId.undefined()) { err = m_idGenerator.id(putId); MojErrCheck(err); err = obj.put(IdKey, putId); MojErrCheck(err); } // assign ids to subobjects in arrays - only for regular objects MojString kindName; bool found = false; err = obj.get(MojDb::KindKey, kindName, found); MojErrCheck(err); if (!found) MojErrThrow(MojErrDbKindNotSpecified); if (!kindName.startsWith(MojDbKindEngine::KindKindIdPrefix)) { err = assignIds(obj); MojErrCheck(err); } // validate, update indexes, etc. MojTokenSet tokenSet; err = m_kindEngine.update(&obj, oldObj, req, op, tokenSet, checkSchema); MojErrCheck(err); // serialize object MojDbObjectHeader header(putId); err = header.extractFrom(obj); MojErrCheck(err); MojBuffer buf; err = header.write(buf, m_kindEngine); MojErrCheck(err); MojObjectWriter writer(buf, &tokenSet); err = obj.visit(writer); MojErrCheck(err); // store it in the db if (oldItem) { err = m_objDb->update(putId, buf, oldItem, req.txn()); MojErrCheck(err); } else { err = m_objDb->insert(putId, buf, req.txn()); MojErrCheck(err); } // put header back in the object for response purposes err = header.addTo(obj); MojErrCheck(err); return MojErrNone; }
MojErr MojDb::putImpl(MojObject& obj, MojUInt32 flags, MojDbReq& req, bool checkSchema) { MojLogTrace(s_log); MojRefCountedPtr<MojDbStorageItem> prevItem; MojObject id; if (obj.get(IdKey, id)) { // get previous revision if we have an id MojErr err = m_objDb->get(id, req.txn(), true, prevItem); MojErrCheck(err); } if (MojFlagGet(flags, FlagIgnoreMissing) && MojFlagGet(flags, FlagMerge)) { if (!prevItem.get()) { MojErr err = obj.putBool(MojDb::IgnoreIdKey, true); // so that we can drop it in output MojErrCheck(err); return MojErrNone; } } MojDbOp op = OpCreate; MojObject* prevPtr = NULL; MojObject prev; if (prevItem.get()) { // deal with prev, if it exists op = OpUpdate; prevPtr = &prev; MojErr err = prevItem->toObject(prev, m_kindEngine); MojErrCheck(err); if (MojFlagGet(flags, FlagMerge)) { // do merge MojObject merged; err = mergeInto(merged, obj, prev); MojErrCheck(err); obj = merged; } else if (!MojFlagGet(flags, FlagForce)) { // if the force flag is not set, throw error if we are updating // an existing, non-deleted object and no rev was specified MojInt64 rev; if (obj.get(RevKey, rev) == false) { bool deleted = false; if (!prev.get(DelKey, deleted) || !deleted) MojErrThrow(MojErrDbRevNotSpecified); } } } // if the new object has a rev and it doesn't match the old rev, don't do the update MojInt64 newRev; if (prevPtr != NULL && obj.get(RevKey, newRev)) { MojInt64 oldRev; MojErr err = prevPtr->getRequired(RevKey, oldRev); MojErrCheck(err); if (!MojFlagGet(flags, FlagForce) && newRev != oldRev) MojErrThrowMsg(MojErrDbRevisionMismatch, _T("db: revision mismatch - expected %lld, got %lld"), oldRev, newRev); } // save it MojErr err = putObj(id, obj, prevPtr, prevItem.get(), req, op, checkSchema); MojErrCheck(err); if (prevItem.get()) { err = prevItem->close(); MojErrCheck(err); } return MojErrNone; }