diff options
-rw-r--r-- | src/mongo/db/exec/sbe/abt/abt_lower.cpp | 47 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/stages/ix_scan.cpp | 67 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/stages/ix_scan.h | 29 | ||||
-rw-r--r-- | src/mongo/db/query/query_feature_flags.idl | 2 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_stage_builder_index_scan.cpp | 46 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_stage_builder_lookup.cpp | 4 |
6 files changed, 94 insertions, 101 deletions
diff --git a/src/mongo/db/exec/sbe/abt/abt_lower.cpp b/src/mongo/db/exec/sbe/abt/abt_lower.cpp index fc3b4982aa7..25ea76bd8d0 100644 --- a/src/mongo/db/exec/sbe/abt/abt_lower.cpp +++ b/src/mongo/db/exec/sbe/abt/abt_lower.cpp @@ -1019,45 +1019,22 @@ std::unique_ptr<sbe::PlanStage> SBENodeLowering::walk(const IndexScanNode& n, co const PlanNodeId planNodeId = _nodeToGroupPropsMap.at(&n)._planNodeId; auto projectForKeyStringBounds = sbe::makeS<sbe::LimitSkipStage>( sbe::makeS<sbe::CoScanStage>(planNodeId), 1, boost::none, planNodeId); - if (hasLowerBound) { - seekKeySlotLower = _slotIdGenerator.generate(); - correlatedSlotsForJoin.push_back(seekKeySlotLower.value()); - projectForKeyStringBounds = sbe::makeProjectStage(std::move(projectForKeyStringBounds), - planNodeId, - seekKeySlotLower.value(), - std::move(lowerBoundExpr)); - } - if (hasUpperBound) { - seekKeySlotUpper = _slotIdGenerator.generate(); - correlatedSlotsForJoin.push_back(seekKeySlotUpper.value()); - projectForKeyStringBounds = sbe::makeProjectStage(std::move(projectForKeyStringBounds), - planNodeId, - seekKeySlotUpper.value(), - std::move(upperBoundExpr)); - } // Unused. boost::optional<sbe::value::SlotId> resultSlot; - auto result = sbe::makeS<sbe::IndexScanStage>(nss.uuid().get(), - indexDefName, - !indexSpec.isReverseOrder(), - resultSlot, - ridSlot, - boost::none, - indexKeysToInclude, - vars, - seekKeySlotLower, - seekKeySlotUpper, - nullptr /*yieldPolicy*/, - planNodeId); - - return sbe::makeS<sbe::LoopJoinStage>(std::move(projectForKeyStringBounds), - std::move(result), - sbe::makeSV(), - std::move(correlatedSlotsForJoin), - nullptr, - planNodeId); + return sbe::makeS<sbe::IndexScanStage>(nss.uuid().get(), + indexDefName, + !indexSpec.isReverseOrder(), + resultSlot, + ridSlot, + boost::none, + indexKeysToInclude, + vars, + std::move(lowerBoundExpr), + std::move(upperBoundExpr), + nullptr /*yieldPolicy*/, + planNodeId); } std::unique_ptr<sbe::PlanStage> SBENodeLowering::walk(const SeekNode& n, diff --git a/src/mongo/db/exec/sbe/stages/ix_scan.cpp b/src/mongo/db/exec/sbe/stages/ix_scan.cpp index 2029ac4d356..1c4a54248dd 100644 --- a/src/mongo/db/exec/sbe/stages/ix_scan.cpp +++ b/src/mongo/db/exec/sbe/stages/ix_scan.cpp @@ -47,15 +47,11 @@ IndexScanStage::IndexScanStage(UUID collUuid, boost::optional<value::SlotId> snapshotIdSlot, IndexKeysInclusionSet indexKeysToInclude, value::SlotVector vars, - boost::optional<value::SlotId> seekKeySlotLow, - boost::optional<value::SlotId> seekKeySlotHigh, + std::unique_ptr<EExpression> seekKeyLow, + std::unique_ptr<EExpression> seekKeyHigh, PlanYieldPolicy* yieldPolicy, - PlanNodeId nodeId, - bool participateInTrialRunTracking) - : PlanStage(seekKeySlotLow ? "ixseek"_sd : "ixscan"_sd, - yieldPolicy, - nodeId, - participateInTrialRunTracking), + PlanNodeId nodeId) + : PlanStage(seekKeyLow ? "ixseek"_sd : "ixscan"_sd, yieldPolicy, nodeId), _collUuid(collUuid), _indexName(indexName), _forward(forward), @@ -64,11 +60,11 @@ IndexScanStage::IndexScanStage(UUID collUuid, _snapshotIdSlot(snapshotIdSlot), _indexKeysToInclude(indexKeysToInclude), _vars(std::move(vars)), - _seekKeySlotLow(seekKeySlotLow), - _seekKeySlotHigh(seekKeySlotHigh) { + _seekKeyLow(std::move(seekKeyLow)), + _seekKeyHigh(std::move(seekKeyHigh)) { // The valid state is when both boundaries, or none is set, or only low key is set. - invariant((_seekKeySlotLow && _seekKeySlotHigh) || (!_seekKeySlotLow && !_seekKeySlotHigh) || - (_seekKeySlotLow && !_seekKeySlotHigh)); + invariant((_seekKeyLow && _seekKeyHigh) || (!_seekKeyLow && !_seekKeyHigh) || + (_seekKeyLow && !_seekKeyHigh)); invariant(_indexKeysToInclude.count() == _vars.size()); } @@ -82,8 +78,8 @@ std::unique_ptr<PlanStage> IndexScanStage::clone() const { _snapshotIdSlot, _indexKeysToInclude, _vars, - _seekKeySlotLow, - _seekKeySlotHigh, + _seekKeyLow->clone(), + _seekKeyHigh->clone(), _yieldPolicy, _commonStats.nodeId, _participateInTrialRunTracking); @@ -108,11 +104,13 @@ void IndexScanStage::prepare(CompileCtx& ctx) { uassert(4822821, str::stream() << "duplicate slot: " << _vars[idx], inserted); } - if (_seekKeySlotLow) { - _seekKeyLowAccessor = ctx.getAccessor(*_seekKeySlotLow); + if (_seekKeyLow) { + ctx.root = this; + _seekKeyLowCodes = _seekKeyLow->compile(ctx); } - if (_seekKeySlotHigh) { - _seekKeyHiAccessor = ctx.getAccessor(*_seekKeySlotHigh); + if (_seekKeyHigh) { + ctx.root = this; + _seekKeyHighCodes = _seekKeyHigh->compile(ctx); _seekKeyHighHolder = std::make_unique<value::OwnedValueAccessor>(); } _seekKeyLowHolder = std::make_unique<value::OwnedValueAccessor>(); @@ -283,28 +281,28 @@ void IndexScanStage::open(bool reOpen) { _cursor = entry->accessMethod()->asSortedData()->newCursor(_opCtx, _forward); } - if (_seekKeyLowAccessor && _seekKeyHiAccessor) { - auto [tagLow, valLow] = _seekKeyLowAccessor->getViewOfValue(); + if (_seekKeyLow && _seekKeyHigh) { + auto [ownedLow, tagLow, valLow] = _bytecode.run(_seekKeyLowCodes.get()); const auto msgTagLow = tagLow; uassert(4822851, str::stream() << "seek key is wrong type: " << msgTagLow, tagLow == value::TypeTags::ksValue); - _seekKeyLowHolder->reset(false, tagLow, valLow); + _seekKeyLowHolder->reset(ownedLow, tagLow, valLow); - auto [tagHi, valHi] = _seekKeyHiAccessor->getViewOfValue(); + auto [ownedHi, tagHi, valHi] = _bytecode.run(_seekKeyHighCodes.get()); const auto msgTagHi = tagHi; uassert(4822852, str::stream() << "seek key is wrong type: " << msgTagHi, tagHi == value::TypeTags::ksValue); - _seekKeyHighHolder->reset(false, tagHi, valHi); - } else if (_seekKeyLowAccessor) { - auto [tagLow, valLow] = _seekKeyLowAccessor->getViewOfValue(); + _seekKeyHighHolder->reset(ownedHi, tagHi, valHi); + } else if (_seekKeyLow) { + auto [ownedLow, tagLow, valLow] = _bytecode.run(_seekKeyLowCodes.get()); const auto msgTagLow = tagLow; uassert(4822853, str::stream() << "seek key is wrong type: " << msgTagLow, tagLow == value::TypeTags::ksValue); - _seekKeyLowHolder->reset(false, tagLow, valLow); + _seekKeyLowHolder->reset(ownedLow, tagLow, valLow); } else { auto sdi = entry->accessMethod()->asSortedData()->getSortedDataInterface(); KeyString::Builder kb(sdi->getKeyStringVersion(), @@ -414,6 +412,7 @@ std::unique_ptr<PlanStageStats> IndexScanStage::getStats(bool includeDebugInfo) ret->specific = std::make_unique<IndexScanStats>(_specificStats); if (includeDebugInfo) { + DebugPrinter printer; BSONObjBuilder bob; bob.append("indexName", _indexName); bob.appendNumber("keysExamined", static_cast<long long>(_specificStats.keysExamined)); @@ -428,11 +427,11 @@ std::unique_ptr<PlanStageStats> IndexScanStage::getStats(bool includeDebugInfo) if (_snapshotIdSlot) { bob.appendNumber("snapshotIdSlot", static_cast<long long>(*_snapshotIdSlot)); } - if (_seekKeySlotLow) { - bob.appendNumber("seekKeySlotLow", static_cast<long long>(*_seekKeySlotLow)); + if (_seekKeyLow) { + bob.append("seekKeyLow", printer.print(_seekKeyLow->debugPrint())); } - if (_seekKeySlotHigh) { - bob.appendNumber("seekKeySlotHigh", static_cast<long long>(*_seekKeySlotHigh)); + if (_seekKeyHigh) { + bob.append("seekKeyHigh", printer.print(_seekKeyHigh->debugPrint())); } bob.append("outputSlots", _vars.begin(), _vars.end()); bob.append("indexKeysToInclude", _indexKeysToInclude.to_string()); @@ -449,10 +448,10 @@ const SpecificStats* IndexScanStage::getSpecificStats() const { std::vector<DebugPrinter::Block> IndexScanStage::debugPrint() const { auto ret = PlanStage::debugPrint(); - if (_seekKeySlotLow) { - DebugPrinter::addIdentifier(ret, _seekKeySlotLow.get()); - if (_seekKeySlotHigh) { - DebugPrinter::addIdentifier(ret, _seekKeySlotHigh.get()); + if (_seekKeyLow) { + DebugPrinter::addBlocks(ret, _seekKeyLow->debugPrint()); + if (_seekKeyHigh) { + DebugPrinter::addBlocks(ret, _seekKeyHigh->debugPrint()); } else { DebugPrinter::addIdentifier(ret, DebugPrinter::kNoneKeyword); } diff --git a/src/mongo/db/exec/sbe/stages/ix_scan.h b/src/mongo/db/exec/sbe/stages/ix_scan.h index da61cb544ec..3d1ae2eec78 100644 --- a/src/mongo/db/exec/sbe/stages/ix_scan.h +++ b/src/mongo/db/exec/sbe/stages/ix_scan.h @@ -31,22 +31,24 @@ #include "mongo/bson/ordering.h" #include "mongo/db/db_raii.h" +#include "mongo/db/exec/sbe/expressions/expression.h" #include "mongo/db/exec/sbe/stages/collection_helpers.h" #include "mongo/db/exec/sbe/stages/stages.h" +#include "mongo/db/exec/sbe/vm/vm.h" #include "mongo/db/storage/record_store.h" #include "mongo/db/storage/sorted_data_interface.h" namespace mongo::sbe { /** * A stage that iterates the entries of a collection index, starting from a bound specified by the - * value in 'seekKeySlotLow' and ending (via IS_EOF) with the 'seekKeySlotHigh' bound. (An - * unspecified 'seekKeySlotHigh' scans to the end of the index. Leaving both bounds unspecified + * value in 'seekKeyLow' and ending (via IS_EOF) with the 'seekKeyHigh' bound. (An + * unspecified 'seekKeyHigh' scans to the end of the index. Leaving both bounds unspecified * scans the index from beginning to end.) * - * The input 'seekKeySlotLow' and 'seekKeySlotHigh' slots get read as part of the open (or re-open) - * call. A common use case for an IndexScanStage is to place it as the inner child of LoopJoinStage. - * The outer side of the LoopJoinStage determines the bounds, and the inner IndexScanStage iterates - * through all the entries within those bounds. + * The input 'seekKeyLow' and 'seekKeyHigh' EExpressions get evaluated as part of the open + * (or re-open) call. A common use case for an IndexScanStage is to place it as the inner child of + * LoopJoinStage. The outer side of the LoopJoinStage determines the bounds, and the inner + * IndexScanStage iterates through all the entries within those bounds. * * The "output" slots are * - 'recordSlot': the "KeyString" representing the index entry, @@ -80,8 +82,8 @@ public: boost::optional<value::SlotId> snapshotIdSlot, IndexKeysInclusionSet indexKeysToInclude, value::SlotVector vars, - boost::optional<value::SlotId> seekKeySlotLow, - boost::optional<value::SlotId> seekKeySlotHigh, + std::unique_ptr<EExpression> seekKeyLow, + std::unique_ptr<EExpression> seekKeyHigh, PlanYieldPolicy* yieldPolicy, PlanNodeId planNodeId, bool participateInTrialRunTracking = true); @@ -128,8 +130,15 @@ private: const boost::optional<value::SlotId> _snapshotIdSlot; const IndexKeysInclusionSet _indexKeysToInclude; const value::SlotVector _vars; - const boost::optional<value::SlotId> _seekKeySlotLow; - const boost::optional<value::SlotId> _seekKeySlotHigh; + + std::unique_ptr<EExpression> _seekKeyLow; + std::unique_ptr<EExpression> _seekKeyHigh; + + // Carries the compiled bytecode for the above '_seekKeyLow' and '_seekKeyHigh'. + std::unique_ptr<vm::CodeFragment> _seekKeyLowCodes; + std::unique_ptr<vm::CodeFragment> _seekKeyHighCodes; + + vm::ByteCode _bytecode; // These members are default constructed to boost::none and are initialized when 'prepare()' // is called. Once they are set, they are never modified again. diff --git a/src/mongo/db/query/query_feature_flags.idl b/src/mongo/db/query/query_feature_flags.idl index 23dda4bcb2a..61e50906c87 100644 --- a/src/mongo/db/query/query_feature_flags.idl +++ b/src/mongo/db/query/query_feature_flags.idl @@ -148,4 +148,4 @@ feature_flags: featureFlagTimeSeriesChangeStreams: description: "Feature flag for $changeStream support for time series" cpp_varname: gFeatureFlagTimeSeriesChangeStreams - default: false
\ No newline at end of file + default: false diff --git a/src/mongo/db/query/sbe_stage_builder_index_scan.cpp b/src/mongo/db/query/sbe_stage_builder_index_scan.cpp index 13ff93f7b53..ec8376d8095 100644 --- a/src/mongo/db/query/sbe_stage_builder_index_scan.cpp +++ b/src/mongo/db/query/sbe_stage_builder_index_scan.cpp @@ -318,8 +318,8 @@ generateOptimizedMultiIntervalIndexScan(StageBuilderState& state, indexSnapshotSlot, indexKeysToInclude, std::move(indexKeySlots), - lowKeySlot, - highKeySlot, + makeVariable(lowKeySlot), + makeVariable(highKeySlot), yieldPolicy, planNodeId); @@ -453,8 +453,8 @@ makeRecursiveBranchForGenericIndexScan(const CollectionPtr& collection, snapshotIdSlot, indexKeysToInclude, std::move(savedIndexKeySlots), - lowKeySlot, - boost::none, + makeVariable(lowKeySlot), + nullptr /* seekKeyHigh */, yieldPolicy, planNodeId); @@ -873,22 +873,30 @@ generateSingleIntervalIndexScan(StageBuilderState& state, // are present. Otherwise the low and high keys will be obtained via variable references to // runtime environment slots. sbe::value::SlotMap<std::unique_ptr<sbe::EExpression>> projects; - auto makeKeySlot = [&](std::unique_ptr<KeyString::Value> key) { + auto makeKeyExpr = [&](std::unique_ptr<KeyString::Value> key) + -> std::pair<std::unique_ptr<sbe::EExpression>, boost::optional<sbe::value::SlotId>> { if (key) { - auto keySlot = slotIdGenerator->generate(); - projects.emplace( - keySlot, + return std::pair{ makeConstant(sbe::value::TypeTags::ksValue, - sbe::value::bitcastFrom<KeyString::Value*>(key.release()))); - return keySlot; + sbe::value::bitcastFrom<KeyString::Value*>(key.release())), + boost::none}; } else { auto keySlot = state.data->env->registerSlot( sbe::value::TypeTags::Nothing, 0, true /* owned */, slotIdGenerator); - return keySlot; + return std::pair{makeVariable(keySlot), keySlot}; } }; - auto lowKeySlot = makeKeySlot(std::move(lowKey)); - auto highKeySlot = makeKeySlot(std::move(highKey)); + + std::unique_ptr<sbe::EExpression> lowKeyExpr; + std::unique_ptr<sbe::EExpression> highKeyExpr; + boost::optional<sbe::value::SlotId> lowKeySlot; + boost::optional<sbe::value::SlotId> highKeySlot; + std::tie(lowKeyExpr, lowKeySlot) = makeKeyExpr(std::move(lowKey)); + std::tie(highKeyExpr, highKeySlot) = makeKeyExpr(std::move(highKey)); + tassert(0000000, + "Either both lowKeySlot and highKeySlot will exist or none of them will exist", + (lowKeySlot && highKeySlot) || (!lowKeySlot && !highKeySlot)); + if (indexIdSlot) { // Construct a copy of 'indexName' to project for use in the index consistency check. @@ -924,8 +932,8 @@ generateSingleIntervalIndexScan(StageBuilderState& state, return sbe::makeS<sbe::FilterStage</* IsConst */ true, /* IsEof */ false>>( std::move(childStage), makeBinaryOp(sbe::EPrimBinary::logicAnd, - makeFunction("exists", makeVariable(lowKeySlot)), - makeFunction("exists", makeVariable(highKeySlot))), + makeFunction("exists", lowKeyExpr->clone()), + makeFunction("exists", highKeyExpr->clone())), planNodeId); }(); @@ -948,8 +956,8 @@ generateSingleIntervalIndexScan(StageBuilderState& state, indexSnapshotSlot, indexKeysToInclude, std::move(indexKeySlots), - lowKeySlot, - highKeySlot, + std::move(lowKeyExpr), + std::move(highKeyExpr), yieldPolicy, planNodeId); @@ -975,11 +983,11 @@ generateSingleIntervalIndexScan(StageBuilderState& state, sbe::makeS<sbe::LoopJoinStage>(std::move(lowHighKeyBranch), std::move(stage), std::move(outerSv), - sbe::makeSV(lowKeySlot, highKeySlot), + sbe::makeSV(), nullptr, planNodeId), boost::make_optional(shouldRegisterLowHighKeyInRuntimeEnv, - std::pair(lowKeySlot, highKeySlot))}; + std::pair(*lowKeySlot, *highKeySlot))}; } std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> generateIndexScan( diff --git a/src/mongo/db/query/sbe_stage_builder_lookup.cpp b/src/mongo/db/query/sbe_stage_builder_lookup.cpp index b1077b0ae79..4525bedd4f7 100644 --- a/src/mongo/db/query/sbe_stage_builder_lookup.cpp +++ b/src/mongo/db/query/sbe_stage_builder_lookup.cpp @@ -804,8 +804,8 @@ std::pair<SlotId, std::unique_ptr<sbe::PlanStage>> buildIndexJoinLookupStage( snapshotIdSlot, IndexKeysInclusionSet{} /* indexKeysToInclude */, makeSV() /* vars */, - lowKeySlot, - highKeySlot, + makeVariable(lowKeySlot), + makeVariable(highKeySlot), yieldPolicy, nodeId); |