summaryrefslogtreecommitdiff
path: root/src/mongo/db/exec/sbe/stages
diff options
context:
space:
mode:
authorJustin Seyster <justin.seyster@mongodb.com>2020-05-29 15:37:48 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-07-16 03:01:28 +0000
commitb1ecda7953b2d9e23857ceeb09f10f9cd18c959b (patch)
tree75d5c0322e310bf4be8b0b59de6c95d7f2efda72 /src/mongo/db/exec/sbe/stages
parentb348b91ffdfab32e5a1ec9e63c9def884adf1acb (diff)
downloadmongo-b1ecda7953b2d9e23857ceeb09f10f9cd18c959b.tar.gz
SERVER-48721 Support for $returnKey in SBE
Diffstat (limited to 'src/mongo/db/exec/sbe/stages')
-rw-r--r--src/mongo/db/exec/sbe/stages/ix_scan.cpp46
-rw-r--r--src/mongo/db/exec/sbe/stages/ix_scan.h37
2 files changed, 63 insertions, 20 deletions
diff --git a/src/mongo/db/exec/sbe/stages/ix_scan.cpp b/src/mongo/db/exec/sbe/stages/ix_scan.cpp
index 9e3cdb21d44..f26dd1dbadb 100644
--- a/src/mongo/db/exec/sbe/stages/ix_scan.cpp
+++ b/src/mongo/db/exec/sbe/stages/ix_scan.cpp
@@ -33,6 +33,7 @@
#include "mongo/db/catalog/index_catalog.h"
#include "mongo/db/exec/sbe/expressions/expression.h"
+#include "mongo/db/exec/sbe/values/bson.h"
#include "mongo/db/index/index_access_method.h"
namespace mongo::sbe {
@@ -41,7 +42,7 @@ IndexScanStage::IndexScanStage(const NamespaceStringOrUUID& name,
bool forward,
boost::optional<value::SlotId> recordSlot,
boost::optional<value::SlotId> recordIdSlot,
- std::vector<std::string> fields,
+ IndexKeysInclusionSet indexKeysToInclude,
value::SlotVector vars,
boost::optional<value::SlotId> seekKeySlotLow,
boost::optional<value::SlotId> seekKeySlotHi,
@@ -53,15 +54,16 @@ IndexScanStage::IndexScanStage(const NamespaceStringOrUUID& name,
_forward(forward),
_recordSlot(recordSlot),
_recordIdSlot(recordIdSlot),
- _fields(std::move(fields)),
+ _indexKeysToInclude(indexKeysToInclude),
_vars(std::move(vars)),
_seekKeySlotLow(seekKeySlotLow),
_seekKeySlotHi(seekKeySlotHi),
_tracker(tracker) {
- invariant(_fields.size() == _vars.size());
// The valid state is when both boundaries, or none is set, or only low key is set.
invariant((_seekKeySlotLow && _seekKeySlotHi) || (!_seekKeySlotLow && !_seekKeySlotHi) ||
(_seekKeySlotLow && !_seekKeySlotHi));
+
+ invariant(_indexKeysToInclude.count() == _vars.size());
}
std::unique_ptr<PlanStage> IndexScanStage::clone() const {
@@ -70,7 +72,7 @@ std::unique_ptr<PlanStage> IndexScanStage::clone() const {
_forward,
_recordSlot,
_recordIdSlot,
- _fields,
+ _indexKeysToInclude,
_vars,
_seekKeySlotLow,
_seekKeySlotHi,
@@ -87,12 +89,10 @@ void IndexScanStage::prepare(CompileCtx& ctx) {
_recordIdAccessor = std::make_unique<value::ViewOfValueAccessor>();
}
- for (size_t idx = 0; idx < _fields.size(); ++idx) {
- auto [it, inserted] =
- _fieldAccessors.emplace(_fields[idx], std::make_unique<value::ViewOfValueAccessor>());
- uassert(4822821, str::stream() << "duplicate field: " << _fields[idx], inserted);
- auto [itRename, insertedRename] = _varAccessors.emplace(_vars[idx], it->second.get());
- uassert(4822822, str::stream() << "duplicate field: " << _vars[idx], insertedRename);
+ _accessors.resize(_vars.size());
+ for (size_t idx = 0; idx < _accessors.size(); ++idx) {
+ auto [it, inserted] = _accessorMap.emplace(_vars[idx], &_accessors[idx]);
+ uassert(4822821, str::stream() << "duplicate slot: " << _vars[idx], inserted);
}
if (_seekKeySlotLow) {
@@ -112,7 +112,7 @@ value::SlotAccessor* IndexScanStage::getAccessor(CompileCtx& ctx, value::SlotId
return _recordIdAccessor.get();
}
- if (auto it = _varAccessors.find(slot); it != _varAccessors.end()) {
+ if (auto it = _accessorMap.find(slot); it != _accessorMap.end()) {
return it->second;
}
@@ -206,6 +206,10 @@ void IndexScanStage::open(bool reOpen) {
_seekKeyLow = &_startPoint;
_seekKeyHi = nullptr;
}
+
+ // TODO SERVER-49385: When the 'prepare()' phase takes the collection lock, it will be
+ // possible to intialize '_ordering' there instead of here.
+ _ordering = entry->ordering();
} else {
_cursor.reset();
}
@@ -256,6 +260,12 @@ PlanState IndexScanStage::getNext() {
value::bitcastFrom<int64_t>(_nextRecord->loc.repr()));
}
+ if (_accessors.size()) {
+ _valuesBuffer.reset();
+ readKeyStringValueIntoAccessors(
+ _nextRecord->keyString, *_ordering, &_valuesBuffer, &_accessors, _indexKeysToInclude);
+ }
+
if (_tracker && _tracker->trackProgress<TrialRunProgressTracker::kNumReads>(1)) {
// If we're collecting execution stats during multi-planning and reached the end of the
// trial period (trackProgress() will return 'true' in this case), then we can reset the
@@ -308,14 +318,18 @@ std::vector<DebugPrinter::Block> IndexScanStage::debugPrint() const {
}
ret.emplace_back(DebugPrinter::Block("[`"));
- for (size_t idx = 0; idx < _fields.size(); ++idx) {
- if (idx) {
+ size_t varIndex = 0;
+ for (size_t keyIndex = 0; keyIndex < _indexKeysToInclude.size(); ++keyIndex) {
+ if (!_indexKeysToInclude[keyIndex]) {
+ continue;
+ }
+ if (varIndex) {
ret.emplace_back(DebugPrinter::Block("`,"));
}
-
- DebugPrinter::addIdentifier(ret, _vars[idx]);
+ invariant(varIndex < _vars.size());
+ DebugPrinter::addIdentifier(ret, _vars[varIndex++]);
ret.emplace_back("=");
- DebugPrinter::addIdentifier(ret, _fields[idx]);
+ ret.emplace_back(std::to_string(keyIndex));
}
ret.emplace_back(DebugPrinter::Block("`]"));
diff --git a/src/mongo/db/exec/sbe/stages/ix_scan.h b/src/mongo/db/exec/sbe/stages/ix_scan.h
index a3d23b2bb82..fcecc127ddd 100644
--- a/src/mongo/db/exec/sbe/stages/ix_scan.h
+++ b/src/mongo/db/exec/sbe/stages/ix_scan.h
@@ -29,6 +29,7 @@
#pragma once
+#include "mongo/bson/ordering.h"
#include "mongo/db/db_raii.h"
#include "mongo/db/exec/sbe/stages/stages.h"
#include "mongo/db/exec/trial_run_progress_tracker.h"
@@ -36,6 +37,27 @@
#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 'seekKeySlotHi' bound. (An unspecified
+ * 'seekKeySlowHi' scans to the end of the index. Leaving both bounds unspecified scans the index
+ * from beginning to end.)
+ *
+ * The input 'seekKeySlotLow' and 'seekKeySlotHi' 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 "output" slots are
+ * - 'recordSlot': the "KeyString" representing the index entry,
+ * - 'recordIdSlot': a reference that can be used to fetch the entire document, and
+ * - 'vars': one slot for each value in the index key that should be "projected" out of the entry.
+ *
+ * The 'indexKeysToInclude' bitset determines which values are included in the projection based
+ * on their order in the index pattern. The number of bits set in 'indexKeysToInclude' must be
+ * the same as the number of slots in the 'vars' SlotVector.
+ */
class IndexScanStage final : public PlanStage {
public:
IndexScanStage(const NamespaceStringOrUUID& name,
@@ -43,7 +65,7 @@ public:
bool forward,
boost::optional<value::SlotId> recordSlot,
boost::optional<value::SlotId> recordIdSlot,
- std::vector<std::string> fields,
+ IndexKeysInclusionSet indexKeysToInclude,
value::SlotVector vars,
boost::optional<value::SlotId> seekKeySlotLow,
boost::optional<value::SlotId> seekKeySlotHi,
@@ -74,7 +96,7 @@ private:
const bool _forward;
const boost::optional<value::SlotId> _recordSlot;
const boost::optional<value::SlotId> _recordIdSlot;
- const std::vector<std::string> _fields;
+ const IndexKeysInclusionSet _indexKeysToInclude;
const value::SlotVector _vars;
const boost::optional<value::SlotId> _seekKeySlotLow;
const boost::optional<value::SlotId> _seekKeySlotHi;
@@ -82,8 +104,10 @@ private:
std::unique_ptr<value::ViewOfValueAccessor> _recordAccessor;
std::unique_ptr<value::ViewOfValueAccessor> _recordIdAccessor;
- value::FieldAccessorMap _fieldAccessors;
- value::SlotAccessorMap _varAccessors;
+ // One accessor and slot for each key component that this stage will bind from an index entry's
+ // KeyString. The accessors are in the same order as the key components they bind to.
+ std::vector<value::ViewOfValueAccessor> _accessors;
+ value::SlotAccessorMap _accessorMap;
value::SlotAccessor* _seekKeyLowAccessor{nullptr};
value::SlotAccessor* _seekKeyHiAccessor{nullptr};
@@ -94,9 +118,14 @@ private:
std::unique_ptr<SortedDataInterface::Cursor> _cursor;
std::weak_ptr<const IndexCatalogEntry> _weakIndexCatalogEntry;
+ boost::optional<Ordering> _ordering{boost::none};
boost::optional<AutoGetCollectionForRead> _coll;
boost::optional<KeyStringEntry> _nextRecord;
+ // This buffer stores values that are projected out of the index entry. Values in the
+ // '_accessors' list that are pointers point to data in this buffer.
+ BufBuilder _valuesBuffer;
+
bool _open{false};
bool _firstGetNext{true};
IndexScanStats _specificStats;