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

kxiao pushed a commit to branch branch-2.0
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-2.0 by this push:
     new 214ee1b712f [fix](eq_for_null) fix incorrect logic in function 
eq_for_null #36004 #36164 (#36123)
214ee1b712f is described below

commit 214ee1b712f42ba12a17a635602c21f2871d784d
Author: zhiqiang <seuhezhiqi...@163.com>
AuthorDate: Thu Jun 20 10:59:38 2024 +0800

    [fix](eq_for_null) fix incorrect logic in function eq_for_null #36004 
#36164 (#36123)
---
 be/src/vec/functions/comparison_equal_for_null.cpp | 139 +++--
 be/src/vec/functions/function.cpp                  |   2 +-
 be/test/vec/function/function_eq_for_null_test.cpp | 643 +++++++++++++++++++++
 3 files changed, 744 insertions(+), 40 deletions(-)

diff --git a/be/src/vec/functions/comparison_equal_for_null.cpp 
b/be/src/vec/functions/comparison_equal_for_null.cpp
index 32786310653..7644e88fd56 100644
--- a/be/src/vec/functions/comparison_equal_for_null.cpp
+++ b/be/src/vec/functions/comparison_equal_for_null.cpp
@@ -29,6 +29,7 @@
 #include "vec/columns/column_const.h"
 #include "vec/columns/column_nullable.h"
 #include "vec/columns/column_vector.h"
+#include "vec/columns/columns_number.h"
 #include "vec/common/assert_cast.h"
 #include "vec/core/block.h"
 #include "vec/core/column_numbers.h"
@@ -38,6 +39,7 @@
 #include "vec/data_types/data_type_nullable.h"
 #include "vec/data_types/data_type_number.h"
 #include "vec/functions/function.h"
