| // 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. |
| // This file is copied from |
| // https://github.com/ClickHouse/ClickHouse/blob/master/src/Functions/FunctionsLogical.cpp |
| // and modified by Doris |
| |
| #include "vec/functions/functions_logical.h" |
| |
| #include <glog/logging.h> |
| |
| #include <algorithm> |
| #include <ranges> |
| #include <utility> |
| |
| #include "common/compiler_util.h" // IWYU pragma: keep |
| #include "common/status.h" |
| #include "vec/aggregate_functions/aggregate_function.h" |
| #include "vec/columns/column.h" |
| #include "vec/columns/column_const.h" |
| #include "vec/columns/column_nullable.h" |
| #include "vec/columns/column_vector.h" |
| #include "vec/common/assert_cast.h" |
| #include "vec/common/string_ref.h" |
| #include "vec/core/block.h" |
| #include "vec/core/column_with_type_and_name.h" |
| #include "vec/data_types/data_type_nullable.h" |
| #include "vec/data_types/data_type_number.h" |
| #include "vec/functions/simple_function_factory.h" |
| |
| namespace doris { |
| class FunctionContext; |
| } // namespace doris |
| |
| namespace doris::vectorized { |
| |
| namespace { |
| using namespace FunctionsLogicalDetail; |
| |
| template <class Op> |
| void vector_const(const IColumn* left, const ColumnConst* right, IColumn* res, size_t rows) { |
| const auto* __restrict l_datas = assert_cast<const ColumnUInt8*>(left)->get_data().data(); |
| auto r_data = (uint8_t)right->get_bool(0); |
| auto* __restrict res_datas = assert_cast<ColumnUInt8*>(res)->get_data().data(); |
| |
| for (size_t i = 0; i < rows; ++i) { |
| res_datas[i] = Op::apply(l_datas[i], r_data); |
| } |
| } |
| |
| template <class Op> |
| void vector_vector(const IColumn* left, const IColumn* right, IColumn* res, size_t rows) { |
| const auto* __restrict l_datas = assert_cast<const ColumnUInt8*>(left)->get_data().data(); |
| const auto* __restrict r_datas = assert_cast<const ColumnUInt8*>(right)->get_data().data(); |
| auto* __restrict res_datas = assert_cast<ColumnUInt8*>(res)->get_data().data(); |
| |
| for (size_t i = 0; i < rows; ++i) { |
| res_datas[i] = Op::apply(l_datas[i], r_datas[i]); |
| } |
| } |
| |
| std::pair<const IColumn*, ColumnPtr> get_nested_and_null_column(const IColumn* column) { |
| auto null_column = check_and_get_column<const ColumnNullable>(column); |
| if (null_column) { |
| return {null_column->get_nested_column_ptr().get(), null_column->get_null_map_column_ptr()}; |
| } else { |
| return {column, ColumnUInt8::create(column->size(), 0)}; |
| } |
| } |
| |
| template <class Op> |
| void vector_const_null(const IColumn* left, const ColumnConst* right, IColumn* res, IColumn* nulls, |
| size_t rows) { |
| auto [data_column, null_column_ptr] = get_nested_and_null_column(left); |
| const auto* __restrict l_datas = |
| assert_cast<const ColumnUInt8*>(data_column)->get_data().data(); |
| const auto* __restrict l_nulls = |
| assert_cast<const ColumnUInt8*>(null_column_ptr.get())->get_data().data(); |
| |
| auto* __restrict res_datas = assert_cast<ColumnUInt8*>(res)->get_data().data(); |
| auto* __restrict res_nulls = assert_cast<ColumnUInt8*>(nulls)->get_data().data(); |
| |
| auto r_data_ptr = right->get_data_at(0); |
| |
| if (r_data_ptr.data == nullptr) { |
| for (size_t i = 0; i < rows; ++i) { |
| res_nulls[i] = Op::apply_null(l_datas[i], l_nulls[i], 1, true); |
| res_datas[i] = Op::apply(l_datas[i], 1); |
| } |
| } else { |
| UInt8 r_data = *(UInt8*)r_data_ptr.data; |
| for (size_t i = 0; i < rows; ++i) { |
| res_nulls[i] = Op::apply_null(l_datas[i], l_nulls[i], r_data, false); |
| res_datas[i] = Op::apply(l_datas[i], r_data); |
| } |
| } |
| } |
| |
| template <class Op> |
| void vector_vector_null(const IColumn* left, const IColumn* right, IColumn* res, IColumn* nulls, |
| size_t rows) { |
| auto [l_datas_ptr, l_nulls_ptr] = get_nested_and_null_column(left); |
| auto [r_datas_ptr, r_nulls_ptr] = get_nested_and_null_column(right); |
| |
| const auto* __restrict l_datas = |
| assert_cast<const ColumnUInt8*>(l_datas_ptr)->get_data().data(); |
| const auto* __restrict r_datas = |
| assert_cast<const ColumnUInt8*>(r_datas_ptr)->get_data().data(); |
| const auto* __restrict l_nulls = |
| assert_cast<const ColumnUInt8*>(l_nulls_ptr.get())->get_data().data(); |
| const auto* __restrict r_nulls = |
| assert_cast<const ColumnUInt8*>(r_nulls_ptr.get())->get_data().data(); |
| |
| auto* __restrict res_datas = assert_cast<ColumnUInt8*>(res)->get_data().data(); |
| auto* __restrict res_nulls = assert_cast<ColumnUInt8*>(nulls)->get_data().data(); |
| |
| for (size_t i = 0; i < rows; ++i) { |
| res_nulls[i] = Op::apply_null(l_datas[i], l_nulls[i], r_datas[i], r_nulls[i]); |
| res_datas[i] = Op::apply(l_datas[i], r_datas[i]); |
| } |
| } |
| |
| template <class Op> |
| void basic_execute_impl(ColumnRawPtrs arguments, ColumnWithTypeAndName& result_info, |
| size_t input_rows_count) { |
| auto col_res = ColumnUInt8::create(input_rows_count); |
| if (auto l = check_and_get_column<ColumnConst>(arguments[0])) { |
| vector_const<Op>(arguments[1], l, col_res.get(), input_rows_count); |
| } else if (auto r = check_and_get_column<ColumnConst>(arguments[1])) { |
| vector_const<Op>(arguments[0], r, col_res.get(), input_rows_count); |
| } else { |
| vector_vector<Op>(arguments[0], arguments[1], col_res.get(), input_rows_count); |
| } |
| result_info.column = std::move(col_res); |
| } |
| |
| template <class Op> |
| void null_execute_impl(ColumnRawPtrs arguments, ColumnWithTypeAndName& result_info, |
| size_t input_rows_count) { |
| auto col_nulls = ColumnUInt8::create(input_rows_count); |
| auto col_res = ColumnUInt8::create(input_rows_count); |
| if (auto l = check_and_get_column<ColumnConst>(arguments[0])) { |
| vector_const_null<Op>(arguments[1], l, col_res.get(), col_nulls.get(), input_rows_count); |
| } else if (auto r = check_and_get_column<ColumnConst>(arguments[1])) { |
| vector_const_null<Op>(arguments[0], r, col_res.get(), col_nulls.get(), input_rows_count); |
| } else { |
| vector_vector_null<Op>(arguments[0], arguments[1], col_res.get(), col_nulls.get(), |
| input_rows_count); |
| } |
| result_info.column = ColumnNullable::create(std::move(col_res), std::move(col_nulls)); |
| } |
| |
| } // namespace |
| |
| bool is_native_number(PrimitiveType type) { |
| return (is_int_or_bool(type) && type != TYPE_LARGEINT) || is_float_or_double(type); |
| } |
| |
| template <typename Impl, typename Name> |
| DataTypePtr FunctionAnyArityLogical<Impl, Name>::get_return_type_impl( |
| const DataTypes& arguments) const { |
| if (arguments.size() < 2) { |
| throw doris::Exception( |
| ErrorCode::INVALID_ARGUMENT, |
| "Number of arguments for function \"{}\" should be at least 2: passed {}", |
| get_name(), arguments.size()); |
| } |
| |
| bool has_nullable_arguments = false; |
| for (size_t i = 0; i < arguments.size(); ++i) { |
| const auto& arg_type = arguments[i]; |
| |
| if (!has_nullable_arguments) { |
| has_nullable_arguments = arg_type->is_nullable(); |
| if (has_nullable_arguments && !Impl::special_implementation_for_nulls()) { |
| LOG(WARNING) << fmt::format( |
| "Logical error: Unexpected type of argument for function \"{}\" argument " |
| "{} is of type {}", |
| get_name(), i + 1, arg_type->get_name()); |
| } |
| } |
| |
| auto arg_primitive_type = arg_type->get_primitive_type(); |
| if (!(is_native_number(arg_primitive_type) || |
| (Impl::special_implementation_for_nulls() && is_native_number(arg_primitive_type)))) { |
| throw doris::Exception(ErrorCode::INVALID_ARGUMENT, |
| "Illegal type ({}) of {} argument of function {}", |
| arg_type->get_name(), i + 1, get_name()); |
| } |
| } |
| |
| auto result_type = std::make_shared<DataTypeUInt8>(); |
| return has_nullable_arguments ? make_nullable(result_type) : result_type; |
| } |
| |
| template <typename Impl, typename Name> |
| Status FunctionAnyArityLogical<Impl, Name>::execute_impl(FunctionContext* context, Block& block, |
| const ColumnNumbers& arguments, |
| uint32_t result_index, |
| size_t input_rows_count) const { |
| ColumnRawPtrs args_in; |
| for (const auto arg_index : arguments) |
| args_in.push_back(block.get_by_position(arg_index).column.get()); |
| |
| auto& result_info = block.get_by_position(result_index); |
| if constexpr (Impl::special_implementation_for_nulls()) { |
| if (result_info.type->is_nullable()) { |
| null_execute_impl<Impl>(std::move(args_in), result_info, input_rows_count); |
| } else { |
| basic_execute_impl<Impl>(std::move(args_in), result_info, input_rows_count); |
| } |
| } else { |
| DCHECK(std::ranges::all_of(args_in, [](const auto& arg) { return !arg->is_nullable(); })); |
| basic_execute_impl<Impl>(std::move(args_in), result_info, input_rows_count); |
| } |
| return Status::OK(); |
| } |
| |
| template <PrimitiveType A, typename Op> |
| struct UnaryOperationImpl { |
| using ArrayA = typename ColumnVector<A>::Container; |
| using ArrayC = typename ColumnVector<Op::ResultType>::Container; |
| |
| static void NO_INLINE vector(const ArrayA& a, ArrayC& c) { |
| std::transform(a.cbegin(), a.cend(), c.begin(), [](const auto x) { return Op::apply(x); }); |
| } |
| }; |
| |
| template <template <PrimitiveType> class Impl, typename Name> |
| DataTypePtr FunctionUnaryLogical<Impl, Name>::get_return_type_impl( |
| const DataTypes& arguments) const { |
| if (!is_native_number(arguments[0]->get_primitive_type())) { |
| throw doris::Exception(ErrorCode::INVALID_ARGUMENT, |
| "Illegal type ({}) of argument of function {}", |
| arguments[0]->get_name(), get_name()); |
| } |
| |
| return std::make_shared<DataTypeUInt8>(); |
| } |
| |
| template <template <PrimitiveType> class Impl, PrimitiveType T> |
| bool functionUnaryExecuteType(Block& block, const ColumnNumbers& arguments, size_t result) { |
| if (auto col = check_and_get_column<ColumnVector<T>>( |
| block.get_by_position(arguments[0]).column.get())) { |
| auto col_res = ColumnUInt8::create(); |
| |
| typename ColumnUInt8::Container& vec_res = col_res->get_data(); |
| vec_res.resize(col->get_data().size()); |
| UnaryOperationImpl<T, Impl<T>>::vector(col->get_data(), vec_res); |
| |
| block.replace_by_position(result, std::move(col_res)); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| template <template <PrimitiveType> class Impl, typename Name> |
| Status FunctionUnaryLogical<Impl, Name>::execute_impl(FunctionContext* context, Block& block, |
| const ColumnNumbers& arguments, |
| uint32_t result, |
| size_t /*input_rows_count*/) const { |
| if (!functionUnaryExecuteType<Impl, TYPE_BOOLEAN>(block, arguments, result)) { |
| throw doris::Exception(ErrorCode::INVALID_ARGUMENT, |
| "Illegal column {} of argument of function {}", |
| block.get_by_position(arguments[0]).column->get_name(), get_name()); |
| } |
| |
| return Status::OK(); |
| } |
| |
| void register_function_logical(SimpleFunctionFactory& instance) { |
| instance.register_function<FunctionAnd>(); |
| instance.register_function<FunctionOr>(); |
| instance.register_function<FunctionNot>(); |
| instance.register_function<FunctionXor>(); |
| } |
| |
| } // namespace doris::vectorized |