void insertObjects(const char *ns, const vector<BSONObj> &objs, bool keepGoing, uint64_t flags, bool logop ) { StringData _ns(ns); if (NamespaceString::isSystem(_ns)) { massert(16748, "need transaction to run insertObjects", cc().txnStackSize() > 0); uassert(10095, "attempt to insert in reserved database name 'system'", nsToDatabaseSubstring(_ns) != "system"); massert(16750, "attempted to insert multiple objects into a system namspace at once", objs.size() == 1); // Trying to insert into a system collection. Fancy side-effects go here: if (nsToCollectionSubstring(ns) == "system.indexes") { BSONObj obj = stripDropDups(objs[0]); NamespaceDetails *d = getAndMaybeCreateNS(obj["ns"].Stringdata(), logop); bool ok = d->ensureIndex(obj); if (!ok) { // Already had that index return; } // Now we have to actually insert that document into system.indexes, we may have // modified it with stripDropDups. vector<BSONObj> newObjs; newObjs.push_back(obj); _insertObjects(ns, newObjs, keepGoing, flags, logop); return; } else if (!legalClientSystemNS(ns, true)) { uasserted(16459, str::stream() << "attempt to insert in system namespace '" << ns << "'"); } } _insertObjects(ns, objs, keepGoing, flags, logop); }
static int handle_system_collection_insert(const char *ns, const BSONObj &obj, bool logop) { // Trying to insert into a system collection. Fancy side-effects go here: // TODO: see insert_checkSys if (mongoutils::str::endsWith(ns, ".system.indexes")) { // obj is something like this: // { _id: ObjectId('511d34f6d3080c48017a14d0'), ns: "test.leif", key: { a: -1.0 }, name: "a_-1", unique: true } const string &coll = obj["ns"].String(); NamespaceDetails *details = getAndMaybeCreateNS(coll.c_str(), logop); BSONObj key = obj["key"].Obj(); int i = details->findIndexByKeyPattern(key); if (i >= 0) { return ASSERT_ID_DUPKEY; } else { details->createIndex(obj); } } else if (legalClientSystemNS(ns, true)) { if (mongoutils::str::endsWith(ns, ".system.users")) { uassert( 14051 , "system.users entry needs 'user' field to be a string", obj["user"].type() == String ); uassert( 14052 , "system.users entry needs 'pwd' field to be a string", obj["pwd"].type() == String ); uassert( 14053 , "system.users entry needs 'user' field to be non-empty", obj["user"].String().size() ); uassert( 14054 , "system.users entry needs 'pwd' field to be non-empty", obj["pwd"].String().size() ); } } else { uasserted(16459, str::stream() << "attempt to insert in system namespace '" << ns << "'"); } return 0; }
void insertObjects(const char *ns, const vector<BSONObj> &objs, bool keepGoing, uint64_t flags, bool logop ) { if (mongoutils::str::contains(ns, "system.")) { massert(16748, "need transaction to run insertObjects", cc().txnStackSize() > 0); uassert(10095, "attempt to insert in reserved database name 'system'", !mongoutils::str::startsWith(ns, "system.")); massert(16750, "attempted to insert multiple objects into a system namspace at once", objs.size() == 1); if (handle_system_collection_insert(ns, objs[0], logop) != 0) { return; } } NamespaceDetails *details = getAndMaybeCreateNS(ns, logop); NamespaceDetailsTransient *nsdt = &NamespaceDetailsTransient::get(ns); for (size_t i = 0; i < objs.size(); i++) { const BSONObj &obj = objs[i]; try { uassert( 10059 , "object to insert too large", obj.objsize() <= BSONObjMaxUserSize); BSONObjIterator i( obj ); while ( i.more() ) { BSONElement e = i.next(); uassert( 13511 , "document to insert can't have $ fields" , e.fieldName()[0] != '$' ); } uassert( 16440 , "_id cannot be an array", obj["_id"].type() != Array ); BSONObj objModified = obj; BSONElementManipulator::lookForTimestamps(objModified); if (details->isCapped() && logop) { // unfortunate hack we need for capped collections // we do this because the logic for generating the pk // and what subsequent rows to delete are buried in the // namespace details object. There is probably a nicer way // to do this, but this works. details->insertObjectIntoCappedAndLogOps(objModified, flags); if (nsdt != NULL) { nsdt->notifyOfWriteOp(); } } else { insertOneObject(details, nsdt, objModified, flags); // may add _id field if (logop) { OpLogHelpers::logInsert(ns, objModified, &cc().txn()); } } } catch (const UserException &) { if (!keepGoing || i == objs.size() - 1) { throw; } } } }
// Does not check magic system collection inserts. void _insertObjects(const char *ns, const vector<BSONObj> &objs, bool keepGoing, uint64_t flags, bool logop ) { NamespaceDetails *details = getAndMaybeCreateNS(ns, logop); for (size_t i = 0; i < objs.size(); i++) { const BSONObj &obj = objs[i]; try { uassert( 10059 , "object to insert too large", obj.objsize() <= BSONObjMaxUserSize); BSONObjIterator i( obj ); while ( i.more() ) { BSONElement e = i.next(); // check no $ modifiers. note we only check top level. // (scanning deep would be quite expensive) uassert( 13511 , "document to insert can't have $ fields" , e.fieldName()[0] != '$' ); // check no regexp for _id (SERVER-9502) if (str::equals(e.fieldName(), "_id")) { uassert(17033, "can't use a regex for _id", e.type() != RegEx); } } uassert( 16440 , "_id cannot be an array", obj["_id"].type() != Array ); BSONObj objModified = obj; BSONElementManipulator::lookForTimestamps(objModified); if (details->isCapped() && logop) { // unfortunate hack we need for capped collections // we do this because the logic for generating the pk // and what subsequent rows to delete are buried in the // namespace details object. There is probably a nicer way // to do this, but this works. details->insertObjectIntoCappedAndLogOps(objModified, flags); details->notifyOfWriteOp(); } else { insertOneObject(details, objModified, flags); // may add _id field if (logop) { OpLogHelpers::logInsert(ns, objModified); } } } catch (const UserException &) { if (!keepGoing || i == objs.size() - 1) { throw; } } } }
UpdateResult _updateObjects( const char* ns, const BSONObj& updateobj, const BSONObj& patternOrig, bool upsert, bool multi, bool logop , OpDebug& debug, bool fromMigrate, const QueryPlanSelectionPolicy& planPolicy ) { TOKULOG(2) << "update: " << ns << " update: " << updateobj << " query: " << patternOrig << " upsert: " << upsert << " multi: " << multi << endl; debug.updateobj = updateobj; NamespaceDetails *d = getAndMaybeCreateNS(ns, logop); auto_ptr<ModSet> mods; const bool isOperatorUpdate = updateobj.firstElementFieldName()[0] == '$'; bool modsAreIndexed = false; if ( isOperatorUpdate ) { if ( d->indexBuildInProgress() ) { set<string> bgKeys; d->inProgIdx().keyPattern().getFieldNames(bgKeys); mods.reset( new ModSet(updateobj, d->indexKeys(), &bgKeys) ); } else { mods.reset( new ModSet(updateobj, d->indexKeys()) ); } modsAreIndexed = mods->isIndexed(); } int idIdxNo = -1; if ( planPolicy.permitOptimalIdPlan() && !multi && !modsAreIndexed && (idIdxNo = d->findIdIndex()) >= 0 && mayUpdateById(d, patternOrig) ) { debug.idhack = true; IndexDetails &idx = d->idx(idIdxNo); BSONObj pk = idx.getKeyFromQuery(patternOrig); TOKULOG(3) << "_updateObjects using simple _id query, pattern " << patternOrig << ", pk " << pk << endl; UpdateResult result = _updateById( pk, isOperatorUpdate, mods.get(), d, ns, updateobj, patternOrig, logop, debug, fromMigrate); if ( result.existing || ! upsert ) { return result; } else if ( upsert && ! isOperatorUpdate && ! logop) { debug.upsert = true; BSONObj objModified = updateobj; insertAndLog( ns, d, objModified, logop, fromMigrate ); return UpdateResult( 0 , 0 , 1 , updateobj ); } } int numModded = 0; debug.nscanned = 0; shared_ptr<Cursor> c = getOptimizedCursor( ns, patternOrig, BSONObj(), planPolicy ); if( c->ok() ) { set<BSONObj> seenObjects; MatchDetails details; auto_ptr<ClientCursor> cc; do { debug.nscanned++; if ( mods.get() && mods->hasDynamicArray() ) { // The Cursor must have a Matcher to record an elemMatchKey. But currently // a modifier on a dynamic array field may be applied even if there is no // elemMatchKey, so a matcher cannot be required. //verify( c->matcher() ); details.requestElemMatchKey(); } if ( !c->currentMatches( &details ) ) { c->advance(); continue; } BSONObj currPK = c->currPK(); if ( c->getsetdup( currPK ) ) { c->advance(); continue; } BSONObj currentObj = c->current(); BSONObj pattern = patternOrig; if ( logop ) { BSONObjBuilder idPattern; BSONElement id; // NOTE: If the matching object lacks an id, we'll log // with the original pattern. This isn't replay-safe. // It might make sense to suppress the log instead // if there's no id. if ( currentObj.getObjectID( id ) ) { idPattern.append( id ); pattern = idPattern.obj(); } else { uassert( 10157 , "multi-update requires all modified objects to have an _id" , ! multi ); } } /* look for $inc etc. note as listed here, all fields to inc must be this type, you can't set some regular ones at the moment. */ struct LogOpUpdateDetails loud; loud.logop = logop; loud.ns = ns; loud.fromMigrate = fromMigrate; if ( isOperatorUpdate ) { if ( multi ) { // Make our own copies of the currPK and currentObj before we invalidate // them by advancing the cursor. currPK = currPK.copy(); currentObj = currentObj.copy(); // Advance past the document to be modified. This used to be because of SERVER-5198, // but TokuMX does it because we want to avoid needing to do manual deduplication // of this PK on the next iteration if the current update modifies the next // entry in the index. For example, an index scan over a:1 with mod {$inc: {a:1}} // would cause every other key read to be a duplicate if we didn't advance here. while ( c->ok() && currPK == c->currPK() ) { c->advance(); } // Multi updates need to do their own deduplication because updates may modify the // keys the cursor is in the process of scanning over. if ( seenObjects.count( currPK ) ) { continue; } else { seenObjects.insert( currPK ); } } ModSet* useMods = mods.get(); auto_ptr<ModSet> mymodset; if ( details.hasElemMatchKey() && mods->hasDynamicArray() ) { useMods = mods->fixDynamicArray( details.elemMatchKey() ); mymodset.reset( useMods ); } auto_ptr<ModSetState> mss = useMods->prepare( currentObj ); updateUsingMods( d, currPK, currentObj, *mss, &loud ); numModded++; if ( ! multi ) return UpdateResult( 1 , 1 , numModded , BSONObj() ); continue; } // end if operator is update uassert( 10158 , "multi update only works with $ operators" , ! multi ); updateNoMods( d, currPK, currentObj, updateobj, &loud ); return UpdateResult( 1 , 0 , 1 , BSONObj() ); } while ( c->ok() ); } // endif if ( numModded ) return UpdateResult( 1 , 1 , numModded , BSONObj() ); if ( upsert ) { BSONObj newObj = updateobj; if ( updateobj.firstElementFieldName()[0] == '$' ) { // upsert of an $operation. build a default object BSONObj newObj = mods->createNewFromQuery( patternOrig ); debug.fastmodinsert = true; insertAndLog( ns, d, newObj, logop, fromMigrate ); return UpdateResult( 0 , 1 , 1 , newObj ); } uassert( 10159 , "multi update only works with $ operators" , ! multi ); debug.upsert = true; insertAndLog( ns, d, newObj, logop, fromMigrate ); return UpdateResult( 0 , 0 , 1 , newObj ); } return UpdateResult( 0 , isOperatorUpdate , 0 , BSONObj() ); }