+#include "vec/functions/function_helpers.h"
 #include "vec/functions/simple_function_factory.h"
 
 namespace doris {
@@ -66,69 +68,119 @@ public:
                         size_t result, size_t input_rows_count) override {
         ColumnWithTypeAndName& col_left = block.get_by_position(arguments[0]);
         ColumnWithTypeAndName& col_right = block.get_by_position(arguments[1]);
+
+        const bool left_const = is_column_const(*col_left.column);
+        const bool right_const = is_column_const(*col_right.column);
         bool left_only_null = col_left.column->only_null();
         bool right_only_null = col_right.column->only_null();
+
         if (left_only_null && right_only_null) {
+            // TODO: return ColumnConst after 
function.cpp::default_implementation_for_constant_arguments supports it.
             auto result_column = ColumnVector<UInt8>::create(input_rows_count, 
1);
             block.get_by_position(result).column = std::move(result_column);
             return Status::OK();
         } else if (left_only_null) {
             auto right_type_nullable = col_right.type->is_nullable();
             if (!right_type_nullable) {
+                // right_column is not nullable, so result is all false.
                 block.get_by_position(result).column =
                         ColumnVector<UInt8>::create(input_rows_count, 0);
             } else {
-                auto const* nullable_right_col =
-                        assert_cast<const 
ColumnNullable*>(col_right.column.get());
-                block.get_by_position(result).column =
-                        
nullable_right_col->get_null_map_column().clone_resized(input_rows_count);
+                // right_column is nullable
+                const ColumnNullable* nullable_right_col = nullptr;
+                if (right_const) {
+                    nullable_right_col = assert_cast<const ColumnNullable*>(
+                            &(assert_cast<const 
ColumnConst*>(col_right.column.get())
+                                      ->get_data_column()));
+                    // Actually, when we reach here, the result can only be 
all false (all not null).
+                    // Since if right column is const, and it is all null, we 
will be short-circuited
+                    // to (left_only_null && right_only_null) branch. So here 
the right column is all not null.
+                    block.get_by_position(result).column = ColumnUInt8::create(
+                            input_rows_count,
+                            
nullable_right_col->get_null_map_column().get_data()[0]);
+                } else {
+                    nullable_right_col = assert_cast<const 
ColumnNullable*>(col_right.column.get());
+                    // left column is all null, so result has same nullmap 
with right column.
+                    block.get_by_position(result).column =
+                            nullable_right_col->get_null_map_column().clone();
+                }
             }
             return Status::OK();
         } else if (right_only_null) {
             auto left_type_nullable = col_left.type->is_nullable();
             if (!left_type_nullable) {
+                // right column is all but left column is not nullable, so 
result is all false.
                 block.get_by_position(result).column =
                         ColumnVector<UInt8>::create(input_rows_count, 
(UInt8)0);
             } else {
-                auto const* nullable_left_col =
-                        assert_cast<const 
ColumnNullable*>(col_left.column.get());
-                block.get_by_position(result).column =
-                        
nullable_left_col->get_null_map_column().clone_resized(input_rows_count);
+                const ColumnNullable* nullable_left_col = nullptr;
+                if (left_const) {
+                    nullable_left_col = assert_cast<const ColumnNullable*>(
+                            &(assert_cast<const 
ColumnConst*>(col_left.column.get())
+                                      ->get_data_column()));
+                    block.get_by_position(result).column = ColumnUInt8::create(
+                            input_rows_count,
+                            
nullable_left_col->get_null_map_column().get_data()[0]);
+                } else {
+                    nullable_left_col = assert_cast<const 
ColumnNullable*>(col_left.column.get());
+                    // right column is all null, so result has same nullmap 
with left column.
+                    block.get_by_position(result).column =
+                            nullable_left_col->get_null_map_column().clone();
+                }
             }
             return Status::OK();
         }
 
-        const auto& [left_col, left_const] = unpack_if_const(col_left.column);
-        const auto& [right_col, right_const] = 
unpack_if_const(col_right.column);
-        const auto left_column = 
check_and_get_column<ColumnNullable>(left_col);
-        const auto right_column = 
check_and_get_column<ColumnNullable>(right_col);
+        const ColumnNullable* left_column = nullptr;
+        const ColumnNullable* right_column = nullptr;
+
+        if (left_const) {
+            left_column = check_and_get_column<const ColumnNullable>(
+                    assert_cast<const 
ColumnConst*>(col_left.column.get())->get_data_column_ptr());
+        } else {
+            left_column = check_and_get_column<const 
ColumnNullable>(col_left.column);
+        }
+
+        if (right_const) {
+            right_column = check_and_get_column<const ColumnNullable>(
+                    assert_cast<const 
ColumnConst*>(col_right.column.get())->get_data_column_ptr());
+        } else {
+            right_column = check_and_get_column<const 
ColumnNullable>(col_right.column);
+        }
 
         bool left_nullable = left_column != nullptr;
         bool right_nullable = right_column != nullptr;
 
         if (left_nullable == right_nullable) {
             auto return_type = std::make_shared<DataTypeUInt8>();
-
+            auto left_column_tmp =
+                    left_nullable ? left_column->get_nested_column_ptr() : 
col_left.column;
+            auto right_column_tmp =
+                    right_nullable ? right_column->get_nested_column_ptr() : 
col_right.column;
             ColumnsWithTypeAndName eq_columns {
                     ColumnWithTypeAndName {
-                            left_nullable ? 
left_column->get_nested_column_ptr() : col_left.column,
+                            left_const ? ColumnConst::create(left_column_tmp, 
input_rows_count)
+                                       : left_column_tmp,
                             left_nullable
                                     ? assert_cast<const 
DataTypeNullable*>(col_left.type.get())
                                               ->get_nested_type()
                                     : col_left.type,
                             ""},
-                    ColumnWithTypeAndName {left_nullable ? 
right_column->get_nested_column_ptr()
-                                                         : col_right.column,
-                                           left_nullable ? assert_cast<const 
DataTypeNullable*>(
-                                                                   
col_right.type.get())
-                                                                   
->get_nested_type()
-                                                         : col_right.type,
-                                           ""}};
+                    ColumnWithTypeAndName {
+                            right_const ? 
ColumnConst::create(right_column_tmp, input_rows_count)
+                                        : right_column_tmp,
+                            left_nullable
+                                    ? assert_cast<const 
DataTypeNullable*>(col_right.type.get())
+                                              ->get_nested_type()
+                                    : col_right.type,
+                            ""}};
             Block temporary_block(eq_columns);
 
             auto func_eq =
                     SimpleFunctionFactory::instance().get_function("eq", 
eq_columns, return_type);
-            DCHECK(func_eq);
+            DCHECK(func_eq) << fmt::format("Left type {} right type {} return 
type {}",
+                                           col_left.type->get_name(), 
col_right.type->get_name(),
+                                           return_type->get_name());
             temporary_block.insert(ColumnWithTypeAndName {nullptr, 
return_type, ""});
             RETURN_IF_ERROR(
                     func_eq->execute(context, temporary_block, {0, 1}, 2, 
input_rows_count));
@@ -147,7 +199,17 @@ public:
             }
 
             block.get_by_position(result).column = 
