This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.1 by this push:
new 0d0e1897b61 branch-4.1: [fix](be) Fix BIGINT conv overflow during base
conversion #67872 (#68424)
0d0e1897b61 is described below
commit 0d0e1897b61c8b690eb2bdd534d4090195ed9aa3
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Sep 24 09:36:33 2026 +0800
branch-4.1: [fix](be) Fix BIGINT conv overflow during base conversion
#67872 (#68424)
Cherry-picked from #67872
Co-authored-by: Mryange <[email protected]>
---
be/src/exprs/math_functions.cpp | 55 +++++++++++++++------------
be/test/exprs/function/function_math_test.cpp | 23 +++++++++++
be/test/exprs/math_functions_test.cpp | 40 +++++++++++++++++++
3 files changed, 93 insertions(+), 25 deletions(-)
diff --git a/be/src/exprs/math_functions.cpp b/be/src/exprs/math_functions.cpp
index af727d8a5ac..b9db9476bf2 100644
--- a/be/src/exprs/math_functions.cpp
+++ b/be/src/exprs/math_functions.cpp
@@ -104,14 +104,10 @@ StringRef MathFunctions::decimal_to_base(FunctionContext*
ctx, int64_t src_num,
char buf[max_digits];
int32_t result_len = 0;
int32_t buf_index = max_digits - 1;
- uint64_t temp_num;
- if (dest_base < 0) {
- // Dest base is negative, treat src_num as signed.
- temp_num = std::abs(src_num);
- } else {
- // Dest base is positive. We must interpret src_num in 2's complement.
- // Convert to an unsigned int to properly deal with 2's complement
conversion.
- temp_num = static_cast<uint64_t>(src_num);
+ auto temp_num = static_cast<uint64_t>(src_num);
+ if (dest_base < 0 && src_num < 0) {
+ // Compute the signed magnitude without overflowing for INT64_MIN.
+ temp_num = uint64_t {0} - temp_num;
}
int abs_base = std::abs(dest_base);
do {
@@ -133,26 +129,35 @@ StringRef MathFunctions::decimal_to_base(FunctionContext*
ctx, int64_t src_num,
}
bool MathFunctions::decimal_in_base_to_decimal(int64_t src_num, int8_t
src_base, int64_t* result) {
- uint64_t temp_num = std::abs(src_num);
- int32_t place = 1;
- *result = 0;
+ auto magnitude = static_cast<uint64_t>(src_num);
+ if (src_num < 0) {
+ magnitude = 0 - magnitude;
+ }
+ uint64_t divisor = 1;
+ uint64_t remaining = magnitude;
+ while (remaining >= 10) {
+ remaining /= 10;
+ divisor *= 10;
+ }
+
+ uint64_t value = 0;
+ constexpr uint64_t max_value = std::numeric_limits<uint64_t>::max();
+ const uint64_t max_div_base = max_value / src_base;
+ const uint64_t max_mod_base = max_value % src_base;
do {
- int32_t digit = temp_num % 10;
- // Reset result if digit is not representable in src_base.
+ const int digit = static_cast<int>(magnitude / divisor);
+ // Keep the prefix preceding the first digit not representable in
src_base.
if (digit >= src_base) {
- *result = 0;
- place = 1;
- } else {
- *result += digit * place;
- place *= src_base;
- // Overflow.
- if (UNLIKELY(*result < digit)) {
- return false;
- }
+ break;
}
- temp_num /= 10;
- } while (temp_num > 0);
- *result = (src_num < 0) ? -(*result) : *result;
+ if (UNLIKELY(value > max_div_base - (digit > max_mod_base))) {
+ return false;
+ }
+ value = value * src_base + digit;
+ magnitude %= divisor;
+ divisor /= 10;
+ } while (divisor > 0);
+ *result = static_cast<int64_t>(src_num < 0 ? 0 - value : value);
return true;
}
diff --git a/be/test/exprs/function/function_math_test.cpp
b/be/test/exprs/function/function_math_test.cpp
index cf1b3a442ea..318ca61dc72 100644
--- a/be/test/exprs/function/function_math_test.cpp
+++ b/be/test/exprs/function/function_math_test.cpp
@@ -636,6 +636,29 @@ TEST(MathFunctionTest, conv_test) {
}
}
+TEST(MathFunctionTest, conv_int64_boundary_test) {
+ InputTypeSet input_types = {PrimitiveType::TYPE_BIGINT,
PrimitiveType::TYPE_TINYINT,
+ PrimitiveType::TYPE_TINYINT};
+ DataSet data_set = {
+ {{BIGINT(8000000000000000LL), TINYINT(16), TINYINT(-10)},
+ VARCHAR("-9223372036854775808")},
+ {{BIGINT(8000000000000000LL), TINYINT(16), TINYINT(10)},
+ VARCHAR("9223372036854775808")},
+ {{BIGINT(std::numeric_limits<int64_t>::min()), TINYINT(10),
TINYINT(-10)},
+ VARCHAR("-9223372036854775808")},
+ {{BIGINT(8000000000000000LL), TINYINT(16), TINYINT(-16)},
VARCHAR("-8000000000000000")},
+ {{BIGINT(10000000000000000LL), TINYINT(16), TINYINT(-10)},
VARCHAR("-1")},
+ {{BIGINT(10000000000000000LL), TINYINT(16), TINYINT(10)},
+ VARCHAR("18446744073709551615")},
+ {{BIGINT(-255), TINYINT(10), TINYINT(-16)}, VARCHAR("-FF")},
+ {{BIGINT(-1), TINYINT(10), TINYINT(16)},
VARCHAR("FFFFFFFFFFFFFFFF")},
+ {{BIGINT(255), TINYINT(10), TINYINT(-16)}, VARCHAR("FF")},
+ {{BIGINT(0), TINYINT(16), TINYINT(-10)}, VARCHAR("0")},
+ {{Null(), TINYINT(16), TINYINT(-10)}, Null()},
+ };
+ check_function_all_arg_comb<DataTypeString, true>("conv", input_types,
data_set);
+}
+
TEST(MathFunctionTest, money_format_test) {
std::string func_name = "money_format";
diff --git a/be/test/exprs/math_functions_test.cpp
b/be/test/exprs/math_functions_test.cpp
index ce0fc635d64..d2d6a9d7c08 100644
--- a/be/test/exprs/math_functions_test.cpp
+++ b/be/test/exprs/math_functions_test.cpp
@@ -25,6 +25,46 @@ namespace doris {
struct MathFunctionsTest : public ::testing::Test {};
+TEST_F(MathFunctionsTest, DecimalInBaseToDecimal) {
+ struct TestCase {
+ int64_t input;
+ int8_t base;
+ uint64_t expected;
+ TestCase(int64_t input_value, int8_t base_value, uint64_t
expected_value)
+ : input(input_value), base(base_value),
expected(expected_value) {}
+ };
+ const TestCase cases[] = {
+ {0, 16, 0},
+ {1111111111, 16, 73300775185ULL},
+ {10000000, 16, 268435456},
+ {80000000, 16, 2147483648ULL},
+ {100000000, 16, 4294967296ULL},
+ {8000000000000000, 16, 1ULL << 63},
+ {std::numeric_limits<int64_t>::min(), 10, 1ULL << 63},
+ {-1111111111, 16, 0ULL - 73300775185ULL},
+ {15, 4, 1},
+ {12345, 4, 27},
+ {5111, 4, 0},
+ // Invalid leading digits produce an empty prefix.
+ {999999999999999999, 9, 0},
+ {std::numeric_limits<int64_t>::max(), 10,
+ static_cast<uint64_t>(std::numeric_limits<int64_t>::max())},
+ };
+ for (const auto& test : cases) {
+ SCOPED_TRACE(test.input);
+ int64_t result = 0;
+ ASSERT_TRUE(MathFunctions::decimal_in_base_to_decimal(test.input,
test.base, &result));
+ EXPECT_EQ(test.expected, static_cast<uint64_t>(result));
+ }
+}
+
+TEST_F(MathFunctionsTest, DecimalInBaseToDecimalOverflow) {
+ int64_t result = 0;
+ EXPECT_FALSE(MathFunctions::decimal_in_base_to_decimal(10000000000000000,
16, &result));
+ EXPECT_FALSE(MathFunctions::decimal_in_base_to_decimal(-10000000000000000,
16, &result));
+ EXPECT_FALSE(MathFunctions::decimal_in_base_to_decimal(999999999999999999,
36, &result));
+}
+
// Regular rounding test (truncate = false)
TEST_F(MathFunctionsTest, DoubleRoundBasic) {
// Positive number rounding
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]