add two options to disable Decimal overflow checks CLICKHOUSE-3906

This commit is contained in:
chertus 2018-08-22 16:22:56 +03:00
parent eac6dd1c99
commit 314dcc5e9e
7 changed files with 134 additions and 44 deletions

View File

@ -8,6 +8,7 @@
#include <IO/readFloatText.h>
#include <Parsers/IAST.h>
#include <Parsers/ASTLiteral.h>
#include <Interpreters/Context.h>
namespace DB
{
@ -20,6 +21,10 @@ namespace ErrorCodes
}
bool decimalCheckComparisonOverflow(const Context & context) { return context.getSettingsRef().decimal_check_comparison_overflow; }
bool decimalCheckArithmeticOverflow(const Context & context) { return context.getSettingsRef().decimal_check_arithmetic_overflow; }
//
template <typename T>

View File

@ -63,6 +63,11 @@ class DataTypeSimpleSerialization : public IDataType
};
class Context;
bool decimalCheckComparisonOverflow(const Context & context);
bool decimalCheckArithmeticOverflow(const Context & context);
static constexpr size_t minDecimalPrecision() { return 1; }
template <typename T> static constexpr size_t maxDecimalPrecision() { return 0; }
template <> constexpr size_t maxDecimalPrecision<Decimal32>() { return 9; }

View File

@ -725,7 +725,7 @@ template <> struct NativeType<Decimal128> { using Type = Int128; };
/// +|- 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<B>::getScale()).
template <typename A, typename B, template <typename, typename> typename Operation, typename ResultType_>
template <typename A, typename B, template <typename, typename> typename Operation, typename ResultType_, bool _check_overflow = true>
struct DecimalBinaryOperation
{
using ResultType = ResultType_;
@ -734,6 +734,7 @@ struct DecimalBinaryOperation
using ArrayA = typename ColumnVector<A>::Container;
using ArrayB = typename ColumnVector<B>::Container;
using ArrayC = typename ColumnVector<ResultType>::Container;
using XOverflow = DecimalBinaryOperation<A, B, Operation, ResultType_, !_check_overflow>;
static constexpr bool is_plus_minus = std::is_same_v<Operation<Int32, Int32>, PlusImpl<Int32, Int32>> ||
std::is_same_v<Operation<Int32, Int32>, MinusImpl<Int32, Int32>>;
@ -855,7 +856,7 @@ private:
/// there's implicit type convertion here
static NativeResultType apply(NativeResultType a, NativeResultType b)
{
if constexpr (can_overflow)
if constexpr (can_overflow && _check_overflow)
{
NativeResultType res;
if (Op::template apply<NativeResultType>(a, b, res))
@ -873,19 +874,30 @@ private:
{
NativeResultType res;
bool overflow = false;
if constexpr (scale_left)
overflow |= common::mulOverflow(a, scale, a);
else
overflow |= common::mulOverflow(b, scale, b);
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<NativeResultType>(a, b, res);
if constexpr (can_overflow)
overflow |= Op::template apply<NativeResultType>(a, b, res);
else
res = Op::template apply<NativeResultType>(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<NativeResultType>(a, b);
if (overflow)
throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW);
}
return res;
}
@ -895,12 +907,21 @@ private:
{
if constexpr (is_division)
{
bool overflow = false;
if constexpr (!IsDecimalNumber<A>)
overflow |= common::mulOverflow(scale, scale, scale);
overflow |= common::mulOverflow(a, scale, a);
if (overflow)
throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW);
if constexpr (_check_overflow)
{
bool overflow = false;
if constexpr (!IsDecimalNumber<A>)
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<A>)
scale *= scale;
a *= scale;
}
return Op::template apply<NativeResultType>(a, b);
}
@ -1072,7 +1093,7 @@ public:
static constexpr auto name = Name::name;
static FunctionPtr create(const Context & context) { return std::make_shared<FunctionBinaryArithmetic>(context); }
FunctionBinaryArithmetic(const Context & context) : context(context) {}
FunctionBinaryArithmetic(const Context & context_) : context(context_) {}
String getName() const override
{
@ -1197,10 +1218,20 @@ public:
typename ResultDataType::FieldType scale_b = type.scaleFactorFor(right, is_multiply || is_division);
if constexpr (IsDecimal<RightDataType> && is_division)
scale_a = right.getScaleMultiplier();
auto res = OpImpl::constant_constant(col_left->template getValue<T0>(), col_right->template getValue<T1>(),
scale_a, scale_b);
block.getByPosition(result).column =
ResultDataType(type.getPrecision(), type.getScale()).createColumnConst(col_left->size(), toField(res));
if (decimalCheckArithmeticOverflow(context))
{
auto res = OpImpl::constant_constant(
col_left->template getValue<T0>(), col_right->template getValue<T1>(), scale_a, scale_b);
block.getByPosition(result).column =
ResultDataType(type.getPrecision(), type.getScale()).createColumnConst(col_left->size(), toField(res));
}
else
{
auto res = OpImpl::XOverflow::constant_constant(
col_left->template getValue<T0>(), col_right->template getValue<T1>(), scale_a, scale_b);
block.getByPosition(result).column =
ResultDataType(type.getPrecision(), type.getScale()).createColumnConst(col_left->size(), toField(res));
}
}
else
{
@ -1227,7 +1258,11 @@ public:
typename ResultDataType::FieldType scale_b = type.scaleFactorFor(right, is_multiply || is_division);
if constexpr (IsDecimal<RightDataType> && is_division)
scale_a = right.getScaleMultiplier();
OpImpl::constant_vector(col_left->template getValue<T0>(), col_right->getData(), vec_res, scale_a, scale_b);
if (decimalCheckArithmeticOverflow(context))
OpImpl::constant_vector(col_left->template getValue<T0>(), col_right->getData(), vec_res, scale_a, scale_b);
else
OpImpl::XOverflow::constant_vector(
col_left->template getValue<T0>(), col_right->getData(), vec_res, scale_a, scale_b);
}
else
OpImpl::constant_vector(col_left->template getValue<T0>(), col_right->getData(), vec_res);
@ -1247,9 +1282,20 @@ public:
if constexpr (IsDecimal<RightDataType> && is_division)
scale_a = right.getScaleMultiplier();
if (auto col_right = checkAndGetColumn<ColVecT1>(col_right_raw))
OpImpl::vector_vector(col_left->getData(), col_right->getData(), vec_res, scale_a, scale_b);
{
if (decimalCheckArithmeticOverflow(context))
OpImpl::vector_vector(col_left->getData(), col_right->getData(), vec_res, scale_a, scale_b);
else
OpImpl::XOverflow::vector_vector(col_left->getData(), col_right->getData(), vec_res, scale_a, scale_b);
}
else if (auto col_right = checkAndGetColumnConst<ColVecT1>(col_right_raw))
OpImpl::vector_constant(col_left->getData(), col_right->template getValue<T1>(), vec_res, scale_a, scale_b);
{
if (decimalCheckArithmeticOverflow(context))
OpImpl::vector_constant(col_left->getData(), col_right->template getValue<T1>(), vec_res, scale_a, scale_b);
else
OpImpl::XOverflow::vector_constant(
col_left->getData(), col_right->template getValue<T1>(), vec_res, scale_a, scale_b);
}
else
return false;
}

