#pragma once // Include this first, because `#define _asan_poison_address` from // llvm/Support/Compiler.h conflicts with its forward declaration in // sanitizer/asan_interface.h #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "IFunctionImpl.h" #include "FunctionHelpers.h" #include "intDiv.h" #include "castTypeToEither.h" #include "FunctionFactory.h" #include #include #include #if USE_EMBEDDED_COMPILER #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #include #pragma GCC diagnostic pop #endif namespace DB { namespace ErrorCodes { extern const int ILLEGAL_COLUMN; extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int LOGICAL_ERROR; extern const int DECIMAL_OVERFLOW; extern const int CANNOT_ADD_DIFFERENT_AGGREGATE_STATES; extern const int ILLEGAL_DIVISION; } /** Arithmetic operations: +, -, *, /, %, * intDiv (integer division) * Bitwise operations: |, &, ^, ~. * Etc. */ template struct BinaryOperationImplBase { using ResultType = ResultType_; static const constexpr bool allow_fixed_string = false; static void NO_INLINE vector_vector(const A * __restrict a, const B * __restrict b, ResultType * __restrict c, size_t size) { for (size_t i = 0; i < size; ++i) c[i] = Op::template apply(a[i], b[i]); } static void NO_INLINE vector_constant(const A * __restrict a, B b, ResultType * __restrict c, size_t 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 B * __restrict b, ResultType * __restrict c, size_t 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 FixedStringOperationImpl { static void NO_INLINE vector_vector(const UInt8 * __restrict a, const UInt8 * __restrict b, UInt8 * __restrict c, size_t size) { for (size_t i = 0; i < size; ++i) c[i] = Op::template apply(a[i], b[i]); } static void NO_INLINE vector_constant(const UInt8 * __restrict a, const UInt8 * __restrict b, UInt8 * __restrict c, size_t size, size_t N) { for (size_t i = 0; i < size; ++i) c[i] = Op::template apply(a[i], b[i % N]); } static void NO_INLINE constant_vector(const UInt8 * __restrict a, const UInt8 * __restrict b, UInt8 * __restrict c, size_t size, size_t N) { for (size_t i = 0; i < size; ++i) c[i] = Op::template apply(a[i % N], b[i]); } }; 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::getScale()). 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 = std::conditional_t, /// substitute divide by intDiv (throw on division by zero) 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); } 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] = applyScaled(a[i], b[i], scale_a); return; } else if (scale_b != 1) { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a[i], b[i], scale_b); return; } } else if constexpr (is_division && IsDecimalNumber) { for (size_t i = 0; i < size; ++i) c[i] = applyScaledDiv(a[i], b[i], scale_a); return; } /// default: use it if no return before for (size_t i = 0; i < size; ++i) c[i] = apply(a[i], b[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] = applyScaled(a[i], b, scale_a); return; } else if (scale_b != 1) { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a[i], b, scale_b); return; } } else if constexpr (is_division && IsDecimalNumber) { for (size_t i = 0; i < size; ++i) c[i] = applyScaledDiv(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] = applyScaled(a, b[i], scale_a); return; } else if (scale_b != 1) { for (size_t i = 0; i < size; ++i) c[i] = applyScaled(a, b[i], scale_b); return; } } else if constexpr (is_division && IsDecimalNumber) { for (size_t i = 0; i < size; ++i) c[i] = applyScaledDiv(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 applyScaled(a, b, scale_a); else if (scale_b != 1) return applyScaled(a, b, scale_b); } else if constexpr (is_division && IsDecimalNumber) return applyScaledDiv(a, b, scale_a); return apply(a, b); } private: /// there's implicit type convertion here static NativeResultType apply(NativeResultType a, NativeResultType b) { if constexpr (can_overflow && _check_overflow) { NativeResultType res; if (Op::template apply(a, b, res)) throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW); return res; } else return Op::template apply(a, b); } template static NO_SANITIZE_UNDEFINED NativeResultType applyScaled(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::mulOverflow(a, scale, a); else overflow |= common::mulOverflow(b, scale, b); if constexpr (can_overflow) overflow |= Op::template apply(a, b, res); else res = Op::template apply(a, b); if (overflow) throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW); } else { if constexpr (scale_left) a *= scale; else b *= scale; res = Op::template apply(a, b); } return res; } } static NO_SANITIZE_UNDEFINED NativeResultType applyScaledDiv(NativeResultType a, NativeResultType b, NativeResultType scale) { if constexpr (is_division) { if constexpr (_check_overflow) { bool overflow = false; if constexpr (!IsDecimalNumber) overflow |= common::mulOverflow(scale, scale, scale); overflow |= common::mulOverflow(a, scale, a); if (overflow) throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW); } else { if constexpr (!IsDecimalNumber) scale *= scale; a *= scale; } return Op::template apply(a, b); } } }; /// 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 constexpr bool IsFloatingPoint = false; template <> inline constexpr bool IsFloatingPoint = true; template <> inline constexpr bool IsFloatingPoint = true; template constexpr bool IsDateOrDateTime = false; template <> inline constexpr bool IsDateOrDateTime = true; template <> inline constexpr bool IsDateOrDateTime = 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