SERVER-117423 Factor out common get_executor logic into new files (#47189)

GitOrigin-RevId: cb3802eb34492a3342080ed6842c3fbfeb54752a
This commit is contained in:
Matthew Boros 2026-02-04 00:33:10 -05:00 committed by MongoDB Bot
parent 8e914b420d
commit 7a1cf68baa
12 changed files with 830 additions and 330 deletions

1
.github/CODEOWNERS vendored
View File

@ -2591,6 +2591,7 @@ WORKSPACE.bazel @10gen/devprod-build @svc-auto-approve-bot
/src/mongo/db/query/**/count_request* @10gen/query-optimization @svc-auto-approve-bot
/src/mongo/db/query/**/dbref.h @10gen/query-optimization @svc-auto-approve-bot
/src/mongo/db/query/**/distinct_access* @10gen/query-optimization @svc-auto-approve-bot
/src/mongo/db/query/**/engine_selection* @10gen/query-execution @svc-auto-approve-bot
/src/mongo/db/query/**/eof_node_type* @10gen/query-optimization @svc-auto-approve-bot
/src/mongo/db/query/**/explain* @10gen/query-optimization @svc-auto-approve-bot
/src/mongo/db/query/**/expression_walker* @10gen/query-optimization @svc-auto-approve-bot

View File

@ -871,9 +871,12 @@ mongo_cc_library(
"//src/mongo/db/pipeline:plan_explainer_pipeline.cpp",
"//src/mongo/db/pipeline:sbe_pushdown.cpp",
"//src/mongo/db/query:all_indices_required_checker.cpp",
"//src/mongo/db/query:engine_selection.cpp",
"//src/mongo/db/query:explain.cpp",
"//src/mongo/db/query:find.cpp",
"//src/mongo/db/query:get_executor.cpp",
"//src/mongo/db/query:get_executor_fast_paths.cpp",
"//src/mongo/db/query:get_executor_helpers.cpp",
"//src/mongo/db/query:internal_plans.cpp",
"//src/mongo/db/query:plan_executor_factory.cpp",
"//src/mongo/db/query:plan_executor_impl.cpp",

View File

@ -49,6 +49,9 @@ filters:
- "distinct_access*":
approvers:
- 10gen/query-optimization
- "engine_selection*":
approvers:
- 10gen/query-execution
- "eof_node_type*":
approvers:
- 10gen/query-optimization

View File

@ -0,0 +1,226 @@
/**
* Copyright (C) 2026-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/db/query/engine_selection.h"
#include <boost/container/flat_set.hpp>
#include <boost/container/small_vector.hpp>
#include <boost/container/vector.hpp>
#include <boost/cstdint.hpp>
#include <boost/none.hpp>
#include <boost/optional.hpp>
#include <boost/optional/optional.hpp>
#include <boost/smart_ptr/intrusive_ptr.hpp>
// IWYU pragma: no_include "ext/alloc_traits.h"
#include "mongo/db/exec/classic/delete_stage.h"
#include "mongo/db/pipeline/document_source_lookup.h"
#include "mongo/db/pipeline/sbe_pushdown.h"
#include "mongo/db/query/canonical_query.h"
#include "mongo/db/query/plan_cache/plan_cache.h"
#include "mongo/db/query/query_utils.h"
#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kQuery
namespace mongo {
namespace {
/**
* Returns true iff 'descriptor' has fields A and B where all of the following hold
*
* - A is a path prefix of B
* - A is a hashed field in the index
* - B is a non-hashed field in the index
*
* TODO SERVER-99889 this is a workaround for an SBE stage builder bug.
*/
bool indexHasHashedPathPrefixOfNonHashedPath(const IndexDescriptor* descriptor) {
boost::optional<StringData> hashedPath;
for (const auto& elt : descriptor->keyPattern()) {
if (elt.valueStringDataSafe() == "hashed") {
// Indexes may only contain one hashed field.
hashedPath = elt.fieldNameStringData();
break;
}
}
if (hashedPath == boost::none) {
// No hashed fields in the index.
return false;
}
// Check if 'hashedPath' is a path prefix for any field in the index.
for (const auto& elt : descriptor->keyPattern()) {
if (expression::isPathPrefixOf(hashedPath.get(), elt.fieldNameStringData())) {
return true;
}
}
return false;
}
/**
* Returns true if 'collection' has an index that contains two fields, one of which is a path prefix
* of the other, where the prefix field is hashed. Indexes can only contain one hashed field.
*
* TODO SERVER-99889: At the time of writing, there is a bug in the SBE stage builders that
* constructs ExpressionFieldPaths over hashed values. This leads to wrong query results.
*
* The bug arises for covered index scans where a path P is a non-hashed path in the index and a
* strict prefix P' of P is a hashed path in the index.
*/
bool collectionHasIndexWithHashedPathPrefixOfNonHashedPath(const CollectionPtr& collection,
ExpressionContext* expCtx) {
const IndexCatalog* indexCatalog = collection->getIndexCatalog();
tassert(10230200, "'CollectionPtr' does not have an 'IndexCatalog'", indexCatalog);
OperationContext* opCtx = expCtx->getOperationContext();
tassert(10230201, "'ExpressionContext' does not have an 'OperationContext'", opCtx);
std::unique_ptr<IndexCatalog::IndexIterator> indexIter =
indexCatalog->getIndexIterator(IndexCatalog::InclusionPolicy::kReady);
while (indexIter->more()) {
const IndexCatalogEntry* entry = indexIter->next();
if (indexHasHashedPathPrefixOfNonHashedPath(entry->descriptor())) {
return true;
}
}
return false;
}
/**
* Checks if the given query can be executed with the SBE engine based on the canonical query.
*
* This method determines whether the query may be compatible with SBE based only on high-level
* information from the canonical query, before query planning has taken place (such as ineligible
* expressions or collections).
*
* If this method returns true, query planning should be done, followed by another layer of
* validation to make sure the query plan can be executed with SBE. If it returns false, SBE query
* planning can be short-circuited as it is already known that the query is ineligible for SBE.
*/
bool isQuerySbeCompatible(const CollectionPtr& collection, const CanonicalQuery& cq) {
auto expCtx = cq.getExpCtxRaw();
// If we don't support all expressions used or the query is eligible for IDHack, don't use SBE.
if (!expCtx || expCtx->getSbeCompatibility() == SbeCompatibility::notCompatible ||
expCtx->getSbePipelineCompatibility() == SbeCompatibility::notCompatible ||
(collection && isIdHackEligibleQuery(collection, cq))) {
return false;
}
const auto* proj = cq.getProj();
if (proj && (proj->requiresMatchDetails() || proj->containsElemMatch())) {
return false;
}
// Tailable and resumed scans are not supported either.
if (expCtx->isTailable() || cq.getFindCommandRequest().getRequestResumeToken()) {
return false;
}
const auto& nss = cq.nss();
const auto isTimeseriesColl = collection && collection->isTimeseriesCollection();
auto& queryKnob = cq.getExpCtx()->getQueryKnobConfiguration();
if ((!feature_flags::gFeatureFlagTimeSeriesInSbe.isEnabled() ||
queryKnob.getSbeDisableTimeSeriesForOp()) &&
isTimeseriesColl) {
return false;
}
// Queries against the oplog are not supported. Also queries on the inner side of a $lookup are
// not considered for SBE except search queries.
if ((expCtx->getInLookup() && !cq.isSearchQuery()) || nss.isOplog() ||
!cq.metadataDeps().none()) {
return false;
}
// Queries against collections with a particular shape of compound hashed indexes are not
// supported.
if (collection && collectionHasIndexWithHashedPathPrefixOfNonHashedPath(collection, expCtx)) {
return false;
}
// Find and aggregate queries with the $_startAt parameter are not supported in SBE.
if (!cq.getFindCommandRequest().getStartAt().isEmpty()) {
return false;
}
const auto& sortPattern = cq.getSortPattern();
return !sortPattern || isSortSbeCompatible(*sortPattern);
}
/**
* Function which returns true if 'cq' uses features that are currently supported in SBE without
* 'featureFlagSbeFull' being set; false otherwise.
*/
bool shouldUseRegularSbe(OperationContext* opCtx,
const CanonicalQuery& cq,
const CollectionPtr& mainCollection,
const bool sbeFull) {
// When featureFlagSbeFull is not enabled, we cannot use SBE unless 'trySbeEngine' is enabled or
// if 'trySbeRestricted' is enabled, and we have eligible pushed down stages in the cq pipeline.
auto& queryKnob = cq.getExpCtx()->getQueryKnobConfiguration();
if (!queryKnob.canPushDownFullyCompatibleStages() && cq.cqPipeline().empty()) {
return false;
}
if (mainCollection && mainCollection->isTimeseriesCollection() && cq.cqPipeline().empty()) {
// TS queries only use SBE when there's a pipeline.
return false;
}
// Return true if all the expressions in the CanonicalQuery's filter and projection are SBE
// compatible.
SbeCompatibility minRequiredCompatibility =
getMinRequiredSbeCompatibility(queryKnob.getInternalQueryFrameworkControlForOp(), sbeFull);
return cq.getExpCtx()->getSbeCompatibility() >= minRequiredCompatibility;
}
} // namespace
bool useSbe(OperationContext* opCtx,
const MultipleCollectionAccessor& collections,
CanonicalQuery* cq,
Pipeline* pipeline,
bool needsMerge,
std::unique_ptr<QueryPlannerParams> plannerParams) {
const auto& mainColl = collections.getMainCollection();
const bool forceClassic =
cq->getExpCtx()->getQueryKnobConfiguration().isForceClassicEngineEnabled();
if (forceClassic || !isQuerySbeCompatible(mainColl, *cq)) {
return false;
}
// Add the stages that are candidates for SBE lowering from the 'pipeline' into the
// 'canonicalQuery'. This must be done _before_ checking shouldUseRegularSbe() or
// creating the planner.
attachPipelineStages(collections, pipeline, needsMerge, cq, std::move(plannerParams));
const bool sbeFull = feature_flags::gFeatureFlagSbeFull.isEnabled();
return sbeFull || shouldUseRegularSbe(opCtx, *cq, mainColl, sbeFull);
}
} // namespace mongo