temporary_block.get_by_position(2).column;
-        } else { //left_nullable != right_nullable
+        } else {
+            // left_nullable != right_nullable
+            // If we go here, the DataType of left and right column is 
different.
+            // So there will be EXACTLLY one Column has Nullable data type.
+            // Possible cases:
+            // 1. left Datatype is nullable, right column is not nullable.
+            // 2. left Datatype is not nullable, right column is nullable.
+
+            // Why make_nullable here?
+            // Because function eq uses default implementation for null,
+            // and we have nullable arguments, so the return type should be 
nullable.
             auto return_type = 
make_nullable(std::make_shared<DataTypeUInt8>());
 
             const ColumnsWithTypeAndName eq_columns {
@@ -165,13 +227,24 @@ public:
             auto res_nullable_column = assert_cast<ColumnNullable*>(
                     
std::move(*temporary_block.get_by_position(2).column).mutate().get());
             auto& null_map = res_nullable_column->get_null_map_data();
-            auto& res_map =
+            auto& res_nested_col =
                     
assert_cast<ColumnVector<UInt8>&>(res_nullable_column->get_nested_column())
                             .get_data();
 
-            auto* __restrict res = res_map.data();
-            auto* __restrict l = null_map.data();
-            _exec_nullable_inequal(res, l, input_rows_count, left_const);
+            // Input of eq_for_null:
+            // Left: [1, 1, 1, 1](ColumnConst(ColumnInt32))
+            // Right: [1, 1, 1, 1] & [0, 1, 0, 1] (ColumnNullable(ColumnInt32))
+            // Above input will be passed to function eq, and result will be
+            // Result: [1, x, 1, x] & [0, 1, 0, 1] 
(ColumnNullable(ColumnUInt8)), x means default value.
+            // The expceted result of eq_for_null is:
+            // Except: [1, 0, 1, 0] (ColumnUInt8)
+            // We already have assumption that there is only one nullable 
column in input.
+            // So if one row of res_nullable_column is null, the result row of 
eq_for_null should be 0.
+            // For others, the result will be same with function eq.
+
+            for (int i = 0; i < input_rows_count; ++i) {
+                res_nested_col[i] &= (null_map[i] != 1);
+            }
 
             block.get_by_position(result).column = 
res_nullable_column->get_nested_column_ptr();
         }
@@ -196,18 +269,6 @@ private:
             }
         }
     }
