// 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/FunctionBinaryArithmetic.h // and modified by Doris #pragma once #include "common/logging.h" #include "vec/columns/column_const.h" #include "vec/columns/column_decimal.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/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. */ template struct BinaryOperationImplBase { using ResultType = ResultType_; static void NO_INLINE vector_vector(const PaddedPODArray& a, const PaddedPODArray& b, PaddedPODArray& c) { size_t size = a.size(); for (size_t i = 0; i < size; ++i) { c[i] = Op::template apply(a[i], b[i]); } } static void NO_INLINE vector_vector(const PaddedPODArray& a, const PaddedPODArray& b, PaddedPODArray& c, NullMap& null_map) { size_t size = a.size(); for (size_t i = 0; i < size; ++i) { c[i] = Op::template apply(a[i], b[i], null_map, i); } } static void NO_INLINE vector_constant(const PaddedPODArray& a, B b, PaddedPODArray& c) { size_t size = a.size(); for (size_t i = 0; i < size; ++i) c[i] = Op::template apply(a[i], b); } static void NO_INLINE constant_vector(A a, const PaddedPODArray& b, PaddedPODArray& c) { size_t size = b.size(); for (size_t i = 0; i < size; ++i) c[i] = Op::template apply(a, b[i]); } static ResultType constant_constant(A a, B b) { return Op::template apply(a, b); } }; template struct BinaryOperationImpl : BinaryOperationImplBase {}; template struct PlusImpl; template struct MinusImpl; template struct MultiplyImpl; template struct DivideFloatingImpl; template struct DivideIntegralImpl; template struct DivideIntegralOrZeroImpl; template struct LeastBaseImpl; template struct GreatestBaseImpl; template struct ModuloImpl; /// 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::get_scale()). template typename Operation, typename ResultType_, bool _check_overflow = true> struct DecimalBinaryOperation { static constexpr bool is_plus_minus = std::is_same_v, PlusImpl> || std::is_same_v, MinusImpl>; static constexpr bool is_multiply = std::is_same_v, MultiplyImpl>; static constexpr bool is_float_division = std::is_same_v, DivideFloatingImpl>; static constexpr bool is_int_division = std::is_same_v, DivideIntegralImpl> || std::is_same_v, DivideIntegralOrZeroImpl>; static constexpr bool is_division = is_float_division || is_int_division; static constexpr bool is_compare = std::is_same_v, LeastBaseImpl> || std::is_same_v, GreatestBaseImpl>; static constexpr bool is_plus_minus_compare = is_plus_minus || is_compare; static constexpr bool can_overflow = is_plus_minus || is_multiply; using ResultType = ResultType_; using NativeResultType = typename NativeType::Type; using Op = Operation; using ColVecA = std::conditional_t, ColumnDecimal, ColumnVector>; using ColVecB = std::conditional_t, ColumnDecimal, ColumnVector>; using ArrayA = typename ColVecA::Container; using ArrayB = typename ColVecB::Container; using ArrayC = typename ColumnDecimal::Container; using SelfNoOverflow = DecimalBinaryOperation; 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]]) { size_t size = a.size(); if constexpr (is_plus_minus_compare) { if (scale_a != 1) { for (size_t i = 0; i < size; ++i) { c[i] = apply_scaled(a[i], b[i], scale_a); } return; } else if (scale_b != 1) { for (size_t i = 0; i < size; ++i) { c[i] = apply_scaled(a[i], b[i], scale_b); } return; } } /// default: use it if no return before for (size_t i = 0; i < size; ++i) { c[i] = apply(a[i], b[i]); } } /// 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) { 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); } } static void NO_INLINE vector_constant(const 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 (scale_a != 1) { for (size_t i = 0; i < size; ++i) c[i] = apply_scaled(a[i], b, scale_a); return; } else if (scale_b != 1) { for (size_t i = 0; i < size; ++i) c[i] = apply_scaled(a[i], b, scale_b); return; } } else if constexpr (is_division && IsDecimalNumber) { 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); } static void NO_INLINE constant_vector(A a, const 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 (scale_a != 1) { for (size_t i = 0; i < size; ++i) c[i] = apply_scaled(a, b[i], scale_a); return; } else if (scale_b != 1) { for (size_t i = 0; i < size; ++i) c[i] = apply_scaled(a, b[i], scale_b); return; } } else if constexpr (is_division && IsDecimalNumber) { 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]); } 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) return apply_scaled(a, b, scale_a); else if (scale_b != 1) return apply_scaled(a, b, scale_b); } else if constexpr (is_division && IsDecimalNumber) return apply_scaled_div(a, b, scale_a); return apply(a, b); } private: /// there's implicit type convertion here static NativeResultType apply(NativeResultType a, NativeResultType b) { // Now, Doris only support decimal +-*/ decimal. // overflow in consider in operator DecimalV2Value l(a); DecimalV2Value r(b); auto ans = Op::template apply(l, r); NativeResultType result; memcpy(&result, &ans, sizeof(NativeResultType)); return result; } /// null_map for divide and mod static NativeResultType apply(NativeResultType a, NativeResultType b, NullMap& null_map, size_t index) { DecimalV2Value l(a); DecimalV2Value r(b); auto ans = Op::template apply(l, r, null_map, index); NativeResultType result; memcpy(&result, &ans, std::min(sizeof(result), sizeof(ans))); return result; } template static NativeResultType apply_scaled(NativeResultType a, NativeResultType b, NativeResultType scale) { if constexpr (is_plus_minus_compare) { NativeResultType res; if constexpr (_check_overflow) { bool overflow = false; if constexpr (scale_left) overflow |= common::mul_overflow(a, scale, a); else overflow |= common::mul_overflow(b, scale, b); if constexpr (can_overflow) overflow |= Op::template apply(a, b, res); else res = Op::template apply(a, b); if (overflow) { LOG(FATAL) << "Decimal math overflow"; } } else { if constexpr (scale_left) a *= scale; else b *= scale; res = Op::template apply(a, b); } return res; } } 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) { bool overflow = false; if constexpr (!IsDecimalNumber) 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) scale *= scale; a *= scale; } return Op::template apply(a, b, null_map, index); } } }; /// Used to indicate undefined operation struct InvalidType; template struct Case : std::bool_constant { using type = T; }; /// Switch, ...> -- select the first Ti for which Ci is true; InvalidType if none. template using Switch = typename std::disjunction>::type; template constexpr bool IsIntegral = false; template <> inline constexpr bool IsIntegral = true; template <> inline constexpr bool IsIntegral = true; template <> inline constexpr bool IsIntegral = true; template <> inline constexpr bool IsIntegral = true; template <> inline constexpr bool IsIntegral = true; template <> inline constexpr bool IsIntegral = true; template <> inline constexpr bool IsIntegral = true; template <> inline constexpr bool IsIntegral = true; template <> inline constexpr bool IsIntegral = true; template constexpr bool IsFloatingPoint = false; template <> inline constexpr bool IsFloatingPoint = true; template <> inline constexpr bool IsFloatingPoint = true; template constexpr bool UseLeftDecimal = false; template <> inline constexpr bool UseLeftDecimal, DataTypeDecimal> = true; template <> inline constexpr bool UseLeftDecimal, DataTypeDecimal> = true; template <> inline constexpr bool UseLeftDecimal, DataTypeDecimal> = true; template using DataTypeFromFieldType = std::conditional_t, InvalidType, DataTypeNumber>; template