diff options
Diffstat (limited to 'src/mongo/db/dbcommands.cpp')
-rw-r--r-- | src/mongo/db/dbcommands.cpp | 2023 |
1 files changed, 1009 insertions, 1014 deletions
diff --git a/src/mongo/db/dbcommands.cpp b/src/mongo/db/dbcommands.cpp index 8369de72ec8..935b4ec41f6 100644 --- a/src/mongo/db/dbcommands.cpp +++ b/src/mongo/db/dbcommands.cpp @@ -110,1224 +110,1219 @@ namespace mongo { - using std::endl; - using std::ostringstream; - using std::string; - using std::stringstream; - using std::unique_ptr; - - class CmdShutdownMongoD : public CmdShutdown { - public: - virtual void help(stringstream& help) const { - help << "shutdown the database. must be ran against admin db and " - << "either (1) ran from localhost or (2) authenticated. If " - << "this is a primary in a replica set and there is no member " - << "within 10 seconds of its optime, it will not shutdown " - << "without force : true. You can also specify timeoutSecs : " - << "N to wait N seconds for other members to catch up."; - } +using std::endl; +using std::ostringstream; +using std::string; +using std::stringstream; +using std::unique_ptr; + +class CmdShutdownMongoD : public CmdShutdown { +public: + virtual void help(stringstream& help) const { + help << "shutdown the database. must be ran against admin db and " + << "either (1) ran from localhost or (2) authenticated. If " + << "this is a primary in a replica set and there is no member " + << "within 10 seconds of its optime, it will not shutdown " + << "without force : true. You can also specify timeoutSecs : " + << "N to wait N seconds for other members to catch up."; + } - virtual bool run(OperationContext* txn, - const string& dbname, - BSONObj& cmdObj, - int options, - string& errmsg, - BSONObjBuilder& result) { - bool force = cmdObj.hasField("force") && cmdObj["force"].trueValue(); - - long long timeoutSecs = 0; - if (cmdObj.hasField("timeoutSecs")) { - timeoutSecs = cmdObj["timeoutSecs"].numberLong(); - } + virtual bool run(OperationContext* txn, + const string& dbname, + BSONObj& cmdObj, + int options, + string& errmsg, + BSONObjBuilder& result) { + bool force = cmdObj.hasField("force") && cmdObj["force"].trueValue(); - Status status = repl::getGlobalReplicationCoordinator()->stepDown( - txn, - force, - Seconds(timeoutSecs), - Seconds(120)); - if (!status.isOK() && status.code() != ErrorCodes::NotMaster) { // ignore not master - return appendCommandStatus(result, status); - } + long long timeoutSecs = 0; + if (cmdObj.hasField("timeoutSecs")) { + timeoutSecs = cmdObj["timeoutSecs"].numberLong(); + } - // Never returns - shutdownHelper(); - return true; + Status status = repl::getGlobalReplicationCoordinator()->stepDown( + txn, force, Seconds(timeoutSecs), Seconds(120)); + if (!status.isOK() && status.code() != ErrorCodes::NotMaster) { // ignore not master + return appendCommandStatus(result, status); } - } cmdShutdownMongoD; + // Never returns + shutdownHelper(); + return true; + } - class CmdDropDatabase : public Command { - public: - virtual void help( stringstream& help ) const { - help << "drop (delete) this database"; - } - virtual bool slaveOk() const { - return false; - } +} cmdShutdownMongoD; - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) { - ActionSet actions; - actions.addAction(ActionType::dropDatabase); - out->push_back(Privilege(ResourcePattern::forDatabaseName(dbname), actions)); - } +class CmdDropDatabase : public Command { +public: + virtual void help(stringstream& help) const { + help << "drop (delete) this database"; + } + virtual bool slaveOk() const { + return false; + } - virtual bool isWriteCommandForConfigServer() const { return true; } - - CmdDropDatabase() : Command("dropDatabase") {} - - bool run(OperationContext* txn, - const string& dbname, - BSONObj& cmdObj, - int, - string& errmsg, - BSONObjBuilder& result) { - // disallow dropping the config database - if (serverGlobalParams.configsvr && (dbname == "config")) { - return appendCommandStatus(result, - Status(ErrorCodes::IllegalOperation, - "Cannot drop 'config' database if mongod started " - "with --configsvr")); - } + virtual void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) { + ActionSet actions; + actions.addAction(ActionType::dropDatabase); + out->push_back(Privilege(ResourcePattern::forDatabaseName(dbname), actions)); + } - if ((repl::getGlobalReplicationCoordinator()->getReplicationMode() != - repl::ReplicationCoordinator::modeNone) && - (dbname == "local")) { - return appendCommandStatus(result, - Status(ErrorCodes::IllegalOperation, - "Cannot drop 'local' database while replication " - "is active")); - } - BSONElement e = cmdObj.firstElement(); - int p = (int) e.number(); - if ( p != 1 ) { - return appendCommandStatus(result, - Status(ErrorCodes::IllegalOperation, - "have to pass 1 as db parameter")); - } + virtual bool isWriteCommandForConfigServer() const { + return true; + } - Status status = dropDatabase(txn, dbname); - if (status == ErrorCodes::DatabaseNotFound) { - return appendCommandStatus(result, Status::OK()); - } - if (status.isOK()) { - result.append( "dropped" , dbname ); - } - return appendCommandStatus(result, status); - } + CmdDropDatabase() : Command("dropDatabase") {} - } cmdDropDatabase; + bool run(OperationContext* txn, + const string& dbname, + BSONObj& cmdObj, + int, + string& errmsg, + BSONObjBuilder& result) { + // disallow dropping the config database + if (serverGlobalParams.configsvr && (dbname == "config")) { + return appendCommandStatus(result, + Status(ErrorCodes::IllegalOperation, + "Cannot drop 'config' database if mongod started " + "with --configsvr")); + } - class CmdRepairDatabase : public Command { - public: - virtual bool slaveOk() const { - return true; + if ((repl::getGlobalReplicationCoordinator()->getReplicationMode() != + repl::ReplicationCoordinator::modeNone) && + (dbname == "local")) { + return appendCommandStatus(result, + Status(ErrorCodes::IllegalOperation, + "Cannot drop 'local' database while replication " + "is active")); } - virtual bool maintenanceMode() const { return true; } - virtual void help( stringstream& help ) const { - help << "repair database. also compacts. note: slow."; + BSONElement e = cmdObj.firstElement(); + int p = (int)e.number(); + if (p != 1) { + return appendCommandStatus( + result, Status(ErrorCodes::IllegalOperation, "have to pass 1 as db parameter")); } - virtual bool isWriteCommandForConfigServer() const { return true; } - - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) { - ActionSet actions; - actions.addAction(ActionType::repairDatabase); - out->push_back(Privilege(ResourcePattern::forDatabaseName(dbname), actions)); + Status status = dropDatabase(txn, dbname); + if (status == ErrorCodes::DatabaseNotFound) { + return appendCommandStatus(result, Status::OK()); + } + if (status.isOK()) { + result.append("dropped", dbname); } + return appendCommandStatus(result, status); + } - CmdRepairDatabase() : Command("repairDatabase") { +} cmdDropDatabase; - } +class CmdRepairDatabase : public Command { +public: + virtual bool slaveOk() const { + return true; + } + virtual bool maintenanceMode() const { + return true; + } + virtual void help(stringstream& help) const { + help << "repair database. also compacts. note: slow."; + } - bool run(OperationContext* txn, - const string& dbname, - BSONObj& cmdObj, - int, - string& errmsg, - BSONObjBuilder& result) { - BSONElement e = cmdObj.firstElement(); - if ( e.numberInt() != 1 ) { - errmsg = "bad option"; - return false; - } + virtual bool isWriteCommandForConfigServer() const { + return true; + } - // TODO: SERVER-4328 Don't lock globally - ScopedTransaction transaction(txn, MODE_X); - Lock::GlobalWrite lk(txn->lockState()); - OldClientContext context(txn, dbname); - - log() << "repairDatabase " << dbname; - BackgroundOperation::assertNoBgOpInProgForDb(dbname); - - e = cmdObj.getField( "preserveClonedFilesOnFailure" ); - bool preserveClonedFilesOnFailure = e.isBoolean() && e.boolean(); - e = cmdObj.getField( "backupOriginalFiles" ); - bool backupOriginalFiles = e.isBoolean() && e.boolean(); - - StorageEngine* engine = getGlobalServiceContext()->getGlobalStorageEngine(); - bool shouldReplicateWrites = txn->writesAreReplicated(); - txn->setReplicatedWrites(false); - ON_BLOCK_EXIT(&OperationContext::setReplicatedWrites, txn, shouldReplicateWrites); - Status status = repairDatabase(txn, engine, dbname, preserveClonedFilesOnFailure, - backupOriginalFiles ); - - // Open database before returning - dbHolder().openDb(txn, dbname); - return appendCommandStatus( result, status ); - } - } cmdRepairDatabase; - - /* set db profiling level - todo: how do we handle profiling information put in the db with replication? - sensibly or not? - */ - class CmdProfile : public Command { - public: - virtual bool slaveOk() const { - return true; - } + virtual void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) { + ActionSet actions; + actions.addAction(ActionType::repairDatabase); + out->push_back(Privilege(ResourcePattern::forDatabaseName(dbname), actions)); + } - virtual void help( stringstream& help ) const { - help << "enable or disable performance profiling\n"; - help << "{ profile : <n> }\n"; - help << "0=off 1=log slow ops 2=log all\n"; - help << "-1 to get current values\n"; - help << "http://docs.mongodb.org/manual/reference/command/profile/#dbcmd.profile"; + CmdRepairDatabase() : Command("repairDatabase") {} + + bool run(OperationContext* txn, + const string& dbname, + BSONObj& cmdObj, + int, + string& errmsg, + BSONObjBuilder& result) { + BSONElement e = cmdObj.firstElement(); + if (e.numberInt() != 1) { + errmsg = "bad option"; + return false; } - virtual bool isWriteCommandForConfigServer() const { return true; } - - virtual Status checkAuthForCommand(ClientBasic* client, - const std::string& dbname, - const BSONObj& cmdObj) { - AuthorizationSession* authzSession = AuthorizationSession::get(client); - - if (cmdObj.firstElement().numberInt() == -1 && !cmdObj.hasField("slowms")) { - // If you just want to get the current profiling level you can do so with just - // read access to system.profile, even if you can't change the profiling level. - if (authzSession->isAuthorizedForActionsOnResource( - ResourcePattern::forExactNamespace(NamespaceString(dbname, - "system.profile")), - ActionType::find)) { - return Status::OK(); - } - } + // TODO: SERVER-4328 Don't lock globally + ScopedTransaction transaction(txn, MODE_X); + Lock::GlobalWrite lk(txn->lockState()); + OldClientContext context(txn, dbname); - if (authzSession->isAuthorizedForActionsOnResource( - ResourcePattern::forDatabaseName(dbname), ActionType::enableProfiler)) { - return Status::OK(); - } + log() << "repairDatabase " << dbname; + BackgroundOperation::assertNoBgOpInProgForDb(dbname); - return Status(ErrorCodes::Unauthorized, "unauthorized"); - } + e = cmdObj.getField("preserveClonedFilesOnFailure"); + bool preserveClonedFilesOnFailure = e.isBoolean() && e.boolean(); + e = cmdObj.getField("backupOriginalFiles"); + bool backupOriginalFiles = e.isBoolean() && e.boolean(); - CmdProfile() : Command("profile") { + StorageEngine* engine = getGlobalServiceContext()->getGlobalStorageEngine(); + bool shouldReplicateWrites = txn->writesAreReplicated(); + txn->setReplicatedWrites(false); + ON_BLOCK_EXIT(&OperationContext::setReplicatedWrites, txn, shouldReplicateWrites); + Status status = + repairDatabase(txn, engine, dbname, preserveClonedFilesOnFailure, backupOriginalFiles); - } + // Open database before returning + dbHolder().openDb(txn, dbname); + return appendCommandStatus(result, status); + } +} cmdRepairDatabase; - bool run(OperationContext* txn, - const string& dbname, - BSONObj& cmdObj, - int options, - string& errmsg, - BSONObjBuilder& result) { - // Needs to be locked exclusively, because creates the system.profile collection - // in the local database. - ScopedTransaction transaction(txn, MODE_IX); - Lock::DBLock dbXLock(txn->lockState(), dbname, MODE_X); - OldClientContext ctx(txn, dbname); - - BSONElement e = cmdObj.firstElement(); - result.append("was", ctx.db()->getProfilingLevel()); - result.append("slowms", serverGlobalParams.slowMS); - - int p = (int) e.number(); - Status status = Status::OK(); - - if (p == -1) - status = Status::OK(); - else if ( p >= 0 && p <= 2 ) { - status = ctx.db()->setProfilingLevel(txn, p); - } +/* set db profiling level + todo: how do we handle profiling information put in the db with replication? + sensibly or not? +*/ +class CmdProfile : public Command { +public: + virtual bool slaveOk() const { + return true; + } - const BSONElement slow = cmdObj["slowms"]; - if (slow.isNumber()) { - serverGlobalParams.slowMS = slow.numberInt(); - } + virtual void help(stringstream& help) const { + help << "enable or disable performance profiling\n"; + help << "{ profile : <n> }\n"; + help << "0=off 1=log slow ops 2=log all\n"; + help << "-1 to get current values\n"; + help << "http://docs.mongodb.org/manual/reference/command/profile/#dbcmd.profile"; + } + + virtual bool isWriteCommandForConfigServer() const { + return true; + } - if (!status.isOK()) { - errmsg = status.reason(); + virtual Status checkAuthForCommand(ClientBasic* client, + const std::string& dbname, + const BSONObj& cmdObj) { + AuthorizationSession* authzSession = AuthorizationSession::get(client); + + if (cmdObj.firstElement().numberInt() == -1 && !cmdObj.hasField("slowms")) { + // If you just want to get the current profiling level you can do so with just + // read access to system.profile, even if you can't change the profiling level. + if (authzSession->isAuthorizedForActionsOnResource( + ResourcePattern::forExactNamespace(NamespaceString(dbname, "system.profile")), + ActionType::find)) { + return Status::OK(); } + } - return status.isOK(); + if (authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(dbname), + ActionType::enableProfiler)) { + return Status::OK(); } - } cmdProfile; + return Status(ErrorCodes::Unauthorized, "unauthorized"); + } - class CmdDiagLogging : public Command { - public: - virtual bool slaveOk() const { - return true; - } - CmdDiagLogging() : Command("diagLogging") { } - bool adminOnly() const { - return true; - } + CmdProfile() : Command("profile") {} - void help(stringstream& h) const { h << "http://dochub.mongodb.org/core/monitoring#MonitoringandDiagnostics-DatabaseRecord%2FReplay%28diagLoggingcommand%29"; } + bool run(OperationContext* txn, + const string& dbname, + BSONObj& cmdObj, + int options, + string& errmsg, + BSONObjBuilder& result) { + // Needs to be locked exclusively, because creates the system.profile collection + // in the local database. + ScopedTransaction transaction(txn, MODE_IX); + Lock::DBLock dbXLock(txn->lockState(), dbname, MODE_X); + OldClientContext ctx(txn, dbname); - virtual bool isWriteCommandForConfigServer() const { return true; } + BSONElement e = cmdObj.firstElement(); + result.append("was", ctx.db()->getProfilingLevel()); + result.append("slowms", serverGlobalParams.slowMS); - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) { - ActionSet actions; - actions.addAction(ActionType::diagLogging); - out->push_back(Privilege(ResourcePattern::forClusterResource(), actions)); - } + int p = (int)e.number(); + Status status = Status::OK(); - bool run(OperationContext* txn, - const string& dbname, - BSONObj& cmdObj, - int, - string& errmsg, - BSONObjBuilder& result) { - const char* deprecationWarning = - "CMD diagLogging is deprecated and will be removed in a future release"; - warning() << deprecationWarning << startupWarningsLog; - - // This doesn't look like it requires exclusive DB lock, because it uses its own diag - // locking, but originally the lock was set to be WRITE, so preserving the behaviour. - // - ScopedTransaction transaction(txn, MODE_IX); - Lock::DBLock dbXLock(txn->lockState(), dbname, MODE_X); - OldClientContext ctx(txn, dbname); - - int was = _diaglog.setLevel( cmdObj.firstElement().numberInt() ); - _diaglog.flush(); - if (!serverGlobalParams.quiet) { - LOG(0) << "CMD: diagLogging set to " << _diaglog.getLevel() << " from: " << was << endl; - } - result.append( "was" , was ); - result.append( "note", deprecationWarning ); - return true; + if (p == -1) + status = Status::OK(); + else if (p >= 0 && p <= 2) { + status = ctx.db()->setProfilingLevel(txn, p); } - } cmddiaglogging; - /* drop collection */ - class CmdDrop : public Command { - public: - CmdDrop() : Command("drop") { } - virtual bool slaveOk() const { - return false; + const BSONElement slow = cmdObj["slowms"]; + if (slow.isNumber()) { + serverGlobalParams.slowMS = slow.numberInt(); } - virtual bool adminOnly() const { - return false; - } - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) { - ActionSet actions; - actions.addAction(ActionType::dropCollection); - out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); + + if (!status.isOK()) { + errmsg = status.reason(); } - virtual void help( stringstream& help ) const { help << "drop a collection\n{drop : <collectionName>}"; } - virtual bool isWriteCommandForConfigServer() const { return true; } + return status.isOK(); + } - virtual bool run(OperationContext* txn, - const string& dbname, - BSONObj& cmdObj, - int, - string& errmsg, - BSONObjBuilder& result) { - const std::string nsToDrop = parseNsCollectionRequired(dbname, cmdObj); +} cmdProfile; - if (nsToDrop.find('$') != string::npos) { - errmsg = "can't drop collection with reserved $ character in name"; - return false; - } +class CmdDiagLogging : public Command { +public: + virtual bool slaveOk() const { + return true; + } + CmdDiagLogging() : Command("diagLogging") {} + bool adminOnly() const { + return true; + } - if ((repl::getGlobalReplicationCoordinator()->getReplicationMode() != - repl::ReplicationCoordinator::modeNone) && - NamespaceString(nsToDrop).isOplog()) { - errmsg = "can't drop live oplog while replicating"; - return false; - } + void help(stringstream& h) const { + h << "http://dochub.mongodb.org/core/" + "monitoring#MonitoringandDiagnostics-DatabaseRecord%2FReplay%28diagLoggingcommand%29"; + } - return appendCommandStatus(result, - dropCollection(txn, NamespaceString(nsToDrop), result)); - } + virtual bool isWriteCommandForConfigServer() const { + return true; + } + + virtual void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) { + ActionSet actions; + actions.addAction(ActionType::diagLogging); + out->push_back(Privilege(ResourcePattern::forClusterResource(), actions)); + } + + bool run(OperationContext* txn, + const string& dbname, + BSONObj& cmdObj, + int, + string& errmsg, + BSONObjBuilder& result) { + const char* deprecationWarning = + "CMD diagLogging is deprecated and will be removed in a future release"; + warning() << deprecationWarning << startupWarningsLog; + + // This doesn't look like it requires exclusive DB lock, because it uses its own diag + // locking, but originally the lock was set to be WRITE, so preserving the behaviour. + // + ScopedTransaction transaction(txn, MODE_IX); + Lock::DBLock dbXLock(txn->lockState(), dbname, MODE_X); + OldClientContext ctx(txn, dbname); + + int was = _diaglog.setLevel(cmdObj.firstElement().numberInt()); + _diaglog.flush(); + if (!serverGlobalParams.quiet) { + LOG(0) << "CMD: diagLogging set to " << _diaglog.getLevel() << " from: " << was << endl; + } + result.append("was", was); + result.append("note", deprecationWarning); + return true; + } +} cmddiaglogging; + +/* drop collection */ +class CmdDrop : public Command { +public: + CmdDrop() : Command("drop") {} + virtual bool slaveOk() const { + return false; + } + virtual bool adminOnly() const { + return false; + } + virtual void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) { + ActionSet actions; + actions.addAction(ActionType::dropCollection); + out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); + } + virtual void help(stringstream& help) const { + help << "drop a collection\n{drop : <collectionName>}"; + } + + virtual bool isWriteCommandForConfigServer() const { + return true; + } - } cmdDrop; + virtual bool run(OperationContext* txn, + const string& dbname, + BSONObj& cmdObj, + int, + string& errmsg, + BSONObjBuilder& result) { + const std::string nsToDrop = parseNsCollectionRequired(dbname, cmdObj); - /* create collection */ - class CmdCreate : public Command { - public: - CmdCreate() : Command("create") { } - virtual bool slaveOk() const { + if (nsToDrop.find('$') != string::npos) { + errmsg = "can't drop collection with reserved $ character in name"; return false; } - virtual bool adminOnly() const { + + if ((repl::getGlobalReplicationCoordinator()->getReplicationMode() != + repl::ReplicationCoordinator::modeNone) && + NamespaceString(nsToDrop).isOplog()) { + errmsg = "can't drop live oplog while replicating"; return false; } - virtual bool isWriteCommandForConfigServer() const { return true; } + return appendCommandStatus(result, dropCollection(txn, NamespaceString(nsToDrop), result)); + } - virtual void help( stringstream& help ) const { - help << "create a collection explicitly\n" - "{ create: <ns>[, capped: <bool>, size: <collSizeInBytes>, max: <nDocs>] }"; - } - virtual Status checkAuthForCommand(ClientBasic* client, - const std::string& dbname, - const BSONObj& cmdObj) { - AuthorizationSession* authzSession = AuthorizationSession::get(client); - if (cmdObj["capped"].trueValue()) { - if (!authzSession->isAuthorizedForActionsOnResource( - parseResourcePattern(dbname, cmdObj), ActionType::convertToCapped)) { - return Status(ErrorCodes::Unauthorized, "unauthorized"); - } - } +} cmdDrop; - // ActionType::createCollection or ActionType::insert are both acceptable - if (authzSession->isAuthorizedForActionsOnResource( - parseResourcePattern(dbname, cmdObj), ActionType::createCollection) || - authzSession->isAuthorizedForActionsOnResource( - parseResourcePattern(dbname, cmdObj), ActionType::insert)) { - return Status::OK(); - } +/* create collection */ +class CmdCreate : public Command { +public: + CmdCreate() : Command("create") {} + virtual bool slaveOk() const { + return false; + } + virtual bool adminOnly() const { + return false; + } + + virtual bool isWriteCommandForConfigServer() const { + return true; + } - return Status(ErrorCodes::Unauthorized, "unauthorized"); + virtual void help(stringstream& help) const { + help << "create a collection explicitly\n" + "{ create: <ns>[, capped: <bool>, size: <collSizeInBytes>, max: <nDocs>] }"; + } + virtual Status checkAuthForCommand(ClientBasic* client, + const std::string& dbname, + const BSONObj& cmdObj) { + AuthorizationSession* authzSession = AuthorizationSession::get(client); + if (cmdObj["capped"].trueValue()) { + if (!authzSession->isAuthorizedForActionsOnResource( + parseResourcePattern(dbname, cmdObj), ActionType::convertToCapped)) { + return Status(ErrorCodes::Unauthorized, "unauthorized"); + } } - virtual bool run(OperationContext* txn, - const string& dbname, - BSONObj& cmdObj, - int, - string& errmsg, - BSONObjBuilder& result) { - return appendCommandStatus(result, - createCollection(txn, dbname, cmdObj)); + + // ActionType::createCollection or ActionType::insert are both acceptable + if (authzSession->isAuthorizedForActionsOnResource(parseResourcePattern(dbname, cmdObj), + ActionType::createCollection) || + authzSession->isAuthorizedForActionsOnResource(parseResourcePattern(dbname, cmdObj), + ActionType::insert)) { + return Status::OK(); } - } cmdCreate; + return Status(ErrorCodes::Unauthorized, "unauthorized"); + } + virtual bool run(OperationContext* txn, + const string& dbname, + BSONObj& cmdObj, + int, + string& errmsg, + BSONObjBuilder& result) { + return appendCommandStatus(result, createCollection(txn, dbname, cmdObj)); + } +} cmdCreate; - class CmdFileMD5 : public Command { - public: - CmdFileMD5() : Command( "filemd5" ) { - } +class CmdFileMD5 : public Command { +public: + CmdFileMD5() : Command("filemd5") {} - virtual bool slaveOk() const { - return true; - } + virtual bool slaveOk() const { + return true; + } - virtual void help( stringstream& help ) const { - help << " example: { filemd5 : ObjectId(aaaaaaa) , root : \"fs\" }"; - } + virtual void help(stringstream& help) const { + help << " example: { filemd5 : ObjectId(aaaaaaa) , root : \"fs\" }"; + } - virtual bool isWriteCommandForConfigServer() const { return false; } + virtual bool isWriteCommandForConfigServer() const { + return false; + } - virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const { - std::string collectionName = cmdObj.getStringField("root"); - if (collectionName.empty()) - collectionName = "fs"; - collectionName += ".chunks"; - return NamespaceString(dbname, collectionName).ns(); - } + virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const { + std::string collectionName = cmdObj.getStringField("root"); + if (collectionName.empty()) + collectionName = "fs"; + collectionName += ".chunks"; + return NamespaceString(dbname, collectionName).ns(); + } - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) { - out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), ActionType::find)); - } + virtual void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) { + out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), ActionType::find)); + } - bool run(OperationContext* txn, - const string& dbname, - BSONObj& jsobj, - int, - string& errmsg, - BSONObjBuilder& result) { - const std::string ns = parseNs(dbname, jsobj); - - md5digest d; - md5_state_t st; - md5_init(&st); - - int n = 0; - - bool partialOk = jsobj["partialOk"].trueValue(); - if (partialOk) { - // WARNING: This code depends on the binary layout of md5_state. It will not be - // compatible with different md5 libraries or work correctly in an environment with - // mongod's of different endians. It is ok for mongos to be a different endian since - // it just passes the buffer through to another mongod. - BSONElement stateElem = jsobj["md5state"]; - if (!stateElem.eoo()){ - int len; - const char* data = stateElem.binDataClean(len); - massert(16247, "md5 state not correct size", len == sizeof(st)); - memcpy(&st, data, sizeof(st)); - } - n = jsobj["startAt"].numberInt(); + bool run(OperationContext* txn, + const string& dbname, + BSONObj& jsobj, + int, + string& errmsg, + BSONObjBuilder& result) { + const std::string ns = parseNs(dbname, jsobj); + + md5digest d; + md5_state_t st; + md5_init(&st); + + int n = 0; + + bool partialOk = jsobj["partialOk"].trueValue(); + if (partialOk) { + // WARNING: This code depends on the binary layout of md5_state. It will not be + // compatible with different md5 libraries or work correctly in an environment with + // mongod's of different endians. It is ok for mongos to be a different endian since + // it just passes the buffer through to another mongod. + BSONElement stateElem = jsobj["md5state"]; + if (!stateElem.eoo()) { + int len; + const char* data = stateElem.binDataClean(len); + massert(16247, "md5 state not correct size", len == sizeof(st)); + memcpy(&st, data, sizeof(st)); } + n = jsobj["startAt"].numberInt(); + } - BSONObj query = BSON( "files_id" << jsobj["filemd5"] << "n" << GTE << n ); - BSONObj sort = BSON( "files_id" << 1 << "n" << 1 ); + BSONObj query = BSON("files_id" << jsobj["filemd5"] << "n" << GTE << n); + BSONObj sort = BSON("files_id" << 1 << "n" << 1); - MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN { - CanonicalQuery* cq; - if (!CanonicalQuery::canonicalize(ns, query, sort, BSONObj(), &cq).isOK()) { - uasserted(17240, "Can't canonicalize query " + query.toString()); - return 0; - } + MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN { + CanonicalQuery* cq; + if (!CanonicalQuery::canonicalize(ns, query, sort, BSONObj(), &cq).isOK()) { + uasserted(17240, "Can't canonicalize query " + query.toString()); + return 0; + } - // Check shard version at startup. - // This will throw before we've done any work if shard version is outdated - // We drop and re-acquire these locks every document because md5'ing is expensive - unique_ptr<AutoGetCollectionForRead> ctx(new AutoGetCollectionForRead(txn, ns)); - Collection* coll = ctx->getCollection(); - - PlanExecutor* rawExec; - if (!getExecutor(txn, coll, cq, PlanExecutor::YIELD_MANUAL, &rawExec, - QueryPlannerParams::NO_TABLE_SCAN).isOK()) { - uasserted(17241, "Can't get executor for query " + query.toString()); - return 0; - } + // Check shard version at startup. + // This will throw before we've done any work if shard version is outdated + // We drop and re-acquire these locks every document because md5'ing is expensive + unique_ptr<AutoGetCollectionForRead> ctx(new AutoGetCollectionForRead(txn, ns)); + Collection* coll = ctx->getCollection(); + + PlanExecutor* rawExec; + if (!getExecutor(txn, + coll, + cq, + PlanExecutor::YIELD_MANUAL, + &rawExec, + QueryPlannerParams::NO_TABLE_SCAN).isOK()) { + uasserted(17241, "Can't get executor for query " + query.toString()); + return 0; + } - unique_ptr<PlanExecutor> exec(rawExec); - // Process notifications when the lock is released/reacquired in the loop below - exec->registerExec(); - - BSONObj obj; - PlanExecutor::ExecState state; - while (PlanExecutor::ADVANCED == (state = exec->getNext(&obj, NULL))) { - BSONElement ne = obj["n"]; - verify(ne.isNumber()); - int myn = ne.numberInt(); - if ( n != myn ) { - if (partialOk) { - break; // skipped chunk is probably on another shard - } - log() << "should have chunk: " << n << " have:" << myn << endl; - dumpChunks(txn, ns, query, sort); - uassert( 10040 , "chunks out of order" , n == myn ); - } + unique_ptr<PlanExecutor> exec(rawExec); + // Process notifications when the lock is released/reacquired in the loop below + exec->registerExec(); - // make a copy of obj since we access data in it while yielding locks - BSONObj owned = obj.getOwned(); - exec->saveState(); - // UNLOCKED - ctx.reset(); - - int len; - const char * data = owned["data"].binDataClean( len ); - // This is potentially an expensive operation, so do it out of the lock - md5_append( &st , (const md5_byte_t*)(data) , len ); - n++; - - try { - // RELOCKED - ctx.reset(new AutoGetCollectionForRead(txn, ns)); - } - catch (const SendStaleConfigException& ex) { - LOG(1) << "chunk metadata changed during filemd5, will retarget and continue"; - break; + BSONObj obj; + PlanExecutor::ExecState state; + while (PlanExecutor::ADVANCED == (state = exec->getNext(&obj, NULL))) { + BSONElement ne = obj["n"]; + verify(ne.isNumber()); + int myn = ne.numberInt(); + if (n != myn) { + if (partialOk) { + break; // skipped chunk is probably on another shard } + log() << "should have chunk: " << n << " have:" << myn << endl; + dumpChunks(txn, ns, query, sort); + uassert(10040, "chunks out of order", n == myn); + } + + // make a copy of obj since we access data in it while yielding locks + BSONObj owned = obj.getOwned(); + exec->saveState(); + // UNLOCKED + ctx.reset(); + + int len; + const char* data = owned["data"].binDataClean(len); + // This is potentially an expensive operation, so do it out of the lock + md5_append(&st, (const md5_byte_t*)(data), len); + n++; + + try { + // RELOCKED + ctx.reset(new AutoGetCollectionForRead(txn, ns)); + } catch (const SendStaleConfigException& ex) { + LOG(1) << "chunk metadata changed during filemd5, will retarget and continue"; + break; + } - // Have the lock again. See if we were killed. - if (!exec->restoreState(txn)) { - if (!partialOk) { - uasserted(13281, "File deleted during filemd5 command"); - } + // Have the lock again. See if we were killed. + if (!exec->restoreState(txn)) { + if (!partialOk) { + uasserted(13281, "File deleted during filemd5 command"); } } + } - if (partialOk) - result.appendBinData("md5state", sizeof(st), BinDataGeneral, &st); + if (partialOk) + result.appendBinData("md5state", sizeof(st), BinDataGeneral, &st); - // This must be *after* the capture of md5state since it mutates st - md5_finish(&st, d); + // This must be *after* the capture of md5state since it mutates st + md5_finish(&st, d); - result.append( "numChunks" , n ); - result.append( "md5" , digestToString( d ) ); - } MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "filemd5", dbname); - return true; + result.append("numChunks", n); + result.append("md5", digestToString(d)); } + MONGO_WRITE_CONFLICT_RETRY_LOOP_END(txn, "filemd5", dbname); + return true; + } - void dumpChunks(OperationContext* txn, - const string& ns, - const BSONObj& query, - const BSONObj& sort) { - DBDirectClient client(txn); - Query q(query); - q.sort(sort); - unique_ptr<DBClientCursor> c = client.query(ns, q); - while(c->more()) - PRINT(c->nextSafe()); - } - } cmdFileMD5; + void dumpChunks(OperationContext* txn, + const string& ns, + const BSONObj& query, + const BSONObj& sort) { + DBDirectClient client(txn); + Query q(query); + q.sort(sort); + unique_ptr<DBClientCursor> c = client.query(ns, q); + while (c->more()) + PRINT(c->nextSafe()); + } +} cmdFileMD5; - class CmdDatasize : public Command { - virtual string parseNs(const string& dbname, const BSONObj& cmdObj) const { - return parseNsFullyQualified(dbname, cmdObj); - } - public: - CmdDatasize() : Command( "dataSize", false, "datasize" ) { +class CmdDatasize : public Command { + virtual string parseNs(const string& dbname, const BSONObj& cmdObj) const { + return parseNsFullyQualified(dbname, cmdObj); + } - } +public: + CmdDatasize() : Command("dataSize", false, "datasize") {} - virtual bool slaveOk() const { return true; } - virtual bool isWriteCommandForConfigServer() const { return false; } - virtual void help( stringstream &help ) const { - help << - "determine data size for a set of data in a certain range" - "\nexample: { dataSize:\"blog.posts\", keyPattern:{x:1}, min:{x:10}, max:{x:55} }" - "\nmin and max parameters are optional. They must either both be included or both omitted" - "\nkeyPattern is an optional parameter indicating an index pattern that would be useful" - "for iterating over the min/max bounds. If keyPattern is omitted, it is inferred from " - "the structure of min. " - "\nnote: This command may take a while to run"; - } + virtual bool slaveOk() const { + return true; + } + virtual bool isWriteCommandForConfigServer() const { + return false; + } + virtual void help(stringstream& help) const { + help << "determine data size for a set of data in a certain range" + "\nexample: { dataSize:\"blog.posts\", keyPattern:{x:1}, min:{x:10}, max:{x:55} }" + "\nmin and max parameters are optional. They must either both be included or both " + "omitted" + "\nkeyPattern is an optional parameter indicating an index pattern that would be " + "useful" + "for iterating over the min/max bounds. If keyPattern is omitted, it is inferred " + "from " + "the structure of min. " + "\nnote: This command may take a while to run"; + } - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) { - ActionSet actions; - actions.addAction(ActionType::find); - out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); - } + virtual void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) { + ActionSet actions; + actions.addAction(ActionType::find); + out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); + } - bool run(OperationContext* txn, - const string& dbname, - BSONObj& jsobj, - int, - string& errmsg, - BSONObjBuilder& result) { - Timer timer; - - string ns = jsobj.firstElement().String(); - BSONObj min = jsobj.getObjectField( "min" ); - BSONObj max = jsobj.getObjectField( "max" ); - BSONObj keyPattern = jsobj.getObjectField( "keyPattern" ); - bool estimate = jsobj["estimate"].trueValue(); - - AutoGetCollectionForRead ctx(txn, ns); - - Collection* collection = ctx.getCollection(); - - if ( !collection || collection->numRecords(txn) == 0 ) { - result.appendNumber( "size" , 0 ); - result.appendNumber( "numObjects" , 0 ); - result.append( "millis" , timer.millis() ); - return true; - } + bool run(OperationContext* txn, + const string& dbname, + BSONObj& jsobj, + int, + string& errmsg, + BSONObjBuilder& result) { + Timer timer; - result.appendBool( "estimate" , estimate ); + string ns = jsobj.firstElement().String(); + BSONObj min = jsobj.getObjectField("min"); + BSONObj max = jsobj.getObjectField("max"); + BSONObj keyPattern = jsobj.getObjectField("keyPattern"); + bool estimate = jsobj["estimate"].trueValue(); - unique_ptr<PlanExecutor> exec; - if ( min.isEmpty() && max.isEmpty() ) { - if ( estimate ) { - result.appendNumber( "size" , static_cast<long long>(collection->dataSize(txn)) ); - result.appendNumber( "numObjects", - static_cast<long long>( collection->numRecords(txn) ) ); - result.append( "millis" , timer.millis() ); - return 1; - } - exec.reset(InternalPlanner::collectionScan(txn, ns,collection)); - } - else if ( min.isEmpty() || max.isEmpty() ) { - errmsg = "only one of min or max specified"; - return false; - } - else { + AutoGetCollectionForRead ctx(txn, ns); - if ( keyPattern.isEmpty() ){ - // if keyPattern not provided, try to infer it from the fields in 'min' - keyPattern = Helpers::inferKeyPattern( min ); - } + Collection* collection = ctx.getCollection(); - IndexDescriptor* idx = collection->getIndexCatalog()->findShardKeyPrefixedIndex( - txn, - keyPattern, - true ); // requireSingleKey + if (!collection || collection->numRecords(txn) == 0) { + result.appendNumber("size", 0); + result.appendNumber("numObjects", 0); + result.append("millis", timer.millis()); + return true; + } - if ( idx == NULL ) { - errmsg = "couldn't find valid index containing key pattern"; - return false; - } - // If both min and max non-empty, append MinKey's to make them fit chosen index - KeyPattern kp( idx->keyPattern() ); - min = Helpers::toKeyFormat( kp.extendRangeBound( min, false ) ); - max = Helpers::toKeyFormat( kp.extendRangeBound( max, false ) ); + result.appendBool("estimate", estimate); - exec.reset(InternalPlanner::indexScan(txn, collection, idx, min, max, false)); + unique_ptr<PlanExecutor> exec; + if (min.isEmpty() && max.isEmpty()) { + if (estimate) { + result.appendNumber("size", static_cast<long long>(collection->dataSize(txn))); + result.appendNumber("numObjects", + static_cast<long long>(collection->numRecords(txn))); + result.append("millis", timer.millis()); + return 1; + } + exec.reset(InternalPlanner::collectionScan(txn, ns, collection)); + } else if (min.isEmpty() || max.isEmpty()) { + errmsg = "only one of min or max specified"; + return false; + } else { + if (keyPattern.isEmpty()) { + // if keyPattern not provided, try to infer it from the fields in 'min' + keyPattern = Helpers::inferKeyPattern(min); } - long long avgObjSize = collection->dataSize(txn) / collection->numRecords(txn); + IndexDescriptor* idx = + collection->getIndexCatalog()->findShardKeyPrefixedIndex(txn, + keyPattern, + true); // requireSingleKey - long long maxSize = jsobj["maxSize"].numberLong(); - long long maxObjects = jsobj["maxObjects"].numberLong(); + if (idx == NULL) { + errmsg = "couldn't find valid index containing key pattern"; + return false; + } + // If both min and max non-empty, append MinKey's to make them fit chosen index + KeyPattern kp(idx->keyPattern()); + min = Helpers::toKeyFormat(kp.extendRangeBound(min, false)); + max = Helpers::toKeyFormat(kp.extendRangeBound(max, false)); - long long size = 0; - long long numObjects = 0; + exec.reset(InternalPlanner::indexScan(txn, collection, idx, min, max, false)); + } - RecordId loc; - PlanExecutor::ExecState state; - while (PlanExecutor::ADVANCED == (state = exec->getNext(NULL, &loc))) { - if ( estimate ) - size += avgObjSize; - else - size += collection->getRecordStore()->dataFor(txn, loc).size(); + long long avgObjSize = collection->dataSize(txn) / collection->numRecords(txn); - numObjects++; + long long maxSize = jsobj["maxSize"].numberLong(); + long long maxObjects = jsobj["maxObjects"].numberLong(); - if ( ( maxSize && size > maxSize ) || - ( maxObjects && numObjects > maxObjects ) ) { - result.appendBool( "maxReached" , true ); - break; - } - } + long long size = 0; + long long numObjects = 0; - if (PlanExecutor::IS_EOF != state) { - warning() << "Internal error while reading " << ns << endl; - } + RecordId loc; + PlanExecutor::ExecState state; + while (PlanExecutor::ADVANCED == (state = exec->getNext(NULL, &loc))) { + if (estimate) + size += avgObjSize; + else + size += collection->getRecordStore()->dataFor(txn, loc).size(); - ostringstream os; - os << "Finding size for ns: " << ns; - if ( ! min.isEmpty() ) { - os << " between " << min << " and " << max; - } - logIfSlow( timer , os.str() ); + numObjects++; - result.appendNumber( "size", size ); - result.appendNumber( "numObjects" , numObjects ); - result.append( "millis" , timer.millis() ); - return true; + if ((maxSize && size > maxSize) || (maxObjects && numObjects > maxObjects)) { + result.appendBool("maxReached", true); + break; + } } - } cmdDatasize; - - class CollectionStats : public Command { - public: - CollectionStats() : Command( "collStats", false, "collstats" ) { - + if (PlanExecutor::IS_EOF != state) { + warning() << "Internal error while reading " << ns << endl; } - virtual bool slaveOk() const { return true; } - virtual bool isWriteCommandForConfigServer() const { return false; } - virtual void help( stringstream &help ) const { - help << "{ collStats:\"blog.posts\" , scale : 1 } scale divides sizes e.g. for KB use 1024\n" - " avgObjSize - in bytes"; + ostringstream os; + os << "Finding size for ns: " << ns; + if (!min.isEmpty()) { + os << " between " << min << " and " << max; } + logIfSlow(timer, os.str()); - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) { - ActionSet actions; - actions.addAction(ActionType::collStats); - out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); - } + result.appendNumber("size", size); + result.appendNumber("numObjects", numObjects); + result.append("millis", timer.millis()); + return true; + } - bool run(OperationContext* txn, - const string& dbname, - BSONObj& jsobj, - int, - string& errmsg, - BSONObjBuilder& result) { - int scale = 1; - if ( jsobj["scale"].isNumber() ) { - scale = jsobj["scale"].numberInt(); - if ( scale <= 0 ) { - errmsg = "scale has to be >= 1"; - return false; - } - } - else if ( jsobj["scale"].trueValue() ) { - errmsg = "scale has to be a number >= 1"; - return false; - } +} cmdDatasize; - bool verbose = jsobj["verbose"].trueValue(); +class CollectionStats : public Command { +public: + CollectionStats() : Command("collStats", false, "collstats") {} - const NamespaceString nss(parseNs(dbname, jsobj)); + virtual bool slaveOk() const { + return true; + } + virtual bool isWriteCommandForConfigServer() const { + return false; + } + virtual void help(stringstream& help) const { + help + << "{ collStats:\"blog.posts\" , scale : 1 } scale divides sizes e.g. for KB use 1024\n" + " avgObjSize - in bytes"; + } - if (nss.coll().empty()) { - errmsg = "No collection name specified"; - return false; - } + virtual void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) { + ActionSet actions; + actions.addAction(ActionType::collStats); + out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); + } - AutoGetCollectionForRead ctx(txn, nss); - if (!ctx.getDb()) { - errmsg = "Database [" + nss.db().toString() + "] not found."; + bool run(OperationContext* txn, + const string& dbname, + BSONObj& jsobj, + int, + string& errmsg, + BSONObjBuilder& result) { + int scale = 1; + if (jsobj["scale"].isNumber()) { + scale = jsobj["scale"].numberInt(); + if (scale <= 0) { + errmsg = "scale has to be >= 1"; return false; } + } else if (jsobj["scale"].trueValue()) { + errmsg = "scale has to be a number >= 1"; + return false; + } - Collection* collection = ctx.getCollection(); - if (!collection) { - errmsg = "Collection [" + nss.toString() + "] not found."; - return false; - } + bool verbose = jsobj["verbose"].trueValue(); - result.append( "ns" , nss ); + const NamespaceString nss(parseNs(dbname, jsobj)); - long long size = collection->dataSize(txn) / scale; - long long numRecords = collection->numRecords(txn); - result.appendNumber( "count" , numRecords ); - result.appendNumber( "size" , size ); - if( numRecords ) - result.append( "avgObjSize" , collection->averageObjectSize(txn) ); + if (nss.coll().empty()) { + errmsg = "No collection name specified"; + return false; + } - result.appendNumber("storageSize", - static_cast<long long>(collection->getRecordStore() - ->storageSize(txn, - &result, - verbose ? 1 : 0)) / scale); + AutoGetCollectionForRead ctx(txn, nss); + if (!ctx.getDb()) { + errmsg = "Database [" + nss.db().toString() + "] not found."; + return false; + } - collection->getRecordStore()->appendCustomStats( txn, &result, scale ); + Collection* collection = ctx.getCollection(); + if (!collection) { + errmsg = "Collection [" + nss.toString() + "] not found."; + return false; + } - IndexCatalog* indexCatalog = collection->getIndexCatalog(); - result.append( "nindexes" , indexCatalog->numIndexesReady( txn ) ); + result.append("ns", nss); - // indexes - BSONObjBuilder indexDetails; + long long size = collection->dataSize(txn) / scale; + long long numRecords = collection->numRecords(txn); + result.appendNumber("count", numRecords); + result.appendNumber("size", size); + if (numRecords) + result.append("avgObjSize", collection->averageObjectSize(txn)); - IndexCatalog::IndexIterator i = indexCatalog->getIndexIterator(txn, false); - while (i.more()) { - const IndexDescriptor* descriptor = i.next(); - IndexAccessMethod* iam = indexCatalog->getIndex(descriptor); - invariant(iam); + result.appendNumber("storageSize", + static_cast<long long>(collection->getRecordStore()->storageSize( + txn, &result, verbose ? 1 : 0)) / + scale); - BSONObjBuilder bob; - if (iam->appendCustomStats(txn, &bob, scale)) { - indexDetails.append(descriptor->indexName(), bob.obj()); - } - } + collection->getRecordStore()->appendCustomStats(txn, &result, scale); - result.append("indexDetails", indexDetails.done()); + IndexCatalog* indexCatalog = collection->getIndexCatalog(); + result.append("nindexes", indexCatalog->numIndexesReady(txn)); - BSONObjBuilder indexSizes; - long long indexSize = collection->getIndexSize(txn, &indexSizes, scale); + // indexes + BSONObjBuilder indexDetails; - result.appendNumber("totalIndexSize", indexSize / scale); - result.append("indexSizes", indexSizes.obj()); + IndexCatalog::IndexIterator i = indexCatalog->getIndexIterator(txn, false); + while (i.more()) { + const IndexDescriptor* descriptor = i.next(); + IndexAccessMethod* iam = indexCatalog->getIndex(descriptor); + invariant(iam); - return true; + BSONObjBuilder bob; + if (iam->appendCustomStats(txn, &bob, scale)) { + indexDetails.append(descriptor->indexName(), bob.obj()); + } } - } cmdCollectionStats; + result.append("indexDetails", indexDetails.done()); - class CollectionModCommand : public Command { - public: - CollectionModCommand() : Command( "collMod" ) { + BSONObjBuilder indexSizes; + long long indexSize = collection->getIndexSize(txn, &indexSizes, scale); - } + result.appendNumber("totalIndexSize", indexSize / scale); + result.append("indexSizes", indexSizes.obj()); + + return true; + } + +} cmdCollectionStats; + +class CollectionModCommand : public Command { +public: + CollectionModCommand() : Command("collMod") {} - virtual bool slaveOk() const { return false; } - virtual bool isWriteCommandForConfigServer() const { return true; } - virtual void help( stringstream &help ) const { - help << - "Sets collection options.\n" + virtual bool slaveOk() const { + return false; + } + virtual bool isWriteCommandForConfigServer() const { + return true; + } + virtual void help(stringstream& help) const { + help << "Sets collection options.\n" "Example: { collMod: 'foo', usePowerOf2Sizes:true }\n" "Example: { collMod: 'foo', index: {keyPattern: {a: 1}, expireAfterSeconds: 600} }"; - } + } - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) { - ActionSet actions; - actions.addAction(ActionType::collMod); - out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); - } + virtual void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) { + ActionSet actions; + actions.addAction(ActionType::collMod); + out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); + } - bool run(OperationContext* txn, - const string& dbname, - BSONObj& jsobj, - int, - string& errmsg, - BSONObjBuilder& result) { - const std::string ns = parseNsCollectionRequired(dbname, jsobj); - return appendCommandStatus(result, - collMod(txn, NamespaceString(ns), jsobj, &result)); - } + bool run(OperationContext* txn, + const string& dbname, + BSONObj& jsobj, + int, + string& errmsg, + BSONObjBuilder& result) { + const std::string ns = parseNsCollectionRequired(dbname, jsobj); + return appendCommandStatus(result, collMod(txn, NamespaceString(ns), jsobj, &result)); + } - } collectionModCommand; +} collectionModCommand; - class DBStats : public Command { - public: - DBStats() : Command( "dbStats", false, "dbstats" ) { +class DBStats : public Command { +public: + DBStats() : Command("dbStats", false, "dbstats") {} - } - - virtual bool slaveOk() const { return true; } - virtual bool isWriteCommandForConfigServer() const { return false; } - virtual void help( stringstream &help ) const { - help << - "Get stats on a database. Not instantaneous. Slower for databases with large " + virtual bool slaveOk() const { + return true; + } + virtual bool isWriteCommandForConfigServer() const { + return false; + } + virtual void help(stringstream& help) const { + help << "Get stats on a database. Not instantaneous. Slower for databases with large " ".ns files.\n" "Example: { dbStats:1, scale:1 }"; - } + } - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) { - ActionSet actions; - actions.addAction(ActionType::dbStats); - out->push_back(Privilege(ResourcePattern::forDatabaseName(dbname), actions)); - } + virtual void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) { + ActionSet actions; + actions.addAction(ActionType::dbStats); + out->push_back(Privilege(ResourcePattern::forDatabaseName(dbname), actions)); + } - bool run(OperationContext* txn, - const string& dbname, - BSONObj& jsobj, - int, - string& errmsg, - BSONObjBuilder& result) { - int scale = 1; - if ( jsobj["scale"].isNumber() ) { - scale = jsobj["scale"].numberInt(); - if ( scale <= 0 ) { - errmsg = "scale has to be > 0"; - return false; - } - } - else if ( jsobj["scale"].trueValue() ) { - errmsg = "scale has to be a number > 0"; + bool run(OperationContext* txn, + const string& dbname, + BSONObj& jsobj, + int, + string& errmsg, + BSONObjBuilder& result) { + int scale = 1; + if (jsobj["scale"].isNumber()) { + scale = jsobj["scale"].numberInt(); + if (scale <= 0) { + errmsg = "scale has to be > 0"; return false; } + } else if (jsobj["scale"].trueValue()) { + errmsg = "scale has to be a number > 0"; + return false; + } - const string ns = parseNs(dbname, jsobj); + const string ns = parseNs(dbname, jsobj); - // TODO: OldClientContext legacy, needs to be removed - CurOp::get(txn)->ensureStarted(); + // TODO: OldClientContext legacy, needs to be removed + CurOp::get(txn)->ensureStarted(); + { + stdx::lock_guard<Client> lk(*txn->getClient()); + CurOp::get(txn)->setNS_inlock(dbname); + } + + // We lock the entire database in S-mode in order to ensure that the contents will not + // change for the stats snapshot. This might be unnecessary and if it becomes a + // performance issue, we can take IS lock and then lock collection-by-collection. + ScopedTransaction scopedXact(txn, MODE_IS); + AutoGetDb autoDb(txn, ns, MODE_S); + + result.append("db", ns); + + Database* db = autoDb.getDb(); + if (!db) { + // TODO: This preserves old behaviour where we used to create an empty database + // metadata even when the database is accessed for read. Without this several + // unit-tests will fail, which are fairly easy to fix. If backwards compatibility + // is not needed for the missing DB case, we can just do the same that's done in + // CollectionStats. + result.appendNumber("collections", 0); + result.appendNumber("objects", 0); + result.append("avgObjSize", 0); + result.appendNumber("dataSize", 0); + result.appendNumber("storageSize", 0); + result.appendNumber("numExtents", 0); + result.appendNumber("indexes", 0); + result.appendNumber("indexSize", 0); + result.appendNumber("fileSize", 0); + } else { { stdx::lock_guard<Client> lk(*txn->getClient()); - CurOp::get(txn)->setNS_inlock(dbname); - } - - // We lock the entire database in S-mode in order to ensure that the contents will not - // change for the stats snapshot. This might be unnecessary and if it becomes a - // performance issue, we can take IS lock and then lock collection-by-collection. - ScopedTransaction scopedXact(txn, MODE_IS); - AutoGetDb autoDb(txn, ns, MODE_S); - - result.append("db", ns); - - Database* db = autoDb.getDb(); - if (!db) { - // TODO: This preserves old behaviour where we used to create an empty database - // metadata even when the database is accessed for read. Without this several - // unit-tests will fail, which are fairly easy to fix. If backwards compatibility - // is not needed for the missing DB case, we can just do the same that's done in - // CollectionStats. - result.appendNumber("collections", 0); - result.appendNumber("objects", 0); - result.append("avgObjSize", 0); - result.appendNumber("dataSize", 0); - result.appendNumber("storageSize", 0); - result.appendNumber("numExtents", 0); - result.appendNumber("indexes", 0); - result.appendNumber("indexSize", 0); - result.appendNumber("fileSize", 0); + // TODO: OldClientContext legacy, needs to be removed + CurOp::get(txn)->enter_inlock(dbname.c_str(), db->getProfilingLevel()); } - else { - { - stdx::lock_guard<Client> lk(*txn->getClient()); - // TODO: OldClientContext legacy, needs to be removed - CurOp::get(txn)->enter_inlock(dbname.c_str(), db->getProfilingLevel()); - } - db->getStats(txn, &result, scale); - } - - return true; + db->getStats(txn, &result, scale); } - } cmdDBStats; - - /* Returns client's uri */ - class CmdWhatsMyUri : public Command { - public: - CmdWhatsMyUri() : Command("whatsmyuri") { } - virtual bool slaveOk() const { - return true; - } - virtual bool isWriteCommandForConfigServer() const { return false; } - virtual void help( stringstream &help ) const { - help << "{whatsmyuri:1}"; - } - virtual void addRequiredPrivileges(const std::string& dbname, - const BSONObj& cmdObj, - std::vector<Privilege>* out) {} // No auth required - virtual bool run(OperationContext* txn, - const string& dbname, - BSONObj& cmdObj, - int, - string& errmsg, - BSONObjBuilder& result) { - result << "you" << txn->getClient()->clientAddress(true /*includePort*/); - return true; - } - } cmdWhatsMyUri; + return true; + } - class AvailableQueryOptions: public Command { - public: - AvailableQueryOptions(): Command("availableQueryOptions", - false, - "availablequeryoptions") { - } +} cmdDBStats; - virtual bool slaveOk() const { return true; } - virtual bool isWriteCommandForConfigServer() const { return false; } - virtual Status checkAuthForCommand(ClientBasic* client, - const std::string& dbname, - const BSONObj& cmdObj) { - return Status::OK(); - } +/* Returns client's uri */ +class CmdWhatsMyUri : public Command { +public: + CmdWhatsMyUri() : Command("whatsmyuri") {} + virtual bool slaveOk() const { + return true; + } + virtual bool isWriteCommandForConfigServer() const { + return false; + } + virtual void help(stringstream& help) const { + help << "{whatsmyuri:1}"; + } + virtual void addRequiredPrivileges(const std::string& dbname, + const BSONObj& cmdObj, + std::vector<Privilege>* out) {} // No auth required + virtual bool run(OperationContext* txn, + const string& dbname, + BSONObj& cmdObj, + int, + string& errmsg, + BSONObjBuilder& result) { + result << "you" << txn->getClient()->clientAddress(true /*includePort*/); + return true; + } +} cmdWhatsMyUri; - virtual bool run(OperationContext* txn, - const string& dbname, - BSONObj& cmdObj, - int, - string& errmsg, - BSONObjBuilder& result) { - result << "options" << QueryOption_AllSupported; - return true; - } - } availableQueryOptionsCmd; - - /** - * Guard object for making a good-faith effort to enter maintenance mode and leave it when it - * goes out of scope. - * - * Sometimes we cannot set maintenance mode, in which case the call to setMaintenanceMode will - * return a non-OK status. This class does not treat that case as an error which means that - * anybody using it is assuming it is ok to continue execution without maintenance mode. - * - * TODO: This assumption needs to be audited and documented, or this behavior should be moved - * elsewhere. - */ - class MaintenanceModeSetter { - public: - MaintenanceModeSetter() : - maintenanceModeSet( - repl::getGlobalReplicationCoordinator()->setMaintenanceMode(true).isOK()) - {} - ~MaintenanceModeSetter() { - if (maintenanceModeSet) - repl::getGlobalReplicationCoordinator()->setMaintenanceMode(false); - } - private: - bool maintenanceModeSet; - }; - - /** - * this handles - - auth - - maintenance mode - - opcounters - - locking - - context - then calls run() - */ - void Command::execCommand(OperationContext* txn, - Command* command, - const rpc::RequestInterface& request, - rpc::ReplyBuilderInterface* replyBuilder) { - - try { +class AvailableQueryOptions : public Command { +public: + AvailableQueryOptions() : Command("availableQueryOptions", false, "availablequeryoptions") {} - { - stdx::lock_guard<Client> lk(*txn->getClient()); - CurOp::get(txn)->setCommand_inlock(command); - } - // TODO: move this back to runCommands when mongos supports OperationContext - // see SERVER-18515 for details. - uassertStatusOK(rpc::readRequestMetadata(txn, request.getMetadata())); + virtual bool slaveOk() const { + return true; + } + virtual bool isWriteCommandForConfigServer() const { + return false; + } + virtual Status checkAuthForCommand(ClientBasic* client, + const std::string& dbname, + const BSONObj& cmdObj) { + return Status::OK(); + } - dassert(replyBuilder->getState() == rpc::ReplyBuilderInterface::State::kMetadata); + virtual bool run(OperationContext* txn, + const string& dbname, + BSONObj& cmdObj, + int, + string& errmsg, + BSONObjBuilder& result) { + result << "options" << QueryOption_AllSupported; + return true; + } +} availableQueryOptionsCmd; - std::string dbname = request.getDatabase().toString(); - unique_ptr<MaintenanceModeSetter> mmSetter; +/** + * Guard object for making a good-faith effort to enter maintenance mode and leave it when it + * goes out of scope. + * + * Sometimes we cannot set maintenance mode, in which case the call to setMaintenanceMode will + * return a non-OK status. This class does not treat that case as an error which means that + * anybody using it is assuming it is ok to continue execution without maintenance mode. + * + * TODO: This assumption needs to be audited and documented, or this behavior should be moved + * elsewhere. + */ +class MaintenanceModeSetter { +public: + MaintenanceModeSetter() + : maintenanceModeSet( + repl::getGlobalReplicationCoordinator()->setMaintenanceMode(true).isOK()) {} + ~MaintenanceModeSetter() { + if (maintenanceModeSet) + repl::getGlobalReplicationCoordinator()->setMaintenanceMode(false); + } - if (isHelpRequest(request)) { - CurOp::get(txn)->ensureStarted(); - generateHelpResponse(txn, request, replyBuilder, *command); - return; - } +private: + bool maintenanceModeSet; +}; - ImpersonationSessionGuard guard(txn); +/** + * this handles + - auth + - maintenance mode + - opcounters + - locking + - context + then calls run() +*/ +void Command::execCommand(OperationContext* txn, + Command* command, + const rpc::RequestInterface& request, + rpc::ReplyBuilderInterface* replyBuilder) { + try { + { + stdx::lock_guard<Client> lk(*txn->getClient()); + CurOp::get(txn)->setCommand_inlock(command); + } + // TODO: move this back to runCommands when mongos supports OperationContext + // see SERVER-18515 for details. + uassertStatusOK(rpc::readRequestMetadata(txn, request.getMetadata())); - uassertStatusOK( - _checkAuthorization(command, - txn->getClient(), - dbname, - request.getCommandArgs()) - ); + dassert(replyBuilder->getState() == rpc::ReplyBuilderInterface::State::kMetadata); - { - repl::ReplicationCoordinator* replCoord = repl::getGlobalReplicationCoordinator(); + std::string dbname = request.getDatabase().toString(); + unique_ptr<MaintenanceModeSetter> mmSetter; - bool iAmPrimary = replCoord->canAcceptWritesForDatabase(dbname); - bool commandCanRunOnSecondary = command->slaveOk(); + if (isHelpRequest(request)) { + CurOp::get(txn)->ensureStarted(); + generateHelpResponse(txn, request, replyBuilder, *command); + return; + } - bool commandIsOverriddenToRunOnSecondary = command->slaveOverrideOk() && + ImpersonationSessionGuard guard(txn); - // The $secondaryOk option is set. - (rpc::ServerSelectionMetadata::get(txn).isSecondaryOk() || + uassertStatusOK( + _checkAuthorization(command, txn->getClient(), dbname, request.getCommandArgs())); - // Or the command has a read preference (may be incorrect, see SERVER-18194). - (rpc::ServerSelectionMetadata::get(txn).getReadPreference() != boost::none)); + { + repl::ReplicationCoordinator* replCoord = repl::getGlobalReplicationCoordinator(); - bool iAmStandalone = !txn->writesAreReplicated(); - bool canRunHere = iAmPrimary || - commandCanRunOnSecondary || - commandIsOverriddenToRunOnSecondary || - iAmStandalone; + bool iAmPrimary = replCoord->canAcceptWritesForDatabase(dbname); + bool commandCanRunOnSecondary = command->slaveOk(); - // This logic is clearer if we don't have to invert it. - if (!canRunHere && command->slaveOverrideOk()) { - uasserted(ErrorCodes::NotMasterNoSlaveOkCode, - "not master and slaveOk=false"); - } + bool commandIsOverriddenToRunOnSecondary = command->slaveOverrideOk() && - uassert(ErrorCodes::NotMaster, - "not master", - canRunHere); + // The $secondaryOk option is set. + (rpc::ServerSelectionMetadata::get(txn).isSecondaryOk() || - if (!command->maintenanceOk() - && replCoord->getReplicationMode() == repl::ReplicationCoordinator::modeReplSet - && !replCoord->canAcceptWritesForDatabase(dbname) - && !replCoord->getMemberState().secondary()) { + // Or the command has a read preference (may be incorrect, see SERVER-18194). + (rpc::ServerSelectionMetadata::get(txn).getReadPreference() != boost::none)); - uasserted(ErrorCodes::NotMasterOrSecondaryCode, - "node is recovering"); - } - } + bool iAmStandalone = !txn->writesAreReplicated(); + bool canRunHere = iAmPrimary || commandCanRunOnSecondary || + commandIsOverriddenToRunOnSecondary || iAmStandalone; - if (command->adminOnly()) { - LOG(2) << "command: " << request.getCommandName(); + // This logic is clearer if we don't have to invert it. + if (!canRunHere && command->slaveOverrideOk()) { + uasserted(ErrorCodes::NotMasterNoSlaveOkCode, "not master and slaveOk=false"); } - if (command->maintenanceMode()) { - mmSetter.reset(new MaintenanceModeSetter); - } + uassert(ErrorCodes::NotMaster, "not master", canRunHere); - if (command->shouldAffectCommandCounter()) { - OpCounters* opCounters = &globalOpCounters; - opCounters->gotCommand(); + if (!command->maintenanceOk() && + replCoord->getReplicationMode() == repl::ReplicationCoordinator::modeReplSet && + !replCoord->canAcceptWritesForDatabase(dbname) && + !replCoord->getMemberState().secondary()) { + uasserted(ErrorCodes::NotMasterOrSecondaryCode, "node is recovering"); } + } - // Handle command option maxTimeMS. - int maxTimeMS = uassertStatusOK( - LiteParsedQuery::parseMaxTimeMSCommand(request.getCommandArgs()) - ); - - uassert(ErrorCodes::InvalidOptions, - "no such command option $maxTimeMs; use maxTimeMS instead", - !request.getCommandArgs().hasField("$maxTimeMS")); + if (command->adminOnly()) { + LOG(2) << "command: " << request.getCommandName(); + } - CurOp::get(txn)->setMaxTimeMicros(static_cast<unsigned long long>(maxTimeMS) - * 1000); + if (command->maintenanceMode()) { + mmSetter.reset(new MaintenanceModeSetter); + } - // Can throw - txn->checkForInterrupt(); // May trigger maxTimeAlwaysTimeOut fail point. + if (command->shouldAffectCommandCounter()) { + OpCounters* opCounters = &globalOpCounters; + opCounters->gotCommand(); + } - bool retval = false; + // Handle command option maxTimeMS. + int maxTimeMS = + uassertStatusOK(LiteParsedQuery::parseMaxTimeMSCommand(request.getCommandArgs())); - CurOp::get(txn)->ensureStarted(); + uassert(ErrorCodes::InvalidOptions, + "no such command option $maxTimeMs; use maxTimeMS instead", + !request.getCommandArgs().hasField("$maxTimeMS")); - command->_commandsExecuted.increment(); + CurOp::get(txn)->setMaxTimeMicros(static_cast<unsigned long long>(maxTimeMS) * 1000); - retval = command->run(txn, request, replyBuilder); + // Can throw + txn->checkForInterrupt(); // May trigger maxTimeAlwaysTimeOut fail point. - dassert(replyBuilder->getState() == rpc::ReplyBuilderInterface::State::kOutputDocs); + bool retval = false; - if (!retval) { - command->_commandsFailed.increment(); - } - } - catch (const DBException& exception) { - Command::generateErrorResponse(txn, replyBuilder, exception, request, command); - } - } + CurOp::get(txn)->ensureStarted(); - // This really belongs in commands.cpp, but we need to move it here so we can - // use shardingState and the repl coordinator without changing our entire library - // structure. - // It will be moved back as part of SERVER-18236. - bool Command::run(OperationContext* txn, - const rpc::RequestInterface& request, - rpc::ReplyBuilderInterface* replyBuilder) { + command->_commandsExecuted.increment(); - BSONObjBuilder replyBuilderBob; + retval = command->run(txn, request, replyBuilder); - repl::ReplicationCoordinator* replCoord = repl::getGlobalReplicationCoordinator(); - { - // Handle read after opTime. - repl::ReadAfterOpTimeArgs readAfterOptimeSettings; - auto readAfterParseStatus = readAfterOptimeSettings.initialize(request.getCommandArgs()); - if (!readAfterParseStatus.isOK()) { - replyBuilder - ->setMetadata(rpc::makeEmptyMetadata()) - .setCommandReply(readAfterParseStatus); - return false; - } + dassert(replyBuilder->getState() == rpc::ReplyBuilderInterface::State::kOutputDocs); - auto readAfterResult = replCoord->waitUntilOpTime(txn, readAfterOptimeSettings); - readAfterResult.appendInfo(&replyBuilderBob); - if (!readAfterResult.getStatus().isOK()) { - replyBuilder - ->setMetadata(rpc::makeEmptyMetadata()) - .setCommandReply(readAfterResult.getStatus(), replyBuilderBob.done()); - return false; - } + if (!retval) { + command->_commandsFailed.increment(); + } + } catch (const DBException& exception) { + Command::generateErrorResponse(txn, replyBuilder, exception, request, command); + } +} + +// This really belongs in commands.cpp, but we need to move it here so we can +// use shardingState and the repl coordinator without changing our entire library +// structure. +// It will be moved back as part of SERVER-18236. +bool Command::run(OperationContext* txn, + const rpc::RequestInterface& request, + rpc::ReplyBuilderInterface* replyBuilder) { + BSONObjBuilder replyBuilderBob; + + repl::ReplicationCoordinator* replCoord = repl::getGlobalReplicationCoordinator(); + { + // Handle read after opTime. + repl::ReadAfterOpTimeArgs readAfterOptimeSettings; + auto readAfterParseStatus = readAfterOptimeSettings.initialize(request.getCommandArgs()); + if (!readAfterParseStatus.isOK()) { + replyBuilder->setMetadata(rpc::makeEmptyMetadata()) + .setCommandReply(readAfterParseStatus); + return false; } - // run expects non-const bsonobj - BSONObj cmd = request.getCommandArgs(); - // Implementation just forwards to the old method signature for now. - std::string errmsg; + auto readAfterResult = replCoord->waitUntilOpTime(txn, readAfterOptimeSettings); + readAfterResult.appendInfo(&replyBuilderBob); + if (!readAfterResult.getStatus().isOK()) { + replyBuilder->setMetadata(rpc::makeEmptyMetadata()) + .setCommandReply(readAfterResult.getStatus(), replyBuilderBob.done()); + return false; + } + } - // run expects const db std::string (can't bind to temporary) - const std::string db = request.getDatabase().toString(); + // run expects non-const bsonobj + BSONObj cmd = request.getCommandArgs(); + // Implementation just forwards to the old method signature for now. + std::string errmsg; - // TODO: remove queryOptions parameter from command's run method. - bool result = this->run(txn, db, cmd, 0, errmsg, replyBuilderBob); + // run expects const db std::string (can't bind to temporary) + const std::string db = request.getDatabase().toString(); - BSONObjBuilder metadataBob; + // TODO: remove queryOptions parameter from command's run method. + bool result = this->run(txn, db, cmd, 0, errmsg, replyBuilderBob); - // For commands from mongos, append some info to help getLastError(w) work. - // TODO: refactor out of here as part of SERVER-18326 - if (replCoord->getReplicationMode() == repl::ReplicationCoordinator::modeReplSet && - shardingState.enabled()) { - rpc::ShardingMetadata( - repl::ReplClientInfo::forClient(txn->getClient()).getLastOp().getTimestamp(), - replCoord->getElectionId() - ).writeToMetadata(&metadataBob); - } + BSONObjBuilder metadataBob; - auto cmdResponse = replyBuilderBob.done(); - replyBuilder->setMetadata(metadataBob.done()); + // For commands from mongos, append some info to help getLastError(w) work. + // TODO: refactor out of here as part of SERVER-18326 + if (replCoord->getReplicationMode() == repl::ReplicationCoordinator::modeReplSet && + shardingState.enabled()) { + rpc::ShardingMetadata( + repl::ReplClientInfo::forClient(txn->getClient()).getLastOp().getTimestamp(), + replCoord->getElectionId()).writeToMetadata(&metadataBob); + } - if (result) { - replyBuilder->setCommandReply(std::move(cmdResponse)); - } - else { - // maintain existing behavior of returning all data appended to builder - // even if command returned false - replyBuilder->setCommandReply(Status(ErrorCodes::CommandFailed, errmsg), - std::move(cmdResponse)); - } + auto cmdResponse = replyBuilderBob.done(); + replyBuilder->setMetadata(metadataBob.done()); - return result; + if (result) { + replyBuilder->setCommandReply(std::move(cmdResponse)); + } else { + // maintain existing behavior of returning all data appended to builder + // even if command returned false + replyBuilder->setCommandReply(Status(ErrorCodes::CommandFailed, errmsg), + std::move(cmdResponse)); } - void Command::registerError(OperationContext* txn, const DBException& exception) { - CurOp::get(txn)->debug().exceptionInfo = exception.getInfo(); - } + return result; +} + +void Command::registerError(OperationContext* txn, const DBException& exception) { + CurOp::get(txn)->debug().exceptionInfo = exception.getInfo(); +} -} // namespace mongo +} // namespace mongo |