This is an automated email from the ASF dual-hosted git repository.

Mryange pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new d3f92ed7be0 [fix](be) Fix BIGINT conv overflow during base conversion 
(#67872)
d3f92ed7be0 is described below

commit d3f92ed7be0ef7e42131d4af365ab8626e9039a4
Author: Mryange <[email protected]>
AuthorDate: Tue Sep 22 15:43:06 2026 +0800

    [fix](be) Fix BIGINT conv overflow during base conversion (#67872)
    
    `conv()` returned incorrect results for BIGINT inputs when converting
    from non-decimal bases because the integer path used 32-bit intermediate
    place values. Use unsigned 64-bit accumulation with pre-operation
    overflow checks and preserve existing invalid-digit and signed-value
    handling.
    ### Release note
    
    None
    
    ### Check List (For Author)
    
    - Test <!-- At least one of them must be included. -->
        - [ ] Regression test
        - [ ] Unit Test
        - [ ] Manual test (add detailed scripts or steps below)
        - [ ] No need to test or manual test. Explain why:
    - [ ] This is a refactor/code format and no logic has been changed.
            - [ ] Previous test can cover this change.
            - [ ] No code files have been changed.
            - [ ] Other reason <!-- Add your reason?  -->
    
    - Behavior changed:
        - [ ] No.
        - [ ] Yes. <!-- Explain the behavior change -->
    
    - Does this need documentation?
        - [ ] No.
    - [ ] Yes. <!-- Add document PR link here. eg:
    https://github.com/apache/doris-website/pull/1214 -->
    
    ### Check List (For Reviewer who merge this PR)
    
    - [ ] Confirm the release note
    - [ ] Confirm test cases
    - [ ] Confirm document
    - [ ] Add branch pick label <!-- Add branch pick label that this PR
    should merge into -->
---
 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]

Reply via email to