summaryrefslogtreecommitdiff
path: root/src/mongo/db/dbcommands.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/dbcommands.cpp')
-rw-r--r--src/mongo/db/dbcommands.cpp2023
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