MojErr MojDbQuery::addClause(WhereMap& map, const MojChar* propName, CompOp op, const MojObject& val, MojDbCollationStrength coll) { MojAssert(propName); // only allow valid ops if (!(op >= OpEq && op <= OpSubString)) MojErrThrowMsg(MojErrDbInvalidQuery, _T("db: invalid query op")); // only allow array values for = or % or %% ops if (val.type() == MojObject::TypeArray && op != OpEq && op != OpPrefix && op != OpSubString) MojErrThrowMsg(MojErrDbInvalidQuery, _T("db: query contains array value for non-eq op")); // check to see if the prop is referenced in a prior clause WhereMap::Iterator iter; MojErr err = map.find(propName, iter); MojErrCheck(err); if (iter == map.end()) { // create new clause err = createClause(map, propName, op, val, coll); MojErrCheck(err); } else { // add clause to previously referenced prop. err = updateClause(iter.value(), op, val, coll); MojErrCheck(err); } return MojErrNone; }
MojErr MojDbQuery::validate() const { bool hasInequalityOp = false; bool hasArrayVal = false; for (WhereMap::ConstIterator i = m_whereClauses.begin(); i != m_whereClauses.end(); ++i) { // verify that we only have inequality op on one prop if (i->lowerOp() != OpEq) { if (hasInequalityOp) MojErrThrowMsg(MojErrDbInvalidQuery, _T("db: query contains inequality operations on multiple properties")); hasInequalityOp = true; } // verify that we only have one array val and it's on an = or % op if (i->lowerVal().type() == MojObject::TypeArray && i.key() != DelKey) { if (hasArrayVal) MojErrThrowMsg(MojErrDbInvalidQuery, _T("db: query contains array values on multiple properties")); hasArrayVal = true; } } for (WhereMap::ConstIterator i = m_filterClauses.begin(); i != m_filterClauses.end(); ++i) { // verify that we only have inequality op on one prop if (i->lowerOp() == OpPrefix) { MojErrThrowMsg(MojErrDbInvalidQuery, _T("db: query contains prefix operator in filter")); } if (i->lowerOp() == OpSearch) { MojErrThrowMsg(MojErrDbInvalidQuery, _T("db: query contains search operator in filter")); } } return MojErrNone; }
MojErr MojDbIndex::validateName(const MojString& name) { if (name.length() > MaxIndexNameLen) { MojErrThrowMsg(MojErrDbInvalidIndexName, _T("db: index name '%s' invalid: length is %zd chars, max is %zd"), name.data(), name.length(), MaxIndexNameLen); } for (MojString::ConstIterator i = name.begin(); i < name.end(); ++i) { if (!MojIsAlNum(*i) && *i != _T('_')) { MojErrThrowMsg(MojErrDbInvalidIndexName, _T("db: index name '%s' invalid: char at position %zd not allowed"), name.data(), (MojSize) (i - name.begin())); } } return MojErrNone; }
MojErr MojDbServiceHandler::handleMerge(MojServiceMessage* msg, MojObject& payload, MojDbReq& req) { MojAssert(msg); MojLogTrace(s_log); MojErr err = MojErrNone; MojUInt32 count = 0; MojObject obj; if (payload.get(MojDbServiceDefs::ObjectsKey, obj)) { if (payload.contains(MojDbServiceDefs::QueryKey)) MojErrThrowMsg(MojErrInvalidArg, _T("db: cannot have both an objects param and a query param")); MojObject::ArrayIterator begin; bool ignoreM = false; payload.get(MojDbServiceDefs::IgnoreMissingKey, ignoreM); MojUInt32 mflags = MojDb::FlagNone; if (ignoreM) mflags = mflags | MojDb::FlagIgnoreMissing; err = obj.arrayBegin(begin); MojErrCheck(err); MojObject::ConstArrayIterator end = obj.arrayEnd(); err = m_db.merge(begin, end, mflags, req); MojErrCheck(err); err = formatPut(msg, begin, end); // TO DO: we need to drop non-existing objects MojErrCheck(err); } else if (payload.get(MojDbServiceDefs::QueryKey, obj)) { MojObject props; err = payload.getRequired(MojDbServiceDefs::PropsKey, props); MojErrCheck(err); bool ignoreM = false; if (payload.get(MojDbServiceDefs::IgnoreMissingKey, ignoreM)) MojErrThrowMsg(MojErrInvalidArg, _T("db: ignoreMissing - invalid option for merge query")); MojDbQuery query; err = query.fromObject(obj); MojErrCheck(err); MojUInt32 queryCount = 0; err = m_db.merge(query, props, queryCount, MojDb::FlagNone, req); MojErrCheck(err); count += queryCount; err = formatCount(msg, count); MojErrCheck(err); } else { MojErrThrowMsg(MojErrInvalidArg, _T("db: either objects or query param required for merge")); } return MojErrNone; }
MojErr MojDbQuery::validateFind() const { for (WhereMap::ConstIterator i = m_whereClauses.begin(); i != m_whereClauses.end(); ++i) { // if clause is order-defining, verify that it matches the order specified if (i->definesOrder() && !(m_orderProp.empty() || m_orderProp == i.key())) MojErrThrowMsg(MojErrDbInvalidQuery, _T("db: query order not compatible with where clause")); // disallow search operator if (i->lowerOp() == OpSearch) { MojErrThrowMsg(MojErrDbInvalidQuery, _T("db: search operator not allowed in find")); } } if (!m_filterClauses.empty()) { MojErrThrowMsg(MojErrDbInvalidQuery, _T("db: filter not allowed in find")); } 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 MojDbIndex::addProp(const MojObject& propObj, bool pushFront) { LOG_TRACE("Entering function %s", __FUNCTION__); MojAssert(!isOpen()); // create extractor MojRefCountedPtr<MojDbExtractor> extractor; MojErr err = createExtractor(propObj, extractor); MojErrCheck(err); const MojString& name = extractor->name(); // check for repeats if (m_propNames.find(name) != MojInvalidSize) { MojErrThrowMsg(MojErrDbInvalidIndex, _T("db: invalid index '%s' - property '%s' repeated"), m_name.data(), name.data()); } // add to vecs if (pushFront) { err = m_propNames.insert(0, 1, name); MojErrCheck(err); err = m_props.insert(0, 1, extractor); MojErrCheck(err); } else { err = m_propNames.push(name); MojErrCheck(err); err = m_props.push(extractor); MojErrCheck(err); } return MojErrNone; }
MojErr MojDbKind::configureRevSets(const MojObject& obj) { MojLogTrace(s_log); m_revSets.clear(); MojSet<MojString> setNames; MojObject array; if (obj.get(RevisionSetsKey, array)) { MojObject::ConstArrayIterator end = array.arrayEnd(); for (MojObject::ConstArrayIterator i = array.arrayBegin(); i != end; ++i) { MojRefCountedPtr<MojDbRevisionSet> set(new MojDbRevisionSet()); MojAllocCheck(set.get()); MojErr err = set->fromObject(*i); MojErrCheck(err); if (setNames.contains(set->name())) { MojErrThrowMsg(MojErrDbInvalidRevisionSet, _T("db: cannot repeat revSet name: '%s'"), set->name().data()); } err = setNames.put(set->name()); MojErrCheck(err); err = m_revSets.push(set); MojErrCheck(err); } } return MojErrNone; }
/** * is kind exist? */ MojErr MojDbShardManagerTest::verifyKindExistance (MojString kindId, MojDb& db) { bool foundOurKind = false; MojString str; //for debug //kinds map MojDbKindEngine::KindMap& map = db.kindEngine()->kindMap(); for (MojDbKindEngine::KindMap::ConstIterator it = map.begin(); it != map.end(); ++it) { str = it.key(); if(kindId == str) { foundOurKind = true; break; } } if (!foundOurKind) MojErrThrowMsg(MojErrDbKindNotRegistered, "Kind %s not found in kindMap", kindId.data()); return MojErrNone; }
MojErr MojDbKind::update(MojObject* newObj, const MojObject* oldObj, MojDbOp op, MojDbReq& req, bool checkSchema) { MojLogTrace(s_log); MojErr err = checkPermission(op, req); MojErrCheck(err); err = req.curKind(this); MojErrCheck(err); #if defined(TESTDBKIND) MojString s; MojErr e2; if (oldObj) { e2 = oldObj->toJson(s); MojLogInfo(s_log, _T("Kind_Update_OldObj: %s ;\n"), s.data()); } if (newObj) { e2 = newObj->toJson(s); MojLogInfo(s_log, _T("Kind_Update_NewObj: %s ;\n"), s.data()); } #endif if (newObj) { // add the _backup property if not set if (m_backup && !newObj->contains(MojDb::SyncKey)) { err = newObj->putBool(MojDb::SyncKey, true); MojErrCheck(err); } // TEMPORARY!!! This should be done in pre-update to also check parent kinds // warning message comes from preUpdate if(checkSchema) { MojSchema::Result res; err = m_schema.validate(*newObj, res); MojErrCheck(err); if (!res.valid()) { MojErrThrowMsg(MojErrSchemaValidation, _T("schema validation failed for kind '%s': %s"), m_id.data(), res.msg().data()); } } } // update revSets and validate schema err = preUpdate(newObj, oldObj, req); MojErrCheck(err); // update indexes MojVector<MojDbKind*> kindVec; MojInt32 idxcount = 0; err = updateIndexes(newObj, oldObj, req, op, kindVec, idxcount); MojLogInfo(s_log, _T("Kind_UpdateIndexes_End: %s; supers = %zu; indexcount = %zu; updated = %d \n"), this->id().data(), m_supers.size(), m_indexes.size(), idxcount); MojErrCheck(err); return MojErrNone; }
MojErr MojDb::requireOpen() { if (!m_isOpen) { MojErrThrowMsg(MojErrNotOpen, _T("db not open")); } return MojErrNone; }
TEST(SignalTest, easy_peasy) { int signaled = -1; MojEasySignal<int> signal; decltype(signal)::EasySlot slot([&](int x) { signaled = x; return MojErrNone; }); signal.connect(slot.slot()); MojExpectNoErr( signal.call(42) ); EXPECT_EQ(42, signaled); MojExpectNoErr( signal.fire(45) ); EXPECT_EQ(45, signaled); MojExpectNoErr( signal.fire(41) ); EXPECT_EQ(45, signaled); MojEasySlot<int> badSlot([&](int x) { signaled = x; MojErrThrowMsg(MojErrUnknown, "intentional"); }); signal.connect(badSlot.slot()); EXPECT_EQ( MojErrUnknown, signal.call(41) ); EXPECT_EQ(41, signaled); EXPECT_EQ( MojErrUnknown, signal.call(41) ); EXPECT_EQ(41, signaled); }
MojErr MojDbKindEngine::getKind(const MojChar* kindName, MojDbKind*& kind) { MojLogTrace(s_log); KindMap::ConstIterator iter = m_kinds.find(kindName); if (iter == m_kinds.end()) MojErrThrowMsg(MojErrDbKindNotRegistered, _T("kind not registered: '%s'"), kindName); kind = iter.value().get(); return MojErrNone; }
MojErr MojLogger::levelFromString(const MojChar* str, Level& levelOut) { MojAssertNoLog(str); for (int i = 0; i <= LevelMax; ++i) { if (!MojStrCmp(str, s_levelNames[i])) { levelOut = (Level) i; return MojErrNone; } } MojErrThrowMsg(MojErrLogLevelNotFound, _T("log: level not found: '%s'"), str); }
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 MojLogEngine::createAppender(const MojObject& conf, const MojChar* name, MojAutoPtr<MojLogAppender>& appenderOut) { MojString type; MojErr err = conf.getRequired(TypeKey, type); MojErrCheck(err); // TODO: use some sort of factory registry to do this if (type == _T("file")) { MojString path; err = conf.getRequired(PathKey, path); MojErrCheck(err); MojAutoPtr<MojFileAppender> appender(new MojFileAppender()); MojAllocCheck(appender.get()); err = appender->open(path.data()); MojErrCheck(err); appenderOut = appender; } else if (type == _T("stderr")) { MojAutoPtr<MojFileAppender> appender(new MojFileAppender()); MojAllocCheck(appender.get()); appender->open(MojStdErrFile); appenderOut = appender; } else if (type == _T("stdout")) { MojAutoPtr<MojFileAppender> appender(new MojFileAppender()); MojAllocCheck(appender.get()); appender->open(MojStdOutFile); appenderOut = appender; } #ifdef MOJ_USE_SYSLOG else if (type == _T("syslog")) { MojAutoPtr<MojSyslogAppender> appender(new MojSyslogAppender()); MojAllocCheck(appender.get()); appender->open(name); appenderOut = appender; } #endif // MOJ_USE_SYSLOG #ifdef MOJ_USE_PMLOG else if (type == _T("pmlog")) { MojAutoPtr<MojPmLogAppender> appender(new MojPmLogAppender()); MojAllocCheck(appender.get()); appenderOut = appender; } #endif // MOJ_USE_PMLOG else { MojErrThrowMsg(MojErrLogAppenderNotFound, _T("log: appender not found '%s'"), type.data()); } return MojErrNone; }
// although we might have only 1 real Level DB - just to be safe and consistent // provide interface to add/drop multiple DB MojErr MojDbSandwichEngine::drop(const MojChar* path, MojDbStorageTxn* txn) { MojAssert(m_isOpen); MojThreadGuard guard(m_dbMutex); // close sequences for (SequenceVec::ConstIterator i = m_seqs.begin(); i != m_seqs.end(); ++i) { MojErr err = (*i)->close(); MojErrCheck(err); } m_seqs.clear(); // TODO: drop transaction // Must drop database by path! Throw as not implemented error MojErrThrowMsg(MojErrNotImplemented, _T("Not fully implemented")) ; }
MojErr MojDbServiceHandler::handleReserveIds(MojServiceMessage* msg, MojObject& payload, MojDbReq& req) { MojAssert(msg); MojLogTrace(s_log); // check space level if( MojDbServiceHandlerInternal::spaceAlertLevel() == MojDbServiceHandlerInternal::AlertLevelHigh) return MojErrDbQuotaExceeded; MojInt64 count; MojErr err = payload.getRequired(MojDbServiceDefs::CountKey, count); MojErrCheck(err); MojObjectVisitor& writer = msg->writer(); err = writer.beginObject(); MojErrCheck(err); err = writer.boolProp(MojServiceMessage::ReturnValueKey, true); MojErrCheck(err); err = writer.propName(MojDbServiceDefs::IdsKey); MojErrCheck(err); err = writer.beginArray(); MojErrCheck(err); if (count > (MojInt64) MaxReserveIdCount) { MojErrThrowMsg(MojErrDbMaxCountExceeded, _T("cannot reserve more than %d ids"), MaxReserveIdCount); } for (MojInt64 i = 0; i < count; ++i) { MojObject id; err = m_db.reserveId(id); MojErrCheck(err); err = id.visit(writer); MojErrCheck(err); } err = writer.endArray(); MojErrCheck(err); err = writer.endObject(); MojErrCheck(err); return MojErrNone; }
MojErr MojDbIndex::fromObject(const MojObject& obj, const MojString& locale) { LOG_TRACE("Entering function %s", __FUNCTION__); // check name MojString name; MojErr err = obj.getRequired(NameKey, name); MojErrCheck(err); err = validateName(name); MojErrCheck(err); m_name = name; m_locale = locale; if(m_locale == _T("en_CN")) m_locale = locale; // get deleted flag bool includeDel = false; if (obj.get(IncludeDeletedKey, includeDel)) { incDel(includeDel); } // add props MojObject props; err = obj.getRequired(PropsKey, props); MojErrCheck(err); MojObject propObj; MojSize j = 0; while (props.at(j++, propObj)) { err = addProp(propObj); MojErrCheck(err); } if (m_props.empty()) { MojErrThrowMsg(MojErrDbInvalidIndex, _T("db: no properties specified in index '%s'"), name.data()); } m_obj = obj; return MojErrNone; }
MojErr MojDbServiceHandler::handleDel(MojServiceMessage* msg, MojObject& payload, MojDbReq& req) { MojAssert(msg); MojLogTrace(s_log); MojErr err = MojErrNone; MojUInt32 count = 0; MojUInt32 flags = MojDb::FlagNone; bool purge = false; if (payload.get(MojDbServiceDefs::PurgeKey, purge) && purge) { flags |= MojDb::FlagPurge; } MojObject obj; if (payload.get(MojDbServiceDefs::IdsKey, obj)) { if (payload.contains(MojDbServiceDefs::QueryKey)) MojErrThrowMsg(MojErrInvalidArg, _T("db: cannot have both an objects argument and a query argument")); MojObject deletedObjects; err = m_db.del(obj.arrayBegin(), obj.arrayEnd(), count, deletedObjects, flags, req); MojErrCheck(err); err = formatPut(msg, deletedObjects.arrayBegin(), deletedObjects.arrayEnd()); MojErrCheck(err); } else if (payload.get(MojDbServiceDefs::QueryKey, obj)) { MojDbQuery query; err = query.fromObject(obj); MojErrCheck(err); MojUInt32 queryCount = 0; err = m_db.del(query, queryCount, flags, req); MojErrCheck(err); count += queryCount; err = formatCount(msg, count); 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 MojDbServiceHandler::findImpl(MojServiceMessage* msg, MojObject& payload, MojDbReq& req, MojDbCursor& cursor, bool doCount) { MojAssert(msg); MojLogTrace(s_log); MojObject queryObj; MojErr err = payload.getRequired(MojDbServiceDefs::QueryKey, queryObj); MojErrCheck(err); bool doWatch = false; payload.get(MojDbServiceDefs::WatchKey, doWatch); MojDbQuery query; err = query.fromObject(queryObj); MojErrCheck(err); MojUInt32 limit = query.limit(); if (limit == MojDbQuery::LimitDefault){ query.limit(MaxQueryLimit); } else if (limit > MaxQueryLimit) { MojErrThrowMsg(MojErrDbInvalidQuery, _T("db: limit greater than %d not allowed"), MaxQueryLimit); } if (doWatch) { MojRefCountedPtr<Watcher> watcher(new Watcher(msg)); MojAllocCheck(watcher.get()); err = m_db.find(query, cursor, watcher->m_watchSlot, req); MojErrCheck(err); } else { err = m_db.find(query, cursor, req); MojErrCheck(err); } // append results MojObjectVisitor& writer = msg->writer(); err = writer.beginObject(); MojErrCheck(err); err = writer.boolProp(MojServiceMessage::ReturnValueKey, true); MojErrCheck(err); err = writer.propName(MojDbServiceDefs::ResultsKey); MojErrCheck(err); err = writer.beginArray(); MojErrCheck(err); err = cursor.visit(writer); MojErrCheck(err); err = writer.endArray(); MojErrCheck(err); // append next page MojDbQuery::Page page; err = cursor.nextPage(page); MojErrCheck(err); if (!page.empty()) { MojObject pageObj; err = page.toObject(pageObj); MojErrCheck(err); err = writer.objectProp(MojDbServiceDefs::NextKey, pageObj); MojErrCheck(err); } // append count if (doCount) { MojUInt32 count = 0; err = cursor.count(count); MojErrCheck(err); err = writer.intProp(MojDbServiceDefs::CountKey, (MojInt64) count); MojErrCheck(err); } err = writer.endObject(); MojErrCheck(err); if (doWatch) { // if this is a watched query, it cannot be part of a batch so it's safe to reply here err = msg->reply(); MojErrCheck(err); } // notifications can fire any time after the cursor is closed, // so don't close it until after sending the reply. err = cursor.close(); MojErrCheck(err); return MojErrNone; }
MojErr MojDbKind::configureIndexes(const MojObject& obj, const MojString& locale, MojDbReq& req) { MojLogTrace(s_log); // make sure indexes changes count against our usage MojErr err = req.curKind(this); MojErrCheck(err); // add default id index to set MojObject idIndex; err = idIndex.fromJson(IdIndexJson); MojErrCheck(err); ObjectSet newIndexObjects; err = newIndexObjects.put(idIndex); MojErrCheck(err); // change back to a set and use contains MojSet<MojString> indexNames; MojString defaultIdxName; err = defaultIdxName.assign(IdIndexName); MojErrCheck(err); err = indexNames.put(defaultIdxName); MojErrCheck(err); // add indexes to set to uniquify and order them MojObject indexArray; if (obj.get(IndexesKey, indexArray)) { MojObject::ConstArrayIterator end = indexArray.arrayEnd(); for (MojObject::ConstArrayIterator i = indexArray.arrayBegin(); i != end; ++i) { MojString indexName; err = i->getRequired(MojDbIndex::NameKey, indexName); MojErrCheck(err); err = indexName.toLower(); MojErrCheck(err); if (!indexNames.contains(indexName)) { MojObject idx = *i; // make sure we keep the lower-cased index name err = idx.putString(MojDbIndex::NameKey, indexName); MojErrCheck(err); err = newIndexObjects.put(idx); MojErrCheck(err); err = indexNames.put(indexName); MojErrCheck(err); } else { MojErrThrowMsg(MojErrDbInvalidIndexName, _T("db: cannot repeat index name: '%s'"), indexName.data()); } } } // figure out what to add and what to delete ObjectSet toDrop; err = m_indexObjects.diff(newIndexObjects, toDrop); MojErrCheck(err); ObjectSet toAdd; err = newIndexObjects.diff(m_indexObjects, toAdd); MojErrCheck(err); // drop deleted indexes IndexVec newIndexes; for (IndexVec::ConstIterator i = m_indexes.begin(); i != m_indexes.end(); ++i) { if (toDrop.contains((*i)->object())) { err = dropIndex(i->get(), req); MojErrCheck(err); } else { err = newIndexes.push(*i); MojErrCheck(err); } } // add new indexes for (ObjectSet::ConstIterator i = toAdd.begin(); i != toAdd.end(); ++i) { // create index MojRefCountedPtr<MojDbIndex> index(new MojDbIndex(this, m_kindEngine)); MojAllocCheck(index.get()); err = index->fromObject(*i, locale); MojErrCheck(err); // open index err = openIndex(index.get(), req); MojErrCheck(err); err = newIndexes.push(index); MojErrCheck(err); } // sort indexes by the prop vec so that for indexes that share prop prefixes, the shortest one comes first err = newIndexes.sort(); MojErrCheck(err); // update members m_indexObjects = newIndexObjects; m_indexes = newIndexes; return MojErrNone; }
MojErr MojDbQuery::fromObject(const MojObject& obj) { // TODO: validate against query schema bool found; MojErr err; MojObject array; MojString str; // distinct found = false; err = obj.get(DistinctKey, str, found); MojErrCheck(err); if (found) { err = distinct(str); MojErrCheck(err); // if "distinct" is set, force "distinct" column into "select". err = select(str); MojErrCheck(err); // order err = order(str); MojErrCheck(err); } else { // select if (obj.get(SelectKey, array)) { if(array.empty()) { MojErrThrowMsg(MojErrDbInvalidQuery, _T("db: select clause but no selected properties")); } MojObject prop; MojSize i = 0; while (array.at(i++, prop)) { MojErr err = prop.stringValue(str); MojErrCheck(err); err = select(str); MojErrCheck(err); } } // order found = false; err = obj.get(OrderByKey, str, found); MojErrCheck(err); if (found) { err = order(str); MojErrCheck(err); } } // from err = obj.getRequired(FromKey, str); MojErrCheck(err); err = from(str); MojErrCheck(err); // where if (obj.get(WhereKey, array)) { err = addClauses(m_whereClauses, array); MojErrCheck(err); } // filter if (obj.get(FilterKey, array)) { err = addClauses(m_filterClauses, array); MojErrCheck(err); } // desc bool descVal; if (obj.get(DescKey, descVal)) { desc(descVal); } // limit MojInt64 lim; if (obj.get(LimitKey, lim)) { if (lim < 0) MojErrThrowMsg(MojErrDbInvalidQuery, _T("db: negative query limit")); } else { lim = LimitDefault; } limit((MojUInt32) lim); // page MojObject pageObj; if (obj.get(PageKey, pageObj)) { Page pagec; err = pagec.fromObject(pageObj); MojErrCheck(err); page(pagec); } bool incDel = false; if (obj.get(IncludeDeletedKey, incDel) && incDel) { err = includeDeleted(); 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; }
MojErr MojDb::requireNotOpen() { if (m_isOpen) MojErrThrowMsg(MojErrAlreadyOpen, _T("db already open")); return MojErrNone; }