This is an automated email from the ASF dual-hosted git repository. lihaopeng pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-doris.git
The following commit(s) were added to refs/heads/master by this push: new 98bfeaf560 [Enhancement] [Vectorized] Refactor and optimize BinaryOperation (#9087) 98bfeaf560 is described below commit 98bfeaf560f7ac04f2324499e4dc7cc3bbf0a437 Author: Pxl <952130...@qq.com> AuthorDate: Sat May 7 10:55:15 2022 +0800 [Enhancement] [Vectorized] Refactor and optimize BinaryOperation (#9087) --- be/src/vec/data_types/data_type_decimal.h | 6 +- be/src/vec/data_types/number_traits.h | 87 +- be/src/vec/functions/divide.cpp | 34 +- be/src/vec/functions/function.h | 9 - be/src/vec/functions/function_binary_arithmetic.h | 881 ++++++++++++--------- .../function_binary_arithmetic_to_null_type.h | 247 ------ be/src/vec/functions/function_bit.cpp | 8 +- be/src/vec/functions/function_cast.h | 1 - be/src/vec/functions/int_div.cpp | 118 +-- be/src/vec/functions/int_div.h | 36 +- be/src/vec/functions/math.cpp | 39 +- be/src/vec/functions/minus.cpp | 2 +- be/src/vec/functions/modulo.cpp | 180 ++--- be/src/vec/functions/multiply.cpp | 2 +- be/src/vec/functions/plus.cpp | 2 +- 15 files changed, 676 insertions(+), 976 deletions(-) diff --git a/be/src/vec/data_types/data_type_decimal.h b/be/src/vec/data_types/data_type_decimal.h index d701ecee71..0613653324 100644 --- a/be/src/vec/data_types/data_type_decimal.h +++ b/be/src/vec/data_types/data_type_decimal.h @@ -106,10 +106,12 @@ public: } // Now, Doris only support precision:27, scale: 9 - DCHECK(precision_ == 27); - DCHECK(scale_ == 9); + DCHECK(precision == 27); + DCHECK(scale == 9); } + DataTypeDecimal(const DataTypeDecimal& rhs) : precision(rhs.precision), scale(rhs.scale) {} + const char* get_family_name() const override { return "Decimal"; } std::string do_get_name() const override; TypeIndex get_type_id() const override { return TypeId<T>::value; } diff --git a/be/src/vec/data_types/number_traits.h b/be/src/vec/data_types/number_traits.h index 70830bfcd1..8b87e55d93 100644 --- a/be/src/vec/data_types/number_traits.h +++ b/be/src/vec/data_types/number_traits.h @@ -22,6 +22,8 @@ #include <type_traits> +#include "vec/columns/column_decimal.h" +#include "vec/columns/column_vector.h" #include "vec/common/uint128.h" #include "vec/core/types.h" @@ -155,7 +157,8 @@ struct ResultOfSubtraction { */ template <typename A, typename B> struct ResultOfFloatingPointDivision { - using Type = Float64; + using Type = std::conditional_t<IsDecimalNumber<A>, A, + std::conditional_t<IsDecimalNumber<B>, B, Float64>>; }; /** For integer division, we get a number with the same number of bits as in divisible. @@ -171,13 +174,8 @@ struct ResultOfIntegerDivision { template <typename A, typename B> struct ResultOfModulo { using Type = typename Construct<std::is_signed_v<A> || std::is_signed_v<B>, - std::is_floating_point_v<A>, max(sizeof(A), sizeof(B))>::Type; -}; - -template <typename A> -struct ResultOfNegate { - using Type = typename Construct<true, std::is_floating_point_v<A>, - std::is_signed_v<A> ? sizeof(A) : next_size(sizeof(A))>::Type; + std::is_floating_point_v<A> || std::is_floating_point_v<B>, + max(sizeof(A), sizeof(B))>::Type; }; template <typename A> @@ -200,76 +198,15 @@ struct ResultOfBitNot { using Type = typename Construct<std::is_signed_v<A>, false, sizeof(A)>::Type; }; -/** Type casting for `if` function: - * UInt<x>, UInt<y> -> UInt<max(x,y)> - * Int<x>, Int<y> -> Int<max(x,y)> - * Float<x>, Float<y> -> Float<max(x, y)> - * UInt<x>, Int<y> -> Int<max(x*2, y)> - * Float<x>, [U]Int<y> -> Float<max(x, y*2)> - * Decimal<x>, Decimal<y> -> Decimal<max(x,y)> - * UUID, UUID -> UUID - * UInt64 , Int<x> -> Error - * Float<x>, [U]Int64 -> Error - */ template <typename A, typename B> -struct ResultOfIf { - static constexpr bool has_float = std::is_floating_point_v<A> || std::is_floating_point_v<B>; - static constexpr bool has_integer = std::is_integral_v<A> || std::is_integral_v<B>; - static constexpr bool has_signed = std::is_signed_v<A> || std::is_signed_v<B>; - static constexpr bool has_unsigned = !std::is_signed_v<A> || !std::is_signed_v<B>; - - static constexpr size_t max_size_of_unsigned_integer = - max(std::is_signed_v<A> ? 0 : sizeof(A), std::is_signed_v<B> ? 0 : sizeof(B)); - static constexpr size_t max_size_of_signed_integer = - max(std::is_signed_v<A> ? sizeof(A) : 0, std::is_signed_v<B> ? sizeof(B) : 0); - static constexpr size_t max_size_of_integer = - max(std::is_integral_v<A> ? sizeof(A) : 0, std::is_integral_v<B> ? sizeof(B) : 0); - static constexpr size_t max_size_of_float = max(std::is_floating_point_v<A> ? sizeof(A) : 0, - std::is_floating_point_v<B> ? sizeof(B) : 0); - - using ConstructedType = - typename Construct<has_signed, has_float, - ((has_float && has_integer && - max_size_of_integer >= max_size_of_float) || - (has_signed && has_unsigned && - max_size_of_unsigned_integer >= max_size_of_signed_integer)) - ? max(sizeof(A), sizeof(B)) * 2 - : max(sizeof(A), sizeof(B))>::Type; - - using ConstructedWithUUID = - std::conditional_t<std::is_same_v<A, UInt128> && std::is_same_v<B, UInt128>, A, - ConstructedType>; - - using Type = std::conditional_t< - !IsDecimalNumber<A> && !IsDecimalNumber<B>, ConstructedWithUUID, - std::conditional_t<IsDecimalNumber<A> && IsDecimalNumber<B>, - std::conditional_t<(sizeof(A) > sizeof(B)), A, B>, Error>>; +struct BinaryOperatorTraits { + using ColumnVectorA = std::conditional_t<IsDecimalNumber<A>, ColumnDecimal<A>, ColumnVector<A>>; + using ColumnVectorB = std::conditional_t<IsDecimalNumber<B>, ColumnDecimal<B>, ColumnVector<B>>; + using ArrayA = typename ColumnVectorA::Container; + using ArrayB = typename ColumnVectorB::Container; + using ArrayNull = PaddedPODArray<UInt8>; }; -/** Before applying operator `%` and bitwise operations, operands are casted to whole numbers. */ -template <typename A> -struct ToInteger { - using Type = typename Construct<std::is_signed_v<A>, false, - std::is_floating_point_v<A> ? 8 : sizeof(A)>::Type; -}; - -// CLICKHOUSE-29. The same depth, different signs -// NOTE: This case is applied for 64-bit integers only (for backward compatibility), but could be used for any-bit integers -template <typename A, typename B> -constexpr bool LeastGreatestSpecialCase = std::is_integral_v<A>&& std::is_integral_v<B> && - (8 == sizeof(A) && sizeof(A) == sizeof(B)) && - (std::is_signed_v<A> ^ std::is_signed_v<B>); - -template <typename A, typename B> -using ResultOfLeast = std::conditional_t<LeastGreatestSpecialCase<A, B>, - typename Construct<true, false, sizeof(A)>::Type, - typename ResultOfIf<A, B>::Type>; - -template <typename A, typename B> -using ResultOfGreatest = std::conditional_t<LeastGreatestSpecialCase<A, B>, - typename Construct<false, false, sizeof(A)>::Type, - typename ResultOfIf<A, B>::Type>; - } // namespace NumberTraits } // namespace doris::vectorized diff --git a/be/src/vec/functions/divide.cpp b/be/src/vec/functions/divide.cpp index b6d3a2b35e..a602cb8b33 100644 --- a/be/src/vec/functions/divide.cpp +++ b/be/src/vec/functions/divide.cpp @@ -18,7 +18,7 @@ // https://github.com/ClickHouse/ClickHouse/blob/master/src/Functions/divide.cpp // and modified by Doris -#include "vec/functions/function_binary_arithmetic_to_null_type.h" +#include "vec/functions/function_binary_arithmetic.h" #include "vec/functions/simple_function_factory.h" namespace doris::vectorized { @@ -28,26 +28,42 @@ static const DecimalV2Value one(1, 0); template <typename A, typename B> struct DivideFloatingImpl { using ResultType = typename NumberTraits::ResultOfFloatingPointDivision<A, B>::Type; + using Traits = NumberTraits::BinaryOperatorTraits<A, B>; + static const constexpr bool allow_decimal = true; + template <typename Result = ResultType> + static void apply(const typename Traits::ArrayA& a, B b, + typename ColumnVector<Result>::Container& c, + typename Traits::ArrayNull& null_map) { + size_t size = c.size(); + UInt8 is_null = b == 0; + memset(null_map.data(), is_null, size); + + if (!is_null) { + for (size_t i = 0; i < size; i++) { + c[i] = (double)a[i] / (double)b; + } + } + } + template <typename Result = DecimalV2Value> - static inline DecimalV2Value apply(DecimalV2Value a, DecimalV2Value b, NullMap& null_map, - size_t index) { - null_map[index] = b.is_zero(); - return a / (b.is_zero() ? one : b); + static inline DecimalV2Value apply(DecimalV2Value a, DecimalV2Value b, UInt8& is_null) { + is_null = b.is_zero(); + return a / (is_null ? one : b); } template <typename Result = ResultType> - static inline Result apply(A a, B b, NullMap& null_map, size_t index) { - null_map[index] = b == 0; - return static_cast<Result>(a) / (b + (b == 0)); + static inline Result apply(A a, B b, UInt8& is_null) { + is_null = b == 0; + return static_cast<Result>(a) / (b + is_null); } }; struct NameDivide { static constexpr auto name = "divide"; }; -using FunctionDivide = FunctionBinaryArithmeticToNullType<DivideFloatingImpl, NameDivide>; +using FunctionDivide = FunctionBinaryArithmetic<DivideFloatingImpl, NameDivide, true>; void register_function_divide(SimpleFunctionFactory& factory) { factory.register_function<FunctionDivide>(); diff --git a/be/src/vec/functions/function.h b/be/src/vec/functions/function.h index 0ea494a06b..802ef6fdf4 100644 --- a/be/src/vec/functions/function.h +++ b/be/src/vec/functions/function.h @@ -95,11 +95,6 @@ protected: */ virtual ColumnNumbers get_arguments_that_are_always_constant() const { return {}; } - /** True if function can be called on default arguments (include Nullable's) and won't throw. - * Counterexample: modulo(0, 0) - */ - virtual bool can_be_executed_on_default_arguments() const { return true; } - private: Status default_implementation_for_nulls(FunctionContext* context, Block& block, const ColumnNumbers& args, size_t result, @@ -386,7 +381,6 @@ public: bool use_default_implementation_for_constants() const override { return false; } bool use_default_implementation_for_low_cardinality_columns() const override { return true; } ColumnNumbers get_arguments_that_are_always_constant() const override { return {}; } - bool can_be_executed_on_default_arguments() const override { return true; } bool can_be_executed_on_low_cardinality_dictionary() const override { return is_deterministic_in_scope_of_query(); } @@ -460,9 +454,6 @@ protected: ColumnNumbers get_arguments_that_are_always_constant() const final { return function->get_arguments_that_are_always_constant(); } - bool can_be_executed_on_default_arguments() const override { - return function->can_be_executed_on_default_arguments(); - } private: std::shared_ptr<IFunction> function; diff --git a/be/src/vec/functions/function_binary_arithmetic.h b/be/src/vec/functions/function_binary_arithmetic.h index 744b55a09f..74b7df4260 100644 --- a/be/src/vec/functions/function_binary_arithmetic.h +++ b/be/src/vec/functions/function_binary_arithmetic.h @@ -20,167 +20,210 @@ #pragma once -#include "common/logging.h" +#include "runtime/tuple.h" #include "vec/columns/column_const.h" #include "vec/columns/column_decimal.h" +#include "vec/columns/column_nullable.h" #include "vec/columns/column_vector.h" -#include "vec/common/assert_cast.h" -#include "vec/common/typeid_cast.h" -#include "vec/data_types/data_type.h" -#include "vec/data_types/data_type_decimal.h" -#include "vec/data_types/data_type_number.h" +#include "vec/core/types.h" +#include "vec/data_types/data_type_nullable.h" #include "vec/data_types/number_traits.h" #include "vec/functions/cast_type_to_either.h" #include "vec/functions/function.h" -#include "vec/functions/function_helpers.h" -#include "vec/functions/int_div.h" -#include "vec/utils/util.hpp" namespace doris::vectorized { -/** Arithmetic operations: +, -, *, - * Bitwise operations: |, &, ^, ~. - * Etc. - */ +// Arithmetic operations: +, -, *, |, &, ^, ~ +// need implement apply(a, b) -template <typename A, typename B, typename Op, typename ResultType_ = typename Op::ResultType> +// Arithmetic operations (to null type): /, %, intDiv (integer division), log +// need implement apply(a, b, is_null), apply(array_a, b, null_map) +// apply(array_a, b, null_map) is only used on vector_constant + +// TODO: vector_constant optimization not work on decimal type now + +template <typename, typename> +struct PlusImpl; +template <typename, typename> +struct MinusImpl; +template <typename, typename> +struct MultiplyImpl; +template <typename, typename> +struct DivideFloatingImpl; +template <typename, typename> +struct DivideIntegralImpl; +template <typename, typename> +struct ModuloImpl; + +template <template <typename, typename> typename Operation> +struct OperationTraits { + using T = UInt8; + static constexpr bool is_plus_minus = std::is_same_v<Operation<T, T>, PlusImpl<T, T>> || + std::is_same_v<Operation<T, T>, MinusImpl<T, T>>; + static constexpr bool is_multiply = std::is_same_v<Operation<T, T>, MultiplyImpl<T, T>>; + static constexpr bool is_division = std::is_same_v<Operation<T, T>, DivideFloatingImpl<T, T>> || + std::is_same_v<Operation<T, T>, DivideIntegralImpl<T, T>>; + static constexpr bool allow_decimal = + std::is_same_v<Operation<T, T>, PlusImpl<T, T>> || + std::is_same_v<Operation<T, T>, MinusImpl<T, T>> || + std::is_same_v<Operation<T, T>, MultiplyImpl<T, T>> || + std::is_same_v<Operation<T, T>, ModuloImpl<T, T>> || + std::is_same_v<Operation<T, T>, DivideFloatingImpl<T, T>> || + std::is_same_v<Operation<T, T>, DivideIntegralImpl<T, T>>; + static constexpr bool can_overflow = is_plus_minus || is_multiply; +}; + +template <typename A, typename B, typename Op, typename ResultType = typename Op::ResultType> struct BinaryOperationImplBase { - using ResultType = ResultType_; + using Traits = NumberTraits::BinaryOperatorTraits<A, B>; + using ColumnVectorResult = + std::conditional_t<IsDecimalNumber<ResultType>, ColumnDecimal<ResultType>, + ColumnVector<ResultType>>; - static void NO_INLINE vector_vector(const PaddedPODArray<A>& a, const PaddedPODArray<B>& b, - PaddedPODArray<ResultType>& c) { + static void vector_vector(const PaddedPODArray<A>& a, const PaddedPODArray<B>& b, + PaddedPODArray<ResultType>& c) { size_t size = a.size(); for (size_t i = 0; i < size; ++i) { c[i] = Op::template apply<ResultType>(a[i], b[i]); } } - static void NO_INLINE vector_vector(const PaddedPODArray<A>& a, const PaddedPODArray<B>& b, - PaddedPODArray<ResultType>& c, NullMap& null_map) { + static void vector_vector(const PaddedPODArray<A>& a, const PaddedPODArray<B>& b, + PaddedPODArray<ResultType>& c, PaddedPODArray<UInt8>& null_map) { size_t size = a.size(); for (size_t i = 0; i < size; ++i) { - c[i] = Op::template apply<ResultType>(a[i], b[i], null_map, i); + c[i] = Op::template apply<ResultType>(a[i], b[i], null_map[i]); } } - static void NO_INLINE vector_constant(const PaddedPODArray<A>& a, B b, - PaddedPODArray<ResultType>& c) { + static void vector_constant(const PaddedPODArray<A>& a, B b, PaddedPODArray<ResultType>& c) { size_t size = a.size(); - for (size_t i = 0; i < size; ++i) c[i] = Op::template apply<ResultType>(a[i], b); + for (size_t i = 0; i < size; ++i) { + c[i] = Op::template apply<ResultType>(a[i], b); + } } - static void NO_INLINE constant_vector(A a, const PaddedPODArray<B>& b, - PaddedPODArray<ResultType>& c) { + static void vector_constant(const PaddedPODArray<A>& a, B b, PaddedPODArray<ResultType>& c, + PaddedPODArray<UInt8>& null_map) { + Op::template apply<ResultType>(a, b, c, null_map); + } + + static void constant_vector(A a, const PaddedPODArray<B>& b, PaddedPODArray<ResultType>& c) { size_t size = b.size(); - for (size_t i = 0; i < size; ++i) c[i] = Op::template apply<ResultType>(a, b[i]); + for (size_t i = 0; i < size; ++i) { + c[i] = Op::template apply<ResultType>(a, b[i]); + } + } + + static void constant_vector(A a, const PaddedPODArray<B>& b, PaddedPODArray<ResultType>& c, + PaddedPODArray<UInt8>& null_map) { + size_t size = b.size(); + for (size_t i = 0; i < size; ++i) { + c[i] = Op::template apply<ResultType>(a, b[i], null_map[i]); + } } static ResultType constant_constant(A a, B b) { return Op::template apply<ResultType>(a, b); } + + static ResultType constant_constant(A a, B b, UInt8& is_null) { + return Op::template apply<ResultType>(a, b, is_null); + } }; -template <typename A, typename B, typename Op, typename ResultType = typename Op::ResultType> -struct BinaryOperationImpl : BinaryOperationImplBase<A, B, Op, ResultType> {}; +template <typename A, typename B, typename Op, bool is_to_null_type, + typename ResultType = typename Op::ResultType> +struct BinaryOperationImpl { + using Base = BinaryOperationImplBase<A, B, Op, ResultType>; + + static ColumnPtr adapt_normal_constant_constant(A a, B b) { + auto column_result = Base::ColumnVectorResult::create(1); + + if constexpr (is_to_null_type) { + auto null_map = ColumnUInt8::create(1, 0); + column_result->get_element(0) = Base::constant_constant(a, b, null_map->get_element(0)); + return ColumnNullable::create(std::move(column_result), std::move(null_map)); + } else { + column_result->get_element(0) = Base::constant_constant(a, b); + return column_result; + } + } -template <typename, typename> -struct PlusImpl; -template <typename, typename> -struct MinusImpl; -template <typename, typename> -struct MultiplyImpl; -template <typename, typename> -struct DivideFloatingImpl; -template <typename, typename> -struct DivideIntegralImpl; -template <typename, typename> -struct DivideIntegralOrZeroImpl; -template <typename, typename> -struct LeastBaseImpl; -template <typename, typename> -struct GreatestBaseImpl; -template <typename, typename> -struct ModuloImpl; + static ColumnPtr adapt_normal_vector_constant(ColumnPtr column_left, B b) { + auto column_left_ptr = + check_and_get_column<typename Base::Traits::ColumnVectorA>(column_left); + auto column_result = Base::ColumnVectorResult::create(column_left->size()); + DCHECK(column_left_ptr != nullptr); + + if constexpr (is_to_null_type) { + auto null_map = ColumnUInt8::create(column_left->size(), 0); + Base::vector_constant(column_left_ptr->get_data(), b, column_result->get_data(), + null_map->get_data()); + return ColumnNullable::create(std::move(column_result), std::move(null_map)); + } else { + Base::vector_constant(column_left_ptr->get_data(), b, column_result->get_data()); + return column_result; + } + } + + static ColumnPtr adapt_normal_constant_vector(A a, ColumnPtr column_right) { + auto column_right_ptr = + check_and_get_column<typename Base::Traits::ColumnVectorB>(column_right); + auto column_result = Base::ColumnVectorResult::create(column_right->size()); + DCHECK(column_right_ptr != nullptr); + + if constexpr (is_to_null_type) { + auto null_map = ColumnUInt8::create(column_right->size(), 0); + Base::constant_vector(a, column_right_ptr->get_data(), column_result->get_data(), + null_map->get_data()); + return ColumnNullable::create(std::move(column_result), std::move(null_map)); + } else { + Base::constant_vector(a, column_right_ptr->get_data(), column_result->get_data()); + return column_result; + } + } + + static ColumnPtr adapt_normal_vector_vector(ColumnPtr column_left, ColumnPtr column_right) { + auto column_left_ptr = + check_and_get_column<typename Base::Traits::ColumnVectorA>(column_left); + auto column_right_ptr = + check_and_get_column<typename Base::Traits::ColumnVectorB>(column_right); + + auto column_result = Base::ColumnVectorResult::create(column_left->size()); + DCHECK(column_left_ptr != nullptr && column_right_ptr != nullptr); + + if constexpr (is_to_null_type) { + auto null_map = ColumnUInt8::create(column_result->size(), 0); + Base::vector_vector(column_left_ptr->get_data(), column_right_ptr->get_data(), + column_result->get_data(), null_map->get_data()); + return ColumnNullable::create(std::move(column_result), std::move(null_map)); + } else { + Base::vector_vector(column_left_ptr->get_data(), column_right_ptr->get_data(), + column_result->get_data()); + return column_result; + } + } +}; /// Binary operations for Decimals need scale args /// +|- scale one of args (which scale factor is not 1). ScaleR = oneof(Scale1, Scale2); /// * no agrs scale. ScaleR = Scale1 + Scale2; /// / first arg scale. ScaleR = Scale1 (scale_a = DecimalType<B>::get_scale()). template <typename A, typename B, template <typename, typename> typename Operation, - typename ResultType_, bool _check_overflow = true> + typename ResultType, bool is_to_null_type, bool check_overflow = false> struct DecimalBinaryOperation { - static constexpr bool is_plus_minus = - std::is_same_v<Operation<Int32, Int32>, PlusImpl<Int32, Int32>> || - std::is_same_v<Operation<Int32, Int32>, MinusImpl<Int32, Int32>>; - static constexpr bool is_multiply = - std::is_same_v<Operation<Int32, Int32>, MultiplyImpl<Int32, Int32>>; - static constexpr bool is_float_division = - std::is_same_v<Operation<Int32, Int32>, DivideFloatingImpl<Int32, Int32>>; - static constexpr bool is_int_division = - std::is_same_v<Operation<Int32, Int32>, DivideIntegralImpl<Int32, Int32>> || - std::is_same_v<Operation<Int32, Int32>, DivideIntegralOrZeroImpl<Int32, Int32>>; - static constexpr bool is_division = is_float_division || is_int_division; - static constexpr bool is_compare = - std::is_same_v<Operation<Int32, Int32>, LeastBaseImpl<Int32, Int32>> || - std::is_same_v<Operation<Int32, Int32>, GreatestBaseImpl<Int32, Int32>>; - static constexpr bool is_plus_minus_compare = is_plus_minus || is_compare; - static constexpr bool can_overflow = is_plus_minus || is_multiply; + using OpTraits = OperationTraits<Operation>; - using ResultType = ResultType_; using NativeResultType = typename NativeType<ResultType>::Type; using Op = Operation<NativeResultType, NativeResultType>; - using ColVecA = std::conditional_t<IsDecimalNumber<A>, ColumnDecimal<A>, ColumnVector<A>>; - using ColVecB = std::conditional_t<IsDecimalNumber<B>, ColumnDecimal<B>, ColumnVector<B>>; - using ArrayA = typename ColVecA::Container; - using ArrayB = typename ColVecB::Container; + using Traits = NumberTraits::BinaryOperatorTraits<A, B>; using ArrayC = typename ColumnDecimal<ResultType>::Container; - using SelfNoOverflow = DecimalBinaryOperation<A, B, Operation, ResultType_, false>; - static void vector_vector(const ArrayA& a, const ArrayB& b, ArrayC& c, ResultType scale_a, - ResultType scale_b, bool check_overflow) { - if (check_overflow) - vector_vector(a, b, c, scale_a, scale_b); - else - SelfNoOverflow::vector_vector(a, b, c, scale_a, scale_b); - } - - /// null_map for divide and mod - static void vector_vector(const ArrayA& a, const ArrayB& b, ArrayC& c, ResultType scale_a, - ResultType scale_b, bool check_overflow, NullMap& null_map) { - if (check_overflow) - vector_vector(a, b, c, scale_a, scale_b, null_map); - else - SelfNoOverflow::vector_vector(a, b, c, scale_a, scale_b, null_map); - } - - static void vector_constant(const ArrayA& a, B b, ArrayC& c, ResultType scale_a, - ResultType scale_b, bool check_overflow) { - if (check_overflow) - vector_constant(a, b, c, scale_a, scale_b); - else - SelfNoOverflow::vector_constant(a, b, c, scale_a, scale_b); - } - - static void constant_vector(A a, const ArrayB& b, ArrayC& c, ResultType scale_a, - ResultType scale_b, bool check_overflow) { - if (check_overflow) - constant_vector(a, b, c, scale_a, scale_b); - else - SelfNoOverflow::constant_vector(a, b, c, scale_a, scale_b); - } - - static ResultType constant_constant(A a, B b, ResultType scale_a, ResultType scale_b, - bool check_overflow) { - if (check_overflow) - return constant_constant(a, b, scale_a, scale_b); - else - return SelfNoOverflow::constant_constant(a, b, scale_a, scale_b); - } - - static void NO_INLINE vector_vector(const ArrayA& a, const ArrayB& b, ArrayC& c, - ResultType scale_a [[maybe_unused]], - ResultType scale_b [[maybe_unused]]) { + static void vector_vector(const typename Traits::ArrayA& a, const typename Traits::ArrayB& b, + ArrayC& c, ResultType scale_a [[maybe_unused]], + ResultType scale_b [[maybe_unused]]) { size_t size = a.size(); - if constexpr (is_plus_minus_compare) { + if constexpr (OpTraits::is_plus_minus) { if (scale_a != 1) { for (size_t i = 0; i < size; ++i) { c[i] = apply_scaled<true>(a[i], b[i], scale_a); @@ -201,71 +244,207 @@ struct DecimalBinaryOperation { } /// null_map for divide and mod - static void NO_INLINE vector_vector(const ArrayA& a, const ArrayB& b, ArrayC& c, - ResultType scale_a [[maybe_unused]], - ResultType scale_b [[maybe_unused]], NullMap& null_map) { + static void vector_vector(const typename Traits::ArrayA& a, const typename Traits::ArrayB& b, + ArrayC& c, ResultType scale_a [[maybe_unused]], + ResultType scale_b [[maybe_unused]], NullMap& null_map) { size_t size = a.size(); /// default: use it if no return before for (size_t i = 0; i < size; ++i) { - c[i] = apply(a[i], b[i], null_map, i); + c[i] = apply(a[i], b[i], null_map[i]); } } - static void NO_INLINE vector_constant(const ArrayA& a, B b, ArrayC& c, - ResultType scale_a [[maybe_unused]], - ResultType scale_b [[maybe_unused]]) { + static void vector_constant(const typename Traits::ArrayA& a, B b, ArrayC& c, + ResultType scale_a [[maybe_unused]], + ResultType scale_b [[maybe_unused]]) { size_t size = a.size(); - if constexpr (is_plus_minus_compare) { + if constexpr (OpTraits::is_plus_minus) { if (scale_a != 1) { - for (size_t i = 0; i < size; ++i) c[i] = apply_scaled<true>(a[i], b, scale_a); + for (size_t i = 0; i < size; ++i) { + c[i] = apply_scaled<true>(a[i], b, scale_a); + } return; } else if (scale_b != 1) { - for (size_t i = 0; i < size; ++i) c[i] = apply_scaled<false>(a[i], b, scale_b); + for (size_t i = 0; i < size; ++i) { + c[i] = apply_scaled<false>(a[i], b, scale_b); + } return; } - } else if constexpr (is_division && IsDecimalNumber<B>) { - for (size_t i = 0; i < size; ++i) c[i] = apply_scaled_div(a[i], b, scale_a); + } else if constexpr (OpTraits::is_division && IsDecimalNumber<B>) { + for (size_t i = 0; i < size; ++i) { + c[i] = apply_scaled_div(a[i], b, scale_a); + } return; } /// default: use it if no return before - for (size_t i = 0; i < size; ++i) c[i] = apply(a[i], b); + for (size_t i = 0; i < size; ++i) { + c[i] = apply(a[i], b); + } + } + + static void vector_constant(const typename Traits::ArrayA& a, B b, ArrayC& c, + ResultType scale_a [[maybe_unused]], + ResultType scale_b [[maybe_unused]], NullMap& null_map) { + size_t size = a.size(); + if constexpr (OpTraits::is_division && IsDecimalNumber<B>) { + for (size_t i = 0; i < size; ++i) { + c[i] = apply_scaled_div(a[i], b, scale_a, null_map[i]); + } + return; + } + + for (size_t i = 0; i < size; ++i) { + c[i] = apply(a[i], b, null_map[i]); + } } - static void NO_INLINE constant_vector(A a, const ArrayB& b, ArrayC& c, - ResultType scale_a [[maybe_unused]], - ResultType scale_b [[maybe_unused]]) { + static void constant_vector(A a, const typename Traits::ArrayB& b, ArrayC& c, + ResultType scale_a [[maybe_unused]], + ResultType scale_b [[maybe_unused]]) { size_t size = b.size(); - if constexpr (is_plus_minus_compare) { + if constexpr (OpTraits::is_plus_minus) { if (scale_a != 1) { - for (size_t i = 0; i < size; ++i) c[i] = apply_scaled<true>(a, b[i], scale_a); + for (size_t i = 0; i < size; ++i) { + c[i] = apply_scaled<true>(a, b[i], scale_a); + } return; } else if (scale_b != 1) { - for (size_t i = 0; i < size; ++i) c[i] = apply_scaled<false>(a, b[i], scale_b); + for (size_t i = 0; i < size; ++i) { + c[i] = apply_scaled<false>(a, b[i], scale_b); + } return; } - } else if constexpr (is_division && IsDecimalNumber<B>) { - for (size_t i = 0; i < size; ++i) c[i] = apply_scaled_div(a, b[i], scale_a); + } else if constexpr (OpTraits::is_division && IsDecimalNumber<B>) { + for (size_t i = 0; i < size; ++i) { + c[i] = apply_scaled_div(a, b[i], scale_a); + } return; } /// default: use it if no return before - for (size_t i = 0; i < size; ++i) c[i] = apply(a, b[i]); + for (size_t i = 0; i < size; ++i) { + c[i] = apply(a, b[i]); + } + } + + static void constant_vector(A a, const typename Traits::ArrayB& b, ArrayC& c, + ResultType scale_a [[maybe_unused]], + ResultType scale_b [[maybe_unused]], NullMap& null_map) { + size_t size = b.size(); + if constexpr (OpTraits::is_division && IsDecimalNumber<B>) { + for (size_t i = 0; i < size; ++i) { + c[i] = apply_scaled_div(a, b[i], scale_a, null_map[i]); + } + return; + } + + for (size_t i = 0; i < size; ++i) { + c[i] = apply(a, b[i], null_map[i]); + } } static ResultType constant_constant(A a, B b, ResultType scale_a [[maybe_unused]], ResultType scale_b [[maybe_unused]]) { - if constexpr (is_plus_minus_compare) { - if (scale_a != 1) + if constexpr (OpTraits::is_plus_minus) { + if (scale_a != 1) { return apply_scaled<true>(a, b, scale_a); - else if (scale_b != 1) + } else if (scale_b != 1) { return apply_scaled<false>(a, b, scale_b); - } else if constexpr (is_division && IsDecimalNumber<B>) + } + } else if constexpr (OpTraits::is_division && IsDecimalNumber<B>) { return apply_scaled_div(a, b, scale_a); + } return apply(a, b); } + static ResultType constant_constant(A a, B b, ResultType scale_a [[maybe_unused]], + ResultType scale_b [[maybe_unused]], UInt8& is_null) { + if constexpr (OpTraits::is_plus_minus) { + if (scale_a != 1) { + return apply_scaled<true>(a, b, scale_a, is_null); + } else if (scale_b != 1) { + return apply_scaled<false>(a, b, scale_b, is_null); + } + } else if constexpr (OpTraits::is_division && IsDecimalNumber<B>) { + return apply_scaled_div(a, b, scale_a, is_null); + } + return apply(a, b, is_null); + } + + static ColumnPtr adapt_decimal_constant_constant(A a, B b, UInt32 scale, ResultType scale_a, + ResultType scale_b) { + auto column_result = ColumnDecimal<ResultType>::create(1, scale); + + if constexpr (is_to_null_type) { + auto null_map = ColumnUInt8::create(1, 0); + column_result->get_element(0) = + constant_constant(a, b, scale_a, scale_b, null_map->get_element(0)); + return ColumnNullable::create(std::move(column_result), std::move(null_map)); + } else { + column_result->get_element(0) = constant_constant(a, b, scale_a, scale_b); + return column_result; + } + } + + static ColumnPtr adapt_decimal_vector_constant(ColumnPtr column_left, B b, UInt32 column_scale, + ResultType scale_a, ResultType scale_b) { + auto column_left_ptr = check_and_get_column<typename Traits::ColumnVectorA>(column_left); + auto column_result = ColumnDecimal<ResultType>::create(column_left->size(), column_scale); + DCHECK(column_left_ptr != nullptr); + + if constexpr (is_to_null_type) { + auto null_map = ColumnUInt8::create(column_left->size(), 0); + vector_constant(column_left_ptr->get_data(), b, column_result->get_data(), scale_a, + scale_b, null_map->get_data()); + return ColumnNullable::create(std::move(column_result), std::move(null_map)); + } else { + vector_constant(column_left_ptr->get_data(), b, column_result->get_data(), scale_a, + scale_b); + return column_result; + } + } + + static ColumnPtr adapt_decimal_constant_vector(A a, ColumnPtr column_right, UInt32 column_scale, + ResultType scale_a, ResultType scale_b) { + auto column_right_ptr = check_and_get_column<typename Traits::ColumnVectorB>(column_right); + auto column_result = ColumnDecimal<ResultType>::create(column_right->size(), column_scale); + DCHECK(column_right_ptr != nullptr); + + if constexpr (is_to_null_type) { + auto null_map = ColumnUInt8::create(column_right->size(), 0); + constant_vector(a, column_right_ptr->get_data(), column_result->get_data(), scale_a, + scale_b, null_map->get_data()); + return ColumnNullable::create(std::move(column_result), std::move(null_map)); + } else { + constant_vector(a, column_right_ptr->get_data(), column_result->get_data(), scale_a, + scale_b); + return column_result; + } + } + + static ColumnPtr adapt_decimal_vector_vector(ColumnPtr column_left, ColumnPtr column_right, + UInt32 column_scale, ResultType scale_a, + ResultType scale_b) { + auto column_left_ptr = check_and_get_column<typename Traits::ColumnVectorA>(column_left); + auto column_right_ptr = check_and_get_column<typename Traits::ColumnVectorB>(column_right); + + auto column_result = ColumnDecimal<ResultType>::create(column_left->size(), column_scale); + DCHECK(column_left_ptr != nullptr && column_right_ptr != nullptr); + + if constexpr (is_to_null_type) { + auto null_map = ColumnUInt8::create(column_result->size(), 0); + vector_vector(column_left_ptr->get_data(), column_right_ptr->get_data(), + column_result->get_data(), scale_a, scale_b, null_map->get_data()); + return ColumnNullable::create(std::move(column_result), std::move(null_map)); + } else { + vector_vector(column_left_ptr->get_data(), column_right_ptr->get_data(), + column_result->get_data(), scale_a, scale_b); + return column_result; + } + } + private: /// there's implicit type convertion here static NativeResultType apply(NativeResultType a, NativeResultType b) { @@ -280,11 +459,10 @@ private: } /// null_map for divide and mod - static NativeResultType apply(NativeResultType a, NativeResultType b, NullMap& null_map, - size_t index) { + static NativeResultType apply(NativeResultType a, NativeResultType b, UInt8& is_null) { DecimalV2Value l(a); DecimalV2Value r(b); - auto ans = Op::template apply(l, r, null_map, index); + auto ans = Op::template apply(l, r, is_null); NativeResultType result; memcpy(&result, &ans, std::min(sizeof(result), sizeof(ans))); return result; @@ -293,30 +471,33 @@ private: template <bool scale_left> static NativeResultType apply_scaled(NativeResultType a, NativeResultType b, NativeResultType scale) { - if constexpr (is_plus_minus_compare) { + if constexpr (OpTraits::is_plus_minus) { NativeResultType res; - if constexpr (_check_overflow) { + if constexpr (check_overflow) { bool overflow = false; - if constexpr (scale_left) + if constexpr (scale_left) { overflow |= common::mul_overflow(a, scale, a); - else + } else { overflow |= common::mul_overflow(b, scale, b); + } - if constexpr (can_overflow) + if constexpr (OpTraits::can_overflow) { overflow |= Op::template apply<NativeResultType>(a, b, res); - else + } else { res = Op::template apply<NativeResultType>(a, b); + } if (overflow) { LOG(FATAL) << "Decimal math overflow"; } } else { - if constexpr (scale_left) + if constexpr (scale_left) { a *= scale; - else + } else { b *= scale; - res = Op::template apply<NativeResultType>(a, b); + } + res = apply(a, b); } return res; @@ -324,23 +505,25 @@ private: } static NativeResultType apply_scaled_div(NativeResultType a, NativeResultType b, - NativeResultType scale, NullMap& null_map, - size_t index) { - if constexpr (is_division) { - if constexpr (_check_overflow) { + NativeResultType scale, UInt8& is_null) { + if constexpr (OpTraits::is_division) { + if constexpr (check_overflow) { bool overflow = false; - if constexpr (!IsDecimalNumber<A>) + if constexpr (!IsDecimalNumber<A>) { overflow |= common::mul_overflow(scale, scale, scale); + } overflow |= common::mul_overflow(a, scale, a); if (overflow) { LOG(FATAL) << "Decimal math overflow"; } } else { - if constexpr (!IsDecimalNumber<A>) scale *= scale; + if constexpr (!IsDecimalNumber<A>) { + scale *= scale; + } a *= scale; } - return Op::template apply<NativeResultType>(a, b, null_map, index); + return apply(a, b, is_null); } } }; @@ -362,12 +545,6 @@ constexpr bool IsIntegral = false; template <> inline constexpr bool IsIntegral<DataTypeUInt8> = true; template <> -inline constexpr bool IsIntegral<DataTypeUInt16> = true; -template <> -inline constexpr bool IsIntegral<DataTypeUInt32> = true; -template <> -inline constexpr bool IsIntegral<DataTypeUInt64> = true; -template <> inline constexpr bool IsIntegral<DataTypeInt8> = true; template <> inline constexpr bool IsIntegral<DataTypeInt16> = true; @@ -378,14 +555,7 @@ inline constexpr bool IsIntegral<DataTypeInt64> = true; template <> inline constexpr bool IsIntegral<DataTypeInt128> = true; -template <typename DataType> -constexpr bool IsFloatingPoint = false; -template <> -inline constexpr bool IsFloatingPoint<DataTypeFloat32> = true; -template <> -inline constexpr bool IsFloatingPoint<DataTypeFloat64> = true; - -template <typename T0, typename T1> +template <typename A, typename B> constexpr bool UseLeftDecimal = false; template <> inline constexpr bool UseLeftDecimal<DataTypeDecimal<Decimal128>, DataTypeDecimal<Decimal32>> = @@ -403,29 +573,13 @@ using DataTypeFromFieldType = template <template <typename, typename> class Operation, typename LeftDataType, typename RightDataType> struct BinaryOperationTraits { - using T0 = typename LeftDataType::FieldType; - using T1 = typename RightDataType::FieldType; - -private: /// it's not correct for Decimal - using Op = Operation<T0, T1>; - -public: - static constexpr bool allow_decimal = - std::is_same_v<Operation<T0, T0>, PlusImpl<T0, T0>> || - std::is_same_v<Operation<T0, T0>, MinusImpl<T0, T0>> || - std::is_same_v<Operation<T0, T0>, MultiplyImpl<T0, T0>> || - std::is_same_v<Operation<T0, T0>, ModuloImpl<T0, T0>> || - std::is_same_v<Operation<T0, T0>, DivideFloatingImpl<T0, T0>> || - std::is_same_v<Operation<T0, T0>, DivideIntegralImpl<T0, T0>> || - std::is_same_v<Operation<T0, T0>, DivideIntegralOrZeroImpl<T0, T0>> || - std::is_same_v<Operation<T0, T0>, LeastBaseImpl<T0, T0>> || - std::is_same_v<Operation<T0, T0>, GreatestBaseImpl<T0, T0>>; - + using Op = Operation<typename LeftDataType::FieldType, typename RightDataType::FieldType>; + using OpTraits = OperationTraits<Operation>; /// Appropriate result type for binary operator on numeric types. "Date" can also mean /// DateTime, but if both operands are Dates, their type must be the same (e.g. Date - DateTime is invalid). using ResultDataType = Switch< /// Decimal cases - Case<!allow_decimal && + Case<!OpTraits::allow_decimal && (IsDataTypeDecimal<LeftDataType> || IsDataTypeDecimal<RightDataType>), InvalidType>, Case<IsDataTypeDecimal<LeftDataType> && IsDataTypeDecimal<RightDataType> && @@ -451,20 +605,135 @@ public: DataTypeFromFieldType<typename Op::ResultType>>>; }; -template <template <typename, typename> class Op, typename Name, - bool CanBeExecutedOnDefaultArguments = true> +template <typename LeftDataType, typename RightDataType, + template <typename, typename> class Operation, bool is_to_null_type> +struct ConstOrVectorAdapter { + static constexpr bool result_is_decimal = + IsDataTypeDecimal<LeftDataType> || IsDataTypeDecimal<RightDataType>; + using OpTraits = OperationTraits<Operation>; + + using ResultDataType = + typename BinaryOperationTraits<Operation, LeftDataType, RightDataType>::ResultDataType; + using ResultType = typename ResultDataType::FieldType; + using A = typename LeftDataType::FieldType; + using B = typename RightDataType::FieldType; + + using OperationImpl = std::conditional_t< + IsDataTypeDecimal<ResultDataType>, + DecimalBinaryOperation<A, B, Operation, ResultType, is_to_null_type>, + BinaryOperationImpl<A, B, Operation<A, B>, is_to_null_type, ResultType>>; + + static ColumnPtr execute(ColumnPtr column_left, ColumnPtr column_right, + const LeftDataType& type_left, const RightDataType& type_right) { + bool is_const_left = is_column_const(*column_left); + bool is_const_right = is_column_const(*column_right); + + if (is_const_left && is_const_right) { + return constant_constant(column_left, column_right, type_left, type_right); + } else if (is_const_left) { + return constant_vector(column_left, column_right, type_left, type_right); + } else if (is_const_right) { + return vector_constant(column_left, column_right, type_left, type_right); + } else { + return vector_vector(column_left, column_right, type_left, type_right); + } + } + +private: + static auto get_decimal_infos(const LeftDataType& type_left, const RightDataType& type_right) { + ResultDataType type = decimal_result_type(type_left, type_right, OpTraits::is_multiply, + OpTraits::is_division); + typename ResultDataType::FieldType scale_a = + type.scale_factor_for(type_left, OpTraits::is_multiply); + typename ResultDataType::FieldType scale_b = + type.scale_factor_for(type_right, OpTraits::is_multiply || OpTraits::is_division); + return std::make_tuple(type, scale_a, scale_b); + } + + static ColumnPtr constant_constant(ColumnPtr column_left, ColumnPtr column_right, + const LeftDataType& type_left, + const RightDataType& type_right) { + auto column_left_ptr = check_and_get_column<ColumnConst>(column_left); + auto column_right_ptr = check_and_get_column<ColumnConst>(column_right); + DCHECK(column_left_ptr != nullptr && column_right_ptr != nullptr); + + ColumnPtr column_result = nullptr; + + if constexpr (result_is_decimal) { + auto [type, scale_a, scale_b] = get_decimal_infos(type_left, type_right); + + column_result = OperationImpl::adapt_decimal_constant_constant( + column_left_ptr->template get_value<A>(), + column_right_ptr->template get_value<B>(), type.get_scale(), scale_a, scale_b); + + } else { + column_result = OperationImpl::adapt_normal_constant_constant( + column_left_ptr->template get_value<A>(), + column_right_ptr->template get_value<B>()); + } + + return ColumnConst::create(std::move(column_result), column_left->size()); + } + + static ColumnPtr vector_constant(ColumnPtr column_left, ColumnPtr column_right, + const LeftDataType& type_left, + const RightDataType& type_right) { + auto column_right_ptr = check_and_get_column<ColumnConst>(column_right); + DCHECK(column_right_ptr != nullptr); + + if constexpr (result_is_decimal) { + auto [type, scale_a, scale_b] = get_decimal_infos(type_left, type_right); + + return OperationImpl::adapt_decimal_vector_constant( + column_left->get_ptr(), column_right_ptr->template get_value<B>(), + type.get_scale(), scale_a, scale_b); + } else { + return OperationImpl::adapt_normal_vector_constant( + column_left->get_ptr(), column_right_ptr->template get_value<B>()); + } + } + + static ColumnPtr constant_vector(ColumnPtr column_left, ColumnPtr column_right, + const LeftDataType& type_left, + const RightDataType& type_right) { + auto column_left_ptr = check_and_get_column<ColumnConst>(column_left); + DCHECK(column_left_ptr != nullptr); + + if constexpr (result_is_decimal) { + auto [type, scale_a, scale_b] = get_decimal_infos(type_left, type_right); + + return OperationImpl::adapt_decimal_constant_vector( + column_left_ptr->template get_value<A>(), column_right->get_ptr(), + type.get_scale(), scale_a, scale_b); + } else { + return OperationImpl::adapt_normal_constant_vector( + column_left_ptr->template get_value<A>(), column_right->get_ptr()); + } + } + + static ColumnPtr vector_vector(ColumnPtr column_left, ColumnPtr column_right, + const LeftDataType& type_left, const RightDataType& type_right) { + if constexpr (result_is_decimal) { + auto [type, scale_a, scale_b] = get_decimal_infos(type_left, type_right); + + return OperationImpl::adapt_decimal_vector_vector(column_left->get_ptr(), + column_right->get_ptr(), + type.get_scale(), scale_a, scale_b); + } else { + return OperationImpl::adapt_normal_vector_vector(column_left->get_ptr(), + column_right->get_ptr()); + } + } +}; + +template <template <typename, typename> class Operation, typename Name, bool is_to_null_type> class FunctionBinaryArithmetic : public IFunction { - bool check_decimal_overflow = true; - static constexpr bool has_variadic_argument = - !std::is_void_v<decltype(has_variadic_argument_types(std::declval<Op<int, int>>()))>; + using OpTraits = OperationTraits<Operation>; template <typename F> static bool cast_type(const IDataType* type, F&& f) { - return cast_type_to_either<DataTypeUInt8, DataTypeUInt16, DataTypeUInt32, DataTypeUInt64, - DataTypeInt8, DataTypeInt16, DataTypeInt32, DataTypeInt64, - DataTypeInt128, DataTypeFloat32, DataTypeFloat64, - // DataTypeDate, - // DataTypeDateTime, + return cast_type_to_either<DataTypeUInt8, DataTypeInt8, DataTypeInt16, DataTypeInt32, + DataTypeInt64, DataTypeInt128, DataTypeFloat32, DataTypeFloat64, DataTypeDecimal<Decimal32>, DataTypeDecimal<Decimal64>, DataTypeDecimal<Decimal128>>(type, std::forward<F>(f)); } @@ -476,39 +745,17 @@ class FunctionBinaryArithmetic : public IFunction { }); } - bool is_aggregate_multiply(const DataTypePtr& type0, const DataTypePtr& type1) const { - if constexpr (!std::is_same_v<Op<UInt8, UInt8>, MultiplyImpl<UInt8, UInt8>>) return false; - - WhichDataType which0(type0); - WhichDataType which1(type1); - - return (which0.is_aggregate_function() && which1.is_native_uint()) || - (which0.is_native_uint() && which1.is_aggregate_function()); - } - - bool is_aggregate_addition(const DataTypePtr& type0, const DataTypePtr& type1) const { - if constexpr (!std::is_same_v<Op<UInt8, UInt8>, PlusImpl<UInt8, UInt8>>) return false; - - WhichDataType which0(type0); - WhichDataType which1(type1); - - return which0.is_aggregate_function() && which1.is_aggregate_function(); - } - public: static constexpr auto name = Name::name; + static FunctionPtr create() { return std::make_shared<FunctionBinaryArithmetic>(); } - FunctionBinaryArithmetic() {} + FunctionBinaryArithmetic() = default; + String get_name() const override { return name; } size_t get_number_of_arguments() const override { return 2; } - DataTypes get_variadic_argument_types_impl() const override { - if constexpr (has_variadic_argument) return Op<int, int>::get_variadic_argument_types(); - return {}; - } - DataTypePtr get_return_type_impl(const DataTypes& arguments) const override { DataTypePtr type_res; bool valid = cast_both_types( @@ -516,29 +763,26 @@ public: using LeftDataType = std::decay_t<decltype(left)>; using RightDataType = std::decay_t<decltype(right)>; using ResultDataType = - typename BinaryOperationTraits<Op, LeftDataType, + typename BinaryOperationTraits<Operation, LeftDataType, RightDataType>::ResultDataType; if constexpr (!std::is_same_v<ResultDataType, InvalidType>) { if constexpr (IsDataTypeDecimal<LeftDataType> && IsDataTypeDecimal<RightDataType>) { - constexpr bool is_multiply = - std::is_same_v<Op<UInt8, UInt8>, MultiplyImpl<UInt8, UInt8>>; - constexpr bool is_division = false; - - ResultDataType result_type = - decimal_result_type(left, right, is_multiply, is_division); + ResultDataType result_type = decimal_result_type( + left, right, OpTraits::is_multiply, OpTraits::is_division); type_res = std::make_shared<ResultDataType>(result_type.get_precision(), result_type.get_scale()); - } else if constexpr (IsDataTypeDecimal<LeftDataType>) + } else if constexpr (IsDataTypeDecimal<LeftDataType>) { type_res = std::make_shared<LeftDataType>(left.get_precision(), left.get_scale()); - else if constexpr (IsDataTypeDecimal<RightDataType>) + } else if constexpr (IsDataTypeDecimal<RightDataType>) { type_res = std::make_shared<RightDataType>(right.get_precision(), right.get_scale()); - else if constexpr (IsDataTypeDecimal<ResultDataType>) + } else if constexpr (IsDataTypeDecimal<ResultDataType>) { type_res = std::make_shared<ResultDataType>(27, 9); - else + } else { type_res = std::make_shared<ResultDataType>(); + } return true; } return false; @@ -549,6 +793,10 @@ public: get_name()); } + if constexpr (is_to_null_type) { + return make_nullable(type_res); + } + return type_res; } @@ -556,150 +804,29 @@ public: size_t result, size_t input_rows_count) override { auto* left_generic = block.get_by_position(arguments[0]).type.get(); auto* right_generic = block.get_by_position(arguments[1]).type.get(); + if (left_generic->is_nullable()) { + left_generic = + static_cast<const DataTypeNullable*>(left_generic)->get_nested_type().get(); + } + if (right_generic->is_nullable()) { + right_generic = + static_cast<const DataTypeNullable*>(right_generic)->get_nested_type().get(); + } + bool valid = cast_both_types( left_generic, right_generic, [&](const auto& left, const auto& right) { using LeftDataType = std::decay_t<decltype(left)>; using RightDataType = std::decay_t<decltype(right)>; using ResultDataType = - typename BinaryOperationTraits<Op, LeftDataType, + typename BinaryOperationTraits<Operation, LeftDataType, RightDataType>::ResultDataType; - if constexpr (!std::is_same_v<ResultDataType, InvalidType>) { - constexpr bool result_is_decimal = - IsDataTypeDecimal<LeftDataType> || IsDataTypeDecimal<RightDataType>; - constexpr bool is_multiply = - std::is_same_v<Op<UInt8, UInt8>, MultiplyImpl<UInt8, UInt8>>; - constexpr bool is_division = false; - - using T0 = typename LeftDataType::FieldType; - using T1 = typename RightDataType::FieldType; - using ResultType = typename ResultDataType::FieldType; - using ColVecT0 = std::conditional_t<IsDecimalNumber<T0>, ColumnDecimal<T0>, - ColumnVector<T0>>; - using ColVecT1 = std::conditional_t<IsDecimalNumber<T1>, ColumnDecimal<T1>, - ColumnVector<T1>>; - using ColVecResult = std::conditional_t<IsDecimalNumber<ResultType>, - ColumnDecimal<ResultType>, - ColumnVector<ResultType>>; - - /// Decimal operations need scale. Operations are on result type. - using OpImpl = std::conditional_t< - IsDataTypeDecimal<ResultDataType>, - DecimalBinaryOperation<T0, T1, Op, ResultType>, - BinaryOperationImpl<T0, T1, Op<T0, T1>, ResultType>>; - - auto col_left_raw = block.get_by_position(arguments[0]).column.get(); - auto col_right_raw = block.get_by_position(arguments[1]).column.get(); - if (auto col_left = check_and_get_column_const<ColVecT0>(col_left_raw)) { - if (auto col_right = - check_and_get_column_const<ColVecT1>(col_right_raw)) { - /// the only case with a non-vector result - if constexpr (result_is_decimal) { - ResultDataType type = decimal_result_type( - left, right, is_multiply, is_division); - typename ResultDataType::FieldType scale_a = - type.scale_factor_for(left, is_multiply); - typename ResultDataType::FieldType scale_b = - type.scale_factor_for(right, - is_multiply || is_division); - - auto res = OpImpl::constant_constant( - col_left->template get_value<T0>(), - col_right->template get_value<T1>(), scale_a, scale_b, - check_decimal_overflow); - block.get_by_position(result).column = - ResultDataType(type.get_precision(), type.get_scale()) - .create_column_const( - col_left->size(), - to_field(res, type.get_scale())); - - } else { - auto res = OpImpl::constant_constant( - col_left->template get_value<T0>(), - col_right->template get_value<T1>()); - block.get_by_position(result).column = - ResultDataType().create_column_const(col_left->size(), - to_field(res)); - } - return true; - } - } - typename ColVecResult::MutablePtr col_res = nullptr; - if constexpr (result_is_decimal) { - ResultDataType type = - decimal_result_type(left, right, is_multiply, is_division); - col_res = ColVecResult::create(0, type.get_scale()); - } else - col_res = ColVecResult::create(); - - auto& vec_res = col_res->get_data(); - vec_res.resize(block.rows()); - - if (auto col_left_const = - check_and_get_column_const<ColVecT0>(col_left_raw)) { - if (auto col_right = check_and_get_column<ColVecT1>(col_right_raw)) { - if constexpr (result_is_decimal) { - ResultDataType type = decimal_result_type( - left, right, is_multiply, is_division); - - typename ResultDataType::FieldType scale_a = - type.scale_factor_for(left, is_multiply); - typename ResultDataType::FieldType scale_b = - type.scale_factor_for(right, - is_multiply || is_division); - if constexpr (IsDataTypeDecimal<RightDataType> && is_division) - scale_a = right.get_scale_multiplier(); - - OpImpl::constant_vector( - col_left_const->template get_value<T0>(), - col_right->get_data(), vec_res, scale_a, scale_b, - check_decimal_overflow); - } else - OpImpl::constant_vector( - col_left_const->template get_value<T0>(), - col_right->get_data(), vec_res); - } else - return false; - } else if (auto col_left = check_and_get_column<ColVecT0>(col_left_raw)) { - if constexpr (result_is_decimal) { - ResultDataType type = - decimal_result_type(left, right, is_multiply, is_division); - - typename ResultDataType::FieldType scale_a = - type.scale_factor_for(left, is_multiply); - typename ResultDataType::FieldType scale_b = - type.scale_factor_for(right, is_multiply || is_division); - if (auto col_right = - check_and_get_column<ColVecT1>(col_right_raw)) { - OpImpl::vector_vector(col_left->get_data(), - col_right->get_data(), vec_res, scale_a, - scale_b, check_decimal_overflow); - } else if (auto col_right_const = - check_and_get_column_const<ColVecT1>( - col_right_raw)) { - OpImpl::vector_constant( - col_left->get_data(), - col_right_const->template get_value<T1>(), vec_res, - scale_a, scale_b, check_decimal_overflow); - } else - return false; - } else { - if (auto col_right = check_and_get_column<ColVecT1>(col_right_raw)) - OpImpl::vector_vector(col_left->get_data(), - col_right->get_data(), vec_res); - else if (auto col_right_const = - check_and_get_column_const<ColVecT1>( - col_right_raw)) - OpImpl::vector_constant( - col_left->get_data(), - col_right_const->template get_value<T1>(), vec_res); - else - return false; - } - } else - return false; - - block.replace_by_position(result, std::move(col_res)); + if constexpr (!std::is_same_v<ResultDataType, InvalidType>) { + auto column_result = ConstOrVectorAdapter<LeftDataType, RightDataType, + Operation, is_to_null_type>:: + execute(block.get_by_position(arguments[0]).column, + block.get_by_position(arguments[1]).column, left, right); + block.replace_by_position(result, std::move(column_result)); return true; } return false; @@ -711,10 +838,6 @@ public: return Status::OK(); } - - bool can_be_executed_on_default_arguments() const override { - return CanBeExecutedOnDefaultArguments; - } }; } // namespace doris::vectorized diff --git a/be/src/vec/functions/function_binary_arithmetic_to_null_type.h b/be/src/vec/functions/function_binary_arithmetic_to_null_type.h deleted file mode 100644 index 66316116ae..0000000000 --- a/be/src/vec/functions/function_binary_arithmetic_to_null_type.h +++ /dev/null @@ -1,247 +0,0 @@ -// 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. - -#pragma once - -#include "vec/functions/function_binary_arithmetic.h" - -namespace doris::vectorized { - -/** - * Arithmetic operations: /, % - * intDiv (integer division) - */ - -template <template <typename, typename> class Op, typename Name, - bool CanBeExecutedOnDefaultArguments = true> -class FunctionBinaryArithmeticToNullType : public IFunction { - bool check_decimal_overflow = true; - - template <typename F> - static bool cast_type(const IDataType* type, F&& f) { - return cast_type_to_either<DataTypeUInt8, DataTypeUInt16, DataTypeUInt32, DataTypeUInt64, - DataTypeInt8, DataTypeInt16, DataTypeInt32, DataTypeInt64, - DataTypeInt128, DataTypeFloat32, DataTypeFloat64, - DataTypeDecimal<Decimal32>, DataTypeDecimal<Decimal64>, - DataTypeDecimal<Decimal128>>(type, std::forward<F>(f)); - } - - template <typename F> - static bool cast_both_types(const IDataType* left, const IDataType* right, F&& f) { - return cast_type(left, [&](const auto& left_) { - return cast_type(right, [&](const auto& right_) { return f(left_, right_); }); - }); - } - -public: - static constexpr auto name = Name::name; - static FunctionPtr create() { return std::make_shared<FunctionBinaryArithmeticToNullType>(); } - - FunctionBinaryArithmeticToNullType() {} - String get_name() const override { return name; } - - size_t get_number_of_arguments() const override { return 2; } - - DataTypePtr get_return_type_impl(const DataTypes& arguments) const override { - DataTypePtr type_res; - - const IDataType* first_type = arguments[0].get(); - const IDataType* secord_type = arguments[1].get(); - if (first_type->is_nullable()) { - first_type = static_cast<const DataTypeNullable*>(first_type)->get_nested_type().get(); - } - if (secord_type->is_nullable()) { - secord_type = - static_cast<const DataTypeNullable*>(secord_type)->get_nested_type().get(); - } - - bool valid = - cast_both_types(first_type, secord_type, [&](const auto& left, const auto& right) { - using LeftDataType = std::decay_t<decltype(left)>; - using RightDataType = std::decay_t<decltype(right)>; - using ResultDataType = - typename BinaryOperationTraits<Op, LeftDataType, - RightDataType>::ResultDataType; - if constexpr (!std::is_same_v<ResultDataType, InvalidType>) { - if constexpr (IsDataTypeDecimal<LeftDataType> && - IsDataTypeDecimal<RightDataType>) { - constexpr bool is_multiply = false; - constexpr bool is_division = - std::is_same_v<Op<UInt8, UInt8>, - DivideFloatingImpl<UInt8, UInt8>> || - std::is_same_v<Op<UInt8, UInt8>, - DivideIntegralImpl<UInt8, UInt8>> || - std::is_same_v<Op<UInt8, UInt8>, - DivideIntegralOrZeroImpl<UInt8, UInt8>>; - - ResultDataType result_type = - decimal_result_type(left, right, is_multiply, is_division); - type_res = std::make_shared<ResultDataType>(result_type.get_precision(), - result_type.get_scale()); - } else if constexpr (IsDataTypeDecimal<LeftDataType>) - type_res = std::make_shared<LeftDataType>(left.get_precision(), - left.get_scale()); - else if constexpr (IsDataTypeDecimal<RightDataType>) - type_res = std::make_shared<RightDataType>(right.get_precision(), - right.get_scale()); - else if constexpr (IsDataTypeDecimal<ResultDataType>) - type_res = std::make_shared<ResultDataType>(27, 9); - else - type_res = std::make_shared<ResultDataType>(); - return true; - } - return false; - }); - if (!valid) { - LOG(FATAL) << fmt::format("Illegal types {} and {} of arguments of function {}", - arguments[0]->get_name(), arguments[1]->get_name(), - get_name()); - } - - return make_nullable(type_res); - } - - bool use_default_implementation_for_nulls() const override { return true; } - - Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments, - size_t result, size_t input_rows_count) override { - auto* left_generic = block.get_by_position(arguments[0]).type.get(); - auto* right_generic = block.get_by_position(arguments[1]).type.get(); - if (left_generic->is_nullable()) { - left_generic = - static_cast<const DataTypeNullable*>(left_generic)->get_nested_type().get(); - } - if (right_generic->is_nullable()) { - right_generic = - static_cast<const DataTypeNullable*>(right_generic)->get_nested_type().get(); - } - - bool valid = cast_both_types( - left_generic, right_generic, [&](const auto& left, const auto& right) { - using LeftDataType = std::decay_t<decltype(left)>; - using RightDataType = std::decay_t<decltype(right)>; - using ResultDataType = - typename BinaryOperationTraits<Op, LeftDataType, - RightDataType>::ResultDataType; - - if constexpr (!std::is_same_v<ResultDataType, InvalidType>) { - constexpr bool result_is_decimal = - IsDataTypeDecimal<LeftDataType> || IsDataTypeDecimal<RightDataType>; - constexpr bool is_multiply = false; - constexpr bool is_division = - std::is_same_v<Op<UInt8, UInt8>, - DivideFloatingImpl<UInt8, UInt8>> || - std::is_same_v<Op<UInt8, UInt8>, - DivideIntegralImpl<UInt8, UInt8>> || - std::is_same_v<Op<UInt8, UInt8>, - DivideIntegralOrZeroImpl<UInt8, UInt8>>; - - using T0 = typename LeftDataType::FieldType; - using T1 = typename RightDataType::FieldType; - using ResultType = typename ResultDataType::FieldType; - using ColVecT0 = std::conditional_t<IsDecimalNumber<T0>, ColumnDecimal<T0>, - ColumnVector<T0>>; - using ColVecT1 = std::conditional_t<IsDecimalNumber<T1>, ColumnDecimal<T1>, - ColumnVector<T1>>; - using ColVecResult = std::conditional_t<IsDecimalNumber<ResultType>, - ColumnDecimal<ResultType>, - ColumnVector<ResultType>>; - - /// Decimal operations need scale. Operations are on result type. - using OpImpl = std::conditional_t< - IsDataTypeDecimal<ResultDataType>, - DecimalBinaryOperation<T0, T1, Op, ResultType>, - BinaryOperationImpl<T0, T1, Op<T0, T1>, ResultType>>; - - auto null_map = ColumnUInt8::create(input_rows_count, 0); - auto& null_map_data = null_map->get_data(); - size_t argument_size = arguments.size(); - ColumnPtr argument_columns[argument_size]; - - for (size_t i = 0; i < argument_size; ++i) { - argument_columns[i] = - block.get_by_position(arguments[i]) - .column->convert_to_full_column_if_const(); - } - - auto col_left_raw = argument_columns[0].get(); - auto col_right_raw = argument_columns[1].get(); - - typename ColVecResult::MutablePtr col_res = nullptr; - if constexpr (result_is_decimal) { - ResultDataType type = - decimal_result_type(left, right, is_multiply, is_division); - col_res = ColVecResult::create(0, type.get_scale()); - } else { - col_res = ColVecResult::create(); - } - - auto& vec_res = col_res->get_data(); - vec_res.resize(block.rows()); - - if (auto col_left = check_and_get_column<ColVecT0>(col_left_raw)) { - if constexpr (result_is_decimal) { - ResultDataType type = - decimal_result_type(left, right, is_multiply, is_division); - - typename ResultDataType::FieldType scale_a = - type.scale_factor_for(left, is_multiply); - typename ResultDataType::FieldType scale_b = - type.scale_factor_for(right, is_multiply || is_division); - if constexpr (IsDataTypeDecimal<RightDataType> && is_division) - scale_a = right.get_scale_multiplier(); - if (auto col_right = - check_and_get_column<ColVecT1>(col_right_raw)) { - OpImpl::vector_vector(col_left->get_data(), - col_right->get_data(), vec_res, scale_a, - scale_b, check_decimal_overflow, - null_map_data); - } - } else { - if (auto col_right = - check_and_get_column<ColVecT1>(col_right_raw)) { - OpImpl::vector_vector(col_left->get_data(), - col_right->get_data(), vec_res, - null_map_data); - } - } - } else { - return false; - } - - block.get_by_position(result).column = - ColumnNullable::create(std::move(col_res), std::move(null_map)); - return true; - } else { - return false; - } - }); - - if (!valid) { - return Status::RuntimeError( - fmt::format("{}'s arguments do not match the expected data types", get_name())); - } - - return Status::OK(); - } - - bool can_be_executed_on_default_arguments() const override { - return CanBeExecutedOnDefaultArguments; - } -}; - -} // namespace doris::vectorized diff --git a/be/src/vec/functions/function_bit.cpp b/be/src/vec/functions/function_bit.cpp index 0f4fb87d27..7af28601a0 100644 --- a/be/src/vec/functions/function_bit.cpp +++ b/be/src/vec/functions/function_bit.cpp @@ -20,9 +20,9 @@ #include "vec/data_types/number_traits.h" #include "vec/functions/function_binary_arithmetic.h" +#include "vec/functions/function_totype.h" #include "vec/functions/function_unary_arithmetic.h" #include "vec/functions/simple_function_factory.h" -#include "vec/functions/function_totype.h" namespace doris::vectorized { @@ -101,10 +101,10 @@ struct BitLengthImpl { } }; -using FunctionBitAnd = FunctionBinaryArithmetic<BitAndImpl, NameBitAnd>; +using FunctionBitAnd = FunctionBinaryArithmetic<BitAndImpl, NameBitAnd, false>; using FunctionBitNot = FunctionUnaryArithmetic<BitNotImpl, NameBitNot, false>; -using FunctionBitOr = FunctionBinaryArithmetic<BitOrImpl, NameBitOr>; -using FunctionBitXor = FunctionBinaryArithmetic<BitXorImpl, NameBitXor>; +using FunctionBitOr = FunctionBinaryArithmetic<BitOrImpl, NameBitOr, false>; +using FunctionBitXor = FunctionBinaryArithmetic<BitXorImpl, NameBitXor, false>; using FunctionBitLength = FunctionUnaryToType<BitLengthImpl, NameBitLength>; void register_function_bit(SimpleFunctionFactory& factory) { diff --git a/be/src/vec/functions/function_cast.h b/be/src/vec/functions/function_cast.h index 144b782554..092842f5fa 100644 --- a/be/src/vec/functions/function_cast.h +++ b/be/src/vec/functions/function_cast.h @@ -518,7 +518,6 @@ public: bool use_default_implementation_for_constants() const override { return true; } ColumnNumbers get_arguments_that_are_always_constant() const override { return {1}; } - bool can_be_executed_on_default_arguments() const override { return false; } Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments, size_t result, size_t input_rows_count) override { diff --git a/be/src/vec/functions/int_div.cpp b/be/src/vec/functions/int_div.cpp index 6dd50d44f8..59b2d1c2dc 100644 --- a/be/src/vec/functions/int_div.cpp +++ b/be/src/vec/functions/int_div.cpp @@ -18,133 +18,17 @@ // https://github.com/ClickHouse/ClickHouse/blob/master/src/Functions/IntDiv.cpp // and modified by Doris -#ifdef __SSE2__ -#define LIBDIVIDE_SSE2 1 -#endif - #include "vec/functions/int_div.h" -#include <libdivide.h> - #include "vec/functions/function_binary_arithmetic.h" -#include "vec/functions/function_binary_arithmetic_to_null_type.h" #include "vec/functions/simple_function_factory.h" namespace doris::vectorized { -/// Optimizations for integer division by a constant. - -template <typename A, typename B> -struct DivideIntegralByConstantImpl : BinaryOperationImplBase<A, B, DivideIntegralImpl<A, B>> { - using ResultType = typename DivideIntegralImpl<A, B>::ResultType; - - static void vector_constant(const PaddedPODArray<A>& a, B b, PaddedPODArray<ResultType>& c) { - // TODO: Support return null in the furture - if (UNLIKELY(b == 0)) { - // throw Exception("Division by zero", TStatusCode::VEC_ILLEGAL_DIVISION); - memset(c.data(), 0, sizeof(ResultType) * c.size()); - return; - } - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wsign-compare" - - if (UNLIKELY(std::is_signed_v<B> && b == -1)) { - size_t size = a.size(); - for (size_t i = 0; i < size; ++i) c[i] = -c[i]; - return; - } - -#pragma GCC diagnostic pop - - libdivide::divider<A> divider(b); - - size_t size = a.size(); - const A* a_pos = a.data(); - const A* a_end = a_pos + size; - ResultType* c_pos = c.data(); - -#ifdef __SSE2__ - static constexpr size_t values_per_sse_register = 16 / sizeof(A); - const A* a_end_sse = a_pos + size / values_per_sse_register * values_per_sse_register; - - while (a_pos < a_end_sse) { - _mm_storeu_si128(reinterpret_cast<__m128i*>(c_pos), - _mm_loadu_si128(reinterpret_cast<const __m128i*>(a_pos)) / divider); - - a_pos += values_per_sse_register; - c_pos += values_per_sse_register; - } -#endif - - while (a_pos < a_end) { - *c_pos = *a_pos / divider; - ++a_pos; - ++c_pos; - } - } -}; - -/** Specializations are specified for dividing numbers of the type UInt64 and UInt32 by the numbers of the same sign. - * Can be expanded to all possible combinations, but more code is needed. - */ - -template <> -struct BinaryOperationImpl<UInt64, UInt8, DivideIntegralImpl<UInt64, UInt8>> - : DivideIntegralByConstantImpl<UInt64, UInt8> {}; -template <> -struct BinaryOperationImpl<UInt64, UInt16, DivideIntegralImpl<UInt64, UInt16>> - : DivideIntegralByConstantImpl<UInt64, UInt16> {}; -template <> -struct BinaryOperationImpl<UInt64, UInt32, DivideIntegralImpl<UInt64, UInt32>> - : DivideIntegralByConstantImpl<UInt64, UInt32> {}; -template <> -struct BinaryOperationImpl<UInt64, UInt64, DivideIntegralImpl<UInt64, UInt64>> - : DivideIntegralByConstantImpl<UInt64, UInt64> {}; - -template <> -struct BinaryOperationImpl<UInt32, UInt8, DivideIntegralImpl<UInt32, UInt8>> - : DivideIntegralByConstantImpl<UInt32, UInt8> {}; -template <> -struct BinaryOperationImpl<UInt32, UInt16, DivideIntegralImpl<UInt32, UInt16>> - : DivideIntegralByConstantImpl<UInt32, UInt16> {}; -template <> -struct BinaryOperationImpl<UInt32, UInt32, DivideIntegralImpl<UInt32, UInt32>> - : DivideIntegralByConstantImpl<UInt32, UInt32> {}; -template <> -struct BinaryOperationImpl<UInt32, UInt64, DivideIntegralImpl<UInt32, UInt64>> - : DivideIntegralByConstantImpl<UInt32, UInt64> {}; - -template <> -struct BinaryOperationImpl<Int64, Int8, DivideIntegralImpl<Int64, Int8>> - : DivideIntegralByConstantImpl<Int64, Int8> {}; -template <> -struct BinaryOperationImpl<Int64, Int16, DivideIntegralImpl<Int64, Int16>> - : DivideIntegralByConstantImpl<Int64, Int16> {}; -template <> -struct BinaryOperationImpl<Int64, Int32, DivideIntegralImpl<Int64, Int32>> - : DivideIntegralByConstantImpl<Int64, Int32> {}; -template <> -struct BinaryOperationImpl<Int64, Int64, DivideIntegralImpl<Int64, Int64>> - : DivideIntegralByConstantImpl<Int64, Int64> {}; - -template <> -struct BinaryOperationImpl<Int32, Int8, DivideIntegralImpl<Int32, Int8>> - : DivideIntegralByConstantImpl<Int32, Int8> {}; -template <> -struct BinaryOperationImpl<Int32, Int16, DivideIntegralImpl<Int32, Int16>> - : DivideIntegralByConstantImpl<Int32, Int16> {}; -template <> -struct BinaryOperationImpl<Int32, Int32, DivideIntegralImpl<Int32, Int32>> - : DivideIntegralByConstantImpl<Int32, Int32> {}; -template <> -struct BinaryOperationImpl<Int32, Int64, DivideIntegralImpl<Int32, Int64>> - : DivideIntegralByConstantImpl<Int32, Int64> {}; - struct NameIntDiv { static constexpr auto name = "int_divide"; }; -using FunctionIntDiv = FunctionBinaryArithmeticToNullType<DivideIntegralImpl, NameIntDiv, false>; +using FunctionIntDiv = FunctionBinaryArithmetic<DivideIntegralImpl, NameIntDiv, true>; void register_function_int_div(SimpleFunctionFactory& factory) { factory.register_function<FunctionIntDiv>(); diff --git a/be/src/vec/functions/int_div.h b/be/src/vec/functions/int_div.h index 1bfebcb538..2357eb5af1 100644 --- a/be/src/vec/functions/int_div.h +++ b/be/src/vec/functions/int_div.h @@ -20,19 +20,49 @@ #pragma once +#include <libdivide.h> + +#include <type_traits> + +#include "vec/columns/column_decimal.h" #include "vec/columns/column_nullable.h" +#include "vec/core/types.h" #include "vec/data_types/number_traits.h" +#include "vec/functions/function_binary_arithmetic.h" namespace doris::vectorized { template <typename A, typename B> struct DivideIntegralImpl { using ResultType = typename NumberTraits::ResultOfIntegerDivision<A, B>::Type; + using Traits = NumberTraits::BinaryOperatorTraits<A, B>; + + template <typename Result = ResultType> + static void apply(const typename Traits::ArrayA& a, B b, + typename ColumnVector<Result>::Container& c, + typename Traits::ArrayNull& null_map) { + size_t size = c.size(); + UInt8 is_null = b == 0; + memset(null_map.data(), is_null, size); + + if (!is_null) { + if constexpr (!std::is_floating_point_v<A> && !std::is_same_v<A, Int128> && + !std::is_same_v<A, Int8> && !std::is_same_v<A, UInt8>) { + for (size_t i = 0; i < size; i++) { + c[i] = a[i] / libdivide::divider<A>(b); + } + } else { + for (size_t i = 0; i < size; i++) { + c[i] = a[i] / b; + } + } + } + } template <typename Result = ResultType> - static inline Result apply(A a, B b, NullMap& null_map, size_t index) { - null_map[index] = b == 0; - return a / (b + null_map[index]); + static inline Result apply(A a, B b, UInt8& is_null) { + is_null = b == 0; + return a / (b + is_null); } }; diff --git a/be/src/vec/functions/math.cpp b/be/src/vec/functions/math.cpp index 1aedd83187..1e6952e1f6 100644 --- a/be/src/vec/functions/math.cpp +++ b/be/src/vec/functions/math.cpp @@ -15,10 +15,11 @@ // specific language governing permissions and limitations // under the License. +#include "vec/core/types.h" #include "vec/data_types/number_traits.h" #include "vec/functions/function_const.h" #include "vec/functions/function_binary_arithmetic.h" -#include "vec/functions/function_binary_arithmetic_to_null_type.h" +#include "vec/functions/function_binary_arithmetic.h" #include "vec/functions/function_math_unary.h" #include "vec/functions/function_math_unary_to_null_type.h" #include "vec/functions/function_string.h" @@ -150,17 +151,39 @@ struct LogName { template <typename A, typename B> struct LogImpl { using ResultType = Float64; + using Traits = NumberTraits::BinaryOperatorTraits<A, B>; + static const constexpr bool allow_decimal = false; + static constexpr double EPSILON = 1e-9; + + template <typename Result = ResultType> + static void apply(const typename Traits::ArrayA& a, B b, + typename ColumnVector<Result>::Container& c, + typename Traits::ArrayNull& null_map) { + size_t size = c.size(); + UInt8 is_null = b <= 0; + memset(null_map.data(), is_null, size); + + if (!is_null) { + for (size_t i = 0; i < size; i++) { + if (a[i] <= 0 || std::fabs(a[i] - 1.0) < EPSILON) { + null_map[i] = 1; + } else { + c[i] = static_cast<Float64>(std::log(static_cast<Float64>(b)) / + std::log(static_cast<Float64>(a[i]))); + } + } + } + } template <typename Result> - static inline Result apply(A a, B b, NullMap& null_map, size_t index) { - constexpr double EPSILON = 1e-9; - null_map[index] = a <= 0 || b <= 0 || std::fabs(a - 1.0) < EPSILON; + static inline Result apply(A a, B b, UInt8& is_null) { + is_null = a <= 0 || b <= 0 || std::fabs(a - 1.0) < EPSILON; return static_cast<Float64>(std::log(static_cast<Float64>(b)) / std::log(static_cast<Float64>(a))); } }; -using FunctionLog = FunctionBinaryArithmeticToNullType<LogImpl, LogName>; +using FunctionLog = FunctionBinaryArithmetic<LogImpl, LogName, true>; struct CeilName { static constexpr auto name = "ceil"; @@ -357,7 +380,7 @@ struct PowImpl { struct PowName { static constexpr auto name = "pow"; }; -using FunctionPow = FunctionBinaryArithmetic<PowImpl, PowName>; +using FunctionPow = FunctionBinaryArithmetic<PowImpl, PowName, false>; template <typename A, typename B> struct TruncateImpl { @@ -374,7 +397,7 @@ struct TruncateImpl { struct TruncateName { static constexpr auto name = "truncate"; }; -using FunctionTruncate = FunctionBinaryArithmetic<TruncateImpl, TruncateName>; +using FunctionTruncate = FunctionBinaryArithmetic<TruncateImpl, TruncateName, false>; /// round(double,int32)-->double /// key_str:roundFloat64Int32 @@ -395,7 +418,7 @@ struct RoundTwoImpl { my_double_round(static_cast<Float64>(a), static_cast<Int32>(b), false, false)); } }; -using FunctionRoundTwo = FunctionBinaryArithmetic<RoundTwoImpl, RoundName>; +using FunctionRoundTwo = FunctionBinaryArithmetic<RoundTwoImpl, RoundName, false>; // TODO: Now math may cause one thread compile time too long, because the function in math // so mush. Split it to speed up compile time in the future diff --git a/be/src/vec/functions/minus.cpp b/be/src/vec/functions/minus.cpp index 52b86a1085..e282935729 100644 --- a/be/src/vec/functions/minus.cpp +++ b/be/src/vec/functions/minus.cpp @@ -49,7 +49,7 @@ struct MinusImpl { struct NameMinus { static constexpr auto name = "subtract"; }; -using FunctionMinus = FunctionBinaryArithmetic<MinusImpl, NameMinus>; +using FunctionMinus = FunctionBinaryArithmetic<MinusImpl, NameMinus, false>; void register_function_minus(SimpleFunctionFactory& factory) { factory.register_function<FunctionMinus>(); diff --git a/be/src/vec/functions/modulo.cpp b/be/src/vec/functions/modulo.cpp index 28b0ec768d..599790d0d4 100644 --- a/be/src/vec/functions/modulo.cpp +++ b/be/src/vec/functions/modulo.cpp @@ -18,16 +18,12 @@ // https://github.com/ClickHouse/ClickHouse/blob/master/src/Functions/Modulo.cpp // and modified by Doris -#include "runtime/decimalv2_value.h" -#ifdef __SSE2__ -#define LIBDIVIDE_SSE2 1 -#endif - #include <libdivide.h> #include "common/status.h" +#include "runtime/decimalv2_value.h" +#include "vec/core/types.h" #include "vec/functions/function_binary_arithmetic.h" -#include "vec/functions/function_binary_arithmetic_to_null_type.h" #include "vec/functions/simple_function_factory.h" namespace doris::vectorized { @@ -35,144 +31,90 @@ namespace doris::vectorized { template <typename A, typename B> struct ModuloImpl { using ResultType = typename NumberTraits::ResultOfModulo<A, B>::Type; + using Traits = NumberTraits::BinaryOperatorTraits<A, B>; + + template <typename Result = ResultType> + static void apply(const typename Traits::ArrayA& a, B b, + typename ColumnVector<Result>::Container& c, + typename Traits::ArrayNull& null_map) { + size_t size = c.size(); + UInt8 is_null = b == 0; + memset(null_map.data(), is_null, sizeof(UInt8) * size); + + if (!is_null) { + for (size_t i = 0; i < size; i++) { + if constexpr (std::is_floating_point_v<ResultType>) { + c[i] = std::fmod((double)a[i], (double)b); + } else { + c[i] = a[i] % b; + } + } + } + } template <typename Result = ResultType> - static inline Result apply(A a, B b, NullMap& null_map, size_t index) { + static inline Result apply(A a, B b, UInt8& is_null) { + is_null = b == 0; + b += is_null; + if constexpr (std::is_floating_point_v<Result>) { - null_map[index] = b == 0; return std::fmod((double)a, (double)b); } else { - null_map[index] = b == 0; - return typename NumberTraits::ToInteger<A>::Type(a) % - (typename NumberTraits::ToInteger<B>::Type(b) + (b == 0)); + return a % b; } } template <typename Result = DecimalV2Value> - static inline DecimalV2Value apply(DecimalV2Value a, DecimalV2Value b, NullMap& null_map, - size_t index) { - null_map[index] = b == DecimalV2Value(0); - return a % (b + DecimalV2Value(b == DecimalV2Value(0))); + static inline DecimalV2Value apply(DecimalV2Value a, DecimalV2Value b, UInt8& is_null) { + is_null = b == DecimalV2Value(0); + return a % (b + DecimalV2Value(is_null)); } }; template <typename A, typename B> struct PModuloImpl { using ResultType = typename NumberTraits::ResultOfModulo<A, B>::Type; + using Traits = NumberTraits::BinaryOperatorTraits<A, B>; + + template <typename Result = ResultType> + static void apply(const typename Traits::ArrayA& a, B b, + typename ColumnVector<Result>::Container& c, + typename Traits::ArrayNull& null_map) { + size_t size = c.size(); + UInt8 is_null = b == 0; + memset(null_map.data(), is_null, size); + + if (!is_null) { + for (size_t i = 0; i < size; i++) { + if constexpr (std::is_floating_point_v<ResultType>) { + c[i] = std::fmod(std::fmod((double)a[i], (double)b) + (double)b, double(b)); + } else { + c[i] = (a[i] % b + b) % b; + } + } + } + } template <typename Result = ResultType> - static inline Result apply(A a, B b, NullMap& null_map, size_t index) { + static inline Result apply(A a, B b, UInt8& is_null) { + is_null = b == 0; + b += is_null; + if constexpr (std::is_floating_point_v<Result>) { - null_map[index] = 0; return std::fmod(std::fmod((double)a, (double)b) + (double)b, (double)b); } else { - null_map[index] = b == 0; - return (typename NumberTraits::ToInteger<A>::Type(a) % - (typename NumberTraits::ToInteger<B>::Type(b) + (b == 0)) + - typename NumberTraits::ToInteger<B>::Type(b)) % - (typename NumberTraits::ToInteger<B>::Type(b) + (b == 0)); + return (a % b + b) % b; } } template <typename Result = DecimalV2Value> - static inline DecimalV2Value apply(DecimalV2Value a, DecimalV2Value b, NullMap& null_map, - size_t index) { - null_map[index] = b == DecimalV2Value(0); - return (a % (b + DecimalV2Value(b == DecimalV2Value(0))) + b) % - (b + DecimalV2Value(b == DecimalV2Value(0))); + static inline DecimalV2Value apply(DecimalV2Value a, DecimalV2Value b, UInt8& is_null) { + is_null = b == DecimalV2Value(0); + b += DecimalV2Value(is_null); + return (a % b + b) % b; } }; -template <typename A, typename B> -struct ModuloByConstantImpl : BinaryOperationImplBase<A, B, ModuloImpl<A, B>> { - using ResultType = typename ModuloImpl<A, B>::ResultType; - - static void vector_constant(const PaddedPODArray<A>& a, B b, PaddedPODArray<ResultType>& c) { - // TODO: Support return NULL in the future - if (UNLIKELY(b == 0)) { - // throw Exception("Division by zero", TStatusCode::VEC_ILLEGAL_DIVISION); - memset(c.data(), 0, sizeof(ResultType) * c.size()); - return; - } - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wsign-compare" - - if (UNLIKELY((std::is_signed_v<B> && b == -1) || b == 1)) { - size_t size = a.size(); - for (size_t i = 0; i < size; ++i) c[i] = 0; - return; - } - -#pragma GCC diagnostic pop - - libdivide::divider<A> divider(b); - - /// Here we failed to make the SSE variant from libdivide give an advantage. - size_t size = a.size(); - for (size_t i = 0; i < size; ++i) - c[i] = a[i] - - (a[i] / divider) * - b; /// NOTE: perhaps, the division semantics with the remainder of negative numbers is not preserved. - } -}; - -/** Specializations are specified for dividing numbers of the type UInt64 and UInt32 by the numbers of the same sign. - * Can be expanded to all possible combinations, but more code is needed. - */ - -template <> -struct BinaryOperationImpl<UInt64, UInt8, ModuloImpl<UInt64, UInt8>> - : ModuloByConstantImpl<UInt64, UInt8> {}; -template <> -struct BinaryOperationImpl<UInt64, UInt16, ModuloImpl<UInt64, UInt16>> - : ModuloByConstantImpl<UInt64, UInt16> {}; -template <> -struct BinaryOperationImpl<UInt64, UInt32, ModuloImpl<UInt64, UInt32>> - : ModuloByConstantImpl<UInt64, UInt32> {}; -template <> -struct BinaryOperationImpl<UInt64, UInt64, ModuloImpl<UInt64, UInt64>> - : ModuloByConstantImpl<UInt64, UInt64> {}; - -template <> -struct BinaryOperationImpl<UInt32, UInt8, ModuloImpl<UInt32, UInt8>> - : ModuloByConstantImpl<UInt32, UInt8> {}; -template <> -struct BinaryOperationImpl<UInt32, UInt16, ModuloImpl<UInt32, UInt16>> - : ModuloByConstantImpl<UInt32, UInt16> {}; -template <> -struct BinaryOperationImpl<UInt32, UInt32, ModuloImpl<UInt32, UInt32>> - : ModuloByConstantImpl<UInt32, UInt32> {}; -template <> -struct BinaryOperationImpl<UInt32, UInt64, ModuloImpl<UInt32, UInt64>> - : ModuloByConstantImpl<UInt32, UInt64> {}; - -template <> -struct BinaryOperationImpl<Int64, Int8, ModuloImpl<Int64, Int8>> - : ModuloByConstantImpl<Int64, Int8> {}; -template <> -struct BinaryOperationImpl<Int64, Int16, ModuloImpl<Int64, Int16>> - : ModuloByConstantImpl<Int64, Int16> {}; -template <> -struct BinaryOperationImpl<Int64, Int32, ModuloImpl<Int64, Int32>> - : ModuloByConstantImpl<Int64, Int32> {}; -template <> -struct BinaryOperationImpl<Int64, Int64, ModuloImpl<Int64, Int64>> - : ModuloByConstantImpl<Int64, Int64> {}; - -template <> -struct BinaryOperationImpl<Int32, Int8, ModuloImpl<Int32, Int8>> - : ModuloByConstantImpl<Int32, Int8> {}; -template <> -struct BinaryOperationImpl<Int32, Int16, ModuloImpl<Int32, Int16>> - : ModuloByConstantImpl<Int32, Int16> {}; -template <> -struct BinaryOperationImpl<Int32, Int32, ModuloImpl<Int32, Int32>> - : ModuloByConstantImpl<Int32, Int32> {}; -template <> -struct BinaryOperationImpl<Int32, Int64, ModuloImpl<Int32, Int64>> - : ModuloByConstantImpl<Int32, Int64> {}; - struct NameModulo { static constexpr auto name = "mod"; }; @@ -180,8 +122,8 @@ struct NamePModulo { static constexpr auto name = "pmod"; }; -using FunctionModulo = FunctionBinaryArithmeticToNullType<ModuloImpl, NameModulo, false>; -using FunctionPModulo = FunctionBinaryArithmeticToNullType<PModuloImpl, NamePModulo, false>; +using FunctionModulo = FunctionBinaryArithmetic<ModuloImpl, NameModulo, true>; +using FunctionPModulo = FunctionBinaryArithmetic<PModuloImpl, NamePModulo, true>; void register_function_modulo(SimpleFunctionFactory& factory) { factory.register_function<FunctionModulo>(); diff --git a/be/src/vec/functions/multiply.cpp b/be/src/vec/functions/multiply.cpp index b2840e42e5..95475ed847 100644 --- a/be/src/vec/functions/multiply.cpp +++ b/be/src/vec/functions/multiply.cpp @@ -48,7 +48,7 @@ struct MultiplyImpl { struct NameMultiply { static constexpr auto name = "multiply"; }; -using FunctionMultiply = FunctionBinaryArithmetic<MultiplyImpl, NameMultiply>; +using FunctionMultiply = FunctionBinaryArithmetic<MultiplyImpl, NameMultiply, false>; void register_function_multiply(SimpleFunctionFactory& factory) { factory.register_function<FunctionMultiply>(); diff --git a/be/src/vec/functions/plus.cpp b/be/src/vec/functions/plus.cpp index 0da30dfa20..d573d3a3d6 100644 --- a/be/src/vec/functions/plus.cpp +++ b/be/src/vec/functions/plus.cpp @@ -50,7 +50,7 @@ struct PlusImpl { struct NamePlus { static constexpr auto name = "add"; }; -using FunctionPlus = FunctionBinaryArithmetic<PlusImpl, NamePlus>; +using FunctionPlus = FunctionBinaryArithmetic<PlusImpl, NamePlus, false>; void register_function_plus(SimpleFunctionFactory& factory) { factory.register_function<FunctionPlus>(); --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org