summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMoustafa Maher <m.maher@10gen.com>2021-02-03 18:04:52 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-02-10 18:57:09 +0000
commitbcd2d887611fc144f3fce31d9fc7d01628b99e61 (patch)
treed551245d50988b05d568377f6b3b1e499e9511b9 /src
parent1003c9a7eaba7648bbb98c840848158c38c4a77e (diff)
downloadmongo-bcd2d887611fc144f3fce31d9fc7d01628b99e61.tar.gz
SERVER-53146: Specify input to explain command in IDL
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/SConscript4
-rw-r--r--src/mongo/db/commands/explain_cmd.cpp20
-rw-r--r--src/mongo/db/explain.idl67
-rw-r--r--src/mongo/db/explain_test.cpp120
-rw-r--r--src/mongo/db/query/SConscript2
-rw-r--r--src/mongo/db/query/explain_options.cpp58
-rw-r--r--src/mongo/db/query/explain_options.h31
-rw-r--r--src/mongo/db/query/explain_options_test.cpp109
-rw-r--r--src/mongo/db/query/explain_verbosity.idl50
-rw-r--r--src/mongo/s/commands/cluster_explain_cmd.cpp16
10 files changed, 264 insertions, 213 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index 1f472114232..9387bcf0fe6 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -578,6 +578,7 @@ env.Library(
'drop.idl',
'drop_database.idl',
'drop_indexes.idl',
+ "explain.idl",
'list_collections.idl',
'list_indexes.idl',
],
@@ -590,6 +591,7 @@ env.Library(
'$BUILD_DIR/mongo/bson/mutable/mutable_bson',
'$BUILD_DIR/mongo/db/catalog/collection_options_idl',
'$BUILD_DIR/mongo/db/commands/create_command',
+ '$BUILD_DIR/mongo/db/query/explain_options',
'$BUILD_DIR/mongo/idl/idl_parser',
'$BUILD_DIR/mongo/rpc/command_status',
'$BUILD_DIR/mongo/rpc/rpc',
@@ -2277,6 +2279,7 @@ if wiredtiger:
'dbdirectclient_test.cpp',
'dbmessage_test.cpp',
'db_raii_test.cpp',
+ "explain_test.cpp",
'field_parser_test.cpp',
'field_ref_set_test.cpp',
'field_ref_test.cpp',
@@ -2328,6 +2331,7 @@ if wiredtiger:
'$BUILD_DIR/mongo/db/catalog/index_build_entry_idl',
'$BUILD_DIR/mongo/db/mongohasher',
'$BUILD_DIR/mongo/db/op_msg_fuzzer_fixture',
+ '$BUILD_DIR/mongo/db/query/explain_options',
'$BUILD_DIR/mongo/db/query/query_test_service_context',
'$BUILD_DIR/mongo/db/storage/wiredtiger/storage_wiredtiger',
'$BUILD_DIR/mongo/executor/async_timer_mock',
diff --git a/src/mongo/db/commands/explain_cmd.cpp b/src/mongo/db/commands/explain_cmd.cpp
index 665ea42bc4b..8f754f6e1d4 100644
--- a/src/mongo/db/commands/explain_cmd.cpp
+++ b/src/mongo/db/commands/explain_cmd.cpp
@@ -31,6 +31,7 @@
#include "mongo/db/command_can_run_here.h"
#include "mongo/db/commands.h"
+#include "mongo/db/explain_gen.h"
#include "mongo/db/query/explain.h"
#include "mongo/util/str.h"
@@ -151,16 +152,15 @@ private:
std::unique_ptr<CommandInvocation> CmdExplain::parse(OperationContext* opCtx,
const OpMsgRequest& request) {
CommandHelpers::uassertNoDocumentSequences(getName(), request);
- std::string dbname = request.getDatabase().toString();
- const BSONObj& cmdObj = request.body;
- uassert(ErrorCodes::FailedToParse,
- "Unrecognized field 'jsonSchema'. This command may be meant for a mongocryptd process.",
- !cmdObj.hasField("jsonSchema"_sd));
- ExplainOptions::Verbosity verbosity = uassertStatusOK(ExplainOptions::parseCmdBSON(cmdObj));
- uassert(ErrorCodes::BadValue,
- "explain command requires a nested object",
- cmdObj.firstElement().type() == Object);
- auto explainedObj = cmdObj.firstElement().Obj();
+
+ // To enforce API versioning
+ auto cmdObj = ExplainCmd::parse(
+ IDLParserErrorContext(ExplainCmd::kCommandName,
+ APIParameters::get(opCtx).getAPIStrict().value_or(false)),
+ request.body);
+ std::string dbname = cmdObj.getDbName().toString();
+ ExplainOptions::Verbosity verbosity = cmdObj.getVerbosity();
+ auto explainedObj = cmdObj.getCommandParameter();
// Extract 'comment' field from the 'explainedObj' only if there is no top-level comment.
auto commentField = explainedObj["comment"];
diff --git a/src/mongo/db/explain.idl b/src/mongo/db/explain.idl
new file mode 100644
index 00000000000..866fa670608
--- /dev/null
+++ b/src/mongo/db/explain.idl
@@ -0,0 +1,67 @@
+# Copyright (C) 2018-present MongoDB, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the Server Side Public License, version 1,
+# as published by MongoDB, Inc.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# Server Side Public License for more details.
+#
+# You should have received a copy of the Server Side Public License
+# along with this program. If not, see
+# <http:#www.mongodb.com/licensing/server-side-public-license>.
+#
+# As a special exception, the copyright holders give permission to link the
+# code of portions of this program with the OpenSSL library under certain
+# conditions as described in each individual source file and distribute
+# linked combinations including the program with the OpenSSL library. You
+# must comply with the Server Side Public License in all respects for
+# all of the code used other than as permitted herein. If you modify file(s)
+# with this exception, you may extend this exception to your version of the
+# file(s), but you are not obligated to do so. If you do not wish to do so,
+# delete this exception statement from your version. If you delete this
+# exception statement from all source files in the program, then also delete
+# it in the license file.
+#
+
+global:
+ cpp_namespace: "mongo"
+
+imports:
+ - "mongo/idl/basic_types.idl"
+ - "mongo/db/query/explain_verbosity.idl"
+
+commands:
+ explain:
+ description: "Parser for the explain command"
+ command_name: explain
+ namespace: type
+ type: object
+ cpp_name: ExplainCmd
+ strict: true
+ api_version: "1"
+ fields:
+ verbosity:
+ description: "The verbosity for explain command."
+ type: Verbosity
+ default : kExecAllPlans
+ # TODO SERVER-48560: we ingest these fields for compatibility with 4.4,
+ # whose mongoS incorrectly adds them to the explain command for an
+ # aggregation instead of adding them into the wrapped aggregate command
+ # itself. Remove these fields when 5.0 becomes last-lts.
+ collation:
+ type: IDLAnyType
+ ignore: true
+ unstable: true
+ use44SortKeys:
+ type: IDLAnyType
+ ignore: true
+ unstable: true
+ useNewUpsert:
+ type: IDLAnyType
+ ignore: true
+ unstable: true
+ # Dummy reply type as we won't use it to parse explain reply.
+ reply_type: OkReply \ No newline at end of file
diff --git a/src/mongo/db/explain_test.cpp b/src/mongo/db/explain_test.cpp
new file mode 100644
index 00000000000..9b7d3df9c06
--- /dev/null
+++ b/src/mongo/db/explain_test.cpp
@@ -0,0 +1,120 @@
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/bson/bsonmisc.h"
+#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/bson/json.h"
+#include "mongo/db/explain_gen.h"
+#include "mongo/db/query/explain_options.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+namespace {
+
+using Verbosity = explain::VerbosityEnum;
+
+TEST(ExplainTest, VerbosityEnumToStringReturnsCorrectValues) {
+ ASSERT_EQ(explain::Verbosity_serializer(Verbosity::kQueryPlanner), "queryPlanner"_sd);
+ ASSERT_EQ(explain::Verbosity_serializer(Verbosity::kExecStats), "executionStats"_sd);
+ ASSERT_EQ(explain::Verbosity_serializer(Verbosity::kExecAllPlans), "allPlansExecution"_sd);
+}
+
+TEST(ExplainTest, ExplainSerializeToBSONCorrectly) {
+ ASSERT_BSONOBJ_EQ(BSON("verbosity"
+ << "queryPlanner"),
+ ExplainOptions::toBSON(Verbosity::kQueryPlanner));
+ ASSERT_BSONOBJ_EQ(BSON("verbosity"
+ << "executionStats"),
+ ExplainOptions::toBSON(Verbosity::kExecStats));
+ ASSERT_BSONOBJ_EQ(BSON("verbosity"
+ << "allPlansExecution"),
+ ExplainOptions::toBSON(Verbosity::kExecAllPlans));
+}
+
+TEST(ExplainTest, CanParseExplainVerbosity) {
+ auto verbosity =
+ ExplainCmd::parse(IDLParserErrorContext("explain"),
+ fromjson("{explain: {}, verbosity: 'queryPlanner', $db: 'dummy'}"))
+ .getVerbosity();
+ ASSERT(verbosity == Verbosity::kQueryPlanner);
+ verbosity =
+ ExplainCmd::parse(IDLParserErrorContext("explain"),
+ fromjson("{explain: {}, verbosity: 'executionStats', $db: 'dummy'}"))
+ .getVerbosity();
+ ASSERT(verbosity == Verbosity::kExecStats);
+ verbosity =
+ ExplainCmd::parse(IDLParserErrorContext("explain"),
+ fromjson("{explain: {}, verbosity: 'allPlansExecution', $db: 'dummy'}"))
+ .getVerbosity();
+ ASSERT(verbosity == Verbosity::kExecAllPlans);
+}
+
+TEST(ExplainTest, ParsingFailsIfVerbosityIsNotAString) {
+ ASSERT_THROWS_CODE(ExplainCmd::parse(IDLParserErrorContext("explain"),
+ fromjson("{explain: {}, verbosity: 1}")),
+ DBException,
+ ErrorCodes::TypeMismatch);
+ ASSERT_THROWS_CODE(ExplainCmd::parse(IDLParserErrorContext("explain"),
+ fromjson("{explain: {}, verbosity: {foo: 'bar'}}")),
+ DBException,
+ ErrorCodes::TypeMismatch);
+}
+
+TEST(ExplainTest, ParsingFailsIfVerbosityStringIsNotRecognized) {
+ ASSERT_THROWS_CODE(ExplainCmd::parse(IDLParserErrorContext("explain"),
+ fromjson("{explain: {}, verbosity: 'badVerbosity'}")),
+ DBException,
+ ErrorCodes::BadValue);
+}
+
+TEST(ExplainTest, ParsingFailsIfFirstElementIsNotAnObject) {
+ ASSERT_THROWS_CODE(ExplainCmd::parse(IDLParserErrorContext("explain"),
+ fromjson("{explain: 1, verbosity: 'queryPlanner'}")),
+ DBException,
+ 40414);
+}
+
+TEST(ExplainTest, ParsingFailsIfUnknownFieldInCommandObject) {
+ ASSERT_THROWS_CODE(
+ ExplainCmd::parse(IDLParserErrorContext("explain"),
+ fromjson("{explain: {}, verbosity: 'queryPlanner', unknownField: true}")),
+ DBException,
+ 40415);
+}
+
+TEST(ExplainTest, CanParseGenericCommandArguments) {
+ ExplainCmd::parse(
+ IDLParserErrorContext("explain"),
+ fromjson("{explain: {}, verbosity: 'queryPlanner', comment: true, $db: 'test'}"));
+}
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript
index 6633ad8c4d4..ed2627eab26 100644
--- a/src/mongo/db/query/SConscript
+++ b/src/mongo/db/query/SConscript
@@ -126,6 +126,7 @@ env.Library(
target="explain_options",
source=[
"explain_options.cpp",
+ "explain_verbosity.idl"
],
LIBDEPS=[
"$BUILD_DIR/mongo/base",
@@ -289,7 +290,6 @@ env.CppUnitTest(
"classic_stage_builder_test.cpp",
"count_command_test.cpp",
"cursor_response_test.cpp",
- "explain_options_test.cpp",
"get_executor_test.cpp",
"getmore_request_test.cpp",
"hint_parser_test.cpp",
diff --git a/src/mongo/db/query/explain_options.cpp b/src/mongo/db/query/explain_options.cpp
index 772681434fc..dc8a8c15257 100644
--- a/src/mongo/db/query/explain_options.cpp
+++ b/src/mongo/db/query/explain_options.cpp
@@ -37,66 +37,10 @@
namespace mongo {
-constexpr StringData ExplainOptions::kCommandName;
constexpr StringData ExplainOptions::kVerbosityName;
-constexpr StringData ExplainOptions::kQueryPlannerVerbosityStr;
-constexpr StringData ExplainOptions::kExecStatsVerbosityStr;
-constexpr StringData ExplainOptions::kAllPlansExecutionVerbosityStr;
StringData ExplainOptions::verbosityString(ExplainOptions::Verbosity verbosity) {
- switch (verbosity) {
- case Verbosity::kQueryPlanner:
- return kQueryPlannerVerbosityStr;
- case Verbosity::kExecStats:
- return kExecStatsVerbosityStr;
- case Verbosity::kExecAllPlans:
- return kAllPlansExecutionVerbosityStr;
- default:
- MONGO_UNREACHABLE;
- }
-}
-
-StatusWith<ExplainOptions::Verbosity> ExplainOptions::parseCmdBSON(const BSONObj& cmdObj) {
- auto verbosity = Verbosity::kExecAllPlans;
- for (auto&& field : cmdObj) {
- auto fieldName = field.fieldNameStringData();
-
- if (fieldName == kCommandName) {
- if (BSONType::Object != field.type()) {
- return Status(ErrorCodes::FailedToParse,
- "explain command requires a nested object");
- }
- } else if (fieldName == kVerbosityName) {
- if (field.type() != BSONType::String) {
- return Status(ErrorCodes::FailedToParse, "explain verbosity must be a string");
- }
-
- auto verbStr = field.valueStringData();
- if (verbStr == kQueryPlannerVerbosityStr) {
- verbosity = Verbosity::kQueryPlanner;
- } else if (verbStr == kExecStatsVerbosityStr) {
- verbosity = Verbosity::kExecStats;
- } else if (verbStr != kAllPlansExecutionVerbosityStr) {
- return Status(ErrorCodes::FailedToParse,
- str::stream()
- << "verbosity string must be one of {'"
- << kQueryPlannerVerbosityStr << "', '" << kExecStatsVerbosityStr
- << "', '" << kAllPlansExecutionVerbosityStr << "'}");
- }
- } else if (fieldName == "collation" || fieldName == "use44SortKeys" ||
- fieldName == "useNewUpsert") {
- // TODO SERVER-48560: we ingest these fields for compatibility with 4.4,
- // whose mongoS incorrectly adds them to the explain command for an
- // aggregation instead of adding them into the wrapped aggregate command
- // itself. Remove this block when 5.0 becomes last-lts.
- continue;
- } else if (!isGenericArgument(fieldName)) {
- return Status(ErrorCodes::InvalidOptions,
- str::stream() << "unexpected field '" << fieldName
- << "' in explain command object");
- }
- }
- return verbosity;
+ return Verbosity_serializer(verbosity);
}
BSONObj ExplainOptions::toBSON(ExplainOptions::Verbosity verbosity) {
diff --git a/src/mongo/db/query/explain_options.h b/src/mongo/db/query/explain_options.h
index d58a22677e5..a2a46ca355e 100644
--- a/src/mongo/db/query/explain_options.h
+++ b/src/mongo/db/query/explain_options.h
@@ -31,6 +31,7 @@
#include "mongo/base/status.h"
#include "mongo/bson/bsonobj.h"
+#include "mongo/db/query/explain_verbosity_gen.h"
namespace mongo {
@@ -44,44 +45,16 @@ public:
* The various supported verbosity levels for explain. The order is significant: the enum values
* are assigned in order of increasing verbosity.
*/
- enum class Verbosity {
- // At all verbosities greater than or equal to QUERY_PLANNER, we display information about
- // the plan selected and alternate rejected plans. Does not include any execution- related
- // info. String alias is "queryPlanner".
- kQueryPlanner = 0,
+ using Verbosity = explain::VerbosityEnum;
- // At all verbosities greater than or equal to EXEC_STATS, we display a section of output
- // containing both overall execution stats, and stats per stage in the execution tree.
- // String alias is "execStats".
- kExecStats = 1,
-
- // At this verbosity level, we generate the execution stats for each rejected plan as well
- // as the winning plan. String alias is "allPlansExecution".
- kExecAllPlans = 2,
- };
-
- static constexpr StringData kCommandName = "explain"_sd;
static constexpr StringData kVerbosityName = "verbosity"_sd;
- // String representations for verbosity levels.
- static constexpr StringData kQueryPlannerVerbosityStr = "queryPlanner"_sd;
- static constexpr StringData kExecStatsVerbosityStr = "executionStats"_sd;
- static constexpr StringData kAllPlansExecutionVerbosityStr = "allPlansExecution"_sd;
-
/**
* Converts an explain verbosity to its string representation.
*/
static StringData verbosityString(ExplainOptions::Verbosity verbosity);
/**
- * Does some basic validation of the command BSON, then extracts and returns the explain
- * verbosity.
- *
- * Returns a non-OK status if parsing fails.
- */
- static StatusWith<ExplainOptions::Verbosity> parseCmdBSON(const BSONObj& cmdObj);
-
- /**
* Converts 'verbosity' to its corresponding representation as a BSONObj containing explain
* command parameters.
*/
diff --git a/src/mongo/db/query/explain_options_test.cpp b/src/mongo/db/query/explain_options_test.cpp
deleted file mode 100644
index 7a4a0e11260..00000000000
--- a/src/mongo/db/query/explain_options_test.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/db/query/explain_options.h"
-
-#include "mongo/bson/bsonmisc.h"
-#include "mongo/bson/bsonobjbuilder.h"
-#include "mongo/bson/json.h"
-#include "mongo/unittest/unittest.h"
-
-namespace mongo {
-namespace {
-
-TEST(ExplainOptionsTest, VerbosityEnumToStringReturnsCorrectValues) {
- ASSERT_EQ(ExplainOptions::verbosityString(ExplainOptions::Verbosity::kQueryPlanner),
- "queryPlanner"_sd);
- ASSERT_EQ(ExplainOptions::verbosityString(ExplainOptions::Verbosity::kExecStats),
- "executionStats"_sd);
- ASSERT_EQ(ExplainOptions::verbosityString(ExplainOptions::Verbosity::kExecAllPlans),
- "allPlansExecution"_sd);
-}
-
-TEST(ExplainOptionsTest, ExplainOptionsSerializeToBSONCorrectly) {
- ASSERT_BSONOBJ_EQ(BSON("verbosity"
- << "queryPlanner"),
- ExplainOptions::toBSON(ExplainOptions::Verbosity::kQueryPlanner));
- ASSERT_BSONOBJ_EQ(BSON("verbosity"
- << "executionStats"),
- ExplainOptions::toBSON(ExplainOptions::Verbosity::kExecStats));
- ASSERT_BSONOBJ_EQ(BSON("verbosity"
- << "allPlansExecution"),
- ExplainOptions::toBSON(ExplainOptions::Verbosity::kExecAllPlans));
-}
-
-TEST(ExplainOptionsTest, CanParseExplainVerbosity) {
- auto verbosity = unittest::assertGet(
- ExplainOptions::parseCmdBSON(fromjson("{explain: {}, verbosity: 'queryPlanner'}")));
- ASSERT(verbosity == ExplainOptions::Verbosity::kQueryPlanner);
- verbosity = unittest::assertGet(
- ExplainOptions::parseCmdBSON(fromjson("{explain: {}, verbosity: 'executionStats'}")));
- ASSERT(verbosity == ExplainOptions::Verbosity::kExecStats);
- verbosity = unittest::assertGet(
- ExplainOptions::parseCmdBSON(fromjson("{explain: {}, verbosity: 'allPlansExecution'}")));
- ASSERT(verbosity == ExplainOptions::Verbosity::kExecAllPlans);
-}
-
-TEST(ExplainOptionsTest, ParsingFailsIfVerbosityIsNotAString) {
- ASSERT_EQ(ExplainOptions::parseCmdBSON(fromjson("{explain: {}, verbosity: 1}")).getStatus(),
- ErrorCodes::FailedToParse);
- ASSERT_EQ(ExplainOptions::parseCmdBSON(fromjson("{explain: {}, verbosity: {foo: 'bar'}}"))
- .getStatus(),
- ErrorCodes::FailedToParse);
-}
-
-TEST(ExplainOptionsTest, ParsingFailsIfVerbosityStringIsNotRecognized) {
- ASSERT_EQ(ExplainOptions::parseCmdBSON(fromjson("{explain: {}, verbosity: 'badVerbosity'}"))
- .getStatus(),
- ErrorCodes::FailedToParse);
-}
-
-TEST(ExplainOptionsTest, ParsingFailsIfFirstElementIsNotAnObject) {
- ASSERT_EQ(ExplainOptions::parseCmdBSON(fromjson("{explain: 1, verbosity: 'queryPlanner'}"))
- .getStatus(),
- ErrorCodes::FailedToParse);
-}
-
-TEST(ExplainOptionsTest, ParsingFailsIfUnknownFieldInCommandObject) {
- ASSERT_EQ(ExplainOptions::parseCmdBSON(
- fromjson("{explain: {}, verbosity: 'queryPlanner', unknownField: true}"))
- .getStatus(),
- ErrorCodes::InvalidOptions);
-}
-
-TEST(ExplainOptionsTest, CanParseGenericCommandArguments) {
- ASSERT_OK(ExplainOptions::parseCmdBSON(
- fromjson("{explain: {}, verbosity: 'queryPlanner', comment: true, $db: 'test'}"))
- .getStatus());
-}
-
-} // namespace
-} // namespace mongo
diff --git a/src/mongo/db/query/explain_verbosity.idl b/src/mongo/db/query/explain_verbosity.idl
new file mode 100644
index 00000000000..5b6114e1e3c
--- /dev/null
+++ b/src/mongo/db/query/explain_verbosity.idl
@@ -0,0 +1,50 @@
+# Copyright (C) 2018-present MongoDB, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the Server Side Public License, version 1,
+# as published by MongoDB, Inc.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# Server Side Public License for more details.
+#
+# You should have received a copy of the Server Side Public License
+# along with this program. If not, see
+# <http:#www.mongodb.com/licensing/server-side-public-license>.
+#
+# As a special exception, the copyright holders give permission to link the
+# code of portions of this program with the OpenSSL library under certain
+# conditions as described in each individual source file and distribute
+# linked combinations including the program with the OpenSSL library. You
+# must comply with the Server Side Public License in all respects for
+# all of the code used other than as permitted herein. If you modify file(s)
+# with this exception, you may extend this exception to your version of the
+# file(s), but you are not obligated to do so. If you do not wish to do so,
+# delete this exception statement from your version. If you delete this
+# exception statement from all source files in the program, then also delete
+# it in the license file.
+#
+
+global:
+ cpp_namespace: "mongo::explain"
+
+enums:
+ Verbosity:
+ description: "The various supported verbosity levels for explain. The order is significant:
+ the enum values are assigned in order of increasing verbosity"
+ type: string
+ values:
+ # At all verbosities greater than or equal to kQueryPlanner, we display information
+ # about the plan selected and alternate rejected plans. Does not include any
+ # execution-related info. String alias is "queryPlanner".
+ kQueryPlanner: "queryPlanner"
+
+ # At all verbosities greater than or equal to kExecStats, we display a section of output
+ # containing both overall execution stats, and stats per stage in the execution tree.
+ # String alias is "executionStats".
+ kExecStats: "executionStats"
+
+ # At this verbosity level, we generate the execution stats for each rejected plan as
+ # well as the winning plan. String alias is "allPlansExecution".
+ kExecAllPlans: "allPlansExecution" \ No newline at end of file
diff --git a/src/mongo/s/commands/cluster_explain_cmd.cpp b/src/mongo/s/commands/cluster_explain_cmd.cpp
index ce2fb30491c..4a69c0360fa 100644
--- a/src/mongo/s/commands/cluster_explain_cmd.cpp
+++ b/src/mongo/s/commands/cluster_explain_cmd.cpp
@@ -30,6 +30,7 @@
#include "mongo/platform/basic.h"
#include "mongo/db/commands.h"
+#include "mongo/db/explain_gen.h"
#include "mongo/db/query/explain.h"
#include "mongo/idl/command_generic_argument.h"
#include "mongo/s/query/cluster_find.h"
@@ -172,17 +173,18 @@ BSONObj makeExplainedObj(const BSONObj& outerObj, StringData dbName) {
std::unique_ptr<CommandInvocation> ClusterExplainCmd::parse(OperationContext* opCtx,
const OpMsgRequest& request) {
CommandHelpers::uassertNoDocumentSequences(getName(), request);
- std::string dbName = request.getDatabase().toString();
- const BSONObj& cmdObj = request.body;
- uassert(ErrorCodes::FailedToParse,
- "Unrecognized field 'jsonSchema'. This command may be meant for a mongocryptd process.",
- !cmdObj.hasField("jsonSchema"_sd));
- ExplainOptions::Verbosity verbosity = uassertStatusOK(ExplainOptions::parseCmdBSON(cmdObj));
+ // To enforce API versioning
+ auto cmdObj = ExplainCmd::parse(
+ IDLParserErrorContext(ExplainCmd::kCommandName,
+ APIParameters::get(opCtx).getAPIStrict().value_or(false)),
+ request.body);
+ std::string dbName = cmdObj.getDbName().toString();
+ ExplainOptions::Verbosity verbosity = cmdObj.getVerbosity();
// This is the nested command which we are explaining. We need to propagate generic
// arguments into the inner command since it is what is passed to the virtual
// CommandInvocation::explain() method.
- const BSONObj explainedObj = makeExplainedObj(cmdObj, dbName);
+ const BSONObj explainedObj = makeExplainedObj(request.body, dbName);
// Extract 'comment' field from the 'explainedObj' only if there is no top-level comment.
auto commentField = explainedObj["comment"];