View File

@ -221,7 +221,8 @@ struct DecCompareInt
};
///
template <typename A, typename B, template <typename, typename> typename Operation, bool _actual = IsDecimalNumber<A> || IsDecimalNumber<B>>
template <typename A, typename B, template <typename, typename> typename Operation, bool _check_overflow = true,
bool _actual = IsDecimalNumber<A> || IsDecimalNumber<B>>
class DecimalComparison
{
public:
@ -404,24 +405,35 @@ private:
{
CompareInt x = a;
CompareInt y = b;
bool overflow = false;
if constexpr (sizeof(A) > sizeof(CompareInt))
overflow |= (A(x) != a);
if constexpr (sizeof(B) > sizeof(CompareInt))
overflow |= (B(y) != b);
if constexpr (std::is_unsigned_v<A>)
overflow |= (x < 0);
if constexpr (std::is_unsigned_v<B>)
overflow |= (y < 0);
if constexpr (_check_overflow)
{
bool overflow = false;
if constexpr (scale_left)
overflow |= common::mulOverflow(x, scale, x);
if constexpr (scale_right)
overflow |= common::mulOverflow(y, scale, y);
if constexpr (sizeof(A) > sizeof(CompareInt))
overflow |= (A(x) != a);
if constexpr (sizeof(B) > sizeof(CompareInt))
overflow |= (B(y) != b);
if constexpr (std::is_unsigned_v<A>)
overflow |= (x < 0);
if constexpr (std::is_unsigned_v<B>)
overflow |= (y < 0);
if (overflow)
throw Exception("Can't compare", ErrorCodes::DECIMAL_OVERFLOW);
if constexpr (scale_left)
overflow |= common::mulOverflow(x, scale, x);
if constexpr (scale_right)
overflow |= common::mulOverflow(y, scale, y);
if (overflow)
throw Exception("Can't compare", ErrorCodes::DECIMAL_OVERFLOW);
}
else
{
if constexpr (scale_left)
x *= scale;
if constexpr (scale_right)
y *= scale;
}
return Op::apply(x, y);
}
@ -1030,7 +1042,10 @@ private:
using LeftDataType = typename Types::LeftType;
using RightDataType = typename Types::RightType;
DecimalComparison<LeftDataType, RightDataType, Op>(block, result, col_left, col_right);
if (decimalCheckComparisonOverflow(context))
DecimalComparison<LeftDataType, RightDataType, Op, true>(block, result, col_left, col_right);
else
DecimalComparison<LeftDataType, RightDataType, Op, false>(block, result, col_left, col_right);
return true;
};

View File

