diff options
Diffstat (limited to 'src/mongo/db/exec/sbe/values')
| -rw-r--r-- | src/mongo/db/exec/sbe/values/slot.cpp | 11 | ||||
| -rw-r--r-- | src/mongo/db/exec/sbe/values/slot.h | 16 | ||||
| -rw-r--r-- | src/mongo/db/exec/sbe/values/value.cpp | 6 | ||||
| -rw-r--r-- | src/mongo/db/exec/sbe/values/value.h | 9 | ||||
| -rw-r--r-- | src/mongo/db/exec/sbe/values/value_builder.h | 34 | ||||
| -rw-r--r-- | src/mongo/db/exec/sbe/values/value_serialization_test.cpp | 25 |
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 |
