@ -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) const 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) {
|
||||
|
||||
@ -179,7 +179,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();
|
||||
|
||||
647
be/test/vec/function/function_eq_for_null_test.cpp
Normal file
647
be/test/vec/function/function_eq_for_null_test.cpp
Normal file
@ -0,0 +1,647 @@
|
||||
// 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(1, 1);
|
||||
|
||||
ColumnWithTypeAndName left {
|
||||
ColumnNullable::create(left_i32->clone_resized(1),
|
||||
null_map_for_all_null->clone_resized(1)),
|
||||
std::make_shared<DataTypeNullable>(std::make_shared<DataTypeInt32>()), "left"};
|
||||
|
||||
ColumnWithTypeAndName right {
|
||||
ColumnNullable::create(right_i32->clone_resized(1),
|
||||
null_map_for_all_null->clone_resized(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());
|
||||
|
||||
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_resized(1), ColumnUInt8::create(1, 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_resized(1), ColumnUInt8::create(1, 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_resized(1), ColumnUInt8::create(1, 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_resized(1), ColumnUInt8::create(1, 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_resized(1),
|
||||
null_map_for_all_null->clone_resized(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());
|
||||
|
||||
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_resized(1),
|
||||
null_map_for_all_null->clone_resized(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());
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user