@ -278,6 +278,8 @@ struct Settings
M(SettingBool, low_cardinality_use_single_dictionary_for_part, false, "LowCardinality type serialization setting. If is true, than will use additional keys when global dictionary overflows. Otherwise, will create several shared dictionaries.") \
M(SettingBool, allow_experimental_low_cardinality_type, false, "Allows to create table with LowCardinality types.") \
M(SettingBool, allow_experimental_decimal_type, false, "Enables Decimal data type.") \
M(SettingBool, decimal_check_comparison_overflow, true, "Check overflow of decimal comparison operations") \
M(SettingBool, decimal_check_arithmetic_overflow, true, "Check overflow of decimal arithmetic operations") \
\
M(SettingBool, prefer_localhost_replica, 1, "1 - always send query to local replica, if it exists. 0 - choose replica to send query between local and remote ones according to load_balancing") \
M(SettingUInt64, max_fetch_partition_retries_count, 5, "Amount of retries while fetching partition from another host.") \

View File

@ -2,6 +2,8 @@
84 0 1764 1
84 0 1764 1
84.840 0.000 1799.456400 1.000
84.840000000 0.000000000
84.840000000000000000 0.000000000000000000
84.84 0.00 1799.4564 1.00
63 21 -42 882 -882 2 0
63 21 -42 882 -882 2 0
@ -23,3 +25,7 @@
42 42 42 0.420000000 0.420000000000000000 0.42000000000000000000000000000000000000 42.420 42.420000000 42.42
0 0 0 0.000000000 0.000000000000000000 0.00000000000000000000000000000000000000 0.000 0.000000000 0.00
42 42 42 0.420000000 0.420000000000000000 0.42000000000000000000000000000000000000 42.420 42.420000000 42.42
1 1
1 1
1 0 1 0
1 0 1 0

View File

@ -29,7 +29,9 @@ SELECT e + e, e - e, e * e, e / e FROM test.decimal WHERE e > 0; -- { serverErro
SELECT f + f, f - f, f * f, f / f FROM test.decimal WHERE f > 0; -- { serverError 69 }
SELECT g + g, g - g, g * g, g / g FROM test.decimal WHERE g > 0;
SELECT h + h, h - h, h * h, h / h FROM test.decimal WHERE h > 0; -- { serverError 407 }
SELECT h + h, h - h FROM test.decimal WHERE h > 0;
SELECT i + i, i - i, i * i, i / i FROM test.decimal WHERE i > 0; -- { serverError 407 }
SELECT i + i, i - i FROM test.decimal WHERE i > 0;
SELECT j + j, j - j, j * j, j / j FROM test.decimal WHERE j > 0;
SELECT a + 21, a - 21, a - 84, a * 21, a * -21, a / 21, a / 84 FROM test.decimal WHERE a = 42;
@ -48,11 +50,20 @@ SELECT 21 + c, 21 - c, 84 - c, 21 * c, -21 * c, 21 / c, 84 / c FROM test.decimal
SELECT 21 + e, 21 - e, 84 - e, 21 * e, -21 * e, 21 / e, 84 / e FROM test.decimal WHERE e > 0; -- { serverError 407 }
SELECT 21 + f, 21 - f, 84 - f, 21 * f, -21 * f, 21 / f, 84 / f FROM test.decimal WHERE f > 0; -- { serverError 407 }
SELECT 21 + g, 21 - g, 84 - g, 21 * g, -21 * g, 21 / g, 84 / g FROM test.decimal WHERE g > 0;
SELECT 21 + h, 21 - h, 84 - h, 21 * h, -21 * h FROM test.decimal WHERE h > 0; --overflow 21 / h, 84 / h
SELECT 21 + h, 21 - h, 84 - h, 21 * h, -21 * h, 21 / h, 84 / h FROM test.decimal WHERE h > 0; -- { serverError 407 }
SELECT 21 + h, 21 - h, 84 - h, 21 * h, -21 * h FROM test.decimal WHERE h > 0;
SELECT 21 + i, 21 - i, 84 - i, 21 * i, -21 * i, 21 / i, 84 / i FROM test.decimal WHERE i > 0;
SELECT 21 + j, 21 - j, 84 - j, 21 * j, -21 * j, 21 / j, 84 / j FROM test.decimal WHERE j > 0;
SELECT a, -a, -b, -c, -d, -e, -f, -g, -h, -j from test.decimal ORDER BY a;
SELECT abs(a), abs(b), abs(c), abs(d), abs(e), abs(f), abs(g), abs(h), abs(j) from test.decimal ORDER BY a;
SET decimal_check_arithmetic_overflow = 0;
SELECT (h * h) != 0, (h / h) != 1 FROM test.decimal WHERE h > 0;
SELECT (i * i) != 0, (i / i) = 1 FROM test.decimal WHERE i > 0;
SELECT e + 1 > e, e + 10 > e, 1 + e > e, 10 + e > e FROM test.decimal WHERE e > 0;
SELECT f + 1 > f, f + 10 > f, 1 + f > f, 10 + f > f FROM test.decimal WHERE f > 0;
DROP TABLE IF EXISTS test.decimal;