diff options
author | Anton Korshunov <anton.korshunov@mongodb.com> | 2020-10-29 18:07:06 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-11-04 16:47:25 +0000 |
commit | b9630a4621e919837749c5010f3c8e3429a944a9 (patch) | |
tree | b9a73bacf2e96c6207caa05204bc01154efa40c1 /src/mongo/db/query | |
parent | 4666dc59f9aa93e4927bf9835b4bd038488d98f9 (diff) | |
download | mongo-b9630a4621e919837749c5010f3c8e3429a944a9.tar.gz |
SERVER-50728 Use QuerySolution to generate explain output at queryPlanner verbosity in SBE
Diffstat (limited to 'src/mongo/db/query')
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()); |