diff options
author | auto-revert-processor <dev-prod-dag@mongodb.com> | 2022-12-14 02:38:42 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-12-14 03:12:23 +0000 |
commit | 67603a852563fcc445c25ba9e8d0a47796c1b078 (patch) | |
tree | 7fea77591c8c063ed138228262d7c5c8a4ba2fca /src/mongo/db | |
parent | c528dba0e3079c744773b5b9357d8c57bbbb6c55 (diff) | |
download | mongo-67603a852563fcc445c25ba9e8d0a47796c1b078.tar.gz |
Revert "SERVER-69508 Generate SBE Expressions by means of constant-folded ABT trees"
This reverts commit 6d6ccec09a77c8b7a02ec8dfb90924509d3ec87d.
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/SConscript | 4 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/db/query/SConscript | 16 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_expression_bm.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_stage_builder.cpp | 93 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_stage_builder_eval_frame.cpp | 68 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_stage_builder_eval_frame.h | 76 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_stage_builder_expression.cpp | 246 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_stage_builder_filter.cpp | 71 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_stage_builder_helpers.cpp | 126 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_stage_builder_helpers.h | 83 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_stage_builder_projection.cpp | 37 |
12 files changed, 173 insertions, 651 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript index 9caaa5f0961..eaadb2bc861 100644 --- a/src/mongo/db/SConscript +++ b/src/mongo/db/SConscript @@ -1457,10 +1457,8 @@ env.Library( 'query/sbe_stage_builder.cpp', 'query/sbe_stage_builder_accumulator.cpp', 'query/sbe_stage_builder_coll_scan.cpp', - 'query/sbe_stage_builder_eval_frame.cpp', 'query/sbe_stage_builder_expression.cpp', 'query/sbe_stage_builder_filter.cpp', - 'query/sbe_stage_builder_helpers.cpp', 'query/sbe_stage_builder_index_scan.cpp', 'query/sbe_stage_builder_lookup.cpp', 'query/sbe_stage_builder_projection.cpp', @@ -1491,7 +1489,6 @@ env.Library( 'cursor_server_params', 'dbdirectclient', 'exec/projection_executor', - 'exec/sbe/query_sbe_stages', 'exec/sbe/query_sbe_storage', 'exec/scoped_timer', 'exec/sort_executor', @@ -1507,6 +1504,7 @@ env.Library( 'query/query_common', 'query/query_plan_cache', 'query/query_planner', + 'query/sbe_stage_builder_helpers', 'repl/repl_coordinator_interface', 's/sharding_api_d', 'session/logical_session_cache', diff --git a/src/mongo/db/exec/sbe/SConscript b/src/mongo/db/exec/sbe/SConscript index 8e2d1762d1e..9be25c996cd 100644 --- a/src/mongo/db/exec/sbe/SConscript +++ b/src/mongo/db/exec/sbe/SConscript @@ -151,7 +151,7 @@ env.Library( ], LIBDEPS=[ '$BUILD_DIR/mongo/db/catalog/catalog_test_fixture', - '$BUILD_DIR/mongo/db/query_exec', + '$BUILD_DIR/mongo/db/query/sbe_stage_builder_helpers', '$BUILD_DIR/mongo/unittest/unittest', 'query_sbe', 'query_sbe_stages', diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript index 305bff56717..1d3e11ccc13 100644 --- a/src/mongo/db/query/SConscript +++ b/src/mongo/db/query/SConscript @@ -116,6 +116,22 @@ env.Library( ) env.Library( + target='sbe_stage_builder_helpers', + source=[ + 'sbe_stage_builder_helpers.cpp', + ], + LIBDEPS=[ + '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/exec/sbe/query_sbe', + '$BUILD_DIR/mongo/db/exec/sbe/query_sbe_stages', + '$BUILD_DIR/mongo/db/exec/sbe/query_sbe_storage', + '$BUILD_DIR/mongo/db/index/index_access_method', + '$BUILD_DIR/mongo/db/query_expressions', + '$BUILD_DIR/mongo/db/storage/execution_context', + ], +) + +env.Library( target='projection_ast', source=[ 'projection.cpp', diff --git a/src/mongo/db/query/sbe_expression_bm.cpp b/src/mongo/db/query/sbe_expression_bm.cpp index 15093a8af6b..4ebee3f4e6d 100644 --- a/src/mongo/db/query/sbe_expression_bm.cpp +++ b/src/mongo/db/query/sbe_expression_bm.cpp @@ -113,7 +113,7 @@ public: "sbe expression benchmark PlanStage", "stage"_attr = debugPrint(stage.get())); - auto expr = evalExpr.extractExpr(state.slotVarMap); + auto expr = evalExpr.extractExpr(); LOGV2_DEBUG(6979802, 1, "sbe expression benchmark EExpression", diff --git a/src/mongo/db/query/sbe_stage_builder.cpp b/src/mongo/db/query/sbe_stage_builder.cpp index 525b88e0913..08898b861a1 100644 --- a/src/mongo/db/query/sbe_stage_builder.cpp +++ b/src/mongo/db/query/sbe_stage_builder.cpp @@ -594,6 +594,25 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder } namespace { +std::unique_ptr<sbe::EExpression> abtToExpr(optimizer::ABT& abt, optimizer::SlotVarMap& slotMap) { + auto env = optimizer::VariableEnvironment::build(abt); + + // Do not use descriptive names here. + auto prefixId = optimizer::PrefixId::create(false /*useDescriptiveNames*/); + // Convert paths into ABT expressions. + optimizer::EvalPathLowering pathLower{prefixId, env}; + pathLower.optimize(abt); + + // Run the constant folding to eliminate lambda applications as they are not directly + // supported by the SBE VM. + optimizer::ConstEval constEval{env}; + constEval.optimize(abt); + + // And finally convert to the SBE expression. + optimizer::SBEExpressionLowering exprLower{env, slotMap}; + return exprLower.optimize(abt); +} + std::unique_ptr<sbe::EExpression> generatePerColumnPredicate(StageBuilderState& state, const MatchExpression* me, const sbe::EVariable& inputVar) { @@ -602,34 +621,34 @@ std::unique_ptr<sbe::EExpression> generatePerColumnPredicate(StageBuilderState& // the element is an object or array. case MatchExpression::REGEX: return generateRegexExpr(state, checked_cast<const RegexMatchExpression*>(me), inputVar) - .extractExpr(state.slotVarMap); + .extractExpr(); case MatchExpression::MOD: return generateModExpr(state, checked_cast<const ModMatchExpression*>(me), inputVar) - .extractExpr(state.slotVarMap); + .extractExpr(); case MatchExpression::BITS_ALL_SET: return generateBitTestExpr(state, checked_cast<const BitTestMatchExpression*>(me), sbe::BitTestBehavior::AllSet, inputVar) - .extractExpr(state.slotVarMap); + .extractExpr(); case MatchExpression::BITS_ALL_CLEAR: return generateBitTestExpr(state, checked_cast<const BitTestMatchExpression*>(me), sbe::BitTestBehavior::AllClear, inputVar) - .extractExpr(state.slotVarMap); + .extractExpr(); case MatchExpression::BITS_ANY_SET: return generateBitTestExpr(state, checked_cast<const BitTestMatchExpression*>(me), sbe::BitTestBehavior::AnySet, inputVar) - .extractExpr(state.slotVarMap); + .extractExpr(); case MatchExpression::BITS_ANY_CLEAR: return generateBitTestExpr(state, checked_cast<const BitTestMatchExpression*>(me), sbe::BitTestBehavior::AnyClear, inputVar) - .extractExpr(state.slotVarMap); + .extractExpr(); case MatchExpression::EXISTS: return makeConstant(sbe::value::TypeTags::Boolean, true); case MatchExpression::LT: @@ -637,37 +656,37 @@ std::unique_ptr<sbe::EExpression> generatePerColumnPredicate(StageBuilderState& checked_cast<const ComparisonMatchExpression*>(me), sbe::EPrimBinary::less, inputVar) - .extractExpr(state.slotVarMap); + .extractExpr(); case MatchExpression::GT: return generateComparisonExpr(state, checked_cast<const ComparisonMatchExpression*>(me), sbe::EPrimBinary::greater, inputVar) - .extractExpr(state.slotVarMap); + .extractExpr(); case MatchExpression::EQ: return generateComparisonExpr(state, checked_cast<const ComparisonMatchExpression*>(me), sbe::EPrimBinary::eq, inputVar) - .extractExpr(state.slotVarMap); + .extractExpr(); case MatchExpression::LTE: return generateComparisonExpr(state, checked_cast<const ComparisonMatchExpression*>(me), sbe::EPrimBinary::lessEq, inputVar) - .extractExpr(state.slotVarMap); + .extractExpr(); case MatchExpression::GTE: return generateComparisonExpr(state, checked_cast<const ComparisonMatchExpression*>(me), sbe::EPrimBinary::greaterEq, inputVar) - .extractExpr(state.slotVarMap); + .extractExpr(); case MatchExpression::MATCH_IN: { auto expr = checked_cast<const InMatchExpression*>(me); tassert(6988583, "Push-down of non-scalar values in $in is not supported.", !expr->hasNonScalarOrNonEmptyValues()); - return generateInExpr(state, expr, inputVar).extractExpr(state.slotVarMap); + return generateInExpr(state, expr, inputVar).extractExpr(); } case MatchExpression::TYPE_OPERATOR: { const auto& expr = checked_cast<const TypeMatchExpression*>(me); @@ -990,12 +1009,8 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder auto sortKeysSet = StringSet{sortKeys.begin(), sortKeys.end()}; auto fieldsAndSortKeys = appendVectorUnique(std::move(fields), std::move(sortKeys)); - auto [outStage, outSlots] = projectFieldsToSlots(std::move(stage), - fieldsAndSortKeys, - resultSlot, - root->nodeId(), - &_slotIdGenerator, - _state.slotVarMap); + auto [outStage, outSlots] = projectFieldsToSlots( + std::move(stage), fieldsAndSortKeys, resultSlot, root->nodeId(), &_slotIdGenerator); stage = std::move(outStage); auto collatorSlot = _data.env->getSlotIfExists("collator"_sd); @@ -1736,8 +1751,7 @@ SlotBasedStageBuilder::buildProjectionDefault(const QuerySolutionNode* root, auto [resultSlot, resultStage] = projectEvalExpr(std::move(projectionExpr), EvalStage{std::move(stage), {}}, root->nodeId(), - &_slotIdGenerator, - _state.slotVarMap); + &_slotIdGenerator); stage = resultStage.extractStage(root->nodeId()); outputs.set(kResult, resultSlot); @@ -2277,8 +2291,8 @@ EvalStage optimizeFieldPaths(StageBuilderState& state, auto rootExpr = rootSlot.has_value() ? EvalExpr{*rootSlot} : EvalExpr{}; auto expr = generateExpression(state, fieldExpr, std::move(rootExpr), &outputs); - auto [slot, projectStage] = projectEvalExpr( - std::move(expr), std::move(stage), nodeId, state.slotIdGenerator, state.slotVarMap); + auto [slot, projectStage] = + projectEvalExpr(std::move(expr), std::move(stage), nodeId, state.slotIdGenerator); state.preGeneratedExprs.emplace(fieldPathStr, slot); stage = std::move(projectStage); @@ -2321,18 +2335,15 @@ std::tuple<sbe::value::SlotVector, EvalStage, std::unique_ptr<sbe::EExpression>> auto [groupByEvalExpr, groupByEvalStage] = generateGroupByKeyImpl( state, fieldExpr, outputs, rootSlot, std::move(stage), nodeId, slotIdGenerator); - auto [slot, projectStage] = projectEvalExpr(std::move(groupByEvalExpr), - std::move(groupByEvalStage), - nodeId, - slotIdGenerator, - state.slotVarMap); + auto [slot, projectStage] = projectEvalExpr( + std::move(groupByEvalExpr), std::move(groupByEvalStage), nodeId, slotIdGenerator); slots.push_back(slot); groupByEvalExpr = slot; stage = std::move(projectStage); exprs.emplace_back(makeConstant(fieldName)); - exprs.emplace_back(groupByEvalExpr.extractExpr(state.slotVarMap)); + exprs.emplace_back(groupByEvalExpr.extractExpr()); } // When there's only one field in the document _id expression, 'Nothing' is converted to @@ -2342,11 +2353,8 @@ std::tuple<sbe::value::SlotVector, EvalStage, std::unique_ptr<sbe::EExpression>> // SERVER-21992 issue goes away and the distinct scan should be able to return 'Nothing' and // 'Null' separately. if (slots.size() == 1) { - auto [slot, projectStage] = projectEvalExpr(makeFillEmptyNull(std::move(exprs[1])), - std::move(stage), - nodeId, - slotIdGenerator, - state.slotVarMap); + auto [slot, projectStage] = projectEvalExpr( + makeFillEmptyNull(std::move(exprs[1])), std::move(stage), nodeId, slotIdGenerator); slots[0] = slot; exprs[1] = makeVariable(slots[0]); stage = std::move(projectStage); @@ -2363,12 +2371,9 @@ std::tuple<sbe::value::SlotVector, EvalStage, std::unique_ptr<sbe::EExpression>> // The group-by field may end up being 'Nothing' and in that case _id: null will be // returned. Calling 'makeFillEmptyNull' for the group-by field takes care of that. - auto fillEmptyNullExpr = makeFillEmptyNull(groupByEvalExpr.extractExpr(state.slotVarMap)); - auto [slot, projectStage] = projectEvalExpr(std::move(fillEmptyNullExpr), - std::move(groupByEvalStage), - nodeId, - slotIdGenerator, - state.slotVarMap); + auto fillEmptyNullExpr = makeFillEmptyNull(groupByEvalExpr.extractExpr()); + auto [slot, projectStage] = projectEvalExpr( + std::move(fillEmptyNullExpr), std::move(groupByEvalStage), nodeId, slotIdGenerator); stage = std::move(projectStage); return {sbe::value::SlotVector{slot}, std::move(stage), nullptr}; @@ -2395,7 +2400,7 @@ std::tuple<sbe::value::SlotVector, EvalStage> generateAccumulator( // as sum(1). auto collatorSlot = state.data->env->getSlotIfExists("collator"_sd); auto accExprs = stage_builder::buildAccumulator( - accStmt, argExpr.extractExpr(state.slotVarMap), collatorSlot, *state.frameIdGenerator); + accStmt, argExpr.extractExpr(), collatorSlot, *state.frameIdGenerator); sbe::value::SlotVector aggSlots; for (auto& accExpr : accExprs) { @@ -3045,12 +3050,8 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder outputs.has(PlanStageSlots::kResult)); auto resultSlot = outputs.get(PlanStageSlots::kResult); - auto [outStage, outSlots] = projectFieldsToSlots(std::move(stage), - fields, - resultSlot, - root->nodeId(), - &_slotIdGenerator, - _state.slotVarMap); + auto [outStage, outSlots] = projectFieldsToSlots( + std::move(stage), fields, resultSlot, root->nodeId(), &_slotIdGenerator); stage = std::move(outStage); for (size_t i = 0; i < fields.size(); ++i) { diff --git a/src/mongo/db/query/sbe_stage_builder_eval_frame.cpp b/src/mongo/db/query/sbe_stage_builder_eval_frame.cpp deleted file mode 100644 index dd6ed6a56dd..00000000000 --- a/src/mongo/db/query/sbe_stage_builder_eval_frame.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/** - * 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/platform/basic.h" - -#include "mongo/db/query/sbe_stage_builder_eval_frame.h" -#include "mongo/db/query/sbe_stage_builder_helpers.h" - -namespace mongo::stage_builder { - -std::unique_ptr<sbe::EExpression> EvalExpr::extractExpr(optimizer::SlotVarMap& varMap) { - if (hasSlot()) { - return sbe::makeE<sbe::EVariable>(stdx::get<sbe::value::SlotId>(_storage)); - } - - if (hasABT()) { - return abtToExpr(stdx::get<optimizer::ABT>(_storage), varMap); - } - - tassert(6950800, - "Unexpected: extractExpr() method invoked on an empty object", - stdx::holds_alternative<std::unique_ptr<sbe::EExpression>>(_storage)); - - return std::move(stdx::get<std::unique_ptr<sbe::EExpression>>(_storage)); -} - -optimizer::ABT EvalExpr::extractABT(optimizer::SlotVarMap& varMap) { - if (hasSlot()) { - auto slotId = stdx::get<sbe::value::SlotId>(_storage); - auto varName = makeVariableName(slotId); - varMap.emplace(varName, slotId); - return optimizer::make<optimizer::Variable>(varName); - } - - tassert(6950801, - "Unexpected: extractABT() method invoked on an EExpression object", - stdx::holds_alternative<optimizer::ABT>(_storage)); - - return std::move(stdx::get<optimizer::ABT>(_storage)); -} - -} // namespace mongo::stage_builder diff --git a/src/mongo/db/query/sbe_stage_builder_eval_frame.h b/src/mongo/db/query/sbe_stage_builder_eval_frame.h index 592f563ee3b..40645b98cea 100644 --- a/src/mongo/db/query/sbe_stage_builder_eval_frame.h +++ b/src/mongo/db/query/sbe_stage_builder_eval_frame.h @@ -31,12 +31,9 @@ #include <stack> -#include "mongo/db/exec/sbe/abt/abt_lower.h" #include "mongo/db/exec/sbe/expressions/expression.h" #include "mongo/db/exec/sbe/stages/co_scan.h" #include "mongo/db/exec/sbe/stages/limit_skip.h" -#include "mongo/db/query/optimizer/node.h" -#include "mongo/db/query/optimizer/syntax/syntax.h" #include "mongo/stdx/variant.h" namespace mongo::stage_builder { @@ -48,77 +45,52 @@ namespace mongo::stage_builder { */ class EvalExpr { public: - EvalExpr() : _storage{false} {} + EvalExpr() : _exprOrSlot{std::unique_ptr<sbe::EExpression>{}} {} - EvalExpr(EvalExpr&& e) : _storage(std::move(e._storage)) { + EvalExpr(EvalExpr&& e) : _exprOrSlot(std::move(e._exprOrSlot)) { e.reset(); } - EvalExpr(std::unique_ptr<sbe::EExpression>&& e) : _storage(std::move(e)) {} + EvalExpr(std::unique_ptr<sbe::EExpression>&& e) : _exprOrSlot(std::move(e)) {} - EvalExpr(sbe::value::SlotId s) : _storage(s) {} - - EvalExpr(const optimizer::ABT& a) : _storage(a) {} - - EvalExpr(optimizer::ABT&& a) : _storage(std::move(a)) {} + EvalExpr(sbe::value::SlotId s) : _exprOrSlot(s) {} EvalExpr& operator=(EvalExpr&& e) { if (this == &e) { return *this; } - _storage = std::move(e._storage); + _exprOrSlot = std::move(e._exprOrSlot); e.reset(); return *this; } EvalExpr& operator=(std::unique_ptr<sbe::EExpression>&& e) { - _storage = std::move(e); + _exprOrSlot = std::move(e); e.reset(); return *this; } EvalExpr& operator=(sbe::value::SlotId s) { - _storage = s; - return *this; - } - - EvalExpr& operator=(optimizer::ABT&& a) { - _storage = std::move(a); + _exprOrSlot = s; return *this; } boost::optional<sbe::value::SlotId> getSlot() const { - return hasSlot() ? boost::make_optional(stdx::get<sbe::value::SlotId>(_storage)) + return hasSlot() ? boost::make_optional(stdx::get<sbe::value::SlotId>(_exprOrSlot)) : boost::none; } bool hasSlot() const { - return stdx::holds_alternative<sbe::value::SlotId>(_storage); - } - - bool hasExpr() const { - return stdx::holds_alternative<std::unique_ptr<sbe::EExpression>>(_storage); - } - - bool hasABT() const { - return stdx::holds_alternative<optimizer::ABT>(_storage); + return stdx::holds_alternative<sbe::value::SlotId>(_exprOrSlot); } EvalExpr clone() const { if (hasSlot()) { - return stdx::get<sbe::value::SlotId>(_storage); - } - - if (hasABT()) { - return stdx::get<optimizer::ABT>(_storage); - } - - if (stdx::holds_alternative<bool>(_storage)) { - return EvalExpr{}; + return stdx::get<sbe::value::SlotId>(_exprOrSlot); } - const auto& expr = stdx::get<std::unique_ptr<sbe::EExpression>>(_storage); + const auto& expr = stdx::get<std::unique_ptr<sbe::EExpression>>(_exprOrSlot); if (expr) { return expr->clone(); } @@ -127,7 +99,7 @@ public: } bool isNull() const { - return stdx::holds_alternative<bool>(_storage); + return !hasSlot() && !stdx::get<std::unique_ptr<sbe::EExpression>>(_exprOrSlot); } explicit operator bool() const { @@ -135,27 +107,19 @@ public: } void reset() { - _storage = false; + _exprOrSlot = std::unique_ptr<sbe::EExpression>{}; } - /** - * Extract the expression on top of the stack as an SBE EExpression node. If the expression is - * stored as an ABT node, it is lowered into an SBE expression, using the provided map to - * convert variable names into slot ids. - */ - std::unique_ptr<sbe::EExpression> extractExpr(optimizer::SlotVarMap& varMap); + std::unique_ptr<sbe::EExpression> extractExpr() { + if (hasSlot()) { + return sbe::makeE<sbe::EVariable>(stdx::get<sbe::value::SlotId>(_exprOrSlot)); + } - /** - * Extract the expression on top of the stack as an ABT node. If the expression is stored as a - * slot id, the mapping between the generated ABT node and the slot id is recorded in the map. - * Throws an exception if the expression is stored as an SBE EExpression. - */ - optimizer::ABT extractABT(optimizer::SlotVarMap& varMap); + return std::move(stdx::get<std::unique_ptr<sbe::EExpression>>(_exprOrSlot)); + } private: - // The bool type as the first option is used to represent the empty storage. - stdx::variant<bool, std::unique_ptr<sbe::EExpression>, sbe::value::SlotId, optimizer::ABT> - _storage; + stdx::variant<std::unique_ptr<sbe::EExpression>, sbe::value::SlotId> _exprOrSlot; }; /** diff --git a/src/mongo/db/query/sbe_stage_builder_expression.cpp b/src/mongo/db/query/sbe_stage_builder_expression.cpp index a3076c3bebd..4779d78c991 100644 --- a/src/mongo/db/query/sbe_stage_builder_expression.cpp +++ b/src/mongo/db/query/sbe_stage_builder_expression.cpp @@ -92,55 +92,29 @@ struct ExpressionVisitorContext { invariant(exprStack.size() >= arity); } - /** - * Return whether the last @arity items in the stack are not holding EExpression objects. - */ - bool hasAllAbtEligibleEntries(size_t arity) { - bool allAbt = true; - auto it = exprStack.crbegin(); - for (size_t i = 0; it != exprStack.crend() && i < arity; i++, it++) { - allAbt &= it->hasABT() || it->hasSlot(); - } - return allAbt; - } - std::unique_ptr<sbe::EExpression> popExpr() { tassert(6987500, "tried to pop from empty EvalExpr stack", !exprStack.empty()); - auto expr = std::move(exprStack.back()); - exprStack.pop_back(); - return expr.extractExpr(state.slotVarMap); + auto expr = std::move(exprStack.top()); + exprStack.pop(); + return expr.extractExpr(); } void pushExpr(EvalExpr expr) { - exprStack.emplace_back(std::move(expr)); - } - - optimizer::ABT popABTExpr() { - tassert(6987504, "tried to pop from empty EvalExpr stack", !exprStack.empty()); - - auto expr = std::move(exprStack.back()); - exprStack.pop_back(); - return expr.extractABT(state.slotVarMap); + exprStack.push(std::move(expr)); } EvalExpr done() { tassert(6987501, "expected exactly one EvalExpr on the stack", exprStack.size() == 1); - auto expr = std::move(exprStack.back()); - exprStack.pop_back(); + auto expr = std::move(exprStack.top()); + exprStack.pop(); return expr; } - optimizer::ProjectionName registerVariable(sbe::value::SlotId slotId) { - auto varName = stage_builder::makeVariableName(slotId); - state.slotVarMap.emplace(varName, slotId); - return varName; - } - StageBuilderState& state; - std::vector<EvalExpr> exprStack; + std::stack<EvalExpr> exprStack; EvalExpr rootExpr; @@ -237,89 +211,6 @@ std::unique_ptr<sbe::EExpression> generateTraverse( } /** - * Version of the generateTraverse helper functions that process ABT trees instead of EExpression - * ones. - */ -optimizer::ABT generateTraverseHelper( - ExpressionVisitorContext* context, - boost::optional<optimizer::ABT>&& inputExpr, - const FieldPath& fp, - size_t level, - sbe::value::FrameIdGenerator* frameIdGenerator, - boost::optional<sbe::value::SlotId> topLevelFieldSlot = boost::none) { - using namespace std::literals; - - invariant(level < fp.getPathLength()); - tassert(6950802, - "Expected an input expression or top level field", - inputExpr.has_value() || topLevelFieldSlot.has_value()); - - // Generate an expression to read a sub-field at the current nested level. - auto fieldName = makeABTConstant(fp.getFieldName(level)); - auto fieldExpr = topLevelFieldSlot - ? optimizer::make<optimizer::Variable>(context->registerVariable(*topLevelFieldSlot)) - : makeABTFunction("getField"_sd, std::move(*inputExpr), std::move(fieldName)); - - if (level == fp.getPathLength() - 1) { - // For the last level, we can just return the field slot without the need for a - // traverse stage. - return fieldExpr; - } - - // Generate nested traversal. - auto lambdaFrameId = frameIdGenerator->generate(); - auto lambdaParamName = makeLocalVariableName(lambdaFrameId, 0); - boost::optional<optimizer::ABT> lambdaParam = - optimizer::make<optimizer::Variable>(lambdaParamName); - - auto resultExpr = - generateTraverseHelper(context, std::move(lambdaParam), fp, level + 1, frameIdGenerator); - - auto lambdaExpr = - optimizer::make<optimizer::LambdaAbstraction>(lambdaParamName, std::move(resultExpr)); - - // Generate the traverse stage for the current nested level. - return makeABTFunction( - "traverseP"_sd, std::move(fieldExpr), std::move(lambdaExpr), optimizer::Constant::int32(1)); -} - -optimizer::ABT generateTraverse( - ExpressionVisitorContext* context, - boost::optional<optimizer::ABT>&& inputExpr, - bool expectsDocumentInputOnly, - const FieldPath& fp, - sbe::value::FrameIdGenerator* frameIdGenerator, - boost::optional<sbe::value::SlotId> topLevelFieldSlot = boost::none) { - size_t level = 0; - - if (expectsDocumentInputOnly) { - // When we know for sure that 'inputExpr' will be a document and _not_ an array (such as - // when accessing a field on the root document), we can generate a simpler expression. - return generateTraverseHelper( - context, std::move(inputExpr), fp, level, frameIdGenerator, topLevelFieldSlot); - } else { - tassert(6950803, "Expected an input expression", inputExpr.has_value()); - // The general case: the value in the 'inputExpr' may be an array that will require - // traversal. - auto lambdaFrameId = frameIdGenerator->generate(); - auto lambdaParamName = makeLocalVariableName(lambdaFrameId, 0); - boost::optional<optimizer::ABT> lambdaParam = - optimizer::make<optimizer::Variable>(lambdaParamName); - - auto resultExpr = - generateTraverseHelper(context, std::move(lambdaParam), fp, level, frameIdGenerator); - - auto lambdaExpr = - optimizer::make<optimizer::LambdaAbstraction>(lambdaParamName, std::move(resultExpr)); - - return makeABTFunction("traverseP"_sd, - std::move(*inputExpr), - std::move(lambdaExpr), - optimizer::Constant::int32(1)); - } -} - -/** * Generates an EExpression that converts the input to upper or lower case. */ void generateStringCaseConversionExpression(ExpressionVisitorContext* _context, @@ -781,7 +672,7 @@ public: void visit(const ExpressionConstant* expr) final { auto [tag, val] = makeValue(expr->getValue()); - _context->pushExpr(makeABTConstant(tag, val)); + _context->pushExpr(sbe::makeE<sbe::EConstant>(tag, val)); } void visit(const ExpressionAbs* expr) final { @@ -989,77 +880,6 @@ public: } void visit(const ExpressionCompare* expr) final { _context->ensureArity(2); - // Build an ABT node if both operands are stored as either a slot or an ABT expression, and - // the comparison doesn't involve a collation, because binary operations in ABT don't - // support it yet. - if (!_context->state.data->env->getSlotIfExists("collator"_sd) && - _context->hasAllAbtEligibleEntries(2)) { - auto rhs = _context->popABTExpr(); - auto lhs = _context->popABTExpr(); - auto lhsRef = makeLocalVariableName(_context->state.frameIdGenerator->generate(), 0); - auto rhsRef = makeLocalVariableName(_context->state.frameIdGenerator->generate(), 0); - - auto comparisonOperator = [expr]() { - switch (expr->getOp()) { - case ExpressionCompare::CmpOp::EQ: - return optimizer::Operations::Eq; - case ExpressionCompare::CmpOp::NE: - return optimizer::Operations::Neq; - case ExpressionCompare::CmpOp::GT: - return optimizer::Operations::Gt; - case ExpressionCompare::CmpOp::GTE: - return optimizer::Operations::Gte; - case ExpressionCompare::CmpOp::LT: - return optimizer::Operations::Lt; - case ExpressionCompare::CmpOp::LTE: - return optimizer::Operations::Lte; - case ExpressionCompare::CmpOp::CMP: - return optimizer::Operations::Cmp3w; - } - MONGO_UNREACHABLE; - }(); - - // We use the "cmp3w" primitive for every comparison, because it "type brackets" its - // comparisons (for example, a number will always compare as less than a string). The - // other comparison primitives are designed for comparing values of the same type. - auto cmp3w = - optimizer::make<optimizer::BinaryOp>(optimizer::Operations::Cmp3w, - optimizer::make<optimizer::Variable>(lhsRef), - optimizer::make<optimizer::Variable>(rhsRef)); - auto cmp = (comparisonOperator == optimizer::Operations::Cmp3w) - ? std::move(cmp3w) - : optimizer::make<optimizer::BinaryOp>( - comparisonOperator, std::move(cmp3w), optimizer::Constant::int32(0)); - - // If either operand evaluates to "Nothing", then the entire operation expressed by - // 'cmp' will also evaluate to "Nothing". MQL comparisons, however, treat "Nothing" as - // if it is a value that is less than everything other than MinKey. (Notably, two - // expressions that evaluate to "Nothing" are considered equal to each other.) We also - // need to explicitly check for 'bsonUndefined' type because it is considered equal to - // "Nothing" according to MQL semantics. - auto generateExists = [&](const optimizer::ProjectionName& var) { - auto undefinedTypeMask = static_cast<int32_t>(getBSONTypeMask(BSONType::Undefined)); - return optimizer::make<optimizer::BinaryOp>( - optimizer::Operations::And, - makeABTFunction("exists"_sd, optimizer::make<optimizer::Variable>(var)), - makeABTFunction("typeMatch"_sd, - optimizer::make<optimizer::Variable>(var), - optimizer::Constant::int32(~undefinedTypeMask))); - }; - - auto nothingFallbackCmp = optimizer::make<optimizer::BinaryOp>( - comparisonOperator, generateExists(lhsRef), generateExists(rhsRef)); - - auto cmpWithFallback = optimizer::make<optimizer::BinaryOp>( - optimizer::Operations::FillEmpty, std::move(cmp), std::move(nothingFallbackCmp)); - - _context->pushExpr(optimizer::make<optimizer::Let>( - lhsRef, lhs, optimizer::make<optimizer::Let>(rhsRef, rhs, cmpWithFallback))); - - return; - } - // Fallback to generate an SBE EExpression node. - sbe::EExpression::Vector operands(2); for (auto it = operands.rbegin(); it != operands.rend(); ++it) { *it = _context->popExpr(); @@ -2024,10 +1844,8 @@ public: expr->getFieldPath().getPathLength() != 1 && !expr->isVariableReference()); if (auto optionalSlot = it->second.getSlot(); optionalSlot) { _context->pushExpr(*optionalSlot); - } else if (it->second.hasABT()) { - _context->pushExpr(it->second.extractABT(_context->state.slotVarMap)); } else { - auto preGeneratedExpr = it->second.extractExpr(_context->state.slotVarMap); + auto preGeneratedExpr = it->second.extractExpr(); _context->pushExpr(preGeneratedExpr->clone()); it->second = std::move(preGeneratedExpr); } @@ -2055,7 +1873,7 @@ public: } else if (expr->getVariableId() == Variables::kRemoveId) { // For the field paths that begin with "$$REMOVE", we always produce Nothing, // so no traversal is necessary. - _context->pushExpr(optimizer::Constant::nothing()); + _context->pushExpr(sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::Nothing, 0)); return; } else { auto it = Variables::kIdToBuiltinVarName.find(expr->getVariableId()); @@ -2094,24 +1912,13 @@ public: !inputExpr.isNull() || topLevelFieldSlot.has_value()); // Dereference a dotted path, which may contain arrays requiring implicit traversal. - if (inputExpr.hasExpr()) { - auto resultExpr = generateTraverse(inputExpr.extractExpr(_context->state.slotVarMap), - expectsDocumentInputOnly, - *fp, - _context->state.frameIdGenerator, - topLevelFieldSlot); - - _context->pushExpr(std::move(resultExpr)); - } else { - auto resultExpr = generateTraverse(_context, - inputExpr.extractABT(_context->state.slotVarMap), - expectsDocumentInputOnly, - *fp, - _context->state.frameIdGenerator, - topLevelFieldSlot); - - _context->pushExpr(std::move(resultExpr)); - } + auto resultExpr = generateTraverse(inputExpr.extractExpr(), + expectsDocumentInputOnly, + *fp, + _context->state.frameIdGenerator, + topLevelFieldSlot); + + _context->pushExpr(std::move(resultExpr)); } void visit(const ExpressionFilter* expr) final { // Remove index tracking current child of $filter expression, since it is not used anymore. @@ -3121,25 +2928,10 @@ private: // Empty $and and $or always evaluate to their logical operator's identity value: true // and false, respectively. auto logicIdentityVal = (logicOp == sbe::EPrimBinary::logicAnd); - _context->pushExpr(optimizer::Constant::boolean(logicIdentityVal)); - return; - } - - if (_context->hasAllAbtEligibleEntries(numChildren)) { - std::vector<optimizer::ABT> exprs; - for (size_t i = 0; i < numChildren; ++i) { - exprs.emplace_back( - makeFillEmptyFalse(makeABTFunction("coerceToBool", _context->popABTExpr()))); - } - std::reverse(exprs.begin(), exprs.end()); - - _context->pushExpr(makeBalancedBooleanOpTree(logicOp == sbe::EPrimBinary::logicAnd - ? optimizer::Operations::And - : optimizer::Operations::Or, - std::move(exprs))); + _context->pushExpr(sbe::makeE<sbe::EConstant>( + sbe::value::TypeTags::Boolean, sbe::value::bitcastFrom<bool>(logicIdentityVal))); return; } - // Fallback to generate an SBE EExpression node. std::vector<std::unique_ptr<sbe::EExpression>> exprs; for (size_t i = 0; i < numChildren; ++i) { diff --git a/src/mongo/db/query/sbe_stage_builder_filter.cpp b/src/mongo/db/query/sbe_stage_builder_filter.cpp index 396c3d5b903..ca050bc5fb6 100644 --- a/src/mongo/db/query/sbe_stage_builder_filter.cpp +++ b/src/mongo/db/query/sbe_stage_builder_filter.cpp @@ -160,10 +160,9 @@ struct MatchExpressionVisitorContext { projectCurrentExprToOutputSlot(this); } invariant(frame.exprsCount() == 1); - frame.setStage(makeFilter<false>( - frame.extractStage(), - stateHelper.getBool(frame.popExpr().extractExpr(state.slotVarMap)), - planNodeId)); + frame.setStage(makeFilter<false>(frame.extractStage(), + stateHelper.getBool(frame.popExpr().extractExpr()), + planNodeId)); } if (outputSlot && stateHelper.stateContainsValue()) { @@ -226,11 +225,8 @@ struct MatchExpressionVisitorContext { void projectCurrentExprToOutputSlot(MatchExpressionVisitorContext* context) { tassert(5291405, "Output slot is not empty", !context->outputSlot); auto& frame = context->evalStack.topFrame(); - auto [projectedExprSlot, stage] = projectEvalExpr(frame.popExpr(), - frame.extractStage(), - context->planNodeId, - context->state.slotIdGenerator, - context->state.slotVarMap); + auto [projectedExprSlot, stage] = projectEvalExpr( + frame.popExpr(), frame.extractStage(), context->planNodeId, context->state.slotIdGenerator); context->outputSlot = projectedExprSlot; frame.pushExpr(projectedExprSlot); frame.setStage(std::move(stage)); @@ -411,7 +407,6 @@ EvalExprStagePair generatePathTraversal(EvalStage inputStage, PlanNodeId planNodeId, sbe::value::SlotIdGenerator* slotIdGenerator, sbe::value::FrameIdGenerator* frameIdGenerator, - optimizer::SlotVarMap& varSlotMap, const MakePredicateFn& makePredicate, LeafTraversalMode mode, const FilterStateHelper& stateHelper) { @@ -557,8 +552,7 @@ EvalExprStagePair generatePathTraversal(EvalStage inputStage, std::tie(innerExpr, innerBranch) = isLeafField // Base case: Evaluate the predicate. Predicate returns boolean value, we need to convert it // to state using 'stateHelper.makePredicateCombinator'. - ? stateHelper.makePredicateCombinator(makePredicate(innerInputSlot, std::move(innerBranch)), - varSlotMap) + ? stateHelper.makePredicateCombinator(makePredicate(innerInputSlot, std::move(innerBranch))) // Recursive case. : generatePathTraversal(std::move(innerBranch), innerInputSlot, @@ -568,7 +562,6 @@ EvalExprStagePair generatePathTraversal(EvalStage inputStage, planNodeId, slotIdGenerator, frameIdGenerator, - varSlotMap, makePredicate, mode, stateHelper); @@ -589,7 +582,7 @@ EvalExprStagePair generatePathTraversal(EvalStage inputStage, stateHelper.makeInitialState(stateHelper.getBool(state.clone())), state.clone()); }, - innerExpr.extractExpr(varSlotMap)); + innerExpr.extractExpr()); } sbe::value::SlotId innerResultSlot; @@ -597,8 +590,7 @@ EvalExprStagePair generatePathTraversal(EvalStage inputStage, projectEvalExpr(std::move(innerExpr), std::move(innerBranch), // NOLINT(bugprone-use-after-move) planNodeId, - slotIdGenerator, - varSlotMap); + slotIdGenerator); // Generate the traverse stage for the current nested level. There are several cases covered // during this phase: @@ -709,8 +701,7 @@ void generatePredicateImpl(MatchExpressionVisitorContext* context, // slot" that holds the value of the ElemMatchValueMatchExpression's field path. auto result = makePredicate(*frame.data().inputSlot, frame.extractStage()); if (useCombinator) { - return context->stateHelper.makePredicateCombinator(std::move(result), - context->state.slotVarMap); + return context->stateHelper.makePredicateCombinator(std::move(result)); } return result; } @@ -729,8 +720,7 @@ void generatePredicateImpl(MatchExpressionVisitorContext* context, // we can just evaluate the predicate on the slot directly and return. auto result = makePredicate(*slot, frame.extractStage()); if (useCombinator) { - return context->stateHelper.makePredicateCombinator( - std::move(result), context->state.slotVarMap); + return context->stateHelper.makePredicateCombinator(std::move(result)); } return result; } @@ -772,7 +762,6 @@ void generatePredicateImpl(MatchExpressionVisitorContext* context, context->planNodeId, context->state.slotIdGenerator, context->state.frameIdGenerator, - context->state.slotVarMap, makePredicate, mode, context->stateHelper); @@ -857,8 +846,7 @@ void generateComparison(MatchExpressionVisitorContext* context, sbe::EPrimBinary::Op binaryOp) { auto makePredicateExpr = [context, expr, binaryOp](const sbe::EVariable& var) -> std::unique_ptr<sbe::EExpression> { - return generateComparisonExpr(context->state, expr, binaryOp, var) - .extractExpr(context->state.slotVarMap); + return generateComparisonExpr(context->state, expr, binaryOp, var).extractExpr(); }; // A 'kArrayAndItsElements' traversal mode matches the following semantics: when the path we are @@ -899,8 +887,7 @@ void generateBitTest(MatchExpressionVisitorContext* context, const sbe::BitTestBehavior& bitOp) { auto makePredicateExpr = [context, expr, bitOp](const sbe::EVariable& var) -> std::unique_ptr<sbe::EExpression> { - return generateBitTestExpr(context->state, expr, bitOp, var) - .extractExpr(context->state.slotVarMap); + return generateBitTestExpr(context->state, expr, bitOp, var).extractExpr(); }; generatePredicateExpr( @@ -950,7 +937,6 @@ void buildLogicalExpression(sbe::EPrimBinary::Op op, std::move(branches), context->planNodeId, context->state.slotIdGenerator, - context->state.slotVarMap, context->stateHelper); frame.pushExpr(std::move(expr)); @@ -1262,8 +1248,7 @@ public: invariant(frame.exprsCount() > 0); frame.setStage( makeFilter<false>(frame.extractStage(), - _context->stateHelper.getBool( - frame.popExpr().extractExpr(_context->state.slotVarMap)), + _context->stateHelper.getBool(frame.popExpr().extractExpr()), _context->planNodeId)); } return; @@ -1304,8 +1289,7 @@ public: auto [predicateSlot, predicateStage] = projectEvalExpr(std::move(expr), std::move(stage), _context->planNodeId, - _context->state.slotIdGenerator, - _context->state.slotVarMap); + _context->state.slotIdGenerator); auto isObjectOrArrayExpr = makeBinaryOp(sbe::EPrimBinary::logicOr, @@ -1363,14 +1347,12 @@ public: std::move(childStages), _context->planNodeId, _context->state.slotIdGenerator, - _context->state.slotVarMap, _context->stateHelper); auto filterPair = projectEvalExpr(std::move(filterExpr), std::move(filterStage), _context->planNodeId, - _context->state.slotIdGenerator, - _context->state.slotVarMap); + _context->state.slotIdGenerator); // We're using 'kDoNotTraverseLeaf' traverse mode, so we're guaranteed that 'makePredcate' // will only be called once, so it's safe to bind the reference to 'filterStage' subtree @@ -1440,8 +1422,7 @@ public: _context->slots); // We need to convert the result of the '{$expr: ..}' expression to a boolean value. - auto logicExpr = makeFillEmptyFalse( - makeFunction("coerceToBool", expr.extractExpr(_context->state.slotVarMap))); + auto logicExpr = makeFillEmptyFalse(makeFunction("coerceToBool", expr.extractExpr())); frame.pushExpr(_context->stateHelper.makeState(std::move(logicExpr))); } @@ -1609,8 +1590,7 @@ public: auto makePredicateExpr = [context = _context, expr](const sbe::EVariable& var) -> std::unique_ptr<sbe::EExpression> { - return generateModExpr(context->state, expr, var) - .extractExpr(context->state.slotVarMap); + return generateModExpr(context->state, expr, var).extractExpr(); }; generatePredicateExpr( @@ -1627,8 +1607,8 @@ public: // This matches the behaviour of classic engine, which does not pass 'MatchDetails' object // to children of NOR and thus does not get any information on 'elemMatchKey' from them. auto& frame = _context->evalStack.topFrame(); - frame.pushExpr(_context->stateHelper.makeState(makeNot(_context->stateHelper.getBool( - frame.popExpr().extractExpr(_context->state.slotVarMap))))); + frame.pushExpr(_context->stateHelper.makeState( + makeNot(_context->stateHelper.getBool(frame.popExpr().extractExpr())))); } void visit(const NotMatchExpression* expr) final { @@ -1638,8 +1618,8 @@ public: // Here we discard the index value of the state even if it was set by expressions below NOT. // This matches the behaviour of classic engine, which does not pass 'MatchDetails' object // to children of NOT and thus does not get any information on 'elemMatchKey' from them. - frame.pushExpr(_context->stateHelper.makeState(makeNot(_context->stateHelper.getBool( - frame.popExpr().extractExpr(_context->state.slotVarMap))))); + frame.pushExpr(_context->stateHelper.makeState( + makeNot(_context->stateHelper.getBool(frame.popExpr().extractExpr())))); } void visit(const OrMatchExpression* expr) final { @@ -1650,8 +1630,7 @@ public: auto makePredicateExpr = [context = _context, expr](const sbe::EVariable& var) -> std::unique_ptr<sbe::EExpression> { - return generateRegexExpr(context->state, expr, var) - .extractExpr(context->state.slotVarMap); + return generateRegexExpr(context->state, expr, var).extractExpr(); }; generatePredicateExpr( @@ -1719,8 +1698,7 @@ public: auto& frame = _context->evalStack.topFrame(); auto resultExpr = generateWhereExpr(_context->state, expr, sbe::EVariable{*frame.data().inputSlot}); - frame.pushExpr( - _context->stateHelper.makeState(resultExpr.extractExpr(_context->state.slotVarMap))); + frame.pushExpr(_context->stateHelper.makeState(resultExpr.extractExpr())); } void visit(const WhereNoOpMatchExpression* expr) final {} @@ -1747,8 +1725,7 @@ public: invariant(frame.exprsCount() > 0); frame.setStage( makeFilter<false>(frame.extractStage(), - _context->stateHelper.getBool( - frame.popExpr().extractExpr(_context->state.slotVarMap)), + _context->stateHelper.getBool(frame.popExpr().extractExpr()), _context->planNodeId)); return; } diff --git a/src/mongo/db/query/sbe_stage_builder_helpers.cpp b/src/mongo/db/query/sbe_stage_builder_helpers.cpp index 68652223cf1..188ab751c1e 100644 --- a/src/mongo/db/query/sbe_stage_builder_helpers.cpp +++ b/src/mongo/db/query/sbe_stage_builder_helpers.cpp @@ -49,8 +49,6 @@ #include "mongo/db/exec/sbe/values/bson.h" #include "mongo/db/index/index_access_method.h" #include "mongo/db/matcher/matcher_type_set.h" -#include "mongo/db/query/optimizer/rewrites/const_eval.h" -#include "mongo/db/query/optimizer/rewrites/path_lower.h" #include "mongo/db/query/sbe_stage_builder.h" #include "mongo/db/storage/execution_context.h" #include "mongo/logv2/log.h" @@ -322,8 +320,7 @@ std::pair<sbe::value::SlotId, EvalStage> projectEvalExpr( EvalExpr expr, EvalStage stage, PlanNodeId planNodeId, - sbe::value::SlotIdGenerator* slotIdGenerator, - optimizer::SlotVarMap& slotVarMap) { + sbe::value::SlotIdGenerator* slotIdGenerator) { // If expr's value is already in a slot, return the slot. if (expr.hasSlot()) { return {*expr.getSlot(), std::move(stage)}; @@ -332,7 +329,7 @@ std::pair<sbe::value::SlotId, EvalStage> projectEvalExpr( // If expr's value is an expression, create a ProjectStage to evaluate the expression // into a slot. auto slot = slotIdGenerator->generate(); - stage = makeProject(std::move(stage), planNodeId, slot, expr.extractExpr(slotVarMap)); + stage = makeProject(std::move(stage), planNodeId, slot, expr.extractExpr()); return {slot, std::move(stage)}; } @@ -540,8 +537,7 @@ std::unique_ptr<sbe::EExpression> makeIfNullExpr( EvalExprStagePair generateUnion(std::vector<EvalExprStagePair> branches, BranchFn branchFn, PlanNodeId planNodeId, - sbe::value::SlotIdGenerator* slotIdGenerator, - optimizer::SlotVarMap& slotVarMap) { + sbe::value::SlotIdGenerator* slotIdGenerator) { sbe::PlanStage::Vector stages; std::vector<sbe::value::SlotVector> inputs; stages.reserve(branches.size()); @@ -553,11 +549,10 @@ EvalExprStagePair generateUnion(std::vector<EvalExprStagePair> branches, if (!branchFn || i + 1 == branches.size()) { return projectEvalExpr( - std::move(expr), std::move(stage), planNodeId, slotIdGenerator, slotVarMap); + std::move(expr), std::move(stage), planNodeId, slotIdGenerator); } - return branchFn( - std::move(expr), std::move(stage), planNodeId, slotIdGenerator, slotVarMap); + return branchFn(std::move(expr), std::move(stage), planNodeId, slotIdGenerator); }(); stages.emplace_back(stage.extractStage(planNodeId)); @@ -575,35 +570,14 @@ EvalExprStagePair generateUnion(std::vector<EvalExprStagePair> branches, EvalExprStagePair generateSingleResultUnion(std::vector<EvalExprStagePair> branches, BranchFn branchFn, PlanNodeId planNodeId, - sbe::value::SlotIdGenerator* slotIdGenerator, - optimizer::SlotVarMap& slotVarMap) { - auto [unionEvalExpr, unionEvalStage] = generateUnion( - std::move(branches), std::move(branchFn), planNodeId, slotIdGenerator, slotVarMap); + sbe::value::SlotIdGenerator* slotIdGenerator) { + auto [unionEvalExpr, unionEvalStage] = + generateUnion(std::move(branches), std::move(branchFn), planNodeId, slotIdGenerator); return {std::move(unionEvalExpr), EvalStage{makeLimitTree(unionEvalStage.extractStage(planNodeId), planNodeId), unionEvalStage.extractOutSlots()}}; } -optimizer::ABT makeBalancedBooleanOpTreeImpl(optimizer::Operations logicOp, - std::vector<optimizer::ABT>& leaves, - size_t from, - size_t until) { - invariant(from < until); - if (from + 1 == until) { - return std::move(leaves[from]); - } else { - size_t mid = (from + until) / 2; - auto lhs = makeBalancedBooleanOpTreeImpl(logicOp, leaves, from, mid); - auto rhs = makeBalancedBooleanOpTreeImpl(logicOp, leaves, mid, until); - return optimizer::make<optimizer::BinaryOp>(logicOp, std::move(lhs), std::move(rhs)); - } -} - -optimizer::ABT makeBalancedBooleanOpTree(optimizer::Operations logicOp, - std::vector<optimizer::ABT> leaves) { - return makeBalancedBooleanOpTreeImpl(logicOp, leaves, 0, leaves.size()); -} - std::unique_ptr<sbe::EExpression> makeBalancedBooleanOpTreeImpl( sbe::EPrimBinary::Op logicOp, std::vector<std::unique_ptr<sbe::EExpression>>& leaves, @@ -629,7 +603,6 @@ EvalExprStagePair generateShortCircuitingLogicalOp(sbe::EPrimBinary::Op logicOp, std::vector<EvalExprStagePair> branches, PlanNodeId planNodeId, sbe::value::SlotIdGenerator* slotIdGenerator, - optimizer::SlotVarMap& slotVarMap, const FilterStateHelper& stateHelper) { invariant(logicOp == sbe::EPrimBinary::logicAnd || logicOp == sbe::EPrimBinary::logicOr); @@ -639,11 +612,7 @@ EvalExprStagePair generateShortCircuitingLogicalOp(sbe::EPrimBinary::Op logicOp, // NOTE: There is no technical reason for that. We could support index tracking for OR // expression, but this would differ from the existing behaviour. auto& [expr, _] = branches.back(); - if (expr.hasExpr()) { - expr = stateHelper.makeState(stateHelper.getBool(expr.extractExpr(slotVarMap))); - } else { - expr = stateHelper.makeState(stateHelper.getBool(expr.extractABT(slotVarMap))); - } + expr = stateHelper.makeState(stateHelper.getBool(expr.extractExpr())); } // For AND and OR, if 'branches' only has one element, we can just return branches[0]. @@ -651,34 +620,20 @@ EvalExprStagePair generateShortCircuitingLogicalOp(sbe::EPrimBinary::Op logicOp, return std::move(branches[0]); } - bool exprOnlyBranches = true, allAbtEligibleBranches = true; + bool exprOnlyBranches = true; for (const auto& [expr, stage] : branches) { - exprOnlyBranches &= stage.isNull(); - allAbtEligibleBranches &= expr.hasABT() || expr.hasSlot(); + if (!stage.isNull()) { + exprOnlyBranches = false; + break; + } } if (exprOnlyBranches) { - if (allAbtEligibleBranches) { - std::vector<optimizer::ABT> leaves; - leaves.reserve(branches.size()); - for (auto& branch : branches) { - auto& [expr, _] = branch; - leaves.emplace_back(stateHelper.getBool(expr.extractABT(slotVarMap))); - } - // Create the balanced binary tree to keep the tree shallow and safe for recursion. - auto exprOnlyOp = makeBalancedBooleanOpTree(logicOp == sbe::EPrimBinary::logicAnd - ? optimizer::Operations::And - : optimizer::Operations::Or, - std::move(leaves)); - return {EvalExpr{std::move(exprOnlyOp)}, EvalStage{}}; - } - // Fallback to generate an SBE EExpression node. - std::vector<std::unique_ptr<sbe::EExpression>> leaves; leaves.reserve(branches.size()); for (auto& branch : branches) { auto& [expr, _] = branch; - leaves.push_back(stateHelper.getBool(expr.extractExpr(slotVarMap))); + leaves.push_back(stateHelper.getBool(expr.extractExpr())); } // Create the balanced binary tree to keep the tree shallow and safe for recursion. auto exprOnlyOp = makeBalancedBooleanOpTree(logicOp, std::move(leaves)); @@ -695,8 +650,7 @@ EvalExprStagePair generateShortCircuitingLogicalOp(sbe::EPrimBinary::Op logicOp, auto branchFn = [logicOp, &stateHelper](EvalExpr expr, EvalStage stage, PlanNodeId planNodeId, - sbe::value::SlotIdGenerator* slotIdGenerator, - optimizer::SlotVarMap& slotVarMap) { + sbe::value::SlotIdGenerator* slotIdGenerator) { // Create a FilterStage for each branch (except the last one). If a branch's filter // condition is true, it will "short-circuit" the evaluation process. For AND, short- // circuiting should happen if an operand evalautes to false. For OR, short-circuiting @@ -704,7 +658,7 @@ EvalExprStagePair generateShortCircuitingLogicalOp(sbe::EPrimBinary::Op logicOp, // Set up an output value to be returned if short-circuiting occurs. For AND, when // short-circuiting occurs, the output returned should be false. For OR, when short- // circuiting occurs, the output returned should be true. - auto filterExpr = stateHelper.getBool(expr.extractExpr(slotVarMap)); + auto filterExpr = stateHelper.getBool(expr.extractExpr()); if (logicOp == sbe::EPrimBinary::logicAnd) { filterExpr = makeNot(std::move(filterExpr)); } @@ -717,8 +671,7 @@ EvalExprStagePair generateShortCircuitingLogicalOp(sbe::EPrimBinary::Op logicOp, return std::make_pair(resultSlot, std::move(stage)); }; - return generateSingleResultUnion( - std::move(branches), branchFn, planNodeId, slotIdGenerator, slotVarMap); + return generateSingleResultUnion(std::move(branches), branchFn, planNodeId, slotIdGenerator); } std::pair<sbe::value::SlotId, std::unique_ptr<sbe::PlanStage>> generateVirtualScan( @@ -1315,7 +1268,6 @@ std::pair<std::unique_ptr<sbe::PlanStage>, sbe::value::SlotVector> projectFields sbe::value::SlotId resultSlot, PlanNodeId nodeId, sbe::value::SlotIdGenerator* slotIdGenerator, - optimizer::SlotVarMap& slotVarMap, const PlanStageSlots* slots) { // 'outputSlots' will match the order of 'fields'. Bail out early if 'fields' is empty. auto outputSlots = sbe::makeSV(); @@ -1415,7 +1367,7 @@ std::pair<std::unique_ptr<sbe::PlanStage>, sbe::value::SlotVector> projectFields auto getFieldExpr = makeFunction("getField"_sd, parent->value.hasSlot() ? makeVariable(*parent->value.getSlot()) - : parent->value.extractExpr(slotVarMap), + : parent->value.extractExpr(), makeConstant(node->name)); auto hasOneChildToVisit = [&] { @@ -1472,44 +1424,4 @@ std::pair<std::unique_ptr<sbe::PlanStage>, sbe::value::SlotVector> projectFields return {std::move(stage), std::move(outputSlots)}; } - -std::unique_ptr<sbe::EExpression> abtToExpr(optimizer::ABT& abt, optimizer::SlotVarMap& slotMap) { - auto env = optimizer::VariableEnvironment::build(abt); - - // Do not use descriptive names here. - auto prefixId = optimizer::PrefixId::create(false /*useDescriptiveNames*/); - // Convert paths into ABT expressions. - optimizer::EvalPathLowering pathLower{prefixId, env}; - pathLower.optimize(abt); - - // Run the constant folding to eliminate lambda applications as they are not directly - // supported by the SBE VM. - optimizer::ConstEval constEval{env}; - constEval.optimize(abt); - - // And finally convert to the SBE expression. - optimizer::SBEExpressionLowering exprLower{env, slotMap}; - return exprLower.optimize(abt); -} - -optimizer::ABT makeFillEmptyFalse(optimizer::ABT e) { - using namespace std::literals; - return optimizer::make<optimizer::BinaryOp>( - optimizer::Operations::FillEmpty, std::move(e), optimizer::Constant::boolean(false)); -} - -optimizer::ProjectionName makeVariableName(sbe::value::SlotId slotId) { - // Use a naming scheme that reduces that chances of clashing into a user-created variable name. - str::stream varName; - varName << "__s" << slotId; - return optimizer::ProjectionName{varName}; -} - -optimizer::ProjectionName makeLocalVariableName(sbe::FrameId frameId, sbe::value::SlotId slotId) { - // Use a naming scheme that reduces that chances of clashing into a user-created variable name. - str::stream varName; - varName << "__l" << frameId << "." << slotId; - return optimizer::ProjectionName{varName}; -} - } // namespace mongo::stage_builder diff --git a/src/mongo/db/query/sbe_stage_builder_helpers.h b/src/mongo/db/query/sbe_stage_builder_helpers.h index 1958e3988c6..e1be0c29f26 100644 --- a/src/mongo/db/query/sbe_stage_builder_helpers.h +++ b/src/mongo/db/query/sbe_stage_builder_helpers.h @@ -34,7 +34,6 @@ #include <string> #include <utility> -#include "mongo/db/exec/sbe/abt/abt_lower.h" #include "mongo/db/exec/sbe/expressions/expression.h" #include "mongo/db/exec/sbe/match_path.h" #include "mongo/db/exec/sbe/stages/filter.h" @@ -339,8 +338,7 @@ std::pair<sbe::value::SlotId, EvalStage> projectEvalExpr( EvalExpr expr, EvalStage stage, PlanNodeId planNodeId, - sbe::value::SlotIdGenerator* slotIdGenerator, - optimizer::SlotVarMap& slotVarMap); + sbe::value::SlotIdGenerator* slotIdGenerator); template <bool IsConst, bool IsEof = false> EvalStage makeFilter(EvalStage stage, @@ -440,8 +438,7 @@ using BranchFn = std::function<std::pair<sbe::value::SlotId, EvalStage>( EvalExpr expr, EvalStage stage, PlanNodeId planNodeId, - sbe::value::SlotIdGenerator* slotIdGenerator, - optimizer::SlotVarMap& slotVarMap)>; + sbe::value::SlotIdGenerator* slotIdGenerator)>; /** * Creates a chain of EIf expressions that will inspect each arg in order and return the first @@ -458,8 +455,7 @@ std::unique_ptr<sbe::EExpression> makeIfNullExpr( EvalExprStagePair generateUnion(std::vector<EvalExprStagePair> branches, BranchFn branchFn, PlanNodeId planNodeId, - sbe::value::SlotIdGenerator* slotIdGenerator, - optimizer::SlotVarMap& slotVarMap); + sbe::value::SlotIdGenerator* slotIdGenerator); /** * Creates limit-1/union stage with specified branches. Each branch is passed to 'branchFn' first. * If 'branchFn' is not set, expression from branch is simply projected to a slot. @@ -467,8 +463,7 @@ EvalExprStagePair generateUnion(std::vector<EvalExprStagePair> branches, EvalExprStagePair generateSingleResultUnion(std::vector<EvalExprStagePair> branches, BranchFn branchFn, PlanNodeId planNodeId, - sbe::value::SlotIdGenerator* slotIdGenerator, - optimizer::SlotVarMap& slotVarMap); + sbe::value::SlotIdGenerator* slotIdGenerator); /** This helper takes an SBE SlotIdGenerator and an SBE Array and returns an output slot and a * unwind/project/limit/coscan subtree that streams out the elements of the array one at a time via @@ -590,7 +585,6 @@ public: * value. Index part of the constructed state is empty. */ virtual Expression makeState(Expression expr) const = 0; - virtual optimizer::ABT makeState(optimizer::ABT expr) const = 0; /** * Creates an expression that constructs an initial state from 'expr'. 'expr' must evaluate to a @@ -604,7 +598,6 @@ public: * Creates an expression that extracts boolean value from the state evaluated from 'expr'. */ virtual Expression getBool(Expression expr) const = 0; - virtual optimizer::ABT getBool(optimizer::ABT expr) const = 0; Expression getBool(sbe::value::SlotId slotId) const { return getBool(sbe::makeE<sbe::EVariable>(slotId)); @@ -634,8 +627,7 @@ public: * Uses an expression from 'EvalExprStagePair' to construct state. Expresion must evaluate to * boolean value. */ - virtual EvalExprStagePair makePredicateCombinator(EvalExprStagePair pair, - optimizer::SlotVarMap& varMap) const = 0; + virtual EvalExprStagePair makePredicateCombinator(EvalExprStagePair pair) const = 0; /** * Creates traverse stage with fold and final expressions tuned to maintain consistent state. @@ -686,11 +678,6 @@ public: return sbe::makeE<sbe::EIf>(std::move(expr), makeState(true), makeState(false)); } - optimizer::ABT makeState(optimizer::ABT expr) const override { - return optimizer::make<optimizer::If>( - std::move(expr), optimizer::Constant::int64(0), optimizer::Constant::int64(-1)); - } - Expression makeInitialState(Expression expr) const override { return sbe::makeE<sbe::EIf>( std::move(expr), makeConstant(ValueType, 1), makeConstant(ValueType, -2)); @@ -701,11 +688,6 @@ public: sbe::EPrimBinary::greaterEq, std::move(expr), makeConstant(ValueType, 0)); } - optimizer::ABT getBool(optimizer::ABT expr) const override { - return optimizer::make<optimizer::BinaryOp>( - optimizer::Operations::Gte, std::move(expr), optimizer::Constant::int64(0)); - } - Expression mergeStates(Expression left, Expression right, sbe::value::FrameIdGenerator* frameIdGenerator) const override { @@ -753,10 +735,9 @@ public: return {indexSlot, std::move(resultStage)}; } - EvalExprStagePair makePredicateCombinator(EvalExprStagePair pair, - optimizer::SlotVarMap& varMap) const override { + EvalExprStagePair makePredicateCombinator(EvalExprStagePair pair) const override { auto [expr, stage] = std::move(pair); - return {makeState(expr.extractExpr(varMap)), std::move(stage)}; + return {makeState(expr.extractExpr()), std::move(stage)}; } EvalStage makeTraverseCombinator(EvalStage outer, @@ -786,10 +767,6 @@ public: return expr; } - optimizer::ABT makeState(optimizer::ABT expr) const override { - return expr; - } - Expression makeInitialState(Expression expr) const override { return expr; } @@ -798,10 +775,6 @@ public: return expr; } - optimizer::ABT getBool(optimizer::ABT expr) const override { - return expr; - } - Expression mergeStates(Expression left, Expression right, sbe::value::FrameIdGenerator* frameIdGenerator) const override { @@ -818,8 +791,7 @@ public: return {stateSlot, std::move(stage)}; } - EvalExprStagePair makePredicateCombinator(EvalExprStagePair pair, - optimizer::SlotVarMap& varMap) const override { + EvalExprStagePair makePredicateCombinator(EvalExprStagePair pair) const override { return pair; } @@ -866,7 +838,6 @@ EvalExprStagePair generateShortCircuitingLogicalOp(sbe::EPrimBinary::Op logicOp, std::vector<EvalExprStagePair> branches, PlanNodeId planNodeId, sbe::value::SlotIdGenerator* slotIdGenerator, - optimizer::SlotVarMap& slotVarMap, const FilterStateHelper& stateHelper); /** @@ -965,9 +936,6 @@ struct StageBuilderState { // corresponding to field paths to 'generateExpression' to avoid repeated expression generation. // Key is expected to represent field paths in form CURRENT.<field_name>[.<field_name>]*. stdx::unordered_map<std::string /*field path*/, EvalExpr> preGeneratedExprs; - - // Holds the mapping between the custom ABT variable names and the slot id they are referencing. - optimizer::SlotVarMap slotVarMap; }; /** @@ -1442,7 +1410,6 @@ std::pair<std::unique_ptr<sbe::PlanStage>, sbe::value::SlotVector> projectFields sbe::value::SlotId resultSlot, PlanNodeId nodeId, sbe::value::SlotIdGenerator* slotIdGenerator, - optimizer::SlotVarMap& slotVarMap, const PlanStageSlots* slots = nullptr); template <typename T> @@ -1514,38 +1481,4 @@ inline std::vector<T> appendVectorUnique(std::vector<T> lhs, std::vector<T> rhs) } return lhs; } - -std::unique_ptr<sbe::EExpression> abtToExpr(optimizer::ABT& abt, optimizer::SlotVarMap& slotMap); - -template <typename... Args> -inline auto makeABTFunction(StringData name, Args&&... args) { - return optimizer::make<optimizer::FunctionCall>( - name.toString(), optimizer::makeSeq(std::forward<Args>(args)...)); -} - -template <typename T> -inline auto makeABTConstant(sbe::value::TypeTags tag, T value) { - return optimizer::make<optimizer::Constant>(tag, sbe::value::bitcastFrom<T>(value)); -} - -inline auto makeABTConstant(StringData str) { - auto [tag, value] = sbe::value::makeNewString(str); - return makeABTConstant(tag, value); -} - -/** - * Creates a balanced boolean binary expression tree from given collection of leaf expression. - */ -optimizer::ABT makeBalancedBooleanOpTree(optimizer::Operations logicOp, - std::vector<optimizer::ABT> leaves); - -/** - * Check if expression returns Nothing and return boolean false if so. Otherwise, return the - * expression. - */ -optimizer::ABT makeFillEmptyFalse(optimizer::ABT e); - -optimizer::ProjectionName makeVariableName(sbe::value::SlotId slotId); -optimizer::ProjectionName makeLocalVariableName(sbe::FrameId frameId, sbe::value::SlotId slotId); - } // namespace mongo::stage_builder diff --git a/src/mongo/db/query/sbe_stage_builder_projection.cpp b/src/mongo/db/query/sbe_stage_builder_projection.cpp index 28c447022d3..8a5ce212c96 100644 --- a/src/mongo/db/query/sbe_stage_builder_projection.cpp +++ b/src/mongo/db/query/sbe_stage_builder_projection.cpp @@ -84,17 +84,16 @@ struct ProjectionTraversalVisitorContext { EvalExpr getInputEvalExpr() const { return inputExpr.clone(); } - - std::unique_ptr<sbe::EExpression> getInputExpr(optimizer::SlotVarMap& slotVarMap) const { - return getInputEvalExpr().extractExpr(slotVarMap); + std::unique_ptr<sbe::EExpression> getInputExpr() const { + return getInputEvalExpr().extractExpr(); } EvalExpr extractInputEvalExpr() { return std::move(inputExpr); } - std::unique_ptr<sbe::EExpression> extractInputExpr(optimizer::SlotVarMap& slotVarMap) { + std::unique_ptr<sbe::EExpression> extractInputExpr() { auto evalExpr = extractInputEvalExpr(); - return evalExpr.extractExpr(slotVarMap); + return evalExpr.extractExpr(); } // The input expression for the current level. This is the parent sub-document for each of @@ -375,7 +374,7 @@ public: auto lambdaFrame = _context->topLevel().lambdaFrame; tassert(6897005, "Expected lambda frame to be set", lambdaFrame); - auto childInputExpr = _context->topLevel().extractInputExpr(_context->state.slotVarMap); + auto childInputExpr = _context->topLevel().extractInputExpr(); const bool containsComputedField = _context->topLevel().subtreeContainsComputedField; @@ -395,7 +394,7 @@ public: auto args = sbe::makeEs(std::move(makeObjSpecExpr), childInputExpr->clone()); for (auto& expr : projectExprs) { - args.push_back(expr.extractExpr(_context->state.slotVarMap)); + args.push_back(expr.extractExpr()); } auto innerExpr = sbe::makeE<sbe::EFunction>("makeBsonObj", std::move(args)); @@ -425,7 +424,7 @@ public: auto fromExpr = [&]() { if (_context->isLastLevel()) { - return _context->topLevel().getInputExpr(_context->state.slotVarMap); + return _context->topLevel().getInputExpr(); } if (_context->numLevels() == 2 && _context->slots) { auto name = @@ -435,7 +434,7 @@ public: } } return makeFunction("getField"_sd, - _context->topLevel().getInputExpr(_context->state.slotVarMap), + _context->topLevel().getInputExpr(), makeConstant(_context->topFrontField())); }(); @@ -521,7 +520,7 @@ public: auto lambdaFrame = _context->topLevel().lambdaFrame; tassert(6929406, "Expected lambda frame to be set", lambdaFrame); - auto childInputExpr = _context->topLevel().extractInputExpr(_context->state.slotVarMap); + auto childInputExpr = _context->topLevel().extractInputExpr(); // We've finished extracting what we need from the child level, so pop if off the stack. _context->popLevel(); @@ -537,7 +536,7 @@ public: auto args = sbe::makeEs(std::move(makeObjSpecExpr), childInputExpr->clone()); for (auto& expr : projectExprs) { - args.push_back(expr.extractExpr(_context->state.slotVarMap)); + args.push_back(expr.extractExpr()); } auto innerExpr = sbe::makeE<sbe::EFunction>("makeBsonObj", std::move(args)); @@ -549,7 +548,7 @@ public: auto fromExpr = [&]() { if (_context->isLastLevel()) { - return _context->topLevel().getInputExpr(_context->state.slotVarMap); + return _context->topLevel().getInputExpr(); } if (_context->numLevels() == 2 && _context->slots) { auto name = @@ -559,7 +558,7 @@ public: } } return makeFunction("getField"_sd, - _context->topLevel().getInputExpr(_context->state.slotVarMap), + _context->topLevel().getInputExpr(), makeConstant(_context->topFrontField())); }(); @@ -579,10 +578,9 @@ public: void visit(const projection_ast::ProjectionSliceASTNode* node) final { using namespace std::literals; - auto arrayFromField = - makeFunction("getField"_sd, - _context->topLevel().getInputExpr(_context->state.slotVarMap), - makeConstant(_context->topFrontField())); + auto arrayFromField = makeFunction("getField"_sd, + _context->topLevel().getInputExpr(), + makeConstant(_context->topFrontField())); auto binds = sbe::makeEs(std::move(arrayFromField)); auto frameId = _context->state.frameId(); sbe::EVariable arrayVariable{frameId, 0}; @@ -647,7 +645,7 @@ EvalExpr generateProjection(StageBuilderState& state, } auto frameId = state.frameId(); - auto binds = sbe::makeEs(resultExpr.extractExpr(state.slotVarMap)); + auto binds = sbe::makeEs(resultExpr.extractExpr()); sbe::EVariable resultRef{frameId, 0}; // $slice projectional operator has different path traversal semantics compared to other @@ -665,7 +663,6 @@ EvalExpr generateProjection(StageBuilderState& state, auto sliceResultExpr = sliceContext.done(); - return sbe::makeE<sbe::ELocalBind>( - frameId, std::move(binds), sliceResultExpr.extractExpr(state.slotVarMap)); + return sbe::makeE<sbe::ELocalBind>(frameId, std::move(binds), sliceResultExpr.extractExpr()); } } // namespace mongo::stage_builder |