Compare commits
21 Commits
r8.0.0-alp
...
eric/id-ha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e112f7a91 | ||
|
|
44a40ed8dd | ||
|
|
3d4c0a15fd | ||
|
|
519ad6e521 | ||
|
|
cdb509d90a | ||
|
|
1ce8f717a0 | ||
|
|
b01d44832e | ||
|
|
e41eb06388 | ||
|
|
e27fb37145 | ||
|
|
d8ae3f7be6 | ||
|
|
51fd0d447b | ||
|
|
330b4c092b | ||
|
|
30cf050f75 | ||
|
|
08f108f5a2 | ||
|
|
630fb62eb3 | ||
|
|
792c13cafc | ||
|
|
0d1ebb9878 | ||
|
|
8e31a264fd | ||
|
|
ed7caa14b4 | ||
|
|
b24df431f6 | ||
|
|
c6ddce5629 |
@@ -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,
|
||||
|
||||
@@ -170,8 +170,8 @@ TEST_F(PlanSizeTest, IndexScan) {
|
||||
generateSlotId(),
|
||||
IndexKeysInclusionSet(1),
|
||||
mockSV(),
|
||||
generateSlotId(),
|
||||
generateSlotId(),
|
||||
makeE<EVariable>(generateSlotId()),
|
||||
makeE<EVariable>(generateSlotId()),
|
||||
nullptr,
|
||||
kEmptyPlanNodeId);
|
||||
assertPlanSize(*stage);
|
||||
|
||||
@@ -47,12 +47,12 @@ 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,
|
||||
: PlanStage(seekKeyLow ? "ixseek"_sd : "ixscan"_sd,
|
||||
yieldPolicy,
|
||||
nodeId,
|
||||
participateInTrialRunTracking),
|
||||
@@ -64,11 +64,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 +82,8 @@ std::unique_ptr<PlanStage> IndexScanStage::clone() const {
|
||||
_snapshotIdSlot,
|
||||
_indexKeysToInclude,
|
||||
_vars,
|
||||
_seekKeySlotLow,
|
||||
_seekKeySlotHigh,
|
||||
_seekKeyLow ? _seekKeyLow->clone() : nullptr,
|
||||
_seekKeyHigh ? _seekKeyHigh->clone() : nullptr,
|
||||
_yieldPolicy,
|
||||
_commonStats.nodeId,
|
||||
_participateInTrialRunTracking);
|
||||
@@ -108,11 +108,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 +285,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 +416,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 +431,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 +452,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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -143,9 +143,10 @@ feature_flags:
|
||||
featureFlagSbeFull:
|
||||
description: "Feature flag to enable using SBE for a larger number of queries"
|
||||
cpp_varname: gFeatureFlagSbeFull
|
||||
default: false
|
||||
default: true
|
||||
version: 6.0
|
||||
|
||||
featureFlagTimeSeriesChangeStreams:
|
||||
description: "Feature flag for $changeStream support for time series"
|
||||
cpp_varname: gFeatureFlagTimeSeriesChangeStreams
|
||||
default: false
|
||||
default: false
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -870,26 +870,35 @@ generateSingleIntervalIndexScan(StageBuilderState& state,
|
||||
|
||||
// Construct a constant table scan to deliver a single row with two fields 'lowKeySlot' and
|
||||
// 'highKeySlot', representing seek boundaries, into the index scan if 'lowKey' and 'highKey'
|
||||
// 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) {
|
||||
// are present. If 'lowKey' and 'highKey' are present they will be presented to the index scan
|
||||
// stage as a constant expression. Otherwise the low and high keys will be obtained via variable
|
||||
// references to runtime environment slots.
|
||||
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;
|
||||
boost::optional<sbe::value::SlotId> lowKeySlot;
|
||||
std::tie(lowKeyExpr, lowKeySlot) = makeKeyExpr(std::move(lowKey));
|
||||
|
||||
std::unique_ptr<sbe::EExpression> highKeyExpr;
|
||||
boost::optional<sbe::value::SlotId> highKeySlot;
|
||||
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));
|
||||
|
||||
sbe::value::SlotMap<std::unique_ptr<sbe::EExpression>> projects;
|
||||
if (indexIdSlot) {
|
||||
// Construct a copy of 'indexName' to project for use in the index consistency check.
|
||||
projects.emplace(*indexIdSlot, makeConstant(indexName));
|
||||
@@ -924,8 +933,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 +957,8 @@ generateSingleIntervalIndexScan(StageBuilderState& state,
|
||||
indexSnapshotSlot,
|
||||
indexKeysToInclude,
|
||||
std::move(indexKeySlots),
|
||||
lowKeySlot,
|
||||
highKeySlot,
|
||||
std::move(lowKeyExpr),
|
||||
std::move(highKeyExpr),
|
||||
yieldPolicy,
|
||||
planNodeId);
|
||||
|
||||
@@ -975,11 +984,12 @@ 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))};
|
||||
shouldRegisterLowHighKeyInRuntimeEnv
|
||||
? boost::make_optional(std::pair(*lowKeySlot, *highKeySlot))
|
||||
: boost::none};
|
||||
}
|
||||
|
||||
std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> generateIndexScan(
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user