Merge pull request #13228 from ClickHouse/decimal-too-large-negative-exponent

Fix assert when decimal has too large negative exponent
This commit is contained in:
alexey-milovidov 2020-08-07 21:57:20 +03:00 committed by GitHub
commit 7786fd4119
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 55 additions and 6 deletions

View File

@ -1,9 +1,29 @@
#pragma once
#include <limits>
#include <IO/ReadHelpers.h>
#include <Common/intExp.h>
/// This is only needed for non-official, "unbundled" build.
/// https://stackoverflow.com/questions/41198673/uint128-t-not-working-with-clang-and-libstdc
#if !defined(_LIBCPP_LIMITS) && !defined(__GLIBCXX_BITSIZE_INT_N_0) && defined(__SIZEOF_INT128__)
namespace std
{
template <>
struct numeric_limits<__int128_t>
{
static constexpr bool is_specialized = true;
static constexpr bool is_signed = true;
static constexpr bool is_integer = true;
static constexpr int radix = 2;
static constexpr int digits = 127;
static constexpr int digits10 = 38;
};
}
#endif
namespace DB
{
@ -160,12 +180,24 @@ inline void readDecimalText(ReadBuffer & buf, T & x, uint32_t precision, uint32_
if (static_cast<int32_t>(scale) + exponent < 0)
{
/// Too many digits after point. Just cut off excessive digits.
auto divisor = intExp10OfSize<T>(-exponent - static_cast<int32_t>(scale));
assert(divisor > 0); /// This is for Clang Static Analyzer. It is not smart enough to infer it automatically.
x.value /= divisor;
scale = 0;
return;
auto divisor_exp = -exponent - static_cast<int32_t>(scale);
if (divisor_exp >= std::numeric_limits<typename T::NativeType>::digits10)
{
/// Too big negative exponent
x.value = 0;
scale = 0;
return;
}
else
{
/// Too many digits after point. Just cut off excessive digits.
auto divisor = intExp10OfSize<T>(divisor_exp);
assert(divisor > 0); /// This is for Clang Static Analyzer. It is not smart enough to infer it automatically.
x.value /= divisor;
scale = 0;
return;
}
}
scale += exponent;

View File

@ -34,6 +34,7 @@ namespace std
static constexpr bool is_integer = true;
static constexpr int radix = 2;
static constexpr int digits = 128;
static constexpr int digits10 = 38;
static constexpr __uint128_t min () { return 0; } // used in boost 1.65.1+
static constexpr __uint128_t max () { return __uint128_t(0) - 1; } // used in boost 1.68.0+
};

View File

@ -0,0 +1,6 @@
1E-9 0
1E-8 0
1E-7 0
1e-7 0
1E-9 0.000000001
1E-10 0.000000000

View File

@ -0,0 +1,10 @@
SELECT '-1E9-1E9-1E9-1E9' AS x, toDecimal32(x, 0); -- { serverError 6 }
SELECT '-1E9' AS x, toDecimal32(x, 0); -- { serverError 69 }
SELECT '1E-9' AS x, toDecimal32(x, 0);
SELECT '1E-8' AS x, toDecimal32(x, 0);
SELECT '1E-7' AS x, toDecimal32(x, 0);
SELECT '1e-7' AS x, toDecimal32(x, 0);
SELECT '1E-9' AS x, toDecimal32(x, 9);
SELECT '1E-9' AS x, toDecimal32(x, 10); -- { serverError 69 }
SELECT '1E-10' AS x, toDecimal32(x, 10); -- { serverError 69 }
SELECT '1E-10' AS x, toDecimal32(x, 9);