SERVER-126887 Add numeric cast overload that accepts TagValueView in SBE (#54394)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: evanbergeron <6565683+evanbergeron@users.noreply.github.com>
GitOrigin-RevId: a546de94771b8c04746c7083daf78aa64280cdbe
This commit is contained in:
Evan Bergeron 2026-05-26 15:51:17 -04:00 committed by MongoDB Bot
parent 67c6620a03
commit 8f8e471e1a
9 changed files with 62 additions and 39 deletions

View File

@ -2465,6 +2465,11 @@ inline T numericCast(TypeTags tag, Value val) noexcept {
}
}
template <typename T>
inline T numericCast(TagValueView v) noexcept {
return numericCast<T>(v.tag, v.value);
}
/**
* Performs a lossless numeric conversion from a value to a destination type denoted by the target
* TypeTag. In the case that a conversion is lossy, we return Nothing.

View File

@ -530,4 +530,25 @@ TEST_F(SbeValueTest, SortSpecCompareInvalid) {
ASSERT_EQ(cmpTag, value::TypeTags::Nothing);
ASSERT_EQ(cmpVal, 0);
}
TEST(SbeNumericCastTest, TagValueViewOverload) {
using namespace value;
TagValueView v32{TypeTags::NumberInt32, bitcastFrom<int32_t>(7)};
ASSERT_EQ(numericCast<int32_t>(v32), 7);
ASSERT_EQ(numericCast<int64_t>(v32), int64_t{7});
ASSERT_EQ(numericCast<double>(v32), 7.0);
ASSERT_EQ(numericCast<Decimal128>(v32), Decimal128(7));
TagValueView v64{TypeTags::NumberInt64, bitcastFrom<int64_t>(100LL)};
ASSERT_EQ(numericCast<int64_t>(v64), 100LL);
ASSERT_EQ(numericCast<double>(v64), 100.0);
TagValueView vd{TypeTags::NumberDouble, bitcastFrom<double>(2.5)};
ASSERT_EQ(numericCast<double>(vd), 2.5);
auto [decTag, decVal] = makeCopyDecimal(Decimal128("3.14"));
ValueGuard guard{decTag, decVal};
TagValueView vDec{decTag, decVal};
ASSERT_EQ(numericCast<Decimal128>(vDec), Decimal128("3.14"));
}
} // namespace mongo::sbe

View File

@ -1072,12 +1072,12 @@ value::TagValueMaybeOwned ByteCode::genericPow(value::TagValueView base,
if (base.tag == value::TypeTags::NumberDecimal ||
exponent.tag == value::TypeTags::NumberDecimal) {
auto baseDecimal = numericCast<Decimal128>(base.tag, base.value);
auto exponenetDecimal = numericCast<Decimal128>(exponent.tag, exponent.value);
if (baseDecimal == Decimal128("0") && exponenetDecimal < Decimal128("0")) {
auto baseDecimal = numericCast<Decimal128>(base);
auto exponentDecimal = numericCast<Decimal128>(exponent);
if (baseDecimal == Decimal128("0") && exponentDecimal < Decimal128("0")) {
return {false, value::TypeTags::Nothing, 0};
}
auto result = baseDecimal.power(exponenetDecimal);
auto result = baseDecimal.power(exponentDecimal);
auto [resTag, resValue] = value::makeCopyDecimal(result);
return {true, resTag, resValue};
}
@ -1085,8 +1085,8 @@ value::TagValueMaybeOwned ByteCode::genericPow(value::TagValueView base,
// If either argument is a double, return a double.
if (base.tag == value::TypeTags::NumberDouble ||
exponent.tag == value::TypeTags::NumberDouble) {
auto baseDouble = numericCast<double>(base.tag, base.value);
auto exponentDouble = numericCast<double>(exponent.tag, exponent.value);
auto baseDouble = numericCast<double>(base);
auto exponentDouble = numericCast<double>(exponent);
if (baseDouble == 0 && exponentDouble < 0) {
return {false, value::TypeTags::Nothing, 0};
}
@ -1221,13 +1221,13 @@ value::TagValueMaybeOwned ByteCode::genericAtan2(value::TagValueView operand1,
case value::TypeTags::NumberInt32:
case value::TypeTags::NumberInt64:
case value::TypeTags::NumberDouble: {
auto result = std::atan2(numericCast<double>(operand1.tag, operand1.value),
numericCast<double>(operand2.tag, operand2.value));
auto result =
std::atan2(numericCast<double>(operand1), numericCast<double>(operand2));
return {false, value::TypeTags::NumberDouble, value::bitcastFrom<double>(result)};
}
case value::TypeTags::NumberDecimal: {
auto result = numericCast<Decimal128>(operand1.tag, operand1.value)
.atan2(numericCast<Decimal128>(operand2.tag, operand2.value));
auto result =
numericCast<Decimal128>(operand1).atan2(numericCast<Decimal128>(operand2));
auto [resTag, resValue] = value::makeCopyDecimal(result);
return {true, resTag, resValue};
}
@ -1252,12 +1252,11 @@ value::TagValueMaybeOwned ByteCode::genericDegreesToRadians(value::TagValueView
case value::TypeTags::NumberInt32:
case value::TypeTags::NumberInt64:
case value::TypeTags::NumberDouble: {
auto result = numericCast<double>(operand.tag, operand.value) * kDoublePiOver180;
auto result = numericCast<double>(operand) * kDoublePiOver180;
return {false, value::TypeTags::NumberDouble, value::bitcastFrom<double>(result)};
}
case value::TypeTags::NumberDecimal: {
auto result = numericCast<Decimal128>(operand.tag, operand.value)
.multiply(Decimal128::kPiOver180);
auto result = numericCast<Decimal128>(operand).multiply(Decimal128::kPiOver180);
auto [resTag, resValue] = value::makeCopyDecimal(result);
return {true, resTag, resValue};
}
@ -1274,12 +1273,11 @@ value::TagValueMaybeOwned ByteCode::genericRadiansToDegrees(value::TagValueView
case value::TypeTags::NumberInt32:
case value::TypeTags::NumberInt64:
case value::TypeTags::NumberDouble: {
auto result = numericCast<double>(operand.tag, operand.value) * kDouble180OverPi;
auto result = numericCast<double>(operand) * kDouble180OverPi;
return {false, value::TypeTags::NumberDouble, value::bitcastFrom<double>(result)};
}
case value::TypeTags::NumberDecimal: {
auto result = numericCast<Decimal128>(operand.tag, operand.value)
.multiply(Decimal128::k180OverPi);
auto result = numericCast<Decimal128>(operand).multiply(Decimal128::k180OverPi);
auto [resTag, resValue] = value::makeCopyDecimal(result);
return {true, resTag, resValue};
}

View File

@ -1268,7 +1268,7 @@ value::TagValueMaybeOwned ByteCode::builtinAggExpMovingAvg(ArityType arity) {
auto currentResultTagVal = state->getAt(static_cast<size_t>(AggExpMovingAvgElems::kResult));
auto decimalVal = value::numericCast<Decimal128>(field.tag, field.value);
auto decimalVal = value::numericCast<Decimal128>(field);
auto result = [&]() {
if (currentResultTagVal.tag == value::TypeTags::Null) {
// Accumulator result has not been yet initialised. We will now

View File

@ -291,7 +291,7 @@ value::TagValueMaybeOwned ByteCode::builtinDoubleDoubleSum(ArityType arity) {
if (arg.tag == value::TypeTags::Date) {
sum = sum.add(Decimal128(value::bitcastTo<int64_t>(arg.value)));
} else {
sum = sum.add(value::numericCast<Decimal128>(arg.tag, arg.value));
sum = sum.add(value::numericCast<Decimal128>(arg));
}
}
if (haveDate) {
@ -305,11 +305,11 @@ value::TagValueMaybeOwned ByteCode::builtinDoubleDoubleSum(ArityType arity) {
for (ArityType idx = 0; idx < arity; ++idx) {
auto arg = viewFromStack(idx);
if (arg.tag == value::TypeTags::NumberInt32) {
sum.addInt(value::numericCast<int32_t>(arg.tag, arg.value));
sum.addInt(value::numericCast<int32_t>(arg));
} else if (arg.tag == value::TypeTags::NumberInt64) {
sum.addLong(value::numericCast<int64_t>(arg.tag, arg.value));
sum.addLong(value::numericCast<int64_t>(arg));
} else if (arg.tag == value::TypeTags::NumberDouble) {
sum.addDouble(value::numericCast<double>(arg.tag, arg.value));
sum.addDouble(value::numericCast<double>(arg));
} else if (arg.tag == value::TypeTags::Date) {
sum.addLong(value::bitcastTo<int64_t>(arg.value));
}

View File

@ -84,9 +84,9 @@ value::TagValueMaybeOwned ByteCode::builtinNewArrayFromRange(ArityType arity) {
}
// Cast to broader type 'int64_t' to prevent overflow during loop.
auto startVal = value::numericCast<int64_t>(startView.tag, startView.value);
auto endVal = value::numericCast<int64_t>(endView.tag, endView.value);
auto stepVal = value::numericCast<int64_t>(stepView.tag, stepView.value);
auto startVal = value::numericCast<int64_t>(startView);
auto endVal = value::numericCast<int64_t>(endView);
auto stepVal = value::numericCast<int64_t>(stepView);
if (stepVal == 0) {
return {false, value::TypeTags::Nothing, 0};

View File

@ -107,8 +107,8 @@ value::TagValueMaybeOwned ByteCode::builtinBitTestZero(ArityType arity) {
return {false, value::TypeTags::Nothing, 0};
}
auto maskNum = value::numericCast<int64_t>(mask.tag, mask.value);
auto inputNum = value::numericCast<int64_t>(input.tag, input.value);
auto maskNum = value::numericCast<int64_t>(mask);
auto inputNum = value::numericCast<int64_t>(input);
auto result = (maskNum & inputNum) == 0;
return {false, value::TypeTags::Boolean, value::bitcastFrom<bool>(result)};
}
@ -123,8 +123,8 @@ value::TagValueMaybeOwned ByteCode::builtinBitTestMask(ArityType arity) {
return {false, value::TypeTags::Nothing, 0};
}
auto maskNum = value::numericCast<int64_t>(mask.tag, mask.value);
auto inputNum = value::numericCast<int64_t>(input.tag, input.value);
auto maskNum = value::numericCast<int64_t>(mask);
auto inputNum = value::numericCast<int64_t>(input);
auto result = (maskNum & inputNum) == maskNum;
return {false, value::TypeTags::Boolean, value::bitcastFrom<bool>(result)};
}

View File

@ -80,13 +80,13 @@ value::TagValueMaybeOwned builtinDateHelper(DateFn computeDateFn,
const auto tz = tzString == "" ? timeZoneDB->utcZone() : timeZoneDB->getTimeZone(tzString);
auto date = computeDateFn(tz,
value::numericCast<int64_t>(yearOrWeekYear.tag, yearOrWeekYear.value),
value::numericCast<int64_t>(monthOrWeek.tag, monthOrWeek.value),
value::numericCast<int64_t>(day.tag, day.value),
value::numericCast<int64_t>(hour.tag, hour.value),
value::numericCast<int64_t>(minute.tag, minute.value),
value::numericCast<int64_t>(second.tag, second.value),
value::numericCast<int64_t>(millisecond.tag, millisecond.value));
value::numericCast<int64_t>(yearOrWeekYear),
value::numericCast<int64_t>(monthOrWeek),
value::numericCast<int64_t>(day),
value::numericCast<int64_t>(hour),
value::numericCast<int64_t>(minute),
value::numericCast<int64_t>(second),
value::numericCast<int64_t>(millisecond));
return {false, value::TypeTags::Date, value::bitcastFrom<int64_t>(date.asInt64())};
}

View File

@ -49,9 +49,8 @@ value::TagValueMaybeOwned ByteCode::genericNewKeyString(ArityType arity,
return {false, value::TypeTags::Nothing, 0};
}
auto version = value::numericCast<int64_t>(versionView.tag, versionView.value);
auto discriminator =
value::numericCast<int64_t>(discriminatorView.tag, discriminatorView.value);
auto version = value::numericCast<int64_t>(versionView);
auto discriminator = value::numericCast<int64_t>(discriminatorView);
if ((version < 0 || version > 1) || (discriminator < 0 || discriminator > 2)) {
return {false, value::TypeTags::Nothing, 0};
}
@ -59,7 +58,7 @@ value::TagValueMaybeOwned ByteCode::genericNewKeyString(ArityType arity,
auto ksVersion = static_cast<key_string::Version>(version);
auto ksDiscriminator = static_cast<key_string::Discriminator>(discriminator);
uint32_t orderingBits = value::numericCast<int32_t>(orderingView.tag, orderingView.value);
uint32_t orderingBits = value::numericCast<int32_t>(orderingView);
// Maximum number of orderings. An 'Ordering' cannot have more than 32 values at the moment.
// Limit the usage to 32 bytes here anyway, because if that definition ever changes, the amount