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

yiguolei 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 a5aa078fefa [fix](be) Restore null literal return type handling 
(#66280)
a5aa078fefa is described below

commit a5aa078fefa4ae2c4cff24d3f38d012b163ac400
Author: Jerry Hu <[email protected]>
AuthorDate: Wed Aug 5 09:07:00 2026 +0800

    [fix](be) Restore null literal return type handling (#66280)
    
    ### What problem does this PR solve?
    
    Issue Number: None
    
    Related PR: None
    
    Problem Summary:
    
    When constant folding is skipped, an untyped `NULL` argument reaches the
    backend as a nullable `UInt8` placeholder marked as a null literal.
    `FunctionBuilderImpl` stripped the nullable wrapper and invoked
    function-specific return type inference, so functions such as
    `array_zip` could interpret that placeholder as an array and dereference
    a null type pointer during prepare.
    
    Restore the generic `Nullable(Nothing)` short circuit for null literals
    before function-specific return type inference. The sentinel lets the
    builder use the FE-planned nullable result type, while the existing
    default NULL execution path produces the typed NULL result. Add focused
    builder unit coverage and SQL regression coverage for folded and
    non-folded NULL arguments.
    
    The added argument scan runs only while building a function and
    introduces no per-row execution cost. Functions with custom NULL
    handling, typed NULL arguments, ordinary nullable columns, and non-NULL
    inputs keep their existing paths.
    
    ### Release note
    
    Fix a backend crash when `array_zip` receives an untyped `NULL`
    argument.
    
    ### Check List (For Author)
    
    - Test
        - [x] Regression test
    - Generated and reran `test_array_zip_array_enumerate_uniq` with an
    isolated regression configuration.
        - [x] Unit Test
    - `./run-be-ut.sh --run
    
--filter=SimpleFunctionFactoryTest.test_null_literal_skips_return_type_inference`
        - [x] Build
            - `BUILD_TYPE=ASAN ./build.sh --be --fe`
        - [x] Static/style checks
            - `./build-support/clang-format.sh`
            - `./build-support/check-format.sh`
    - `./build-support/run-clang-tidy.sh --build-dir be/ut_build_ASAN`
            - `git diff --check`
        - [ ] 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
    
    - Behavior changed:
        - [ ] No.
    - [x] Yes. `array_zip` with an untyped `NULL` now returns `NULL` instead
    of crashing the backend when constant folding is skipped.
    
    - Does this need documentation?
        - [x] No.
        - [ ] Yes.
---
 be/src/exprs/function/function.cpp                 |  5 +++
 .../function/simple_function_factory_test.cpp      | 40 +++++++++++++++++++++-
 .../test_array_zip_array_enumerate_uniq.out        | 15 ++++++++
 .../test_array_zip_array_enumerate_uniq.groovy     | 20 +++++++++++
 4 files changed, 79 insertions(+), 1 deletion(-)

diff --git a/be/src/exprs/function/function.cpp 
b/be/src/exprs/function/function.cpp
index 4f8c4e25059..c02ef344f6b 100644
--- a/be/src/exprs/function/function.cpp
+++ b/be/src/exprs/function/function.cpp
@@ -296,6 +296,11 @@ DataTypePtr FunctionBuilderImpl::get_return_type(const 
ColumnsWithTypeAndName& a
     check_number_of_arguments(arguments.size());
 
     if (!arguments.empty() && use_default_implementation_for_nulls()) {
+        if (std::ranges::any_of(arguments, [](const auto& argument) {
+                return argument.type->is_null_literal();
+            })) {
+            return make_nullable(std::make_shared<DataTypeNothing>());
+        }
         if (have_null_column(arguments)) {
             ColumnNumbers numbers(arguments.size());
             std::iota(numbers.begin(), numbers.end(), 0);
diff --git a/be/test/exprs/function/simple_function_factory_test.cpp 
b/be/test/exprs/function/simple_function_factory_test.cpp
index dc8dcdf8583..ab2dbb018fd 100644
--- a/be/test/exprs/function/simple_function_factory_test.cpp
+++ b/be/test/exprs/function/simple_function_factory_test.cpp
@@ -25,6 +25,7 @@
 #include <vector>
 
 #include "core/data_type/data_type_bitmap.h"
+#include "core/data_type/data_type_factory.hpp"
 #include "core/data_type/data_type_nullable.h"
 #include "core/data_type/data_type_number.h"
 
@@ -48,11 +49,32 @@ public:
     }
 };
 
+class FunctionNullLiteralBeTestMock : public IFunction {
+public:
+    static constexpr auto name = "null_literal_be_test_mock";
+
+    static FunctionPtr create() { return 
std::make_shared<FunctionNullLiteralBeTestMock>(); }
+
+    String get_name() const override { return name; }
+
+    size_t get_number_of_arguments() const override { return 1; }
+
+    DataTypePtr get_return_type_impl(const DataTypes&) const override {
+        return std::make_shared<DataTypeInt64>();
+    }
+
+    Status execute_impl(FunctionContext*, Block&, const ColumnNumbers&, 
uint32_t,
+                        size_t) const override {
+        return Status::OK();
+    }
+};
+
 class SimpleFunctionFactoryTest : public testing::Test {
     void SetUp() override {
         static std::once_flag oc;
         std::call_once(oc, []() {
             
SimpleFunctionFactory::instance().register_function<FunctionBeTestMock>();
+            
SimpleFunctionFactory::instance().register_function<FunctionNullLiteralBeTestMock>();
         });
     }
 
@@ -74,6 +96,22 @@ TEST_F(SimpleFunctionFactoryTest, test_return_type_check) {
                  doris::Exception);
 }
 
+TEST_F(SimpleFunctionFactoryTest, 
test_null_literal_skips_return_type_inference) {
+    auto null_literal_type =
+            
DataTypeFactory::instance().create_data_type(PrimitiveType::TYPE_NULL, true);
+    ASSERT_TRUE(null_literal_type->is_nullable());
+    ASSERT_TRUE(null_literal_type->is_null_literal());
+
+    ColumnsWithTypeAndName arguments = {{nullptr, null_literal_type, "null"}};
+    // The mock infers BIGINT, but a NULL literal should let the FE-provided 
nullable type win.
+    auto expected_return_type = 
make_nullable(std::make_shared<DataTypeInt32>());
+    FunctionBasePtr function;
+    ASSERT_NO_THROW(function = SimpleFunctionFactory::instance().get_function(
+                            FunctionNullLiteralBeTestMock::name, arguments, 
expected_return_type));
+    ASSERT_NE(function, nullptr);
+    EXPECT_TRUE(function->get_return_type()->equals(*expected_return_type));
+}
+
 TEST_F(SimpleFunctionFactoryTest, test_return_all) {
     auto factory = SimpleFunctionFactory::instance();
 
@@ -130,4 +168,4 @@ TEST_F(SimpleFunctionFactoryTest, 
test_bitmap_count_new_version_return_type) {
     }
 }
 
-} // namespace doris
\ No newline at end of file
+} // namespace doris
diff --git 
a/regression-test/data/datatype_p0/nested_types/query/array_functions/test_array_zip_array_enumerate_uniq.out
 
b/regression-test/data/datatype_p0/nested_types/query/array_functions/test_array_zip_array_enumerate_uniq.out
index 069148cd3f4..0ea4aa18c82 100644
--- 
a/regression-test/data/datatype_p0/nested_types/query/array_functions/test_array_zip_array_enumerate_uniq.out
+++ 
b/regression-test/data/datatype_p0/nested_types/query/array_functions/test_array_zip_array_enumerate_uniq.out
@@ -1,4 +1,19 @@
 -- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !array_zip_folded_null --
+\N
+
+-- !array_zip_null_first --
+\N
+
+-- !array_zip_null_middle --
+\N
+
+-- !array_zip_null_last --
+\N
+
+-- !array_zip_typed_null --
+\N
+
 -- !sql --
 array_enumerate_uniq
 
diff --git 
a/regression-test/suites/datatype_p0/nested_types/query/array_functions/test_array_zip_array_enumerate_uniq.groovy
 
b/regression-test/suites/datatype_p0/nested_types/query/array_functions/test_array_zip_array_enumerate_uniq.groovy
index 82ed0c8a73c..205748653e6 100644
--- 
a/regression-test/suites/datatype_p0/nested_types/query/array_functions/test_array_zip_array_enumerate_uniq.groovy
+++ 
b/regression-test/suites/datatype_p0/nested_types/query/array_functions/test_array_zip_array_enumerate_uniq.groovy
@@ -17,6 +17,26 @@
 
 suite("test_array_zip_array_enumerate_uniq", "p0") {
 //     ========== array-zip ==========
+    sql "SET debug_skip_fold_constant = false"
+    order_qt_array_zip_folded_null """
+        SELECT array_zip([1.1, 2.2, 3.3], [1, 2, 3], NULL)
+    """
+
+    sql "SET debug_skip_fold_constant = true"
+    order_qt_array_zip_null_first """
+        SELECT array_zip(NULL, [1, 2, 3], ['a', 'b', 'c'])
+    """
+    order_qt_array_zip_null_middle """
+        SELECT array_zip([1.1, 2.2, 3.3], NULL, [1, 2, 3])
+    """
+    order_qt_array_zip_null_last """
+        SELECT array_zip([1.1, 2.2, 3.3], [1, 2, 3], NULL)
+    """
+    order_qt_array_zip_typed_null """
+        SELECT array_zip([1.1, 2.2, 3.3], CAST(NULL AS ARRAY<INT>), [1, 2, 3])
+    """
+    sql "SET debug_skip_fold_constant = false"
+
 //     wrong case
     test {
         sql """ SELECT array_zip() """


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

Reply via email to