MojErr MojDbQuery::updateClause(WhereClause& clause, CompOp op, const MojObject& val, MojDbCollationStrength coll) { // the only case where we can have two ops for the same prop is a combination // of '<' or '<=' with '>' or '>=' switch (op) { case OpLessThan: case OpLessThanEq: { CompOp lowerOp = clause.lowerOp(); if (lowerOp != OpNone && lowerOp != OpGreaterThan && lowerOp != OpGreaterThanEq) MojErrThrow(MojErrDbInvalidQueryOpCombo); MojErr err = clause.setUpper(op, val, coll); MojErrCheck(err); break; } case OpGreaterThan: case OpGreaterThanEq: { CompOp upperOp = clause.upperOp(); if (upperOp != OpNone && upperOp != OpLessThan && upperOp != OpLessThanEq) MojErrThrow(MojErrDbInvalidQueryOpCombo); MojErr err = clause.setLower(op, val, coll); MojErrCheck(err); break; } default: // all other ops invalid in combination MojErrThrow(MojErrDbInvalidQueryOpCombo); } return MojErrNone; }
MojErr MojDbServiceHandler::handleBatch(MojServiceMessage* msg, MojObject& payload, MojDbReq& req) { MojAssert(msg); MojLogTrace(s_log); MojObject operations; MojErr err = payload.getRequired(MojDbServiceDefs::OperationsKey, operations); MojErrCheck(err); MojObject::ArrayIterator begin; err = operations.arrayBegin(begin); MojErrCheck(err); //start the response array MojObjectVisitor& writer = msg->writer(); err = writer.beginObject(); MojErrCheck(err); err = writer.boolProp(MojServiceMessage::ReturnValueKey, true); MojErrCheck(err); err = writer.propName(MojDbServiceDefs::ResponsesKey); MojErrCheck(err); err = writer.beginArray(); MojErrCheck(err); for (MojObject* i = begin; i != operations.arrayEnd(); ++i) { MojString method; err = i->getRequired(MojDbServiceDefs::MethodKey, method); MojErrCheck(err); MojObject params; err = i->getRequired(MojDbServiceDefs::ParamsKey, params); MojErrCheck(err); // not allowed to specify watch key in a batch bool watchValue = false; if (payload.get(MojDbServiceDefs::WatchKey, watchValue) && watchValue) { MojErrThrow(MojErrDbInvalidBatch); } DbCallback cb; if (!m_batchCallbacks.get(method, cb)) { MojErrThrow(MojErrDbInvalidBatch); } err = (this->*cb)(msg, params, req); MojErrCheck(err); } err = writer.endArray(); MojErrCheck(err); err = writer.endObject(); MojErrCheck(err); return MojErrNone; }
MojErr MojDbQuery::appendClause(MojObjectVisitor& visitor, const MojChar* propName, CompOp op, const MojObject& val, MojDbCollationStrength coll) { MojAssert(propName); MojErr err = visitor.beginObject(); MojErrCheck(err); err = visitor.stringProp(PropKey, propName); MojErrCheck(err); const MojChar* opStr = opToString(op); if (!opStr) { MojErrThrow(MojErrDbInvalidQueryOp); } err = visitor.stringProp(OpKey, opStr); MojErrCheck(err); err = visitor.objectProp(ValKey, val); MojErrCheck(err); if (coll != MojDbCollationInvalid) { err = visitor.stringProp(CollateKey, MojDbUtils::collationToString(coll)); MojErrCheck(err); } err = visitor.endObject(); MojErrCheck(err); return MojErrNone; }
MojErr MojDbPropExtractor::fromObjectImpl(const MojObject& obj, const MojDbPropExtractor& defaultConfig, const MojChar* locale) { LOG_TRACE("Entering function %s", __FUNCTION__); // super MojErr err = MojDbExtractor::fromObject(obj, locale); MojErrCheck(err); // default value m_default = defaultConfig.m_default; MojObject defaultVal; if (obj.get(DefaultKey, defaultVal)) { MojObject::Type type = defaultVal.type(); MojDbKey key; if (type == MojObject::TypeArray) { // if the value is an array, act on its elements rather than the array object itself MojObject::ConstArrayIterator end = defaultVal.arrayEnd(); for (MojObject::ConstArrayIterator j = defaultVal.arrayBegin(); j != end; ++j) { err = key.assign(*j); MojErrCheck(err); err = m_default.put(key); MojErrCheck(err); } } else { err = key.assign(defaultVal); MojErrCheck(err); err = m_default.put(key); MojErrCheck(err); } } // tokenizer m_tokenizer = defaultConfig.m_tokenizer; MojString tokenize; bool found = false; err = obj.get(TokenizeKey, tokenize, found); MojErrCheck(err); if (found) { if (tokenize == AllKey || tokenize == DefaultKey) { m_tokenizer.reset(new MojDbTextTokenizer); MojAllocCheck(m_tokenizer.get()); err = m_tokenizer->init(locale); MojErrCheck(err); } else { MojErrThrow(MojErrDbInvalidTokenization); } } // collator if (m_collation == MojDbCollationInvalid) m_collation = defaultConfig.m_collation; err = updateLocale(locale); MojErrCheck(err); // set prop err = prop(m_name); MojErrCheck(err); return MojErrNone; }
MojErr MojService::CategoryHandler::invoke(const MojChar* method, MojServiceMessage* msg, MojObject& payload) { MojAssert(method && msg); MojLogTrace(s_log); MojTime startTime; MojErr err = MojGetCurrentTime(startTime); MojErrCheck(err); // lookup callback CallbackInfo cb; if (!m_callbackMap.get(method, cb)) { MojErrThrow(MojErrMethodNotFound); } // validate schema if (cb.m_schema.get()) { MojSchema::Result res; err = cb.m_schema->validate(payload, res); MojErrCheck(err); if (!res.valid()) { MojErrThrowMsg(MojErrInvalidArg, _T("invalid parameters: caller='%s' error='%s'"), msg->senderName(), res.msg().data()); } } // invoke method err = invoke(cb.m_callback, msg, payload); MojErrCheck(err); // log timing MojTime endTime; err = MojGetCurrentTime(endTime); MojErrCheck(err); MojLogInfo(s_log, _T("%s invoked: %.3fms"), method, (double) (endTime.microsecs() - startTime.microsecs()) / 1000); return MojErrNone; }
MojErr MojDbLevelIndex::del(const MojDbKey& key, MojDbStorageTxn* txn) { LOG_TRACE("Entering function %s", __FUNCTION__); MojAssert(txn); MojAssert(isOpen()); MojDbLevelItem keyItem; keyItem.fromBytesNoCopy(key.data(), key.size()); bool found = false; MojErr err = m_db->del(keyItem, found, txn); #ifdef MOJ_DEBUG char s[1024]; size_t size1 = keyItem.size(); MojErr err2 = MojByteArrayToHex(keyItem.data(), size1, s); MojErrCheck(err2); if (size1 > 16) // if the object-id is in key strncat(s, (char *)(keyItem.data()) + (size1 - 17), 16); LOG_DEBUG("[db_ldb] ldbindexdel: %s; keylen: %zu, key: %s ; err = %d\n", m_db->m_name.data(), size1, s, err); if (!found) LOG_WARNING(MSGID_LEVEL_DB_WARNING, 1, PMLOGKS("index", s), "ldbindexdel_warn: not found: %s \n", s); #endif MojErrCheck(err); if (!found) { //MojErrThrow(MojErrDbInconsistentIndex); // fix this to work around to deal with out of sync indexes MojErrThrow(MojErrInternalIndexOnDel); } return MojErrNone; }
MojErr MojDb::updateLocale(const MojChar* locale, MojDbReqRef req) { MojAssert(locale); MojLogTrace(s_log); MojErr err = beginReq(req, true); MojErrCheck(err); MojString oldLocale; err = getLocale(oldLocale, req); MojErrCheck(err); MojString newLocale; err = newLocale.assign(locale); MojErrCheck(err); MojErr updateErr = err = updateLocaleImpl(oldLocale, newLocale, req); MojErrCatchAll(err) { err = req->abort(); MojErrCheck(err); err = m_kindEngine.close(); MojErrCheck(err); MojDbReq openReq; err = beginReq(openReq, true); MojErrCheck(err); err = m_kindEngine.open(this, openReq); MojErrCheck(err); err = openReq.end(); MojErrCheck(err); MojErrThrow(updateErr); } return MojErrNone; }
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 MojSocketMessageHeader::read(MojDataReader& reader) { MojErr err = reader.readUInt32(m_version); MojErrCheck(err); if (m_version != s_protocolVersion) { MojErrThrow(MojErrInvalidMsg); } err = reader.readUInt32(m_messageLen); MojErrCheck(err); if (m_messageLen > s_maxMsgSize || m_messageLen <= 0) { MojErrThrow(MojErrInvalidMsg); } return MojErrNone; }
MojErr MojDbQuery::WhereClause::collation(MojDbCollationStrength collation) { if (m_collation != MojDbCollationInvalid && collation != m_collation) MojErrThrow(MojErrDbInvalidQueryCollationMismatch); m_collation = collation; return MojErrNone; }
MojErr MojDbQuotaEngine::Offset::apply(MojInt64 offset) { MojInt64 newOffset = m_offset + offset; if (m_quota.get() && newOffset > m_quota->available()) MojErrThrow(MojErrDbQuotaExceeded); m_offset = newOffset; return MojErrNone; }
MojErr MojDbKind::deny(MojDbReq& req) { MojLogWarning(s_log, _T("db: permission denied for caller '%s' on kind '%s'"), req.domain().data(), m_id.data()); if (m_kindEngine->permissionEngine()->enabled()) { // don't leak any information in an error message MojErrThrow(MojErrDbPermissionDenied); } return MojErrNone; }
MojErr MojDbKindEngine::idFromToken(MojInt64 tok, MojString& idOut) { TokMap::ConstIterator i = m_tokens.find(tok); if (i == m_tokens.end()) MojErrThrow(MojErrDbInvalidKindToken); idOut = i.value(); return MojErrNone; }
MojErr MojVPrintF(const MojChar* format, va_list args) { MojAssert(format); int res = vprintf(format, args); if (res < 0) MojErrThrow(MojErrFormat); return MojErrNone; }
MojErr MojDbQuery::stringToOp(const MojChar* str, CompOp& opOut) { for (const StrOp* op = s_ops; op->m_str != NULL; ++op) { if (MojStrCmp(op->m_str, str) == 0) { opOut = op->m_op; return MojErrNone; } } MojErrThrow(MojErrDbInvalidQueryOp); }
virtual MojErr del(const MojDbKey& key, MojDbStorageTxn* txn) { bool found = false; MojErr err = m_set.del(key, found); MojErrCheck(err); if (!found) MojErrThrow(MojErrNotFound); ++m_delCount; return MojErrNone; }
MojErr MojDbQuotaEngine::Offset::apply(MojInt64 offset) { LOG_TRACE("Entering function %s", __FUNCTION__); MojInt64 newOffset = m_offset + offset; if (m_quota.get() && newOffset > m_quota->available()) MojErrThrow(MojErrDbQuotaExceeded); m_offset = newOffset; return MojErrNone; }
MojErr MojService::getRequest(MojServiceMessage* msg, MojRefCountedPtr<MojServiceRequest>& reqOut) { MojAssert(msg); MojAssertMutexUnlocked(m_mutex); MojLogTrace(s_log); MojThreadGuard guard(m_mutex); if (!m_requests.get(msg->token(), reqOut)) { MojErrThrow(MojErrResponseHandlerNotFound); } return MojErrNone; }
MojErr MojDb::findImpl(const MojDbQuery& query, MojDbCursor& cursor, MojDbWatcher* watcher, MojDbReq& req, MojDbOp op) { MojLogTrace(s_log); if (cursor.isOpen()) MojErrThrow(MojErrDbCursorAlreadyOpen); MojErr err = m_kindEngine.find(query, cursor, watcher, req, op); MojErrCheck(err); return MojErrNone; }
MojErr MojService::getCategory(const MojChar* name, MojRefCountedPtr<Category>& catOut) { MojAssert(name); MojAssertMutexUnlocked(m_mutex); MojLogTrace(s_log); MojThreadGuard guard(m_mutex); if (!m_categories.get(name, catOut)) { MojErrThrow(MojErrCategoryNotFound); } return MojErrNone; }
MojErr MojDbQuotaEngine::quotaUsage(const MojChar* owner, MojInt64& sizeOut, MojInt64& usageOut) { sizeOut = 0; usageOut = 0; QuotaMap::ConstIterator iter = m_quotas.find(owner); if (iter == m_quotas.end()) { MojErrThrow(MojErrNotFound); } sizeOut = iter.value()->size(); usageOut = iter.value()->usage(); return MojErrNone; }
MojErr MojDbQuery::WhereClause::setUpper(CompOp op, const MojObject& val, MojDbCollationStrength coll) { if (m_upperOp != OpNone) MojErrThrow(MojErrDbInvalidQueryOpCombo); MojErr err = collation(coll); MojErrCheck(err); m_upperOp = op; m_upperVal = val; 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::init(const MojString& id) { MojLogTrace(s_log); // parse name and version out of id if (id.length() > KindIdLenMax) MojErrThrowMsg(MojErrDbMalformedId, _T("db: kind id too long")); MojSize sepIdx = id.rfind(VersionSeparator); if (sepIdx == MojInvalidIndex) MojErrThrow(MojErrDbMalformedId); MojErr err = id.substring(0, sepIdx, m_name); MojErrCheck(err); MojString str; err = id.substring(sepIdx + 1, id.length() - sepIdx - 1, str); MojErrCheck(err); const MojChar* end = NULL; MojInt64 ver = MojStrToInt64(str.data(), &end, 0); if (*end != '\0' || ver < 0 || ver > MojUInt32Max) MojErrThrow(MojErrDbMalformedId); m_version = (MojUInt32) ver; m_id = id; return MojErrNone; }
MojErr MojDbKindEngine::getKind(const MojObject& obj, MojDbKind*& kind) { MojLogTrace(s_log); MojString kindName; bool found = false; MojErr err = obj.get(MojDb::KindKey, kindName, found); MojErrCheck(err); if (!found) MojErrThrow(MojErrDbKindNotSpecified); err = getKind(kindName.data(), kind); MojErrCheck(err); return MojErrNone; }
MojErr MojDbQuotaEngine::quotaUsage(const MojChar* owner, MojInt64& sizeOut, MojInt64& usageOut) { LOG_TRACE("Entering function %s", __FUNCTION__); sizeOut = 0; usageOut = 0; QuotaMap::ConstIterator iter = m_quotas.find(owner); if (iter == m_quotas.end()) { MojErrThrow(MojErrNotFound); } sizeOut = iter.value()->size(); usageOut = iter.value()->usage(); return MojErrNone; }
MojErr MojDbWatcher::handleCancel() { MojThreadGuard guard(m_mutex); MojLogDebug(MojDb::s_log, _T("Watcher_handleCancel: state= %d; index name = %s; domain = %s\n"), (int)m_state, ((m_index) ? m_index->name().data() : NULL), ((m_domain) ? m_domain.data() : NULL)); if (m_index == NULL) MojErrThrow(MojErrDbWatcherNotRegistered); if (m_state != StateInvalid) { MojErr err = invalidate(); MojErrCheck(err); } return MojErrNone; }
MojErr MojDbPutHandler::validateWildcard(const MojString& val, MojErr errToThrow) { LOG_TRACE("Entering function %s", __FUNCTION__); if (val.empty()) MojErrThrow(errToThrow); MojSize wildcardPos = val.find(_T('*')); if (wildcardPos != MojInvalidSize) { if (wildcardPos != val.length() - 1 || (wildcardPos != 0 && val.at(wildcardPos - 1) != _T('.'))) { MojErrThrowMsg(errToThrow, _T("db: invalid wildcard in - '%s'"), val.data()); } } return MojErrNone; }
MojErr MojSockAddr::fromPath(const MojChar* path) { MojAssert(path); MojSockAddrUnT addr; MojZero(&addr, sizeof(addr)); if (MojStrLen(path) > (sizeof(addr.sun_path) - 1)) MojErrThrow(MojErrPathTooLong); MojStrCpy(addr.sun_path, path); addr.sun_family = MOJ_PF_LOCAL; fromAddr((MojSockAddrT*) &addr, sizeof(addr)); return MojErrNone; }
MojErr MojDbUtils::collationFromString(const MojString& str, MojDbCollationStrength& collOut) { LOG_TRACE("Entering function %s", __FUNCTION__); collOut = MojDbCollationInvalid; MojDbCollationStrInfo* info = s_collStrings; do { collOut = info->m_coll; if (str == info->m_str) break; } while ((info++)->m_coll != MojDbCollationInvalid); if (collOut == MojDbCollationInvalid) MojErrThrow(MojErrDbInvalidCollation); return MojErrNone; }