summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Shteinfeld <ben.shteinfeld@mongodb.com>2022-08-25 21:05:13 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-08-25 22:29:05 +0000
commit7936a08758813938df3653ca63391b822612f238 (patch)
treedefb93784725319b596febd5480907affdb15b57
parent42aef32217428c8d349ceb80ef62d077eafb835e (diff)
downloadmongo-7936a08758813938df3653ca63391b822612f238.tar.gz
SERVER-62407 Translate find queries directly to ABT
-rw-r--r--buildscripts/resmokeconfig/suites/cqf_passthrough.yml2
-rw-r--r--jstests/libs/optimizer_utils.js8
-rw-r--r--jstests/noPassthrough/cqf_fallback.js3
-rw-r--r--src/mongo/db/SConscript3
-rw-r--r--src/mongo/db/commands/SConscript2
-rw-r--r--src/mongo/db/commands/find_cmd.cpp15
-rw-r--r--src/mongo/db/commands/run_aggregate.cpp12
-rw-r--r--src/mongo/db/dbhelpers.cpp3
-rw-r--r--src/mongo/db/exec/sbe/abt/sbe_abt_test_util.cpp2
-rw-r--r--src/mongo/db/pipeline/SConscript3
-rw-r--r--src/mongo/db/pipeline/abt/algebrizer_context.h92
-rw-r--r--src/mongo/db/pipeline/abt/canonical_query_translation.cpp68
-rw-r--r--src/mongo/db/pipeline/abt/canonical_query_translation.h46
-rw-r--r--src/mongo/db/pipeline/abt/collation_translation.cpp65
-rw-r--r--src/mongo/db/pipeline/abt/collation_translation.h (renamed from src/mongo/db/commands/cqf/cqf_aggregate.h)18
-rw-r--r--src/mongo/db/pipeline/abt/document_source_visitor.cpp281
-rw-r--r--src/mongo/db/pipeline/abt/transformer_visitor.cpp230
-rw-r--r--src/mongo/db/pipeline/abt/transformer_visitor.h93
-rw-r--r--src/mongo/db/query/canonical_query.h17
-rw-r--r--src/mongo/db/query/ce/ce_sampling.cpp2
-rw-r--r--src/mongo/db/query/cqf_command_utils.cpp (renamed from src/mongo/db/commands/cqf/cqf_command_utils.cpp)4
-rw-r--r--src/mongo/db/query/cqf_command_utils.h (renamed from src/mongo/db/commands/cqf/cqf_command_utils.h)0
-rw-r--r--src/mongo/db/query/cqf_get_executor.cpp (renamed from src/mongo/db/commands/cqf/cqf_aggregate.cpp)209
-rw-r--r--src/mongo/db/query/cqf_get_executor.h60
-rw-r--r--src/mongo/db/query/get_executor.cpp6
-rw-r--r--src/mongo/db/query/optimizer/cascades/memo.h6
-rw-r--r--src/mongo/db/query/optimizer/opt_phase_manager.cpp11
-rw-r--r--src/mongo/db/query/optimizer/opt_phase_manager.h15
28 files changed, 897 insertions, 379 deletions
diff --git a/buildscripts/resmokeconfig/suites/cqf_passthrough.yml b/buildscripts/resmokeconfig/suites/cqf_passthrough.yml
index 8c6140bc78a..910ffa500c3 100644
--- a/buildscripts/resmokeconfig/suites/cqf_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/cqf_passthrough.yml
@@ -50,6 +50,8 @@ selector:
- jstests/core/projection_meta_index_key.js
- jstests/core/positional_projection.js
- jstests/core/positional_projection_multiple_array_fields.js
+ # TODO SERVER-62034 Prevent distinct() from using CQF.
+ - jstests/core/profile_distinct.js
# Transactions are not supported on MongoDB standalone nodes, so we do not run these tests in the
# 'core' suite. Instead we run them against a 1-node replica set in the 'core_txns' suite.
- jstests/core/txns/**/*.js
diff --git a/jstests/libs/optimizer_utils.js b/jstests/libs/optimizer_utils.js
index 9a0cd866e31..a43b635a8de 100644
--- a/jstests/libs/optimizer_utils.js
+++ b/jstests/libs/optimizer_utils.js
@@ -13,12 +13,8 @@ function checkCascadesOptimizerEnabled(theDB) {
* Given the result of an explain command, returns whether the bonsai optimizer was used.
*/
function usedBonsaiOptimizer(explain) {
- if (explain.hasOwnProperty("queryPlanner") &&
- !explain.queryPlanner.winningPlan.hasOwnProperty("optimizerPlan")) {
- // Find command explain which means new optimizer was not used.
- // TODO SERVER-62407 this assumption may no longer hold true if the translation to ABT
- // happens directly from a find command.
- return false;
+ if (!isAggregationPlan(explain)) {
+ return explain.queryPlanner.winningPlan.hasOwnProperty("optimizerPlan");
}
const plannerOutput = getAggPlanStage(explain, "$cursor");
diff --git a/jstests/noPassthrough/cqf_fallback.js b/jstests/noPassthrough/cqf_fallback.js
index 688a75c641b..2157340e97c 100644
--- a/jstests/noPassthrough/cqf_fallback.js
+++ b/jstests/noPassthrough/cqf_fallback.js
@@ -116,6 +116,9 @@ assertNotSupportedByBonsai({
},
true);
+// Sort on a find() is not supported.
+assertNotSupportedByBonsai({find: coll.getName(), filter: {}, sort: {a: 1}}, true);
+
// Numeric path components are not supported, either in a match expression or projection.
assertNotSupportedByBonsai({find: coll.getName(), filter: {'a.0': 5}});
assertNotSupportedByBonsai({find: coll.getName(), filter: {'a.0.b': 5}});
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index a9404ebedfa..e4d96586964 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -1391,6 +1391,8 @@ env.Library(
'query/all_indices_required_checker.cpp',
'query/bind_input_params.cpp',
'query/classic_stage_builder.cpp',
+ 'query/cqf_command_utils.cpp',
+ 'query/cqf_get_executor.cpp',
'query/explain.cpp',
'query/find.cpp',
'query/get_executor.cpp',
@@ -1429,6 +1431,7 @@ env.Library(
'$BUILD_DIR/mongo/base',
'$BUILD_DIR/mongo/db/auth/auth_checks',
'$BUILD_DIR/mongo/db/index/index_access_methods',
+ '$BUILD_DIR/mongo/db/query/ce/query_ce',
'$BUILD_DIR/mongo/scripting/scripting',
'$BUILD_DIR/mongo/util/background_job',
'$BUILD_DIR/mongo/util/elapsed_tracker',
diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript
index 56fc590f96f..e83f7356c54 100644
--- a/src/mongo/db/commands/SConscript
+++ b/src/mongo/db/commands/SConscript
@@ -338,8 +338,6 @@ env.Library(
source=[
"analyze_cmd.cpp",
"count_cmd.cpp",
- "cqf/cqf_aggregate.cpp",
- "cqf/cqf_command_utils.cpp",
"create_command.cpp",
"create_indexes.cpp",
"current_op.cpp",
diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp
index 9ebf3c97908..86bff60820b 100644
--- a/src/mongo/db/commands/find_cmd.cpp
+++ b/src/mongo/db/commands/find_cmd.cpp
@@ -36,7 +36,6 @@
#include "mongo/db/client.h"
#include "mongo/db/clientcursor.h"
#include "mongo/db/commands.h"
-#include "mongo/db/commands/cqf/cqf_command_utils.h"
#include "mongo/db/commands/run_aggregate.h"
#include "mongo/db/commands/test_commands_enabled.h"
#include "mongo/db/cursor_manager.h"
@@ -48,6 +47,8 @@
#include "mongo/db/pipeline/aggregation_request_helper.h"
#include "mongo/db/pipeline/variables.h"
#include "mongo/db/query/collation/collator_factory_interface.h"
+#include "mongo/db/query/cqf_command_utils.h"
+#include "mongo/db/query/cqf_get_executor.h"
#include "mongo/db/query/cursor_response.h"
#include "mongo/db/query/explain.h"
#include "mongo/db/query/find.h"
@@ -309,9 +310,9 @@ public:
extensionsCallback,
MatchExpressionParser::kAllowAllSpecialFeatures));
- // If we are running a query against a view, or if we are trying to test the new
- // optimizer, redirect this query through the aggregation system.
- if (ctx->getView() || isEligibleForBonsai(*cq, opCtx, ctx->getCollection())) {
+ // If we are running a query against a view redirect this query through the aggregation
+ // system.
+ if (ctx->getView()) {
// Relinquish locks. The aggregation command will re-acquire them.
ctx.reset();
@@ -514,9 +515,9 @@ public:
extensionsCallback,
MatchExpressionParser::kAllowAllSpecialFeatures));
- // If we are running a query against a view, or if we are trying to test the new
- // optimizer, redirect this query through the aggregation system.
- if (ctx->getView() || isEligibleForBonsai(*cq, opCtx, ctx->getCollection())) {
+ // If we are running a query against a view redirect this query through the aggregation
+ // system.
+ if (ctx->getView()) {
// Relinquish locks. The aggregation command will re-acquire them.
ctx.reset();
diff --git a/src/mongo/db/commands/run_aggregate.cpp b/src/mongo/db/commands/run_aggregate.cpp
index acc2597af08..28909dd442b 100644
--- a/src/mongo/db/commands/run_aggregate.cpp
+++ b/src/mongo/db/commands/run_aggregate.cpp
@@ -41,8 +41,6 @@
#include "mongo/db/catalog/database_holder.h"
#include "mongo/db/change_stream_change_collection_manager.h"
#include "mongo/db/change_stream_pre_images_collection_manager.h"
-#include "mongo/db/commands/cqf/cqf_aggregate.h"
-#include "mongo/db/commands/cqf/cqf_command_utils.h"
#include "mongo/db/curop.h"
#include "mongo/db/cursor_manager.h"
#include "mongo/db/db_raii.h"
@@ -66,6 +64,8 @@
#include "mongo/db/pipeline/search_helper.h"
#include "mongo/db/query/collation/collator_factory_interface.h"
#include "mongo/db/query/collection_query_info.h"
+#include "mongo/db/query/cqf_command_utils.h"
+#include "mongo/db/query/cqf_get_executor.h"
#include "mongo/db/query/cursor_response.h"
#include "mongo/db/query/find_common.h"
#include "mongo/db/query/get_executor.h"
@@ -976,8 +976,12 @@ Status runAggregate(OperationContext* opCtx,
!request.getExchange().has_value());
auto timeBegin = Date_t::now();
- execs.emplace_back(getSBEExecutorViaCascadesOptimizer(
- opCtx, expCtx, nss, collections.getMainCollection(), request.getHint(), *pipeline));
+ execs.emplace_back(getSBEExecutorViaCascadesOptimizer(opCtx,
+ expCtx,
+ nss,
+ collections.getMainCollection(),
+ request.getHint(),
+ std::move(pipeline)));
auto elapsed =
(Date_t::now().toMillisSinceEpoch() - timeBegin.toMillisSinceEpoch()) / 1000.0;
OPTIMIZER_DEBUG_LOG(
diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp
index aaf0551b82f..377d8f60cc3 100644
--- a/src/mongo/db/dbhelpers.cpp
+++ b/src/mongo/db/dbhelpers.cpp
@@ -120,6 +120,9 @@ RecordId Helpers::findOne(OperationContext* opCtx,
massertStatusOK(statusWithCQ.getStatus());
unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue());
+ // TODO SERVER-69102: Enable CQF once it supports RecordId outputs.
+ cq->setUseCqfIfEligible(false);
+
auto exec = uassertStatusOK(getExecutor(opCtx,
&collection,
std::move(cq),
diff --git a/src/mongo/db/exec/sbe/abt/sbe_abt_test_util.cpp b/src/mongo/db/exec/sbe/abt/sbe_abt_test_util.cpp
index 4b6ce1a09a7..cfb6372aae3 100644
--- a/src/mongo/db/exec/sbe/abt/sbe_abt_test_util.cpp
+++ b/src/mongo/db/exec/sbe/abt/sbe_abt_test_util.cpp
@@ -29,12 +29,12 @@
#include "mongo/db/exec/sbe/abt/sbe_abt_test_util.h"
-#include "mongo/db/commands/cqf/cqf_command_utils.h"
#include "mongo/db/exec/sbe/abt/abt_lower.h"
#include "mongo/db/pipeline/abt/document_source_visitor.h"
#include "mongo/db/pipeline/aggregate_command_gen.h"
#include "mongo/db/pipeline/document_source_queue.h"
#include "mongo/db/pipeline/expression_context_for_test.h"
+#include "mongo/db/query/cqf_command_utils.h"
#include "mongo/db/query/optimizer/explain.h"
#include "mongo/db/query/optimizer/opt_phase_manager.h"
#include "mongo/db/query/plan_executor.h"
diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript
index a01d33f0702..cf92ade7e0a 100644
--- a/src/mongo/db/pipeline/SConscript
+++ b/src/mongo/db/pipeline/SConscript
@@ -223,11 +223,14 @@ pipelineEnv.InjectThirdParty(libraries=['snappy'])
pipelineEnv.Library(
target='pipeline',
source=[
+ 'abt/canonical_query_translation.cpp',
'abt/document_source_visitor.cpp',
'abt/agg_expression_visitor.cpp',
+ 'abt/collation_translation.cpp',
'abt/expr_algebrizer_context.cpp',
'abt/field_map_builder.cpp',
'abt/match_expression_visitor.cpp',
+ 'abt/transformer_visitor.cpp',
'abt/utils.cpp',
'document_source.cpp',
'document_source_add_fields.cpp',
diff --git a/src/mongo/db/pipeline/abt/algebrizer_context.h b/src/mongo/db/pipeline/abt/algebrizer_context.h
new file mode 100644
index 00000000000..c85c7dde0c7
--- /dev/null
+++ b/src/mongo/db/pipeline/abt/algebrizer_context.h
@@ -0,0 +1,92 @@
+/**
+ * Copyright (C) 2022-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/query/optimizer/defs.h"
+#include "mongo/db/query/optimizer/node.h"
+#include "mongo/db/query/optimizer/syntax/syntax.h"
+#include "mongo/db/query/optimizer/utils/utils.h"
+
+namespace mongo::optimizer {
+
+/**
+ * Used to track information including the current root node of the ABT and the current projection
+ * representing output documents.
+ */
+class AlgebrizerContext {
+public:
+ struct NodeWithRootProjection {
+ NodeWithRootProjection(ProjectionName rootProjection, ABT node)
+ : _rootProjection(std::move(rootProjection)), _node(std::move(node)) {}
+
+ ProjectionName _rootProjection;
+ ABT _node;
+ };
+
+ AlgebrizerContext(PrefixId& prefixId, NodeWithRootProjection node)
+ : _node(std::move(node)), _scanProjName(_node._rootProjection), _prefixId(prefixId) {
+ assertNodeSort(_node._node);
+ }
+
+ template <typename T, typename... Args>
+ inline auto setNode(ProjectionName rootProjection, Args&&... args) {
+ setNode(std::move(rootProjection), std::move(ABT::make<T>(std::forward<Args>(args)...)));
+ }
+
+ void setNode(ProjectionName rootProjection, ABT node) {
+ assertNodeSort(node);
+ _node._node = std::move(node);
+ _node._rootProjection = std::move(rootProjection);
+ }
+
+ NodeWithRootProjection& getNode() {
+ return _node;
+ }
+
+ std::string getNextId(const std::string& key) {
+ return _prefixId.getNextId(key);
+ }
+
+ PrefixId& getPrefixId() {
+ return _prefixId;
+ }
+
+ const ProjectionName& getScanProjName() const {
+ return _scanProjName;
+ }
+
+private:
+ NodeWithRootProjection _node;
+ ProjectionName _scanProjName;
+
+ PrefixId& _prefixId;
+};
+
+} // namespace mongo::optimizer
diff --git a/src/mongo/db/pipeline/abt/canonical_query_translation.cpp b/src/mongo/db/pipeline/abt/canonical_query_translation.cpp
new file mode 100644
index 00000000000..9501b855053
--- /dev/null
+++ b/src/mongo/db/pipeline/abt/canonical_query_translation.cpp
@@ -0,0 +1,68 @@
+/**
+ * Copyright (C) 2022-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/pipeline/abt/canonical_query_translation.h"
+
+#include "mongo/db/pipeline/abt/algebrizer_context.h"
+#include "mongo/db/pipeline/abt/collation_translation.h"
+#include "mongo/db/pipeline/abt/match_expression_visitor.h"
+#include "mongo/db/pipeline/abt/transformer_visitor.h"
+
+namespace mongo::optimizer {
+
+ABT translateCanonicalQueryToABT(const Metadata& metadata,
+ const CanonicalQuery& canonicalQuery,
+ ProjectionName scanProjName,
+ ABT initialNode,
+ PrefixId& prefixId) {
+ auto abt = generateMatchExpression(
+ canonicalQuery.root(), false /* allowAggExpression */, scanProjName, "match");
+
+ abt = make<FilterNode>(make<EvalFilter>(std::move(abt), make<Variable>(scanProjName)),
+ std::move(initialNode));
+
+ AlgebrizerContext ctx{prefixId, {scanProjName, std::move(abt)}};
+
+ if (auto sortPattern = canonicalQuery.getSortPattern()) {
+ generateCollationNode(ctx, *sortPattern);
+ }
+
+ if (auto proj = canonicalQuery.getProj()) {
+ translateProjection(ctx, scanProjName, canonicalQuery.getExpCtx(), proj);
+ }
+
+ // TODO SERVER-68692: Support limit.
+ // TODO SERVER-68693: Support skip.
+
+ return make<RootNode>(properties::ProjectionRequirement{ProjectionNameVector{
+ std::move(ctx.getNode()._rootProjection)}},
+ std::move(ctx.getNode()._node));
+}
+
+} // namespace mongo::optimizer
diff --git a/src/mongo/db/pipeline/abt/canonical_query_translation.h b/src/mongo/db/pipeline/abt/canonical_query_translation.h
new file mode 100644
index 00000000000..d1a0eb33006
--- /dev/null
+++ b/src/mongo/db/pipeline/abt/canonical_query_translation.h
@@ -0,0 +1,46 @@
+/**
+ * Copyright (C) 2022-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/query/canonical_query.h"
+#include "mongo/db/query/optimizer/defs.h"
+#include "mongo/db/query/optimizer/metadata.h"
+#include "mongo/db/query/optimizer/syntax/syntax.h"
+#include "mongo/db/query/optimizer/utils/utils.h"
+
+namespace mongo::optimizer {
+
+ABT translateCanonicalQueryToABT(const Metadata& metadata,
+ const CanonicalQuery& canonicalQuery,
+ ProjectionName scanProjName,
+ ABT initialNode,
+ PrefixId& prefixId);
+
+} // namespace mongo::optimizer
diff --git a/src/mongo/db/pipeline/abt/collation_translation.cpp b/src/mongo/db/pipeline/abt/collation_translation.cpp
new file mode 100644
index 00000000000..56bc3345fe7
--- /dev/null
+++ b/src/mongo/db/pipeline/abt/collation_translation.cpp
@@ -0,0 +1,65 @@
+/**
+ * Copyright (C) 2022-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/pipeline/abt/collation_translation.h"
+
+namespace mongo::optimizer {
+
+void generateCollationNode(AlgebrizerContext& ctx, const SortPattern& sortPattern) {
+ ProjectionCollationSpec collationSpec;
+ auto rootProjection = ctx.getNode()._rootProjection;
+ // Create Evaluation node for each sort field.
+ for (const auto& part : sortPattern) {
+ if (!part.fieldPath.has_value()) {
+ continue;
+ }
+ const auto& sortProjName = ctx.getNextId("sort");
+ collationSpec.emplace_back(
+ sortProjName, part.isAscending ? CollationOp::Ascending : CollationOp::Descending);
+ const FieldPath& fieldPath = part.fieldPath.value();
+ ABT sortPath = make<PathIdentity>();
+ for (size_t j = 0; j < fieldPath.getPathLength(); j++) {
+ sortPath = make<PathGet>(fieldPath.getFieldName(j).toString(), std::move(sortPath));
+ }
+
+ ctx.setNode<EvaluationNode>(
+ rootProjection,
+ std::move(sortProjName),
+ make<EvalPath>(std::move(sortPath), make<Variable>(rootProjection)),
+ std::move(ctx.getNode()._node));
+ }
+ if (collationSpec.empty()) {
+ return;
+ }
+ ctx.setNode<CollationNode>(std::move(rootProjection),
+ properties::CollationRequirement(std::move(collationSpec)),
+ std::move(ctx.getNode()._node));
+}
+
+} // namespace mongo::optimizer
diff --git a/src/mongo/db/commands/cqf/cqf_aggregate.h b/src/mongo/db/pipeline/abt/collation_translation.h
index ec7ee64b257..cfaf7e750d3 100644
--- a/src/mongo/db/commands/cqf/cqf_aggregate.h
+++ b/src/mongo/db/pipeline/abt/collation_translation.h
@@ -29,17 +29,13 @@
#pragma once
-#include "mongo/db/catalog/collection.h"
-#include "mongo/db/query/plan_executor.h"
+#include "mongo/db/pipeline/abt/algebrizer_context.h"
+#include "mongo/db/query/optimizer/syntax/syntax.h"
+#include "mongo/db/query/optimizer/utils/utils.h"
+#include "mongo/db/query/sort_pattern.h"
-namespace mongo {
+namespace mongo::optimizer {
-std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOptimizer(
- OperationContext* opCtx,
- boost::intrusive_ptr<ExpressionContext> expCtx,
- const NamespaceString& nss,
- const CollectionPtr& collection,
- const boost::optional<BSONObj>& indexHint,
- const Pipeline& pipeline);
+void generateCollationNode(AlgebrizerContext& ctx, const SortPattern& sortPattern);
-} // namespace mongo
+} // namespace mongo::optimizer
diff --git a/src/mongo/db/pipeline/abt/document_source_visitor.cpp b/src/mongo/db/pipeline/abt/document_source_visitor.cpp
index af9b741b345..f563b93f96f 100644
--- a/src/mongo/db/pipeline/abt/document_source_visitor.cpp
+++ b/src/mongo/db/pipeline/abt/document_source_visitor.cpp
@@ -29,12 +29,11 @@
#include "mongo/db/pipeline/abt/document_source_visitor.h"
-#include "mongo/db/exec/add_fields_projection_executor.h"
-#include "mongo/db/exec/exclusion_projection_executor.h"
-#include "mongo/db/exec/inclusion_projection_executor.h"
#include "mongo/db/pipeline/abt/agg_expression_visitor.h"
-#include "mongo/db/pipeline/abt/field_map_builder.h"
+#include "mongo/db/pipeline/abt/algebrizer_context.h"
+#include "mongo/db/pipeline/abt/collation_translation.h"
#include "mongo/db/pipeline/abt/match_expression_visitor.h"
+#include "mongo/db/pipeline/abt/transformer_visitor.h"
#include "mongo/db/pipeline/abt/utils.h"
#include "mongo/db/pipeline/document_source_bucket_auto.h"
#include "mongo/db/pipeline/document_source_coll_stats.h"
@@ -80,243 +79,9 @@
namespace mongo::optimizer {
-/**
- * Used to track information including the current root node of the ABT and the current projection
- * representing output documents.
- */
-class DSAlgebrizerContext {
-public:
- struct NodeWithRootProjection {
- NodeWithRootProjection(ProjectionName rootProjection, ABT node)
- : _rootProjection(std::move(rootProjection)), _node(std::move(node)) {}
-
- ProjectionName _rootProjection;
- ABT _node;
- };
-
- DSAlgebrizerContext(PrefixId& prefixId, NodeWithRootProjection node)
- : _node(std::move(node)), _scanProjName(_node._rootProjection), _prefixId(prefixId) {
- assertNodeSort(_node._node);
- }
-
- template <typename T, typename... Args>
- inline auto setNode(ProjectionName rootProjection, Args&&... args) {
- setNode(std::move(rootProjection), std::move(ABT::make<T>(std::forward<Args>(args)...)));
- }
-
- void setNode(ProjectionName rootProjection, ABT node) {
- assertNodeSort(node);
- _node._node = std::move(node);
- _node._rootProjection = std::move(rootProjection);
- }
-
- NodeWithRootProjection& getNode() {
- return _node;
- }
-
- std::string getNextId(const std::string& key) {
- return _prefixId.getNextId(key);
- }
-
- PrefixId& getPrefixId() {
- return _prefixId;
- }
-
- const ProjectionName& getScanProjName() const {
- return _scanProjName;
- }
-
-private:
- NodeWithRootProjection _node;
- ProjectionName _scanProjName;
-
- // We don't own this.
- PrefixId& _prefixId;
-};
-
-class ABTTransformerVisitor : public TransformerInterfaceConstVisitor {
-public:
- ABTTransformerVisitor(DSAlgebrizerContext& ctx, FieldMapBuilder& builder)
- : _ctx(ctx), _builder(builder) {}
-
- void visit(const projection_executor::AddFieldsProjectionExecutor* transformer) override {
- visitInclusionNode(transformer->getRoot(), true /*isAddingFields*/);
- }
-
- void visit(const projection_executor::ExclusionProjectionExecutor* transformer) override {
- visitExclusionNode(*transformer->getRoot());
- }
-
- void visit(const projection_executor::InclusionProjectionExecutor* transformer) override {
- visitInclusionNode(*transformer->getRoot(), false /*isAddingFields*/);
- }
-
- void visit(const GroupFromFirstDocumentTransformation* transformer) override {
- unsupportedTransformer(transformer);
- }
-
- void visit(const ReplaceRootTransformation* transformer) override {
- auto entry = _ctx.getNode();
- const std::string& projName = _ctx.getNextId("newRoot");
- ABT expr = generateAggExpression(
- transformer->getExpression().get(), entry._rootProjection, projName);
-
- _ctx.setNode<EvaluationNode>(projName, projName, std::move(expr), std::move(entry._node));
- }
-
- // Creates a single EvaluationNode representing simple projections (e.g. inclusion projections)
- // and computed projections, if present, and updates the context with the new node.
- void generateCombinedProjection() const {
- auto result = _builder.generateABT();
- if (!result) {
- return;
- }
-
- auto entry = _ctx.getNode();
- const ProjectionName projName = _ctx.getNextId("combinedProjection");
- _ctx.setNode<EvaluationNode>(
- projName, projName, std::move(*result), std::move(entry._node));
- }
-
-private:
- void unsupportedTransformer(const TransformerInterface* transformer) const {
- uasserted(ErrorCodes::InternalErrorNotSupported,
- str::stream() << "Transformer is not supported (code: "
- << static_cast<int>(transformer->getType()) << ")");
- }
-
- void assertSupportedPath(const std::string& path) {
- uassert(ErrorCodes::InternalErrorNotSupported,
- "Projection contains unsupported numeric path component",
- !FieldRef(path).hasNumericPathComponents());
- }
-
- /**
- * Handles simple inclusion projections.
- */
- void processProjectedPaths(const projection_executor::InclusionNode& node) {
- // For each preserved path, mark that each path element along the field path should be
- // included.
- OrderedPathSet preservedPaths;
- node.reportProjectedPaths(&preservedPaths);
-
- for (const std::string& preservedPathStr : preservedPaths) {
- assertSupportedPath(preservedPathStr);
-
- _builder.integrateFieldPath(FieldPath(preservedPathStr),
- [](const bool isLastElement, FieldMapEntry& entry) {
- entry._hasLeadingObj = true;
- entry._hasKeep = true;
- });
- }
- }
-
- /**
- * Handles renamed fields and computed projections.
- */
- void processComputedPaths(const projection_executor::InclusionNode& node,
- const std::string& rootProjection,
- const bool isAddingFields) {
- OrderedPathSet computedPaths;
- StringMap<std::string> renamedPaths;
- node.reportComputedPaths(&computedPaths, &renamedPaths);
-
- // Handle path renames: essentially single element FieldPath expression.
- for (const auto& renamedPathEntry : renamedPaths) {
- ABT path = translateFieldPath(
- FieldPath(renamedPathEntry.second),
- make<PathIdentity>(),
- [](const std::string& fieldName, const bool isLastElement, ABT input) {
- return make<PathGet>(
- fieldName,
- isLastElement
- ? std::move(input)
- : make<PathTraverse>(std::move(input), PathTraverse::kUnlimited));
- });
-
- auto entry = _ctx.getNode();
- const std::string& renamedProjName = _ctx.getNextId("projRenamedPath");
- _ctx.setNode<EvaluationNode>(
- entry._rootProjection,
- renamedProjName,
- make<EvalPath>(std::move(path), make<Variable>(entry._rootProjection)),
- std::move(entry._node));
-
- _builder.integrateFieldPath(FieldPath(renamedPathEntry.first),
- [&renamedProjName, &isAddingFields](
- const bool isLastElement, FieldMapEntry& entry) {
- if (!isAddingFields) {
- entry._hasKeep = true;
- }
- if (isLastElement) {
- entry._constVarName = renamedProjName;
- entry._hasTrailingDefault = true;
- }
- });
- }
-
- // Handle general expression projection.
- for (const std::string& computedPathStr : computedPaths) {
- assertSupportedPath(computedPathStr);
-
- const FieldPath computedPath(computedPathStr);
-
- auto entry = _ctx.getNode();
- const std::string& getProjName = _ctx.getNextId("projGetPath");
- ABT getExpr = generateAggExpression(
- node.getExpressionForPath(computedPath).get(), rootProjection, getProjName);
-
- _ctx.setNode<EvaluationNode>(std::move(entry._rootProjection),
- getProjName,
- std::move(getExpr),
- std::move(entry._node));
-
- _builder.integrateFieldPath(
- computedPath,
- [&getProjName, &isAddingFields](const bool isLastElement, FieldMapEntry& entry) {
- if (!isAddingFields) {
- entry._hasKeep = true;
- }
- if (isLastElement) {
- entry._constVarName = getProjName;
- entry._hasTrailingDefault = true;
- }
- });
- }
- }
-
- void visitInclusionNode(const projection_executor::InclusionNode& node,
- const bool isAddingFields) {
- auto entry = _ctx.getNode();
- const std::string rootProjection = entry._rootProjection;
-
- processProjectedPaths(node);
- processComputedPaths(node, rootProjection, isAddingFields);
- }
-
- void visitExclusionNode(const projection_executor::ExclusionNode& node) {
- // Handle simple exclusion projections: for each excluded path, mark that the last field
- // path element should be dropped.
- OrderedPathSet excludedPaths;
- node.reportProjectedPaths(&excludedPaths);
- for (const std::string& excludedPathStr : excludedPaths) {
- assertSupportedPath(excludedPathStr);
- _builder.integrateFieldPath(FieldPath(excludedPathStr),
- [](const bool isLastElement, FieldMapEntry& entry) {
- if (isLastElement) {
- entry._hasDrop = true;
- }
- });
- }
- }
-
- DSAlgebrizerContext& _ctx;
- FieldMapBuilder& _builder;
-};
-
class ABTDocumentSourceVisitor : public DocumentSourceConstVisitor {
public:
- ABTDocumentSourceVisitor(DSAlgebrizerContext& ctx, const Metadata& metadata)
+ ABTDocumentSourceVisitor(AlgebrizerContext& ctx, const Metadata& metadata)
: _ctx(ctx), _metadata(metadata) {}
void visit(const DocumentSourceBucketAuto* source) override {
@@ -717,39 +482,7 @@ public:
}
void visit(const DocumentSourceSort* source) override {
- ProjectionCollationSpec collationSpec;
- const SortPattern& pattern = source->getSortKeyPattern();
- for (size_t i = 0; i < pattern.size(); i++) {
- const SortPattern::SortPatternPart& part = pattern[i];
- if (!part.fieldPath.has_value()) {
- // TODO: consider metadata expression.
- continue;
- }
-
- const std::string& sortProjName = _ctx.getNextId("sort");
- collationSpec.emplace_back(
- sortProjName, part.isAscending ? CollationOp::Ascending : CollationOp::Descending);
-
- const FieldPath& fieldPath = part.fieldPath.value();
- ABT sortPath = make<PathIdentity>();
- for (size_t j = 0; j < fieldPath.getPathLength(); j++) {
- sortPath = make<PathGet>(fieldPath.getFieldName(j).toString(), std::move(sortPath));
- }
-
- auto entry = _ctx.getNode();
- _ctx.setNode<EvaluationNode>(
- entry._rootProjection,
- sortProjName,
- make<EvalPath>(std::move(sortPath), make<Variable>(entry._rootProjection)),
- std::move(entry._node));
- }
-
- if (!collationSpec.empty()) {
- auto entry = _ctx.getNode();
- _ctx.setNode<CollationNode>(std::move(entry._rootProjection),
- properties::CollationRequirement(std::move(collationSpec)),
- std::move(entry._node));
- }
+ generateCollationNode(_ctx, source->getSortKeyPattern());
if (source->getLimit().has_value()) {
// We need to limit the result of the collation.
@@ -906,7 +639,7 @@ private:
std::move(entry._node));
}
- DSAlgebrizerContext& _ctx;
+ AlgebrizerContext& _ctx;
const Metadata& _metadata;
};
@@ -915,7 +648,7 @@ ABT translatePipelineToABT(const Metadata& metadata,
ProjectionName scanProjName,
ABT initialNode,
PrefixId& prefixId) {
- DSAlgebrizerContext ctx(prefixId, {scanProjName, std::move(initialNode)});
+ AlgebrizerContext ctx(prefixId, {scanProjName, std::move(initialNode)});
ABTDocumentSourceVisitor visitor(ctx, metadata);
DocumentSourceWalker walker(nullptr /*preVisitor*/, &visitor);
diff --git a/src/mongo/db/pipeline/abt/transformer_visitor.cpp b/src/mongo/db/pipeline/abt/transformer_visitor.cpp
new file mode 100644
index 00000000000..ccfbeb56e31
--- /dev/null
+++ b/src/mongo/db/pipeline/abt/transformer_visitor.cpp
@@ -0,0 +1,230 @@
+/**
+ * Copyright (C) 2022-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/pipeline/abt/transformer_visitor.h"
+
+#include "mongo/db/pipeline/abt/agg_expression_visitor.h"
+#include "mongo/db/pipeline/abt/utils.h"
+#include "mongo/db/pipeline/document_source_group.h"
+#include "mongo/db/pipeline/document_source_replace_root.h"
+#include "mongo/db/pipeline/visitors/transformer_interface_walker.h"
+
+namespace mongo::optimizer {
+
+void ABTTransformerVisitor::visit(
+ const projection_executor::AddFieldsProjectionExecutor* transformer) {
+ visitInclusionNode(transformer->getRoot(), true /*isAddingFields*/);
+}
+
+void ABTTransformerVisitor::visit(
+ const projection_executor::ExclusionProjectionExecutor* transformer) {
+ visitExclusionNode(*transformer->getRoot());
+}
+
+void ABTTransformerVisitor::visit(
+ const projection_executor::InclusionProjectionExecutor* transformer) {
+ visitInclusionNode(*transformer->getRoot(), false /*isAddingFields*/);
+}
+
+void ABTTransformerVisitor::visit(const GroupFromFirstDocumentTransformation* transformer) {
+ unsupportedTransformer(transformer);
+}
+
+void ABTTransformerVisitor::visit(const ReplaceRootTransformation* transformer) {
+ auto entry = _ctx.getNode();
+ const std::string& projName = _ctx.getNextId("newRoot");
+ ABT expr =
+ generateAggExpression(transformer->getExpression().get(), entry._rootProjection, projName);
+
+ _ctx.setNode<EvaluationNode>(projName, projName, std::move(expr), std::move(entry._node));
+}
+
+void ABTTransformerVisitor::generateCombinedProjection() const {
+ auto result = _builder.generateABT();
+ if (!result) {
+ return;
+ }
+
+ auto entry = _ctx.getNode();
+ const ProjectionName projName = _ctx.getNextId("combinedProjection");
+ _ctx.setNode<EvaluationNode>(projName, projName, std::move(*result), std::move(entry._node));
+}
+
+void ABTTransformerVisitor::unsupportedTransformer(const TransformerInterface* transformer) const {
+ uasserted(ErrorCodes::InternalErrorNotSupported,
+ str::stream() << "Transformer is not supported (code: "
+ << static_cast<int>(transformer->getType()) << ")");
+}
+
+void ABTTransformerVisitor::assertSupportedPath(const std::string& path) {
+ uassert(ErrorCodes::InternalErrorNotSupported,
+ "Projection contains unsupported numeric path component",
+ !FieldRef(path).hasNumericPathComponents());
+}
+
+/**
+ * Handles simple inclusion projections.
+ */
+void ABTTransformerVisitor::processProjectedPaths(const projection_executor::InclusionNode& node) {
+ // For each preserved path, mark that each path element along the field path should be
+ // included.
+ OrderedPathSet preservedPaths;
+ node.reportProjectedPaths(&preservedPaths);
+
+ for (const std::string& preservedPathStr : preservedPaths) {
+ assertSupportedPath(preservedPathStr);
+
+ _builder.integrateFieldPath(FieldPath(preservedPathStr),
+ [](const bool isLastElement, FieldMapEntry& entry) {
+ entry._hasLeadingObj = true;
+ entry._hasKeep = true;
+ });
+ }
+}
+
+/**
+ * Handles renamed fields and computed projections.
+ */
+void ABTTransformerVisitor::processComputedPaths(const projection_executor::InclusionNode& node,
+ const std::string& rootProjection,
+ const bool isAddingFields) {
+ OrderedPathSet computedPaths;
+ StringMap<std::string> renamedPaths;
+ node.reportComputedPaths(&computedPaths, &renamedPaths);
+
+ // Handle path renames: essentially single element FieldPath expression.
+ for (const auto& renamedPathEntry : renamedPaths) {
+ ABT path = translateFieldPath(
+ FieldPath(renamedPathEntry.second),
+ make<PathIdentity>(),
+ [](const std::string& fieldName, const bool isLastElement, ABT input) {
+ return make<PathGet>(
+ fieldName,
+ isLastElement ? std::move(input)
+ : make<PathTraverse>(std::move(input), PathTraverse::kUnlimited));
+ });
+
+ auto entry = _ctx.getNode();
+ const std::string& renamedProjName = _ctx.getNextId("projRenamedPath");
+ _ctx.setNode<EvaluationNode>(
+ entry._rootProjection,
+ renamedProjName,
+ make<EvalPath>(std::move(path), make<Variable>(entry._rootProjection)),
+ std::move(entry._node));
+
+ _builder.integrateFieldPath(
+ FieldPath(renamedPathEntry.first),
+ [&renamedProjName, &isAddingFields](const bool isLastElement, FieldMapEntry& entry) {
+ if (!isAddingFields) {
+ entry._hasKeep = true;
+ }
+ if (isLastElement) {
+ entry._constVarName = renamedProjName;
+ entry._hasTrailingDefault = true;
+ }
+ });
+ }
+
+ // Handle general expression projection.
+ for (const std::string& computedPathStr : computedPaths) {
+ assertSupportedPath(computedPathStr);
+
+ const FieldPath computedPath(computedPathStr);
+
+ auto entry = _ctx.getNode();
+ const std::string& getProjName = _ctx.getNextId("projGetPath");
+ ABT getExpr = generateAggExpression(
+ node.getExpressionForPath(computedPath).get(), rootProjection, getProjName);
+
+ _ctx.setNode<EvaluationNode>(std::move(entry._rootProjection),
+ getProjName,
+ std::move(getExpr),
+ std::move(entry._node));
+
+ _builder.integrateFieldPath(
+ computedPath,
+ [&getProjName, &isAddingFields](const bool isLastElement, FieldMapEntry& entry) {
+ if (!isAddingFields) {
+ entry._hasKeep = true;
+ }
+ if (isLastElement) {
+ entry._constVarName = getProjName;
+ entry._hasTrailingDefault = true;
+ }
+ });
+ }
+}
+
+void ABTTransformerVisitor::visitInclusionNode(const projection_executor::InclusionNode& node,
+ const bool isAddingFields) {
+ auto entry = _ctx.getNode();
+ const std::string rootProjection = entry._rootProjection;
+
+ processProjectedPaths(node);
+ processComputedPaths(node, rootProjection, isAddingFields);
+}
+
+void ABTTransformerVisitor::visitExclusionNode(const projection_executor::ExclusionNode& node) {
+ // Handle simple exclusion projections: for each excluded path, mark that the last field
+ // path element should be dropped.
+ OrderedPathSet excludedPaths;
+ node.reportProjectedPaths(&excludedPaths);
+ for (const std::string& excludedPathStr : excludedPaths) {
+ assertSupportedPath(excludedPathStr);
+ _builder.integrateFieldPath(FieldPath(excludedPathStr),
+ [](const bool isLastElement, FieldMapEntry& entry) {
+ if (isLastElement) {
+ entry._hasDrop = true;
+ }
+ });
+ }
+}
+
+/**
+ * TODO SERVER-68690: Refactor this function to be shared with the DocumentSource implementation.
+ */
+void translateProjection(AlgebrizerContext& ctx,
+ ProjectionName scanProjName,
+ boost::intrusive_ptr<ExpressionContext> expCtx,
+ const projection_ast::Projection* proj) {
+
+ const auto projExecutor = projection_executor::buildProjectionExecutor(
+ expCtx,
+ proj,
+ ProjectionPolicies::findProjectionPolicies(),
+ projection_executor::BuilderParamsBitSet{projection_executor::kDefaultBuilderParams});
+
+ FieldMapBuilder builder(scanProjName, true);
+ ABTTransformerVisitor visitor(ctx, builder);
+ TransformerInterfaceWalker walker(&visitor);
+ walker.walk(projExecutor.get());
+ visitor.generateCombinedProjection();
+}
+
+} // namespace mongo::optimizer
diff --git a/src/mongo/db/pipeline/abt/transformer_visitor.h b/src/mongo/db/pipeline/abt/transformer_visitor.h
new file mode 100644
index 00000000000..c29de4f6169
--- /dev/null
+++ b/src/mongo/db/pipeline/abt/transformer_visitor.h
@@ -0,0 +1,93 @@
+/**
+ * Copyright (C) 2022-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/add_fields_projection_executor.h"
+#include "mongo/db/exec/exclusion_projection_executor.h"
+#include "mongo/db/exec/inclusion_projection_executor.h"
+#include "mongo/db/exec/projection_executor_builder.h"
+#include "mongo/db/pipeline/abt/algebrizer_context.h"
+#include "mongo/db/pipeline/abt/field_map_builder.h"
+#include "mongo/db/pipeline/visitors/transformer_interface_visitor.h"
+
+namespace mongo::optimizer {
+
+class ABTTransformerVisitor : public TransformerInterfaceConstVisitor {
+public:
+ ABTTransformerVisitor(AlgebrizerContext& ctx, FieldMapBuilder& builder)
+ : _ctx(ctx), _builder(builder) {}
+
+ void visit(const projection_executor::AddFieldsProjectionExecutor* transformer) override;
+
+ void visit(const projection_executor::ExclusionProjectionExecutor* transformer) override;
+
+ void visit(const projection_executor::InclusionProjectionExecutor* transformer) override;
+
+ void visit(const GroupFromFirstDocumentTransformation* transformer) override;
+
+ void visit(const ReplaceRootTransformation* transformer) override;
+
+ /**
+ * Creates a single EvaluationNode representing simple projections (e.g. inclusion projections)
+ * and computed projections, if present, and updates the context with the new node.
+ */
+ void generateCombinedProjection() const;
+
+private:
+ void unsupportedTransformer(const TransformerInterface* transformer) const;
+
+ void assertSupportedPath(const std::string& path);
+
+ /**
+ * Handles simple inclusion projections.
+ */
+ void processProjectedPaths(const projection_executor::InclusionNode& node);
+
+ /**
+ * Handles renamed fields and computed projections.
+ */
+ void processComputedPaths(const projection_executor::InclusionNode& node,
+ const std::string& rootProjection,
+ bool isAddingFields);
+
+ void visitInclusionNode(const projection_executor::InclusionNode& node, bool isAddingFields);
+
+ void visitExclusionNode(const projection_executor::ExclusionNode& node);
+
+ AlgebrizerContext& _ctx;
+ FieldMapBuilder& _builder;
+};
+
+void translateProjection(AlgebrizerContext& ctx,
+ ProjectionName scanProjName,
+ boost::intrusive_ptr<ExpressionContext> expCtx,
+ const projection_ast::Projection* proj);
+
+} // namespace mongo::optimizer
diff --git a/src/mongo/db/query/canonical_query.h b/src/mongo/db/query/canonical_query.h
index 1c0213d0132..0d050d82c63 100644
--- a/src/mongo/db/query/canonical_query.h
+++ b/src/mongo/db/query/canonical_query.h
@@ -233,6 +233,14 @@ public:
return _sbeCompatible;
}
+ void setUseCqfIfEligible(bool useCqfIfEligible) {
+ _useCqfIfEligible = useCqfIfEligible;
+ }
+
+ bool useCqfIfEligible() const {
+ return _useCqfIfEligible;
+ }
+
bool isParameterized() const {
return !_inputParamIdToExpressionMap.empty();
}
@@ -310,6 +318,15 @@ private:
// True if this query can be executed by the SBE.
bool _sbeCompatible = false;
+ // If true, indicates that we should use CQF if this query is eligible (see the
+ // isEligibleForBonsai() function for eligiblitly requirements).
+ // If false, indicates that we shouldn't use CQF even if this query is eligible.
+ // Warning: This field is used soley as a workaround for SERVER-69102. It is intended to be
+ // temporary and will be removed once SERVER-69102 is fixed. Do not introduce new uses or
+ // dependencies on this field.
+ // TODO SERVER-69102: Delete this field.
+ bool _useCqfIfEligible = true;
+
// A map from assigned InputParamId's to parameterised MatchExpression's.
std::vector<const MatchExpression*> _inputParamIdToExpressionMap;
};
diff --git a/src/mongo/db/query/ce/ce_sampling.cpp b/src/mongo/db/query/ce/ce_sampling.cpp
index 3ee77deaf93..37c9cd60e91 100644
--- a/src/mongo/db/query/ce/ce_sampling.cpp
+++ b/src/mongo/db/query/ce/ce_sampling.cpp
@@ -29,8 +29,8 @@
#include "mongo/db/query/ce/ce_sampling.h"
-#include "mongo/db/commands/cqf/cqf_command_utils.h"
#include "mongo/db/exec/sbe/abt/abt_lower.h"
+#include "mongo/db/query/cqf_command_utils.h"
#include "mongo/db/query/optimizer/cascades/ce_heuristic.h"
#include "mongo/db/query/optimizer/explain.h"
#include "mongo/db/query/optimizer/utils/abt_hash.h"
diff --git a/src/mongo/db/commands/cqf/cqf_command_utils.cpp b/src/mongo/db/query/cqf_command_utils.cpp
index 3a64454a60b..ac4a74e0efb 100644
--- a/src/mongo/db/commands/cqf/cqf_command_utils.cpp
+++ b/src/mongo/db/query/cqf_command_utils.cpp
@@ -27,7 +27,7 @@
* it in the license file.
*/
-#include "mongo/db/commands/cqf/cqf_command_utils.h"
+#include "mongo/db/query/cqf_command_utils.h"
#include "mongo/db/commands/test_commands_enabled.h"
#include "mongo/db/exec/add_fields_projection_executor.h"
@@ -746,7 +746,7 @@ bool isEligibleForBonsai(const CanonicalQuery& cq,
walker.walk(projExecutor.get());
}
- return eligible;
+ return eligible && cq.useCqfIfEligible();
}
} // namespace mongo
diff --git a/src/mongo/db/commands/cqf/cqf_command_utils.h b/src/mongo/db/query/cqf_command_utils.h
index 4ceb333d364..4ceb333d364 100644
--- a/src/mongo/db/commands/cqf/cqf_command_utils.h
+++ b/src/mongo/db/query/cqf_command_utils.h
diff --git a/src/mongo/db/commands/cqf/cqf_aggregate.cpp b/src/mongo/db/query/cqf_get_executor.cpp
index b7d1717b8f7..19f6961cf1a 100644
--- a/src/mongo/db/commands/cqf/cqf_aggregate.cpp
+++ b/src/mongo/db/query/cqf_get_executor.cpp
@@ -27,17 +27,18 @@
* it in the license file.
*/
-#include "mongo/db/commands/cqf/cqf_aggregate.h"
+#include "mongo/db/query/cqf_get_executor.h"
-#include "mongo/db/commands/cqf/cqf_command_utils.h"
#include "mongo/db/curop.h"
#include "mongo/db/exec/sbe/abt/abt_lower.h"
+#include "mongo/db/pipeline/abt/canonical_query_translation.h"
#include "mongo/db/pipeline/abt/document_source_visitor.h"
#include "mongo/db/pipeline/abt/match_expression_visitor.h"
#include "mongo/db/query/ce/ce_histogram.h"
#include "mongo/db/query/ce/ce_sampling.h"
#include "mongo/db/query/ce/collection_statistics.h"
#include "mongo/db/query/ce_mode_parameter.h"
+#include "mongo/db/query/cqf_command_utils.h"
#include "mongo/db/query/optimizer/cascades/ce_heuristic.h"
#include "mongo/db/query/optimizer/cascades/cost_derivation.h"
#include "mongo/db/query/optimizer/explain.h"
@@ -244,13 +245,14 @@ static QueryHints getHintsFromQueryKnobs() {
static std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> optimizeAndCreateExecutor(
OptPhaseManager& phaseManager,
- ABT abtTree,
+ ABT abt,
OperationContext* opCtx,
boost::intrusive_ptr<ExpressionContext> expCtx,
const NamespaceString& nss,
- const CollectionPtr& collection) {
+ const CollectionPtr& collection,
+ std::unique_ptr<CanonicalQuery> cq) {
- const bool optimizationResult = phaseManager.optimize(abtTree);
+ const bool optimizationResult = phaseManager.optimize(abt);
uassert(6624252, "Optimization failed", optimizationResult);
{
@@ -275,7 +277,7 @@ static std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> optimizeAndCreateExe
OPTIMIZER_DEBUG_LOG(6264801, 5, "Optimized ABT", "explain"_attr = explain);
}
- auto env = VariableEnvironment::build(abtTree);
+ auto env = VariableEnvironment::build(abt);
SlotVarMap slotMap;
sbe::value::SlotIdGenerator ids;
SBENodeLowering g{env,
@@ -284,7 +286,7 @@ static std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> optimizeAndCreateExe
phaseManager.getMetadata(),
phaseManager.getNodeToGroupPropsMap(),
phaseManager.getRIDProjections()};
- auto sbePlan = g.optimize(abtTree);
+ auto sbePlan = g.optimize(abt);
uassert(6624253, "Lowering failed: did not produce a plan.", sbePlan != nullptr);
uassert(6624254, "Lowering failed: did not produce any output slots.", !slotMap.empty());
@@ -313,10 +315,10 @@ static std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> optimizeAndCreateExe
sbePlan->prepare(data.ctx);
auto planExec = uassertStatusOK(plan_executor_factory::make(
opCtx,
- nullptr /*cq*/,
+ std::move(cq),
nullptr /*solution*/,
{std::move(sbePlan), std::move(data)},
- std::make_unique<ABTPrinter>(std::move(abtTree), phaseManager.getNodeToGroupPropsMap()),
+ std::make_unique<ABTPrinter>(std::move(abt), phaseManager.getNodeToGroupPropsMap()),
MultipleCollectionAccessor(collection),
QueryPlannerParams::Options::DEFAULT,
nss,
@@ -324,16 +326,17 @@ static std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> optimizeAndCreateExe
return planExec;
}
-static void populateAdditionalScanDefs(OperationContext* opCtx,
- boost::intrusive_ptr<ExpressionContext> expCtx,
- const Pipeline& pipeline,
- const boost::optional<BSONObj>& indexHint,
- const size_t numberOfPartitions,
- PrefixId& prefixId,
- opt::unordered_map<std::string, ScanDefinition>& scanDefs,
- const DisableIndexOptions disableIndexOptions,
- bool& disableScan) {
- for (const auto& involvedNss : pipeline.getInvolvedCollections()) {
+static void populateAdditionalScanDefs(
+ OperationContext* opCtx,
+ boost::intrusive_ptr<ExpressionContext> expCtx,
+ const stdx::unordered_set<NamespaceString>& involvedCollections,
+ const boost::optional<BSONObj>& indexHint,
+ const size_t numberOfPartitions,
+ PrefixId& prefixId,
+ opt::unordered_map<std::string, ScanDefinition>& scanDefs,
+ const DisableIndexOptions disableIndexOptions,
+ bool& disableScan) {
+ for (const auto& involvedNss : involvedCollections) {
// TODO handle views?
AutoGetCollectionForReadCommandMaybeLockFree ctx(
opCtx, involvedNss, AutoGetCollectionViewMode::kViewsForbidden);
@@ -378,19 +381,10 @@ static void populateAdditionalScanDefs(OperationContext* opCtx,
}
}
-std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOptimizer(
- OperationContext* opCtx,
- boost::intrusive_ptr<ExpressionContext> expCtx,
- const NamespaceString& nss,
- const CollectionPtr& collection,
- const boost::optional<BSONObj>& indexHint,
- const Pipeline& pipeline) {
- const bool collectionExists = collection != nullptr;
- const std::string uuidStr = collectionExists ? collection->uuid().toString() : "<missing_uuid>";
- const std::string collNameStr = nss.coll().toString();
- const std::string scanDefName = collNameStr + "_" + uuidStr;
-
- if (indexHint && !pipeline.getInvolvedCollections().empty()) {
+void validateCommandOptions(const CollectionPtr& collection,
+ const boost::optional<BSONObj>& indexHint,
+ const stdx::unordered_set<NamespaceString>& involvedCollections) {
+ if (indexHint && !involvedCollections.empty()) {
uasserted(6624256,
"For now we can apply hints only for queries involving a single collection");
}
@@ -406,14 +400,20 @@ std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOp
uassert(ErrorCodes::InternalErrorNotSupported,
"Timeseries collections are not supported",
!collection || !collection->getTimeseriesOptions());
+}
- auto curOp = CurOp::get(opCtx);
- curOp->debug().cqfUsed = true;
-
- QueryHints queryHints = getHintsFromQueryKnobs();
-
- PrefixId prefixId;
- const ProjectionName& scanProjName = prefixId.getNextId("scan");
+Metadata populateMetadata(boost::intrusive_ptr<ExpressionContext> expCtx,
+ const CollectionPtr& collection,
+ const stdx::unordered_set<NamespaceString>& involvedCollections,
+ const NamespaceString& nss,
+ const boost::optional<BSONObj>& indexHint,
+ const ProjectionName& scanProjName,
+ const std::string& uuidStr,
+ const std::string& scanDefName,
+ QueryHints& queryHints,
+ PrefixId& prefixId) {
+ auto opCtx = expCtx->opCtx;
+ const bool collectionExists = collection != nullptr;
// Add the base collection metadata.
opt::unordered_map<std::string, optimizer::IndexDefinition> indexDefs;
@@ -440,7 +440,7 @@ std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOp
ScanDefinition({{"type", "mongod"},
{"database", nss.db().toString()},
{"uuid", uuidStr},
- {ScanNode::kDefaultCollectionNameSpec, collNameStr}},
+ {ScanNode::kDefaultCollectionNameSpec, nss.coll().toString()}},
std::move(indexDefs),
std::move(distribution),
collectionExists,
@@ -450,7 +450,7 @@ std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOp
// been accounted for above and isn't included here.
populateAdditionalScanDefs(opCtx,
expCtx,
- pipeline,
+ involvedCollections,
indexHint,
numberOfPartitions,
prefixId,
@@ -458,16 +458,69 @@ std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOp
queryHints._disableIndexes,
queryHints._disableScan);
- Metadata metadata(std::move(scanDefs), numberOfPartitions);
+ return {std::move(scanDefs), numberOfPartitions};
+}
- ABT abtTree = collectionExists ? make<ScanNode>(scanProjName, scanDefName)
- : make<ValueScanNode>(ProjectionNameVector{scanProjName});
- abtTree =
- translatePipelineToABT(metadata, pipeline, scanProjName, std::move(abtTree), prefixId);
+std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOptimizer(
+ OperationContext* opCtx,
+ boost::intrusive_ptr<ExpressionContext> expCtx,
+ const NamespaceString& nss,
+ const CollectionPtr& collection,
+ const boost::optional<BSONObj>& indexHint,
+ std::unique_ptr<Pipeline, PipelineDeleter> pipeline,
+ std::unique_ptr<CanonicalQuery> canonicalQuery) {
+
+ // Ensure that either pipeline or canonicalQuery is set.
+ tassert(624070,
+ "getSBEExecutorViaCascadesOptimizer expects exactly one of the following to be set: "
+ "canonicalQuery, pipeline",
+ static_cast<bool>(pipeline) != static_cast<bool>(canonicalQuery));
+
+ stdx::unordered_set<NamespaceString> involvedCollections;
+ if (pipeline) {
+ involvedCollections = pipeline->getInvolvedCollections();
+ }
+
+ validateCommandOptions(collection, indexHint, involvedCollections);
+
+ auto curOp = CurOp::get(opCtx);
+ curOp->debug().cqfUsed = true;
+
+ const bool collectionExists = collection != nullptr;
+ const std::string uuidStr = collectionExists ? collection->uuid().toString() : "<missing_uuid>";
+ const std::string collNameStr = nss.coll().toString();
+ const std::string scanDefName = collNameStr + "_" + uuidStr;
+ PrefixId prefixId;
+ const ProjectionName& scanProjName = prefixId.getNextId("scan");
+ QueryHints queryHints = getHintsFromQueryKnobs();
+
+ auto metadata = populateMetadata(expCtx,
+ collection,
+ involvedCollections,
+ nss,
+ indexHint,
+ scanProjName,
+ uuidStr,
+ scanDefName,
+ queryHints,
+ prefixId);
+
+ ABT abt = collectionExists ? make<ScanNode>(scanProjName, scanDefName)
+ : make<ValueScanNode>(ProjectionNameVector{scanProjName});
+
+ if (pipeline) {
+ abt = translatePipelineToABT(metadata, *pipeline, scanProjName, std::move(abt), prefixId);
+ } else {
+ abt = translateCanonicalQueryToABT(
+ metadata, *canonicalQuery, scanProjName, std::move(abt), prefixId);
+ }
OPTIMIZER_DEBUG_LOG(
- 6264803, 5, "Translated ABT", "explain"_attr = ExplainGenerator::explainV2(abtTree));
+ 6264803, 5, "Translated ABT", "explain"_attr = ExplainGenerator::explainV2(abt));
+
+ const int64_t numRecords = collectionExists ? collection->numRecords(opCtx) : -1;
+ // TODO SERVER-68919: Move OptPhaseManager construction to its own function.
if (internalQueryCardinalityEstimatorMode == ce::kSampling && collectionExists &&
numRecords > 0) {
Metadata metadataForSampling = metadata;
@@ -483,7 +536,8 @@ std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOp
std::move(metadataForSampling),
std::make_unique<HeuristicCE>(),
std::make_unique<DefaultCosting>(),
- DebugInfo::kDefaultForProd);
+ DebugInfo::kDefaultForProd,
+ {});
OptPhaseManager phaseManager{
OptPhaseManager::getAllRewritesSet(),
@@ -492,11 +546,16 @@ std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOp
std::move(metadata),
std::make_unique<CESamplingTransport>(opCtx, phaseManagerForSampling, numRecords),
std::make_unique<DefaultCosting>(),
- DebugInfo::kDefaultForProd};
- phaseManager.getHints() = queryHints;
+ DebugInfo::kDefaultForProd,
+ std::move(queryHints)};
- return optimizeAndCreateExecutor(
- phaseManager, std::move(abtTree), opCtx, expCtx, nss, collection);
+ return optimizeAndCreateExecutor(phaseManager,
+ std::move(abt),
+ opCtx,
+ expCtx,
+ nss,
+ collection,
+ std::move(canonicalQuery));
} else if (internalQueryCardinalityEstimatorMode == ce::kHistogram &&
ce::CollectionStatistics::hasCollectionStatistics(nss)) {
@@ -508,22 +567,42 @@ std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOp
std::move(metadata),
std::move(ceDerivation),
std::make_unique<DefaultCosting>(),
- DebugInfo::kDefaultForProd};
+ DebugInfo::kDefaultForProd,
+ std::move(queryHints)};
+
+ return optimizeAndCreateExecutor(phaseManager,
+ std::move(abt),
+ opCtx,
+ expCtx,
+ nss,
+ collection,
+ std::move(canonicalQuery));
+ }
+ // Default to using heuristics.
+ OptPhaseManager phaseManager{OptPhaseManager::getAllRewritesSet(),
+ prefixId,
+ std::move(metadata),
+ DebugInfo::kDefaultForProd,
+ std::move(queryHints)};
+
+ return optimizeAndCreateExecutor(
+ phaseManager, std::move(abt), opCtx, expCtx, nss, collection, std::move(canonicalQuery));
+}
- return optimizeAndCreateExecutor(
- phaseManager, std::move(abtTree), opCtx, expCtx, nss, collection);
+std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOptimizer(
+ const CollectionPtr& collection, std::unique_ptr<CanonicalQuery> query) {
- } else {
- // Default to using heuristics.
- OptPhaseManager phaseManager{OptPhaseManager::getAllRewritesSet(),
- prefixId,
- std::move(metadata),
- DebugInfo::kDefaultForProd};
- phaseManager.getHints() = queryHints;
+ boost::optional<BSONObj> indexHint = query->getFindCommandRequest().getHint().isEmpty()
+ ? boost::none
+ : boost::make_optional(query->getFindCommandRequest().getHint());
- return optimizeAndCreateExecutor(
- phaseManager, std::move(abtTree), opCtx, expCtx, nss, collection);
- }
+
+ auto opCtx = query->getOpCtx();
+ auto expCtx = query->getExpCtx();
+ auto nss = query->nss();
+
+ return getSBEExecutorViaCascadesOptimizer(
+ opCtx, expCtx, nss, collection, indexHint, nullptr /* pipeline */, std::move(query));
}
} // namespace mongo
diff --git a/src/mongo/db/query/cqf_get_executor.h b/src/mongo/db/query/cqf_get_executor.h
new file mode 100644
index 00000000000..81454fad196
--- /dev/null
+++ b/src/mongo/db/query/cqf_get_executor.h
@@ -0,0 +1,60 @@
+/**
+ * Copyright (C) 2022-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/catalog/collection.h"
+#include "mongo/db/pipeline/pipeline.h"
+#include "mongo/db/query/canonical_query.h"
+#include "mongo/db/query/plan_executor.h"
+
+namespace mongo {
+
+/**
+ * Returns a PlanExecutor for the given Pipeline.
+ *
+ * The CanonicalQuery parameter allows for code reuse between functions in this file and should not
+ * be set by callers.
+ */
+std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOptimizer(
+ OperationContext* opCtx,
+ boost::intrusive_ptr<ExpressionContext> expCtx,
+ const NamespaceString& nss,
+ const CollectionPtr& collection,
+ const boost::optional<BSONObj>& indexHint,
+ std::unique_ptr<Pipeline, PipelineDeleter> pipeline,
+ std::unique_ptr<CanonicalQuery> = nullptr);
+
+/**
+ * Returns a PlanExecutor for the given CanonicalQuery.
+ */
+std::unique_ptr<PlanExecutor, PlanExecutor::Deleter> getSBEExecutorViaCascadesOptimizer(
+ const CollectionPtr& collection, std::unique_ptr<CanonicalQuery> query);
+
+} // namespace mongo
diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp
index 670fd05ff08..26ef055f60e 100644
--- a/src/mongo/db/query/get_executor.cpp
+++ b/src/mongo/db/query/get_executor.cpp
@@ -71,6 +71,8 @@
#include "mongo/db/query/collation/collation_index_key.h"
#include "mongo/db/query/collation/collator_factory_interface.h"
#include "mongo/db/query/collection_query_info.h"
+#include "mongo/db/query/cqf_command_utils.h"
+#include "mongo/db/query/cqf_get_executor.h"
#include "mongo/db/query/explain.h"
#include "mongo/db/query/index_bounds_builder.h"
#include "mongo/db/query/internal_plans.h"
@@ -1434,6 +1436,10 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> getExecutor(
canonicalQuery->setSbeCompatible(
sbe::isQuerySbeCompatible(&mainColl, canonicalQuery.get(), plannerParams.options));
+ if (isEligibleForBonsai(*canonicalQuery, opCtx, mainColl)) {
+ return getSBEExecutorViaCascadesOptimizer(mainColl, std::move(canonicalQuery));
+ }
+
// Use SBE if 'canonicalQuery' is SBE compatible.
if (!canonicalQuery->getForceClassicEngine() && canonicalQuery->isSbeCompatible()) {
if (extractAndAttachPipelineStages) {
diff --git a/src/mongo/db/query/optimizer/cascades/memo.h b/src/mongo/db/query/optimizer/cascades/memo.h
index c41d46f8f37..7038f236634 100644
--- a/src/mongo/db/query/optimizer/cascades/memo.h
+++ b/src/mongo/db/query/optimizer/cascades/memo.h
@@ -187,6 +187,12 @@ public:
std::unique_ptr<LogicalPropsInterface> logicalPropsDerivation,
std::unique_ptr<CEInterface> ceDerivation);
+ // TODO SERVER-68914: Fix object ownership issues of data members of the Memo class.
+ Memo(const Memo&) = delete;
+ Memo& operator=(const Memo&) = delete;
+ Memo(Memo&&) = delete;
+ Memo& operator=(Memo&&) = delete;
+
const Group& getGroup(GroupIdType groupId) const;
Group& getGroup(GroupIdType groupId);
size_t getGroupCount() const;
diff --git a/src/mongo/db/query/optimizer/opt_phase_manager.cpp b/src/mongo/db/query/optimizer/opt_phase_manager.cpp
index 17e38eecad4..e7d862f25a3 100644
--- a/src/mongo/db/query/optimizer/opt_phase_manager.cpp
+++ b/src/mongo/db/query/optimizer/opt_phase_manager.cpp
@@ -50,14 +50,16 @@ OptPhaseManager::PhaseSet OptPhaseManager::_allRewrites = {OptPhase::ConstEvalPr
OptPhaseManager::OptPhaseManager(OptPhaseManager::PhaseSet phaseSet,
PrefixId& prefixId,
Metadata metadata,
- DebugInfo debugInfo)
+ DebugInfo debugInfo,
+ QueryHints queryHints)
: OptPhaseManager(std::move(phaseSet),
prefixId,
false /*requireRID*/,
std::move(metadata),
std::make_unique<HeuristicCE>(),
std::make_unique<DefaultCosting>(),
- std::move(debugInfo)) {}
+ std::move(debugInfo),
+ std::move(queryHints)) {}
OptPhaseManager::OptPhaseManager(OptPhaseManager::PhaseSet phaseSet,
PrefixId& prefixId,
@@ -65,10 +67,11 @@ OptPhaseManager::OptPhaseManager(OptPhaseManager::PhaseSet phaseSet,
Metadata metadata,
std::unique_ptr<CEInterface> ceDerivation,
std::unique_ptr<CostingInterface> costDerivation,
- DebugInfo debugInfo)
+ DebugInfo debugInfo,
+ QueryHints queryHints)
: _phaseSet(std::move(phaseSet)),
_debugInfo(std::move(debugInfo)),
- _hints(),
+ _hints(std::move(queryHints)),
_metadata(std::move(metadata)),
_memo(_debugInfo,
_metadata,
diff --git a/src/mongo/db/query/optimizer/opt_phase_manager.h b/src/mongo/db/query/optimizer/opt_phase_manager.h
index b06742be250..b81d7845c2d 100644
--- a/src/mongo/db/query/optimizer/opt_phase_manager.h
+++ b/src/mongo/db/query/optimizer/opt_phase_manager.h
@@ -70,14 +70,25 @@ public:
using PhaseSet = opt::unordered_set<OptPhase>;
- OptPhaseManager(PhaseSet phaseSet, PrefixId& prefixId, Metadata metadata, DebugInfo debugInfo);
+ OptPhaseManager(PhaseSet phaseSet,
+ PrefixId& prefixId,
+ Metadata metadata,
+ DebugInfo debugInfo,
+ QueryHints queryHints = {});
OptPhaseManager(PhaseSet phaseSet,
PrefixId& prefixId,
bool requireRID,
Metadata metadata,
std::unique_ptr<CEInterface> ceDerivation,
std::unique_ptr<CostingInterface> costDerivation,
- DebugInfo debugInfo);
+ DebugInfo debugInfo,
+ QueryHints queryHints = {});
+
+ // TODO SERVER-68914: Fix object ownership issues of data members of the Memo class.
+ OptPhaseManager(const OptPhaseManager&) = delete;
+ OptPhaseManager& operator=(const OptPhaseManager&) = delete;
+ OptPhaseManager(OptPhaseManager&&) = delete;
+ OptPhaseManager& operator=(OptPhaseManager&&) = delete;
/**
* Optimization modifies the input argument.