// Copyright 2023 PingCAP, Inc. // // Licensed 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 #include #include #include namespace DB { namespace ErrorCodes { extern const int DECIMAL_OVERFLOW_ERROR; extern const int LOGICAL_ERROR; } // namespace ErrorCodes using PrecType = UInt32; using ScaleType = UInt32; constexpr PrecType decimal_max_prec = 65; constexpr ScaleType decimal_max_scale = 30; // IntPrec indicates the max precision of different integer types. // For now, the binary arithmetic functions use it to calculate result precision. // And cast function use it to do some optimizations, such as skipping overflow check. // But in TiDB the signed types will plus 1, for example IntPrec::prec is 4. // This is a little confusing because we will add 1 when return result to client. // Here we make sure TiFlash code is clean and will fix TiDB later. template struct IntPrec { }; template <> struct IntPrec { static constexpr PrecType prec = 3; }; template <> struct IntPrec { static constexpr PrecType prec = 3; }; template <> struct IntPrec { static constexpr PrecType prec = 5; }; template <> struct IntPrec { static constexpr PrecType prec = 5; }; template <> struct IntPrec { static constexpr PrecType prec = 10; }; template <> struct IntPrec { static constexpr PrecType prec = 10; }; template <> struct IntPrec { static constexpr PrecType prec = 19; }; template <> struct IntPrec { static constexpr PrecType prec = 20; }; template <> struct IntPrec { static constexpr PrecType prec = 39; }; template <> struct IntPrec { static constexpr PrecType prec = 78; }; // 1) If the declared type of both operands of a dyadic arithmetic operator is exact numeric, then the declared // type of the result is an implementation-defined exact numeric type, with precision and scale determined as // follows: // a) Let S1 and S2 be the scale of the first and second operands respectively. // b) The precision of the result of addition and subtraction is implementation-defined, and the scale is the // maximum of S1 and S2. // c) The precision of the result of multiplication is implementation-defined, and the scale is S1 + S2. // d) The precision and scale of the result of division are implementation-defined. struct PlusDecimalInferer { static std::tuple infer( PrecType left_prec, ScaleType left_scale, PrecType right_prec, ScaleType right_scale, ScaleType /*div_precincrement is not used*/) { ScaleType result_scale = std::max(left_scale, right_scale); PrecType result_int = std::max(left_prec - left_scale, right_prec - right_scale); PrecType result_prec = std::min(result_scale + result_int + 1, decimal_max_prec); return {result_prec, result_scale}; } }; struct MulDecimalInferer { static std::tuple infer( PrecType left_prec, ScaleType left_scale, PrecType right_prec, ScaleType right_scale, ScaleType /*div_precincrement is not used*/) { return { std::min(left_prec + right_prec, decimal_max_prec), std::min(left_scale + right_scale, decimal_max_scale)}; } }; struct DivDecimalInferer { static std::tuple infer( PrecType left_prec, ScaleType left_scale, PrecType /* right_prec is not used */, ScaleType right_scale, ScaleType div_precincrement) { return { std::min(left_prec + right_scale + div_precincrement, decimal_max_prec), std::min(left_scale + div_precincrement, decimal_max_scale)}; } }; struct SumDecimalInferer { static constexpr PrecType decimal_longlong_digits = 22; static std::tuple infer(PrecType prec, ScaleType scale) { return {std::min(prec + decimal_longlong_digits, decimal_max_prec), scale}; } }; struct AvgDecimalInferer { static std::tuple infer(PrecType left_prec, ScaleType left_scale, ScaleType div_precincrement) { return { std::min(left_prec + div_precincrement, decimal_max_prec), std::min(left_scale + div_precincrement, decimal_max_scale)}; } }; struct ModDecimalInferer { static std::tuple infer( PrecType left_prec, ScaleType left_scale, PrecType right_prec, ScaleType right_scale, ScaleType /*div_precincrement is not used*/) { return {std::max(left_prec, right_prec), std::max(left_scale, right_scale)}; } }; struct OtherInferer { static std::tuple infer(PrecType, ScaleType, PrecType, ScaleType, ScaleType) { return {}; } }; template struct Decimal { T value; using NativeType = T; Decimal(const Decimal & d) = default; Decimal() = default; Decimal(T v_) // NOLINT(google-explicit-constructor) : value(v_) {} constexpr Decimal & operator=(Decimal &&) = default; constexpr Decimal & operator=(const Decimal &) = default; String toString(ScaleType) const; template < typename U, std::enable_if_t< std::is_same_v || std::is_same_v || std::is_integral_v || std::is_same_v> * = nullptr> operator U() const // NOLINT(google-explicit-constructor) { return static_cast(value); } template = sizeof(T)> * = nullptr> operator Decimal() const // NOLINT(google-explicit-constructor) { return static_cast(value); } operator T() const // NOLINT(google-explicit-constructor) { return value; } template std::enable_if_t, U> toFloat(ScaleType scale) const { U result = static_cast(value); for (ScaleType i = 0; i < scale; i++) { result /= 10; } return result; } template const Decimal & operator+=(const Decimal & x) { value += static_cast(x.value); return *this; } const Decimal & operator-=(const T & x) { value -= x; return *this; } const Decimal & operator*=(const T & x) { value *= x; return *this; } const Decimal & operator/=(const T & x) { value /= x; return *this; } const Decimal & operator%=(const T & x) { value %= x; return *this; } }; template inline bool operator<(const Decimal & x, const Decimal & y) { return x.value < y.value; } template inline bool operator<=(const Decimal & x, const Decimal & y) { return x.value <= y.value; } template inline bool operator>(const Decimal & x, const Decimal & y) { return x.value > y.value; } template inline bool operator>=(const Decimal & x, const Decimal & y) { return x.value >= y.value; } template inline bool operator==(const Decimal & x, const Decimal & y) { return x.value == y.value; } template inline bool operator!=(const Decimal & x, const Decimal & y) { return x.value != y.value; } template inline Decimal operator+(const Decimal & x, const Decimal & y) { return x.value + y.value; } template inline Decimal operator-(const Decimal & x, const Decimal & y) { return x.value - y.value; } template inline Decimal operator*(const Decimal & x, const Decimal & y) { return x.value * y.value; } template inline Decimal operator/(const Decimal & x, const Decimal & y) { return x.value / y.value; } template inline Decimal operator-(const Decimal & x) { return -x.value; } using Decimal32 = Decimal; using Decimal64 = Decimal; using Decimal128 = Decimal; using Decimal256 = Decimal; template static constexpr PrecType maxDecimalPrecision() { return 0; } template <> constexpr PrecType maxDecimalPrecision() { return 9; } template <> constexpr PrecType maxDecimalPrecision() { return 18; } template <> constexpr PrecType maxDecimalPrecision() { return 38; } template <> constexpr PrecType maxDecimalPrecision() { return 65; } template constexpr PrecType minDecimalPrecision() { /// return a invalid value return maxDecimalPrecision() + 1; } template <> constexpr PrecType minDecimalPrecision() { return 1; } template <> constexpr PrecType minDecimalPrecision() { return maxDecimalPrecision() + 1; } template <> constexpr PrecType minDecimalPrecision() { return maxDecimalPrecision() + 1; } template <> constexpr PrecType minDecimalPrecision() { return maxDecimalPrecision() + 1; } template struct PromoteType { }; template <> struct PromoteType { using Type = Int64; }; template <> struct PromoteType { using Type = Int128; }; template <> struct PromoteType { using Type = Int256; }; template <> struct PromoteType { using Type = Int512; }; template constexpr bool IsDecimal = false; template <> inline constexpr bool IsDecimal = true; template <> inline constexpr bool IsDecimal = true; template <> inline constexpr bool IsDecimal = true; template <> inline constexpr bool IsDecimal = true; class Field; // parse str into integer representation. // it returns std::nullopt if parsing fails. Otherwise, return value, corresponding precision and scale. std::optional> parseDecimal(const char * str, size_t len); // parse str into field. str should not contain "+" or "-". bool parseDecimal(const char * str, size_t len, bool negative, Field & field); class DecimalMaxValue final : public ext::Singleton { friend class ext::Singleton; Int256 number[decimal_max_prec + 1]; public: static Int256 get(PrecType idx) { // In case DecimalMaxValue::get(IntPrec::prec), where IntPrec::prec > 65. assert(idx <= decimal_max_prec); return instance().getInternal(idx); } static Int256 maxValue() { return get(maxDecimalPrecision()); } private: DecimalMaxValue() { for (PrecType i = 1; i <= decimal_max_prec; i++) { number[i] = number[i - 1] * 10 + 9; } } Int256 getInternal(PrecType idx) const { return number[idx]; } }; // In some case, getScaleMultiplier and its callee may not be auto inline by the compiler. // This may hurt performance. __attribute__((flatten)) tells compliler to inline the callee of this function. template > * = nullptr> __attribute__((flatten)) inline typename T::NativeType getScaleMultiplier(ScaleType scale) { return static_cast(DecimalMaxValue::get(scale) + 1); } template > * = nullptr> __attribute__((flatten)) inline T getScaleMultiplier(ScaleType scale) { return static_cast(DecimalMaxValue::get(scale) + 1); } template inline void checkDecimalOverflow(Decimal v, PrecType prec) { auto max_value = DecimalMaxValue::get(prec); if (v.value > max_value || v.value < -max_value) { throw TiFlashException("Decimal value overflow", Errors::Decimal::Overflow); } } template <> struct TypeName { static const char * get() { return "Decimal32"; } }; template <> struct TypeName { static const char * get() { return "Decimal64"; } }; template <> struct TypeName { static const char * get() { return "Decimal128"; } }; template <> struct TypeName { static const char * get() { return "Decimal256"; } }; template <> struct TypeId { static constexpr const TypeIndex value = TypeIndex::Decimal32; }; template <> struct TypeId { static constexpr const TypeIndex value = TypeIndex::Decimal64; }; template <> struct TypeId { static constexpr const TypeIndex value = TypeIndex::Decimal128; }; template <> struct TypeId { static constexpr const TypeIndex value = TypeIndex::Decimal256; }; template std::enable_if_t, U> ToDecimal(T value, ScaleType scale) { using UType = typename U::NativeType; UType scale_mul = getScaleMultiplier(scale); U result = static_cast(value) * scale_mul; return result; } template std::enable_if_t, U> ToDecimal(T value, ScaleType scale) { bool neg = false; if (value < 0) { neg = true; value = -value; } for (ScaleType i = 0; i < scale; i++) { value *= 10; } if (std::abs(value) > static_cast(DecimalMaxValue::get(decimal_max_prec))) { throw TiFlashException("Decimal value overflow", Errors::Decimal::Overflow); } // rounding T ten_times_value = value * 10; using UType = typename U::NativeType; UType v(value); if (static_cast(ten_times_value) % 10 >= 5) { v++; } if (neg) { v = -v; } return v; } template std::enable_if_t, U> ToDecimal(T /*value*/, ScaleType /*scale*/) { throw Exception("Should not call here", ErrorCodes::LOGICAL_ERROR); } template std::enable_if_t, U> ToDecimal(const T & v, ScaleType v_scale, ScaleType scale) { auto value = Int256(v.value); if (v_scale <= scale) { for (ScaleType i = v_scale; i < scale; i++) value *= 10; } else { bool need_to_round = false; for (ScaleType i = scale; i < v_scale; i++) { need_to_round = (value < 0 ? -value : value) % 10 >= 5; value /= 10; } if (need_to_round) { if (value < 0) value--; else value++; } } return static_cast(value); } template std::enable_if_t, U> ToDecimal(const T & /*v*/, ScaleType /*v_scale*/, ScaleType /*scale*/) { throw Exception("Should not call here", ErrorCodes::LOGICAL_ERROR); } } // namespace DB