From 5e9654aba7a366422c9a8c56e0ac5d461b7033e9 Mon Sep 17 00:00:00 2001 From: Mryange Date: Tue, 22 Sep 2026 17:14:15 +0800 Subject: [PATCH 1/2] [fix](expr) enforce nullable column output matching --- be/src/exprs/vexpr.cpp | 9 --------- be/test/exprs/vexpr_test.cpp | 7 +++---- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/be/src/exprs/vexpr.cpp b/be/src/exprs/vexpr.cpp index 557d04a4e1f522..3c06f3bbdee89f 100644 --- a/be/src/exprs/vexpr.cpp +++ b/be/src/exprs/vexpr.cpp @@ -1075,15 +1075,6 @@ Status VExpr::execute_column(VExprContext* context, const Block* block, const Se auto result_type = execute_type(block); if (result_type != nullptr) { Status st = result_type->check_column(*result_column); - if (!st.ok()) { - // Nullable(T) may legitimately produce a non-nullable T column when all rows are - // non-null (use_default_implementation_for_nulls optimization). Allow this. - const auto* nullable_type = - check_and_get_data_type(result_type.get()); - if (nullable_type && !check_and_get_column(result_column.get())) { - st = nullable_type->get_nested_type()->check_column(*result_column); - } - } if (!st.ok()) { return Status::InternalError( "Expr {} return column type mismatch: declared={}, actual={}", expr_name(), diff --git a/be/test/exprs/vexpr_test.cpp b/be/test/exprs/vexpr_test.cpp index 10bdfb815d7d57..939a4d2472e7cb 100644 --- a/be/test/exprs/vexpr_test.cpp +++ b/be/test/exprs/vexpr_test.cpp @@ -893,11 +893,10 @@ TEST(VExprExecuteColumnTest, TypeMismatchFails) { EXPECT_FALSE(st.ok()); } -TEST(VExprExecuteColumnTest, NullableTypeWithNonNullableColumnPasses) { +TEST(VExprExecuteColumnTest, NullableTypeWithNonNullableColumnFails) { using namespace doris; FakeVExpr expr; - // Declared type is Nullable(Int32) but result is Int32 (non-nullable). - // This mirrors the use_default_implementation_for_nulls optimization and must pass. + // Declared type is Nullable(Int32), so the result must carry a nullable column wrapper. expr.set_data_type(std::make_shared(std::make_shared())); auto col = ColumnInt32::create(); @@ -906,7 +905,7 @@ TEST(VExprExecuteColumnTest, NullableTypeWithNonNullableColumnPasses) { ColumnPtr result; auto st = expr.execute_column(nullptr, nullptr, nullptr, 1, result); - EXPECT_TRUE(st.ok()); + EXPECT_FALSE(st.ok()); } TEST(VExprExecuteColumnTest, ColumnNothingPassesTypeCheck) { From 5c2500f7b54b12c4358f5616db6c2a726ab7b84a Mon Sep 17 00:00:00 2001 From: Mryange Date: Wed, 23 Sep 2026 09:42:58 +0800 Subject: [PATCH 2/2] [fix](expr) Wrap non-null results for nullable expressions --- be/src/exprs/vexpr.cpp | 3 +++ be/test/exprs/vexpr_test.cpp | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/be/src/exprs/vexpr.cpp b/be/src/exprs/vexpr.cpp index 3c06f3bbdee89f..e49bb037865582 100644 --- a/be/src/exprs/vexpr.cpp +++ b/be/src/exprs/vexpr.cpp @@ -1074,6 +1074,9 @@ Status VExpr::execute_column(VExprContext* context, const Block* block, const Se if (!check_and_get_column(result_column.get())) { auto result_type = execute_type(block); if (result_type != nullptr) { + if (result_type->is_nullable() && !result_column->is_nullable()) { + result_column = make_nullable(result_column, false); + } Status st = result_type->check_column(*result_column); if (!st.ok()) { return Status::InternalError( diff --git a/be/test/exprs/vexpr_test.cpp b/be/test/exprs/vexpr_test.cpp index 939a4d2472e7cb..6138fc89ea3489 100644 --- a/be/test/exprs/vexpr_test.cpp +++ b/be/test/exprs/vexpr_test.cpp @@ -893,7 +893,7 @@ TEST(VExprExecuteColumnTest, TypeMismatchFails) { EXPECT_FALSE(st.ok()); } -TEST(VExprExecuteColumnTest, NullableTypeWithNonNullableColumnFails) { +TEST(VExprExecuteColumnTest, NullableTypeWithNonNullableColumnIsWrapped) { using namespace doris; FakeVExpr expr; // Declared type is Nullable(Int32), so the result must carry a nullable column wrapper. @@ -905,7 +905,8 @@ TEST(VExprExecuteColumnTest, NullableTypeWithNonNullableColumnFails) { ColumnPtr result; auto st = expr.execute_column(nullptr, nullptr, nullptr, 1, result); - EXPECT_FALSE(st.ok()); + EXPECT_TRUE(st.ok()); + EXPECT_TRUE(result->is_nullable()); } TEST(VExprExecuteColumnTest, ColumnNothingPassesTypeCheck) {