-    static void _exec_nullable_inequal(unsigned char* result, const unsigned 
char* left,
-                                       size_t rows, bool left_const) {
-        if (left_const) {
-            for (int i = 0; i < rows; ++i) {
-                result[i] &= (left[0] != 1);
-            }
-        } else {
-            for (int i = 0; i < rows; ++i) {
-                result[i] &= (left[i] != 1);
-            }
-        }
-    }
 };
 
 void register_function_comparison_eq_for_null(SimpleFunctionFactory& factory) {
diff --git a/be/src/vec/functions/function.cpp 
b/be/src/vec/functions/function.cpp
index a4ed868bdfd..300193ac39e 100644
--- a/be/src/vec/functions/function.cpp
+++ b/be/src/vec/functions/function.cpp
@@ -210,7 +210,7 @@ Status 
PreparedFunctionImpl::default_implementation_for_constant_arguments(
     } else {
         result_column = temporary_block.get_by_position(arguments_size).column;
     }
-
+    // We shuold handle the case where the result column is also a ColumnConst.
     block.get_by_position(result).column = ColumnConst::create(result_column, 
input_rows_count);
     *executed = true;
     return Status::OK();
diff --git a/be/test/vec/function/function_eq_for_null_test.cpp 
b/be/test/vec/function/function_eq_for_null_test.cpp
new file mode 100644
index 00000000000..90b04b51608
--- /dev/null
+++ b/be/test/vec/function/function_eq_for_null_test.cpp
@@ -0,0 +1,643 @@
+// 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.
+
+#include <gtest/gtest.h>
+
+#include <cassert>
+#include <cstddef>
+#include <string>
+#include <vector>
+
+#include "common/status.h"
+#include "function_test_util.h"
+#include "gtest/gtest_pred_impl.h"
+#include "udf/udf.h"
+#include "vec/columns/column_const.h"
+#include "vec/columns/column_nullable.h"
+#include "vec/columns/columns_number.h"
+#include "vec/core/column_with_type_and_name.h"
+#include "vec/core/types.h"
+#include "vec/data_types/data_type_nullable.h"
+#include "vec/data_types/data_type_string.h"
+#include "vec/functions/function.h"
+#include "vec/functions/function_helpers.h"
+
+namespace doris::vectorized {
+
+TEST(EqForNullFunctionTest, both_only_null) {
+    const size_t input_rows_count = 100;
+
+    auto left_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto right_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto null_map_for_all_null = ColumnUInt8::create(input_rows_count, 1);
+
+    ColumnWithTypeAndName left {
+            ColumnNullable::create(left_i32->clone(), 
null_map_for_all_null->clone()),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "left"};
+
+    ColumnWithTypeAndName right {
+            ColumnNullable::create(right_i32->clone(), 
null_map_for_all_null->clone()),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "right"};
+
+    auto return_type = std::make_shared<DataTypeUInt8>();
+    auto func_eq_for_null = SimpleFunctionFactory::instance().get_function(
+            "eq_for_null", ColumnsWithTypeAndName {left, right}, return_type);
+
+    Block temporary_block(ColumnsWithTypeAndName {left, right});
+    temporary_block.insert(ColumnWithTypeAndName {nullptr, return_type, ""});
+    FunctionContext* context = nullptr;
+    auto status = func_eq_for_null->execute(context, temporary_block, {0, 1}, 
2, input_rows_count);
+
+    ASSERT_TRUE(status.ok());
+
+    auto result_column =
+            assert_cast<const 
ColumnUInt8*>(temporary_block.get_by_position(2).column.get());
+
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        ASSERT_EQ(result_column->get_data()[i], 1);
+    }
+}
+
+TEST(EqForNullFunctionTest, both_only_null_const) {
+    const size_t input_rows_count = 100;
+
+    auto left_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto right_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto null_map_for_all_null = ColumnUInt8::create(input_rows_count, 1);
+
+    ColumnWithTypeAndName left {
+            
ColumnConst::create(ColumnNullable::create(left_i32->clone_resized(1),
+                                                       
null_map_for_all_null->clone_resized(1)),
+                                input_rows_count),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "left"};
+
+    ColumnWithTypeAndName right {
+            
ColumnConst::create(ColumnNullable::create(right_i32->clone_resized(1),
+                                                       
null_map_for_all_null->clone_resized(1)),
+                                input_rows_count),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "right"};
+
+    auto return_type = std::make_shared<DataTypeUInt8>();
+    auto func_eq_for_null = SimpleFunctionFactory::instance().get_function(
+            "eq_for_null", ColumnsWithTypeAndName {left, right}, return_type);
+
+    Block temporary_block(ColumnsWithTypeAndName {left, right});
+    temporary_block.insert(ColumnWithTypeAndName {nullptr, return_type, ""});
+    FunctionContext* context = nullptr;
+    auto status = func_eq_for_null->execute(context, temporary_block, {0, 1}, 
2, input_rows_count);
+
+    ASSERT_TRUE(status.ok());
+
+    auto col_holder = 
temporary_block.get_by_position(2).column->convert_to_full_column_if_const();
+    auto result_column = assert_cast<const ColumnUInt8*>(col_holder.get());
+
+    std::cout << "Output rows count " << result_column->size() << std::endl;
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        ASSERT_EQ(result_column->get_data()[i], 1);
+    }
+}
+
+TEST(EqForNullFunctionTest, left_only_null_right_const) {
+    const size_t input_rows_count = 100;
+    // Input [NULL, NULL, NULL, NULL] & [1, 1, 1, 1]
+    auto left_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto right_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto null_map_for_all_null = ColumnUInt8::create(input_rows_count, 1);
+
+    ColumnWithTypeAndName left {
+            ColumnNullable::create(left_i32->clone(), 
ColumnUInt8::create(input_rows_count, 1)),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "left"};
+    std::cout << "left only null " << left.column->only_null() << std::endl;
+    ColumnWithTypeAndName right {
+            ColumnConst::create(
+                    ColumnNullable::create(right_i32->clone_resized(1), 
ColumnUInt8::create(1, 0)),
+                    input_rows_count),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "right"};
+
+    auto return_type = std::make_shared<DataTypeUInt8>();
+    auto func_eq_for_null = SimpleFunctionFactory::instance().get_function(
+            "eq_for_null", ColumnsWithTypeAndName {left, right}, return_type);
+
+    Block temporary_block(ColumnsWithTypeAndName {left, right});
+    temporary_block.insert(ColumnWithTypeAndName {nullptr, return_type, ""});
+    FunctionContext* context = nullptr;
+    auto status = func_eq_for_null->execute(context, temporary_block, {0, 1}, 
2, input_rows_count);
+
+    ASSERT_TRUE(status.ok());
+
+    auto result_column =
+            assert_cast<const 
ColumnUInt8*>(temporary_block.get_by_position(2).column.get());
+
+    std::cout << "Output rows count " << result_column->size() << std::endl;
+
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        ASSERT_EQ(result_column->get_data()[i], 0)
+                << fmt::format("Data {}, i {}", result_column->get_data()[i], 
i);
+    }
+}
+
+TEST(EqForNullFunctionTest, left_const_right_only_null) {
+    const size_t input_rows_count = 100;
+
+    auto left_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto right_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto null_map_for_all_null = ColumnUInt8::create(input_rows_count, 1);
+
+    ColumnWithTypeAndName left {
+            ColumnConst::create(
+                    ColumnNullable::create(left_i32->clone_resized(1), 
ColumnUInt8::create(1, 0)),
+                    input_rows_count),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "left"};
+
+    ColumnWithTypeAndName right {
+            ColumnNullable::create(right_i32->clone(), 
ColumnUInt8::create(input_rows_count, 1)),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "right"};
+
+    auto return_type = std::make_shared<DataTypeUInt8>();
+    auto func_eq_for_null = SimpleFunctionFactory::instance().get_function(
+            "eq_for_null", ColumnsWithTypeAndName {left, right}, return_type);
+
+    Block temporary_block(ColumnsWithTypeAndName {left, right});
+    temporary_block.insert(ColumnWithTypeAndName {nullptr, return_type, ""});
+
+    FunctionContext* context = nullptr;
+    auto status = func_eq_for_null->execute(context, temporary_block, {0, 1}, 
2, input_rows_count);
+
+    ASSERT_TRUE(status.ok());
+
+    auto result_column =
+            assert_cast<const 
ColumnUInt8*>(temporary_block.get_by_position(2).column.get());
+
+    std::cout << "Output rows count " << result_column->size() << std::endl;
+
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        ASSERT_EQ(result_column->get_data()[i], 0)
+                << fmt::format("Data {}, i {}", result_column->get_data()[i], 
i);
+    }
+}
+
+TEST(EqForNullFunctionTest, left_only_null_right_nullable) {
+    const size_t input_rows_count = 100;
+
+    auto left_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto right_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto null_map_for_all_null = ColumnUInt8::create(input_rows_count, 1);
+    auto common_null_map = ColumnUInt8::create();
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        if (i % 2 == 0) {
+            common_null_map->insert(0);
+        } else {
+            common_null_map->insert(1);
+        }
+    }
+
+    ColumnWithTypeAndName left {
+            ColumnNullable::create(left_i32->clone(), 
ColumnUInt8::create(input_rows_count, 1)),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "left"};
+
+    ColumnWithTypeAndName right {
+            ColumnNullable::create(right_i32->clone(), 
common_null_map->clone()),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "right"};
+
+    auto return_type = std::make_shared<DataTypeUInt8>();
+    auto func_eq_for_null = SimpleFunctionFactory::instance().get_function(
+            "eq_for_null", ColumnsWithTypeAndName {left, right}, return_type);
+
+    Block temporary_block(ColumnsWithTypeAndName {left, right});
+    temporary_block.insert(ColumnWithTypeAndName {nullptr, return_type, ""});
+    FunctionContext* context = nullptr;
+    auto status = func_eq_for_null->execute(context, temporary_block, {0, 1}, 
2, input_rows_count);
+
+    ASSERT_TRUE(status.ok());
+
+    auto result_column =
+            assert_cast<const 
ColumnUInt8*>(temporary_block.get_by_position(2).column.get());
+
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        if (i % 2 == 0) {
+            ASSERT_EQ(result_column->get_data()[i], 0);
+        } else {
+            ASSERT_EQ(result_column->get_data()[i], 1);
+        }
+    }
+}
+
+TEST(EqForNullFunctionTest, left_only_null_right_not_nullable) {
+    const size_t input_rows_count = 100;
+    // left [NULL, NULL, NULL, NULL] & right [1, 1, 1, 1]
+    // result [0, 0, 0, 0]
+    auto left_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto right_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto null_map_for_all_null = ColumnUInt8::create(input_rows_count, 1);
+    auto common_null_map = ColumnUInt8::create();
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        if (i % 2 == 0) {
+            common_null_map->insert(0);
+        } else {
+            common_null_map->insert(1);
+        }
+    }
+
+    ColumnWithTypeAndName left {
+            ColumnNullable::create(left_i32->clone(), 
ColumnUInt8::create(input_rows_count, 1)),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "left"};
+
+    ColumnWithTypeAndName right {right_i32->clone(), 
std::make_shared<DataTypeInt32>(), "right"};
+
+    auto return_type = std::make_shared<DataTypeUInt8>();
+    auto func_eq_for_null = SimpleFunctionFactory::instance().get_function(
+            "eq_for_null", ColumnsWithTypeAndName {left, right}, return_type);
+
+    Block temporary_block(ColumnsWithTypeAndName {left, right});
+    temporary_block.insert(ColumnWithTypeAndName {nullptr, return_type, ""});
+    FunctionContext* context = nullptr;
+    auto status = func_eq_for_null->execute(context, temporary_block, {0, 1}, 
2, input_rows_count);
+
+    ASSERT_TRUE(status.ok());
+
+    auto result_column =
+            assert_cast<const 
ColumnUInt8*>(temporary_block.get_by_position(2).column.get());
+
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        ASSERT_EQ(result_column->get_data()[i], 0);
+    }
+}
+
+TEST(EqForNullFunctionTest, left_nullable_right_only_null) {
+    const size_t input_rows_count = 100;
+    // LEFT: [1, NULL, 1, NULL] & RIGHT: [NULL, NULL, NULL, NULL]
+    auto left_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto right_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto null_map_for_all_null = ColumnUInt8::create(input_rows_count, 1);
+    auto common_null_map = ColumnUInt8::create();
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        if (i % 2 == 0) {
+            common_null_map->insert(0);
+        } else {
+            common_null_map->insert(1);
+        }
+    }
+
+    ColumnWithTypeAndName left {
+            ColumnNullable::create(left_i32->clone(), 
common_null_map->clone()),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "left"};
+
+    ColumnWithTypeAndName right {
+            ColumnNullable::create(right_i32->clone(), 
null_map_for_all_null->clone()),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "right"};
+
+    auto return_type = std::make_shared<DataTypeUInt8>();
+    auto func_eq_for_null = SimpleFunctionFactory::instance().get_function(
+            "eq_for_null", ColumnsWithTypeAndName {left, right}, return_type);
+
+    Block temporary_block(ColumnsWithTypeAndName {left, right});
+    temporary_block.insert(ColumnWithTypeAndName {nullptr, return_type, ""});
+    FunctionContext* context = nullptr;
+    auto status = func_eq_for_null->execute(context, temporary_block, {0, 1}, 
2, input_rows_count);
+
+    ASSERT_TRUE(status.ok());
+
+    auto result_column =
+            assert_cast<const 
ColumnUInt8*>(temporary_block.get_by_position(2).column.get());
+
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        if (i % 2 == 0) {
+            ASSERT_EQ(result_column->get_data()[i], 0);
+        } else {
+            ASSERT_EQ(result_column->get_data()[i], 1);
+        }
+    }
+}
+
+TEST(EqForNullFunctionTest, left_not_nullable_right_only_null) {
+    const size_t input_rows_count = 100;
+    // LEFT: [1, 1, 1, 1] & RIGHT: [NULL, NULL, NULL, NULL]
+    // output [0, 0, 0, 0]
+    auto left_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto right_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto null_map_for_all_null = ColumnUInt8::create(input_rows_count, 1);
+    auto common_null_map = ColumnUInt8::create();
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        if (i % 2 == 0) {
+            common_null_map->insert(0);
+        } else {
+            common_null_map->insert(1);
+        }
+    }
+
+    ColumnWithTypeAndName left {left_i32->clone(), 
std::make_shared<DataTypeInt32>(), "left"};
+
+    ColumnWithTypeAndName right {
+            ColumnNullable::create(right_i32->clone(), 
null_map_for_all_null->clone()),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "right"};
+
+    auto return_type = std::make_shared<DataTypeUInt8>();
+    auto func_eq_for_null = SimpleFunctionFactory::instance().get_function(
+            "eq_for_null", ColumnsWithTypeAndName {left, right}, return_type);
+
+    Block temporary_block(ColumnsWithTypeAndName {left, right});
+    temporary_block.insert(ColumnWithTypeAndName {nullptr, return_type, ""});
+    FunctionContext* context = nullptr;
+    auto status = func_eq_for_null->execute(context, temporary_block, {0, 1}, 
2, input_rows_count);
+
+    ASSERT_TRUE(status.ok());
+
+    auto result_column =
+            assert_cast<const 
ColumnUInt8*>(temporary_block.get_by_position(2).column.get());
+
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        ASSERT_EQ(result_column->get_data()[i], 0);
+    }
+}
+
+TEST(EqForNullFunctionTest, left_nullable_right_nullable) {
+    const size_t input_rows_count = 100;
+    // input: [NULL, 1, NULL, 1] & [NULL, 1, NULL, 1]
+    auto left_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto right_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto common_null_map = ColumnUInt8::create();
+
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        if (i % 2 == 0) {
+            common_null_map->insert(0);
+        } else {
+            common_null_map->insert(1);
+        }
+    }
+
+    ColumnWithTypeAndName left {
+            ColumnNullable::create(left_i32->clone(), 
common_null_map->clone()),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "left"};
+
+    ColumnWithTypeAndName right {
+            ColumnNullable::create(right_i32->clone(), 
common_null_map->clone()),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "right"};
+
+    auto return_type = std::make_shared<DataTypeUInt8>();
+    auto func_eq_for_null = SimpleFunctionFactory::instance().get_function(
+            "eq_for_null", ColumnsWithTypeAndName {left, right}, return_type);
+
+    Block temporary_block(ColumnsWithTypeAndName {left, right});
+    temporary_block.insert(ColumnWithTypeAndName {nullptr, return_type, ""});
+    FunctionContext* context = nullptr;
+    auto status = func_eq_for_null->execute(context, temporary_block, {0, 1}, 
2, input_rows_count);
+
+    ASSERT_TRUE(status.ok());
+
+    auto result_column =
+            assert_cast<const 
ColumnUInt8*>(temporary_block.get_by_position(2).column.get());
+
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        ASSERT_EQ(result_column->get_data()[i], 1);
+    }
+}
+
+TEST(EqForNullFunctionTest, left_nullable_right_not_nullable) {
+    const size_t input_rows_count = 100;
+    // input        [1, NULL, 1, NULL] & [1, 1, 1, 1]
+    // output       [1, 0, 1, 0]
+    auto left_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto right_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto common_null_map = ColumnUInt8::create();
+
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        if (i % 2 == 0) {
+            common_null_map->insert(0);
+        } else {
+            common_null_map->insert(1);
+        }
+    }
+
+    ColumnWithTypeAndName left {
+            ColumnNullable::create(left_i32->clone(), 
common_null_map->clone()),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "left"};
+
+    ColumnWithTypeAndName right {right_i32->clone(), 
std::make_shared<DataTypeInt32>(), "right"};
+
+    auto return_type = std::make_shared<DataTypeUInt8>();
+    auto func_eq_for_null = SimpleFunctionFactory::instance().get_function(
+            "eq_for_null", ColumnsWithTypeAndName {left, right}, return_type);
+
+    Block temporary_block(ColumnsWithTypeAndName {left, right});
+    temporary_block.insert(ColumnWithTypeAndName {nullptr, return_type, ""});
+    FunctionContext* context = nullptr;
+    auto status = func_eq_for_null->execute(context, temporary_block, {0, 1}, 
2, input_rows_count);
+
+    ASSERT_TRUE(status.ok());
+
+    auto result_column =
+            assert_cast<const 
ColumnUInt8*>(temporary_block.get_by_position(2).column.get());
+
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        if (i % 2 == 0) {
+            ASSERT_EQ(result_column->get_data()[i], 1);
+        } else {
+            ASSERT_EQ(result_column->get_data()[i], 0);
+        }
+    }
+}
+
+TEST(EqForNullFunctionTest, left_not_nullable_right_nullable) {
+    const size_t input_rows_count = 100;
+    // input        [1, 1, 1, 1] & [1, NULL, 1, NULL]
+    // output       [1, 0, 1, 0]
+    auto left_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto right_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto common_null_map = ColumnUInt8::create();
+
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        if (i % 2 == 0) {
+            common_null_map->insert(0);
+        } else {
+            common_null_map->insert(1);
+        }
+    }
+
+    ColumnWithTypeAndName left {left_i32->clone(), 
std::make_shared<DataTypeInt32>(), "right"};
+    ColumnWithTypeAndName right {
+            ColumnNullable::create(right_i32->clone(), 
common_null_map->clone()),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "left"};
+
+    auto return_type = std::make_shared<DataTypeUInt8>();
+    auto func_eq_for_null = SimpleFunctionFactory::instance().get_function(
+            "eq_for_null", ColumnsWithTypeAndName {left, right}, return_type);
+
+    Block temporary_block(ColumnsWithTypeAndName {left, right});
+    temporary_block.insert(ColumnWithTypeAndName {nullptr, return_type, ""});
+    FunctionContext* context = nullptr;
+    auto status = func_eq_for_null->execute(context, temporary_block, {0, 1}, 
2, input_rows_count);
+
+    ASSERT_TRUE(status.ok());
+
+    auto result_column =
+            assert_cast<const 
ColumnUInt8*>(temporary_block.get_by_position(2).column.get());
+
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        if (i % 2 == 0) {
+            ASSERT_EQ(result_column->get_data()[i], 1);
+        } else {
+            ASSERT_EQ(result_column->get_data()[i], 0);
+        }
+    }
+}
+
+TEST(EqForNullFunctionTest, left_const_not_nullable_right_nullable) {
+    const size_t input_rows_count = 10;
+    // input        [1, 1, 1, 1] & [1, NULL, 1, NULL]
+    // output       [1, 0, 1, 0]
+    auto left_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto right_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto common_null_map = ColumnUInt8::create();
+
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        if (i % 2 == 0) {
+            common_null_map->insert(0);
+        } else {
+            common_null_map->insert(1);
+        }
+    }
+
+    ColumnWithTypeAndName left 
{ColumnConst::create(left_i32->clone_resized(1), input_rows_count),
+                                std::make_shared<DataTypeInt32>(), "right"};
+    ColumnWithTypeAndName right {
+            ColumnNullable::create(right_i32->clone(), 
common_null_map->clone()),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "left"};
+
+    auto return_type = std::make_shared<DataTypeUInt8>();
+    auto func_eq_for_null = SimpleFunctionFactory::instance().get_function(
+            "eq_for_null", ColumnsWithTypeAndName {left, right}, return_type);
+
+    Block temporary_block(ColumnsWithTypeAndName {left, right});
+    temporary_block.insert(ColumnWithTypeAndName {nullptr, return_type, ""});
+    FunctionContext* context = nullptr;
+    auto status = func_eq_for_null->execute(context, temporary_block, {0, 1}, 
2, input_rows_count);
+
+    ASSERT_TRUE(status.ok());
+
+    auto result_column =
+            assert_cast<const 
ColumnUInt8*>(temporary_block.get_by_position(2).column.get());
+
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        if (i % 2 == 0) {
+            EXPECT_EQ(result_column->get_data()[i], 1)
+                    << fmt::format("i {} value {}", i, 
result_column->get_data()[i]);
+        } else {
+            EXPECT_EQ(result_column->get_data()[i], 0)
+                    << fmt::format("i {} value {}", i, 
result_column->get_data()[i]);
+        }
+    }
+}
+
+TEST(EqForNullFunctionTest, left_const_nullable_right_nullable) {
+    const size_t input_rows_count = 100;
+    // input        [1, 1, 1, 1] & [1, NULL, 1, NULL]
+    // output       [1, 0, 1, 0]
+    auto left_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto right_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto common_null_map = ColumnUInt8::create();
+
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        if (i % 2 == 0) {
+            common_null_map->insert(0);
+        } else {
+            common_null_map->insert(1);
+        }
+    }
+
+    ColumnWithTypeAndName left {
+            ColumnConst::create(
+                    ColumnNullable::create(left_i32->clone_resized(1), 
ColumnUInt8::create(1, 0)),
+                    input_rows_count),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "left"};
+
+    ColumnWithTypeAndName right {
+            ColumnNullable::create(right_i32->clone(), 
common_null_map->clone()),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "right"};
+
+    auto return_type = std::make_shared<DataTypeUInt8>();
+    auto func_eq_for_null = SimpleFunctionFactory::instance().get_function(
+            "eq_for_null", ColumnsWithTypeAndName {left, right}, return_type);
+
+    Block temporary_block(ColumnsWithTypeAndName {left, right});
+    temporary_block.insert(ColumnWithTypeAndName {nullptr, return_type, ""});
+    FunctionContext* context = nullptr;
+    auto status = func_eq_for_null->execute(context, temporary_block, {0, 1}, 
2, input_rows_count);
+
+    ASSERT_TRUE(status.ok());
+
+    auto result_column =
+            assert_cast<const 
ColumnUInt8*>(temporary_block.get_by_position(2).column.get());
+
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        if (i % 2 == 0) {
+            ASSERT_EQ(result_column->get_data()[i], 1);
+        } else {
+            ASSERT_EQ(result_column->get_data()[i], 0);
+        }
+    }
+}
+
+TEST(EqForNullFunctionTest, left_nullable_right_const_nullable) {
+    const size_t input_rows_count = 100;
+    // input        [1, NULL, 1, NULL] & [1, 1, 1, 1]
+    // output       [1, 0, 1, 0]
+    auto left_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto right_i32 = ColumnInt32::create(input_rows_count, 1);
+    auto common_null_map = ColumnUInt8::create();
+
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        if (i % 2 == 0) {
+            common_null_map->insert(0);
+        } else {
+            common_null_map->insert(1);
+        }
+    }
+
+    ColumnWithTypeAndName left {
+            ColumnNullable::create(left_i32->clone(), 
common_null_map->clone()),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "left"};
+
+    ColumnWithTypeAndName right {
+            ColumnConst::create(
+                    ColumnNullable::create(right_i32->clone_resized(1), 
ColumnUInt8::create(1, 0)),
+                    input_rows_count),
+            
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "right"};
+
+    auto return_type = std::make_shared<DataTypeUInt8>();
+    auto func_eq_for_null = SimpleFunctionFactory::instance().get_function(
+            "eq_for_null", ColumnsWithTypeAndName {left, right}, return_type);
+
+    Block temporary_block(ColumnsWithTypeAndName {left, right});
+    temporary_block.insert(ColumnWithTypeAndName {nullptr, return_type, ""});
+    FunctionContext* context = nullptr;
+    auto status = func_eq_for_null->execute(context, temporary_block, {0, 1}, 
2, input_rows_count);
+
+    ASSERT_TRUE(status.ok());
+
+    auto result_column =
+            assert_cast<const 
ColumnUInt8*>(temporary_block.get_by_position(2).column.get());
+
+    for (size_t i = 0; i < input_rows_count; ++i) {
+        if (i % 2 == 0) {
+            ASSERT_EQ(result_column->get_data()[i], 1);
+        } else {
+            ASSERT_EQ(result_column->get_data()[i], 0);
+        }
+    }
+}
+
+} // namespace doris::vectorized
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org


Reply via email to