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 ensureIdIndexForNewNs(const char *ns) { if ( ( strstr( ns, ".system." ) == 0 || legalClientSystemNS( ns , false ) ) && strstr( ns, FREELIST_NS ) == 0 ) { LOG( 1 ) << "adding _id index for collection " << ns << endl; ensureHaveIdIndex( ns, false ); } }
void validateUpdate( const char* ns , const BSONObj& updateobj, const BSONObj& patternOrig ) { uassert( 10155 , "cannot update reserved $ collection", strchr(ns, '$') == 0 ); if ( strstr(ns, ".system.") ) { /* dm: it's very important that system.indexes is never updated as IndexDetails has pointers into it */ uassert( 10156, str::stream() << "cannot update system collection: " << ns << " q: " << patternOrig << " u: " << updateobj, legalClientSystemNS( ns , true ) ); } }
/* ns: namespace, e.g. <database>.<collection> pattern: the "where" clause / criteria justOne: stop after 1 match */ long long deleteObjects(const char *ns, BSONObj pattern, bool justOne, bool logop) { if (NamespaceString::isSystem(ns)) { uassert(12050, "cannot delete from system namespace", legalClientSystemNS(ns, true)); } if (!NamespaceString::normal(ns)) { log() << "cannot delete from collection with reserved $ in name: " << ns << endl; uasserted(10100, "cannot delete from collection with reserved $ in name"); } return _deleteObjects(ns, pattern, justOne, logop); }
bool NOINLINE_DECL insert_checkSys(const char *sys, const char *ns, bool& wouldAddIndex, const void *obuf, bool god) { uassert( 10095 , "attempt to insert in reserved database name 'system'", sys != ns); if ( strstr(ns, ".system.") ) { // later:check for dba-type permissions here if have that at some point separate if (nsToCollectionSubstring(ns) == "system.indexes") wouldAddIndex = true; else if ( legalClientSystemNS( ns , true ) ) { if ( obuf && StringData(ns) == StringData(".system.users", StringData::LiteralTag()) ) { BSONObj t( reinterpret_cast<const char *>( obuf ) ); V2UserDocumentParser parser; uassertStatusOK(parser.checkValidUserDocument(t)); } } else if ( !god ) { uasserted(16459, str::stream() << "attempt to insert in system namespace '" << ns << "'"); } } return true; }
UpdateResult updateObjects( const char* ns, const BSONObj& updateobj, const BSONObj& patternOrig, bool upsert, bool multi, bool logop , OpDebug& debug, bool fromMigrate, const QueryPlanSelectionPolicy& planPolicy ) { uassert( 10155 , "cannot update reserved $ collection", strchr(ns, '$') == 0 ); if ( strstr(ns, ".system.") ) { /* dm: it's very important that system.indexes is never updated as IndexDetails has pointers into it */ uassert( 10156, str::stream() << "cannot update system collection: " << ns << " q: " << patternOrig << " u: " << updateobj, legalClientSystemNS( ns , true ) ); } UpdateResult ur = _updateObjects(false, ns, updateobj, patternOrig, upsert, multi, logop, debug, 0, fromMigrate, planPolicy ); debug.nupdated = ur.num; return ur; }
bool Cloner::go(const char *masterHost, string& errmsg, const string& fromdb, bool logForRepl, bool slaveOk, bool useReplAuth, bool snapshot) { massert( "useReplAuth is not written to replication log", !useReplAuth || !logForRepl ); string todb = cc().database()->name; stringstream a,b; a << "localhost:" << cmdLine.port; b << "127.0.0.1:" << cmdLine.port; bool masterSameProcess = ( a.str() == masterHost || b.str() == masterHost ); if ( masterSameProcess ) { if ( fromdb == todb && cc().database()->path == dbpath ) { // guard against an "infinite" loop /* if you are replicating, the local.sources config may be wrong if you get this */ errmsg = "can't clone from self (localhost)."; return false; } } /* todo: we can put these releases inside dbclient or a dbclient specialization. or just wait until we get rid of global lock anyway. */ string ns = fromdb + ".system.namespaces"; list<BSONObj> toClone; { dbtemprelease r; auto_ptr<DBClientCursor> c; { if ( !masterSameProcess ) { auto_ptr< DBClientConnection > c( new DBClientConnection() ); if ( !c->connect( masterHost, errmsg ) ) return false; if( !replAuthenticate(c.get()) ) return false; conn = c; } else { conn.reset( new DBDirectClient() ); } c = conn->query( ns.c_str(), BSONObj(), 0, 0, 0, slaveOk ? Option_SlaveOk : 0 ); } if ( c.get() == 0 ) { errmsg = "query failed " + ns; return false; } while ( c->more() ){ BSONObj collection = c->next(); log(2) << "\t cloner got " << collection << endl; BSONElement e = collection.findElement("name"); if ( e.eoo() ) { string s = "bad system.namespaces object " + collection.toString(); massert(s.c_str(), false); } assert( !e.eoo() ); assert( e.type() == String ); const char *from_name = e.valuestr(); if( strstr(from_name, ".system.") ) { /* system.users is cloned -- but nothing else from system. */ if( legalClientSystemNS( from_name , true ) == 0 ){ log(2) << "\t\t not cloning because system collection" << endl; continue; } } else if( strchr(from_name, '$') ) { // don't clone index namespaces -- we take care of those separately below. log(2) << "\t\t not cloning because has $ " << endl; continue; } toClone.push_back( collection.getOwned() ); } } for ( list<BSONObj>::iterator i=toClone.begin(); i != toClone.end(); i++ ){ { dbtemprelease r; } BSONObj collection = *i; log(2) << " really will clone: " << collection << endl; const char * from_name = collection["name"].valuestr(); BSONObj options = collection.getObjectField("options"); /* change name "<fromdb>.collection" -> <todb>.collection */ const char *p = strchr(from_name, '.'); assert(p); string to_name = todb + p; { string err; const char *toname = to_name.c_str(); userCreateNS(toname, options, err, logForRepl); } log(1) << "\t\t cloning " << from_name << " -> " << to_name << endl; Query q; if( snapshot ) q.snapshot(); copy(from_name, to_name.c_str(), false, logForRepl, masterSameProcess, slaveOk, q); } // now build the indexes string system_indexes_from = fromdb + ".system.indexes"; string system_indexes_to = todb + ".system.indexes"; /* [dm]: is the ID index sometimes not called "_id_"? There is other code in the system that looks for a "_id" prefix rather than this exact value. we should standardize. OR, remove names - which is in the bugdb. Anyway, this is dubious here at the moment. */ copy(system_indexes_from.c_str(), system_indexes_to.c_str(), true, logForRepl, masterSameProcess, slaveOk, BSON( "name" << NE << "_id_" ) ); return true; }
/* ns: namespace, e.g. <database>.<collection> pattern: the "where" clause / criteria justOne: stop after 1 match god: allow access to system namespaces, and don't yield */ long long deleteObjects(const char *ns, BSONObj pattern, bool justOneOrig, bool logop, bool god, RemoveSaver * rs ) { if( !god ) { if ( strstr(ns, ".system.") ) { /* note a delete from system.indexes would corrupt the db if done here, as there are pointers into those objects in NamespaceDetails. */ uassert(12050, "cannot delete from system namespace", legalClientSystemNS( ns , true ) ); } if ( strchr( ns , '$' ) ) { log() << "cannot delete from collection with reserved $ in name: " << ns << endl; uassert( 10100 , "cannot delete from collection with reserved $ in name", strchr(ns, '$') == 0 ); } } { NamespaceDetails *d = nsdetails( ns ); if ( ! d ) return 0; uassert( 10101 , "can't remove from a capped collection" , ! d->capped ); } long long nDeleted = 0; shared_ptr< Cursor > creal = NamespaceDetailsTransient::getCursor( ns, pattern, BSONObj(), false, 0 ); if( !creal->ok() ) return nDeleted; shared_ptr< Cursor > cPtr = creal; auto_ptr<ClientCursor> cc( new ClientCursor( QueryOption_NoCursorTimeout, cPtr, ns) ); cc->setDoingDeletes( true ); CursorId id = cc->cursorid(); bool justOne = justOneOrig; bool canYield = !god && !(creal->matcher() && creal->matcher()->docMatcher().atomic()); do { // TODO: we can generalize this I believe // bool willNeedRecord = (creal->matcher() && creal->matcher()->needRecord()) || pattern.isEmpty() || isSimpleIdQuery( pattern ); if ( ! willNeedRecord ) { // TODO: this is a total hack right now // check if the index full encompasses query if ( pattern.nFields() == 1 && str::equals( pattern.firstElement().fieldName() , creal->indexKeyPattern().firstElement().fieldName() ) ) willNeedRecord = true; } if ( canYield && ! cc->yieldSometimes( willNeedRecord ? ClientCursor::WillNeed : ClientCursor::MaybeCovered ) ) { cc.release(); // has already been deleted elsewhere // TODO should we assert or something? break; } if ( !cc->ok() ) { break; // if we yielded, could have hit the end } // this way we can avoid calling updateLocation() every time (expensive) // as well as some other nuances handled cc->setDoingDeletes( true ); DiskLoc rloc = cc->currLoc(); BSONObj key = cc->currKey(); bool match = creal->currentMatches(); bool dup = cc->c()->getsetdup(rloc); if ( ! cc->advance() ) justOne = true; if ( ! match ) continue; assert( !dup ); // can't be a dup, we deleted it! if ( !justOne ) { /* NOTE: this is SLOW. this is not good, noteLocation() was designed to be called across getMore blocks. here we might call millions of times which would be bad. */ cc->c()->prepareToTouchEarlierIterate(); } if ( logop ) { BSONElement e; if( BSONObj( rloc.rec() ).getObjectID( e ) ) { BSONObjBuilder b; b.append( e ); bool replJustOne = true; logOp( "d", ns, b.done(), 0, &replJustOne ); } else { problem() << "deleted object without id, not logging" << endl; } } if ( rs ) rs->goingToDelete( rloc.obj() /*cc->c->current()*/ ); theDataFileMgr.deleteRecord(ns, rloc.rec(), rloc); nDeleted++; if ( justOne ) { break; } cc->c()->recoverFromTouchingEarlierIterate(); if( !god ) getDur().commitIfNeeded(); if( debug && god && nDeleted == 100 ) log() << "warning high number of deletes with god=true which could use significant memory" << endl; } while ( cc->ok() ); if ( cc.get() && ClientCursor::find( id , false ) == 0 ) { // TODO: remove this and the id declaration above if this doesn't trigger // if it does, then i'm very confused (ERH 06/2011) error() << "this should be impossible" << endl; printStackTrace(); cc.release(); } return nDeleted; }
void DatabasesCloner::_onListDatabaseFinish(const CommandCallbackArgs& cbd) { Status respStatus = cbd.response.getStatus(); if (respStatus.isOK()) { respStatus = getStatusFromCommandResult(cbd.response.getValue().data); } UniqueLock lk(_mutex); if (!respStatus.isOK()) { LOG(1) << "listDatabases failed: " << respStatus; _setStatus_inlock(respStatus); _failed_inlock(lk); return; } const auto respBSON = cbd.response.getValue().data; // There should not be any cloners yet invariant(_databaseCloners.size() == 0); const auto dbsElem = respBSON["databases"].Obj(); BSONForEach(arrayElement, dbsElem) { const BSONObj dbBSON = arrayElement.Obj(); // Check to see if we want to exclude this db from the clone. if (!_includeDbFn(dbBSON)) { LOG(1) << "excluding db: " << dbBSON; continue; } const std::string dbName = dbBSON["name"].str(); ++_clonersActive; std::shared_ptr<DatabaseCloner> dbCloner{nullptr}; Status startStatus(ErrorCodes::NotYetInitialized, "The DatabasesCloner could not be started."); // filters for DatabasesCloner. const auto collectionFilterPred = [dbName](const BSONObj& collInfo) { const auto collName = collInfo["name"].str(); const NamespaceString ns(dbName, collName); if (ns.isSystem() && !legalClientSystemNS(ns.ns(), true)) { LOG(1) << "Skipping 'system' collection: " << ns.ns(); return false; } if (!ns.isNormal()) { LOG(1) << "Skipping non-normal collection: " << ns.ns(); return false; } LOG(2) << "Allowing cloning of collectionInfo: " << collInfo; return true; }; const auto onCollectionFinish = [](const Status& status, const NamespaceString& srcNss) { if (status.isOK()) { LOG(1) << "collection clone finished: " << srcNss; } else { warning() << "collection clone for '" << srcNss << "' failed due to " << status.toString(); } }; const auto onDbFinish = [this, dbName](const Status& status) { _onEachDBCloneFinish(status, dbName); }; try { dbCloner.reset(new DatabaseCloner( _exec, _source, dbName, BSONObj(), // do not filter collections out during listCollections call. collectionFilterPred, _storage, // use storage provided. onCollectionFinish, onDbFinish)); // Start database cloner. startStatus = dbCloner->start(); } catch (...) { startStatus = exceptionToStatus(); } if (!startStatus.isOK()) { std::string err = str::stream() << "could not create cloner for database: " << dbName << " due to: " << startStatus.toString(); _setStatus_inlock({ErrorCodes::InitialSyncFailure, err}); error() << err; break; // exit for_each loop } // add cloner to list. _databaseCloners.push_back(dbCloner); } if (_databaseCloners.size() == 0) { if (_status.isOK()) { _active = false; lk.unlock(); _finishFn(_status); } else { _failed_inlock(lk); } } }
long long DeleteExecutor::execute(OperationContext* txn, Database* db) { uassertStatusOK(prepare()); uassert(17417, mongoutils::str::stream() << "DeleteExecutor::prepare() failed to parse query " << _request->getQuery(), _isQueryParsed); const bool logop = _request->shouldCallLogOp(); const NamespaceString& ns(_request->getNamespaceString()); if (!_request->isGod()) { if (ns.isSystem()) { uassert(12050, "cannot delete from system namespace", legalClientSystemNS(ns.ns(), true)); } if (ns.ns().find('$') != string::npos) { log() << "cannot delete from collection with reserved $ in name: " << ns << endl; uasserted( 10100, "cannot delete from collection with reserved $ in name" ); } } Collection* collection = db->getCollection(txn, ns.ns()); if (NULL == collection) { return 0; } uassert(10101, str::stream() << "cannot remove from a capped collection: " << ns.ns(), !collection->isCapped()); uassert(ErrorCodes::NotMaster, str::stream() << "Not primary while removing from " << ns.ns(), !logop || repl::isMasterNs(ns.ns().c_str())); long long nDeleted = 0; Runner* rawRunner; if (_canonicalQuery.get()) { uassertStatusOK(getRunner(collection, _canonicalQuery.release(), &rawRunner)); } else { CanonicalQuery* ignored; uassertStatusOK(getRunner(collection, ns.ns(), _request->getQuery(), &rawRunner, &ignored)); } auto_ptr<Runner> runner(rawRunner); ScopedRunnerRegistration safety(runner.get()); DiskLoc rloc; Runner::RunnerState state; CurOp* curOp = txn->getCurOp(); int oldYieldCount = curOp->numYields(); while (Runner::RUNNER_ADVANCED == (state = runner->getNext(NULL, &rloc))) { if (oldYieldCount != curOp->numYields()) { uassert(ErrorCodes::NotMaster, str::stream() << "No longer primary while removing from " << ns.ns(), !logop || repl::isMasterNs(ns.ns().c_str())); oldYieldCount = curOp->numYields(); } BSONObj toDelete; // TODO: do we want to buffer docs and delete them in a group rather than // saving/restoring state repeatedly? runner->saveState(); collection->deleteDocument(txn, rloc, false, false, logop ? &toDelete : NULL ); runner->restoreState(txn); nDeleted++; if (logop) { if ( toDelete.isEmpty() ) { log() << "Deleted object without id in collection " << collection->ns() << ", not logging."; } else { bool replJustOne = true; repl::logOp(txn, "d", ns.ns().c_str(), toDelete, 0, &replJustOne); } } if (!_request->isMulti()) { break; } if (!_request->isGod()) { txn->recoveryUnit()->commitIfNeeded(); } if (debug && _request->isGod() && nDeleted == 100) { log() << "warning high number of deletes with god=true " << " which could use significant memory b/c we don't commit journal"; } } return nDeleted; }
long long DeleteExecutor::execute(Database* db) { uassertStatusOK(prepare()); uassert(17417, mongoutils::str::stream() << "DeleteExecutor::prepare() failed to parse query " << _request->getQuery(), _isQueryParsed); const NamespaceString& ns(_request->getNamespaceString()); if (!_request->isGod()) { if (ns.isSystem()) { uassert(12050, "cannot delete from system namespace", legalClientSystemNS(ns.ns(), true)); } if (ns.ns().find('$') != string::npos) { log() << "cannot delete from collection with reserved $ in name: " << ns << endl; uasserted(10100, "cannot delete from collection with reserved $ in name"); } } Collection* collection = db->getCollection(_request->getOpCtx(), ns.ns()); if (NULL == collection) { return 0; } uassert(10101, str::stream() << "cannot remove from a capped collection: " << ns.ns(), !collection->isCapped()); uassert(ErrorCodes::NotMaster, str::stream() << "Not primary while removing from " << ns.ns(), !_request->shouldCallLogOp() || repl::getGlobalReplicationCoordinator()->canAcceptWritesForDatabase(ns.db())); PlanExecutor* rawExec; if (_canonicalQuery.get()) { // This is the non-idhack branch. uassertStatusOK(getExecutorDelete(_request->getOpCtx(), collection, _canonicalQuery.release(), _request->isMulti(), _request->shouldCallLogOp(), &rawExec)); } else { // This is the idhack branch. uassertStatusOK(getExecutorDelete(_request->getOpCtx(), collection, ns.ns(), _request->getQuery(), _request->isMulti(), _request->shouldCallLogOp(), &rawExec)); } scoped_ptr<PlanExecutor> exec(rawExec); // Concurrently mutating state (by us) so we need to register 'exec'. const ScopedExecutorRegistration safety(exec.get()); uassertStatusOK(exec->executePlan()); // Extract the number of documents deleted from the DeleteStage stats. invariant(exec->getRootStage()->stageType() == STAGE_DELETE); DeleteStage* deleteStage = static_cast<DeleteStage*>(exec->getRootStage()); const DeleteStats* deleteStats = static_cast<const DeleteStats*>(deleteStage->getSpecificStats()); return deleteStats->docsDeleted; }
void ensureIdIndexForNewNs( Collection* collection ) { if ( collection->ns().isSystem() && !legalClientSystemNS( collection->ns().ns(), false ) ) return; uassertStatusOK( collection->getIndexCatalog()->ensureHaveIdIndex() ); }
long long DeleteExecutor::execute() { uassertStatusOK(prepare()); uassert(17417, mongoutils::str::stream() << "DeleteExecutor::prepare() failed to parse query " << _request->getQuery(), _isQueryParsed); const bool logop = _request->shouldCallLogOp(); const NamespaceString& ns(_request->getNamespaceString()); if (!_request->isGod()) { if (ns.isSystem()) { uassert(12050, "cannot delete from system namespace", legalClientSystemNS(ns.ns(), true)); } if (ns.ns().find('$') != string::npos) { log() << "cannot delete from collection with reserved $ in name: " << ns << endl; uasserted( 10100, "cannot delete from collection with reserved $ in name" ); } } massert(17418, mongoutils::str::stream() << "dbname = " << currentClient.get()->database()->name() << "; ns = " << ns.ns(), currentClient.get()->database()->name() == nsToDatabaseSubstring(ns.ns())); Collection* collection = currentClient.get()->database()->getCollection(ns.ns()); if (NULL == collection) { return 0; } uassert(10101, str::stream() << "cannot remove from a capped collection: " << ns.ns(), !collection->isCapped()); uassert(ErrorCodes::NotMaster, str::stream() << "Not primary while removing from " << ns.ns(), !logop || isMasterNs(ns.ns().c_str())); long long nDeleted = 0; const bool canYield = !_request->isGod() && ( _canonicalQuery.get() ? !QueryPlannerCommon::hasNode(_canonicalQuery->root(), MatchExpression::ATOMIC) : LiteParsedQuery::isQueryIsolated(_request->getQuery())); Runner* rawRunner; if (_canonicalQuery.get()) { uassertStatusOK(getRunner(collection, _canonicalQuery.release(), &rawRunner)); } else { CanonicalQuery* ignored; uassertStatusOK(getRunner(collection, ns.ns(), _request->getQuery(), &rawRunner, &ignored)); } auto_ptr<Runner> runner(rawRunner); auto_ptr<ScopedRunnerRegistration> safety; if (canYield) { safety.reset(new ScopedRunnerRegistration(runner.get())); runner->setYieldPolicy(Runner::YIELD_AUTO); } DiskLoc rloc; Runner::RunnerState state; CurOp* curOp = cc().curop(); int oldYieldCount = curOp->numYields(); while (Runner::RUNNER_ADVANCED == (state = runner->getNext(NULL, &rloc))) { if (oldYieldCount != curOp->numYields()) { uassert(ErrorCodes::NotMaster, str::stream() << "No longer primary while removing from " << ns.ns(), !logop || isMasterNs(ns.ns().c_str())); oldYieldCount = curOp->numYields(); } BSONObj toDelete; // TODO: do we want to buffer docs and delete them in a group rather than // saving/restoring state repeatedly? runner->saveState(); collection->deleteDocument(rloc, false, false, logop ? &toDelete : NULL ); runner->restoreState(); nDeleted++; if (logop) { if ( toDelete.isEmpty() ) { problem() << "deleted object without id, not logging" << endl; } else { bool replJustOne = true; logOp("d", ns.ns().c_str(), toDelete, 0, &replJustOne); } } if (!_request->isMulti()) { break; } if (!_request->isGod()) { getDur().commitIfNeeded(); } if (debug && _request->isGod() && nDeleted == 100) { log() << "warning high number of deletes with god=true " << " which could use significant memory b/c we don't commit journal"; } } return nDeleted; }
/* ns: namespace, e.g. <database>.<collection> pattern: the "where" clause / criteria justOne: stop after 1 match god: allow access to system namespaces, and don't yield */ long long deleteObjects(const StringData& ns, BSONObj pattern, bool justOne, bool logop, bool god) { if (!god) { if (ns.find( ".system.") != string::npos) { // note a delete from system.indexes would corrupt the db if done here, as there are // pointers into those objects in NamespaceDetails. uassert(12050, "cannot delete from system namespace", legalClientSystemNS( ns, true ) ); } if (ns.find('$') != string::npos) { log() << "cannot delete from collection with reserved $ in name: " << ns << endl; uasserted( 10100, "cannot delete from collection with reserved $ in name" ); } } Collection* collection = currentClient.get()->database()->getCollection(ns); if (NULL == collection) { return 0; } uassert(10101, str::stream() << "can't remove from a capped collection: " << ns, !collection->isCapped()); string nsForLogOp = ns.toString(); // XXX-ERH long long nDeleted = 0; CanonicalQuery* cq; if (!CanonicalQuery::canonicalize(ns.toString(), pattern, &cq).isOK()) { uasserted(17218, "Can't canonicalize query " + pattern.toString()); return 0; } bool canYield = !god && !QueryPlannerCommon::hasNode(cq->root(), MatchExpression::ATOMIC); Runner* rawRunner; if (!getRunner(cq, &rawRunner).isOK()) { uasserted(17219, "Can't get runner for query " + pattern.toString()); return 0; } auto_ptr<Runner> runner(rawRunner); auto_ptr<ScopedRunnerRegistration> safety; if (canYield) { safety.reset(new ScopedRunnerRegistration(runner.get())); runner->setYieldPolicy(Runner::YIELD_AUTO); } DiskLoc rloc; Runner::RunnerState state; while (Runner::RUNNER_ADVANCED == (state = runner->getNext(NULL, &rloc))) { BSONObj toDelete; // XXX: do we want to buffer docs and delete them in a group rather than // saving/restoring state repeatedly? runner->saveState(); collection->deleteDocument(rloc, false, false, logop ? &toDelete : NULL ); runner->restoreState(); nDeleted++; if (logop) { if ( toDelete.isEmpty() ) { problem() << "deleted object without id, not logging" << endl; } else { bool replJustOne = true; logOp("d", nsForLogOp.c_str(), toDelete, 0, &replJustOne); } } if (justOne) { break; } if (!god) { getDur().commitIfNeeded(); } if (debug && god && nDeleted == 100) { log() << "warning high number of deletes with god=true " << " which could use significant memory b/c we don't commit journal"; } } return nDeleted; }
Status DeleteExecutor::prepareInLock(Database* db) { // If we have a non-NULL PlanExecutor, then we've already done the in-lock preparation. if (_exec.get()) { return Status::OK(); } uassert(17417, mongoutils::str::stream() << "DeleteExecutor::prepare() failed to parse query " << _request->getQuery(), _isQueryParsed); const NamespaceString& ns(_request->getNamespaceString()); if (!_request->isGod()) { if (ns.isSystem()) { uassert(12050, "cannot delete from system namespace", legalClientSystemNS(ns.ns(), true)); } if (ns.ns().find('$') != string::npos) { log() << "cannot delete from collection with reserved $ in name: " << ns << endl; uasserted(10100, "cannot delete from collection with reserved $ in name"); } } // Note that 'collection' may by NULL in the case that the collection we are trying to // delete from does not exist. NULL 'collection' is handled by getExecutorDelete(); we // expect to get back a plan executor whose plan is a DeleteStage on top of an EOFStage. Collection* collection = db->getCollection(_request->getOpCtx(), ns.ns()); if (collection && collection->isCapped()) { return Status(ErrorCodes::IllegalOperation, str::stream() << "cannot remove from a capped collection: " << ns.ns()); } if (_request->shouldCallLogOp() && !repl::getGlobalReplicationCoordinator()->canAcceptWritesForDatabase(ns.db())) { return Status(ErrorCodes::NotMaster, str::stream() << "Not primary while removing from " << ns.ns()); } // If yielding is allowed for this plan, then set an auto yield policy. Otherwise set // a manual yield policy. const bool canYield = !_request->isGod() && PlanExecutor::YIELD_AUTO == _request->getYieldPolicy() && ( _canonicalQuery.get() ? !QueryPlannerCommon::hasNode(_canonicalQuery->root(), MatchExpression::ATOMIC) : !LiteParsedQuery::isQueryIsolated(_request->getQuery())); PlanExecutor::YieldPolicy policy = canYield ? PlanExecutor::YIELD_AUTO : PlanExecutor::YIELD_MANUAL; PlanExecutor* rawExec; Status getExecStatus = Status::OK(); if (_canonicalQuery.get()) { // This is the non-idhack branch. getExecStatus = getExecutorDelete(_request->getOpCtx(), collection, _canonicalQuery.release(), _request->isMulti(), _request->shouldCallLogOp(), _request->isFromMigrate(), _request->isExplain(), policy, &rawExec); } else { // This is the idhack branch. getExecStatus = getExecutorDelete(_request->getOpCtx(), collection, ns.ns(), _request->getQuery(), _request->isMulti(), _request->shouldCallLogOp(), _request->isFromMigrate(), _request->isExplain(), policy, &rawExec); } if (!getExecStatus.isOK()) { return getExecStatus; } invariant(rawExec); _exec.reset(rawExec); return Status::OK(); }