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 91e9e8671a6 [fix](function) Preserve NULL values in parse_data_size 
(#67910)
91e9e8671a6 is described below

commit 91e9e8671a63c69da2e348577ba6e3b377f622c1
Author: Mryange <[email protected]>
AuthorDate: Mon Sep 14 11:05:32 2026 +0800

    [fix](function) Preserve NULL values in parse_data_size (#67910)
    
    `parse_data_size()` fails on string columns containing both valid values
    and NULL. Root cause: the default nullable wrapper passes NULL payloads
    to the parser as empty strings. Use `ColumnView` to skip NULL rows and
    preserve nullability. Invalid non-NULL inputs still report errors.
    
    ### 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/function/function_string.cpp          | 55 +++++++++++++++-------
 be/test/exprs/function/function_string_test.cpp    | 24 ++++++++++
 .../test_parse_data_size_nullable.out              | 24 ++++++++++
 .../test_parse_data_size_nullable.groovy           | 50 ++++++++++++++++++++
 4 files changed, 135 insertions(+), 18 deletions(-)

diff --git a/be/src/exprs/function/function_string.cpp 
b/be/src/exprs/function/function_string.cpp
index 422f0b17560..d62573112bb 100644
--- a/be/src/exprs/function/function_string.cpp
+++ b/be/src/exprs/function/function_string.cpp
@@ -38,6 +38,7 @@
 #include "common/logging.h"
 #include "common/status.h"
 #include "core/column/column.h"
+#include "core/column/column_execute_util.h"
 #include "core/column/column_string.h"
 #include "core/data_type/data_type_nullable.h"
 #include "core/pod_array_fwd.h"
@@ -78,10 +79,6 @@ struct StringASCII {
     }
 };
 
-struct NameParseDataSize {
-    static constexpr auto name = "parse_data_size";
-};
-
 static const std::map<std::string_view, Int128> UNITS = {
         {"B", static_cast<Int128>(1)},        {"kB", static_cast<Int128>(1) << 
10},
         {"MB", static_cast<Int128>(1) << 20}, {"GB", static_cast<Int128>(1) << 
30},
@@ -89,24 +86,47 @@ static const std::map<std::string_view, Int128> UNITS = {
         {"EB", static_cast<Int128>(1) << 60}, {"ZB", static_cast<Int128>(1) << 
70},
         {"YB", static_cast<Int128>(1) << 80}};
 
-struct ParseDataSize {
-    using ReturnType = DataTypeInt128;
-    static constexpr auto PrimitiveTypeImpl = PrimitiveType::TYPE_STRING;
-    using Type = String;
-    using ReturnColumnType = ColumnInt128;
+class FunctionStringParseDataSize : public IFunction {
+public:
+    static constexpr auto name = "parse_data_size";
+    static FunctionPtr create() { return 
std::make_shared<FunctionStringParseDataSize>(); }
+    String get_name() const override { return name; }
+    size_t get_number_of_arguments() const override { return 1; }
+    bool use_default_implementation_for_nulls() const override { return false; 
}
 
-    static Status vector(const ColumnString::Chars& data, const 
ColumnString::Offsets& offsets,
-                         PaddedPODArray<Int128>& res) {
-        auto size = offsets.size();
-        res.resize(size);
-        for (int i = 0; i < size; ++i) {
-            const char* raw_str = reinterpret_cast<const 
char*>(&data[offsets[i - 1]]);
-            int str_size = offsets[i] - offsets[i - 1];
-            res[i] = parse_data_size(std::string_view(raw_str, str_size));
+    DataTypePtr get_return_type_impl(const DataTypes& arguments) const 
override {
+        auto type = std::make_shared<DataTypeInt128>();
+        return arguments[0]->is_nullable() ? make_nullable(type) : type;
+    }
+
+    Status execute_impl(FunctionContext* context, Block& block, const 
ColumnNumbers& arguments,
+                        uint32_t result, size_t input_rows_count) const 
override {
+        const auto input =
+                
ColumnView<TYPE_STRING>::create(block.get_by_position(arguments[0]).column);
+        auto res = ColumnInt128::create(input_rows_count, 0);
+        auto& values = res->get_data();
+        ColumnUInt8::MutablePtr null_map;
+        if (input.null_map != nullptr) {
+            null_map = ColumnUInt8::create(input_rows_count, 0);
+        }
+        for (size_t i = 0; i < input_rows_count; ++i) {
+            if (input.is_null_at(i)) {
+                null_map->get_data()[i] = 1;
+                continue;
+            }
+            const auto value = input.value_at(i);
+            values[i] = parse_data_size(std::string_view(value.data, 
value.size));
+        }
+        if (null_map) {
+            block.replace_by_position(result,
+                                      ColumnNullable::create(std::move(res), 
std::move(null_map)));
+        } else {
+            block.replace_by_position(result, std::move(res));
         }
         return Status::OK();
     }
 
+private:
     static Int128 parse_data_size(const std::string_view& dataSize) {
         int digit_length = 0;
         for (char c : dataSize) {
@@ -1349,7 +1369,6 @@ template <typename LeftDataType, typename RightDataType>
 using StringFindInSetImpl = StringFunctionImpl<LeftDataType, RightDataType, 
FindInSetOp>;
 
 // ready for regist function
-using FunctionStringParseDataSize = FunctionUnaryToType<ParseDataSize, 
NameParseDataSize>;
 using FunctionStringASCII = FunctionUnaryToType<StringASCII, NameStringASCII>;
 using FunctionStringLength = FunctionUnaryToType<StringLengthImpl, 
NameStringLength>;
 using FunctionCrc32 = FunctionUnaryToType<Crc32Impl, NameCrc32>;
diff --git a/be/test/exprs/function/function_string_test.cpp 
b/be/test/exprs/function/function_string_test.cpp
index c9c818d2d59..f20fd3f7233 100644
--- a/be/test/exprs/function/function_string_test.cpp
+++ b/be/test/exprs/function/function_string_test.cpp
@@ -81,6 +81,30 @@ DataSet make_md5_varbinary_dataset(const 
std::vector<std::string>& inputs) {
 
 } // namespace
 
+TEST(function_string_test, parse_data_size_nullable) {
+    const InputTypeSet input_types = {PrimitiveType::TYPE_STRING};
+    const DataSet data_set = {{{Null()}, Null()},
+                              {{std::string("1MB")}, LARGEINT(1048576)},
+                              {{Null()}, Null()},
+                              {{std::string("2.5MB")}, LARGEINT(2621440)},
+                              {{std::string("0B")}, LARGEINT(0)},
+                              {{Null()}, Null()}};
+    check_function_all_arg_comb<DataTypeInt128, true>("parse_data_size", 
input_types, data_set);
+    check_function_all_arg_comb<DataTypeInt128, true>("parse_data_size", 
input_types,
+                                                      {{{Null()}, Null()}, 
{{Null()}, Null()}});
+
+    const InputTypeSet not_null_types = {Notnull {PrimitiveType::TYPE_STRING}};
+    const DataSet not_null_data = {{{std::string("1MB")}, LARGEINT(1048576)},
+                                   {{std::string("0B")}, LARGEINT(0)}};
+    ASSERT_TRUE(
+            check_function<DataTypeInt128>("parse_data_size", not_null_types, 
not_null_data).ok());
+    const InputTypeSet const_not_null_types = {ConstedNotnull 
{PrimitiveType::TYPE_STRING}};
+    for (const auto& row : not_null_data) {
+        ASSERT_TRUE(check_function<DataTypeInt128>("parse_data_size", 
const_not_null_types, {row})
+                            .ok());
+    }
+}
+
 TEST(function_string_test, function_auto_partition_name_case_insensitive_test) 
{
     const InputTypeSet list_input_types = {Consted 
{PrimitiveType::TYPE_VARCHAR},
                                            Consted 
{PrimitiveType::TYPE_VARCHAR}};
diff --git 
a/regression-test/data/query_p0/sql_functions/string_functions/test_parse_data_size_nullable.out
 
b/regression-test/data/query_p0/sql_functions/string_functions/test_parse_data_size_nullable.out
new file mode 100644
index 00000000000..8eccd452d89
--- /dev/null
+++ 
b/regression-test/data/query_p0/sql_functions/string_functions/test_parse_data_size_nullable.out
@@ -0,0 +1,24 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !mixed --
+1      \N
+2      1048576
+3      \N
+4      2621440
+5      0
+6      \N
+
+-- !all_null --
+1      \N
+3      \N
+6      \N
+
+-- !non_null --
+2      1048576
+4      2621440
+5      0
+
+-- !constants --
+\N     1048576
+
+-- !empty --
+
diff --git 
a/regression-test/suites/query_p0/sql_functions/string_functions/test_parse_data_size_nullable.groovy
 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_parse_data_size_nullable.groovy
new file mode 100644
index 00000000000..0223136fe13
--- /dev/null
+++ 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_parse_data_size_nullable.groovy
@@ -0,0 +1,50 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_parse_data_size_nullable") {
+    sql "DROP TABLE IF EXISTS test_parse_data_size_nullable"
+    sql """
+        CREATE TABLE test_parse_data_size_nullable (
+            id INT NOT NULL,
+            value STRING NULL
+        ) DUPLICATE KEY(id)
+        DISTRIBUTED BY HASH(id) BUCKETS 1
+        PROPERTIES ("replication_num" = "1")
+    """
+    sql """INSERT INTO test_parse_data_size_nullable VALUES
+        (1, NULL), (2, '1MB'), (3, NULL), (4, '2.5MB'), (5, '0B'), (6, NULL)"""
+
+    qt_mixed "SELECT id, parse_data_size(value) FROM 
test_parse_data_size_nullable ORDER BY id"
+    qt_all_null """SELECT id, parse_data_size(value) FROM 
test_parse_data_size_nullable
+        WHERE value IS NULL ORDER BY id"""
+    qt_non_null """SELECT id, parse_data_size(value) FROM 
test_parse_data_size_nullable
+        WHERE value IS NOT NULL ORDER BY id"""
+    qt_constants "SELECT parse_data_size(NULL), parse_data_size('1MB')"
+    qt_empty "SELECT parse_data_size(value) FROM test_parse_data_size_nullable 
WHERE id < 0"
+
+    sql "INSERT INTO test_parse_data_size_nullable VALUES (7, '')"
+    test {
+        sql "SELECT parse_data_size(value) FROM test_parse_data_size_nullable 
ORDER BY id"
+        exception 'Invalid Input argument "" of function parse_data_size'
+    }
+    sql "INSERT INTO test_parse_data_size_nullable VALUES (8, 'invalid')"
+    test {
+        sql """SELECT parse_data_size(value) FROM test_parse_data_size_nullable
+            WHERE id IN (1, 8) ORDER BY id"""
+        exception 'Invalid Input argument "invalid" of function 
parse_data_size'
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to