summaryrefslogtreecommitdiff
path: root/src/mongo/db/exec/sbe/values
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/exec/sbe/values')
-rw-r--r--src/mongo/db/exec/sbe/values/slot.cpp11
-rw-r--r--src/mongo/db/exec/sbe/values/slot.h16
-rw-r--r--src/mongo/db/exec/sbe/values/value.cpp6
-rw-r--r--src/mongo/db/exec/sbe/values/value.h9
-rw-r--r--src/mongo/db/exec/sbe/values/value_builder.h34
-rw-r--r--src/mongo/db/exec/sbe/values/value_serialization_test.cpp25
6 files changed, 75 insertions, 26 deletions
diff --git a/src/mongo/db/exec/sbe/values/slot.cpp b/src/mongo/db/exec/sbe/values/slot.cpp
index 45cbc977980..d8b59c6c3db 100644
--- a/src/mongo/db/exec/sbe/values/slot.cpp
+++ b/src/mongo/db/exec/sbe/values/slot.cpp
@@ -457,7 +457,7 @@ static void serializeValueIntoKeyString(KeyString::Builder& buf, TypeTags tag, V
// TODO SERVER-61629: convert this to serialize the 'arr' directly instead of
// constructing a BSONArray.
BSONArrayBuilder builder;
- bson::convertToBsonObj(builder, getArrayView(val));
+ bson::convertToBsonObj(builder, value::ArrayEnumerator{tag, val});
buf.appendBool(true);
buf.appendArray(BSONArray(builder.done()));
break;
@@ -564,8 +564,10 @@ void MaterializedRow::serializeIntoKeyString(KeyString::Builder& buf) const {
}
}
-MaterializedRow MaterializedRow::deserializeFromKeyString(const KeyString::Value& keyString,
- BufBuilder* valueBufferBuilder) {
+MaterializedRow MaterializedRow::deserializeFromKeyString(
+ const KeyString::Value& keyString,
+ BufBuilder* valueBufferBuilder,
+ boost::optional<size_t> numPrefixValsToRead) {
BufReader reader(keyString.getBuffer(), keyString.getSize());
KeyString::TypeBits typeBits(keyString.getTypeBits());
KeyString::TypeBits::Reader typeBitsReader(typeBits);
@@ -577,7 +579,8 @@ MaterializedRow MaterializedRow::deserializeFromKeyString(const KeyString::Value
&reader, &typeBitsReader, false /* inverted */, typeBits.version, &valBuilder);
} while (keepReading);
- MaterializedRow result{valBuilder.numValues()};
+ size_t sizeOfRow = numPrefixValsToRead ? *numPrefixValsToRead : valBuilder.numValues();
+ MaterializedRow result{sizeOfRow};
valBuilder.readValues(result);
return result;
diff --git a/src/mongo/db/exec/sbe/values/slot.h b/src/mongo/db/exec/sbe/values/slot.h
index f853f816d4d..2452c8bd094 100644
--- a/src/mongo/db/exec/sbe/values/slot.h
+++ b/src/mongo/db/exec/sbe/values/slot.h
@@ -483,10 +483,16 @@ public:
* intended for spilling key values used in the HashAgg stage. The format is not guaranteed to
* be stable between versions, so it should not be used for long-term storage or communication
* between instances.
+ *
+ * If 'numPrefixValsToRead' is provided, then only the given number of values from 'keyString'
+ * are decoded into the resulting 'MaterializedRow'. The remaining suffix values in the
+ * 'keyString' are ignored.
*/
- static MaterializedRow deserializeFromKeyString(const KeyString::Value& keyString,
+ static MaterializedRow deserializeFromKeyString(
+ const KeyString::Value& keyString,
+ BufBuilder* valueBufferBuilder,
+ boost::optional<size_t> numPrefixValsToRead = boost::none);
- BufBuilder* valueBufferBuilder);
void serializeIntoKeyString(KeyString::Builder& builder) const;
private:
@@ -577,9 +583,9 @@ private:
};
struct MaterializedRowEq {
- using ComparatorType = StringData::ComparatorInterface*;
+ using ComparatorType = StringData::ComparatorInterface;
- explicit MaterializedRowEq(const ComparatorType comparator = nullptr)
+ explicit MaterializedRowEq(const ComparatorType* comparator = nullptr)
: _comparator(comparator) {}
bool operator()(const MaterializedRow& lhs, const MaterializedRow& rhs) const {
@@ -597,7 +603,7 @@ struct MaterializedRowEq {
}
private:
- const ComparatorType _comparator = nullptr;
+ const ComparatorType* _comparator = nullptr;
};
struct MaterializedRowLess {
diff --git a/src/mongo/db/exec/sbe/values/value.cpp b/src/mongo/db/exec/sbe/values/value.cpp
index 6c21f4f6e5d..e0f73c87ddf 100644
--- a/src/mongo/db/exec/sbe/values/value.cpp
+++ b/src/mongo/db/exec/sbe/values/value.cpp
@@ -860,7 +860,7 @@ bool isInfinity(TypeTags tag, Value val) {
(tag == TypeTags::NumberDecimal && bitcastTo<Decimal128>(val).isInfinite());
}
-void ArraySet::push_back(TypeTags tag, Value val) {
+bool ArraySet::push_back(TypeTags tag, Value val) {
if (tag != TypeTags::Nothing) {
ValueGuard guard{tag, val};
auto [it, inserted] = _values.insert({tag, val});
@@ -868,7 +868,11 @@ void ArraySet::push_back(TypeTags tag, Value val) {
if (inserted) {
guard.reset();
}
+
+ return inserted;
}
+
+ return false;
}
std::pair<TypeTags, Value> ArrayEnumerator::getViewOfValue() const {
diff --git a/src/mongo/db/exec/sbe/values/value.h b/src/mongo/db/exec/sbe/values/value.h
index 26ba3e5e1de..ae207106d8b 100644
--- a/src/mongo/db/exec/sbe/values/value.h
+++ b/src/mongo/db/exec/sbe/values/value.h
@@ -844,7 +844,14 @@ public:
}
}
- void push_back(TypeTags tag, Value val);
+ /**
+ * Adds the given SBE value to the set if an equal value is not already present. Assumes
+ * ownership of the given value.
+ *
+ * Returns true if the value was newly inserted, otherwise returns false to indicate that an
+ * equal value was already present in the set.
+ */
+ bool push_back(TypeTags tag, Value val);
auto& values() const noexcept {
return _values;
diff --git a/src/mongo/db/exec/sbe/values/value_builder.h b/src/mongo/db/exec/sbe/values/value_builder.h
index 9ad2b511242..00333e9f824 100644
--- a/src/mongo/db/exec/sbe/values/value_builder.h
+++ b/src/mongo/db/exec/sbe/values/value_builder.h
@@ -191,8 +191,11 @@ public:
virtual size_t numValues() const = 0;
protected:
+ // We expect most rows to end up containing this many values or fewer.
+ static constexpr int kInlinedVectorSize = 16;
+
std::pair<TypeTags, Value> getValue(size_t index, int bufferLen) {
- invariant(index < _numValues);
+ invariant(index < _tagList.size());
auto tag = _tagList[index];
auto val = _valList[index];
@@ -224,9 +227,8 @@ protected:
}
void appendValue(TypeTags tag, Value val) noexcept {
- _tagList[_numValues] = tag;
- _valList[_numValues] = val;
- ++_numValues;
+ _tagList.push_back(tag);
+ _valList.push_back(val);
}
void appendValue(std::pair<TypeTags, Value> in) noexcept {
@@ -241,14 +243,12 @@ protected:
// storing a pointer, we store an _offset_ into the under-construction buffer. Translation from
// offset to pointer occurs as part of the 'readValues()' function.
void appendValueBufferOffset(TypeTags tag) {
- _tagList[_numValues] = tag;
- _valList[_numValues] = value::bitcastFrom<int32_t>(_valueBufferBuilder->len());
- ++_numValues;
+ _tagList.push_back(tag);
+ _valList.push_back(value::bitcastFrom<int32_t>(_valueBufferBuilder->len()));
}
- std::array<TypeTags, Ordering::kMaxCompoundIndexKeys> _tagList;
- std::array<Value, Ordering::kMaxCompoundIndexKeys> _valList;
- size_t _numValues = 0;
+ absl::InlinedVector<TypeTags, kInlinedVectorSize> _tagList;
+ absl::InlinedVector<Value, kInlinedVectorSize> _valList;
BufBuilder* _valueBufferBuilder;
};
@@ -270,11 +270,12 @@ public:
// buffer, this value will remain in that buffer, even though we've removed it from the
// list. It will still get deallocated along with everything else when that buffer gets
// cleared or deleted, though, so there is no leak.
- --_numValues;
+ _tagList.pop_back();
+ _valList.pop_back();
}
size_t numValues() const override {
- return _numValues;
+ return _tagList.size();
}
/**
@@ -284,7 +285,7 @@ public:
*/
void readValues(std::vector<OwnedValueAccessor>* accessors) {
auto bufferLen = _valueBufferBuilder->len();
- for (size_t i = 0; i < _numValues; ++i) {
+ for (size_t i = 0; i < _tagList.size(); ++i) {
auto [tag, val] = getValue(i, bufferLen);
invariant(i < accessors->size());
(*accessors)[i].reset(false, tag, val);
@@ -304,7 +305,7 @@ public:
size_t numValues() const override {
size_t nVals = 0;
size_t bufIdx = 0;
- while (bufIdx < _numValues) {
+ while (bufIdx < _tagList.size()) {
auto tag = _tagList[bufIdx];
auto val = _valList[bufIdx];
if (tag == TypeTags::Boolean && !bitcastTo<bool>(val)) {
@@ -323,7 +324,10 @@ public:
auto bufferLen = _valueBufferBuilder->len();
size_t bufIdx = 0;
size_t rowIdx = 0;
- while (bufIdx < _numValues) {
+ // The 'row' output parameter might be smaller than the number of values owned by this
+ // builder. Be careful to only read as many values into 'row' as this output 'row' has space
+ // for.
+ while (rowIdx < row.size()) {
invariant(rowIdx < row.size());
auto [_, tagNothing, valNothing] = getValue(bufIdx++, bufferLen);
tassert(6136200, "sbe tag must be 'Boolean'", tagNothing == TypeTags::Boolean);
diff --git a/src/mongo/db/exec/sbe/values/value_serialization_test.cpp b/src/mongo/db/exec/sbe/values/value_serialization_test.cpp
index 5b44bef6549..3be3212c627 100644
--- a/src/mongo/db/exec/sbe/values/value_serialization_test.cpp
+++ b/src/mongo/db/exec/sbe/values/value_serialization_test.cpp
@@ -268,6 +268,18 @@ TEST_F(ValueSerializeForKeyString, SbeArray) {
runTest({{testDataTag, testDataVal}});
}
+TEST_F(ValueSerializeForKeyString, ArraySet) {
+ auto [tag, val] = sbe::value::makeNewArraySet();
+ sbe::value::ValueGuard guard{tag, val};
+ auto* arraySet = sbe::value::getArraySetView(val);
+
+ arraySet->push_back(value::TypeTags::NumberInt32, value::bitcastFrom<int32_t>(1));
+ arraySet->push_back(value::TypeTags::NumberInt64, value::bitcastFrom<int64_t>(2));
+ arraySet->push_back(value::TypeTags::NumberDouble, value::bitcastFrom<double>(3.0));
+
+ runTest({{tag, val}});
+}
+
TEST_F(ValueSerializeForKeyString, DateTime) {
runTest({{value::TypeTags::Date, value::bitcastFrom<int64_t>(1234)},
{value::TypeTags::Timestamp, value::bitcastFrom<uint64_t>(5678)}});
@@ -442,4 +454,17 @@ TEST_F(ValueSerializeForKeyString, BsonCodeWScope) {
runTest({{cwsTag1, cwsVal1}, {cwsTag2, cwsVal2}, {cwsTag3, cwsVal3}});
}
+
+// Test that roundtripping through KeyString works for a wide row. KeyStrings used in indexes are
+// typically constrained in the number of components they can have, since we limit compound indexes
+// to at most 32 components. But roundtripping rows wider than 32 still needs to work.
+//
+// This test was originally designed to reproduce SERVER-76321.
+TEST_F(ValueSerializeForKeyString, RoundtripWideRow) {
+ std::vector<std::pair<sbe::value::TypeTags, sbe::value::Value>> row;
+ for (int32_t i = 0; i < 40; ++i) {
+ row.emplace_back(sbe::value::TypeTags::NumberInt32, sbe::value::bitcastFrom<int32_t>(i));
+ }
+ runTest(row);
+}
} // namespace mongo::sbe