示例#1
0
文件: insert.cpp 项目: aberg001/mongo
    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);
    }
示例#2
0
 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;
 }
示例#3
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 );
     }
 }
示例#4
0
 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 ) );
     }
 }
示例#5
0
    /* 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);
    }
示例#6
0
 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;
 }
示例#7
0
    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;
    }
示例#8
0
    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;
    }
示例#9
0
    /* 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;
    }
示例#10
0
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);
        }
    }
}
示例#11
0
    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;
    }
示例#13
0
文件: pdfile.cpp 项目: andeshc/mongo
    void ensureIdIndexForNewNs( Collection* collection ) {
        if ( collection->ns().isSystem() && !legalClientSystemNS( collection->ns().ns(), false ) )
            return;

        uassertStatusOK( collection->getIndexCatalog()->ensureHaveIdIndex() );
    }
示例#14
0
    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;
    }
示例#15
0
    /* 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;
    }
示例#16
0
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();
}