#pragma once #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int ILLEGAL_DIVISION; } /** Арифметические функции: +, -, *, /, %, * intDiv (целочисленное деление), унарный минус. * Битовые функции: |, &, ^, ~. */ template struct BinaryOperationImplBase { using ResultType = ResultType_; static void 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 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 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 void constant_constant(A a, B b, ResultType & c) { c = Op::template apply(a, b); } }; template struct BinaryOperationImpl : BinaryOperationImplBase { }; template struct UnaryOperationImpl { using ResultType = typename Op::ResultType; static void vector(const PaddedPODArray & a, PaddedPODArray & c) { size_t size = a.size(); for (size_t i = 0; i < size; ++i) c[i] = Op::apply(a[i]); } static void constant(A a, ResultType & c) { c = Op::apply(a); } }; template struct PlusImpl { using ResultType = typename NumberTraits::ResultOfAdditionMultiplication::Type; template static inline Result apply(A a, B b) { /// Далее везде, static_cast - чтобы не было неправильного результата в выражениях вида Int64 c = UInt32(a) * Int32(-1). return static_cast(a) + b; } }; template struct MultiplyImpl { using ResultType = typename NumberTraits::ResultOfAdditionMultiplication::Type; template static inline Result apply(A a, B b) { return static_cast(a) * b; } }; template struct MinusImpl { using ResultType = typename NumberTraits::ResultOfSubtraction::Type; template static inline Result apply(A a, B b) { return static_cast(a) - b; } }; template struct DivideFloatingImpl { using ResultType = typename NumberTraits::ResultOfFloatingPointDivision::Type; template static inline Result apply(A a, B b) { return static_cast(a) / b; } }; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsign-compare" template inline void throwIfDivisionLeadsToFPE(A a, B b) { /// Возможно, лучше вместо проверок использовать siglongjmp? if (unlikely(b == 0)) throw Exception("Division by zero", ErrorCodes::ILLEGAL_DIVISION); /// http://avva.livejournal.com/2548306.html if (unlikely(std::is_signed::value && std::is_signed::value && a == std::numeric_limits::min() && b == -1)) throw Exception("Division of minimal signed number by minus one", ErrorCodes::ILLEGAL_DIVISION); } template inline bool divisionLeadsToFPE(A a, B b) { /// Возможно, лучше вместо проверок использовать siglongjmp? if (unlikely(b == 0)) return true; /// http://avva.livejournal.com/2548306.html if (unlikely(std::is_signed::value && std::is_signed::value && a == std::numeric_limits::min() && b == -1)) return true; return false; } #pragma GCC diagnostic pop template struct DivideIntegralImpl { using ResultType = typename NumberTraits::ResultOfIntegerDivision::Type; template static inline Result apply(A a, B b) { throwIfDivisionLeadsToFPE(a, b); return a / b; } }; template struct DivideIntegralOrZeroImpl { using ResultType = typename NumberTraits::ResultOfIntegerDivision::Type; template static inline Result apply(A a, B b) { return unlikely(divisionLeadsToFPE(a, b)) ? 0 : a / b; } }; template struct ModuloImpl { using ResultType = typename NumberTraits::ResultOfModulo::Type; template static inline Result apply(A a, B b) { throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger::Type(a), typename NumberTraits::ToInteger::Type(b)); return typename NumberTraits::ToInteger::Type(a) % typename NumberTraits::ToInteger::Type(b); } }; template struct BitAndImpl { using ResultType = typename NumberTraits::ResultOfBit::Type; template static inline Result apply(A a, B b) { return static_cast(a) & static_cast(b); } }; template struct BitOrImpl { using ResultType = typename NumberTraits::ResultOfBit::Type; template static inline Result apply(A a, B b) { return static_cast(a) | static_cast(b); } }; template struct BitXorImpl { using ResultType = typename NumberTraits::ResultOfBit::Type; template static inline Result apply(A a, B b) { return static_cast(a) ^ static_cast(b); } }; template struct BitShiftLeftImpl { using ResultType = typename NumberTraits::ResultOfBit::Type; template static inline Result apply(A a, B b) { return static_cast(a) << static_cast(b); } }; template struct BitShiftRightImpl { using ResultType = typename NumberTraits::ResultOfBit::Type; template static inline Result apply(A a, B b) { return static_cast(a) >> static_cast(b); } }; template struct LeastBaseImpl { using ResultType = NumberTraits::ResultOfLeast; template static inline Result apply(A a, B b) { /** gcc 4.9.2 успешно векторизует цикл из этой функции. */ return static_cast(a) < static_cast(b) ? static_cast(a) : static_cast(b); } }; template struct LeastSpecialImpl { using ResultType = std::make_signed_t; using SecondType = std::make_unsigned_t; template static inline Result apply(ResultType a, SecondType b) { static_assert(std::is_same::value, "typeof(a) != Result"); return (a < static_cast(b) || a < 0 || b > static_cast(std::numeric_limits::max())) ? a : static_cast(b); } template static inline Result apply(SecondType a, ResultType b) { static_assert(std::is_same::value, "typeof(b) != Result"); return (b < static_cast(a) || b < 0 || a > static_cast(std::numeric_limits::max())) ? b : static_cast(a); } }; template using LeastImpl = std::conditional_t::value, LeastBaseImpl, LeastSpecialImpl>; template struct GreatestBaseImpl { using ResultType = NumberTraits::ResultOfGreatest; template static inline Result apply(A a, B b) { return static_cast(a) > static_cast(b) ? static_cast(a) : static_cast(b); } }; template struct GreatestSpecialImpl { using ResultType = std::make_unsigned_t; using SecondType = std::make_signed_t; template static inline Result apply(ResultType a, SecondType b) { static_assert(std::is_same::value, "typeof(a) != Result"); return (a > static_cast(b) || b < 0 || a > static_cast((std::numeric_limits::max()))) ? a : static_cast(b); } template static inline Result apply(SecondType a, ResultType b) { static_assert(std::is_same::value, "typeof(b) != Result"); return (b > static_cast(a) || a < 0 || b > static_cast((std::numeric_limits::max()))) ? b : static_cast(a); } }; template using GreatestImpl = std::conditional_t::value, GreatestBaseImpl, GreatestSpecialImpl>; template struct NegateImpl { using ResultType = typename NumberTraits::ResultOfNegate::Type; static inline ResultType apply(A a) { return -static_cast(a); } }; template struct BitNotImpl { using ResultType = typename NumberTraits::ResultOfBitNot::Type; static inline ResultType apply(A a) { return ~static_cast(a); } }; template struct AbsImpl { using ResultType = typename NumberTraits::ResultOfAbs::Type; template static inline ResultType apply(T a, typename std::enable_if::value && std::is_signed::value, void>::type * = nullptr) { return a < 0 ? static_cast(~a) + 1 : a; } template static inline ResultType apply(T a, typename std::enable_if::value && std::is_unsigned::value, void>::type * = nullptr) { return static_cast(a); } template static inline ResultType apply(T a, typename std::enable_if::value, void>::type * = nullptr) { return static_cast(std::abs(a)); } }; /// this one is just for convenience template using If = typename std::conditional::type; /// these ones for better semantics template using Then = T; template using Else = T; /// Used to indicate undefined operation struct InvalidType; template <> struct DataTypeFromFieldType { using Type = InvalidType; }; template struct IsIntegral { static constexpr auto value = false; }; template <> struct IsIntegral { static constexpr auto value = true; }; template <> struct IsIntegral { static constexpr auto value = true; }; template <> struct IsIntegral { static constexpr auto value = true; }; template <> struct IsIntegral { static constexpr auto value = true; }; template <> struct IsIntegral { static constexpr auto value = true; }; template <> struct IsIntegral { static constexpr auto value = true; }; template <> struct IsIntegral { static constexpr auto value = true; }; template <> struct IsIntegral { static constexpr auto value = true; }; template struct IsFloating { static constexpr auto value = false; }; template <> struct IsFloating { static constexpr auto value = true; }; template <> struct IsFloating { static constexpr auto value = true; }; template struct IsNumeric { static constexpr auto value = IsIntegral::value || IsFloating::value; }; template struct IsDateOrDateTime { static constexpr auto value = false; }; template <> struct IsDateOrDateTime { static constexpr auto value = true; }; template <> struct IsDateOrDateTime { static constexpr auto value = true; }; /** Returns appropriate result type for binary operator on dates (or datetimes): * Date + Integral -> Date * Integral + Date -> Date * Date - Date -> Int32 * Date - Integral -> Date * least(Date, Date) -> Date * greatest(Date, Date) -> Date * All other operations are not defined and return InvalidType, operations on * distinct date types are also undefined (e.g. DataTypeDate - DataTypeDateTime) */ template