/* 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(OperationContext* txn, Database* db, StringData ns, BSONObj pattern, PlanExecutor::YieldPolicy policy, bool justOne, bool god, bool fromMigrate) { NamespaceString nsString(ns); DeleteRequest request(nsString); request.setQuery(pattern); request.setMulti(!justOne); request.setGod(god); request.setFromMigrate(fromMigrate); request.setYieldPolicy(policy); Collection* collection = NULL; if (db) { collection = db->getCollection(nsString.ns()); } ParsedDelete parsedDelete(txn, &request); uassertStatusOK(parsedDelete.parseRequest()); // Replicated writes are disallowed with deleteObjects, as we are not properly setting // lastOp for no-op deletes. fassert(22001, !txn->writesAreReplicated()); std::unique_ptr<PlanExecutor> exec = uassertStatusOK(getExecutorDelete(txn, collection, &parsedDelete)); uassertStatusOK(exec->executePlan()); return DeleteStage::getNumDeleted(*exec); }
Status WriteCmd::explain(OperationContext* txn, const std::string& dbname, const BSONObj& cmdObj, ExplainCommon::Verbosity verbosity, BSONObjBuilder* out) const { // For now we only explain update and delete write commands. if ( BatchedCommandRequest::BatchType_Update != _writeType && BatchedCommandRequest::BatchType_Delete != _writeType ) { return Status( ErrorCodes::IllegalOperation, "Only update and delete write ops can be explained" ); } // Parse the batch request. BatchedCommandRequest request( _writeType ); std::string errMsg; if ( !request.parseBSON( cmdObj, &errMsg ) || !request.isValid( &errMsg ) ) { return Status( ErrorCodes::FailedToParse, errMsg ); } // Note that this is a runCommmand, and therefore, the database and the collection name // are in different parts of the grammar for the command. But it's more convenient to // work with a NamespaceString. We built it here and replace it in the parsed command. // Internally, everything work with the namespace string as opposed to just the // collection name. NamespaceString nsString(dbname, request.getNS()); request.setNSS(nsString); // Do the validation of the batch that is shared with non-explained write batches. Status isValid = WriteBatchExecutor::validateBatch( request ); if (!isValid.isOK()) { return isValid; } // Explain must do one additional piece of validation: For now we only explain // singleton batches. if ( request.sizeWriteOps() != 1u ) { return Status( ErrorCodes::InvalidLength, "explained write batches must be of size 1" ); } // Get a reference to the singleton batch item (it's the 0th item in the batch). BatchItemRef batchItem( &request, 0 ); if ( BatchedCommandRequest::BatchType_Update == _writeType ) { // Create the update request. UpdateRequest updateRequest( nsString ); updateRequest.setQuery( batchItem.getUpdate()->getQuery() ); updateRequest.setUpdates( batchItem.getUpdate()->getUpdateExpr() ); updateRequest.setMulti( batchItem.getUpdate()->getMulti() ); updateRequest.setUpsert( batchItem.getUpdate()->getUpsert() ); updateRequest.setUpdateOpLog( true ); UpdateLifecycleImpl updateLifecycle( true, updateRequest.getNamespaceString() ); updateRequest.setLifecycle( &updateLifecycle ); updateRequest.setExplain(); // Explained updates can yield. updateRequest.setYieldPolicy(PlanExecutor::YIELD_AUTO); OpDebug* debug = &txn->getCurOp()->debug(); ParsedUpdate parsedUpdate( txn, &updateRequest ); Status parseStatus = parsedUpdate.parseRequest(); if ( !parseStatus.isOK() ) { return parseStatus; } // Explains of write commands are read-only, but we take write locks so // that timing info is more accurate. AutoGetDb autoDb( txn, nsString.db(), MODE_IX ); Lock::CollectionLock colLock( txn->lockState(), nsString.ns(), MODE_IX ); // We check the shard version explicitly here rather than using Client::Context, // as Context can do implicit database creation if the db does not exist. We want // explain to be a no-op that reports a trivial EOF plan against non-existent dbs // or collections. ensureShardVersionOKOrThrow( nsString.ns() ); // Get a pointer to the (possibly NULL) collection. Collection* collection = NULL; if ( autoDb.getDb() ) { collection = autoDb.getDb()->getCollection( txn, nsString.ns() ); } PlanExecutor* rawExec; uassertStatusOK(getExecutorUpdate(txn, collection, &parsedUpdate, debug, &rawExec)); boost::scoped_ptr<PlanExecutor> exec(rawExec); // Explain the plan tree. Explain::explainStages( exec.get(), verbosity, out ); return Status::OK(); } else { invariant( BatchedCommandRequest::BatchType_Delete == _writeType ); // Create the delete request. DeleteRequest deleteRequest( nsString ); deleteRequest.setQuery( batchItem.getDelete()->getQuery() ); deleteRequest.setMulti( batchItem.getDelete()->getLimit() != 1 ); deleteRequest.setUpdateOpLog(true); deleteRequest.setGod( false ); deleteRequest.setExplain(); // Explained deletes can yield. deleteRequest.setYieldPolicy(PlanExecutor::YIELD_AUTO); ParsedDelete parsedDelete(txn, &deleteRequest); Status parseStatus = parsedDelete.parseRequest(); if (!parseStatus.isOK()) { return parseStatus; } // Explains of write commands are read-only, but we take write locks so that timing // info is more accurate. AutoGetDb autoDb(txn, nsString.db(), MODE_IX); Lock::CollectionLock colLock(txn->lockState(), nsString.ns(), MODE_IX); // We check the shard version explicitly here rather than using Client::Context, // as Context can do implicit database creation if the db does not exist. We want // explain to be a no-op that reports a trivial EOF plan against non-existent dbs // or collections. ensureShardVersionOKOrThrow( nsString.ns() ); // Get a pointer to the (possibly NULL) collection. Collection* collection = NULL; if (autoDb.getDb()) { collection = autoDb.getDb()->getCollection(txn, nsString.ns()); } PlanExecutor* rawExec; uassertStatusOK(getExecutorDelete(txn, collection, &parsedDelete, &rawExec)); boost::scoped_ptr<PlanExecutor> exec(rawExec); // Explain the plan tree. Explain::explainStages(exec.get(), verbosity, out); return Status::OK(); } }