summaryrefslogtreecommitdiff
path: root/src/mongo/db/query
diff options
context:
space:
mode:
authorAnton Korshunov <anton.korshunov@mongodb.com>2020-10-29 18:07:06 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-11-04 16:47:25 +0000
commitb9630a4621e919837749c5010f3c8e3429a944a9 (patch)
treeb9a73bacf2e96c6207caa05204bc01154efa40c1 /src/mongo/db/query
parent4666dc59f9aa93e4927bf9835b4bd038488d98f9 (diff)
downloadmongo-b9630a4621e919837749c5010f3c8e3429a944a9.tar.gz
SERVER-50728 Use QuerySolution to generate explain output at queryPlanner verbosity in SBE
Diffstat (limited to 'src/mongo/db/query')
-rw-r--r--src/mongo/db/query/classic_stage_builder.cpp4
-rw-r--r--src/mongo/db/query/get_executor.cpp1
-rw-r--r--src/mongo/db/query/get_executor_test.cpp2
-rw-r--r--src/mongo/db/query/index_bounds_builder_test.cpp1
-rw-r--r--src/mongo/db/query/index_bounds_builder_test.h1
-rw-r--r--src/mongo/db/query/index_entry.h4
-rw-r--r--src/mongo/db/query/index_entry_test.cpp1
-rw-r--r--src/mongo/db/query/plan_cache_indexability_test.cpp10
-rw-r--r--src/mongo/db/query/plan_cache_test.cpp4
-rw-r--r--src/mongo/db/query/plan_explainer.h6
-rw-r--r--src/mongo/db/query/plan_explainer_impl.cpp71
-rw-r--r--src/mongo/db/query/plan_explainer_impl.h16
-rw-r--r--src/mongo/db/query/plan_explainer_sbe.cpp282
-rw-r--r--src/mongo/db/query/planner_analysis_test.cpp1
-rw-r--r--src/mongo/db/query/planner_ixselect_test.cpp2
-rw-r--r--src/mongo/db/query/planner_wildcard_helpers.cpp1
-rw-r--r--src/mongo/db/query/query_planner_operator_test.cpp2
-rw-r--r--src/mongo/db/query/query_planner_options_test.cpp1
-rw-r--r--src/mongo/db/query/query_planner_test_fixture.cpp9
-rw-r--r--src/mongo/db/query/query_planner_wildcard_index_test.cpp1
-rw-r--r--src/mongo/db/query/query_settings_test.cpp4
-rw-r--r--src/mongo/db/query/query_solution.h5
-rw-r--r--src/mongo/db/query/query_solution_test.cpp1
-rw-r--r--src/mongo/db/query/sbe_stage_builder.cpp2
24 files changed, 383 insertions, 49 deletions
diff --git a/src/mongo/db/query/classic_stage_builder.cpp b/src/mongo/db/query/classic_stage_builder.cpp
index a6ed5c573ed..39a872951f5 100644
--- a/src/mongo/db/query/classic_stage_builder.cpp
+++ b/src/mongo/db/query/classic_stage_builder.cpp
@@ -128,7 +128,7 @@ std::unique_ptr<PlanStage> ClassicStageBuilder::build(const QuerySolutionNode* r
_ws,
SortPattern{snDefault->pattern, _cq.getExpCtx()},
snDefault->limit,
- internalQueryMaxBlockingSortMemoryUsageBytes.load(),
+ snDefault->maxMemoryUsageBytes,
snDefault->addSortKeyMetadata,
std::move(childStage));
}
@@ -140,7 +140,7 @@ std::unique_ptr<PlanStage> ClassicStageBuilder::build(const QuerySolutionNode* r
_ws,
SortPattern{snSimple->pattern, _cq.getExpCtx()},
snSimple->limit,
- internalQueryMaxBlockingSortMemoryUsageBytes.load(),
+ snSimple->maxMemoryUsageBytes,
snSimple->addSortKeyMetadata,
std::move(childStage));
}
diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp
index b38296378fe..d28dd909032 100644
--- a/src/mongo/db/query/get_executor.cpp
+++ b/src/mongo/db/query/get_executor.cpp
@@ -226,6 +226,7 @@ IndexEntry indexEntryFromIndexCatalogEntry(OperationContext* opCtx,
return {desc->keyPattern(),
desc->getIndexType(),
+ desc->version(),
isMultikey,
// The fixed-size vector of multikey paths stored in the index catalog.
ice.getMultikeyPaths(opCtx),
diff --git a/src/mongo/db/query/get_executor_test.cpp b/src/mongo/db/query/get_executor_test.cpp
index 33f22f6ffed..40da9d86e8e 100644
--- a/src/mongo/db/query/get_executor_test.cpp
+++ b/src/mongo/db/query/get_executor_test.cpp
@@ -134,6 +134,7 @@ void testAllowedIndices(std::vector<IndexEntry> indexes,
IndexEntry buildSimpleIndexEntry(const BSONObj& kp, const std::string& indexName) {
return {kp,
IndexNames::nameToType(IndexNames::findPluginName(kp)),
+ IndexDescriptor::kLatestIndexVersion,
false,
{},
{},
@@ -155,6 +156,7 @@ IndexEntry buildWildcardIndexEntry(const BSONObj& kp,
const std::string& indexName) {
return {kp,
IndexNames::nameToType(IndexNames::findPluginName(kp)),
+ IndexDescriptor::kLatestIndexVersion,
false,
{},
{},
diff --git a/src/mongo/db/query/index_bounds_builder_test.cpp b/src/mongo/db/query/index_bounds_builder_test.cpp
index 2d3ca2d0ac5..4c141b52ee2 100644
--- a/src/mongo/db/query/index_bounds_builder_test.cpp
+++ b/src/mongo/db/query/index_bounds_builder_test.cpp
@@ -961,6 +961,7 @@ TEST_F(IndexBoundsBuilderTest, ExistsTrueSparse) {
IndexEntry testIndex =
IndexEntry(keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
false, // multikey
{},
{},
diff --git a/src/mongo/db/query/index_bounds_builder_test.h b/src/mongo/db/query/index_bounds_builder_test.h
index 1080abe7198..f3abf8c212c 100644
--- a/src/mongo/db/query/index_bounds_builder_test.h
+++ b/src/mongo/db/query/index_bounds_builder_test.h
@@ -55,6 +55,7 @@ public:
IndexEntry buildSimpleIndexEntry(const BSONObj& kp = BSONObj()) {
return {kp,
IndexNames::nameToType(IndexNames::findPluginName(kp)),
+ IndexDescriptor::kLatestIndexVersion,
false,
{},
{},
diff --git a/src/mongo/db/query/index_entry.h b/src/mongo/db/query/index_entry.h
index 487e8c6853d..7c7ba04a532 100644
--- a/src/mongo/db/query/index_entry.h
+++ b/src/mongo/db/query/index_entry.h
@@ -33,6 +33,7 @@
#include <string>
#include "mongo/db/field_ref.h"
+#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/index/multikey_paths.h"
#include "mongo/db/index_names.h"
#include "mongo/db/jsobj.h"
@@ -149,6 +150,7 @@ struct CoreIndexInfo {
struct IndexEntry : CoreIndexInfo {
IndexEntry(const BSONObj& kp,
IndexType type,
+ IndexDescriptor::IndexVersion version,
bool mk,
const MultikeyPaths& mkp,
std::set<FieldRef> multikeyPathSet,
@@ -160,6 +162,7 @@ struct IndexEntry : CoreIndexInfo {
const CollatorInterface* ci,
const WildcardProjection* wildcardProjection)
: CoreIndexInfo(kp, type, sp, std::move(ident), fe, ci, wildcardProjection),
+ version(version),
multikey(mk),
multikeyPaths(mkp),
multikeyPathSet(std::move(multikeyPathSet)),
@@ -224,6 +227,7 @@ struct IndexEntry : CoreIndexInfo {
sizeof(*this);
}
+ IndexDescriptor::IndexVersion version;
bool multikey;
// If non-empty, 'multikeyPaths' is a vector with size equal to the number of elements in the
diff --git a/src/mongo/db/query/index_entry_test.cpp b/src/mongo/db/query/index_entry_test.cpp
index 6f80c6c8670..b52a429833f 100644
--- a/src/mongo/db/query/index_entry_test.cpp
+++ b/src/mongo/db/query/index_entry_test.cpp
@@ -45,6 +45,7 @@ IndexEntry makeIndexEntry(BSONObj keyPattern, MultikeyPaths multiKeyPaths) {
return {keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
multiKey,
multiKeyPaths,
{},
diff --git a/src/mongo/db/query/plan_cache_indexability_test.cpp b/src/mongo/db/query/plan_cache_indexability_test.cpp
index f76a7797e1c..af7677a8cd5 100644
--- a/src/mongo/db/query/plan_cache_indexability_test.cpp
+++ b/src/mongo/db/query/plan_cache_indexability_test.cpp
@@ -71,6 +71,7 @@ auto makeWildcardEntry(BSONObj keyPattern, const MatchExpression* filterExpr = n
WildcardKeyGenerator::createProjectionExecutor(keyPattern, {}));
return std::make_pair(IndexEntry(keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
false, // multikey
{},
{},
@@ -91,6 +92,7 @@ TEST(PlanCacheIndexabilityTest, SparseIndexSimple) {
state.updateDiscriminators(
{IndexEntry(keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
false, // multikey
{},
{},
@@ -131,6 +133,7 @@ TEST(PlanCacheIndexabilityTest, SparseIndexCompound) {
state.updateDiscriminators(
{IndexEntry(keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
false, // multikey
{},
{},
@@ -178,6 +181,7 @@ TEST(PlanCacheIndexabilityTest, PartialIndexSimple) {
state.updateDiscriminators(
{IndexEntry(keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
false, // multikey
{},
{},
@@ -227,6 +231,7 @@ TEST(PlanCacheIndexabilityTest, PartialIndexAnd) {
state.updateDiscriminators(
{IndexEntry(keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
false, // multikey
{},
{},
@@ -289,6 +294,7 @@ TEST(PlanCacheIndexabilityTest, MultiplePartialIndexes) {
state.updateDiscriminators(
{IndexEntry(keyPattern_a,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern_a)),
+ IndexDescriptor::kLatestIndexVersion,
false, // multikey
{},
{},
@@ -301,6 +307,7 @@ TEST(PlanCacheIndexabilityTest, MultiplePartialIndexes) {
nullptr),
IndexEntry(keyPattern_b,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern_b)),
+ IndexDescriptor::kLatestIndexVersion,
false, // multikey
{},
{},
@@ -374,6 +381,7 @@ TEST(PlanCacheIndexabilityTest, IndexNeitherSparseNorPartial) {
state.updateDiscriminators(
{IndexEntry(keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
false, // multikey
{},
{},
@@ -395,6 +403,7 @@ TEST(PlanCacheIndexabilityTest, DiscriminatorForCollationIndicatesWhenCollations
auto keyPattern = BSON("a" << 1);
IndexEntry entry(keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
false, // multikey
{},
{},
@@ -485,6 +494,7 @@ TEST(PlanCacheIndexabilityTest, CompoundIndexCollationDiscriminator) {
state.updateDiscriminators(
{IndexEntry(keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
false, // multikey
{},
{},
diff --git a/src/mongo/db/query/plan_cache_test.cpp b/src/mongo/db/query/plan_cache_test.cpp
index 1535f24f10b..57f673301fc 100644
--- a/src/mongo/db/query/plan_cache_test.cpp
+++ b/src/mongo/db/query/plan_cache_test.cpp
@@ -248,6 +248,7 @@ std::pair<IndexEntry, std::unique_ptr<WildcardProjection>> makeWildcardEntry(BSO
WildcardKeyGenerator::createProjectionExecutor(keyPattern, {}));
return {IndexEntry(keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
false, // multikey
{},
{},
@@ -903,6 +904,7 @@ protected:
params.indices.push_back(
IndexEntry(keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
multikey,
{},
{},
@@ -919,6 +921,7 @@ protected:
params.indices.push_back(
IndexEntry(keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
multikey,
{},
{},
@@ -934,6 +937,7 @@ protected:
void addIndex(BSONObj keyPattern, const std::string& indexName, CollatorInterface* collator) {
IndexEntry entry(keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
false,
{},
{},
diff --git a/src/mongo/db/query/plan_explainer.h b/src/mongo/db/query/plan_explainer.h
index 9201418954a..c060affb083 100644
--- a/src/mongo/db/query/plan_explainer.h
+++ b/src/mongo/db/query/plan_explainer.h
@@ -36,6 +36,12 @@
namespace mongo {
/**
+ * The maximum size of a serialized BSON document that details the plan selected by the query
+ * planner.
+ */
+static constexpr int kMaxExplainStatsBSONSizeMB = 10 * 1024 * 1024;
+
+/**
* This interface defines an API to provide information on the execution plans generated by the
* query planner for a user query in various formats.
*/
diff --git a/src/mongo/db/query/plan_explainer_impl.cpp b/src/mongo/db/query/plan_explainer_impl.cpp
index f55cf9776a8..41a5757f044 100644
--- a/src/mongo/db/query/plan_explainer_impl.cpp
+++ b/src/mongo/db/query/plan_explainer_impl.cpp
@@ -166,40 +166,6 @@ size_t getDocsExamined(StageType type, const SpecificStats* specific) {
}
/**
- * Adds the path-level multikey information to the explain output in a field called "multiKeyPaths".
- * The value associated with the "multiKeyPaths" field is an object with keys equal to those in the
- * index key pattern and values equal to an array of strings corresponding to paths that cause the
- * index to be multikey.
- *
- * For example, with the index {'a.b': 1, 'a.c': 1} where the paths "a" and "a.b" cause the
- * index to be multikey, we'd have {'multiKeyPaths': {'a.b': ['a', 'a.b'], 'a.c': ['a']}}.
- *
- * This function should only be called if the associated index supports path-level multikey
- * tracking.
- */
-void appendMultikeyPaths(const BSONObj& keyPattern,
- const MultikeyPaths& multikeyPaths,
- BSONObjBuilder* bob) {
- BSONObjBuilder subMultikeyPaths(bob->subobjStart("multiKeyPaths"));
-
- size_t i = 0;
- for (const auto& keyElem : keyPattern) {
- const FieldRef path{keyElem.fieldNameStringData()};
-
- BSONArrayBuilder arrMultikeyComponents(
- subMultikeyPaths.subarrayStart(keyElem.fieldNameStringData()));
- for (const auto& multikeyComponent : multikeyPaths[i]) {
- arrMultikeyComponents.append(path.dottedSubstring(0, multikeyComponent + 1));
- }
- arrMultikeyComponents.doneFast();
-
- ++i;
- }
-
- subMultikeyPaths.doneFast();
-}
-
-/**
* Converts the stats tree 'stats' into a corresponding BSON object containing explain information.
*
* Generates the BSON stats at a verbosity specified by 'verbosity'.
@@ -211,10 +177,9 @@ void statsToBSON(const PlanStageStats& stats,
invariant(bob);
invariant(topLevelBob);
- // Stop as soon as the BSON object we're building exceeds 10 MB.
- static const int kMaxStatsBSONSize = 10 * 1024 * 1024;
- if (topLevelBob->len() > kMaxStatsBSONSize) {
- bob->append("warning", "stats tree exceeded 10 MB");
+ // Stop as soon as the BSON object we're building exceeds the limit.
+ if (topLevelBob->len() > kMaxExplainStatsBSONSizeMB) {
+ bob->append("warning", "stats tree exceeded BSON size limit for explain");
return;
}
@@ -337,8 +302,8 @@ void statsToBSON(const PlanStageStats& stats,
bob->append("indexVersion", spec->indexVersion);
bob->append("direction", spec->direction > 0 ? "forward" : "backward");
- if ((topLevelBob->len() + spec->indexBounds.objsize()) > kMaxStatsBSONSize) {
- bob->append("warning", "index bounds omitted due to BSON size limit");
+ if ((topLevelBob->len() + spec->indexBounds.objsize()) > kMaxExplainStatsBSONSizeMB) {
+ bob->append("warning", "index bounds omitted due to BSON size limit for explain");
} else {
bob->append("indexBounds", spec->indexBounds);
}
@@ -403,8 +368,8 @@ void statsToBSON(const PlanStageStats& stats,
bob->append("indexVersion", spec->indexVersion);
bob->append("direction", spec->direction > 0 ? "forward" : "backward");
- if ((topLevelBob->len() + spec->indexBounds.objsize()) > kMaxStatsBSONSize) {
- bob->append("warning", "index bounds omitted due to BSON size limit");
+ if ((topLevelBob->len() + spec->indexBounds.objsize()) > kMaxExplainStatsBSONSizeMB) {
+ bob->append("warning", "index bounds omitted due to BSON size limit for explain");
} else {
bob->append("indexBounds", spec->indexBounds);
}
@@ -549,6 +514,28 @@ PlanSummaryStats collectExecutionStatsSummary(const PlanStageStats* stats) {
}
} // namespace
+void appendMultikeyPaths(const BSONObj& keyPattern,
+ const MultikeyPaths& multikeyPaths,
+ BSONObjBuilder* bob) {
+ BSONObjBuilder subMultikeyPaths(bob->subobjStart("multiKeyPaths"));
+
+ size_t i = 0;
+ for (const auto& keyElem : keyPattern) {
+ const FieldRef path{keyElem.fieldNameStringData()};
+
+ BSONArrayBuilder arrMultikeyComponents(
+ subMultikeyPaths.subarrayStart(keyElem.fieldNameStringData()));
+ for (const auto& multikeyComponent : multikeyPaths[i]) {
+ arrMultikeyComponents.append(path.dottedSubstring(0, multikeyComponent + 1));
+ }
+ arrMultikeyComponents.doneFast();
+
+ ++i;
+ }
+
+ subMultikeyPaths.doneFast();
+}
+
bool PlanExplainerImpl::isMultiPlan() const {
return getStageByType(_root, StageType::STAGE_MULTI_PLAN) != nullptr;
}
diff --git a/src/mongo/db/query/plan_explainer_impl.h b/src/mongo/db/query/plan_explainer_impl.h
index 1886441f819..0d18d19a17a 100644
--- a/src/mongo/db/query/plan_explainer_impl.h
+++ b/src/mongo/db/query/plan_explainer_impl.h
@@ -62,4 +62,20 @@ private:
* found.
*/
PlanStage* getStageByType(PlanStage* root, StageType type);
+
+/**
+ * Adds the path-level multikey information to the explain output in a field called "multiKeyPaths".
+ * The value associated with the "multiKeyPaths" field is an object with keys equal to those in the
+ * index key pattern and values equal to an array of strings corresponding to paths that cause the
+ * index to be multikey.
+ *
+ * For example, with the index {'a.b': 1, 'a.c': 1} where the paths "a" and "a.b" cause the
+ * index to be multikey, we'd have {'multiKeyPaths': {'a.b': ['a', 'a.b'], 'a.c': ['a']}}.
+ *
+ * This function should only be called if the associated index supports path-level multikey
+ * tracking.
+ */
+void appendMultikeyPaths(const BSONObj& keyPattern,
+ const MultikeyPaths& multikeyPaths,
+ BSONObjBuilder* bob);
} // namespace mongo
diff --git a/src/mongo/db/query/plan_explainer_sbe.cpp b/src/mongo/db/query/plan_explainer_sbe.cpp
index c91addc4199..08d5db7b6a9 100644
--- a/src/mongo/db/query/plan_explainer_sbe.cpp
+++ b/src/mongo/db/query/plan_explainer_sbe.cpp
@@ -33,9 +33,246 @@
#include <queue>
+#include "mongo/db/fts/fts_query_impl.h"
#include "mongo/db/keypattern.h"
+#include "mongo/db/query/plan_explainer_impl.h"
+#include "mongo/db/query/projection_ast_util.h"
namespace mongo {
+namespace {
+void statsToBSON(const QuerySolutionNode* node,
+ const sbe::PlanStageStats* stats,
+ ExplainOptions::Verbosity verbosity,
+ BSONObjBuilder* bob,
+ const BSONObjBuilder* topLevelBob) {
+ invariant(bob);
+ invariant(topLevelBob);
+ invariant(verbosity < ExplainOptions::Verbosity::kExecStats || stats);
+
+ // Stop as soon as the BSON object we're building exceeds the limit.
+ if (topLevelBob->len() > kMaxExplainStatsBSONSizeMB) {
+ bob->append("warning", "stats tree exceeded BSON size limit for explain");
+ return;
+ }
+
+ bob->append("stage", stageTypeToString(node->getType()));
+ bob->appendNumber("planNodeId", static_cast<size_t>(node->nodeId()));
+
+ // Display the BSON representation of the filter, if there is one.
+ if (node->filter) {
+ bob->append("filter", node->filter->serialize());
+ }
+
+ // Some top-level exec stats get pulled out of the root stage.
+ if (verbosity >= ExplainOptions::Verbosity::kExecStats) {
+ // TODO SERVER-51409.
+ }
+
+ // Stage-specific stats.
+ switch (node->getType()) {
+ case STAGE_AND_HASH: {
+ if (verbosity >= ExplainOptions::Verbosity::kExecStats) {
+ // TODO SERVER-51409.
+ }
+ break;
+ }
+ case STAGE_AND_SORTED: {
+ if (verbosity >= ExplainOptions::Verbosity::kExecStats) {
+ // TODO SERVER-51409.
+ }
+ break;
+ }
+ case STAGE_COLLSCAN: {
+ auto csn = static_cast<const CollectionScanNode*>(node);
+ bob->append("direction", csn->direction > 0 ? "forward" : "backward");
+ if (csn->minTs) {
+ bob->append("minTs", *csn->minTs);
+ }
+ if (csn->maxTs) {
+ bob->append("maxTs", *csn->maxTs);
+ }
+ if (verbosity >= ExplainOptions::Verbosity::kExecStats) {
+ // TODO SERVER-51409.
+ }
+ break;
+ }
+
+ case STAGE_ENSURE_SORTED: {
+ if (verbosity >= ExplainOptions::Verbosity::kExecStats) {
+ // TODO SERVER-51409.
+ }
+ break;
+ }
+ case STAGE_FETCH: {
+ if (verbosity >= ExplainOptions::Verbosity::kExecStats) {
+ // TODO SERVER-51409.
+ }
+ break;
+ }
+
+ case STAGE_GEO_NEAR_2D: {
+ auto geo2d = static_cast<const GeoNear2DNode*>(node);
+
+ bob->append("keyPattern", geo2d->index.keyPattern);
+ bob->append("indexName", geo2d->index.identifier.catalogName);
+ bob->append("indexVersion", geo2d->index.version);
+
+ if (verbosity >= ExplainOptions::Verbosity::kExecStats) {
+ // TODO SERVER-51409.
+ }
+ break;
+ }
+ case STAGE_GEO_NEAR_2DSPHERE: {
+ auto geo2dsphere = static_cast<const GeoNear2DSphereNode*>(node);
+
+ bob->append("keyPattern", geo2dsphere->index.keyPattern);
+ bob->append("indexName", geo2dsphere->index.identifier.catalogName);
+ bob->append("indexVersion", geo2dsphere->index.version);
+
+ if (verbosity >= ExplainOptions::Verbosity::kExecStats) {
+ // TODO SERVER-51409.
+ }
+ break;
+ }
+ case STAGE_IXSCAN: {
+ auto ixn = static_cast<const IndexScanNode*>(node);
+
+ bob->append("keyPattern", ixn->index.keyPattern);
+ bob->append("indexName", ixn->index.identifier.catalogName);
+ auto collation =
+ ixn->index.infoObj.getObjectField(IndexDescriptor::kCollationFieldName);
+ if (!collation.isEmpty()) {
+ bob->append("collation", collation);
+ }
+ bob->appendBool("isMultiKey", ixn->index.multikey);
+ if (!ixn->index.multikeyPaths.empty()) {
+ appendMultikeyPaths(ixn->index.keyPattern, ixn->index.multikeyPaths, bob);
+ }
+ bob->appendBool("isUnique", ixn->index.unique);
+ bob->appendBool("isSparse", ixn->index.sparse);
+ bob->appendBool("isPartial", ixn->index.filterExpr != nullptr);
+ bob->append("indexVersion", static_cast<int>(ixn->index.version));
+ bob->append("direction", ixn->direction > 0 ? "forward" : "backward");
+
+ auto bounds = ixn->bounds.toBSON();
+ if (topLevelBob->len() + bounds.objsize() > kMaxExplainStatsBSONSizeMB) {
+ bob->append("warning", "index bounds omitted due to BSON size limit for explain");
+ } else {
+ bob->append("indexBounds", bounds);
+ }
+
+ if (verbosity >= ExplainOptions::Verbosity::kExecStats) {
+ // TODO SERVER-51409.
+ }
+ break;
+ }
+ case STAGE_OR: {
+ if (verbosity >= ExplainOptions::Verbosity::kExecStats) {
+ // TODO SERVER-51409.
+ }
+ break;
+ }
+ case STAGE_LIMIT: {
+ auto ln = static_cast<const LimitNode*>(node);
+ bob->appendNumber("limitAmount", ln->limit);
+ break;
+ }
+ case STAGE_PROJECTION_DEFAULT:
+ case STAGE_PROJECTION_SIMPLE:
+ case STAGE_PROJECTION_COVERED: {
+ auto pn = static_cast<const ProjectionNode*>(node);
+ bob->append("transformBy", projection_ast::astToDebugBSON(pn->proj.root()));
+ break;
+ }
+ case STAGE_SHARDING_FILTER: {
+ if (verbosity >= ExplainOptions::Verbosity::kExecStats) {
+ // TODO SERVER-51409.
+ }
+ break;
+ }
+ case STAGE_SKIP: {
+ auto sn = static_cast<const SkipNode*>(node);
+ bob->appendNumber("skipAmount", sn->skip);
+ }
+ case STAGE_SORT_SIMPLE:
+ case STAGE_SORT_DEFAULT: {
+ auto sn = static_cast<const SortNode*>(node);
+ bob->append("sortPattern", sn->pattern);
+ bob->appendIntOrLL("memLimit", sn->maxMemoryUsageBytes);
+
+ if (sn->limit > 0) {
+ bob->appendIntOrLL("limitAmount", sn->limit);
+ }
+
+ bob->append("type", node->getType() == STAGE_SORT_SIMPLE ? "simple" : "default");
+
+ if (verbosity >= ExplainOptions::Verbosity::kExecStats) {
+ // TODO SERVER-51409.
+ }
+ break;
+ }
+ case STAGE_SORT_MERGE: {
+ auto smn = static_cast<const MergeSortNode*>(node);
+ bob->append("sortPattern", smn->sort);
+
+ if (verbosity >= ExplainOptions::Verbosity::kExecStats) {
+ // TODO SERVER-51409.
+ }
+ break;
+ }
+ case STAGE_TEXT: {
+ auto tn = static_cast<const TextNode*>(node);
+
+ bob->append("indexPrefix", tn->indexPrefix);
+ bob->append("indexName", tn->index.identifier.catalogName);
+ auto ftsQuery = dynamic_cast<fts::FTSQueryImpl*>(tn->ftsQuery.get());
+ invariant(ftsQuery);
+ bob->append("parsedTextQuery", ftsQuery->toBSON());
+ bob->append("textIndexVersion", tn->index.version);
+ }
+ case STAGE_TEXT_MATCH: {
+ if (verbosity >= ExplainOptions::Verbosity::kExecStats) {
+ // TODO SERVER-51409.
+ }
+ break;
+ }
+ case STAGE_TEXT_OR: {
+ if (verbosity >= ExplainOptions::Verbosity::kExecStats) {
+ // TODO SERVER-51409.
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ // We're done if there are no children.
+ if (node->children.empty()) {
+ return;
+ }
+
+ // If there's just one child (a common scenario), avoid making an array. This makes
+ // the output more readable by saving a level of nesting. Name the field 'inputStage'
+ // rather than 'inputStages'.
+ if (node->children.size() == 1) {
+ BSONObjBuilder childBob;
+ statsToBSON(node->children[0], stats, verbosity, &childBob, topLevelBob);
+ bob->append("inputStage", childBob.obj());
+ return;
+ }
+
+ // There is more than one child. Recursively call statsToBSON(...) on each
+ // of them and add them to the 'inputStages' array.
+ BSONArrayBuilder childrenBob(bob->subarrayStart("inputStages"));
+ for (auto&& child : node->children) {
+ BSONObjBuilder childBob(childrenBob.subobjStart());
+ statsToBSON(child, stats, verbosity, &childBob, topLevelBob);
+ }
+ childrenBob.doneFast();
+}
+} // namespace
+
std::string PlanExplainerSBE::getPlanSummary() const {
if (!_solution) {
return {};
@@ -181,14 +418,51 @@ void PlanExplainerSBE::getSummaryStats(PlanSummaryStats* statsOut) const {
PlanExplainer::PlanStatsDetails PlanExplainerSBE::getWinningPlanStats(
ExplainOptions::Verbosity verbosity) const {
- // TODO: SERVER-50728
- return {{}, PlanSummaryStats{}};
+ invariant(_root);
+ invariant(_solution);
+
+ auto&& [stats, summary] = [&]()
+ -> std::pair<std::unique_ptr<sbe::PlanStageStats>, boost::optional<PlanSummaryStats>> {
+ if (verbosity >= ExplainOptions::Verbosity::kExecStats) {
+ auto stats = _root->getStats();
+ // TODO: SERVER-51409 add support for PlanSummaryStats.
+ return {std::move(stats), PlanSummaryStats{}};
+ }
+ return {};
+ }();
+
+ BSONObjBuilder bob;
+ statsToBSON(_solution->root(), stats.get(), verbosity, &bob, &bob);
+ return {bob.obj(), std::move(summary)};
}
std::vector<PlanExplainer::PlanStatsDetails> PlanExplainerSBE::getRejectedPlansStats(
ExplainOptions::Verbosity verbosity) const {
- // TODO: SERVER-50728
- return {};
+ if (_rejectedCandidates.empty()) {
+ return {};
+ }
+
+ std::vector<PlanStatsDetails> res;
+ res.reserve(_rejectedCandidates.size());
+ for (auto&& candidate : _rejectedCandidates) {
+ invariant(candidate.root);
+ invariant(candidate.solution);
+
+ auto&& [stats, summary] = [&]()
+ -> std::pair<std::unique_ptr<sbe::PlanStageStats>, boost::optional<PlanSummaryStats>> {
+ if (verbosity >= ExplainOptions::Verbosity::kExecStats) {
+ auto stats = candidate.root->getStats();
+ // TODO: SERVER-51409 add support for PlanSummaryStats.
+ return {std::move(stats), PlanSummaryStats{}};
+ }
+ return {};
+ }();
+
+ BSONObjBuilder bob;
+ statsToBSON(candidate.solution->root(), stats.get(), verbosity, &bob, &bob);
+ res.push_back({bob.obj(), std::move(summary)});
+ }
+ return res;
}
std::vector<PlanExplainer::PlanStatsDetails> PlanExplainerSBE::getCachedPlanStats(
diff --git a/src/mongo/db/query/planner_analysis_test.cpp b/src/mongo/db/query/planner_analysis_test.cpp
index 3a6049e3143..d980dcb9444 100644
--- a/src/mongo/db/query/planner_analysis_test.cpp
+++ b/src/mongo/db/query/planner_analysis_test.cpp
@@ -47,6 +47,7 @@ namespace {
IndexEntry buildSimpleIndexEntry(const BSONObj& kp) {
return {kp,
IndexNames::nameToType(IndexNames::findPluginName(kp)),
+ IndexDescriptor::kLatestIndexVersion,
false,
{},
{},
diff --git a/src/mongo/db/query/planner_ixselect_test.cpp b/src/mongo/db/query/planner_ixselect_test.cpp
index 4dacc6a127b..38402324991 100644
--- a/src/mongo/db/query/planner_ixselect_test.cpp
+++ b/src/mongo/db/query/planner_ixselect_test.cpp
@@ -197,6 +197,7 @@ void findRelevantTaggedNodePathsAndIndices(MatchExpression* root,
IndexEntry buildSimpleIndexEntry(const BSONObj& kp) {
return {kp,
IndexNames::nameToType(IndexNames::findPluginName(kp)),
+ IndexDescriptor::kLatestIndexVersion,
false,
{},
{},
@@ -1069,6 +1070,7 @@ auto makeIndexEntry(BSONObj keyPattern,
});
return std::make_pair(IndexEntry(keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
multiKey,
multiKeyPaths,
multiKeyPathSet,
diff --git a/src/mongo/db/query/planner_wildcard_helpers.cpp b/src/mongo/db/query/planner_wildcard_helpers.cpp
index 837180ff85b..395d0253e04 100644
--- a/src/mongo/db/query/planner_wildcard_helpers.cpp
+++ b/src/mongo/db/query/planner_wildcard_helpers.cpp
@@ -397,6 +397,7 @@ void expandWildcardIndexEntry(const IndexEntry& wildcardIndex,
IndexEntry entry(BSON(fieldName << wildcardIndex.keyPattern.firstElement()),
IndexType::INDEX_WILDCARD,
+ IndexDescriptor::kLatestIndexVersion,
isMultikey,
std::move(multikeyPaths),
// Expanded index entries always use the fixed-size multikey paths
diff --git a/src/mongo/db/query/query_planner_operator_test.cpp b/src/mongo/db/query/query_planner_operator_test.cpp
index f09182c15db..2acb55d5687 100644
--- a/src/mongo/db/query/query_planner_operator_test.cpp
+++ b/src/mongo/db/query/query_planner_operator_test.cpp
@@ -396,6 +396,7 @@ TEST_F(QueryPlannerTest, NotEqualsNullInElemMatchObjectSparseMultiKeyAboveElemMa
auto keyPattern = BSON("a.b.c.d" << 1);
IndexEntry ind(keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
true,
{},
{},
@@ -425,6 +426,7 @@ TEST_F(QueryPlannerTest, NotEqualsNullInElemMatchObjectSparseMultiKeyBelowElemMa
auto keyPattern = BSON("a.b.c.d" << 1);
IndexEntry ind(keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
true,
{},
{},
diff --git a/src/mongo/db/query/query_planner_options_test.cpp b/src/mongo/db/query/query_planner_options_test.cpp
index a82a7f405f9..460433db511 100644
--- a/src/mongo/db/query/query_planner_options_test.cpp
+++ b/src/mongo/db/query/query_planner_options_test.cpp
@@ -43,6 +43,7 @@ namespace {
IndexEntry buildSimpleIndexEntry(const BSONObj& kp, const std::string& indexName) {
return {kp,
IndexNames::nameToType(IndexNames::findPluginName(kp)),
+ IndexDescriptor::kLatestIndexVersion,
false,
{},
{},
diff --git a/src/mongo/db/query/query_planner_test_fixture.cpp b/src/mongo/db/query/query_planner_test_fixture.cpp
index 0bc0a8c8f77..6307f9cc9d3 100644
--- a/src/mongo/db/query/query_planner_test_fixture.cpp
+++ b/src/mongo/db/query/query_planner_test_fixture.cpp
@@ -71,6 +71,7 @@ void QueryPlannerTest::clearState() {
void QueryPlannerTest::addIndex(BSONObj keyPattern, bool multikey) {
params.indices.push_back({keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
multikey,
{},
{},
@@ -86,6 +87,7 @@ void QueryPlannerTest::addIndex(BSONObj keyPattern, bool multikey) {
void QueryPlannerTest::addIndex(BSONObj keyPattern, bool multikey, bool sparse) {
params.indices.push_back({keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
multikey,
{},
{},
@@ -102,6 +104,7 @@ void QueryPlannerTest::addIndex(BSONObj keyPattern, bool multikey, bool sparse,
params.indices.push_back(
{keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
multikey,
{},
{},
@@ -117,6 +120,7 @@ void QueryPlannerTest::addIndex(BSONObj keyPattern, bool multikey, bool sparse,
void QueryPlannerTest::addIndex(BSONObj keyPattern, BSONObj infoObj) {
params.indices.push_back({keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
false, // multikey
{},
{},
@@ -132,6 +136,7 @@ void QueryPlannerTest::addIndex(BSONObj keyPattern, BSONObj infoObj) {
void QueryPlannerTest::addIndex(BSONObj keyPattern, MatchExpression* filterExpr) {
params.indices.push_back({keyPattern,
IndexNames::nameToType(IndexNames::findPluginName(keyPattern)),
+ IndexDescriptor::kLatestIndexVersion,
false, // multikey
{},
{},
@@ -159,6 +164,7 @@ void QueryPlannerTest::addIndex(BSONObj keyPattern, const MultikeyPaths& multike
const BSONObj infoObj;
IndexEntry entry(keyPattern,
type,
+ IndexDescriptor::kLatestIndexVersion,
multikey,
{},
{},
@@ -183,6 +189,7 @@ void QueryPlannerTest::addIndex(BSONObj keyPattern, const CollatorInterface* col
const BSONObj infoObj;
IndexEntry entry(keyPattern,
type,
+ IndexDescriptor::kLatestIndexVersion,
multikey,
{},
{},
@@ -209,6 +216,7 @@ void QueryPlannerTest::addIndex(BSONObj keyPattern,
const BSONObj infoObj;
IndexEntry entry(keyPattern,
type,
+ IndexDescriptor::kLatestIndexVersion,
multikey,
{},
{},
@@ -234,6 +242,7 @@ void QueryPlannerTest::addIndex(BSONObj keyPattern,
const BSONObj infoObj;
IndexEntry entry(keyPattern,
type,
+ IndexDescriptor::kLatestIndexVersion,
multikey,
{},
{},
diff --git a/src/mongo/db/query/query_planner_wildcard_index_test.cpp b/src/mongo/db/query/query_planner_wildcard_index_test.cpp
index 0494c13cab4..9d0b5b2df06 100644
--- a/src/mongo/db/query/query_planner_wildcard_index_test.cpp
+++ b/src/mongo/db/query/query_planner_wildcard_index_test.cpp
@@ -75,6 +75,7 @@ protected:
params.indices.push_back({std::move(keyPattern),
IndexType::INDEX_WILDCARD,
+ IndexDescriptor::kLatestIndexVersion,
isMultikey,
{}, // multikeyPaths
std::move(multikeyFieldRefs),
diff --git a/src/mongo/db/query/query_settings_test.cpp b/src/mongo/db/query/query_settings_test.cpp
index 6a6d0dce66f..15d3bebdccf 100644
--- a/src/mongo/db/query/query_settings_test.cpp
+++ b/src/mongo/db/query/query_settings_test.cpp
@@ -53,6 +53,7 @@ TEST(QuerySettingsTest, AllowedIndicesFilterAllowsIndexesByName) {
auto keyPat = fromjson("{a:1, b:1}");
IndexEntry a_idx(keyPat,
mongo::IndexNames::nameToType(mongo::IndexNames::findPluginName(keyPat)),
+ mongo::IndexDescriptor::kLatestIndexVersion,
false,
{},
{},
@@ -65,6 +66,7 @@ TEST(QuerySettingsTest, AllowedIndicesFilterAllowsIndexesByName) {
nullptr);
IndexEntry ab_idx(keyPat,
mongo::IndexNames::nameToType(mongo::IndexNames::findPluginName(keyPat)),
+ mongo::IndexDescriptor::kLatestIndexVersion,
false,
{},
{},
@@ -86,6 +88,7 @@ TEST(QuerySettingsTest, AllowedIndicesFilterAllowsIndexesByKeyPattern) {
auto keyPat_a = fromjson("{a:1}");
IndexEntry a_idx(keyPat_a,
mongo::IndexNames::nameToType(mongo::IndexNames::findPluginName(keyPat_a)),
+ mongo::IndexDescriptor::kLatestIndexVersion,
false,
{},
{},
@@ -99,6 +102,7 @@ TEST(QuerySettingsTest, AllowedIndicesFilterAllowsIndexesByKeyPattern) {
auto keyPat_ab = fromjson("{a:1, b:1}");
IndexEntry ab_idx(keyPat_ab,
mongo::IndexNames::nameToType(mongo::IndexNames::findPluginName(keyPat_ab)),
+ mongo::IndexDescriptor::kLatestIndexVersion,
false,
{},
{},
diff --git a/src/mongo/db/query/query_solution.h b/src/mongo/db/query/query_solution.h
index 5e4280582f8..7801a3ea18e 100644
--- a/src/mongo/db/query/query_solution.h
+++ b/src/mongo/db/query/query_solution.h
@@ -860,6 +860,11 @@ struct SortNode : public QuerySolutionNodeWithSortSet {
bool addSortKeyMetadata = false;
+ // The maximum number of bytes of memory we're willing to use during execution of the sort. If
+ // this limit is exceeded and we're not allowed to spill to disk, the query will fail at
+ // execution time. Otherwise, the data will be spilled to disk.
+ uint64_t maxMemoryUsageBytes = internalQueryMaxBlockingSortMemoryUsageBytes.load();
+
protected:
void cloneSortData(SortNode* copy) const;
diff --git a/src/mongo/db/query/query_solution_test.cpp b/src/mongo/db/query/query_solution_test.cpp
index e7546a854c9..8763b208fcd 100644
--- a/src/mongo/db/query/query_solution_test.cpp
+++ b/src/mongo/db/query/query_solution_test.cpp
@@ -52,6 +52,7 @@ using namespace mongo;
IndexEntry buildSimpleIndexEntry(const BSONObj& kp) {
return {kp,
IndexNames::nameToType(IndexNames::findPluginName(kp)),
+ IndexDescriptor::kLatestIndexVersion,
false,
{},
{},
diff --git a/src/mongo/db/query/sbe_stage_builder.cpp b/src/mongo/db/query/sbe_stage_builder.cpp
index 717803871ec..cc17cccf974 100644
--- a/src/mongo/db/query/sbe_stage_builder.cpp
+++ b/src/mongo/db/query/sbe_stage_builder.cpp
@@ -315,7 +315,7 @@ std::unique_ptr<sbe::PlanStage> SlotBasedStageBuilder::buildSort(const QuerySolu
std::move(values),
sn->limit ? sn->limit
: std::numeric_limits<std::size_t>::max(),
- internalQueryMaxBlockingSortMemoryUsageBytes.load(),
+ sn->maxMemoryUsageBytes,
_cq.getExpCtx()->allowDiskUse,
_data.trialRunProgressTracker.get(),
root->nodeId());