コード例 #1
0
ファイル: MojDb.cpp プロジェクト: KyleMaas/db8
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;
}
コード例 #2
0
ファイル: MojDbIndex.cpp プロジェクト: sailesharya/db8
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;
}
コード例 #3
0
ファイル: MojDbIndex.cpp プロジェクト: sailesharya/db8
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;
}
コード例 #4
0
ファイル: MojDbIndex.cpp プロジェクト: sailesharya/db8
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;
}
コード例 #5
0
ファイル: MojDbKindState.cpp プロジェクト: feniksa/indb8
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;
}
コード例 #6
0
ファイル: MojDbKindState.cpp プロジェクト: feniksa/indb8
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;
}
コード例 #7
0
ファイル: MojDbIndex.cpp プロジェクト: sailesharya/db8
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;
}
コード例 #8
0
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;
}
コード例 #9
0
ファイル: MojDbIndex.cpp プロジェクト: sailesharya/db8
MojErr MojDbIndex::drop(MojDbReq& req)
{
    LOG_TRACE("Entering function %s", __FUNCTION__);
	MojAssert(isOpen());

	MojErr err = m_index->drop(req.txn());
	MojErrCheck(err);

	return MojErrNone;
}
コード例 #10
0
ファイル: MojDbIndex.cpp プロジェクト: KyleMaas/db8
MojErr MojDbIndex::drop(MojDbReq& req)
{
	MojAssert(isOpen());
	MojLogTrace(s_log);

	MojErr err = m_index->drop(req.txn());
	MojErrCheck(err);

	return MojErrNone;
}
コード例 #11
0
ファイル: MojDbQuotaEngine.cpp プロジェクト: ctbrowser/db8
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;
}
コード例 #12
0
ファイル: MojDbKindState.cpp プロジェクト: feniksa/indb8
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;
}
コード例 #13
0
ファイル: MojDbKind.cpp プロジェクト: webOS101/db8
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;
}
コード例 #14
0
ファイル: MojDb.cpp プロジェクト: KyleMaas/db8
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;
}
コード例 #15
0
ファイル: MojDbKindEngine.cpp プロジェクト: feniksa/indb8
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;
}
コード例 #16
0
ファイル: MojDb.cpp プロジェクト: KyleMaas/db8
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;
}
コード例 #17
0
ファイル: MojDb.cpp プロジェクト: KyleMaas/db8
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;
}
コード例 #18
0
ファイル: MojDb.cpp プロジェクト: KyleMaas/db8
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;
}
コード例 #19
0
ファイル: MojDbKind.cpp プロジェクト: webOS101/db8
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;
}
コード例 #20
0
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;
}
コード例 #21
0
ファイル: MojDb.cpp プロジェクト: KyleMaas/db8
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;
}
コード例 #22
0
ファイル: MojDb.cpp プロジェクト: KyleMaas/db8
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;
}
コード例 #23
0
ファイル: MojDb.cpp プロジェクト: KyleMaas/db8
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;
}