summaryrefslogtreecommitdiff
path: root/src/mongo/s/commands/commands_public.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/s/commands/commands_public.cpp')
-rw-r--r--src/mongo/s/commands/commands_public.cpp2581
1 files changed, 1278 insertions, 1303 deletions
diff --git a/src/mongo/s/commands/commands_public.cpp b/src/mongo/s/commands/commands_public.cpp
index 6f0d57bfdd8..395f84c8c7e 100644
--- a/src/mongo/s/commands/commands_public.cpp
+++ b/src/mongo/s/commands/commands_public.cpp
@@ -64,1394 +64,1369 @@
namespace mongo {
- using boost::intrusive_ptr;
- using std::unique_ptr;
- using std::shared_ptr;
- using std::list;
- using std::make_pair;
- using std::map;
- using std::multimap;
- using std::set;
- using std::string;
- using std::stringstream;
- using std::vector;
-
- namespace dbgrid_pub_cmds {
-
- class PublicGridCommand : public Command {
- public:
- PublicGridCommand( const char* n, const char* oldname=NULL ) : Command( n, false, oldname ) {
- }
- virtual bool slaveOk() const {
- return true;
- }
- virtual bool adminOnly() const {
- return false;
- }
-
- // Override if passthrough should also send query options
- // Safer as off by default, can slowly enable as we add more tests
- virtual bool passOptions() const { return false; }
-
- // all grid commands are designed not to lock
- virtual bool isWriteCommandForConfigServer() const { return false; }
-
- protected:
-
- bool passthrough( DBConfigPtr conf, const BSONObj& cmdObj , BSONObjBuilder& result ) {
- return _passthrough(conf->name(), conf, cmdObj, 0, result);
- }
-
- bool adminPassthrough( DBConfigPtr conf, const BSONObj& cmdObj , BSONObjBuilder& result ) {
- return _passthrough("admin", conf, cmdObj, 0, result);
- }
-
- bool passthrough( DBConfigPtr conf, const BSONObj& cmdObj , int options, BSONObjBuilder& result ) {
- return _passthrough(conf->name(), conf, cmdObj, options, result);
- }
-
- private:
- bool _passthrough(const string& db,
- DBConfigPtr conf,
- const BSONObj& cmdObj,
- int options, BSONObjBuilder& result) {
- const auto shard = grid.shardRegistry()->getShard(conf->getPrimaryId());
- ShardConnection conn(shard->getConnString(), "");
-
- BSONObj res;
- bool ok = conn->runCommand(db, cmdObj, res, passOptions() ? options : 0);
- conn.done();
-
- result.appendElements(res);
- return ok;
+using boost::intrusive_ptr;
+using std::unique_ptr;
+using std::shared_ptr;
+using std::list;
+using std::make_pair;
+using std::map;
+using std::multimap;
+using std::set;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+namespace dbgrid_pub_cmds {
+
+class PublicGridCommand : public Command {
+public:
+ PublicGridCommand(const char* n, const char* oldname = NULL) : Command(n, false, oldname) {}
+ virtual bool slaveOk() const {
+ return true;
+ }
+ virtual bool adminOnly() const {
+ return false;
+ }
+
+ // Override if passthrough should also send query options
+ // Safer as off by default, can slowly enable as we add more tests
+ virtual bool passOptions() const {
+ return false;
+ }
+
+ // all grid commands are designed not to lock
+ virtual bool isWriteCommandForConfigServer() const {
+ return false;
+ }
+
+protected:
+ bool passthrough(DBConfigPtr conf, const BSONObj& cmdObj, BSONObjBuilder& result) {
+ return _passthrough(conf->name(), conf, cmdObj, 0, result);
+ }
+
+ bool adminPassthrough(DBConfigPtr conf, const BSONObj& cmdObj, BSONObjBuilder& result) {
+ return _passthrough("admin", conf, cmdObj, 0, result);
+ }
+
+ bool passthrough(DBConfigPtr conf, const BSONObj& cmdObj, int options, BSONObjBuilder& result) {
+ return _passthrough(conf->name(), conf, cmdObj, options, result);
+ }
+
+private:
+ bool _passthrough(const string& db,
+ DBConfigPtr conf,
+ const BSONObj& cmdObj,
+ int options,
+ BSONObjBuilder& result) {
+ const auto shard = grid.shardRegistry()->getShard(conf->getPrimaryId());
+ ShardConnection conn(shard->getConnString(), "");
+
+ BSONObj res;
+ bool ok = conn->runCommand(db, cmdObj, res, passOptions() ? options : 0);
+ conn.done();
+
+ result.appendElements(res);
+ return ok;
+ }
+};
+
+class AllShardsCollectionCommand : public RunOnAllShardsCommand {
+public:
+ AllShardsCollectionCommand(const char* n,
+ const char* oldname = NULL,
+ bool useShardConn = false,
+ bool implicitCreateDb = false)
+ : RunOnAllShardsCommand(n, oldname, useShardConn, implicitCreateDb) {}
+
+ virtual void getShardIds(const string& dbName, BSONObj& cmdObj, vector<ShardId>& shardIds) {
+ const string fullns = dbName + '.' + cmdObj.firstElement().valuestrsafe();
+
+ auto status = grid.catalogCache()->getDatabase(dbName);
+ uassertStatusOK(status.getStatus());
+
+ shared_ptr<DBConfig> conf = status.getValue();
+
+ if (!conf->isShardingEnabled() || !conf->isSharded(fullns)) {
+ shardIds.push_back(conf->getShardId(fullns));
+ } else {
+ grid.shardRegistry()->getAllShardIds(&shardIds);
+ }
+ }
+};
+
+
+class NotAllowedOnShardedCollectionCmd : public PublicGridCommand {
+public:
+ NotAllowedOnShardedCollectionCmd(const char* n) : PublicGridCommand(n) {}
+
+ virtual bool run(OperationContext* txn,
+ const string& dbName,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ const string fullns = parseNs(dbName, cmdObj);
+
+ auto conf = uassertStatusOK(grid.catalogCache()->getDatabase(dbName));
+ if (!conf->isSharded(fullns)) {
+ return passthrough(conf, cmdObj, options, result);
+ }
+
+ return appendCommandStatus(
+ result,
+ Status(ErrorCodes::IllegalOperation,
+ str::stream() << "can't do command: " << name << " on sharded collection"));
+ }
+};
+
+// ----
+
+class DropIndexesCmd : public AllShardsCollectionCommand {
+public:
+ DropIndexesCmd() : AllShardsCollectionCommand("dropIndexes", "deleteIndexes") {}
+ virtual void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) {
+ ActionSet actions;
+ actions.addAction(ActionType::dropIndex);
+ out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
+ }
+} dropIndexesCmd;
+
+class CreateIndexesCmd : public AllShardsCollectionCommand {
+public:
+ CreateIndexesCmd()
+ : AllShardsCollectionCommand("createIndexes",
+ NULL, /* oldName */
+ true /* use ShardConnection */,
+ true /* implicit create db */) {
+ // createIndexes command should use ShardConnection so the getLastError would
+ // be able to properly enforce the write concern (via the saveGLEStats callback).
+ }
+
+ /**
+ * the createIndexes command doesn't require the 'ns' field to be populated
+ * so we make sure its here as its needed for the system.indexes insert
+ */
+ BSONObj fixSpec(const NamespaceString& ns, const BSONObj& original) const {
+ if (original["ns"].type() == String)
+ return original;
+ BSONObjBuilder bb;
+ bb.appendElements(original);
+ bb.append("ns", ns.toString());
+ return bb.obj();
+ }
+
+ /**
+ * @return equivalent of gle
+ */
+ BSONObj createIndexLegacy(const string& server,
+ const NamespaceString& nss,
+ const BSONObj& spec) const {
+ try {
+ ScopedDbConnection conn(server);
+ conn->insert(nss.getSystemIndexesCollection(), spec);
+ BSONObj gle = conn->getLastErrorDetailed(nss.db().toString());
+ conn.done();
+ return gle;
+ } catch (DBException& e) {
+ BSONObjBuilder b;
+ b.append("errmsg", e.toString());
+ b.append("code", e.getCode());
+ return b.obj();
+ }
+ }
+
+ virtual BSONObj specialErrorHandler(const string& server,
+ const string& dbName,
+ const BSONObj& cmdObj,
+ const BSONObj& originalResult) const {
+ string errmsg = originalResult["errmsg"];
+ if (errmsg.find("no such cmd") == string::npos) {
+ // cannot use codes as 2.4 didn't have a code for this
+ return originalResult;
+ }
+
+ // we need to down convert
+
+ NamespaceString nss(dbName, cmdObj["createIndexes"].String());
+
+ if (cmdObj["indexes"].type() != Array)
+ return originalResult;
+
+ BSONObjBuilder newResult;
+ newResult.append("note", "downgraded");
+ newResult.append("sentTo", server);
+
+ BSONArrayBuilder individualResults;
+
+ bool ok = true;
+
+ BSONObjIterator indexIterator(cmdObj["indexes"].Obj());
+ while (indexIterator.more()) {
+ BSONObj spec = indexIterator.next().Obj();
+ spec = fixSpec(nss, spec);
+
+ BSONObj gle = createIndexLegacy(server, nss, spec);
+
+ individualResults.append(BSON("spec" << spec << "gle" << gle));
+
+ BSONElement e = gle["errmsg"];
+ if (e.type() == String && e.String().size() > 0) {
+ ok = false;
+ newResult.appendAs(e, "errmsg");
+ break;
}
- };
-
- class AllShardsCollectionCommand : public RunOnAllShardsCommand {
- public:
- AllShardsCollectionCommand(const char* n,
- const char* oldname = NULL,
- bool useShardConn = false,
- bool implicitCreateDb = false)
- : RunOnAllShardsCommand(n, oldname, useShardConn, implicitCreateDb) {
+ e = gle["err"];
+ if (e.type() == String && e.String().size() > 0) {
+ ok = false;
+ newResult.appendAs(e, "errmsg");
+ break;
}
-
- virtual void getShardIds(const string& dbName,
- BSONObj& cmdObj,
- vector<ShardId>& shardIds) {
- const string fullns = dbName + '.' + cmdObj.firstElement().valuestrsafe();
-
- auto status = grid.catalogCache()->getDatabase(dbName);
- uassertStatusOK(status.getStatus());
-
- shared_ptr<DBConfig> conf = status.getValue();
-
- if (!conf->isShardingEnabled() || !conf->isSharded(fullns)) {
- shardIds.push_back(conf->getShardId(fullns));
+ }
+
+ newResult.append("eachIndex", individualResults.arr());
+
+ newResult.append("ok", ok ? 1 : 0);
+ return newResult.obj();
+ }
+
+ virtual void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) {
+ ActionSet actions;
+ actions.addAction(ActionType::createIndex);
+ out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
+ }
+
+} createIndexesCmd;
+
+class ReIndexCmd : public AllShardsCollectionCommand {
+public:
+ ReIndexCmd() : AllShardsCollectionCommand("reIndex") {}
+ virtual void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) {
+ ActionSet actions;
+ actions.addAction(ActionType::reIndex);
+ out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
+ }
+} reIndexCmd;
+
+class CollectionModCmd : public AllShardsCollectionCommand {
+public:
+ CollectionModCmd() : AllShardsCollectionCommand("collMod") {}
+ 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));
+ }
+} collectionModCmd;
+
+
+class ValidateCmd : public AllShardsCollectionCommand {
+public:
+ ValidateCmd() : AllShardsCollectionCommand("validate") {}
+ virtual void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) {
+ ActionSet actions;
+ actions.addAction(ActionType::validate);
+ out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
+ }
+ virtual void aggregateResults(const vector<ShardAndReply>& results, BSONObjBuilder& output) {
+ for (vector<ShardAndReply>::const_iterator it(results.begin()), end(results.end());
+ it != end;
+ it++) {
+ const BSONObj& result = std::get<1>(*it);
+ const BSONElement valid = result["valid"];
+ if (!valid.eoo()) {
+ if (!valid.trueValue()) {
+ output.appendBool("valid", false);
+ return;
}
- else {
- grid.shardRegistry()->getAllShardIds(&shardIds);
+ } else {
+ // Support pre-1.9.0 output with everything in a big string
+ const char* s = result["result"].valuestrsafe();
+ if (strstr(s, "exception") || strstr(s, "corrupt")) {
+ output.appendBool("valid", false);
+ return;
}
}
- };
-
-
- class NotAllowedOnShardedCollectionCmd : public PublicGridCommand {
- public:
- NotAllowedOnShardedCollectionCmd( const char * n ) : PublicGridCommand( n ) {}
-
- virtual bool run(OperationContext* txn,
- const string& dbName,
- BSONObj& cmdObj,
- int options,
- string& errmsg,
- BSONObjBuilder& result) {
-
- const string fullns = parseNs(dbName, cmdObj);
-
- auto conf = uassertStatusOK(grid.catalogCache()->getDatabase(dbName));
- if (!conf->isSharded(fullns)) {
- return passthrough( conf , cmdObj , options, result );
- }
-
- return appendCommandStatus(result,
- Status(ErrorCodes::IllegalOperation,
- str::stream() << "can't do command: " << name
- << " on sharded collection"));
- }
-
- };
-
- // ----
-
- class DropIndexesCmd : public AllShardsCollectionCommand {
- public:
- DropIndexesCmd() : AllShardsCollectionCommand("dropIndexes", "deleteIndexes") {}
- virtual void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) {
- ActionSet actions;
- actions.addAction(ActionType::dropIndex);
- out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
- }
- } dropIndexesCmd;
-
- class CreateIndexesCmd : public AllShardsCollectionCommand {
- public:
- CreateIndexesCmd():
- AllShardsCollectionCommand("createIndexes",
- NULL, /* oldName */
- true /* use ShardConnection */,
- true /* implicit create db */) {
- // createIndexes command should use ShardConnection so the getLastError would
- // be able to properly enforce the write concern (via the saveGLEStats callback).
+ }
+
+ output.appendBool("valid", true);
+ }
+} validateCmd;
+
+class CreateCmd : public PublicGridCommand {
+public:
+ CreateCmd() : PublicGridCommand("create") {}
+ 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");
}
-
- /**
- * the createIndexes command doesn't require the 'ns' field to be populated
- * so we make sure its here as its needed for the system.indexes insert
- */
- BSONObj fixSpec( const NamespaceString& ns, const BSONObj& original ) const {
- if ( original["ns"].type() == String )
- return original;
- BSONObjBuilder bb;
- bb.appendElements( original );
- bb.append( "ns", ns.toString() );
- return bb.obj();
+ }
+
+ // 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();
+ }
+
+ return Status(ErrorCodes::Unauthorized, "unauthorized");
+ }
+ bool run(OperationContext* txn,
+ const string& dbName,
+ BSONObj& cmdObj,
+ int,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ auto status = grid.implicitCreateDb(dbName);
+ if (!status.isOK()) {
+ return appendCommandStatus(result, status.getStatus());
+ }
+
+ shared_ptr<DBConfig> conf = status.getValue();
+
+ return passthrough(conf, cmdObj, result);
+ }
+
+} createCmd;
+
+class DropCmd : public PublicGridCommand {
+public:
+ DropCmd() : PublicGridCommand("drop") {}
+ 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));
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbName,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ auto status = grid.catalogCache()->getDatabase(dbName);
+ if (!status.isOK()) {
+ if (status == ErrorCodes::DatabaseNotFound) {
+ return true;
}
- /**
- * @return equivalent of gle
- */
- BSONObj createIndexLegacy( const string& server,
- const NamespaceString& nss,
- const BSONObj& spec ) const {
- try {
- ScopedDbConnection conn( server );
- conn->insert( nss.getSystemIndexesCollection(), spec );
- BSONObj gle = conn->getLastErrorDetailed( nss.db().toString() );
- conn.done();
- return gle;
- }
- catch ( DBException& e ) {
- BSONObjBuilder b;
- b.append( "errmsg", e.toString() );
- b.append( "code", e.getCode() );
- return b.obj();
+ return appendCommandStatus(result, status.getStatus());
+ }
+
+ shared_ptr<DBConfig> conf = status.getValue();
+
+ const string fullns = dbName + "." + cmdObj.firstElement().valuestrsafe();
+ log() << "DROP: " << fullns;
+
+ if (!conf->isShardingEnabled() || !conf->isSharded(fullns)) {
+ log() << "\tdrop going to do passthrough";
+ return passthrough(conf, cmdObj, result);
+ }
+
+ //
+ // TODO: There will be problems if we simultaneously shard and drop a collection
+ //
+
+ ChunkManagerPtr cm;
+ ShardPtr primary;
+ conf->getChunkManagerOrPrimary(fullns, cm, primary);
+
+ if (!cm) {
+ log() << "\tdrop going to do passthrough after re-check";
+ return passthrough(conf, cmdObj, result);
+ }
+
+ uassertStatusOK(grid.catalogManager()->dropCollection(fullns));
+
+ if (!conf->removeSharding(fullns)) {
+ warning() << "collection " << fullns
+ << " was reloaded as unsharded before drop completed"
+ << " during single drop";
+ }
+
+ return 1;
+ }
+} dropCmd;
+
+class RenameCollectionCmd : public PublicGridCommand {
+public:
+ RenameCollectionCmd() : PublicGridCommand("renameCollection") {}
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return rename_collection::checkAuthForRenameCollectionCommand(client, dbname, cmdObj);
+ }
+ virtual bool adminOnly() const {
+ return true;
+ }
+ bool run(OperationContext* txn,
+ const string& dbName,
+ BSONObj& cmdObj,
+ int,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ const string fullnsFrom = cmdObj.firstElement().valuestrsafe();
+ const string dbNameFrom = nsToDatabase(fullnsFrom);
+ auto confFrom = uassertStatusOK(grid.catalogCache()->getDatabase(dbNameFrom));
+
+ const string fullnsTo = cmdObj["to"].valuestrsafe();
+ const string dbNameTo = nsToDatabase(fullnsTo);
+ auto confTo = uassertStatusOK(grid.catalogCache()->getDatabase(dbNameTo));
+
+ uassert(13138, "You can't rename a sharded collection", !confFrom->isSharded(fullnsFrom));
+ uassert(13139, "You can't rename to a sharded collection", !confTo->isSharded(fullnsTo));
+
+ const ShardId& shardTo = confTo->getShardId(fullnsTo);
+ const ShardId& shardFrom = confFrom->getShardId(fullnsFrom);
+
+ uassert(13137,
+ "Source and destination collections must be on same shard",
+ shardFrom == shardTo);
+
+ return adminPassthrough(confFrom, cmdObj, result);
+ }
+} renameCollectionCmd;
+
+class CopyDBCmd : public PublicGridCommand {
+public:
+ CopyDBCmd() : PublicGridCommand("copydb") {}
+ virtual bool adminOnly() const {
+ return true;
+ }
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ return copydb::checkAuthForCopydbCommand(client, dbname, cmdObj);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbName,
+ BSONObj& cmdObj,
+ int,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ const string todb = cmdObj.getStringField("todb");
+ uassert(ErrorCodes::EmptyFieldName, "missing todb argument", !todb.empty());
+ uassert(ErrorCodes::InvalidNamespace, "invalid todb argument", nsIsDbOnly(todb));
+
+ auto confTo = uassertStatusOK(grid.implicitCreateDb(todb));
+ uassert(ErrorCodes::IllegalOperation,
+ "cannot copy to a sharded database",
+ !confTo->isShardingEnabled());
+
+ const string fromhost = cmdObj.getStringField("fromhost");
+ if (!fromhost.empty()) {
+ return adminPassthrough(confTo, cmdObj, result);
+ } else {
+ const string fromdb = cmdObj.getStringField("fromdb");
+ uassert(13399, "need a fromdb argument", !fromdb.empty());
+
+ shared_ptr<DBConfig> confFrom =
+ uassertStatusOK(grid.catalogCache()->getDatabase(fromdb));
+
+ uassert(13400, "don't know where source DB is", confFrom);
+ uassert(13401, "cant copy from sharded DB", !confFrom->isShardingEnabled());
+
+ BSONObjBuilder b;
+ BSONForEach(e, cmdObj) {
+ if (strcmp(e.fieldName(), "fromhost") != 0) {
+ b.append(e);
}
}
- virtual BSONObj specialErrorHandler( const string& server,
- const string& dbName,
- const BSONObj& cmdObj,
- const BSONObj& originalResult ) const {
- string errmsg = originalResult["errmsg"];
- if ( errmsg.find( "no such cmd" ) == string::npos ) {
- // cannot use codes as 2.4 didn't have a code for this
- return originalResult;
- }
-
- // we need to down convert
-
- NamespaceString nss( dbName, cmdObj["createIndexes"].String() );
-
- if ( cmdObj["indexes"].type() != Array )
- return originalResult;
-
- BSONObjBuilder newResult;
- newResult.append( "note", "downgraded" );
- newResult.append( "sentTo", server );
-
- BSONArrayBuilder individualResults;
-
- bool ok = true;
-
- BSONObjIterator indexIterator( cmdObj["indexes"].Obj() );
- while ( indexIterator.more() ) {
- BSONObj spec = indexIterator.next().Obj();
- spec = fixSpec( nss, spec );
-
- BSONObj gle = createIndexLegacy( server, nss, spec );
-
- individualResults.append( BSON( "spec" << spec <<
- "gle" << gle ) );
-
- BSONElement e = gle["errmsg"];
- if ( e.type() == String && e.String().size() > 0 ) {
- ok = false;
- newResult.appendAs( e, "errmsg" );
- break;
- }
+ {
+ const auto& shard = grid.shardRegistry()->getShard(confFrom->getPrimaryId());
+ b.append("fromhost", shard->getConnString().toString());
+ }
+ BSONObj fixed = b.obj();
+
+ return adminPassthrough(confTo, fixed, result);
+ }
+ }
+
+} clusterCopyDBCmd;
+
+class CollectionStats : public PublicGridCommand {
+public:
+ CollectionStats() : PublicGridCommand("collStats", "collstats") {}
+ 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));
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbName,
+ BSONObj& cmdObj,
+ int,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ const string fullns = parseNs(dbName, cmdObj);
+
+ auto conf = uassertStatusOK(grid.catalogCache()->getDatabase(dbName));
+ if (!conf->isShardingEnabled() || !conf->isSharded(fullns)) {
+ result.appendBool("sharded", false);
+ result.append("primary", conf->getPrimaryId());
+
+ return passthrough(conf, cmdObj, result);
+ }
+
+ result.appendBool("sharded", true);
+
+ ChunkManagerPtr cm = conf->getChunkManager(fullns);
+ massert(12594, "how could chunk manager be null!", cm);
+
+ BSONObjBuilder shardStats;
+ map<string, long long> counts;
+ map<string, long long> indexSizes;
+ /*
+ long long count=0;
+ long long size=0;
+ long long storageSize=0;
+ */
+ int nindexes = 0;
+ bool warnedAboutIndexes = false;
+
+ set<ShardId> shardIds;
+ cm->getAllShardIds(&shardIds);
+
+ for (const ShardId& shardId : shardIds) {
+ const auto shard = grid.shardRegistry()->getShard(shardId);
+ if (!shard) {
+ continue;
+ }
- e = gle["err"];
- if ( e.type() == String && e.String().size() > 0 ) {
- ok = false;
- newResult.appendAs( e, "errmsg" );
- break;
+ BSONObj res;
+ {
+ ScopedDbConnection conn(shard->getConnString());
+ if (!conn->runCommand(dbName, cmdObj, res)) {
+ if (!res["code"].eoo()) {
+ result.append(res["code"]);
}
-
+ errmsg = "failed on shard: " + res.toString();
+ return false;
}
-
- newResult.append( "eachIndex", individualResults.arr() );
-
- newResult.append( "ok", ok ? 1 : 0 );
- return newResult.obj();
- }
-
- virtual void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) {
- ActionSet actions;
- actions.addAction(ActionType::createIndex);
- out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
+ conn.done();
}
- } createIndexesCmd;
-
- class ReIndexCmd : public AllShardsCollectionCommand {
- public:
- ReIndexCmd() : AllShardsCollectionCommand("reIndex") {}
- virtual void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) {
- ActionSet actions;
- actions.addAction(ActionType::reIndex);
- out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
- }
- } reIndexCmd;
-
- class CollectionModCmd : public AllShardsCollectionCommand {
- public:
- CollectionModCmd() : AllShardsCollectionCommand("collMod") {}
- 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));
- }
- } collectionModCmd;
-
-
- class ValidateCmd : public AllShardsCollectionCommand {
- public:
- ValidateCmd() : AllShardsCollectionCommand("validate") {}
- virtual void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) {
- ActionSet actions;
- actions.addAction(ActionType::validate);
- out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
- }
- virtual void aggregateResults(const vector<ShardAndReply>& results,
- BSONObjBuilder& output) {
-
- for (vector<ShardAndReply>::const_iterator it(results.begin()), end(results.end());
- it!=end; it++) {
- const BSONObj& result = std::get<1>(*it);
- const BSONElement valid = result["valid"];
- if (!valid.eoo()){
- if (!valid.trueValue()) {
- output.appendBool("valid", false);
- return;
- }
+ BSONObjIterator j(res);
+ while (j.more()) {
+ BSONElement e = j.next();
+
+ if (str::equals(e.fieldName(), "ns") || str::equals(e.fieldName(), "ok") ||
+ str::equals(e.fieldName(), "avgObjSize") ||
+ str::equals(e.fieldName(), "lastExtentSize") ||
+ str::equals(e.fieldName(), "paddingFactor")) {
+ continue;
+ } else if (str::equals(e.fieldName(), "count") ||
+ str::equals(e.fieldName(), "size") ||
+ str::equals(e.fieldName(), "storageSize") ||
+ str::equals(e.fieldName(), "numExtents") ||
+ str::equals(e.fieldName(), "totalIndexSize")) {
+ counts[e.fieldName()] += e.numberLong();
+ } else if (str::equals(e.fieldName(), "indexSizes")) {
+ BSONObjIterator k(e.Obj());
+ while (k.more()) {
+ BSONElement temp = k.next();
+ indexSizes[temp.fieldName()] += temp.numberLong();
}
- else {
- // Support pre-1.9.0 output with everything in a big string
- const char* s = result["result"].valuestrsafe();
- if (strstr(s, "exception") || strstr(s, "corrupt")){
- output.appendBool("valid", false);
- return;
+ }
+ // no longer used since 2.2
+ else if (str::equals(e.fieldName(), "flags")) {
+ if (!result.hasField(e.fieldName()))
+ result.append(e);
+ }
+ // flags broken out in 2.4+
+ else if (str::equals(e.fieldName(), "systemFlags")) {
+ if (!result.hasField(e.fieldName()))
+ result.append(e);
+ } else if (str::equals(e.fieldName(), "userFlags")) {
+ if (!result.hasField(e.fieldName()))
+ result.append(e);
+ } else if (str::equals(e.fieldName(), "capped")) {
+ if (!result.hasField(e.fieldName()))
+ result.append(e);
+ } else if (str::equals(e.fieldName(), "paddingFactorNote")) {
+ if (!result.hasField(e.fieldName()))
+ result.append(e);
+ } else if (str::equals(e.fieldName(), "indexDetails")) {
+ // skip this field in the rollup
+ } else if (str::equals(e.fieldName(), "wiredTiger")) {
+ // skip this field in the rollup
+ } else if (str::equals(e.fieldName(), "nindexes")) {
+ int myIndexes = e.numberInt();
+
+ if (nindexes == 0) {
+ nindexes = myIndexes;
+ } else if (nindexes == myIndexes) {
+ // no-op
+ } else {
+ // hopefully this means we're building an index
+
+ if (myIndexes > nindexes)
+ nindexes = myIndexes;
+
+ if (!warnedAboutIndexes) {
+ result.append("warning",
+ "indexes don't all match - ok if ensureIndex is running");
+ warnedAboutIndexes = true;
}
}
+ } else {
+ warning() << "mongos collstats doesn't know about: " << e.fieldName();
}
-
- output.appendBool("valid", true);
}
- } validateCmd;
-
- class CreateCmd : public PublicGridCommand {
- public:
- CreateCmd() : PublicGridCommand( "create" ) {}
- 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");
- }
- }
-
- // 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();
- }
-
- return Status(ErrorCodes::Unauthorized, "unauthorized");
+ shardStats.append(shardId, res);
+ }
+
+ result.append("ns", fullns);
+
+ for (map<string, long long>::iterator i = counts.begin(); i != counts.end(); ++i)
+ result.appendNumber(i->first, i->second);
+
+ {
+ BSONObjBuilder ib(result.subobjStart("indexSizes"));
+ for (map<string, long long>::iterator i = indexSizes.begin(); i != indexSizes.end();
+ ++i)
+ ib.appendNumber(i->first, i->second);
+ ib.done();
+ }
+
+ if (counts["count"] > 0)
+ result.append("avgObjSize", (double)counts["size"] / (double)counts["count"]);
+ else
+ result.append("avgObjSize", 0.0);
+
+ result.append("nindexes", nindexes);
+
+ result.append("nchunks", cm->numChunks());
+ result.append("shards", shardStats.obj());
+
+ return true;
+ }
+} collectionStatsCmd;
+
+class DataSizeCmd : public PublicGridCommand {
+public:
+ DataSizeCmd() : PublicGridCommand("dataSize", "datasize") {}
+ 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& cmdObj,
+ int,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ const string fullns = parseNs(dbName, cmdObj);
+
+ auto conf = uassertStatusOK(grid.catalogCache()->getDatabase(dbName));
+ if (!conf->isShardingEnabled() || !conf->isSharded(fullns)) {
+ return passthrough(conf, cmdObj, result);
+ }
+
+ ChunkManagerPtr cm = conf->getChunkManager(fullns);
+ massert(13407, "how could chunk manager be null!", cm);
+
+ BSONObj min = cmdObj.getObjectField("min");
+ BSONObj max = cmdObj.getObjectField("max");
+ BSONObj keyPattern = cmdObj.getObjectField("keyPattern");
+
+ uassert(13408,
+ "keyPattern must equal shard key",
+ cm->getShardKeyPattern().toBSON() == keyPattern);
+ uassert(13405,
+ str::stream() << "min value " << min << " does not have shard key",
+ cm->getShardKeyPattern().isShardKey(min));
+ uassert(13406,
+ str::stream() << "max value " << max << " does not have shard key",
+ cm->getShardKeyPattern().isShardKey(max));
+
+ min = cm->getShardKeyPattern().normalizeShardKey(min);
+ max = cm->getShardKeyPattern().normalizeShardKey(max);
+
+ // yes these are doubles...
+ double size = 0;
+ double numObjects = 0;
+ int millis = 0;
+
+ set<ShardId> shardIds;
+ cm->getShardIdsForRange(shardIds, min, max);
+ for (const ShardId& shardId : shardIds) {
+ const auto shard = grid.shardRegistry()->getShard(shardId);
+ if (!shard) {
+ continue;
}
- bool run(OperationContext* txn,
- const string& dbName,
- BSONObj& cmdObj,
- int,
- string& errmsg,
- BSONObjBuilder& result) {
- auto status = grid.implicitCreateDb(dbName);
- if (!status.isOK()) {
- return appendCommandStatus(result, status.getStatus());
- }
-
- shared_ptr<DBConfig> conf = status.getValue();
+ ScopedDbConnection conn(shard->getConnString());
+ BSONObj res;
+ bool ok = conn->runCommand(conf->name(), cmdObj, res);
+ conn.done();
- return passthrough(conf, cmdObj, result);
- }
-
- } createCmd;
-
- class DropCmd : public PublicGridCommand {
- public:
- DropCmd() : PublicGridCommand( "drop" ) {}
- 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 (!ok) {
+ result.appendElements(res);
+ return false;
}
- bool run(OperationContext* txn,
+ size += res["size"].number();
+ numObjects += res["numObjects"].number();
+ millis += res["millis"].numberInt();
+ }
+
+ result.append("size", size);
+ result.append("numObjects", numObjects);
+ result.append("millis", millis);
+ return true;
+ }
+
+} DataSizeCmd;
+
+class ConvertToCappedCmd : public NotAllowedOnShardedCollectionCmd {
+public:
+ ConvertToCappedCmd() : NotAllowedOnShardedCollectionCmd("convertToCapped") {}
+ virtual void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) {
+ ActionSet actions;
+ actions.addAction(ActionType::convertToCapped);
+ out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
+ }
+
+} convertToCappedCmd;
+
+
+class GroupCmd : public NotAllowedOnShardedCollectionCmd {
+public:
+ GroupCmd() : NotAllowedOnShardedCollectionCmd("group") {}
+ 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 bool passOptions() const {
+ return true;
+ }
+
+ virtual std::string parseNs(const std::string& dbName, const BSONObj& cmdObj) const {
+ return dbName + "." + cmdObj.firstElement().embeddedObjectUserCheck()["ns"].valuestrsafe();
+ }
+
+ Status explain(OperationContext* txn,
+ const std::string& dbname,
+ const BSONObj& cmdObj,
+ ExplainCommon::Verbosity verbosity,
+ BSONObjBuilder* out) const {
+ const string fullns = parseNs(dbname, cmdObj);
+
+ BSONObjBuilder explainCmdBob;
+ ClusterExplain::wrapAsExplain(cmdObj, verbosity, &explainCmdBob);
+
+ // We will time how long it takes to run the commands on the shards.
+ Timer timer;
+
+ Strategy::CommandResult singleResult;
+ Status commandStat =
+ Strategy::commandOpUnsharded(dbname, explainCmdBob.obj(), 0, fullns, &singleResult);
+ if (!commandStat.isOK()) {
+ return commandStat;
+ }
+
+ long long millisElapsed = timer.millis();
+
+ vector<Strategy::CommandResult> shardResults;
+ shardResults.push_back(singleResult);
+
+ return ClusterExplain::buildExplainResult(
+ shardResults, ClusterExplain::kSingleShard, millisElapsed, out);
+ }
+
+} groupCmd;
+
+class SplitVectorCmd : public NotAllowedOnShardedCollectionCmd {
+public:
+ SplitVectorCmd() : NotAllowedOnShardedCollectionCmd("splitVector") {}
+ virtual bool passOptions() const {
+ return true;
+ }
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ if (!AuthorizationSession::get(client)->isAuthorizedForActionsOnResource(
+ ResourcePattern::forExactNamespace(NamespaceString(parseNs(dbname, cmdObj))),
+ ActionType::splitVector)) {
+ return Status(ErrorCodes::Unauthorized, "Unauthorized");
+ }
+ return Status::OK();
+ }
+ virtual bool run(OperationContext* txn,
const string& dbName,
BSONObj& cmdObj,
int options,
string& errmsg,
BSONObjBuilder& result) {
-
- auto status = grid.catalogCache()->getDatabase(dbName);
- if (!status.isOK()) {
- if (status == ErrorCodes::DatabaseNotFound) {
- return true;
- }
-
- return appendCommandStatus(result, status.getStatus());
- }
-
- shared_ptr<DBConfig> conf = status.getValue();
-
- const string fullns = dbName + "." + cmdObj.firstElement().valuestrsafe();
- log() << "DROP: " << fullns;
-
- if (!conf->isShardingEnabled() || !conf->isSharded(fullns)) {
- log() << "\tdrop going to do passthrough";
- return passthrough( conf , cmdObj , result );
- }
-
- //
- // TODO: There will be problems if we simultaneously shard and drop a collection
- //
-
- ChunkManagerPtr cm;
- ShardPtr primary;
- conf->getChunkManagerOrPrimary( fullns, cm, primary );
-
- if( ! cm ) {
- log() << "\tdrop going to do passthrough after re-check";
- return passthrough( conf , cmdObj , result );
- }
-
- uassertStatusOK(grid.catalogManager()->dropCollection(fullns));
-
- if( ! conf->removeSharding( fullns ) ){
- warning() << "collection " << fullns
- << " was reloaded as unsharded before drop completed"
- << " during single drop";
- }
-
- return 1;
- }
- } dropCmd;
-
- class RenameCollectionCmd : public PublicGridCommand {
- public:
- RenameCollectionCmd() : PublicGridCommand( "renameCollection" ) {}
- virtual Status checkAuthForCommand(ClientBasic* client,
- const std::string& dbname,
- const BSONObj& cmdObj) {
- return rename_collection::checkAuthForRenameCollectionCommand(client,
- dbname,
- cmdObj);
+ string x = parseNs(dbName, cmdObj);
+ if (!str::startsWith(x, dbName)) {
+ errmsg = str::stream() << "doing a splitVector across dbs isn't supported via mongos";
+ return false;
+ }
+ return NotAllowedOnShardedCollectionCmd::run(txn, dbName, cmdObj, options, errmsg, result);
+ }
+ virtual std::string parseNs(const string& dbname, const BSONObj& cmdObj) const {
+ return parseNsFullyQualified(dbname, cmdObj);
+ }
+
+} splitVectorCmd;
+
+
+class DistinctCmd : public PublicGridCommand {
+public:
+ DistinctCmd() : PublicGridCommand("distinct") {}
+ virtual void help(stringstream& help) const {
+ help << "{ distinct : 'collection name' , key : 'a.b' , query : {} }";
+ }
+ virtual bool passOptions() const {
+ return true;
+ }
+ 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& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ const string fullns = parseNs(dbName, cmdObj);
+
+ auto status = grid.catalogCache()->getDatabase(dbName);
+ if (!status.isOK()) {
+ return appendEmptyResultSet(result, status.getStatus(), fullns);
+ }
+
+ shared_ptr<DBConfig> conf = status.getValue();
+ if (!conf->isShardingEnabled() || !conf->isSharded(fullns)) {
+ return passthrough(conf, cmdObj, options, result);
+ }
+
+ ChunkManagerPtr cm = conf->getChunkManager(fullns);
+ massert(10420, "how could chunk manager be null!", cm);
+
+ BSONObj query = getQuery(cmdObj);
+ set<ShardId> shardIds;
+ cm->getShardIdsForQuery(shardIds, query);
+
+ set<BSONObj, BSONObjCmp> all;
+ int size = 32;
+
+ for (const ShardId& shardId : shardIds) {
+ const auto shard = grid.shardRegistry()->getShard(shardId);
+ if (!shard) {
+ continue;
}
- virtual bool adminOnly() const {
- return true;
- }
- bool run(OperationContext* txn,
- const string& dbName,
- BSONObj& cmdObj,
- int,
- string& errmsg,
- BSONObjBuilder& result) {
- const string fullnsFrom = cmdObj.firstElement().valuestrsafe();
- const string dbNameFrom = nsToDatabase(fullnsFrom);
- auto confFrom = uassertStatusOK(grid.catalogCache()->getDatabase(dbNameFrom));
-
- const string fullnsTo = cmdObj["to"].valuestrsafe();
- const string dbNameTo = nsToDatabase(fullnsTo);
- auto confTo = uassertStatusOK(grid.catalogCache()->getDatabase(dbNameTo));
-
- uassert(13138, "You can't rename a sharded collection", !confFrom->isSharded(fullnsFrom));
- uassert(13139, "You can't rename to a sharded collection", !confTo->isSharded(fullnsTo));
-
- const ShardId& shardTo = confTo->getShardId(fullnsTo);
- const ShardId& shardFrom = confFrom->getShardId(fullnsFrom);
- uassert(13137,
- "Source and destination collections must be on same shard",
- shardFrom == shardTo);
+ ShardConnection conn(shard->getConnString(), fullns);
+ BSONObj res;
+ bool ok = conn->runCommand(conf->name(), cmdObj, res, options);
+ conn.done();
- return adminPassthrough( confFrom , cmdObj , result );
+ if (!ok) {
+ result.appendElements(res);
+ return false;
}
- } renameCollectionCmd;
- class CopyDBCmd : public PublicGridCommand {
- public:
- CopyDBCmd() : PublicGridCommand( "copydb" ) {}
- virtual bool adminOnly() const {
- return true;
+ BSONObjIterator it(res["values"].embeddedObject());
+ while (it.more()) {
+ BSONElement nxt = it.next();
+ BSONObjBuilder temp(32);
+ temp.appendAs(nxt, "");
+ all.insert(temp.obj());
}
- virtual Status checkAuthForCommand(ClientBasic* client,
- const std::string& dbname,
- const BSONObj& cmdObj) {
- return copydb::checkAuthForCopydbCommand(client, dbname, cmdObj);
- }
-
- bool run(OperationContext* txn,
- const string& dbName,
- BSONObj& cmdObj,
- int,
- string& errmsg,
- BSONObjBuilder& result) {
-
- const string todb = cmdObj.getStringField("todb");
- uassert(ErrorCodes::EmptyFieldName, "missing todb argument", !todb.empty());
- uassert(ErrorCodes::InvalidNamespace, "invalid todb argument", nsIsDbOnly(todb));
-
- auto confTo = uassertStatusOK(grid.implicitCreateDb(todb));
- uassert(ErrorCodes::IllegalOperation,
- "cannot copy to a sharded database",
- !confTo->isShardingEnabled());
-
- const string fromhost = cmdObj.getStringField("fromhost");
- if (!fromhost.empty()) {
- return adminPassthrough( confTo , cmdObj , result );
- }
- else {
- const string fromdb = cmdObj.getStringField("fromdb");
- uassert(13399, "need a fromdb argument", !fromdb.empty());
-
- shared_ptr<DBConfig> confFrom =
- uassertStatusOK(grid.catalogCache()->getDatabase(fromdb));
-
- uassert(13400, "don't know where source DB is", confFrom);
- uassert(13401, "cant copy from sharded DB", !confFrom->isShardingEnabled());
-
- BSONObjBuilder b;
- BSONForEach(e, cmdObj) {
- if (strcmp(e.fieldName(), "fromhost") != 0) {
- b.append(e);
- }
- }
-
- {
- const auto& shard =
- grid.shardRegistry()->getShard(confFrom->getPrimaryId());
- b.append("fromhost", shard->getConnString().toString());
- }
- BSONObj fixed = b.obj();
-
- return adminPassthrough( confTo , fixed , result );
+ }
+
+ BSONObjBuilder b(size);
+ int n = 0;
+ for (set<BSONObj, BSONObjCmp>::iterator i = all.begin(); i != all.end(); i++) {
+ b.appendAs(i->firstElement(), b.numStr(n++));
+ }
+
+ result.appendArray("values", b.obj());
+ return true;
+ }
+} disinctCmd;
+
+class FileMD5Cmd : public PublicGridCommand {
+public:
+ FileMD5Cmd() : PublicGridCommand("filemd5") {}
+ virtual void help(stringstream& help) const {
+ help << " example: { filemd5 : ObjectId(aaaaaaa) , root : \"fs\" }";
+ }
+
+ 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));
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbName,
+ BSONObj& cmdObj,
+ int,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ const string fullns = parseNs(dbName, cmdObj);
+
+ auto conf = uassertStatusOK(grid.catalogCache()->getDatabase(dbName));
+ if (!conf->isShardingEnabled() || !conf->isSharded(fullns)) {
+ return passthrough(conf, cmdObj, result);
+ }
+
+ ChunkManagerPtr cm = conf->getChunkManager(fullns);
+ massert(13091, "how could chunk manager be null!", cm);
+ if (cm->getShardKeyPattern().toBSON() == BSON("files_id" << 1)) {
+ BSONObj finder = BSON("files_id" << cmdObj.firstElement());
+
+ vector<Strategy::CommandResult> results;
+ Strategy::commandOp(dbName, cmdObj, 0, fullns, finder, &results);
+ verify(results.size() == 1); // querying on shard key so should only talk to one shard
+ BSONObj res = results.begin()->result;
+
+ result.appendElements(res);
+ return res["ok"].trueValue();
+ } else if (cm->getShardKeyPattern().toBSON() == BSON("files_id" << 1 << "n" << 1)) {
+ int n = 0;
+ BSONObj lastResult;
+
+ while (true) {
+ // Theory of operation: Starting with n=0, send filemd5 command to shard
+ // with that chunk (gridfs chunk not sharding chunk). That shard will then
+ // compute a partial md5 state (passed in the "md5state" field) for all
+ // contiguous chunks that it has. When it runs out or hits a discontinuity
+ // (eg [1,2,7]) it returns what it has done so far. This is repeated as
+ // long as we keep getting more chunks. The end condition is when we go to
+ // look for chunk n and it doesn't exist. This means that the file's last
+ // chunk is n-1, so we return the computed md5 results.
+ BSONObjBuilder bb;
+ bb.appendElements(cmdObj);
+ bb.appendBool("partialOk", true);
+ bb.append("startAt", n);
+ if (!lastResult.isEmpty()) {
+ bb.append(lastResult["md5state"]);
}
+ BSONObj shardCmd = bb.obj();
- }
-
- } clusterCopyDBCmd;
-
- class CollectionStats : public PublicGridCommand {
- public:
- CollectionStats() : PublicGridCommand("collStats", "collstats") { }
- 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));
- }
+ BSONObj finder = BSON("files_id" << cmdObj.firstElement() << "n" << n);
- bool run(OperationContext* txn,
- const string& dbName,
- BSONObj& cmdObj,
- int,
- string& errmsg,
- BSONObjBuilder& result) {
- const string fullns = parseNs(dbName, cmdObj);
-
- auto conf = uassertStatusOK(grid.catalogCache()->getDatabase(dbName));
- if (!conf->isShardingEnabled() || !conf->isSharded(fullns)) {
- result.appendBool("sharded", false);
- result.append("primary", conf->getPrimaryId());
-
- return passthrough( conf , cmdObj , result);
+ vector<Strategy::CommandResult> results;
+ try {
+ Strategy::commandOp(dbName, shardCmd, 0, fullns, finder, &results);
+ } catch (DBException& e) {
+ // This is handled below and logged
+ Strategy::CommandResult errResult;
+ errResult.shardTargetId = "";
+ errResult.result = BSON("errmsg" << e.what() << "ok" << 0);
+ results.push_back(errResult);
}
- result.appendBool("sharded", true);
-
- ChunkManagerPtr cm = conf->getChunkManager( fullns );
- massert( 12594 , "how could chunk manager be null!" , cm );
-
- BSONObjBuilder shardStats;
- map<string,long long> counts;
- map<string,long long> indexSizes;
- /*
- long long count=0;
- long long size=0;
- long long storageSize=0;
- */
- int nindexes=0;
- bool warnedAboutIndexes = false;
-
- set<ShardId> shardIds;
- cm->getAllShardIds(&shardIds);
-
- for (const ShardId& shardId : shardIds) {
- const auto shard = grid.shardRegistry()->getShard(shardId);
- if (!shard) {
- continue;
- }
-
- BSONObj res;
- {
- ScopedDbConnection conn(shard->getConnString());
- if ( ! conn->runCommand( dbName , cmdObj , res ) ) {
- if ( !res["code"].eoo() ) {
- result.append( res["code"] );
- }
- errmsg = "failed on shard: " + res.toString();
- return false;
- }
- conn.done();
- }
-
- BSONObjIterator j( res );
- while ( j.more() ) {
- BSONElement e = j.next();
-
- if ( str::equals( e.fieldName() , "ns" ) ||
- str::equals( e.fieldName() , "ok" ) ||
- str::equals( e.fieldName() , "avgObjSize" ) ||
- str::equals( e.fieldName() , "lastExtentSize" ) ||
- str::equals( e.fieldName() , "paddingFactor" ) ) {
- continue;
- }
- else if ( str::equals( e.fieldName() , "count" ) ||
- str::equals( e.fieldName() , "size" ) ||
- str::equals( e.fieldName() , "storageSize" ) ||
- str::equals( e.fieldName() , "numExtents" ) ||
- str::equals( e.fieldName() , "totalIndexSize" ) ) {
- counts[e.fieldName()] += e.numberLong();
- }
- else if ( str::equals( e.fieldName() , "indexSizes" ) ) {
- BSONObjIterator k( e.Obj() );
- while ( k.more() ) {
- BSONElement temp = k.next();
- indexSizes[temp.fieldName()] += temp.numberLong();
- }
- }
- // no longer used since 2.2
- else if ( str::equals( e.fieldName() , "flags" ) ) {
- if ( ! result.hasField( e.fieldName() ) )
- result.append( e );
- }
- // flags broken out in 2.4+
- else if ( str::equals( e.fieldName() , "systemFlags" ) ) {
- if ( ! result.hasField( e.fieldName() ) )
- result.append( e );
- }
- else if ( str::equals( e.fieldName() , "userFlags" ) ) {
- if ( ! result.hasField( e.fieldName() ) )
- result.append( e );
- }
- else if ( str::equals( e.fieldName() , "capped" ) ) {
- if ( ! result.hasField( e.fieldName() ) )
- result.append( e );
- }
- else if ( str::equals( e.fieldName() , "paddingFactorNote" ) ) {
- if ( ! result.hasField( e.fieldName() ) )
- result.append( e );
- }
- else if ( str::equals( e.fieldName() , "indexDetails" ) ) {
- //skip this field in the rollup
- }
- else if ( str::equals( e.fieldName() , "wiredTiger" ) ) {
- //skip this field in the rollup
- }
- else if ( str::equals( e.fieldName() , "nindexes" ) ) {
- int myIndexes = e.numberInt();
-
- if ( nindexes == 0 ) {
- nindexes = myIndexes;
- }
- else if ( nindexes == myIndexes ) {
- // no-op
- }
- else {
- // hopefully this means we're building an index
-
- if ( myIndexes > nindexes )
- nindexes = myIndexes;
-
- if ( ! warnedAboutIndexes ) {
- result.append( "warning" , "indexes don't all match - ok if ensureIndex is running" );
- warnedAboutIndexes = true;
- }
- }
- }
- else {
- warning() << "mongos collstats doesn't know about: " << e.fieldName();
- }
-
+ verify(results.size() ==
+ 1); // querying on shard key so should only talk to one shard
+ BSONObj res = results.begin()->result;
+ bool ok = res["ok"].trueValue();
+
+ if (!ok) {
+ // Add extra info to make debugging easier
+ result.append("failedAt", n);
+ result.append("sentCommand", shardCmd);
+ BSONForEach(e, res) {
+ if (!str::equals(e.fieldName(), "errmsg"))
+ result.append(e);
}
- shardStats.append(shardId, res);
- }
- result.append("ns", fullns);
-
- for ( map<string,long long>::iterator i=counts.begin(); i!=counts.end(); ++i )
- result.appendNumber( i->first , i->second );
-
- {
- BSONObjBuilder ib( result.subobjStart( "indexSizes" ) );
- for ( map<string,long long>::iterator i=indexSizes.begin(); i!=indexSizes.end(); ++i )
- ib.appendNumber( i->first , i->second );
- ib.done();
- }
+ log() << "Sharded filemd5 failed: " << result.asTempObj();
- if ( counts["count"] > 0 )
- result.append("avgObjSize", (double)counts["size"] / (double)counts["count"] );
- else
- result.append( "avgObjSize", 0.0 );
-
- result.append("nindexes", nindexes);
-
- result.append("nchunks", cm->numChunks());
- result.append("shards", shardStats.obj());
-
- return true;
- }
- } collectionStatsCmd;
-
- class DataSizeCmd : public PublicGridCommand {
- public:
- DataSizeCmd() : PublicGridCommand("dataSize", "datasize") { }
- 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& cmdObj,
- int,
- string& errmsg,
- BSONObjBuilder& result) {
- const string fullns = parseNs(dbName, cmdObj);
-
- auto conf = uassertStatusOK(grid.catalogCache()->getDatabase(dbName));
- if (!conf->isShardingEnabled() || !conf->isSharded(fullns)) {
- return passthrough( conf , cmdObj , result);
+ errmsg =
+ string("sharded filemd5 failed because: ") + res["errmsg"].valuestrsafe();
+ return false;
}
- ChunkManagerPtr cm = conf->getChunkManager( fullns );
- massert( 13407 , "how could chunk manager be null!" , cm );
-
- BSONObj min = cmdObj.getObjectField( "min" );
- BSONObj max = cmdObj.getObjectField( "max" );
- BSONObj keyPattern = cmdObj.getObjectField( "keyPattern" );
-
- uassert( 13408, "keyPattern must equal shard key",
- cm->getShardKeyPattern().toBSON() == keyPattern );
- uassert( 13405, str::stream() << "min value " << min << " does not have shard key",
- cm->getShardKeyPattern().isShardKey(min) );
- uassert( 13406, str::stream() << "max value " << max << " does not have shard key",
- cm->getShardKeyPattern().isShardKey(max) );
-
- min = cm->getShardKeyPattern().normalizeShardKey(min);
- max = cm->getShardKeyPattern().normalizeShardKey(max);
-
- // yes these are doubles...
- double size = 0;
- double numObjects = 0;
- int millis = 0;
-
- set<ShardId> shardIds;
- cm->getShardIdsForRange(shardIds, min, max);
- for (const ShardId& shardId : shardIds) {
- const auto shard = grid.shardRegistry()->getShard(shardId);
- if (!shard) {
- continue;
- }
+ uassert(16246,
+ "Shard " + conf->name() +
+ " is too old to support GridFS sharded by {files_id:1, n:1}",
+ res.hasField("md5state"));
- ScopedDbConnection conn(shard->getConnString());
- BSONObj res;
- bool ok = conn->runCommand( conf->name() , cmdObj , res );
- conn.done();
-
- if ( ! ok ) {
- result.appendElements( res );
- return false;
- }
+ lastResult = res;
+ int nNext = res["numChunks"].numberInt();
- size += res["size"].number();
- numObjects += res["numObjects"].number();
- millis += res["millis"].numberInt();
+ if (n == nNext) {
+ // no new data means we've reached the end of the file
+ result.appendElements(res);
+ return true;
}
- result.append( "size", size );
- result.append( "numObjects" , numObjects );
- result.append( "millis" , millis );
- return true;
- }
-
- } DataSizeCmd;
-
- class ConvertToCappedCmd : public NotAllowedOnShardedCollectionCmd {
- public:
- ConvertToCappedCmd() : NotAllowedOnShardedCollectionCmd("convertToCapped") {}
- virtual void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) {
- ActionSet actions;
- actions.addAction(ActionType::convertToCapped);
- out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
+ verify(nNext > n);
+ n = nNext;
}
- } convertToCappedCmd;
-
-
- class GroupCmd : public NotAllowedOnShardedCollectionCmd {
- public:
- GroupCmd() : NotAllowedOnShardedCollectionCmd("group") {}
- 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));
+ verify(0);
+ }
+
+ // We could support arbitrary shard keys by sending commands to all shards but I don't think we should
+ errmsg =
+ "GridFS fs.chunks collection must be sharded on either {files_id:1} or {files_id:1, "
+ "n:1}";
+ return false;
+ }
+} fileMD5Cmd;
+
+class Geo2dFindNearCmd : public PublicGridCommand {
+public:
+ Geo2dFindNearCmd() : PublicGridCommand("geoNear") {}
+ void help(stringstream& h) const {
+ h << "http://dochub.mongodb.org/core/geo#GeospatialIndexing-geoNearCommand";
+ }
+ virtual bool passOptions() const {
+ return true;
+ }
+ 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& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ const string fullns = parseNs(dbName, cmdObj);
+
+ auto conf = uassertStatusOK(grid.catalogCache()->getDatabase(dbName));
+ if (!conf->isShardingEnabled() || !conf->isSharded(fullns)) {
+ return passthrough(conf, cmdObj, options, result);
+ }
+
+ ChunkManagerPtr cm = conf->getChunkManager(fullns);
+ massert(13500, "how could chunk manager be null!", cm);
+
+ BSONObj query = getQuery(cmdObj);
+ set<ShardId> shardIds;
+ cm->getShardIdsForQuery(shardIds, query);
+
+ // We support both "num" and "limit" options to control limit
+ int limit = 100;
+ const char* limitName = cmdObj["num"].isNumber() ? "num" : "limit";
+ if (cmdObj[limitName].isNumber())
+ limit = cmdObj[limitName].numberInt();
+
+ list<shared_ptr<Future::CommandResult>> futures;
+ BSONArrayBuilder shardArray;
+ for (const ShardId& shardId : shardIds) {
+ const auto shard = grid.shardRegistry()->getShard(shardId);
+ if (!shard) {
+ continue;
}
- virtual bool passOptions() const { return true; }
-
- virtual std::string parseNs(const std::string& dbName, const BSONObj& cmdObj) const {
- return dbName + "." + cmdObj.firstElement()
- .embeddedObjectUserCheck()["ns"]
- .valuestrsafe();
- }
-
- Status explain(OperationContext* txn,
- const std::string& dbname,
- const BSONObj& cmdObj,
- ExplainCommon::Verbosity verbosity,
- BSONObjBuilder* out) const {
- const string fullns = parseNs(dbname, cmdObj);
-
- BSONObjBuilder explainCmdBob;
- ClusterExplain::wrapAsExplain(cmdObj, verbosity, &explainCmdBob);
-
- // We will time how long it takes to run the commands on the shards.
- Timer timer;
-
- Strategy::CommandResult singleResult;
- Status commandStat = Strategy::commandOpUnsharded(dbname,
- explainCmdBob.obj(),
- 0,
- fullns,
- &singleResult);
- if (!commandStat.isOK()) {
- return commandStat;
+ futures.push_back(
+ Future::spawnCommand(shard->getConnString().toString(), dbName, cmdObj, options));
+ shardArray.append(shardId);
+ }
+
+ multimap<double, BSONObj> results; // TODO: maybe use merge-sort instead
+ string nearStr;
+ double time = 0;
+ double btreelocs = 0;
+ double nscanned = 0;
+ double objectsLoaded = 0;
+ for (list<shared_ptr<Future::CommandResult>>::iterator i = futures.begin();
+ i != futures.end();
+ i++) {
+ shared_ptr<Future::CommandResult> res = *i;
+ if (!res->join()) {
+ errmsg = res->result()["errmsg"].String();
+ if (res->result().hasField("code")) {
+ result.append(res->result()["code"]);
}
-
- long long millisElapsed = timer.millis();
-
- vector<Strategy::CommandResult> shardResults;
- shardResults.push_back(singleResult);
-
- return ClusterExplain::buildExplainResult(shardResults,
- ClusterExplain::kSingleShard,
- millisElapsed,
- out);
+ return false;
}
- } groupCmd;
-
- class SplitVectorCmd : public NotAllowedOnShardedCollectionCmd {
- public:
- SplitVectorCmd() : NotAllowedOnShardedCollectionCmd("splitVector") {}
- virtual bool passOptions() const { return true; }
- virtual Status checkAuthForCommand(ClientBasic* client,
- const std::string& dbname,
- const BSONObj& cmdObj) {
- if (!AuthorizationSession::get(client)->isAuthorizedForActionsOnResource(
- ResourcePattern::forExactNamespace(NamespaceString(parseNs(dbname,
- cmdObj))),
- ActionType::splitVector)) {
- return Status(ErrorCodes::Unauthorized, "Unauthorized");
- }
- return Status::OK();
+ if (res->result().hasField("near")) {
+ nearStr = res->result()["near"].String();
}
- virtual bool run(OperationContext* txn,
- const string& dbName,
- BSONObj& cmdObj,
- int options,
- string& errmsg,
- BSONObjBuilder& result) {
- string x = parseNs(dbName, cmdObj);
- if ( ! str::startsWith( x , dbName ) ) {
- errmsg = str::stream() << "doing a splitVector across dbs isn't supported via mongos";
- return false;
- }
- return NotAllowedOnShardedCollectionCmd::run(txn,
- dbName,
- cmdObj,
- options,
- errmsg,
- result);
+ time += res->result()["stats"]["time"].Number();
+ if (!res->result()["stats"]["btreelocs"].eoo()) {
+ btreelocs += res->result()["stats"]["btreelocs"].Number();
}
- virtual std::string parseNs(const string& dbname, const BSONObj& cmdObj) const {
- return parseNsFullyQualified(dbname, cmdObj);
+ nscanned += res->result()["stats"]["nscanned"].Number();
+ if (!res->result()["stats"]["objectsLoaded"].eoo()) {
+ objectsLoaded += res->result()["stats"]["objectsLoaded"].Number();
}
- } splitVectorCmd;
-
-
- class DistinctCmd : public PublicGridCommand {
- public:
- DistinctCmd() : PublicGridCommand("distinct") {}
- virtual void help( stringstream &help ) const {
- help << "{ distinct : 'collection name' , key : 'a.b' , query : {} }";
- }
- virtual bool passOptions() const { return true; }
- 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));
+ BSONForEach(obj, res->result()["results"].embeddedObject()) {
+ results.insert(make_pair(obj["dis"].Number(), obj.embeddedObject().getOwned()));
}
- bool run(OperationContext* txn,
- const string& dbName ,
- BSONObj& cmdObj,
- int options,
- string& errmsg,
- BSONObjBuilder& result) {
- const string fullns = parseNs(dbName, cmdObj);
-
- auto status = grid.catalogCache()->getDatabase(dbName);
- if (!status.isOK()) {
- return appendEmptyResultSet(result, status.getStatus(), fullns);
- }
-
- shared_ptr<DBConfig> conf = status.getValue();
- if (!conf->isShardingEnabled() || !conf->isSharded(fullns)) {
- return passthrough(conf, cmdObj, options, result);
- }
-
- ChunkManagerPtr cm = conf->getChunkManager( fullns );
- massert( 10420 , "how could chunk manager be null!" , cm );
-
- BSONObj query = getQuery(cmdObj);
- set<ShardId> shardIds;
- cm->getShardIdsForQuery(shardIds, query);
-
- set<BSONObj,BSONObjCmp> all;
- int size = 32;
-
- for (const ShardId& shardId : shardIds) {
- const auto shard = grid.shardRegistry()->getShard(shardId);
- if (!shard) {
- continue;
- }
+ // TODO: maybe shrink results if size() > limit
+ }
- ShardConnection conn(shard->getConnString(), fullns);
- BSONObj res;
- bool ok = conn->runCommand( conf->name() , cmdObj , res, options );
- conn.done();
+ result.append("ns", fullns);
+ result.append("near", nearStr);
- if ( ! ok ) {
- result.appendElements( res );
- return false;
- }
-
- BSONObjIterator it( res["values"].embeddedObject() );
- while ( it.more() ) {
- BSONElement nxt = it.next();
- BSONObjBuilder temp(32);
- temp.appendAs( nxt , "" );
- all.insert( temp.obj() );
- }
-
- }
-
- BSONObjBuilder b( size );
- int n=0;
- for ( set<BSONObj,BSONObjCmp>::iterator i = all.begin() ; i != all.end(); i++ ) {
- b.appendAs( i->firstElement() , b.numStr( n++ ) );
- }
-
- result.appendArray( "values" , b.obj() );
- return true;
- }
- } disinctCmd;
+ int outCount = 0;
+ double totalDistance = 0;
+ double maxDistance = 0;
+ {
+ BSONArrayBuilder sub(result.subarrayStart("results"));
+ for (multimap<double, BSONObj>::const_iterator it(results.begin()), end(results.end());
+ it != end && outCount < limit;
+ ++it, ++outCount) {
+ totalDistance += it->first;
+ maxDistance = it->first; // guaranteed to be highest so far
- class FileMD5Cmd : public PublicGridCommand {
- public:
- FileMD5Cmd() : PublicGridCommand("filemd5") {}
- virtual void help( stringstream &help ) const {
- help << " example: { filemd5 : ObjectId(aaaaaaa) , root : \"fs\" }";
+ sub.append(it->second);
}
-
- 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));
- }
-
- bool run(OperationContext* txn,
+ sub.done();
+ }
+
+ {
+ BSONObjBuilder sub(result.subobjStart("stats"));
+ sub.append("time", time);
+ sub.append("btreelocs", btreelocs);
+ sub.append("nscanned", nscanned);
+ sub.append("objectsLoaded", objectsLoaded);
+ sub.append("avgDistance", (outCount == 0) ? 0 : (totalDistance / outCount));
+ sub.append("maxDistance", maxDistance);
+ sub.append("shards", shardArray.arr());
+ sub.done();
+ }
+
+ return true;
+ }
+} geo2dFindNearCmd;
+
+class ApplyOpsCmd : public PublicGridCommand {
+public:
+ ApplyOpsCmd() : PublicGridCommand("applyOps") {}
+ virtual void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) {
+ // applyOps can do pretty much anything, so require all privileges.
+ RoleGraph::generateUniversalPrivileges(out);
+ }
+ virtual bool run(OperationContext* txn,
const string& dbName,
BSONObj& cmdObj,
int,
string& errmsg,
BSONObjBuilder& result) {
- const string fullns = parseNs(dbName, cmdObj);
-
- auto conf = uassertStatusOK(grid.catalogCache()->getDatabase(dbName));
- if (!conf->isShardingEnabled() || !conf->isSharded(fullns)) {
- return passthrough( conf , cmdObj , result );
- }
-
- ChunkManagerPtr cm = conf->getChunkManager( fullns );
- massert( 13091 , "how could chunk manager be null!" , cm );
- if(cm->getShardKeyPattern().toBSON() == BSON("files_id" << 1)) {
- BSONObj finder = BSON("files_id" << cmdObj.firstElement());
-
- vector<Strategy::CommandResult> results;
- Strategy::commandOp(dbName, cmdObj, 0, fullns, finder, &results);
- verify(results.size() == 1); // querying on shard key so should only talk to one shard
- BSONObj res = results.begin()->result;
-
- result.appendElements(res);
- return res["ok"].trueValue();
- }
- else if (cm->getShardKeyPattern().toBSON() == BSON("files_id" << 1 << "n" << 1)) {
- int n = 0;
- BSONObj lastResult;
-
- while (true) {
- // Theory of operation: Starting with n=0, send filemd5 command to shard
- // with that chunk (gridfs chunk not sharding chunk). That shard will then
- // compute a partial md5 state (passed in the "md5state" field) for all
- // contiguous chunks that it has. When it runs out or hits a discontinuity
- // (eg [1,2,7]) it returns what it has done so far. This is repeated as
- // long as we keep getting more chunks. The end condition is when we go to
- // look for chunk n and it doesn't exist. This means that the file's last
- // chunk is n-1, so we return the computed md5 results.
- BSONObjBuilder bb;
- bb.appendElements(cmdObj);
- bb.appendBool("partialOk", true);
- bb.append("startAt", n);
- if (!lastResult.isEmpty()){
- bb.append(lastResult["md5state"]);
- }
- BSONObj shardCmd = bb.obj();
-
- BSONObj finder = BSON("files_id" << cmdObj.firstElement() << "n" << n);
-
- vector<Strategy::CommandResult> results;
- try {
- Strategy::commandOp(dbName, shardCmd, 0, fullns, finder, &results);
- }
- catch( DBException& e ){
- //This is handled below and logged
- Strategy::CommandResult errResult;
- errResult.shardTargetId = "";
- errResult.result = BSON("errmsg" << e.what() << "ok" << 0 );
- results.push_back( errResult );
- }
-
- verify(results.size() == 1); // querying on shard key so should only talk to one shard
- BSONObj res = results.begin()->result;
- bool ok = res["ok"].trueValue();
-
- if (!ok) {
- // Add extra info to make debugging easier
- result.append("failedAt", n);
- result.append("sentCommand", shardCmd);
- BSONForEach(e, res){
- if (!str::equals(e.fieldName(), "errmsg"))
- result.append(e);
- }
-
- log() << "Sharded filemd5 failed: " << result.asTempObj();
-
- errmsg = string("sharded filemd5 failed because: ") + res["errmsg"].valuestrsafe();
- return false;
- }
-
- uassert(16246, "Shard " + conf->name() + " is too old to support GridFS sharded by {files_id:1, n:1}",
- res.hasField("md5state"));
-
- lastResult = res;
- int nNext = res["numChunks"].numberInt();
-
- if (n == nNext){
- // no new data means we've reached the end of the file
- result.appendElements(res);
- return true;
- }
-
- verify(nNext > n);
- n = nNext;
- }
-
- verify(0);
- }
-
- // We could support arbitrary shard keys by sending commands to all shards but I don't think we should
- errmsg = "GridFS fs.chunks collection must be sharded on either {files_id:1} or {files_id:1, n:1}";
- return false;
- }
- } fileMD5Cmd;
-
- class Geo2dFindNearCmd : public PublicGridCommand {
- public:
- Geo2dFindNearCmd() : PublicGridCommand( "geoNear" ) {}
- void help(stringstream& h) const { h << "http://dochub.mongodb.org/core/geo#GeospatialIndexing-geoNearCommand"; }
- virtual bool passOptions() const { return true; }
- 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,
+ errmsg = "applyOps not allowed through mongos";
+ return false;
+ }
+} applyOpsCmd;
+
+
+class CompactCmd : public PublicGridCommand {
+public:
+ CompactCmd() : PublicGridCommand("compact") {}
+ virtual void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) {
+ ActionSet actions;
+ actions.addAction(ActionType::compact);
+ out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
+ }
+ virtual bool run(OperationContext* txn,
const string& dbName,
BSONObj& cmdObj,
- int options,
+ int,
string& errmsg,
BSONObjBuilder& result) {
- const string fullns = parseNs(dbName, cmdObj);
-
- auto conf = uassertStatusOK(grid.catalogCache()->getDatabase(dbName));
- if (!conf->isShardingEnabled() || !conf->isSharded(fullns)) {
- return passthrough( conf , cmdObj , options, result );
- }
-
- ChunkManagerPtr cm = conf->getChunkManager( fullns );
- massert( 13500 , "how could chunk manager be null!" , cm );
-
- BSONObj query = getQuery(cmdObj);
- set<ShardId> shardIds;
- cm->getShardIdsForQuery(shardIds, query);
-
- // We support both "num" and "limit" options to control limit
- int limit = 100;
- const char* limitName = cmdObj["num"].isNumber() ? "num" : "limit";
- if (cmdObj[limitName].isNumber())
- limit = cmdObj[limitName].numberInt();
-
- list< shared_ptr<Future::CommandResult> > futures;
- BSONArrayBuilder shardArray;
- for (const ShardId& shardId : shardIds) {
- const auto shard = grid.shardRegistry()->getShard(shardId);
- if (!shard) {
- continue;
- }
-
- futures.push_back(Future::spawnCommand(shard->getConnString().toString(),
- dbName,
- cmdObj,
- options));
- shardArray.append(shardId);
- }
-
- multimap<double, BSONObj> results; // TODO: maybe use merge-sort instead
- string nearStr;
- double time = 0;
- double btreelocs = 0;
- double nscanned = 0;
- double objectsLoaded = 0;
- for ( list< shared_ptr<Future::CommandResult> >::iterator i=futures.begin(); i!=futures.end(); i++ ) {
- shared_ptr<Future::CommandResult> res = *i;
- if ( ! res->join() ) {
- errmsg = res->result()["errmsg"].String();
- if (res->result().hasField("code")) {
- result.append(res->result()["code"]);
- }
- return false;
- }
-
- if (res->result().hasField("near")) {
- nearStr = res->result()["near"].String();
- }
- time += res->result()["stats"]["time"].Number();
- if (!res->result()["stats"]["btreelocs"].eoo()) {
- btreelocs += res->result()["stats"]["btreelocs"].Number();
- }
- nscanned += res->result()["stats"]["nscanned"].Number();
- if (!res->result()["stats"]["objectsLoaded"].eoo()) {
- objectsLoaded += res->result()["stats"]["objectsLoaded"].Number();
- }
-
- BSONForEach(obj, res->result()["results"].embeddedObject()) {
- results.insert(make_pair(obj["dis"].Number(), obj.embeddedObject().getOwned()));
- }
-
- // TODO: maybe shrink results if size() > limit
- }
-
- result.append("ns" , fullns);
- result.append("near", nearStr);
-
- int outCount = 0;
- double totalDistance = 0;
- double maxDistance = 0;
- {
- BSONArrayBuilder sub (result.subarrayStart("results"));
- for (multimap<double, BSONObj>::const_iterator it(results.begin()), end(results.end()); it!= end && outCount < limit; ++it, ++outCount) {
- totalDistance += it->first;
- maxDistance = it->first; // guaranteed to be highest so far
-
- sub.append(it->second);
- }
- sub.done();
- }
-
- {
- BSONObjBuilder sub (result.subobjStart("stats"));
- sub.append("time", time);
- sub.append("btreelocs", btreelocs);
- sub.append("nscanned", nscanned);
- sub.append("objectsLoaded", objectsLoaded);
- sub.append("avgDistance", (outCount == 0) ? 0: (totalDistance / outCount));
- sub.append("maxDistance", maxDistance);
- sub.append("shards", shardArray.arr());
- sub.done();
- }
-
- return true;
- }
- } geo2dFindNearCmd;
-
- class ApplyOpsCmd : public PublicGridCommand {
- public:
- ApplyOpsCmd() : PublicGridCommand( "applyOps" ) {}
- virtual void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) {
- // applyOps can do pretty much anything, so require all privileges.
- RoleGraph::generateUniversalPrivileges(out);
- }
- virtual bool run(OperationContext* txn, const string& dbName , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result) {
- errmsg = "applyOps not allowed through mongos";
- return false;
- }
- } applyOpsCmd;
-
-
- class CompactCmd : public PublicGridCommand {
- public:
- CompactCmd() : PublicGridCommand( "compact" ) {}
- virtual void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) {
- ActionSet actions;
- actions.addAction(ActionType::compact);
- out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
- }
- virtual bool run(OperationContext* txn,
- const string& dbName,
- BSONObj& cmdObj,
- int,
- string& errmsg,
- BSONObjBuilder& result) {
- errmsg = "compact not allowed through mongos";
- return false;
- }
- } compactCmd;
-
- class EvalCmd : public PublicGridCommand {
- public:
- EvalCmd() : PublicGridCommand( "eval", "$eval" ) {}
- virtual void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) {
- // $eval can do pretty much anything, so require all privileges.
- RoleGraph::generateUniversalPrivileges(out);
- }
- virtual bool run(OperationContext* txn,
- const string& dbName,
- BSONObj& cmdObj,
- int,
- string& errmsg,
- BSONObjBuilder& result) {
-
- RARELY {
- warning() << "the eval command is deprecated" << startupWarningsLog;
- }
-
- // $eval isn't allowed to access sharded collections, but we need to leave the
- // shard to detect that.
- auto status = grid.catalogCache()->getDatabase(dbName);
- if (!status.isOK()) {
- return appendCommandStatus(result, status.getStatus());
- }
-
- shared_ptr<DBConfig> conf = status.getValue();
- return passthrough( conf , cmdObj , result );
- }
- } evalCmd;
-
- class CmdListCollections : public PublicGridCommand {
- public:
- CmdListCollections() : PublicGridCommand( "listCollections" ) {}
-
- virtual Status checkAuthForCommand(ClientBasic* client,
- const std::string& dbname,
- const BSONObj& cmdObj) {
- AuthorizationSession* authzSession = AuthorizationSession::get(client);
-
- // Check for the listCollections ActionType on the database
- // or find on system.namespaces for pre 3.0 systems.
- if (authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(dbname),
- ActionType::listCollections) ||
- authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forExactNamespace(
- NamespaceString(dbname, "system.namespaces")),
- ActionType::find)) {
- return Status::OK();
- }
-
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to create users on db: " <<
- dbname);
- }
-
- bool run(OperationContext* txn,
+ errmsg = "compact not allowed through mongos";
+ return false;
+ }
+} compactCmd;
+
+class EvalCmd : public PublicGridCommand {
+public:
+ EvalCmd() : PublicGridCommand("eval", "$eval") {}
+ virtual void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) {
+ // $eval can do pretty much anything, so require all privileges.
+ RoleGraph::generateUniversalPrivileges(out);
+ }
+ virtual bool run(OperationContext* txn,
const string& dbName,
BSONObj& cmdObj,
int,
string& errmsg,
BSONObjBuilder& result) {
-
- auto status = grid.catalogCache()->getDatabase(dbName);
- if (!status.isOK()) {
- return appendEmptyResultSet(result,
- status.getStatus(),
- dbName + ".$cmd.listCollections");
- }
-
- shared_ptr<DBConfig> conf = status.getValue();
- bool retval = passthrough( conf, cmdObj, result );
-
- const auto shard = grid.shardRegistry()->getShard(conf->getPrimaryId());
- Status storeCursorStatus = storePossibleCursor(shard->getConnString().toString(),
- result.asTempObj());
- if (!storeCursorStatus.isOK()) {
- return appendCommandStatus(result, storeCursorStatus);
- }
-
- return retval;
- }
- } cmdListCollections;
-
- class CmdListIndexes : public PublicGridCommand {
- public:
- CmdListIndexes() : PublicGridCommand( "listIndexes" ) {}
- virtual void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) {
- string ns = parseNs( dbname, cmdObj );
- ActionSet actions;
- actions.addAction(ActionType::listIndexes);
- out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
- }
-
- bool run(OperationContext* txn,
- const string& dbName,
+ RARELY {
+ warning() << "the eval command is deprecated" << startupWarningsLog;
+ }
+
+ // $eval isn't allowed to access sharded collections, but we need to leave the
+ // shard to detect that.
+ auto status = grid.catalogCache()->getDatabase(dbName);
+ if (!status.isOK()) {
+ return appendCommandStatus(result, status.getStatus());
+ }
+
+ shared_ptr<DBConfig> conf = status.getValue();
+ return passthrough(conf, cmdObj, result);
+ }
+} evalCmd;
+
+class CmdListCollections : public PublicGridCommand {
+public:
+ CmdListCollections() : PublicGridCommand("listCollections") {}
+
+ virtual Status checkAuthForCommand(ClientBasic* client,
+ const std::string& dbname,
+ const BSONObj& cmdObj) {
+ AuthorizationSession* authzSession = AuthorizationSession::get(client);
+
+ // Check for the listCollections ActionType on the database
+ // or find on system.namespaces for pre 3.0 systems.
+ if (authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(dbname),
+ ActionType::listCollections) ||
+ authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forExactNamespace(NamespaceString(dbname, "system.namespaces")),
+ ActionType::find)) {
+ return Status::OK();
+ }
+
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "Not authorized to create users on db: " << dbname);
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbName,
+ BSONObj& cmdObj,
+ int,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ auto status = grid.catalogCache()->getDatabase(dbName);
+ if (!status.isOK()) {
+ return appendEmptyResultSet(
+ result, status.getStatus(), dbName + ".$cmd.listCollections");
+ }
+
+ shared_ptr<DBConfig> conf = status.getValue();
+ bool retval = passthrough(conf, cmdObj, result);
+
+ const auto shard = grid.shardRegistry()->getShard(conf->getPrimaryId());
+ Status storeCursorStatus =
+ storePossibleCursor(shard->getConnString().toString(), result.asTempObj());
+ if (!storeCursorStatus.isOK()) {
+ return appendCommandStatus(result, storeCursorStatus);
+ }
+
+ return retval;
+ }
+} cmdListCollections;
+
+class CmdListIndexes : public PublicGridCommand {
+public:
+ CmdListIndexes() : PublicGridCommand("listIndexes") {}
+ virtual void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) {
+ string ns = parseNs(dbname, cmdObj);
+ ActionSet actions;
+ actions.addAction(ActionType::listIndexes);
+ out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
+ }
+
+ bool run(OperationContext* txn,
+ const string& dbName,
+ BSONObj& cmdObj,
+ int options,
+ string& errmsg,
+ BSONObjBuilder& result) {
+ auto conf = uassertStatusOK(grid.catalogCache()->getDatabase(dbName));
+ bool retval = passthrough(conf, cmdObj, result);
+
+ const auto shard = grid.shardRegistry()->getShard(conf->getPrimaryId());
+ Status storeCursorStatus =
+ storePossibleCursor(shard->getConnString().toString(), result.asTempObj());
+ if (!storeCursorStatus.isOK()) {
+ return appendCommandStatus(result, storeCursorStatus);
+ }
+
+ return retval;
+ }
+
+} cmdListIndexes;
+
+class AvailableQueryOptions : public Command {
+public:
+ AvailableQueryOptions() : Command("availableQueryOptions", false, "availablequeryoptions") {}
+
+ 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();
+ }
+
+
+ virtual bool run(OperationContext* txn,
+ const string& dbname,
BSONObj& cmdObj,
- int options,
+ int,
string& errmsg,
BSONObjBuilder& result) {
+ result << "options" << QueryOption_AllSupportedForSharding;
+ return true;
+ }
+} availableQueryOptionsCmd;
- auto conf = uassertStatusOK(grid.catalogCache()->getDatabase(dbName));
- bool retval = passthrough( conf, cmdObj, result );
-
- const auto shard = grid.shardRegistry()->getShard(conf->getPrimaryId());
- Status storeCursorStatus = storePossibleCursor(shard->getConnString().toString(),
- result.asTempObj());
- if (!storeCursorStatus.isOK()) {
- return appendCommandStatus(result, storeCursorStatus);
- }
-
- return retval;
- }
-
- } cmdListIndexes;
-
- class AvailableQueryOptions : public Command {
- public:
- AvailableQueryOptions(): Command("availableQueryOptions",
- false ,
- "availablequeryoptions") {
- }
-
- 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();
- }
-
-
- virtual bool run(OperationContext* txn,
- const string& dbname,
- BSONObj& cmdObj,
- int,
- string& errmsg,
- BSONObjBuilder& result) {
- result << "options" << QueryOption_AllSupportedForSharding;
- return true;
- }
- } availableQueryOptionsCmd;
-
- } // namespace pub_grid_cmds
-
-} // namespace mongo
+} // namespace pub_grid_cmds
+} // namespace mongo