diff options
| author | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-11 15:07:35 -0300 |
|---|---|---|
| committer | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-11 15:07:35 -0300 |
| commit | 4cb8841196d0625dfa3825aa326f071cd27c7b8b (patch) | |
| tree | 1682a647d4463397c119183369ae6f750d5fdcff /src/mongo/db/exec/sbe/values | |
| parent | aa03c6362cbaa767638e6eed9b031d86dd2643d1 (diff) | |
| parent | 8f0827553e09872941945a093b647a4211a9db7f (diff) | |
Update upstream source from tag 'upstream/6.0.0'master
Update to upstream version '6.0.0'
with Debian dir 5604a80ec1c96ca76f25f40d78e6ef855abec322
Diffstat (limited to 'src/mongo/db/exec/sbe/values')
| -rw-r--r-- | src/mongo/db/exec/sbe/values/slot.cpp | 110 | ||||
| -rw-r--r-- | src/mongo/db/exec/sbe/values/slot.h | 29 | ||||
| -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 | 38 |
6 files changed, 57 insertions, 169 deletions
diff --git a/src/mongo/db/exec/sbe/values/slot.cpp b/src/mongo/db/exec/sbe/values/slot.cpp index 93605a9abfa..45cbc977980 100644 --- a/src/mongo/db/exec/sbe/values/slot.cpp +++ b/src/mongo/db/exec/sbe/values/slot.cpp @@ -42,9 +42,7 @@ #include "mongo/util/bufreader.h" namespace mongo::sbe::value { - -static std::pair<TypeTags, Value> deserializeValue(BufReader& buf, - const CollatorInterface* collator) { +static std::pair<TypeTags, Value> deserializeValue(BufReader& buf) { auto tag = static_cast<TypeTags>(buf.read<uint8_t>()); Value val; @@ -109,7 +107,7 @@ static std::pair<TypeTags, Value> deserializeValue(BufReader& buf, if (cnt) { arr->reserve(cnt); for (size_t idx = 0; idx < cnt; ++idx) { - auto [tag, val] = deserializeValue(buf, collator); + auto [tag, val] = deserializeValue(buf); arr->push_back(tag, val); } } @@ -118,16 +116,13 @@ static std::pair<TypeTags, Value> deserializeValue(BufReader& buf, break; } case TypeTags::ArraySet: { - // The first byte is a flag to tell us whether the ArraySet had a collation prior to - // serialization. - auto collated = buf.read<char>(); - auto [arrTag, arrVal] = makeNewArraySet(collated ? collator : nullptr); auto cnt = buf.read<LittleEndian<size_t>>(); + auto [arrTag, arrVal] = makeNewArraySet(); auto arr = getArraySetView(arrVal); if (cnt) { arr->reserve(cnt); for (size_t idx = 0; idx < cnt; ++idx) { - auto [tag, val] = deserializeValue(buf, collator); + auto [tag, val] = deserializeValue(buf); arr->push_back(tag, val); } } @@ -143,7 +138,7 @@ static std::pair<TypeTags, Value> deserializeValue(BufReader& buf, obj->reserve(cnt); for (size_t idx = 0; idx < cnt; ++idx) { auto fieldName = buf.readCStr(); - auto [tag, val] = deserializeValue(buf, collator); + auto [tag, val] = deserializeValue(buf); obj->push_back(fieldName, tag, val); } } @@ -218,12 +213,12 @@ static std::pair<TypeTags, Value> deserializeValue(BufReader& buf, } MaterializedRow MaterializedRow::deserializeForSorter(BufReader& buf, - const SorterDeserializeSettings& settings) { + const SorterDeserializeSettings&) { auto cnt = buf.read<LittleEndian<size_t>>(); MaterializedRow result{cnt}; for (size_t idx = 0; idx < cnt; ++idx) { - auto [tag, val] = deserializeValue(buf, settings.collator); + auto [tag, val] = deserializeValue(buf); result.reset(idx, true, tag, val); } @@ -293,12 +288,6 @@ static void serializeValue(BufBuilder& buf, TypeTags tag, Value val) { } case TypeTags::ArraySet: { auto arr = getArraySetView(val); - // If an ArraySet has a collation, we serialize a byte which acts as a flag as to - // whether the set should be created with a collation upon deserialization. Also, we - // assume that the caller which does deserialization will have the context about what - // the collation is, and therefore we can save space by not serializing the full - // description of the collation. - buf.appendChar(arr->getCollator() ? 1 : 0); buf.appendNum(arr->size()); for (auto& kv : arr->values()) { serializeValue(buf, kv.first, kv.second); @@ -382,22 +371,7 @@ static void serializeValue(BufBuilder& buf, TypeTags tag, Value val) { } } -/** - * If non-null 'collator' is provided during serialization, then the encoding guarantees that values - * which are equal up to the collation will encode to the same result, allowing for collation-aware - * equality comparisons. However, the collator-aware encoded values are not always decodable. Groups - * store an original copy of the group key alongside the encoded key string when there is a - * collation, so the key string does not need to be decodable. - */ -static void serializeValueIntoKeyString(KeyString::Builder& buf, - TypeTags tag, - Value val, - const CollatorInterface* collator) { - - const auto stringTransformFn = [&](StringData stringData) { - return collator->getComparisonString(stringData); - }; - +static void serializeValueIntoKeyString(KeyString::Builder& buf, TypeTags tag, Value val) { switch (tag) { case TypeTags::Nothing: { buf.appendBool(false); @@ -460,21 +434,19 @@ static void serializeValueIntoKeyString(KeyString::Builder& buf, buf.appendUndefined(); break; } - case TypeTags::StringSmall: + case TypeTags::StringSmall: { + // Small strings cannot contain null bytes, so it is safe to serialize them as plain + // C-strings with a null terminator. + buf.appendBool(true); + buf.appendString(getStringView(tag, val)); + break; + } case TypeTags::StringBig: case TypeTags::bsonString: { buf.appendBool(true); - if (collator) { - buf.appendString(getStringView(tag, val), stringTransformFn); - } else { - buf.appendString(getStringView(tag, val)); - } + buf.appendString(getStringOrSymbolView(tag, val)); break; } - // Note that the collation would have to apply to bsonSymbol values to match the - // behavior of the Classic engine when spilling groups to disk. bsonSymbol is - // deprecated, however, and SBE no longer provides strict correctness guarantees about - // computations on bsonSymbol values. case TypeTags::bsonSymbol: { buf.appendBool(true); buf.appendSymbol(getStringOrSymbolView(tag, val)); @@ -485,13 +457,9 @@ static void serializeValueIntoKeyString(KeyString::Builder& buf, // TODO SERVER-61629: convert this to serialize the 'arr' directly instead of // constructing a BSONArray. BSONArrayBuilder builder; - bson::convertToBsonObj(builder, value::ArrayEnumerator{tag, val}); + bson::convertToBsonObj(builder, getArrayView(val)); buf.appendBool(true); - if (collator) { - buf.appendArray(builder.arr(), stringTransformFn); - } else { - buf.appendArray(builder.arr()); - } + buf.appendArray(BSONArray(builder.done())); break; } case TypeTags::Object: { @@ -500,37 +468,27 @@ static void serializeValueIntoKeyString(KeyString::Builder& buf, BSONObjBuilder builder; bson::convertToBsonObj(builder, getObjectView(val)); buf.appendBool(true); - if (collator) { - buf.appendObject(builder.done(), stringTransformFn); - } else { - buf.appendObject(builder.done()); - } + buf.appendObject(builder.done()); break; } - case TypeTags::bsonObjectId: case TypeTags::ObjectId: { buf.appendBool(true); - buf.appendOID(OID::from(getRawPointerView(val))); + buf.appendBytes(getObjectIdView(val), sizeof(ObjectIdType)); break; } case TypeTags::bsonObject: { - BSONObj bson{getRawPointerView(val)}; buf.appendBool(true); - if (collator) { - buf.appendObject(bson, stringTransformFn); - } else { - buf.appendObject(bson); - } + buf.appendObject(BSONObj(getRawPointerView(val))); break; } case TypeTags::bsonArray: { - BSONObj bson{getRawPointerView(val)}; buf.appendBool(true); - if (collator) { - buf.appendArray(BSONArray(BSONObj(bson)), stringTransformFn); - } else { - buf.appendArray(BSONArray(BSONObj(bson))); - } + buf.appendArray(BSONArray(BSONObj(getRawPointerView(val)))); + break; + } + case TypeTags::bsonObjectId: { + buf.appendBool(true); + buf.appendOID(OID::from(getRawPointerView(val))); break; } case TypeTags::bsonBinData: { @@ -599,18 +557,15 @@ void MaterializedRow::serializeForSorter(BufBuilder& buf) const { } } -void MaterializedRow::serializeIntoKeyString(KeyString::Builder& buf, - const CollatorInterface* collator) const { +void MaterializedRow::serializeIntoKeyString(KeyString::Builder& buf) const { for (size_t idx = 0; idx < size(); ++idx) { auto [tag, val] = getViewOfValue(idx); - serializeValueIntoKeyString(buf, tag, val, collator); + serializeValueIntoKeyString(buf, tag, val); } } -MaterializedRow MaterializedRow::deserializeFromKeyString( - const KeyString::Value& keyString, - BufBuilder* valueBufferBuilder, - boost::optional<size_t> numPrefixValsToRead) { +MaterializedRow MaterializedRow::deserializeFromKeyString(const KeyString::Value& keyString, + BufBuilder* valueBufferBuilder) { BufReader reader(keyString.getBuffer(), keyString.getSize()); KeyString::TypeBits typeBits(keyString.getTypeBits()); KeyString::TypeBits::Reader typeBitsReader(typeBits); @@ -622,8 +577,7 @@ MaterializedRow MaterializedRow::deserializeFromKeyString( &reader, &typeBitsReader, false /* inverted */, typeBits.version, &valBuilder); } while (keepReading); - size_t sizeOfRow = numPrefixValsToRead ? *numPrefixValsToRead : valBuilder.numValues(); - MaterializedRow result{sizeOfRow}; + MaterializedRow result{valBuilder.numValues()}; 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 e682b2bfeb8..f853f816d4d 100644 --- a/src/mongo/db/exec/sbe/values/slot.h +++ b/src/mongo/db/exec/sbe/values/slot.h @@ -468,11 +468,8 @@ public: } // The following methods are used by the sorter only. - struct SorterDeserializeSettings { - const CollatorInterface* collator{nullptr}; - }; - static MaterializedRow deserializeForSorter(BufReader& buf, - const SorterDeserializeSettings& settings); + struct SorterDeserializeSettings {}; + static MaterializedRow deserializeForSorter(BufReader& buf, const SorterDeserializeSettings&); void serializeForSorter(BufBuilder& buf) const; int memUsageForSorter() const; auto getOwned() const { @@ -486,21 +483,11 @@ 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. - * - * If non-null 'collator' is provided during serialization, then any strings in the row are - * encoded as ICU collation keys prior to being KeyString-encoded. */ - static MaterializedRow deserializeFromKeyString( - const KeyString::Value& keyString, - BufBuilder* valueBufferBuilder, - boost::optional<size_t> numPrefixValsToRead = boost::none); + static MaterializedRow deserializeFromKeyString(const KeyString::Value& keyString, - void serializeIntoKeyString(KeyString::Builder& builder, - const CollatorInterface* collator = nullptr) const; + BufBuilder* valueBufferBuilder); + void serializeIntoKeyString(KeyString::Builder& builder) const; private: static size_t sizeInBytes(size_t count) { @@ -590,9 +577,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 { @@ -610,7 +597,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 e0f73c87ddf..6c21f4f6e5d 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()); } -bool ArraySet::push_back(TypeTags tag, Value val) { +void ArraySet::push_back(TypeTags tag, Value val) { if (tag != TypeTags::Nothing) { ValueGuard guard{tag, val}; auto [it, inserted] = _values.insert({tag, val}); @@ -868,11 +868,7 @@ bool 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 ae207106d8b..26ba3e5e1de 100644 --- a/src/mongo/db/exec/sbe/values/value.h +++ b/src/mongo/db/exec/sbe/values/value.h @@ -844,14 +844,7 @@ public: } } - /** - * 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); + void 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 00333e9f824..9ad2b511242 100644 --- a/src/mongo/db/exec/sbe/values/value_builder.h +++ b/src/mongo/db/exec/sbe/values/value_builder.h @@ -191,11 +191,8 @@ 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 < _tagList.size()); + invariant(index < _numValues); auto tag = _tagList[index]; auto val = _valList[index]; @@ -227,8 +224,9 @@ protected: } void appendValue(TypeTags tag, Value val) noexcept { - _tagList.push_back(tag); - _valList.push_back(val); + _tagList[_numValues] = tag; + _valList[_numValues] = val; + ++_numValues; } void appendValue(std::pair<TypeTags, Value> in) noexcept { @@ -243,12 +241,14 @@ 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.push_back(tag); - _valList.push_back(value::bitcastFrom<int32_t>(_valueBufferBuilder->len())); + _tagList[_numValues] = tag; + _valList[_numValues] = value::bitcastFrom<int32_t>(_valueBufferBuilder->len()); + ++_numValues; } - absl::InlinedVector<TypeTags, kInlinedVectorSize> _tagList; - absl::InlinedVector<Value, kInlinedVectorSize> _valList; + std::array<TypeTags, Ordering::kMaxCompoundIndexKeys> _tagList; + std::array<Value, Ordering::kMaxCompoundIndexKeys> _valList; + size_t _numValues = 0; BufBuilder* _valueBufferBuilder; }; @@ -270,12 +270,11 @@ 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. - _tagList.pop_back(); - _valList.pop_back(); + --_numValues; } size_t numValues() const override { - return _tagList.size(); + return _numValues; } /** @@ -285,7 +284,7 @@ public: */ void readValues(std::vector<OwnedValueAccessor>* accessors) { auto bufferLen = _valueBufferBuilder->len(); - for (size_t i = 0; i < _tagList.size(); ++i) { + for (size_t i = 0; i < _numValues; ++i) { auto [tag, val] = getValue(i, bufferLen); invariant(i < accessors->size()); (*accessors)[i].reset(false, tag, val); @@ -305,7 +304,7 @@ public: size_t numValues() const override { size_t nVals = 0; size_t bufIdx = 0; - while (bufIdx < _tagList.size()) { + while (bufIdx < _numValues) { auto tag = _tagList[bufIdx]; auto val = _valList[bufIdx]; if (tag == TypeTags::Boolean && !bitcastTo<bool>(val)) { @@ -324,10 +323,7 @@ public: auto bufferLen = _valueBufferBuilder->len(); size_t bufIdx = 0; size_t rowIdx = 0; - // 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()) { + while (bufIdx < _numValues) { 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 ceacf610a4a..5b44bef6549 100644 --- a/src/mongo/db/exec/sbe/values/value_serialization_test.cpp +++ b/src/mongo/db/exec/sbe/values/value_serialization_test.cpp @@ -268,18 +268,6 @@ 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)}}); @@ -454,30 +442,4 @@ 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); -} - -// Test that roundtripping through KeyString works for ObjectIdType: ObjectId; bsonObjectId. -TEST_F(ValueSerializeForKeyString, RoundtripObjectIdType) { - auto [objectIdTag, objectIdVal] = value::makeNewObjectId(); - - auto oid = OID::gen(); - auto obj = BSON("" << oid); - auto oidStorage = obj.firstElement().value(); - - sbe::value::ValueGuard testDataGuard{objectIdTag, objectIdVal}; - runTest({{objectIdTag, objectIdVal}, - {value::TypeTags::bsonObjectId, value::bitcastFrom<const char*>(oidStorage)}}); -} } // namespace mongo::sbe |
