summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2021-01-13 23:09:10 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-01-14 22:59:42 +0000
commit466870c47fce1e7c4f11453abc4c234835d35327 (patch)
tree03b92b22a6d6702f010ac718f57e4811396537cc
parentf3f6b1c0a0c540f7037e5c44248b14c7a9e2e4f1 (diff)
downloadmongo-466870c47fce1e7c4f11453abc4c234835d35327.tar.gz
SERVER-52554 Convert ListDatabases to TypedCommand
-rw-r--r--src/mongo/db/commands/list_databases.cpp220
-rw-r--r--src/mongo/db/commands/list_databases.idl1
-rw-r--r--src/mongo/s/commands/cluster_list_databases_cmd.cpp255
3 files changed, 223 insertions, 253 deletions
diff --git a/src/mongo/db/commands/list_databases.cpp b/src/mongo/db/commands/list_databases.cpp
index fa70ed6ac96..e328ebc5237 100644
--- a/src/mongo/db/commands/list_databases.cpp
+++ b/src/mongo/db/commands/list_databases.cpp
@@ -43,29 +43,18 @@
#include "mongo/db/storage/storage_engine.h"
namespace mongo {
+
+// XXX: remove and put into storage api
+std::intmax_t dbSize(const std::string& database);
+
namespace {
-static const StringData kFilterField{"filter"};
-static const StringData kNameField{"name"};
-static const StringData kNameOnlyField{"nameOnly"};
// Failpoint which causes to hang "listDatabases" cmd after acquiring global lock in IS mode.
MONGO_FAIL_POINT_DEFINE(hangBeforeListDatabases);
-} // namespace
-
-using std::set;
-using std::string;
-using std::stringstream;
-using std::vector;
-
-// XXX: remove and put into storage api
-intmax_t dbSize(const string& database);
-class CmdListDatabases : public BasicCommand {
+constexpr auto kName = "name"_sd;
+class CmdListDatabases final : public ListDatabasesCmdVersion1Gen<CmdListDatabases> {
public:
- const std::set<std::string>& apiVersions() const {
- return kApiVersions1;
- }
-
AllowedOnSecondary secondaryAllowed(ServiceContext*) const final {
return AllowedOnSecondary::kOptIn;
}
@@ -75,123 +64,122 @@ public:
bool maintenanceOk() const final {
return false;
}
- bool supportsWriteConcern(const BSONObj& cmd) const final {
- return false;
- }
std::string help() const final {
return "{ listDatabases:1, [filter: <filterObject>] [, nameOnly: true ] }\n"
"list databases on this server";
}
- /* listDatabases is always authorized,
- * however the results returned will be redacted
- * based on read privileges if auth is enabled
- * and the current user does not have listDatabases permisison.
- */
- Status checkAuthForCommand(Client* client,
- const std::string& dbname,
- const BSONObj& cmdObj) const final {
- return Status::OK();
- }
-
- CmdListDatabases() : BasicCommand("listDatabases") {}
-
- bool run(OperationContext* opCtx,
- const string& dbname,
- const BSONObj& cmdObj,
- BSONObjBuilder& result) final {
- CommandHelpers::handleMarkKillOnClientDisconnect(opCtx);
- IDLParserErrorContext ctx("listDatabases");
- auto cmd = ListDatabases::parse(ctx, cmdObj);
- auto* as = AuthorizationSession::get(opCtx->getClient());
-
- // {nameOnly: bool} - default false.
- const bool nameOnly = cmd.getNameOnly();
-
- // {authorizedDatabases: bool} - Dynamic default based on permissions.
- const bool authorizedDatabases = ([as](const boost::optional<bool>& authDB) {
- const bool mayListAllDatabases = as->isAuthorizedForActionsOnResource(
- ResourcePattern::forClusterResource(), ActionType::listDatabases);
-
- if (authDB) {
- uassert(ErrorCodes::Unauthorized,
- "Insufficient permissions to list all databases",
- authDB.get() || mayListAllDatabases);
- return authDB.get();
- }
+ class Invocation final : public InvocationBaseGen {
+ public:
+ using InvocationBaseGen::InvocationBaseGen;
- // By default, list all databases if we can, otherwise
- // only those we're allowed to find on.
- return !mayListAllDatabases;
- })(cmd.getAuthorizedDatabases());
-
- // {filter: matchExpression}.
- std::unique_ptr<MatchExpression> filter;
- if (auto filterObj = cmd.getFilter()) {
- // The collator is null because database metadata objects are compared using simple
- // binary comparison.
- auto expCtx = make_intrusive<ExpressionContext>(
- opCtx, std::unique_ptr<CollatorInterface>(nullptr), NamespaceString(dbname));
- auto matcher =
- uassertStatusOK(MatchExpressionParser::parse(filterObj.get(), std::move(expCtx)));
- filter = std::move(matcher);
+ bool supportsWriteConcern() const final {
+ return false;
}
- vector<string> dbNames;
- StorageEngine* storageEngine = getGlobalServiceContext()->getStorageEngine();
- {
- Lock::GlobalLock lk(opCtx, MODE_IS);
- CurOpFailpointHelpers::waitWhileFailPointEnabled(
- &hangBeforeListDatabases, opCtx, "hangBeforeListDatabases", []() {});
- dbNames = storageEngine->listDatabases();
+ void doCheckAuthorization(OperationContext*) const final {}
+
+ NamespaceString ns() const final {
+ return NamespaceString(request().getDbName(), "");
}
- vector<ListDatabasesReplyItem> items;
+ ListDatabasesReply typedRun(OperationContext* opCtx) final {
+ CommandHelpers::handleMarkKillOnClientDisconnect(opCtx);
+ auto* as = AuthorizationSession::get(opCtx->getClient());
+ auto cmd = request();
+
+ // {nameOnly: bool} - default false.
+ const bool nameOnly = cmd.getNameOnly();
+
+ // {authorizedDatabases: bool} - Dynamic default based on permissions.
+ const bool authorizedDatabases = ([as](const boost::optional<bool>& authDB) {
+ const bool mayListAllDatabases = as->isAuthorizedForActionsOnResource(
+ ResourcePattern::forClusterResource(), ActionType::listDatabases);
+
+ if (authDB) {
+ uassert(ErrorCodes::Unauthorized,
+ "Insufficient permissions to list all databases",
+ authDB.get() || mayListAllDatabases);
+ return authDB.get();
+ }
+
+ // By default, list all databases if we can, otherwise
+ // only those we're allowed to find on.
+ return !mayListAllDatabases;
+ })(cmd.getAuthorizedDatabases());
+
+ // {filter: matchExpression}.
+ std::unique_ptr<MatchExpression> filter;
+ if (auto filterObj = cmd.getFilter()) {
+ // The collator is null because database metadata objects are compared using simple
+ // binary comparison.
+ auto expCtx = make_intrusive<ExpressionContext>(
+ opCtx, std::unique_ptr<CollatorInterface>(nullptr), ns());
+ auto matcher = uassertStatusOK(
+ MatchExpressionParser::parse(filterObj.get(), std::move(expCtx)));
+ filter = std::move(matcher);
+ }
- const bool filterNameOnly = filter &&
- filter->getCategory() == MatchExpression::MatchCategory::kLeaf &&
- filter->path() == kNameField;
- long long totalSize = 0;
- for (const auto& itemName : dbNames) {
- if (authorizedDatabases && !as->isAuthorizedForAnyActionOnAnyResourceInDB(itemName)) {
- // We don't have listDatabases on the cluser or find on this database.
- continue;
+ std::vector<std::string> dbNames;
+ StorageEngine* storageEngine = getGlobalServiceContext()->getStorageEngine();
+ {
+ Lock::GlobalLock lk(opCtx, MODE_IS);
+ CurOpFailpointHelpers::waitWhileFailPointEnabled(
+ &hangBeforeListDatabases, opCtx, "hangBeforeListDatabases", []() {});
+ dbNames = storageEngine->listDatabases();
}
- ListDatabasesReplyItem item(itemName);
+ std::vector<ListDatabasesReplyItem> items;
- long long size = 0;
- if (!nameOnly) {
- // Filtering on name only should not require taking locks on filtered-out names.
- if (filterNameOnly && !filter->matchesBSON(item.toBSON()))
+ const bool filterNameOnly = filter &&
+ filter->getCategory() == MatchExpression::MatchCategory::kLeaf &&
+ filter->path() == kName;
+ long long totalSize = 0;
+ for (const auto& itemName : dbNames) {
+ if (authorizedDatabases &&
+ !as->isAuthorizedForAnyActionOnAnyResourceInDB(itemName)) {
+ // We don't have listDatabases on the cluser or find on this database.
continue;
-
- AutoGetDb autoDb(opCtx, itemName, MODE_IS);
- Database* const db = autoDb.getDb();
- if (!db)
- continue;
-
- writeConflictRetry(opCtx, "sizeOnDisk", itemName, [&] {
- size = storageEngine->sizeOnDiskForDb(opCtx, itemName);
- });
- item.setSizeOnDisk(size);
- item.setEmpty(
- CollectionCatalog::get(opCtx)->getAllCollectionUUIDsFromDb(itemName).empty());
+ }
+
+ ListDatabasesReplyItem item(itemName);
+
+ long long size = 0;
+ if (!nameOnly) {
+ // Filtering on name only should not require taking locks on filtered-out names.
+ if (filterNameOnly && !filter->matchesBSON(item.toBSON())) {
+ continue;
+ }
+
+ AutoGetDb autoDb(opCtx, itemName, MODE_IS);
+ auto* const db = autoDb.getDb();
+ if (!db) {
+ continue;
+ }
+
+ writeConflictRetry(opCtx, "sizeOnDisk", itemName, [&] {
+ size = storageEngine->sizeOnDiskForDb(opCtx, itemName);
+ });
+ item.setSizeOnDisk(size);
+ item.setEmpty(CollectionCatalog::get(opCtx)
+ ->getAllCollectionUUIDsFromDb(itemName)
+ .empty());
+ }
+ if (!filter || filter->matchesBSON(item.toBSON())) {
+ totalSize += size;
+ items.push_back(std::move(item));
+ }
}
- if (!filter || filter->matchesBSON(item.toBSON())) {
- totalSize += size;
- items.push_back(std::move(item));
+
+ ListDatabasesReply reply(items);
+ if (!nameOnly) {
+ reply.setTotalSize(totalSize);
+ reply.setTotalSizeMb(totalSize / (1024 * 1024));
}
- }
- ListDatabasesReply reply(items);
- if (!nameOnly) {
- reply.setTotalSize(totalSize);
- reply.setTotalSizeMb(totalSize / (1024 * 1024));
+ return reply;
}
-
- reply.serialize(&result);
- return true;
- }
+ };
} cmdListDatabases;
+} // namespace
} // namespace mongo
diff --git a/src/mongo/db/commands/list_databases.idl b/src/mongo/db/commands/list_databases.idl
index 8342078de30..89bf17fc689 100644
--- a/src/mongo/db/commands/list_databases.idl
+++ b/src/mongo/db/commands/list_databases.idl
@@ -62,6 +62,7 @@ commands:
listDatabases:
description: "listDatabases Command"
command_name: "listDatabases"
+ cpp_name: ListDatabasesCommand
namespace: ignored
api_version: "1"
strict: true
diff --git a/src/mongo/s/commands/cluster_list_databases_cmd.cpp b/src/mongo/s/commands/cluster_list_databases_cmd.cpp
index d4a9cec8b9e..21ea0a14f2b 100644
--- a/src/mongo/s/commands/cluster_list_databases_cmd.cpp
+++ b/src/mongo/s/commands/cluster_list_databases_cmd.cpp
@@ -47,183 +47,164 @@
namespace mongo {
namespace {
-class ListDatabasesCmd : public BasicCommand {
+class ListDatabasesCmd final : public ListDatabasesCmdVersion1Gen<ListDatabasesCmd> {
public:
- ListDatabasesCmd() : BasicCommand("listDatabases", "listdatabases") {}
-
- const std::set<std::string>& apiVersions() const {
- return kApiVersions1;
- }
-
- AllowedOnSecondary secondaryAllowed(ServiceContext*) const override {
+ AllowedOnSecondary secondaryAllowed(ServiceContext*) const final {
return AllowedOnSecondary::kAlways;
}
- bool maintenanceOk() const override {
+ bool maintenanceOk() const final {
return false;
}
- bool adminOnly() const override {
+ bool adminOnly() const final {
return true;
}
+ class Invocation final : public InvocationBaseGen {
+ public:
+ using InvocationBaseGen::InvocationBaseGen;
- bool supportsWriteConcern(const BSONObj& cmd) const override {
- return false;
- }
+ bool supportsWriteConcern() const final {
+ return false;
+ }
- std::string help() const override {
- return "list databases in a cluster";
- }
+ void doCheckAuthorization(OperationContext*) const final {}
- /* listDatabases is always authorized,
- * however the results returned will be redacted
- * based on read privileges if auth is enabled
- * and the current user does not have listDatabases permisison.
- */
- Status checkAuthForCommand(Client* client,
- const std::string& dbname,
- const BSONObj& cmdObj) const override {
- return Status::OK();
- }
+ NamespaceString ns() const final {
+ return NamespaceString(request().getDbName(), "");
+ }
+ ListDatabasesReply typedRun(OperationContext* opCtx) final {
+ CommandHelpers::handleMarkKillOnClientDisconnect(opCtx);
+ auto* as = AuthorizationSession::get(opCtx->getClient());
+ auto cmd = request();
+
+ // { nameOnly: bool } - Default false.
+ const bool nameOnly = cmd.getNameOnly();
+
+ // { authorizedDatabases: bool } - Dynamic default based on perms.
+ const bool authorizedDatabases = ([as](const boost::optional<bool>& authDB) {
+ const bool mayListAllDatabases = as->isAuthorizedForActionsOnResource(
+ ResourcePattern::forClusterResource(), ActionType::listDatabases);
+ if (authDB) {
+ uassert(ErrorCodes::Unauthorized,
+ "Insufficient permissions to list all databases",
+ authDB.get() || mayListAllDatabases);
+ return authDB.get();
+ }
- bool run(OperationContext* opCtx,
- const std::string& dbname_unused,
- const BSONObj& cmdObj,
- BSONObjBuilder& result) override {
- CommandHelpers::handleMarkKillOnClientDisconnect(opCtx);
-
- IDLParserErrorContext ctx("listDatabases");
- auto cmd = ListDatabases::parse(ctx, cmdObj);
- auto* as = AuthorizationSession::get(opCtx->getClient());
-
- // { nameOnly: bool } - Default false.
- const bool nameOnly = cmd.getNameOnly();
-
- // { authorizedDatabases: bool } - Dynamic default based on perms.
- const bool authorizedDatabases = ([as](const boost::optional<bool>& authDB) {
- const bool mayListAllDatabases = as->isAuthorizedForActionsOnResource(
- ResourcePattern::forClusterResource(), ActionType::listDatabases);
- if (authDB) {
- uassert(ErrorCodes::Unauthorized,
- "Insufficient permissions to list all databases",
- authDB.get() || mayListAllDatabases);
- return authDB.get();
- }
+ // By default, list all databases if we can, otherwise
+ // only those we're allowed to find on.
+ return !mayListAllDatabases;
+ })(cmd.getAuthorizedDatabases());
- // By default, list all databases if we can, otherwise
- // only those we're allowed to find on.
- return !mayListAllDatabases;
- })(cmd.getAuthorizedDatabases());
+ auto const shardRegistry = Grid::get(opCtx)->shardRegistry();
- auto const shardRegistry = Grid::get(opCtx)->shardRegistry();
+ std::map<std::string, long long> sizes;
+ std::map<std::string, std::unique_ptr<BSONObjBuilder>> dbShardInfo;
- std::map<std::string, long long> sizes;
- std::map<std::string, std::unique_ptr<BSONObjBuilder>> dbShardInfo;
+ auto shardIds = shardRegistry->getAllShardIdsNoReload();
+ shardIds.emplace_back(ShardId::kConfigServerId);
- auto shardIds = shardRegistry->getAllShardIdsNoReload();
- shardIds.emplace_back(ShardId::kConfigServerId);
+ // { filter: matchExpression }.
+ auto filteredCmd = applyReadWriteConcern(
+ opCtx, this, CommandHelpers::filterCommandRequestForPassthrough(cmd.toBSON({})));
- // { filter: matchExpression }.
- auto filteredCmd = applyReadWriteConcern(
- opCtx, this, CommandHelpers::filterCommandRequestForPassthrough(cmdObj));
+ for (const ShardId& shardId : shardIds) {
+ const auto shardStatus = shardRegistry->getShard(opCtx, shardId);
+ if (!shardStatus.isOK()) {
+ continue;
+ }
+ const auto s = shardStatus.getValue();
+
+ auto response = uassertStatusOK(
+ s->runCommandWithFixedRetryAttempts(opCtx,
+ ReadPreferenceSetting::get(opCtx),
+ "admin",
+ filteredCmd,
+ Shard::RetryPolicy::kIdempotent));
+ uassertStatusOK(response.commandStatus);
+ BSONObj x = std::move(response.response);
+
+ BSONObjIterator j(x["databases"].Obj());
+ while (j.more()) {
+ BSONObj dbObj = j.next().Obj();
+
+ const auto name = dbObj["name"].String();
+
+ // If this is the admin db, only collect its stats from the config servers.
+ if (name == "admin" && !s->isConfig()) {
+ continue;
+ }
- for (const ShardId& shardId : shardIds) {
- const auto shardStatus = shardRegistry->getShard(opCtx, shardId);
- if (!shardStatus.isOK()) {
- continue;
- }
- const auto s = shardStatus.getValue();
+ // We don't collect config server info for dbs other than "admin" and "config".
+ if (s->isConfig() && name != "config" && name != "admin") {
+ continue;
+ }
+
+ const long long size = dbObj["sizeOnDisk"].numberLong();
+
+ long long& sizeSumForDbAcrossShards = sizes[name];
+ if (size == 1) {
+ if (sizeSumForDbAcrossShards <= 1) {
+ sizeSumForDbAcrossShards = 1;
+ }
+ } else {
+ sizeSumForDbAcrossShards += size;
+ }
- auto response = uassertStatusOK(
- s->runCommandWithFixedRetryAttempts(opCtx,
- ReadPreferenceSetting::get(opCtx),
- "admin",
- filteredCmd,
- Shard::RetryPolicy::kIdempotent));
- uassertStatusOK(response.commandStatus);
- BSONObj x = std::move(response.response);
+ auto& bb = dbShardInfo[name];
+ if (!bb) {
+ bb.reset(new BSONObjBuilder());
+ }
- BSONObjIterator j(x["databases"].Obj());
- while (j.more()) {
- BSONObj dbObj = j.next().Obj();
+ bb->append(s->getId().toString(), size);
+ }
+ }
- const auto name = dbObj["name"].String();
+ // Now that we have aggregated results for all the shards, convert to a response,
+ // and compute total sizes.
+ long long totalSize = 0;
+ std::vector<ListDatabasesReplyItem> items;
+ for (const auto& sizeEntry : sizes) {
+ const auto& name = sizeEntry.first;
+ const long long size = sizeEntry.second;
- // If this is the admin db, only collect its stats from the config servers.
- if (name == "admin" && !s->isConfig()) {
+ // Skip the local database, since all shards have their own independent local
+ if (name == NamespaceString::kLocalDb)
continue;
- }
- // We don't collect config server info for dbs other than "admin" and "config".
- if (s->isConfig() && name != "config" && name != "admin") {
+ if (authorizedDatabases && !as->isAuthorizedForAnyActionOnAnyResourceInDB(name)) {
+ // We don't have listDatabases on the cluser or find on this database.
continue;
}
- const long long size = dbObj["sizeOnDisk"].numberLong();
+ ListDatabasesReplyItem item(name);
+ if (!nameOnly) {
+ item.setSizeOnDisk(size);
+ item.setEmpty(size == 1);
+ item.setShards(dbShardInfo[name]->obj());
- long long& sizeSumForDbAcrossShards = sizes[name];
- if (size == 1) {
- if (sizeSumForDbAcrossShards <= 1) {
- sizeSumForDbAcrossShards = 1;
- }
- } else {
- sizeSumForDbAcrossShards += size;
- }
+ uassert(ErrorCodes::BadValue,
+ str::stream() << "Found negative 'sizeOnDisk' in: " << name,
+ size >= 0);
- auto& bb = dbShardInfo[name];
- if (!bb) {
- bb.reset(new BSONObjBuilder());
+ totalSize += size;
}
- bb->append(s->getId().toString(), size);
- }
- }
-
- // Now that we have aggregated results for all the shards, convert to a response,
- // and compute total sizes.
- long long totalSize = 0;
- std::vector<ListDatabasesReplyItem> items;
- for (const auto& sizeEntry : sizes) {
- const auto& name = sizeEntry.first;
- const long long size = sizeEntry.second;
-
- // Skip the local database, since all shards have their own independent local
- if (name == NamespaceString::kLocalDb)
- continue;
-
- if (authorizedDatabases && !as->isAuthorizedForAnyActionOnAnyResourceInDB(name)) {
- // We don't have listDatabases on the cluser or find on this database.
- continue;
+ items.push_back(std::move(item));
}
- ListDatabasesReplyItem item(name);
+ ListDatabasesReply reply(items);
if (!nameOnly) {
- item.setSizeOnDisk(size);
- item.setEmpty(size == 1);
- item.setShards(dbShardInfo[name]->obj());
-
- uassert(ErrorCodes::BadValue,
- str::stream() << "Found negative 'sizeOnDisk' in: " << name,
- size >= 0);
-
- totalSize += size;
+ reply.setTotalSize(totalSize);
+ reply.setTotalSizeMb(totalSize / (1024 * 1024));
}
- items.push_back(std::move(item));
- }
-
- ListDatabasesReply reply(items);
- if (!nameOnly) {
- reply.setTotalSize(totalSize);
- reply.setTotalSizeMb(totalSize / (1024 * 1024));
+ return reply;
}
-
- reply.serialize(&result);
- return true;
- }
-
+ };
} listDatabasesCmd;
} // namespace