View File

@ -0,0 +1,58 @@
/**
* Copyright (C) 2026-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#pragma once
#include "mongo/db/exec/classic/delete_stage.h"
#include "mongo/db/query/canonical_query.h"
#include "mongo/db/query/compiler/physical_model/query_solution/query_solution.h"
#include "mongo/db/query/query_planner_params.h"
#include "mongo/db/query/write_ops/canonical_update.h"
#include "mongo/db/query/write_ops/parsed_delete.h"
#include "mongo/db/shard_role/shard_catalog/index_catalog_entry.h"
#include "mongo/util/modules.h"
#include <boost/none.hpp>
#include <boost/optional/optional.hpp>
#include <boost/smart_ptr/intrusive_ptr.hpp>
namespace mongo {
/*
* Returns true if SBE should be used given the query details. An optional query solution may be
* passed in, which will be analyzed for SBE eligibility depending on the plan shape.
*/
bool useSbe(OperationContext* opCtx,
const MultipleCollectionAccessor& collections,
CanonicalQuery* cq,
Pipeline* pipeline,
bool needsMerge,
std::unique_ptr<QueryPlannerParams> plannerParams);
} // namespace mongo

View File

@ -75,7 +75,10 @@
#include "mongo/db/query/compiler/parsers/matcher/expression_parser.h"
#include "mongo/db/query/compiler/physical_model/query_solution/eof_node_type.h"
#include "mongo/db/query/distinct_access.h"
#include "mongo/db/query/engine_selection.h"
#include "mongo/db/query/find_command.h"
#include "mongo/db/query/get_executor_fast_paths.h"
#include "mongo/db/query/get_executor_helpers.h"
#include "mongo/db/query/internal_plans.h"
#include "mongo/db/query/plan_cache/classic_plan_cache.h"
#include "mongo/db/query/plan_cache/plan_cache.h"
@ -147,28 +150,6 @@ boost::intrusive_ptr<ExpressionContext> makeExpressionContextForGetExecutor(
}
namespace {
/**
* Struct to hold information about a query plan's cache info.
*/
struct PlanCacheInfo {
boost::optional<uint32_t> planCacheKey;
boost::optional<uint32_t> planCacheShapeHash;
};
/**
* Fills in the given information on the CurOp::OpDebug object, if it has not already been filled in
* by an outer pipeline.
*/
void setOpDebugPlanCacheInfo(OperationContext* opCtx, const PlanCacheInfo& cacheInfo) {
OpDebug& opDebug = CurOp::get(opCtx)->debug();
if (!opDebug.planCacheShapeHash && cacheInfo.planCacheShapeHash) {
opDebug.planCacheShapeHash = *cacheInfo.planCacheShapeHash;
}
if (!opDebug.planCacheKey && cacheInfo.planCacheKey) {
opDebug.planCacheKey = *cacheInfo.planCacheKey;
}
}
/**
* A class to hold the result of preparation of the query to be executed using SBE engine. This
* result stores and provides the following information:
@ -771,25 +752,13 @@ private:
}
std::unique_ptr<ClassicRuntimePlannerResult> buildIdHackPlan() final {
const auto& mainCollection = getCollections().getMainCollection();
if (!isIdHackEligibleQuery(mainCollection, *_cq)) {
auto idHackPlanner =
tryIdHack(_opCtx, getCollections(), _cq, [this]() { return makePlannerData(); });
if (!idHackPlanner) {
return nullptr;
}
const auto indexEntry = mainCollection->getIndexCatalog()->findIdIndex(_opCtx);
if (!indexEntry) {
return nullptr;
}
LOGV2_DEBUG(20922,
2,
"Using classic engine idhack",
"canonicalQuery"_attr = redact(_queryStringForDebugLog));
planCacheCounters.incrementClassicSkippedCounter();
fastPathQueryCounters.incrementIdHackQueryCounter();
auto result = releaseResult();
result->runtimePlanner =
std::make_unique<crp_classic::IdHackPlanner>(makePlannerData(), indexEntry);
result->runtimePlanner = std::move(idHackPlanner);
return result;
}
@ -1187,33 +1156,6 @@ std::unique_ptr<PlannerInterface> getClassicPlannerForSbe(
return std::move(planningResult->runtimePlanner);
}
/**
* Function which returns true if 'cq' uses features that are currently supported in SBE without
* 'featureFlagSbeFull' being set; false otherwise.
*/
bool shouldUseRegularSbe(OperationContext* opCtx,
const CanonicalQuery& cq,
const CollectionPtr& mainCollection,
const bool sbeFull) {
// When featureFlagSbeFull is not enabled, we cannot use SBE unless 'trySbeEngine' is enabled or
// if 'trySbeRestricted' is enabled, and we have eligible pushed down stages in the cq pipeline.
auto& queryKnob = cq.getExpCtx()->getQueryKnobConfiguration();
if (!queryKnob.canPushDownFullyCompatibleStages() && cq.cqPipeline().empty()) {
return false;
}
if (mainCollection && mainCollection->isTimeseriesCollection() && cq.cqPipeline().empty()) {
// TS queries only use SBE when there's a pipeline.
return false;
}
// Return true if all the expressions in the CanonicalQuery's filter and projection are SBE
// compatible.
SbeCompatibility minRequiredCompatibility =
getMinRequiredSbeCompatibility(queryKnob.getInternalQueryFrameworkControlForOp(), sbeFull);
return cq.getExpCtx()->getSbeCompatibility() >= minRequiredCompatibility;
}
bool shouldUseSbePlanCache(const QueryPlannerParams& params) {
// The logic in this funtion depends on the fact that we clear the SBE plan cache on index
// creation.
@ -1233,148 +1175,8 @@ bool shouldUseSbePlanCache(const QueryPlannerParams& params) {
}
return true;
}
boost::optional<ScopedCollectionFilter> getScopedCollectionFilter(
OperationContext* opCtx,
const MultipleCollectionAccessor& collections,
const QueryPlannerParams& plannerParams) {
if (plannerParams.mainCollectionInfo.options & QueryPlannerParams::INCLUDE_SHARD_FILTER) {
auto collFilter = collections.getMainCollectionPtrOrAcquisition().getShardingFilter();
tassert(11321302,
"Attempting to use shard filter when there's no shard filter available for "
"the collection",
collFilter);
return collFilter;
}
return boost::none;
}
} // namespace
/**
* Returns true iff 'descriptor' has fields A and B where all of the following hold
*
* - A is a path prefix of B
* - A is a hashed field in the index
* - B is a non-hashed field in the index
*
* TODO SERVER-99889 this is a workaround for an SBE stage builder bug.
*/
bool indexHasHashedPathPrefixOfNonHashedPath(const IndexDescriptor* descriptor) {
boost::optional<StringData> hashedPath;
for (const auto& elt : descriptor->keyPattern()) {
if (elt.valueStringDataSafe() == "hashed") {
// Indexes may only contain one hashed field.
hashedPath = elt.fieldNameStringData();
break;
}
}
if (hashedPath == boost::none) {
// No hashed fields in the index.
return false;
}
// Check if 'hashedPath' is a path prefix for any field in the index.
for (const auto& elt : descriptor->keyPattern()) {
if (expression::isPathPrefixOf(hashedPath.get(), elt.fieldNameStringData())) {
return true;
}
}
return false;
}
/**
* Returns true if 'collection' has an index that contains two fields, one of which is a path prefix
* of the other, where the prefix field is hashed. Indexes can only contain one hashed field.
*
* TODO SERVER-99889: At the time of writing, there is a bug in the SBE stage builders that
* constructs ExpressionFieldPaths over hashed values. This leads to wrong query results.
*
* The bug arises for covered index scans where a path P is a non-hashed path in the index and a
* strict prefix P' of P is a hashed path in the index.
*/
bool collectionHasIndexWithHashedPathPrefixOfNonHashedPath(const CollectionPtr& collection,
ExpressionContext* expCtx) {
const IndexCatalog* indexCatalog = collection->getIndexCatalog();
tassert(10230200, "'CollectionPtr' does not have an 'IndexCatalog'", indexCatalog);
OperationContext* opCtx = expCtx->getOperationContext();
tassert(10230201, "'ExpressionContext' does not have an 'OperationContext'", opCtx);
std::unique_ptr<IndexCatalog::IndexIterator> indexIter =
indexCatalog->getIndexIterator(IndexCatalog::InclusionPolicy::kReady);
while (indexIter->more()) {
const IndexCatalogEntry* entry = indexIter->next();
if (indexHasHashedPathPrefixOfNonHashedPath(entry->descriptor())) {
return true;
}
}
return false;
}
/**
* Checks if the given query can be executed with the SBE engine based on the canonical query.
*
* This method determines whether the query may be compatible with SBE based only on high-level
* information from the canonical query, before query planning has taken place (such as ineligible
* expressions or collections).
*
* If this method returns true, query planning should be done, followed by another layer of
* validation to make sure the query plan can be executed with SBE. If it returns false, SBE query
* planning can be short-circuited as it is already known that the query is ineligible for SBE.
*/
bool isQuerySbeCompatible(const CollectionPtr& collection, const CanonicalQuery& cq) {
auto expCtx = cq.getExpCtxRaw();
// If we don't support all expressions used or the query is eligible for IDHack, don't use SBE.
if (!expCtx || expCtx->getSbeCompatibility() == SbeCompatibility::notCompatible ||
expCtx->getSbePipelineCompatibility() == SbeCompatibility::notCompatible ||
(collection && isIdHackEligibleQuery(collection, cq))) {
return false;
}
const auto* proj = cq.getProj();
if (proj && (proj->requiresMatchDetails() || proj->containsElemMatch())) {
return false;
}
// Tailable and resumed scans are not supported either.
if (expCtx->isTailable() || cq.getFindCommandRequest().getRequestResumeToken()) {
return false;
}
const auto& nss = cq.nss();
const auto isTimeseriesColl = collection && collection->isTimeseriesCollection();
auto& queryKnob = cq.getExpCtx()->getQueryKnobConfiguration();
if ((!feature_flags::gFeatureFlagTimeSeriesInSbe.isEnabled() ||
queryKnob.getSbeDisableTimeSeriesForOp()) &&
isTimeseriesColl) {
return false;
}
// Queries against the oplog are not supported. Also queries on the inner side of a $lookup are
// not considered for SBE except search queries.
if ((expCtx->getInLookup() && !cq.isSearchQuery()) || nss.isOplog() ||
!cq.metadataDeps().none()) {
return false;
}
// Queries against collections with a particular shape of compound hashed indexes are not
// supported.
if (collection && collectionHasIndexWithHashedPathPrefixOfNonHashedPath(collection, expCtx)) {
return false;
}
// Find and aggregate queries with the $_startAt parameter are not supported in SBE.
if (!cq.getFindCommandRequest().getStartAt().isEmpty()) {
return false;
}
const auto& sortPattern = cq.getSortPattern();
return !sortPattern || isSortSbeCompatible(*sortPattern);
}
StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorFind(
OperationContext* opCtx,
const MultipleCollectionAccessor& collections,
@ -1412,55 +1214,15 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorFind
CurOp::get(opCtx)->stopQueryPlanningTimer();
});
// First try to use the express id point query fast path.
const auto& mainColl = collections.getMainCollection();
const auto expressEligibility = isExpressEligible(opCtx, mainColl, *canonicalQuery);
if (expressEligibility == ExpressEligibility::IdPointQueryEligible) {
planCacheCounters.incrementClassicSkippedCounter();
auto plannerParams =
std::make_unique<QueryPlannerParams>(QueryPlannerParams::ArgsForExpress{
opCtx, *canonicalQuery, collections, plannerOptions});
auto collectionFilter = getScopedCollectionFilter(opCtx, collections, *plannerParams);
const bool isClusteredOnId = plannerParams->clusteredInfo
? clustered_util::isClusteredOnId(plannerParams->clusteredInfo)
: false;
auto expressExecutor = isClusteredOnId
? makeExpressExecutorForFindByClusteredId(
opCtx,
std::move(canonicalQuery),
collections.getMainCollectionPtrOrAcquisition(),
std::move(collectionFilter),
plannerOptions & QueryPlannerParams::RETURN_OWNED_DATA)
: makeExpressExecutorForFindById(opCtx,
std::move(canonicalQuery),
collections.getMainCollectionPtrOrAcquisition(),
std::move(collectionFilter),
plannerOptions &
QueryPlannerParams::RETURN_OWNED_DATA);
return std::move(expressExecutor);
}
// The query might still be eligible for express execution via the index equality fast path.
// However, that requires the full set of planner parameters for the main collection to be
// available and creating those now allows them to be reused for subsequent strategies if
// the express index equality one fails.
auto paramsForSingleCollectionQuery = makeQueryPlannerParams(plannerOptions);
if (expressEligibility == ExpressEligibility::IndexedEqualityEligible) {
if (auto indexEntry =
getIndexForExpressEquality(*canonicalQuery, *paramsForSingleCollectionQuery)) {
auto expressExecutor = makeExpressExecutorForFindByUserIndex(
opCtx,
std::move(canonicalQuery),
collections.getMainCollectionPtrOrAcquisition(),
*indexEntry,
getScopedCollectionFilter(opCtx, collections, *paramsForSingleCollectionQuery),
plannerOptions & QueryPlannerParams::RETURN_OWNED_DATA);
return std::move(expressExecutor);
}
auto expressResult =
tryExpress(opCtx, collections, canonicalQuery, plannerOptions, makeQueryPlannerParams);
if (expressResult.executor) {
return std::move(expressResult.executor);
}
// If no express executor was returned, we can reuse the planner params created by `tryExpress`
// for other planning logic.
tassert(11742300, "Expected planner params to be initialized.", expressResult.plannerParams);
auto paramsForSingleCollectionQuery = std::move(expressResult.plannerParams);
// Initialize path arrayness in ExpressionContext from the CollectionQueryInfo.
// Do not invoke if it has been already initialized.
@ -1471,29 +1233,18 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorFind
CollectionQueryInfo::get(collection).getPathArrayness());
}
const bool useSbeEngine = [&] {
const bool forceClassic =
canonicalQuery->getExpCtx()->getQueryKnobConfiguration().isForceClassicEngineEnabled();
if (forceClassic || !isQuerySbeCompatible(mainColl, *canonicalQuery)) {
return false;
}
// Add the stages that are candidates for SBE lowering from the 'pipeline' into the
// 'canonicalQuery'. This must be done _before_ checking shouldUseRegularSbe() or
// creating the planner.
auto plannerParams =
std::make_unique<QueryPlannerParams>(QueryPlannerParams::ArgsForPushDownStagesDecision{
.opCtx = opCtx,
.canonicalQuery = *canonicalQuery,
.collections = collections,
.plannerOptions = plannerOptions,
});
attachPipelineStages(
collections, pipeline, needsMerge, canonicalQuery.get(), std::move(plannerParams));
const bool sbeFull = feature_flags::gFeatureFlagSbeFull.isEnabled();
return sbeFull || shouldUseRegularSbe(opCtx, *canonicalQuery, mainColl, sbeFull);
}();
const bool useSbeEngine = useSbe(
opCtx,
collections,
canonicalQuery.get(),
pipeline,
needsMerge,
std::make_unique<QueryPlannerParams>(QueryPlannerParams::ArgsForPushDownStagesDecision{
.opCtx = opCtx,
.canonicalQuery = *canonicalQuery,
.collections = collections,
.plannerOptions = plannerOptions,
}));
// If distinct multi-planning is enabled and we have a distinct property, we may not be able to
// commit to SBE yet.
@ -1514,7 +1265,6 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorFind
finalizePipelineStages(pipeline, canonicalQuery.get());
}
auto makePlanner = [&](std::unique_ptr<QueryPlannerParams> plannerParams)
-> std::unique_ptr<PlannerInterface> {
// If we have a distinct, we might get a better plan using classic and DISTINCT_SCAN than
@ -1523,7 +1273,7 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorFind
plannerParams->fillOutSecondaryCollectionsPlannerParams(
opCtx, *canonicalQuery, collections);
plannerParams->setTargetSbeStageBuilder(opCtx, *canonicalQuery, collections);
plannerParams->setTargetSbeStageBuilder(*canonicalQuery, collections);
if (shouldUseSbePlanCache(*plannerParams)) {
canonicalQuery->setUsingSbePlanCache(true);
@ -1555,54 +1305,12 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutorFind
opCtx, collections, canonicalQuery.get(), yieldPolicy, std::move(plannerParams));
};
auto planner = [&] {
static constexpr size_t kMaxIterations = 5;
for (size_t iter = 0; iter < kMaxIterations; ++iter) {
try {
// First try the single collection query parameters, as these would have been
// generated with query settings if present.
return makePlanner(std::move(paramsForSingleCollectionQuery));
} catch (const ExceptionFor<ErrorCodes::NoDistinctScansForDistinctEligibleQuery>&) {
// The planner failed to generate a DISTINCT_SCAN for a distinct-like query. Remove
// the distinct property and replan using SBE or subplanning as applicable.
canonicalQuery->resetDistinct();
if (canonicalQuery->isSbeCompatible()) {
// Stages still need to be finalized for SBE since classic was used previously.
finalizePipelineStages(pipeline, canonicalQuery.get());
}
return makePlanner(makeQueryPlannerParams(plannerOptions));
} catch (const ExceptionFor<ErrorCodes::NoQueryExecutionPlans>& exception) {
// The planner failed to generate a viable plan. Remove the query settings and
// retry if any are present. Otherwise just propagate the exception.
const auto& querySettings = canonicalQuery->getExpCtx()->getQuerySettings();
const bool hasQuerySettings = querySettings.getIndexHints().has_value();
// Planning has been tried without query settings and no execution plan was found.
const bool ignoreQuerySettings =
plannerOptions & QueryPlannerParams::IGNORE_QUERY_SETTINGS;
if (!hasQuerySettings || ignoreQuerySettings) {
throw;
}
LOGV2_DEBUG(
8524200,
2,
"Encountered planning error while running with query settings. Retrying "
"without query settings.",
"query"_attr = redact(canonicalQuery->toStringForErrorMsg()),
"querySettings"_attr = querySettings,
"reason"_attr = exception.reason(),
"code"_attr = exception.codeString());
plannerOptions |= QueryPlannerParams::IGNORE_QUERY_SETTINGS;
// Propagate the params to the next iteration.
paramsForSingleCollectionQuery = makeQueryPlannerParams(plannerOptions);
} catch (const ExceptionFor<ErrorCodes::RetryMultiPlanning>&) {
// Propagate the params to the next iteration.
paramsForSingleCollectionQuery = makeQueryPlannerParams(plannerOptions);
canonicalQuery->getExpCtx()->setWasRateLimited(true);
}
}
tasserted(8712800, "Exceeded retry iterations for making a planner");
}();
auto planner = retryMakePlanner(std::move(paramsForSingleCollectionQuery),
makeQueryPlannerParams,
makePlanner,
canonicalQuery.get(),
plannerOptions,
pipeline);
auto exec = planner->makeExecutor(std::move(canonicalQuery));
return std::move(exec);
}

