diff options
author | Martin Neupauer <martin.neupauer@mongodb.com> | 2022-01-14 19:17:03 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-01-14 20:50:30 +0000 |
commit | 5a86a7dfff7743a7ac2dc9484fc854351c253f69 (patch) | |
tree | 101c6c32d2c2e9d2749b3b08727ca76a765a6f01 | |
parent | 94d5eb0aba620a65439f0252377d74dd7683f485 (diff) | |
download | mongo-5a86a7dfff7743a7ac2dc9484fc854351c253f69.tar.gz |
SERVER-61630 support a string form of RecordId
-rw-r--r-- | jstests/noPassthrough/plan_cache_replan_sort.js | 2 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/sbe_hash_agg_test.cpp | 74 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/stages/ix_scan.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/stages/scan.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/stages/scan.h | 4 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/values/bson.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/values/slot.cpp | 11 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/values/value.cpp | 27 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/values/value.h | 14 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/values/value_builder.h | 4 | ||||
-rw-r--r-- | src/mongo/db/exec/sbe/values/value_serialization_test.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/query/plan_executor_sbe.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_stage_builder_coll_scan.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/record_id.h | 33 | ||||
-rw-r--r-- | src/mongo/db/record_id_test.cpp | 42 |
15 files changed, 203 insertions, 58 deletions
diff --git a/jstests/noPassthrough/plan_cache_replan_sort.js b/jstests/noPassthrough/plan_cache_replan_sort.js index 6683b502592..78c55711c86 100644 --- a/jstests/noPassthrough/plan_cache_replan_sort.js +++ b/jstests/noPassthrough/plan_cache_replan_sort.js @@ -33,7 +33,7 @@ assert.commandWorked(coll.insert({x: 5, y: 1})); // Set the memory limit to be large enough to sort a single document in the collection. const documentBsonSize = Object.bsonsize(docs[0]); -const sizeMultiplier = 4.0; +const sizeMultiplier = 5.0; assert.commandWorked(db.adminCommand({ setParameter: 1, internalQueryMaxBlockingSortMemoryUsageBytes: documentBsonSize * sizeMultiplier diff --git a/src/mongo/db/exec/sbe/sbe_hash_agg_test.cpp b/src/mongo/db/exec/sbe/sbe_hash_agg_test.cpp index 58ecd40b98e..7037bd1e86d 100644 --- a/src/mongo/db/exec/sbe/sbe_hash_agg_test.cpp +++ b/src/mongo/db/exec/sbe/sbe_hash_agg_test.cpp @@ -770,18 +770,50 @@ TEST_F(HashAggStageTest, HashAggBasicCountWithRecordIds) { // Build a scan of record ids [1,10,999,10,1,999,8589869056,999,10,8589869056] input array. auto [inputTag, inputVal] = sbe::value::makeNewArray(); auto testData = sbe::value::getArrayView(inputVal); - - testData->push_back(value::TypeTags::RecordId, value::bitcastFrom<int64_t>(1)); - testData->push_back(value::TypeTags::RecordId, value::bitcastFrom<int64_t>(10)); - testData->push_back(value::TypeTags::RecordId, value::bitcastFrom<int64_t>(999)); - testData->push_back(value::TypeTags::RecordId, value::bitcastFrom<int64_t>(10)); - testData->push_back(value::TypeTags::RecordId, value::bitcastFrom<int64_t>(999)); - testData->push_back(value::TypeTags::RecordId, value::bitcastFrom<int64_t>(1)); - testData->push_back(value::TypeTags::RecordId, value::bitcastFrom<int64_t>(999)); - testData->push_back(value::TypeTags::RecordId, value::bitcastFrom<int64_t>(8589869056)); - testData->push_back(value::TypeTags::RecordId, value::bitcastFrom<int64_t>(999)); - testData->push_back(value::TypeTags::RecordId, value::bitcastFrom<int64_t>(10)); - testData->push_back(value::TypeTags::RecordId, value::bitcastFrom<int64_t>(8589869056)); + { + auto [ridTag, ridVal] = sbe::value::makeNewRecordId(1); + testData->push_back(ridTag, ridVal); + } + { + auto [ridTag, ridVal] = sbe::value::makeNewRecordId(10); + testData->push_back(ridTag, ridVal); + } + { + auto [ridTag, ridVal] = sbe::value::makeNewRecordId(999); + testData->push_back(ridTag, ridVal); + } + { + auto [ridTag, ridVal] = sbe::value::makeNewRecordId(10); + testData->push_back(ridTag, ridVal); + } + { + auto [ridTag, ridVal] = sbe::value::makeNewRecordId(999); + testData->push_back(ridTag, ridVal); + } + { + auto [ridTag, ridVal] = sbe::value::makeNewRecordId(1); + testData->push_back(ridTag, ridVal); + } + { + auto [ridTag, ridVal] = sbe::value::makeNewRecordId(999); + testData->push_back(ridTag, ridVal); + } + { + auto [ridTag, ridVal] = sbe::value::makeNewRecordId(8589869056); + testData->push_back(ridTag, ridVal); + } + { + auto [ridTag, ridVal] = sbe::value::makeNewRecordId(999); + testData->push_back(ridTag, ridVal); + } + { + auto [ridTag, ridVal] = sbe::value::makeNewRecordId(10); + testData->push_back(ridTag, ridVal); + } + { + auto [ridTag, ridVal] = sbe::value::makeNewRecordId(8589869056); + testData->push_back(ridTag, ridVal); + } auto [scanSlot, scanStage] = generateVirtualScan(inputTag, inputVal); @@ -807,8 +839,8 @@ TEST_F(HashAggStageTest, HashAggBasicCountWithRecordIds) { auto [res1ScanTag, res1ScanVal] = resultAccessors[0]->getViewOfValue(); auto [res1Tag, res1Val] = resultAccessors[1]->getViewOfValue(); // There are '2' occurences of '1' in the input. - assertValuesEqual( - res1ScanTag, res1ScanVal, value::TypeTags::RecordId, value::bitcastFrom<int64_t>(1)); + ASSERT_TRUE(res1ScanTag == value::TypeTags::RecordId); + ASSERT_TRUE(sbe::value::getRecordIdView(res1ScanVal)->getLong() == 1); assertValuesEqual( res1Tag, res1Val, value::TypeTags::NumberInt64, value::bitcastFrom<int64_t>(2)); @@ -816,10 +848,8 @@ TEST_F(HashAggStageTest, HashAggBasicCountWithRecordIds) { auto [res2ScanTag, res2ScanVal] = resultAccessors[0]->getViewOfValue(); auto [res2Tag, res2Val] = resultAccessors[1]->getViewOfValue(); // There are '2' occurences of '8589869056' in the input. - assertValuesEqual(res2ScanTag, - res2ScanVal, - value::TypeTags::RecordId, - value::bitcastFrom<int64_t>(8589869056)); + ASSERT_TRUE(res2ScanTag == value::TypeTags::RecordId); + ASSERT_TRUE(sbe::value::getRecordIdView(res2ScanVal)->getLong() == 8589869056); assertValuesEqual( res2Tag, res2Val, value::TypeTags::NumberInt64, value::bitcastFrom<int64_t>(2)); @@ -827,8 +857,8 @@ TEST_F(HashAggStageTest, HashAggBasicCountWithRecordIds) { auto [res3ScanTag, res3ScanVal] = resultAccessors[0]->getViewOfValue(); auto [res3Tag, res3Val] = resultAccessors[1]->getViewOfValue(); // There are '3' occurences of '10' in the input. - assertValuesEqual( - res3ScanTag, res3ScanVal, value::TypeTags::RecordId, value::bitcastFrom<int64_t>(10)); + ASSERT_TRUE(res3ScanTag == value::TypeTags::RecordId); + ASSERT_TRUE(sbe::value::getRecordIdView(res3ScanVal)->getLong() == 10); assertValuesEqual( res3Tag, res3Val, value::TypeTags::NumberInt64, value::bitcastFrom<int64_t>(3)); @@ -836,8 +866,8 @@ TEST_F(HashAggStageTest, HashAggBasicCountWithRecordIds) { auto [res4ScanTag, res4ScanVal] = resultAccessors[0]->getViewOfValue(); auto [res4Tag, res4Val] = resultAccessors[1]->getViewOfValue(); // There are '4' occurences of '999' in the input. - assertValuesEqual( - res4ScanTag, res4ScanVal, value::TypeTags::RecordId, value::bitcastFrom<int64_t>(999)); + ASSERT_TRUE(res4ScanTag == value::TypeTags::RecordId); + ASSERT_TRUE(sbe::value::getRecordIdView(res4ScanVal)->getLong() == 999); assertValuesEqual( res4Tag, res4Val, value::TypeTags::NumberInt64, value::bitcastFrom<int64_t>(4)); ASSERT_TRUE(stage->getNext() == PlanState::IS_EOF); diff --git a/src/mongo/db/exec/sbe/stages/ix_scan.cpp b/src/mongo/db/exec/sbe/stages/ix_scan.cpp index dc8244b047b..ebd2efcc5d6 100644 --- a/src/mongo/db/exec/sbe/stages/ix_scan.cpp +++ b/src/mongo/db/exec/sbe/stages/ix_scan.cpp @@ -381,9 +381,8 @@ PlanState IndexScanStage::getNext() { } if (_recordIdAccessor) { - _recordIdAccessor->reset(false, - value::TypeTags::RecordId, - value::bitcastFrom<int64_t>(_nextRecord->loc.getLong())); + _recordIdAccessor->reset( + false, value::TypeTags::RecordId, value::bitcastFrom<RecordId*>(&_nextRecord->loc)); } if (_accessors.size()) { diff --git a/src/mongo/db/exec/sbe/stages/scan.cpp b/src/mongo/db/exec/sbe/stages/scan.cpp index 03f8a9271ca..ad3d1b7e06b 100644 --- a/src/mongo/db/exec/sbe/stages/scan.cpp +++ b/src/mongo/db/exec/sbe/stages/scan.cpp @@ -317,7 +317,7 @@ void ScanStage::open(bool reOpen) { str::stream() << "seek key is wrong type: " << msgTag, tag == value::TypeTags::RecordId); - _key = RecordId{value::bitcastTo<int64_t>(val)}; + _key = *value::getRecordIdView(val); } if (!_cursor || !_seekKeyAccessor) { @@ -391,9 +391,9 @@ PlanState ScanStage::getNext() { } if (_recordIdAccessor) { - _recordIdAccessor->reset(false, - value::TypeTags::RecordId, - value::bitcastFrom<int64_t>(nextRecord->id.getLong())); + _recordId = nextRecord->id; + _recordIdAccessor->reset( + false, value::TypeTags::RecordId, value::bitcastFrom<RecordId*>(&_recordId)); } if (!_fieldAccessors.empty()) { @@ -899,9 +899,9 @@ PlanState ParallelScanStage::getNext() { } if (_recordIdAccessor) { - _recordIdAccessor->reset(false, - value::TypeTags::RecordId, - value::bitcastFrom<int64_t>(nextRecord->id.getLong())); + _recordId = nextRecord->id; + _recordIdAccessor->reset( + false, value::TypeTags::RecordId, value::bitcastFrom<RecordId*>(&_recordId)); } diff --git a/src/mongo/db/exec/sbe/stages/scan.h b/src/mongo/db/exec/sbe/stages/scan.h index c7f10b35acd..95982e6eb0c 100644 --- a/src/mongo/db/exec/sbe/stages/scan.h +++ b/src/mongo/db/exec/sbe/stages/scan.h @@ -172,6 +172,8 @@ private: value::SlotAccessorMap _varAccessors; value::SlotAccessor* _seekKeyAccessor{nullptr}; + RecordId _recordId; + bool _open{false}; std::unique_ptr<SeekableRecordCursor> _cursor; @@ -292,6 +294,8 @@ private: size_t _currentRange{std::numeric_limits<std::size_t>::max()}; Range _range; + RecordId _recordId; + bool _open{false}; std::unique_ptr<SeekableRecordCursor> _cursor; diff --git a/src/mongo/db/exec/sbe/values/bson.cpp b/src/mongo/db/exec/sbe/values/bson.cpp index d8ebe574b46..446a71b2301 100644 --- a/src/mongo/db/exec/sbe/values/bson.cpp +++ b/src/mongo/db/exec/sbe/values/bson.cpp @@ -287,7 +287,6 @@ void convertToBsonObj(ArrayBuilder& builder, value::ArrayEnumerator arr) { case value::TypeTags::NumberInt32: builder.append(value::bitcastTo<int32_t>(val)); break; - case value::TypeTags::RecordId: case value::TypeTags::NumberInt64: builder.append(value::bitcastTo<int64_t>(val)); break; @@ -426,7 +425,6 @@ void appendValueToBsonObj(ObjBuilder& builder, case value::TypeTags::NumberInt32: builder.append(name, value::bitcastTo<int32_t>(val)); break; - case value::TypeTags::RecordId: case value::TypeTags::NumberInt64: builder.append(name, value::bitcastTo<int64_t>(val)); break; diff --git a/src/mongo/db/exec/sbe/values/slot.cpp b/src/mongo/db/exec/sbe/values/slot.cpp index 607e4dadb59..2a2af526aee 100644 --- a/src/mongo/db/exec/sbe/values/slot.cpp +++ b/src/mongo/db/exec/sbe/values/slot.cpp @@ -52,6 +52,8 @@ static std::pair<TypeTags, Value> deserializeValue(BufReader& buf) { val = bitcastFrom<int32_t>(buf.read<LittleEndian<int32_t>>()); break; case TypeTags::RecordId: + std::tie(tag, val) = makeCopyRecordId(RecordId::deserializeToken(buf)); + break; case TypeTags::NumberInt64: val = bitcastFrom<int64_t>(buf.read<LittleEndian<int64_t>>()); break; @@ -232,6 +234,8 @@ static void serializeValue(BufBuilder& buf, TypeTags tag, Value val) { buf.appendNum(bitcastTo<int32_t>(val)); break; case TypeTags::RecordId: + getRecordIdView(val)->serializeToken(buf); + break; case TypeTags::NumberInt64: buf.appendNum(bitcastTo<int64_t>(val)); break; @@ -530,10 +534,9 @@ static void serializeValueIntoKeyString(KeyString::Builder& buf, TypeTags tag, V break; } case TypeTags::RecordId: { - // TODO SERVER-61630: Support RecordId strings when sbe also supports this. BufBuilder innerBinDataBuf; innerBinDataBuf.appendUChar(static_cast<uint8_t>(tag)); - innerBinDataBuf.appendNum(bitcastTo<int64_t>(val)); + getRecordIdView(val)->serializeToken(innerBinDataBuf); buf.appendBool(true); buf.appendBinData(BSONBinData( innerBinDataBuf.buf(), innerBinDataBuf.len(), BinDataType::BinDataGeneral)); @@ -592,13 +595,15 @@ int getApproximateSize(TypeTags tag, Value val) { case TypeTags::Timestamp: case TypeTags::Boolean: case TypeTags::StringSmall: - case TypeTags::RecordId: case TypeTags::MinKey: case TypeTags::MaxKey: case TypeTags::bsonUndefined: case TypeTags::LocalLambda: break; // There are deep types. + case TypeTags::RecordId: + result += getRecordIdView(val)->memUsage(); + break; case TypeTags::NumberDecimal: result += sizeof(Decimal128); break; diff --git a/src/mongo/db/exec/sbe/values/value.cpp b/src/mongo/db/exec/sbe/values/value.cpp index 84fd7eebc60..0e2e19d08a5 100644 --- a/src/mongo/db/exec/sbe/values/value.cpp +++ b/src/mongo/db/exec/sbe/values/value.cpp @@ -243,8 +243,27 @@ std::pair<TypeTags, Value> makeCopySortSpec(const SortSpec& ss) { return {TypeTags::sortSpec, ssCopy}; } +std::pair<TypeTags, Value> makeNewRecordId(int64_t rid) { + auto val = bitcastFrom<RecordId*>(new RecordId(rid)); + return {TypeTags::RecordId, val}; +} + +std::pair<TypeTags, Value> makeNewRecordId(const char* str, int32_t size) { + auto val = bitcastFrom<RecordId*>(new RecordId(str, size)); + return {TypeTags::RecordId, val}; +} + +std::pair<TypeTags, Value> makeCopyRecordId(const RecordId& rid) { + auto copy = bitcastFrom<RecordId*>(new RecordId(rid)); + return {TypeTags::RecordId, copy}; +} + + void releaseValue(TypeTags tag, Value val) noexcept { switch (tag) { + case TypeTags::RecordId: + delete getRecordIdView(val); + break; case TypeTags::NumberDecimal: delete[] getRawPointerView(val); break; @@ -644,7 +663,7 @@ void writeValueToStream(T& stream, TypeTags tag, Value val, size_t depth = 1) { break; } case TypeTags::RecordId: - stream << "RecordId(" << bitcastTo<int64_t>(val) << ")"; + stream << "RecordId(" << getRecordIdView(val)->toString() << ")"; break; case TypeTags::jsFunction: // TODO: Also include code. @@ -728,6 +747,7 @@ BSONType tagToType(TypeTags tag) noexcept { case TypeTags::NumberInt32: return BSONType::NumberInt; case TypeTags::RecordId: + return BSONType::EOO; case TypeTags::NumberInt64: return BSONType::NumberLong; case TypeTags::NumberDouble: @@ -799,12 +819,12 @@ bool isShallowType(TypeTags tag) noexcept { case TypeTags::Timestamp: case TypeTags::Boolean: case TypeTags::StringSmall: - case TypeTags::RecordId: case TypeTags::MinKey: case TypeTags::MaxKey: case TypeTags::bsonUndefined: case TypeTags::LocalLambda: return true; + case TypeTags::RecordId: case TypeTags::NumberDecimal: case TypeTags::StringBig: case TypeTags::bsonString: @@ -838,6 +858,7 @@ std::size_t hashValue(TypeTags tag, Value val, const CollatorInterface* collator case TypeTags::NumberInt32: return abslHash(bitcastTo<int32_t>(val)); case TypeTags::RecordId: + return getRecordIdView(val)->hash(); case TypeTags::NumberInt64: return abslHash(bitcastTo<int64_t>(val)); case TypeTags::NumberDouble: { @@ -1154,7 +1175,7 @@ std::pair<TypeTags, Value> compareValue(TypeTags lhsTag, // Special case for Nothing in a hash table (group) and sort comparison. return {TypeTags::NumberInt32, bitcastFrom<int32_t>(0)}; } else if (lhsTag == TypeTags::RecordId && rhsTag == TypeTags::RecordId) { - auto result = compareHelper(bitcastTo<int64_t>(lhsValue), bitcastTo<int64_t>(rhsValue)); + int32_t result = getRecordIdView(lhsValue)->compare(*getRecordIdView(rhsValue)); return {TypeTags::NumberInt32, bitcastFrom<int32_t>(result)}; } else if (lhsTag == TypeTags::bsonRegex && rhsTag == TypeTags::bsonRegex) { auto lhsRegex = getBsonRegexView(lhsValue); diff --git a/src/mongo/db/exec/sbe/values/value.h b/src/mongo/db/exec/sbe/values/value.h index c571773b655..b5947391347 100644 --- a/src/mongo/db/exec/sbe/values/value.h +++ b/src/mongo/db/exec/sbe/values/value.h @@ -56,8 +56,10 @@ namespace mongo { /** - * Forward declaration. + * Forward declarations. */ +class RecordId; + namespace KeyString { class Value; } @@ -1084,6 +1086,14 @@ inline uint8_t* getBSONBinDataCompat(TypeTags tag, Value val) { } } +inline RecordId* getRecordIdView(Value val) noexcept { + return reinterpret_cast<RecordId*>(val); +} + +std::pair<TypeTags, Value> makeNewRecordId(int64_t rid); +std::pair<TypeTags, Value> makeNewRecordId(const char* str, int32_t size); +std::pair<TypeTags, Value> makeCopyRecordId(const RecordId&); + inline bool canUseSmallString(StringData input) { auto length = input.size(); auto ptr = input.rawData(); @@ -1360,6 +1370,8 @@ void releaseValue(TypeTags tag, Value val) noexcept; inline std::pair<TypeTags, Value> copyValue(TypeTags tag, Value val) { switch (tag) { + case TypeTags::RecordId: + return makeCopyRecordId(*getRecordIdView(val)); case TypeTags::NumberDecimal: return makeCopyDecimal(bitcastTo<Decimal128>(val)); case TypeTags::Array: diff --git a/src/mongo/db/exec/sbe/values/value_builder.h b/src/mongo/db/exec/sbe/values/value_builder.h index c3937a1d221..9ad2b511242 100644 --- a/src/mongo/db/exec/sbe/values/value_builder.h +++ b/src/mongo/db/exec/sbe/values/value_builder.h @@ -357,8 +357,8 @@ private: return {true, ksTag, ksVal}; } case TypeTags::RecordId: { - int64_t ridValue = buf.read<LittleEndian<int64_t>>(); - return {false, TypeTags::RecordId, bitcastFrom<int64_t>(ridValue)}; + auto [tag, val] = makeCopyRecordId(RecordId::deserializeToken(buf)); + return {true, tag, val}; } default: MONGO_UNREACHABLE; 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 9373750fcfd..5b44bef6549 100644 --- a/src/mongo/db/exec/sbe/values/value_serialization_test.cpp +++ b/src/mongo/db/exec/sbe/values/value_serialization_test.cpp @@ -45,7 +45,8 @@ TEST(ValueSerializeForSorter, Serialize) { testData->push_back(value::TypeTags::Nothing, 0); testData->push_back(value::TypeTags::NumberInt32, value::bitcastFrom<int32_t>(33550336)); - testData->push_back(value::TypeTags::RecordId, value::bitcastFrom<int64_t>(8589869056)); + auto [ridTag, ridVal] = value::makeNewRecordId(8589869056); + testData->push_back(ridTag, ridVal); testData->push_back(value::TypeTags::NumberInt64, value::bitcastFrom<int64_t>(137438691328)); testData->push_back(value::TypeTags::NumberDouble, value::bitcastFrom<double>(2.305e18)); @@ -228,9 +229,9 @@ TEST_F(ValueSerializeForKeyString, Numerics) { } TEST_F(ValueSerializeForKeyString, RecordIdMinKeyMaxKey) { - runTest({{value::TypeTags::MinKey, 0}, - {value::TypeTags::MaxKey, 0}, - {value::TypeTags::RecordId, value::bitcastFrom<int64_t>(8589869056)}}); + auto [ridTag, ridVal] = value::makeNewRecordId(8589869056); + sbe::value::ValueGuard guard{ridTag, ridVal}; + runTest({{value::TypeTags::MinKey, 0}, {value::TypeTags::MaxKey, 0}, {ridTag, ridVal}}); } TEST_F(ValueSerializeForKeyString, BoolNullAndNothing) { diff --git a/src/mongo/db/query/plan_executor_sbe.cpp b/src/mongo/db/query/plan_executor_sbe.cpp index acd7a746988..d15daede90e 100644 --- a/src/mongo/db/query/plan_executor_sbe.cpp +++ b/src/mongo/db/query/plan_executor_sbe.cpp @@ -329,7 +329,9 @@ BSONObj PlanExecutorSBE::getPostBatchResumeToken() const { "but found a result without a valid RecordId: " << msgTag, tag == sbe::value::TypeTags::RecordId); - return BSON("$recordId" << sbe::value::bitcastTo<int64_t>(val)); + BSONObjBuilder builder; + sbe::value::getRecordIdView(val)->serializeToken("$recordId", &builder); + return builder.obj(); } } @@ -383,7 +385,7 @@ sbe::PlanState fetchNext(sbe::PlanStage* root, invariant(recordIdSlot); auto [tag, val] = recordIdSlot->getViewOfValue(); if (tag == sbe::value::TypeTags::RecordId) { - *dlOut = RecordId{sbe::value::bitcastTo<int64_t>(val)}; + *dlOut = *sbe::value::getRecordIdView(val); } } return state; diff --git a/src/mongo/db/query/sbe_stage_builder_coll_scan.cpp b/src/mongo/db/query/sbe_stage_builder_coll_scan.cpp index 1ae5d110a24..d2102cea044 100644 --- a/src/mongo/db/query/sbe_stage_builder_coll_scan.cpp +++ b/src/mongo/db/query/sbe_stage_builder_coll_scan.cpp @@ -268,16 +268,15 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> generateOptimizedOplo auto resumeRecordIdSlot = state.env->getSlot("resumeRecordId"_sd); return {resumeRecordIdSlot, makeVariable(resumeRecordIdSlot)}; } else if (csn->resumeAfterRecordId) { - return { - state.slotId(), - makeConstant(sbe::value::TypeTags::RecordId, csn->resumeAfterRecordId->getLong())}; + auto [tag, val] = sbe::value::makeCopyRecordId(*csn->resumeAfterRecordId); + return {state.slotId(), makeConstant(tag, val)}; } else if (csn->minRecord) { auto cursor = collection->getRecordStore()->getCursor(state.opCtx); auto startRec = cursor->seekNear(*csn->minRecord); if (startRec) { LOGV2_DEBUG(205841, 3, "Using direct oplog seek"); - return {state.slotId(), - makeConstant(sbe::value::TypeTags::RecordId, startRec->id.getLong())}; + auto [tag, val] = sbe::value::makeCopyRecordId(startRec->id); + return {state.slotId(), makeConstant(tag, val)}; } } return {}; @@ -550,9 +549,8 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> generateGenericCollSc auto [seekRecordIdSlot, seekRecordIdExpression] = [&]() -> std::pair<boost::optional<sbe::value::SlotId>, std::unique_ptr<sbe::EExpression>> { if (csn->resumeAfterRecordId) { - return { - state.slotId(), - makeConstant(sbe::value::TypeTags::RecordId, csn->resumeAfterRecordId->getLong())}; + auto [tag, val] = sbe::value::makeCopyRecordId(*csn->resumeAfterRecordId); + return {state.slotId(), makeConstant(tag, val)}; } else if (isTailableResumeBranch) { auto resumeRecordIdSlot = state.env->getSlot("resumeRecordId"_sd); return {resumeRecordIdSlot, makeVariable(resumeRecordIdSlot)}; diff --git a/src/mongo/db/record_id.h b/src/mongo/db/record_id.h index c94e93d5a28..66a58a37a04 100644 --- a/src/mongo/db/record_id.h +++ b/src/mongo/db/record_id.h @@ -287,6 +287,19 @@ public: } /** + * Same as above but in a binary. + */ + void serializeToken(BufBuilder& buf) const { + buf.appendChar(static_cast<char>(_format)); + withFormat([&](Null) {}, + [&](int64_t rid) { buf.appendNum(rid); }, + [&](const char* str, int size) { + buf.appendNum(size); + buf.appendBuf(str, size); + }); + } + + /** * Decode a token created by serializeToken(). */ static RecordId deserializeToken(const BSONElement& elem) { @@ -310,6 +323,26 @@ public: } /** + * Decode a token created by serializeToken(). + */ + static RecordId deserializeToken(BufReader& buf) { + auto format = buf.read<Format>(); + if (format == Format::kNull) { + return RecordId(); + } else if (format == Format::kLong) { + return RecordId(buf.read<LittleEndian<int64_t>>()); + } else if (format == Format::kSmallStr || format == Format::kBigStr) { + const int size = buf.read<LittleEndian<int>>(); + const char* str = static_cast<const char*>(buf.skip(size)); + return RecordId(str, size); + } else { + uasserted(ErrorCodes::BadValue, + fmt::format("Could not deserialize RecordId with type {}", + static_cast<int8_t>(format))); + } + } + + /** * This maximum size for 'small' strings was chosen as a good tradeoff between keeping the * RecordId struct lightweight to copy by value (32 bytes), but also making the struct large * enough to hold a wider variety of strings. Larger strings must be stored in the diff --git a/src/mongo/db/record_id_test.cpp b/src/mongo/db/record_id_test.cpp index e7f0795b6fb..2a0e4360caf 100644 --- a/src/mongo/db/record_id_test.cpp +++ b/src/mongo/db/record_id_test.cpp @@ -248,6 +248,48 @@ TEST(RecordId, RoundTripSerialize) { } } +TEST(RecordId, RoundTripSerializeBinary) { + { + RecordId id(1); + BufBuilder builder; + id.serializeToken(builder); + BufReader reader(builder.buf(), builder.len()); + ASSERT_EQ(id, RecordId::deserializeToken(reader)); + } + + { + RecordId id(4611686018427387904); + BufBuilder builder; + id.serializeToken(builder); + BufReader reader(builder.buf(), builder.len()); + ASSERT_EQ(id, RecordId::deserializeToken(reader)); + } + + { + RecordId id; + BufBuilder builder; + id.serializeToken(builder); + BufReader reader(builder.buf(), builder.len()); + ASSERT_EQ(id, RecordId::deserializeToken(reader)); + } + + { + RecordId id(record_id_helpers::keyForOID(OID::gen())); + BufBuilder builder; + id.serializeToken(builder); + BufReader reader(builder.buf(), builder.len()); + ASSERT_EQ(id, RecordId::deserializeToken(reader)); + } + + { + char buf[1024] = {'x'}; + RecordId id(buf, sizeof(buf)); + BufBuilder builder; + id.serializeToken(builder); + BufReader reader(builder.buf(), builder.len()); + ASSERT_EQ(id, RecordId::deserializeToken(reader)); + } +} TEST(RecordId, RecordIdBigStr) { char buf[1024] = {'x'}; |