diff options
Diffstat (limited to 'src/mongo')
26 files changed, 290 insertions, 126 deletions
diff --git a/src/mongo/db/pipeline/document_source_lookup.h b/src/mongo/db/pipeline/document_source_lookup.h index 3fa95b7e151..20afde99bf3 100644 --- a/src/mongo/db/pipeline/document_source_lookup.h +++ b/src/mongo/db/pipeline/document_source_lookup.h @@ -190,6 +190,13 @@ public: return _localField; } + /** + * "as" field must be present in all types of $lookup queries. + */ + const FieldPath& getAsField() const { + return _as; + } + const std::vector<LetVariable>& getLetVariables() const { return _letVariables; } diff --git a/src/mongo/db/pipeline/pipeline_d.cpp b/src/mongo/db/pipeline/pipeline_d.cpp index ecb2b13d0b5..de8c252d2d9 100644 --- a/src/mongo/db/pipeline/pipeline_d.cpp +++ b/src/mongo/db/pipeline/pipeline_d.cpp @@ -171,8 +171,11 @@ std::vector<std::unique_ptr<InnerPipelineStageInterface>> extractSbeCompatibleSt feature_flags::gFeatureFlagSBELookupPushdown.isEnabledAndIgnoreFCV() && internalEnableMultipleAutoGetCollections.load() && lookupStage->sbeCompatible() && !isForeignSharded && !isForeignView; - uassert( - 5843700, "$lookup push down logic worked correctly", !lookupEligibleForPushdown); + if (lookupEligibleForPushdown) { + stagesForPushdown.push_back(std::make_unique<InnerPipelineStageImpl>(lookupStage)); + sources.erase(itr++); + continue; + } break; } diff --git a/src/mongo/db/query/classic_stage_builder.h b/src/mongo/db/query/classic_stage_builder.h index c99531cae95..d761cedfca5 100644 --- a/src/mongo/db/query/classic_stage_builder.h +++ b/src/mongo/db/query/classic_stage_builder.h @@ -43,11 +43,12 @@ public: const CanonicalQuery& cq, const QuerySolution& solution, WorkingSet* ws) - : StageBuilder<PlanStage>{opCtx, collection, cq, solution}, _ws{ws} {} + : StageBuilder<PlanStage>{opCtx, cq, solution}, _collection(collection), _ws{ws} {} std::unique_ptr<PlanStage> build(const QuerySolutionNode* root) final; private: + const CollectionPtr& _collection; WorkingSet* _ws; boost::optional<size_t> _ftsKeyPrefixSize; diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp index 5e545bdb290..160ff81fd8a 100644 --- a/src/mongo/db/query/get_executor.cpp +++ b/src/mongo/db/query/get_executor.cpp @@ -61,6 +61,7 @@ #include "mongo/db/index_names.h" #include "mongo/db/matcher/extensions_callback_noop.h" #include "mongo/db/matcher/extensions_callback_real.h" +#include "mongo/db/pipeline/document_source_lookup.h" #include "mongo/db/query/bind_input_params.h" #include "mongo/db/query/canonical_query.h" #include "mongo/db/query/canonical_query_encoder.h" @@ -377,24 +378,33 @@ void fillOutPlannerParams(OperationContext* opCtx, } } -void fillOutSecondaryCollectionsInformation(OperationContext* opCtx, - const MultiCollection& collections, - CanonicalQuery* canonicalQuery, - QueryPlannerParams* plannerParams) { +std::map<NamespaceString, SecondaryCollectionInfo> fillOutSecondaryCollectionsInformation( + OperationContext* opCtx, const MultiCollection& collections, CanonicalQuery* canonicalQuery) { + std::map<NamespaceString, SecondaryCollectionInfo> infoMap; bool apiStrict = APIParameters::get(opCtx).getAPIStrict().value_or(false); - for (auto& [collName, secondaryColl] : collections.getSecondaryCollections()) { - plannerParams->secondaryCollectionsInfo.emplace_back(SecondaryCollectionInfo()); - auto& info = plannerParams->secondaryCollectionsInfo.back(); - info.nss = collName; + auto fillOutSecondaryInfo = [&](const NamespaceString& nss, + const CollectionPtr& secondaryColl) { + auto secondaryInfo = SecondaryCollectionInfo(); if (secondaryColl) { - fillOutIndexEntries(opCtx, apiStrict, canonicalQuery, secondaryColl, info.indexes); - info.isSharded = secondaryColl.isSharded(); - info.approximateCollectionSizeBytes = secondaryColl.get()->dataSize(opCtx); - info.isView = !secondaryColl.get()->getCollectionOptions().viewOn.empty(); + fillOutIndexEntries( + opCtx, apiStrict, canonicalQuery, secondaryColl, secondaryInfo.indexes); + secondaryInfo.approximateCollectionSizeBytes = secondaryColl.get()->dataSize(opCtx); } else { - info.exists = false; + secondaryInfo.exists = false; } + infoMap.emplace(nss, std::move(secondaryInfo)); + }; + for (auto& [collName, secondaryColl] : collections.getSecondaryCollections()) { + fillOutSecondaryInfo(collName, secondaryColl); + } + + // In the event of a self $lookup, we must have an entry for the main collection in the map + // of secondary collections. + if (collections.hasMainCollection()) { + const auto& mainColl = collections.getMainCollection(); + fillOutSecondaryInfo(mainColl->ns(), mainColl); } + return infoMap; } void fillOutPlannerParams(OperationContext* opCtx, @@ -402,7 +412,8 @@ void fillOutPlannerParams(OperationContext* opCtx, CanonicalQuery* canonicalQuery, QueryPlannerParams* plannerParams) { fillOutPlannerParams(opCtx, collections.getMainCollection(), canonicalQuery, plannerParams); - fillOutSecondaryCollectionsInformation(opCtx, collections, canonicalQuery, plannerParams); + plannerParams->secondaryCollectionsInfo = + fillOutSecondaryCollectionsInformation(opCtx, collections, canonicalQuery); } bool shouldWaitForOplogVisibility(OperationContext* opCtx, @@ -572,11 +583,6 @@ public: */ virtual const CollectionPtr& getMainCollection() const = 0; - /** - * Fills out planning params needed for the target execution engine. - */ - virtual void fillOutPlannerParamsHelper(QueryPlannerParams* plannerParams) = 0; - StatusWith<std::unique_ptr<ResultType>> prepare() { const auto& mainColl = getMainCollection(); if (!mainColl) { @@ -599,7 +605,7 @@ public: // Fill out the planning params. We use these for both cached solutions and non-cached. QueryPlannerParams plannerParams; plannerParams.options = _plannerOptions; - fillOutPlannerParamsHelper(&plannerParams); + fillOutPlannerParams(_opCtx, getMainCollection(), _cq, &plannerParams); tassert( 5842901, "Fast count queries aren't supported in SBE, therefore, should never lower parts of " @@ -783,10 +789,6 @@ public: return _collection; } - virtual void fillOutPlannerParamsHelper(QueryPlannerParams* plannerParams) override { - fillOutPlannerParams(_opCtx, getMainCollection(), _cq, plannerParams); - } - protected: std::unique_ptr<PlanStage> buildExecutableTree(const QuerySolution& solution) const final { return stage_builder::buildClassicExecutableTree(_opCtx, _collection, *_cq, solution, _ws); @@ -962,17 +964,13 @@ public: return _collections.getMainCollection(); } - void fillOutPlannerParamsHelper(QueryPlannerParams* plannerParams) override { - fillOutPlannerParams(_opCtx, _collections, _cq, plannerParams); - } - std::pair<std::unique_ptr<sbe::PlanStage>, stage_builder::PlanStageData> buildExecutableTree( const QuerySolution& solution) const final { // TODO SERVER-62677 We don't pass '_collections' to the function below because at the // moment, no pushdown is actually happening. This should be changed once the logic for // pushdown is implemented. return stage_builder::buildSlotBasedExecutableTree( - _opCtx, getMainCollection(), *_cq, solution, _yieldPolicy); + _opCtx, _collections, *_cq, solution, _yieldPolicy); } protected: @@ -1200,13 +1198,20 @@ std::unique_ptr<sbe::RuntimePlanner> makeRuntimePlannerIfNeeded( PlanYieldPolicySBE* yieldPolicy, size_t plannerOptions, std::unique_ptr<plan_cache_debug_info::DebugInfoSBE> debugInfo) { - const auto& mainColl = collections.getMainCollection(); // If we have multiple solutions, we always need to do the runtime planning. if (numSolutions > 1) { invariant(!needsSubplanning && !decisionWorks); - return std::make_unique<sbe::MultiPlanner>( - opCtx, mainColl, *canonicalQuery, PlanCachingMode::AlwaysCache, yieldPolicy); + QueryPlannerParams plannerParams; + plannerParams.options = plannerOptions; + fillOutPlannerParams(opCtx, collections, canonicalQuery, &plannerParams); + + return std::make_unique<sbe::MultiPlanner>(opCtx, + collections, + *canonicalQuery, + plannerParams, + PlanCachingMode::AlwaysCache, + yieldPolicy); } // If the query can be run as sub-queries, the needSubplanning flag will be set to true and @@ -1220,7 +1225,7 @@ std::unique_ptr<sbe::RuntimePlanner> makeRuntimePlannerIfNeeded( fillOutPlannerParams(opCtx, collections, canonicalQuery, &plannerParams); return std::make_unique<sbe::SubPlanner>( - opCtx, mainColl, *canonicalQuery, plannerParams, yieldPolicy); + opCtx, collections, *canonicalQuery, plannerParams, yieldPolicy); } invariant(numSolutions == 1); @@ -1235,7 +1240,7 @@ std::unique_ptr<sbe::RuntimePlanner> makeRuntimePlannerIfNeeded( fillOutPlannerParams(opCtx, collections, canonicalQuery, &plannerParams); return std::make_unique<sbe::CachedSolutionPlanner>(opCtx, - mainColl, + collections, *canonicalQuery, plannerParams, *decisionWorks, @@ -1321,7 +1326,10 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getSlotBasedExe invariant(roots.size() == 1); if (!cq->pipeline().empty()) { // Need to extend the solution with the agg pipeline and rebuild the execution tree. - solutions[0] = QueryPlanner::extendWithAggPipeline(*cq, std::move(solutions[0])); + solutions[0] = QueryPlanner::extendWithAggPipeline( + *cq, + std::move(solutions[0]), + fillOutSecondaryCollectionsInformation(opCtx, collections, cq.get())); roots[0] = helper.buildExecutableTree(*(solutions[0])); } return plan_executor_factory::make(opCtx, diff --git a/src/mongo/db/query/get_executor.h b/src/mongo/db/query/get_executor.h index c06caf23f9d..4e038a646c9 100644 --- a/src/mongo/db/query/get_executor.h +++ b/src/mongo/db/query/get_executor.h @@ -84,10 +84,8 @@ void fillOutIndexEntries(OperationContext* opCtx, /** * Fills out information about secondary collections held by 'collections' in 'plannerParams'. */ -void fillOutSecondaryCollectionsInformation(OperationContext* opCtx, - const MultiCollection& collections, - CanonicalQuery* canonicalQuery, - QueryPlannerParams* plannerParams); +std::map<NamespaceString, SecondaryCollectionInfo> fillOutSecondaryCollectionsInformation( + OperationContext* opCtx, const MultiCollection& collections, CanonicalQuery* canonicalQuery); /** * Fill out the provided 'plannerParams' for the 'canonicalQuery' operating on the collection diff --git a/src/mongo/db/query/multi_collection.h b/src/mongo/db/query/multi_collection.h index 9b3230a2cc6..1e7a04357af 100644 --- a/src/mongo/db/query/multi_collection.h +++ b/src/mongo/db/query/multi_collection.h @@ -50,7 +50,6 @@ public: secondaryCollCtxes) { if (mainCollCtx) { _mainColl = &mainCollCtx->getCollection(); - _mainCollName = mainCollCtx->getNss(); } for (auto& secondaryColl : secondaryCollCtxes) { @@ -81,7 +80,7 @@ public: } const CollectionPtr& lookupCollection(const NamespaceString& nss) const { - if (_mainCollName && nss == *_mainCollName) { + if (_mainColl && nss == _mainColl->get()->ns()) { return *_mainColl; } else if (auto itr = _secondaryColls.find(nss); itr != _secondaryColls.end()) { return itr->second; @@ -96,7 +95,6 @@ public: private: const CollectionPtr* _mainColl{&CollectionPtr::null}; - boost::optional<NamespaceString> _mainCollName = boost::none; // Map from namespace to a corresponding CollectionPtr. std::map<NamespaceString, const CollectionPtr&> _secondaryColls{}; diff --git a/src/mongo/db/query/planner_analysis.cpp b/src/mongo/db/query/planner_analysis.cpp index c17fd1069d2..3989188faa7 100644 --- a/src/mongo/db/query/planner_analysis.cpp +++ b/src/mongo/db/query/planner_analysis.cpp @@ -613,6 +613,45 @@ std::unique_ptr<QuerySolution> QueryPlannerAnalysis::removeProjectSimpleBelowGro } // static +void QueryPlannerAnalysis::determineLookupStrategy( + EqLookupNode* eqLookupNode, + const std::map<NamespaceString, SecondaryCollectionInfo>& collectionsInfo, + bool allowDiskUse) { + const auto& foreignCollName = eqLookupNode->foreignCollection; + auto foreignCollItr = collectionsInfo.find(NamespaceString(foreignCollName)); + tassert(5842600, + str::stream() << "Expected collection info, but found none; target collection: " + << foreignCollName, + foreignCollItr != collectionsInfo.end()); + + // Does an eligible index exist? + // TODO SERVER-62913: finalize the logic for indexes analysis. + const auto& foreignField = eqLookupNode->joinFieldForeign; + IndexEntry* prefixedIndex = nullptr; + for (auto idxEntry : foreignCollItr->second.indexes) { + tassert(5842601, "index key pattern should not be empty", !idxEntry.keyPattern.isEmpty()); + if (idxEntry.keyPattern.firstElement().fieldName() == foreignField) { + prefixedIndex = &idxEntry; + break; + } + } + + // TODO SERVER-63449: make this setting configurable and tighten the HJ check to cover the + // number of records and the storage size of the collection. + static constexpr auto kMaxHashJoinCollectionSize = 100 * 1024 * 1024; + + if (prefixedIndex) { + eqLookupNode->lookupStrategy = EqLookupNode::LookupStrategy::kIndexedLoopJoin; + eqLookupNode->idxEntry = *prefixedIndex; + } else if (allowDiskUse && + foreignCollItr->second.approximateCollectionSizeBytes < kMaxHashJoinCollectionSize) { + eqLookupNode->lookupStrategy = EqLookupNode::LookupStrategy::kHashJoin; + } else { + eqLookupNode->lookupStrategy = EqLookupNode::LookupStrategy::kNestedLoopJoin; + } +} + +// static void QueryPlannerAnalysis::analyzeGeo(const QueryPlannerParams& params, QuerySolutionNode* solnRoot) { // Get field names of all 2dsphere indexes with version >= 3. diff --git a/src/mongo/db/query/planner_analysis.h b/src/mongo/db/query/planner_analysis.h index e272cae2948..9efc9419ec1 100644 --- a/src/mongo/db/query/planner_analysis.h +++ b/src/mongo/db/query/planner_analysis.h @@ -121,6 +121,20 @@ public: */ static std::unique_ptr<QuerySolution> removeProjectSimpleBelowGroup( std::unique_ptr<QuerySolution> soln); + + /** + * For the provided 'eqLookupNode', determines what join algorithm should be used to execute it + * and marks the node accordingly. In particular: + * - An indexed nested loop join is chosen if an index on the foreign collection can be used to + * answer the join predicate. + * - A hash join is chosen if disk use is allowed and if the foreign collection is sufficiently + * small. + * - A nested loop join is chosen in all other cases. + */ + static void determineLookupStrategy( + EqLookupNode* eqLookupNode, + const std::map<NamespaceString, SecondaryCollectionInfo>& collectionsInfo, + bool allowDiskUse); }; } // namespace mongo diff --git a/src/mongo/db/query/query_planner.cpp b/src/mongo/db/query/query_planner.cpp index ad91bd0c323..848e8ba6aef 100644 --- a/src/mongo/db/query/query_planner.cpp +++ b/src/mongo/db/query/query_planner.cpp @@ -45,6 +45,7 @@ #include "mongo/db/matcher/expression_geo.h" #include "mongo/db/matcher/expression_text.h" #include "mongo/db/pipeline/document_source_group.h" +#include "mongo/db/pipeline/document_source_lookup.h" #include "mongo/db/query/canonical_query.h" #include "mongo/db/query/classic_plan_cache.h" #include "mongo/db/query/collation/collation_index_key.h" @@ -1252,7 +1253,9 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan( * and later attach the agg portion of the plan to the solution(s) for the "find" part of the query. */ std::unique_ptr<QuerySolution> QueryPlanner::extendWithAggPipeline( - const CanonicalQuery& query, std::unique_ptr<QuerySolution>&& solution) { + const CanonicalQuery& query, + std::unique_ptr<QuerySolution>&& solution, + const std::map<NamespaceString, SecondaryCollectionInfo>& secondaryCollInfos) { if (query.pipeline().empty()) { return nullptr; } @@ -1260,15 +1263,35 @@ std::unique_ptr<QuerySolution> QueryPlanner::extendWithAggPipeline( std::unique_ptr<QuerySolutionNode> solnForAgg = std::make_unique<SentinelNode>(); for (auto& innerStage : query.pipeline()) { auto groupStage = dynamic_cast<DocumentSourceGroup*>(innerStage->documentSource()); - tassert(5842400, - "Cannot support pushdown of a stage other than $group at the moment", - groupStage != nullptr); - - solnForAgg = std::make_unique<GroupNode>(std::move(solnForAgg), - groupStage->getIdExpression(), - groupStage->getAccumulatedFields(), - groupStage->doingMerge()); + if (groupStage) { + solnForAgg = std::make_unique<GroupNode>(std::move(solnForAgg), + groupStage->getIdExpression(), + groupStage->getAccumulatedFields(), + groupStage->doingMerge()); + continue; + } + + auto lookupStage = dynamic_cast<DocumentSourceLookUp*>(innerStage->documentSource()); + if (lookupStage) { + tassert(5842409, + "Only $lookup that use local/foreign field syntax should be lowered", + lookupStage->getLocalField() && lookupStage->getForeignField()); + auto eqLookupNode = + std::make_unique<EqLookupNode>(std::move(solnForAgg), + lookupStage->getFromNs().toString(), + lookupStage->getLocalField()->fullPath(), + lookupStage->getForeignField()->fullPath(), + lookupStage->getAsField().fullPath()); + QueryPlannerAnalysis::determineLookupStrategy( + eqLookupNode.get(), secondaryCollInfos, query.getExpCtx()->allowDiskUse); + solnForAgg = std::move(eqLookupNode); + continue; + } + + tasserted(5842400, + "Cannot support pushdown of a stage other than $group or $lookup at the moment"); } + solution->extendWith(std::move(solnForAgg)); return QueryPlannerAnalysis::removeProjectSimpleBelowGroup(std::move(solution)); } diff --git a/src/mongo/db/query/query_planner.h b/src/mongo/db/query/query_planner.h index e0de1231f12..170083d523b 100644 --- a/src/mongo/db/query/query_planner.h +++ b/src/mongo/db/query/query_planner.h @@ -32,6 +32,7 @@ #include "mongo/base/string_data.h" #include "mongo/db/query/canonical_query.h" #include "mongo/db/query/classic_plan_cache.h" +#include "mongo/db/query/multi_collection.h" #include "mongo/db/query/query_planner_params.h" #include "mongo/db/query/query_solution.h" @@ -91,7 +92,9 @@ public: }; static std::unique_ptr<QuerySolution> extendWithAggPipeline( - const CanonicalQuery& query, std::unique_ptr<QuerySolution>&& solution); + const CanonicalQuery& query, + std::unique_ptr<QuerySolution>&& solution, + const std::map<NamespaceString, SecondaryCollectionInfo>& secondaryCollInfos); /** * Returns the list of possible query solutions for the provided 'query' for multi-planning. diff --git a/src/mongo/db/query/query_planner_group_pushdown_test.cpp b/src/mongo/db/query/query_planner_group_pushdown_test.cpp index 6476bd6002f..bf1e29b086e 100644 --- a/src/mongo/db/query/query_planner_group_pushdown_test.cpp +++ b/src/mongo/db/query/query_planner_group_pushdown_test.cpp @@ -79,7 +79,8 @@ TEST_F(QueryPlannerGroupPushdownTest, PushdownOfASingleGroup) { // Check the plan after lowering $group into the find subsystem. ASSERT(!cq->pipeline().empty()); - auto solution = QueryPlanner::extendWithAggPipeline(*cq, std::move(solns[0])); + auto solution = + QueryPlanner::extendWithAggPipeline(*cq, std::move(solns[0]), {} /* secondaryCollInfos */); ASSERT(QueryPlannerTestLib::solutionMatches( "{group: {key: {_id: '$_id'}, accs: [{count: {$sum: '$x'}}], node: " "{cscan: {dir:1, filter: {x:1}}}" @@ -106,7 +107,8 @@ TEST_F(QueryPlannerGroupPushdownTest, PushdownOfTwoGroups) { // Check the plan after lowering $group into the find subsystem. ASSERT(!cq->pipeline().empty()); - auto solution = QueryPlanner::extendWithAggPipeline(*cq, std::move(solns[0])); + auto solution = + QueryPlanner::extendWithAggPipeline(*cq, std::move(solns[0]), {} /* secondaryCollInfos */); ASSERT(QueryPlannerTestLib::solutionMatches( "{group: {key: {_id: '$_id'}, accs: [{count: {$min: '$count'}}], node: " "{group: {key: {_id: '$_id'}, accs: [{count: {$sum: '$x'}}], node: " @@ -134,7 +136,8 @@ TEST_F(QueryPlannerGroupPushdownTest, PushdownOfOneGroupWithMultipleAccumulators // Check the plan after lowering $group into the find subsystem. ASSERT(!cq->pipeline().empty()); - auto solution = QueryPlanner::extendWithAggPipeline(*cq, std::move(solns[0])); + auto solution = + QueryPlanner::extendWithAggPipeline(*cq, std::move(solns[0]), {} /* secondaryCollInfos */); ASSERT( QueryPlannerTestLib::solutionMatches( "{group: {key: {_id: '$_id'}, accs: [{count: {$sum: '$x'}}, {m: {$min: '$y'}}], node: " diff --git a/src/mongo/db/query/query_planner_params.h b/src/mongo/db/query/query_planner_params.h index 4fb56c7c213..265f1efaf4a 100644 --- a/src/mongo/db/query/query_planner_params.h +++ b/src/mongo/db/query/query_planner_params.h @@ -44,11 +44,8 @@ namespace mongo { * $lookup) useful for query planning. */ struct SecondaryCollectionInfo { - NamespaceString nss; std::vector<IndexEntry> indexes{}; bool exists{true}; - bool isSharded{false}; - bool isView{false}; // The approximate size of the collection in bytes. long long approximateCollectionSizeBytes{0}; @@ -167,7 +164,7 @@ struct QueryPlannerParams { const CollatorInterface* clusteredCollectionCollator; // List of information about any secondary collections that can be executed against. - std::vector<SecondaryCollectionInfo> secondaryCollectionsInfo; + std::map<NamespaceString, SecondaryCollectionInfo> secondaryCollectionsInfo; }; } // namespace mongo diff --git a/src/mongo/db/query/query_solution.h b/src/mongo/db/query/query_solution.h index d63704a3afa..0eaacc977ed 100644 --- a/src/mongo/db/query/query_solution.h +++ b/src/mongo/db/query/query_solution.h @@ -1381,6 +1381,20 @@ struct GroupNode : public QuerySolutionNode { * by direct name rather than QuerySolutionNode. */ struct EqLookupNode : public QuerySolutionNode { + /** + * Enum describing the possible algorithms that can be used to execute a pushed down $lookup. + */ + enum class LookupStrategy { + // Execute the join by storing entries from the foreign collection in a hash table. + kHashJoin, + + // Execute the join by doing an index lookup in the foreign collection. + kIndexedLoopJoin, + + // Execute the join by iterating over the foreign collection for each local key. + kNestedLoopJoin, + }; + EqLookupNode(std::unique_ptr<QuerySolutionNode> child, const std::string& foreignCollection, const std::string& joinFieldLocal, @@ -1445,6 +1459,18 @@ struct EqLookupNode : public QuerySolutionNode { * If the field already exists in the local (outer) document, the field will be overwritten. */ std::string joinField; + + /** + * The algorithm that will be used to execute this 'EqLookupNode'. Defaults to nested loop join + * as it's applicable independent of collection sizes or the availability of indexes. + */ + LookupStrategy lookupStrategy = LookupStrategy::kNestedLoopJoin; + + /** + * The index to be used if we can answer the join predicate with an index on the foreign + * collection. Set to 'boost::none' by default and if a non-indexed strategy is chosen. + */ + boost::optional<IndexEntry> idxEntry = boost::none; }; struct SentinelNode : public QuerySolutionNode { diff --git a/src/mongo/db/query/sbe_cached_solution_planner.cpp b/src/mongo/db/query/sbe_cached_solution_planner.cpp index d0fd8db0b81..61a2df9f6db 100644 --- a/src/mongo/db/query/sbe_cached_solution_planner.cpp +++ b/src/mongo/db/query/sbe_cached_solution_planner.cpp @@ -54,9 +54,10 @@ CandidatePlans CachedSolutionPlanner::plan( // during multiplanning even though multiplanning ran trials of pre-extended plans. if (!_cq.pipeline().empty()) { _yieldPolicy->clearRegisteredPlans(); - solutions[0] = QueryPlanner::extendWithAggPipeline(_cq, std::move(solutions[0])); + solutions[0] = QueryPlanner::extendWithAggPipeline( + _cq, std::move(solutions[0]), _queryParams.secondaryCollectionsInfo); roots[0] = stage_builder::buildSlotBasedExecutableTree( - _opCtx, _collection, _cq, *solutions[0], _yieldPolicy); + _opCtx, _collections, _cq, *solutions[0], _yieldPolicy); } const size_t maxReadsBeforeReplan = internalQueryCacheEvictionRatio * _decisionReads; @@ -172,16 +173,17 @@ CandidatePlans CachedSolutionPlanner::replan(bool shouldCache, std::string reaso // Therefore, if any of the collection's indexes have been dropped, the query should fail with // a 'QueryPlanKilled' error. _indexExistenceChecker.check(); + const auto& mainColl = _collections.getMainCollection(); if (shouldCache) { // Deactivate the current cache entry. - auto cache = CollectionQueryInfo::get(_collection).getPlanCache(); - cache->deactivate(plan_cache_key_factory::make<mongo::PlanCacheKey>(_cq, _collection)); + auto cache = CollectionQueryInfo::get(mainColl).getPlanCache(); + cache->deactivate(plan_cache_key_factory::make<mongo::PlanCacheKey>(_cq, mainColl)); } auto buildExecutableTree = [&](const QuerySolution& sol) { auto [root, data] = stage_builder::buildSlotBasedExecutableTree( - _opCtx, _collection, _cq, sol, _yieldPolicy); + _opCtx, _collections, _cq, sol, _yieldPolicy); data.replanReason.emplace(reason); return std::make_pair(std::move(root), std::move(data)); }; @@ -225,7 +227,7 @@ CandidatePlans CachedSolutionPlanner::replan(bool shouldCache, std::string reaso const auto cachingMode = shouldCache ? PlanCachingMode::AlwaysCache : PlanCachingMode::NeverCache; - MultiPlanner multiPlanner{_opCtx, _collection, _cq, cachingMode, _yieldPolicy}; + MultiPlanner multiPlanner{_opCtx, _collections, _cq, _queryParams, cachingMode, _yieldPolicy}; auto&& [candidates, winnerIdx] = multiPlanner.plan(std::move(solutions), std::move(roots)); auto explainer = plan_explainer_factory::make(candidates[winnerIdx].root.get(), &candidates[winnerIdx].data, diff --git a/src/mongo/db/query/sbe_cached_solution_planner.h b/src/mongo/db/query/sbe_cached_solution_planner.h index ebef51e662b..1b3c5cc8008 100644 --- a/src/mongo/db/query/sbe_cached_solution_planner.h +++ b/src/mongo/db/query/sbe_cached_solution_planner.h @@ -45,14 +45,13 @@ namespace mongo::sbe { class CachedSolutionPlanner final : public BaseRuntimePlanner { public: CachedSolutionPlanner(OperationContext* opCtx, - const CollectionPtr& collection, + const MultiCollection& collections, const CanonicalQuery& cq, const QueryPlannerParams& queryParams, size_t decisionReads, PlanYieldPolicySBE* yieldPolicy, std::unique_ptr<plan_cache_debug_info::DebugInfoSBE> debugInfo) - : BaseRuntimePlanner{opCtx, collection, cq, yieldPolicy}, - _queryParams{queryParams}, + : BaseRuntimePlanner{opCtx, collections, cq, queryParams, yieldPolicy}, _decisionReads{decisionReads}, _debugInfo{std::move(debugInfo)} {} @@ -98,10 +97,6 @@ private: */ CandidatePlans replan(bool shouldCache, std::string reason) const; - // Query parameters used to create a query solution when the plan was first created. Used during - // replanning. - const QueryPlannerParams _queryParams; - // The number of physical reads taken to decide on a winning plan when the plan was first // cached. const size_t _decisionReads; diff --git a/src/mongo/db/query/sbe_multi_planner.cpp b/src/mongo/db/query/sbe_multi_planner.cpp index e8ddbaa1232..7e7cab6ec32 100644 --- a/src/mongo/db/query/sbe_multi_planner.cpp +++ b/src/mongo/db/query/sbe_multi_planner.cpp @@ -50,7 +50,7 @@ CandidatePlans MultiPlanner::plan( std::move(solutions), std::move(roots), trial_period::getTrialPeriodMaxWorks(_opCtx, - _collection, + _collections.getMainCollection(), internalQueryPlanEvaluationWorksSbe.load(), internalQueryPlanEvaluationCollFractionSbe.load())); auto decision = uassertStatusOK(mongo::plan_ranker::pickBestPlan<PlanStageStats>(candidates)); @@ -117,8 +117,12 @@ CandidatePlans MultiPlanner::finalizeExecutionPlans( } // Writes a cache entry for the winning plan to the plan cache if possible. - plan_cache_util::updatePlanCache( - _opCtx, _collection, _cachingMode, _cq, std::move(decision), candidates); + plan_cache_util::updatePlanCache(_opCtx, + _collections.getMainCollection(), + _cachingMode, + _cq, + std::move(decision), + candidates); // Extend the winning candidate with the agg pipeline and rebuild the execution tree. Because // the trial was done with find-only part of the query, we cannot reuse the results. The @@ -127,9 +131,10 @@ CandidatePlans MultiPlanner::finalizeExecutionPlans( if (!_cq.pipeline().empty()) { winner.root->close(); _yieldPolicy->clearRegisteredPlans(); - auto solution = QueryPlanner::extendWithAggPipeline(_cq, std::move(winner.solution)); + auto solution = QueryPlanner::extendWithAggPipeline( + _cq, std::move(winner.solution), _queryParams.secondaryCollectionsInfo); auto [rootStage, data] = stage_builder::buildSlotBasedExecutableTree( - _opCtx, _collection, _cq, *solution, _yieldPolicy); + _opCtx, _collections, _cq, *solution, _yieldPolicy); rootStage->prepare(data.ctx); candidates[winnerIdx] = sbe::plan_ranker::CandidatePlan{ std::move(solution), std::move(rootStage), std::move(data)}; @@ -140,10 +145,10 @@ CandidatePlans MultiPlanner::finalizeExecutionPlans( if (i == winnerIdx) continue; // have already done the winner - auto solution = - QueryPlanner::extendWithAggPipeline(_cq, std::move(candidates[i].solution)); + auto solution = QueryPlanner::extendWithAggPipeline( + _cq, std::move(candidates[i].solution), _queryParams.secondaryCollectionsInfo); auto&& [rootStage, data] = stage_builder::buildSlotBasedExecutableTree( - _opCtx, _collection, _cq, *solution, _yieldPolicy); + _opCtx, _collections, _cq, *solution, _yieldPolicy); candidates[i] = sbe::plan_ranker::CandidatePlan{ std::move(solution), std::move(rootStage), std::move(data)}; } diff --git a/src/mongo/db/query/sbe_multi_planner.h b/src/mongo/db/query/sbe_multi_planner.h index a9aa22bb462..cae5a027f1c 100644 --- a/src/mongo/db/query/sbe_multi_planner.h +++ b/src/mongo/db/query/sbe_multi_planner.h @@ -43,11 +43,12 @@ namespace mongo::sbe { class MultiPlanner final : public BaseRuntimePlanner { public: MultiPlanner(OperationContext* opCtx, - const CollectionPtr& collection, + const MultiCollection& collections, const CanonicalQuery& cq, + const QueryPlannerParams& qpp, PlanCachingMode cachingMode, PlanYieldPolicySBE* yieldPolicy) - : BaseRuntimePlanner{opCtx, collection, cq, yieldPolicy}, _cachingMode{cachingMode} {} + : BaseRuntimePlanner{opCtx, collections, cq, qpp, yieldPolicy}, _cachingMode{cachingMode} {} CandidatePlans plan( std::vector<std::unique_ptr<QuerySolution>> solutions, diff --git a/src/mongo/db/query/sbe_runtime_planner.h b/src/mongo/db/query/sbe_runtime_planner.h index 9f4b6805bfe..eef0a59acbc 100644 --- a/src/mongo/db/query/sbe_runtime_planner.h +++ b/src/mongo/db/query/sbe_runtime_planner.h @@ -33,6 +33,7 @@ #include "mongo/db/exec/sbe/stages/stages.h" #include "mongo/db/query/all_indices_required_checker.h" #include "mongo/db/query/canonical_query.h" +#include "mongo/db/query/multi_collection.h" #include "mongo/db/query/plan_yield_policy_sbe.h" #include "mongo/db/query/query_solution.h" #include "mongo/db/query/sbe_plan_ranker.h" @@ -76,14 +77,16 @@ public: class BaseRuntimePlanner : public RuntimePlanner { public: BaseRuntimePlanner(OperationContext* opCtx, - const CollectionPtr& collection, + const MultiCollection& collections, const CanonicalQuery& cq, + const QueryPlannerParams& queryParams, PlanYieldPolicySBE* yieldPolicy) : _opCtx(opCtx), - _collection(collection), + _collections(collections), _cq(cq), + _queryParams(queryParams), _yieldPolicy(yieldPolicy), - _indexExistenceChecker{collection} { + _indexExistenceChecker(collections.getMainCollection()) { invariant(_opCtx); } @@ -130,9 +133,13 @@ protected: size_t maxTrialPeriodNumReads); OperationContext* const _opCtx; - const CollectionPtr& _collection; + const MultiCollection& _collections; const CanonicalQuery& _cq; + const QueryPlannerParams _queryParams; PlanYieldPolicySBE* const _yieldPolicy; + + // TODO SERVER-62913: When support for indexed nested loop join is added, this member needs + // to be extended to support checking for index existence on multiple collections. const AllIndicesRequiredChecker _indexExistenceChecker; }; } // namespace mongo::sbe diff --git a/src/mongo/db/query/sbe_stage_builder.cpp b/src/mongo/db/query/sbe_stage_builder.cpp index c82baaa1235..51f107bd300 100644 --- a/src/mongo/db/query/sbe_stage_builder.cpp +++ b/src/mongo/db/query/sbe_stage_builder.cpp @@ -643,12 +643,13 @@ std::unique_ptr<fts::FTSMatcher> makeFtsMatcher(OperationContext* opCtx, } // namespace SlotBasedStageBuilder::SlotBasedStageBuilder(OperationContext* opCtx, - const CollectionPtr& collection, + const MultiCollection& collections, const CanonicalQuery& cq, const QuerySolution& solution, PlanYieldPolicySBE* yieldPolicy, ShardFiltererFactoryInterface* shardFiltererFactory) - : StageBuilder(opCtx, collection, cq, solution), + : StageBuilder(opCtx, cq, solution), + _collections(collections), _yieldPolicy(yieldPolicy), _data(makeRuntimeEnvironment(_cq, _opCtx, &_slotIdGenerator)), _shardFiltererFactory(shardFiltererFactory), @@ -712,8 +713,12 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder auto csn = static_cast<const CollectionScanNode*>(root); - auto [stage, outputs] = generateCollScan( - _state, _collection, csn, _yieldPolicy, reqs.getIsTailableCollScanResumeBranch()); + auto [stage, outputs] = + generateCollScan(_state, + _collections.lookupCollection(NamespaceString{csn->name}), + csn, + _yieldPolicy, + reqs.getIsTailableCollScanResumeBranch()); if (reqs.has(kReturnKey)) { // Assign the 'returnKeySlot' to be the empty object. @@ -828,8 +833,13 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder iamMap = nullptr; } - auto [stage, outputs] = generateIndexScan( - _state, _collection, ixn, indexKeyBitset, _yieldPolicy, iamMap, reqs.has(kIndexKeyPattern)); + auto [stage, outputs] = generateIndexScan(_state, + _collections.getMainCollection(), + ixn, + indexKeyBitset, + _yieldPolicy, + iamMap, + reqs.has(kIndexKeyPattern)); if (reqs.has(PlanStageSlots::kReturnKey)) { sbe::EExpression::Vector mkObjArgs; @@ -889,7 +899,7 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder } auto fieldSlotIds = _slotIdGenerator.generateMultiple(csn->fields.size()); - auto stage = std::make_unique<sbe::ColumnScanStage>(_collection->uuid(), + auto stage = std::make_unique<sbe::ColumnScanStage>(_collections.getMainCollection()->uuid(), csn->indexEntry.catalogName, fieldSlotIds, csn->fields, @@ -919,7 +929,7 @@ SlotBasedStageBuilder::makeLoopJoinForFetch(std::unique_ptr<sbe::PlanStage> inpu indexKeyCorruptionCheckCallback, std::bind(indexKeyConsistencyCheckCallback, _1, std::move(iamMap), _2, _3, _4, _5, _6)); // Scan the collection in the range [seekKeySlot, Inf). - auto scanStage = sbe::makeS<sbe::ScanStage>(_collection->uuid(), + auto scanStage = sbe::makeS<sbe::ScanStage>(_collections.getMainCollection()->uuid(), resultSlot, recordIdSlot, snapshotIdSlot, @@ -1850,7 +1860,8 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder::buildTextMatch( const QuerySolutionNode* root, const PlanStageReqs& reqs) { - tassert(5432212, "no collection object", _collection); + const auto& mainColl = _collections.getMainCollection(); + tassert(5432212, "no collection object", mainColl); tassert(5432213, "index keys requsted for text match node", !reqs.getIndexKeyBitset()); tassert(5432215, str::stream() << "text match node must have one child, but got " @@ -1869,7 +1880,7 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder // Create an FTS 'matcher' to apply 'ftsQuery' to matching documents. auto matcher = makeFtsMatcher( - _opCtx, _collection, textNode->index.identifier.catalogName, textNode->ftsQuery.get()); + _opCtx, mainColl, textNode->index.identifier.catalogName, textNode->ftsQuery.get()); // Build an 'ftsMatch' expression to match a document stored in the 'kResult' slot using the // 'matcher' instance. @@ -2631,6 +2642,24 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder return {std::move(outStage), std::move(outputs)}; } +std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder::buildLookup( + const QuerySolutionNode* root, const PlanStageReqs& reqs) { + const auto lookupStage = static_cast<const EqLookupNode*>(root); + switch (lookupStage->lookupStrategy) { + case EqLookupNode::LookupStrategy::kHashJoin: + uasserted(5842602, "$lookup planning logic picked hash join"); + break; + case EqLookupNode::LookupStrategy::kIndexedLoopJoin: + uasserted(5842603, "$lookup planning logic picked indexed loop join"); + break; + case EqLookupNode::LookupStrategy::kNestedLoopJoin: + uasserted(5842604, "$lookup planning logic picked nested loop join"); + break; + default: + MONGO_UNREACHABLE_TASSERT(5842605); + } + MONGO_UNREACHABLE_TASSERT(5842606); +} std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder::makeUnionForTailableCollScan(const QuerySolutionNode* root, const PlanStageReqs& reqs) { @@ -2994,6 +3023,7 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder {STAGE_AND_SORTED, &SlotBasedStageBuilder::buildAndSorted}, {STAGE_SORT_MERGE, &SlotBasedStageBuilder::buildSortMerge}, {STAGE_GROUP, &SlotBasedStageBuilder::buildGroup}, + {STAGE_EQ_LOOKUP, &SlotBasedStageBuilder::buildLookup}, {STAGE_SHARDING_FILTER, &SlotBasedStageBuilder::buildShardFilter}}; tassert(4822884, diff --git a/src/mongo/db/query/sbe_stage_builder.h b/src/mongo/db/query/sbe_stage_builder.h index 9efd2703504..c004f614046 100644 --- a/src/mongo/db/query/sbe_stage_builder.h +++ b/src/mongo/db/query/sbe_stage_builder.h @@ -34,6 +34,7 @@ #include "mongo/db/exec/sbe/values/slot.h" #include "mongo/db/exec/sbe/values/value.h" #include "mongo/db/exec/trial_period_utils.h" +#include "mongo/db/query/multi_collection.h" #include "mongo/db/query/plan_yield_policy_sbe.h" #include "mongo/db/query/sbe_stage_builder_helpers.h" #include "mongo/db/query/shard_filterer_factory_interface.h" @@ -324,7 +325,7 @@ public: static constexpr StringData kIndexKeyPattern = PlanStageSlots::kIndexKeyPattern; SlotBasedStageBuilder(OperationContext* opCtx, - const CollectionPtr& collection, + const MultiCollection& collections, const CanonicalQuery& cq, const QuerySolution& solution, PlanYieldPolicySBE* yieldPolicy, @@ -432,10 +433,15 @@ private: std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> buildGroup( const QuerySolutionNode* root, const PlanStageReqs& reqs); + std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> buildLookup( + const QuerySolutionNode* root, const PlanStageReqs& reqs); + sbe::value::SlotIdGenerator _slotIdGenerator; sbe::value::FrameIdGenerator _frameIdGenerator; sbe::value::SpoolIdGenerator _spoolIdGenerator; + const MultiCollection& _collections; + PlanYieldPolicySBE* const _yieldPolicy{nullptr}; // Apart from generating just an execution tree, this builder will also produce some auxiliary diff --git a/src/mongo/db/query/sbe_stage_builder_test_fixture.cpp b/src/mongo/db/query/sbe_stage_builder_test_fixture.cpp index 924c2fe68a1..758f94634f4 100644 --- a/src/mongo/db/query/sbe_stage_builder_test_fixture.cpp +++ b/src/mongo/db/query/sbe_stage_builder_test_fixture.cpp @@ -61,7 +61,7 @@ SbeStageBuilderTestFixture::buildPlanStage( ASSERT_OK(statusWithCQ.getStatus()); stage_builder::SlotBasedStageBuilder builder{opCtx(), - CollectionPtr::null, + MultiCollection(CollectionPtr::null), *statusWithCQ.getValue(), *querySolution, nullptr /* YieldPolicy */, diff --git a/src/mongo/db/query/sbe_sub_planner.cpp b/src/mongo/db/query/sbe_sub_planner.cpp index 964c8139ecb..e6830dc5622 100644 --- a/src/mongo/db/query/sbe_sub_planner.cpp +++ b/src/mongo/db/query/sbe_sub_planner.cpp @@ -46,12 +46,13 @@ CandidatePlans SubPlanner::plan( return plan_cache_key_factory::make<mongo::PlanCacheKey>(cq, coll); }; + const auto& mainColl = _collections.getMainCollection(); // Plan each branch of the $or. auto subplanningStatus = QueryPlanner::planSubqueries(_opCtx, - CollectionQueryInfo::get(_collection).getPlanCache(), + CollectionQueryInfo::get(mainColl).getPlanCache(), createPlanCacheKey, - _collection, + mainColl, _cq, _queryParams); if (!subplanningStatus.isOK()) { @@ -71,7 +72,7 @@ CandidatePlans SubPlanner::plan( std::vector<std::pair<std::unique_ptr<PlanStage>, stage_builder::PlanStageData>> roots; for (auto&& solution : solutions) { roots.push_back(stage_builder::buildSlotBasedExecutableTree( - _opCtx, _collection, *cq, *solution, _yieldPolicy)); + _opCtx, _collections, *cq, *solution, _yieldPolicy)); } // Clear any plans registered to yield once multiplanning is done for this branch. We don't @@ -83,7 +84,7 @@ CandidatePlans SubPlanner::plan( // not use the 'CachedSolutionPlanner' eviction mechanism. We therefore are more // conservative about putting a potentially bad plan into the cache in the subplan path. MultiPlanner multiPlanner{ - _opCtx, _collection, *cq, PlanCachingMode::SometimesCache, _yieldPolicy}; + _opCtx, _collections, *cq, _queryParams, PlanCachingMode::SometimesCache, _yieldPolicy}; auto&& [candidates, winnerIdx] = multiPlanner.plan(std::move(solutions), std::move(roots)); invariant(winnerIdx < candidates.size()); return std::move(candidates[winnerIdx].solution); @@ -111,11 +112,12 @@ CandidatePlans SubPlanner::plan( // If some agg pipeline stages are being pushed down, extend the solution with them. if (!_cq.pipeline().empty()) { - compositeSolution = QueryPlanner::extendWithAggPipeline(_cq, std::move(compositeSolution)); + compositeSolution = QueryPlanner::extendWithAggPipeline( + _cq, std::move(compositeSolution), _queryParams.secondaryCollectionsInfo); } auto&& [root, data] = stage_builder::buildSlotBasedExecutableTree( - _opCtx, _collection, _cq, *compositeSolution, _yieldPolicy); + _opCtx, _collections, _cq, *compositeSolution, _yieldPolicy); auto status = prepareExecutionPlan(root.get(), &data); uassertStatusOK(status); auto [result, recordId, exitedEarly] = status.getValue(); @@ -134,11 +136,12 @@ CandidatePlans SubPlanner::planWholeQuery() const { if (solutions.size() == 1) { // If some agg pipeline stages are being pushed down, extend the solution with them. if (!_cq.pipeline().empty()) { - solutions[0] = QueryPlanner::extendWithAggPipeline(_cq, std::move(solutions[0])); + solutions[0] = QueryPlanner::extendWithAggPipeline( + _cq, std::move(solutions[0]), _queryParams.secondaryCollectionsInfo); } auto&& [root, data] = stage_builder::buildSlotBasedExecutableTree( - _opCtx, _collection, _cq, *solutions[0], _yieldPolicy); + _opCtx, _collections, _cq, *solutions[0], _yieldPolicy); auto status = prepareExecutionPlan(root.get(), &data); uassertStatusOK(status); auto [result, recordId, exitedEarly] = status.getValue(); @@ -154,10 +157,11 @@ CandidatePlans SubPlanner::planWholeQuery() const { std::vector<std::pair<std::unique_ptr<PlanStage>, stage_builder::PlanStageData>> roots; for (auto&& solution : solutions) { roots.push_back(stage_builder::buildSlotBasedExecutableTree( - _opCtx, _collection, _cq, *solution, _yieldPolicy)); + _opCtx, _collections, _cq, *solution, _yieldPolicy)); } - MultiPlanner multiPlanner{_opCtx, _collection, _cq, PlanCachingMode::AlwaysCache, _yieldPolicy}; + MultiPlanner multiPlanner{ + _opCtx, _collections, _cq, _queryParams, PlanCachingMode::AlwaysCache, _yieldPolicy}; return multiPlanner.plan(std::move(solutions), std::move(roots)); } } // namespace mongo::sbe diff --git a/src/mongo/db/query/sbe_sub_planner.h b/src/mongo/db/query/sbe_sub_planner.h index e9c03d3db4b..b81881006d3 100644 --- a/src/mongo/db/query/sbe_sub_planner.h +++ b/src/mongo/db/query/sbe_sub_planner.h @@ -45,11 +45,11 @@ namespace mongo::sbe { class SubPlanner final : public BaseRuntimePlanner { public: SubPlanner(OperationContext* opCtx, - const CollectionPtr& collection, + const MultiCollection& collections, const CanonicalQuery& cq, const QueryPlannerParams& queryParams, PlanYieldPolicySBE* yieldPolicy) - : BaseRuntimePlanner{opCtx, collection, cq, yieldPolicy}, _queryParams{queryParams} {} + : BaseRuntimePlanner{opCtx, collections, cq, queryParams, yieldPolicy} {} CandidatePlans plan( std::vector<std::unique_ptr<QuerySolution>> solutions, @@ -58,8 +58,5 @@ public: private: CandidatePlans planWholeQuery() const; - - // Query parameters used to create a query solution for each $or branch. - const QueryPlannerParams _queryParams; }; } // namespace mongo::sbe diff --git a/src/mongo/db/query/stage_builder.h b/src/mongo/db/query/stage_builder.h index 7e20618cd83..41b45c748b6 100644 --- a/src/mongo/db/query/stage_builder.h +++ b/src/mongo/db/query/stage_builder.h @@ -40,11 +40,8 @@ namespace mongo::stage_builder { template <typename PlanStageType> class StageBuilder { public: - StageBuilder(OperationContext* opCtx, - const CollectionPtr& collection, - const CanonicalQuery& cq, - const QuerySolution& solution) - : _opCtx(opCtx), _collection(collection), _cq(cq), _solution(solution) {} + StageBuilder(OperationContext* opCtx, const CanonicalQuery& cq, const QuerySolution& solution) + : _opCtx(opCtx), _cq(cq), _solution(solution) {} virtual ~StageBuilder() = default; @@ -56,7 +53,6 @@ public: protected: OperationContext* _opCtx; - const CollectionPtr& _collection; const CanonicalQuery& _cq; const QuerySolution& _solution; }; diff --git a/src/mongo/db/query/stage_builder_util.cpp b/src/mongo/db/query/stage_builder_util.cpp index e9d92bbd6c2..ed41d080153 100644 --- a/src/mongo/db/query/stage_builder_util.cpp +++ b/src/mongo/db/query/stage_builder_util.cpp @@ -55,7 +55,7 @@ std::unique_ptr<PlanStage> buildClassicExecutableTree(OperationContext* opCtx, std::pair<std::unique_ptr<sbe::PlanStage>, stage_builder::PlanStageData> buildSlotBasedExecutableTree(OperationContext* opCtx, - const CollectionPtr& collection, + const MultiCollection& collections, const CanonicalQuery& cq, const QuerySolution& solution, PlanYieldPolicy* yieldPolicy) { @@ -69,10 +69,11 @@ buildSlotBasedExecutableTree(OperationContext* opCtx, auto sbeYieldPolicy = dynamic_cast<PlanYieldPolicySBE*>(yieldPolicy); invariant(sbeYieldPolicy); - auto shardFilterer = std::make_unique<ShardFiltererFactoryImpl>(collection); + auto shardFilterer = + std::make_unique<ShardFiltererFactoryImpl>(collections.getMainCollection()); auto builder = std::make_unique<SlotBasedStageBuilder>( - opCtx, collection, cq, solution, sbeYieldPolicy, shardFilterer.get()); + opCtx, collections, cq, solution, sbeYieldPolicy, shardFilterer.get()); auto root = builder->build(solution.root()); auto data = builder->getPlanStageData(); diff --git a/src/mongo/db/query/stage_builder_util.h b/src/mongo/db/query/stage_builder_util.h index b04e533a20e..c16c04fd99b 100644 --- a/src/mongo/db/query/stage_builder_util.h +++ b/src/mongo/db/query/stage_builder_util.h @@ -52,7 +52,7 @@ std::unique_ptr<PlanStage> buildClassicExecutableTree(OperationContext* opCtx, std::pair<std::unique_ptr<sbe::PlanStage>, stage_builder::PlanStageData> buildSlotBasedExecutableTree(OperationContext* opCtx, - const CollectionPtr& collection, + const MultiCollection& collections, const CanonicalQuery& cq, const QuerySolution& solution, PlanYieldPolicy* yieldPolicy); |