View File

@ -0,0 +1,182 @@
/**
* Copyright (C) 2026-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/db/query/get_executor_fast_paths.h"
#include <boost/container/flat_set.hpp>
#include <boost/container/small_vector.hpp>
#include <boost/container/vector.hpp>
#include <boost/cstdint.hpp>
#include <boost/none.hpp>
#include <boost/optional.hpp>
#include <boost/optional/optional.hpp>
#include <boost/smart_ptr/intrusive_ptr.hpp>
// IWYU pragma: no_include "ext/alloc_traits.h"
#include "mongo/db/client.h"
#include "mongo/db/curop.h"
#include "mongo/db/exec/classic/delete_stage.h"
#include "mongo/db/exec/classic/plan_stage.h"
#include "mongo/db/exec/classic/sort_key_generator.h"
#include "mongo/db/exec/classic/subplan.h"
#include "mongo/db/exec/express/plan_executor_express.h"
#include "mongo/db/exec/runtime_planners/planner_interface.h"
#include "mongo/db/matcher/extensions_callback_real.h"
#include "mongo/db/pipeline/sbe_pushdown.h"
#include "mongo/db/query/canonical_query.h"
#include "mongo/db/query/collection_query_info.h"
#include "mongo/db/query/compiler/parsers/matcher/expression_parser.h"
#include "mongo/db/query/internal_plans.h"
#include "mongo/db/query/plan_executor.h"
#include "mongo/db/query/planner_analysis.h"
#include "mongo/db/query/query_planner.h"
#include "mongo/db/query/query_planner_params.h"
#include "mongo/db/query/query_utils.h"
#include "mongo/db/query/wildcard_multikey_paths.h"
#include "mongo/db/repl/replication_coordinator.h"
#include "mongo/db/server_parameter.h"
#include "mongo/db/service_context.h"
#include "mongo/db/shard_role/shard_catalog/index_catalog.h"
#include "mongo/db/shard_role/shard_catalog/index_descriptor.h"
#include "mongo/db/stats/counters.h"
#include "mongo/db/storage/record_store.h"
#include "mongo/db/storage/recovery_unit.h"
#include "mongo/db/update/update_driver.h"
#include "mongo/logv2/log.h"
#include "mongo/util/assert_util.h"
#include <utility>
#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kQuery
namespace mongo {
namespace {
boost::optional<ScopedCollectionFilter> getScopedCollectionFilter(
OperationContext* opCtx,
const MultipleCollectionAccessor& collections,
const QueryPlannerParams& plannerParams) {
if (plannerParams.mainCollectionInfo.options & QueryPlannerParams::INCLUDE_SHARD_FILTER) {
auto collFilter = collections.getMainCollectionPtrOrAcquisition().getShardingFilter();
tassert(11321302,
"Attempting to use shard filter when there's no shard filter available for "
"the collection",
collFilter);
return collFilter;
}
return boost::none;
}
} // namespace
ExpressResult tryExpress(
OperationContext* opCtx,
const MultipleCollectionAccessor& collections,
std::unique_ptr<CanonicalQuery>& canonicalQuery,
std::size_t plannerOptions,
const std::function<std::unique_ptr<QueryPlannerParams>(size_t)>& makePlannerParams) {
// First try to use the express id point query fast path.
const auto& mainColl = collections.getMainCollection();
const auto expressEligibility = isExpressEligible(opCtx, mainColl, *canonicalQuery);
if (expressEligibility == ExpressEligibility::IdPointQueryEligible) {
planCacheCounters.incrementClassicSkippedCounter();
auto plannerParams =
std::make_unique<QueryPlannerParams>(QueryPlannerParams::ArgsForExpress{
opCtx, *canonicalQuery, collections, plannerOptions});
auto collectionFilter = getScopedCollectionFilter(opCtx, collections, *plannerParams);
const bool isClusteredOnId = plannerParams->clusteredInfo
? clustered_util::isClusteredOnId(plannerParams->clusteredInfo)
: false;
auto expressExecutor = isClusteredOnId
? makeExpressExecutorForFindByClusteredId(
opCtx,
std::move(canonicalQuery),
collections.getMainCollectionPtrOrAcquisition(),
std::move(collectionFilter),
plannerOptions & QueryPlannerParams::RETURN_OWNED_DATA)
: makeExpressExecutorForFindById(opCtx,
std::move(canonicalQuery),
collections.getMainCollectionPtrOrAcquisition(),
std::move(collectionFilter),
plannerOptions &
QueryPlannerParams::RETURN_OWNED_DATA);
return {.executor = std::move(expressExecutor)};
}
// The query might still be eligible for express execution via the index equality fast path.
// However, that requires the full set of planner parameters for the main collection to be
// available and creating those now allows them to be reused for subsequent strategies if
// the express index equality one fails.
auto paramsForSingleCollectionQuery = makePlannerParams(plannerOptions);
if (expressEligibility == ExpressEligibility::IndexedEqualityEligible) {
if (auto indexEntry =
getIndexForExpressEquality(*canonicalQuery, *paramsForSingleCollectionQuery)) {
auto expressExecutor = makeExpressExecutorForFindByUserIndex(
opCtx,
std::move(canonicalQuery),
collections.getMainCollectionPtrOrAcquisition(),
*indexEntry,
getScopedCollectionFilter(opCtx, collections, *paramsForSingleCollectionQuery),
plannerOptions & QueryPlannerParams::RETURN_OWNED_DATA);
return {.executor = std::move(expressExecutor)};
}
}
// Allow reuse of the planner params, in case other planning logic needs it.
return {.plannerParams = std::move(paramsForSingleCollectionQuery)};
}
std::unique_ptr<classic_runtime_planner::IdHackPlanner> tryIdHack(
OperationContext* opCtx,
const MultipleCollectionAccessor& collections,
CanonicalQuery* cq,
const std::function<PlannerData()>& makePlannerData) {
const auto& mainCollection = collections.getMainCollection();
if (!isIdHackEligibleQuery(mainCollection, *cq)) {
return nullptr;
}
const auto indexEntry = mainCollection->getIndexCatalog()->findIdIndex(opCtx);
if (!indexEntry) {
return nullptr;
}
LOGV2_DEBUG(20922,
2,
"Using classic engine idhack",
"canonicalQuery"_attr = redact(cq->toStringShort()));
planCacheCounters.incrementClassicSkippedCounter();
fastPathQueryCounters.incrementIdHackQueryCounter();
return std::make_unique<classic_runtime_planner::IdHackPlanner>(makePlannerData(), indexEntry);
}
} // namespace mongo

View File

@ -0,0 +1,92 @@
/**
* Copyright (C) 2026-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include <boost/container/flat_set.hpp>
#include <boost/container/small_vector.hpp>
#include <boost/container/vector.hpp>
#include <boost/cstdint.hpp>
#include <boost/none.hpp>
#include <boost/optional.hpp>
#include <boost/optional/optional.hpp>
#include <boost/smart_ptr/intrusive_ptr.hpp>
// IWYU pragma: no_include "ext/alloc_traits.h"
#include "mongo/db/client.h"
#include "mongo/db/curop.h"
#include "mongo/db/exec/classic/delete_stage.h"
#include "mongo/db/exec/classic/plan_stage.h"
#include "mongo/db/exec/classic/subplan.h"
#include "mongo/db/exec/runtime_planners/classic_runtime_planner/planner_interface.h"
#include "mongo/db/exec/runtime_planners/classic_runtime_planner_for_sbe/planner_interface.h"
#include "mongo/db/exec/runtime_planners/planner_interface.h"
#include "mongo/db/matcher/extensions_callback_real.h"
#include "mongo/db/pipeline/sbe_pushdown.h"
#include "mongo/db/query/canonical_query.h"
#include "mongo/db/query/collection_query_info.h"
#include "mongo/db/query/compiler/parsers/matcher/expression_parser.h"
#include "mongo/db/query/internal_plans.h"
#include "mongo/db/query/plan_executor.h"
#include "mongo/db/query/query_planner_params.h"
#include "mongo/db/query/wildcard_multikey_paths.h"
#include "mongo/db/repl/replication_coordinator.h"
#include "mongo/db/server_parameter.h"
#include "mongo/db/service_context.h"
#include "mongo/db/shard_role/shard_catalog/index_descriptor.h"
#include "mongo/db/storage/record_store.h"
#include "mongo/db/storage/recovery_unit.h"
#include "mongo/db/update/update_driver.h"
namespace mongo {
struct ExpressResult {
std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> executor;
std::unique_ptr<QueryPlannerParams> plannerParams;
};
/*
* Builds an express executor if the query is eligible. Otherwise returns the planner params created
* to check express eligibility, for reuse.
*/
ExpressResult tryExpress(
OperationContext* opCtx,
const MultipleCollectionAccessor& collections,
std::unique_ptr<CanonicalQuery>& canonicalQuery,
std::size_t plannerOptions,
const std::function<std::unique_ptr<QueryPlannerParams>(size_t)>& makePlannerParams);
/*
* Builds an IdHack planner if eligible, otherwise returns nullptr.
*/
std::unique_ptr<classic_runtime_planner::IdHackPlanner> tryIdHack(
OperationContext* opCtx,
const MultipleCollectionAccessor& collections,
CanonicalQuery* cq,
const std::function<PlannerData()>& makePlannerData);
} // namespace mongo

