This is an automated email from the ASF dual-hosted git repository.
mrhhsg 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 a58bf4059a8 branch-4.1: [fix](be) Restore null literal return type
handling #66280 (#67189)
a58bf4059a8 is described below
commit a58bf4059a8e12b3d50c8ac8c8a0328a02402b7c
Author: Jerry Hu <[email protected]>
AuthorDate: Fri Aug 28 17:55:20 2026 +0800
branch-4.1: [fix](be) Restore null literal return type handling #66280
(#67189)
### What problem does this PR solve?
Cherry-pick #66280 to branch-4.1.
Issue Number: None
Related PR: #66280
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.
Conflict resolution note: the only conflict was in
`be/test/exprs/function/simple_function_factory_test.cpp`, because
branch-4.1 does not contain the
`test_bitmap_count_new_version_return_type` test (and its
`data_type_bitmap.h` include) from another master PR. Those unrelated
parts were dropped; the rest of the change is identical to the master
commit.
### Release note
Fix a backend crash when `array_zip` receives an untyped `NULL`
argument.
### Check List (For Author)
- Test
- [x] Regression test
- `test_array_zip_array_enumerate_uniq` (cases added in #66280)
- [x] Unit Test
- `./run-be-ut.sh --run --filter=SimpleFunctionFactoryTest.*`
- [x] Build
- [x] Static/style checks
- `clang-format` on changed files, `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 | 41 +++++++++++++++++++++-
.../test_array_zip_array_enumerate_uniq.out | 15 ++++++++
.../test_array_zip_array_enumerate_uniq.groovy | 20 +++++++++++
4 files changed, 80 insertions(+), 1 deletion(-)
diff --git a/be/src/exprs/function/function.cpp
b/be/src/exprs/function/function.cpp
index 552fafdb459..a1834bb3e90 100644
--- a/be/src/exprs/function/function.cpp
+++ b/be/src/exprs/function/function.cpp
@@ -297,6 +297,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 86f344a3f03..09308cbf12d 100644
--- a/be/test/exprs/function/simple_function_factory_test.cpp
+++ b/be/test/exprs/function/simple_function_factory_test.cpp
@@ -21,6 +21,8 @@
#include <memory>
+#include "core/data_type/data_type_factory.hpp"
+#include "core/data_type/data_type_nullable.h"
#include "core/data_type/data_type_number.h"
namespace doris {
@@ -43,11 +45,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>();
});
}
@@ -69,6 +92,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();
@@ -95,4 +134,4 @@ TEST_F(SimpleFunctionFactoryTest, test_return_all) {
}
}
-} // 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]