View File

@ -0,0 +1,137 @@
/**
* Copyright (C) 2026-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/db/query/get_executor_helpers.h"
#include <boost/container/flat_set.hpp>
#include <boost/container/small_vector.hpp>
#include <boost/container/vector.hpp>
#include <boost/cstdint.hpp>
#include <boost/none.hpp>
#include <boost/optional.hpp>
#include <boost/optional/optional.hpp>
#include <boost/smart_ptr/intrusive_ptr.hpp>
// IWYU pragma: no_include "ext/alloc_traits.h"
#include "mongo/db/client.h"
#include "mongo/db/curop.h"
#include "mongo/db/exec/classic/delete_stage.h"
#include "mongo/db/exec/classic/plan_stage.h"
#include "mongo/db/exec/classic/sort_key_generator.h"
#include "mongo/db/exec/classic/subplan.h"
#include "mongo/db/matcher/extensions_callback_real.h"
#include "mongo/db/pipeline/sbe_pushdown.h"
#include "mongo/db/query/canonical_query.h"
#include "mongo/db/query/collection_query_info.h"
#include "mongo/db/query/compiler/parsers/matcher/expression_parser.h"
#include "mongo/db/query/internal_plans.h"
#include "mongo/db/query/planner_analysis.h"
#include "mongo/db/query/query_planner.h"
#include "mongo/db/query/query_utils.h"
#include "mongo/db/query/wildcard_multikey_paths.h"
#include "mongo/db/repl/replication_coordinator.h"
#include "mongo/db/server_parameter.h"
#include "mongo/db/service_context.h"
#include "mongo/db/shard_role/shard_catalog/index_catalog.h"
#include "mongo/db/shard_role/shard_catalog/index_descriptor.h"
#include "mongo/db/storage/record_store.h"
#include "mongo/db/storage/recovery_unit.h"
#include "mongo/db/update/update_driver.h"
#include "mongo/logv2/log.h"
#include "mongo/util/assert_util.h"
#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kQuery
namespace mongo {
void setOpDebugPlanCacheInfo(OperationContext* opCtx, const PlanCacheInfo& cacheInfo) {
OpDebug& opDebug = CurOp::get(opCtx)->debug();
if (!opDebug.planCacheShapeHash && cacheInfo.planCacheShapeHash) {
opDebug.planCacheShapeHash = *cacheInfo.planCacheShapeHash;
}
if (!opDebug.planCacheKey && cacheInfo.planCacheKey) {
opDebug.planCacheKey = *cacheInfo.planCacheKey;
}
}
std::unique_ptr<PlannerInterface> retryMakePlanner(
std::unique_ptr<QueryPlannerParams> plannerParams,
const MakePlannerParamsFn& makeQueryPlannerParams,
const MakePlannerFn& makePlanner,
CanonicalQuery* canonicalQuery,
std::size_t plannerOptions,
Pipeline* pipeline) {
static constexpr size_t kMaxIterations = 5;
for (size_t iter = 0; iter < kMaxIterations; ++iter) {
try {
// First try the single collection query parameters, as these would have been
// generated with query settings if present.
return makePlanner(std::move(plannerParams));
} catch (const ExceptionFor<ErrorCodes::NoDistinctScansForDistinctEligibleQuery>&) {
// The planner failed to generate a DISTINCT_SCAN for a distinct-like query. Remove
// the distinct property and replan using SBE or subplanning as applicable.
canonicalQuery->resetDistinct();
if (canonicalQuery->isSbeCompatible()) {
// Stages still need to be finalized for SBE since classic was used previously.
finalizePipelineStages(pipeline, canonicalQuery);
}
return makePlanner(makeQueryPlannerParams(plannerOptions));
} catch (const ExceptionFor<ErrorCodes::NoQueryExecutionPlans>& exception) {
// The planner failed to generate a viable plan. Remove the query settings and
// retry if any are present. Otherwise just propagate the exception.
const auto& querySettings = canonicalQuery->getExpCtx()->getQuerySettings();
const bool hasQuerySettings = querySettings.getIndexHints().has_value();
// Planning has been tried without query settings and no execution plan was found.
const bool ignoreQuerySettings =
plannerOptions & QueryPlannerParams::IGNORE_QUERY_SETTINGS;
if (!hasQuerySettings || ignoreQuerySettings) {
throw;
}
LOGV2_DEBUG(8524200,
2,
"Encountered planning error while running with query settings. Retrying "
"without query settings.",
"query"_attr = redact(canonicalQuery->toStringForErrorMsg()),
"querySettings"_attr = querySettings,
"reason"_attr = exception.reason(),
"code"_attr = exception.codeString());
plannerOptions |= QueryPlannerParams::IGNORE_QUERY_SETTINGS;
// Propagate the params to the next iteration.
plannerParams = makeQueryPlannerParams(plannerOptions);
} catch (const ExceptionFor<ErrorCodes::RetryMultiPlanning>&) {
// Propagate the params to the next iteration.
plannerParams = makeQueryPlannerParams(plannerOptions);
canonicalQuery->getExpCtx()->setWasRateLimited(true);
}
}
tasserted(8712800, "Exceeded retry iterations for making a planner");
}
} // namespace mongo

View File

@ -0,0 +1,92 @@
/**
* Copyright (C) 2026-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#pragma once
#include "mongo/base/status_with.h"
#include "mongo/db/curop.h"
#include "mongo/db/exec/classic/delete_stage.h"
#include "mongo/db/exec/runtime_planners/planner_interface.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/pipeline/sbe_pushdown.h"
#include "mongo/db/query/canonical_distinct.h"
#include "mongo/db/query/canonical_query.h"
#include "mongo/db/query/engine_selection.h"
#include "mongo/db/query/multiple_collection_accessor.h"
#include "mongo/db/query/plan_executor.h"
#include "mongo/db/query/plan_yield_policy.h"
#include "mongo/db/query/query_planner_params.h"
#include "mongo/db/query/write_ops/canonical_update.h"
#include "mongo/db/query/write_ops/parsed_delete.h"
#include "mongo/db/shard_role/shard_catalog/index_catalog_entry.h"
#include "mongo/db/update/update_driver.h"
#include "mongo/util/modules.h"
#include <cstddef>
#include <memory>
#include <boost/none.hpp>
#include <boost/optional/optional.hpp>
#include <boost/smart_ptr/intrusive_ptr.hpp>
namespace mongo {
/**
* Struct to hold information about a query plan's cache info.
*/
struct PlanCacheInfo {
boost::optional<uint32_t> planCacheKey;
boost::optional<uint32_t> planCacheShapeHash;
};
/**
* Fills in the given information on the CurOp::OpDebug object, if it has not already been filled in
* by an outer pipeline.
*/
void setOpDebugPlanCacheInfo(OperationContext* opCtx, const PlanCacheInfo& cacheInfo);
using MakePlannerParamsFn = std::function<std::unique_ptr<QueryPlannerParams>(size_t)>;
using MakePlannerFn =
std::function<std::unique_ptr<PlannerInterface>(std::unique_ptr<QueryPlannerParams>)>;
/*
* Calls `makePlanner` with five retries in case exceptions are thrown. Uses the given plannerParams
* at first to avoid additional calls to `makeQueryPlannerParams`.
*/
std::unique_ptr<PlannerInterface> retryMakePlanner(
std::unique_ptr<QueryPlannerParams> plannerParams,
const MakePlannerParamsFn& makeQueryPlannerParams,
const MakePlannerFn& makePlanner,
CanonicalQuery* canonicalQuery,
std::size_t plannerOptions,
Pipeline* pipeline);
} // namespace mongo

View File

@ -558,8 +558,7 @@ void QueryPlannerParams::fillOutMainCollectionPlannerParams(
opCtx, mainColl, &mainCollectionInfo.stats, false /* includeSizeStats */);
}
void QueryPlannerParams::setTargetSbeStageBuilder(OperationContext* opCtx,
const CanonicalQuery& canonicalQuery,
void QueryPlannerParams::setTargetSbeStageBuilder(const CanonicalQuery& canonicalQuery,
const MultipleCollectionAccessor& collections) {
// Set 'TARGET_SBE_STAGE_BUILDER' on the main collection and the secondary collections. We
// also update 'providedOptions' in case fillOutSecondaryCollectionsPlannerParams() hasn't

View File

@ -358,8 +358,7 @@ struct MONGO_MOD_NEEDS_REPLACEMENT QueryPlannerParams {
* This method updates this QueryPlannerParams object as needed so that it can be used with
* the SBE engine.
*/
void setTargetSbeStageBuilder(OperationContext* opCtx,
const CanonicalQuery& canonicalQuery,
void setTargetSbeStageBuilder(const CanonicalQuery& canonicalQuery,
const MultipleCollectionAccessor& collections);
/**