#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include /// TODO Core should not depend on Functions
namespace DB
{
namespace ErrorCodes
{
extern const int LOGICAL_ERROR;
extern const int DECIMAL_OVERFLOW;
}
inline bool allowDecimalComparison(const DataTypePtr & left_type, const DataTypePtr & right_type)
{
if (isColumnedAsDecimal(left_type))
{
if (isColumnedAsDecimal(right_type) || isNotDecimalButComparableToDecimal(right_type))
return true;
}
else if (isNotDecimalButComparableToDecimal(left_type) && isColumnedAsDecimal(right_type))
{
return true;
}
return false;
}
template struct ConstructDecInt;
template <> struct ConstructDecInt<1> { using Type = Int32; };
template <> struct ConstructDecInt<2> { using Type = Int32; };
template <> struct ConstructDecInt<4> { using Type = Int32; };
template <> struct ConstructDecInt<8> { using Type = Int64; };
template <> struct ConstructDecInt<16> { using Type = Int128; };
template <> struct ConstructDecInt<32> { using Type = Int256; };
template
struct DecCompareInt
{
using Type = typename ConstructDecInt<(!is_decimal || sizeof(T) > sizeof(U)) ? sizeof(T) : sizeof(U)>::Type;
using TypeA = Type;
using TypeB = Type;
};
template typename Operation, bool _check_overflow = true,
bool _actual = is_decimal || is_decimal>
class DecimalComparison
{
public:
using CompareInt = typename DecCompareInt::Type;
using Op = Operation;
using ColVecA = ColumnVectorOrDecimal;
using ColVecB = ColumnVectorOrDecimal;
using ArrayA = typename ColVecA::Container;
using ArrayB = typename ColVecB::Container;
static ColumnPtr apply(const ColumnWithTypeAndName & col_left, const ColumnWithTypeAndName & col_right)
{
if constexpr (_actual)
{
ColumnPtr c_res;
Shift shift = getScales(col_left.type, col_right.type);
return applyWithScale(col_left.column, col_right.column, shift);
}
else
return nullptr;
}
static bool compare(A a, B b, UInt32 scale_a, UInt32 scale_b)
{
static const UInt32 max_scale = DecimalUtils::max_precision;
if (scale_a > max_scale || scale_b > max_scale)
throw Exception(ErrorCodes::DECIMAL_OVERFLOW, "Bad scale of decimal field");
Shift shift;
if (scale_a < scale_b)
shift.a = static_cast(DecimalUtils::scaleMultiplier(scale_b - scale_a));
if (scale_a > scale_b)
shift.b = static_cast(DecimalUtils::scaleMultiplier(scale_a - scale_b));
return applyWithScale(a, b, shift);
}
private:
struct Shift
{
CompareInt a = 1;
CompareInt b = 1;
bool none() const { return a == 1 && b == 1; }
bool left() const { return a != 1; }
bool right() const { return b != 1; }
};
template
static auto applyWithScale(T a, U b, const Shift & shift)
{
if (shift.left())
return apply(a, b, shift.a);
else if (shift.right())
return apply(a, b, shift.b);
return apply(a, b, 1);
}
template
requires is_decimal && is_decimal
static Shift getScales(const DataTypePtr & left_type, const DataTypePtr & right_type)
{
const DataTypeDecimalBase * decimal0 = checkDecimalBase(*left_type);
const DataTypeDecimalBase * decimal1 = checkDecimalBase(*right_type);
Shift shift;
if (decimal0 && decimal1)
{
auto result_type = DecimalUtils::binaryOpResult(*decimal0, *decimal1);
shift.a = static_cast(result_type.scaleFactorFor(decimal0->getTrait(), false).value);
shift.b = static_cast(result_type.scaleFactorFor(decimal1->getTrait(), false).value);
}
else if (decimal0)
shift.b = static_cast(decimal0->getScaleMultiplier().value);
else if (decimal1)
shift.a = static_cast(decimal1->getScaleMultiplier().value);
return shift;
}
template
requires is_decimal && (!is_decimal)
static Shift getScales(const DataTypePtr & left_type, const DataTypePtr &)
{
Shift shift;
const DataTypeDecimalBase * decimal0 = checkDecimalBase(*left_type);
if (decimal0)
shift.b = static_cast(decimal0->getScaleMultiplier().value);
return shift;
}
template
requires (!is_decimal) && is_decimal
static Shift getScales(const DataTypePtr &, const DataTypePtr & right_type)
{
Shift shift;
const DataTypeDecimalBase * decimal1 = checkDecimalBase(*right_type);
if (decimal1)
shift.a = static_cast(decimal1->getScaleMultiplier().value);
return shift;
}
template
static ColumnPtr apply(const ColumnPtr & c0, const ColumnPtr & c1, CompareInt scale)
{
auto c_res = ColumnUInt8::create();
if constexpr (_actual)
{
bool c0_is_const = isColumnConst(*c0);
bool c1_is_const = isColumnConst(*c1);
if (c0_is_const && c1_is_const)
{
const ColumnConst * c0_const = checkAndGetColumnConst(c0.get());
const ColumnConst * c1_const = checkAndGetColumnConst(c1.get());
A a = c0_const->template getValue();
B b = c1_const->template getValue();
UInt8 res = apply(a, b, scale);
return DataTypeUInt8().createColumnConst(c0->size(), toField(res));
}
ColumnUInt8::Container & vec_res = c_res->getData();
vec_res.resize(c0->size());
if (c0_is_const)
{
const ColumnConst * c0_const = checkAndGetColumnConst(c0.get());
A a = c0_const->template getValue();
if (const ColVecB * c1_vec = checkAndGetColumn(c1.get()))
constantVector(a, c1_vec->getData(), vec_res, scale);
else
throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong column in Decimal comparison");
}
else if (c1_is_const)
{
const ColumnConst * c1_const = checkAndGetColumnConst(c1.get());
B b = c1_const->template getValue();
if (const ColVecA * c0_vec = checkAndGetColumn(c0.get()))
vectorConstant(c0_vec->getData(), b, vec_res, scale);
else
throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong column in Decimal comparison");
}
else
{
if (const ColVecA * c0_vec = checkAndGetColumn(c0.get()))
{
if (const ColVecB * c1_vec = checkAndGetColumn(c1.get()))
vectorVector(c0_vec->getData(), c1_vec->getData(), vec_res, scale);
else
throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong column in Decimal comparison");
}
else
throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong column in Decimal comparison");
}
}
return c_res;
}
template
static NO_INLINE UInt8 apply(A a, B b, CompareInt scale [[maybe_unused]])
{
CompareInt x;
if constexpr (is_decimal)
x = a.value;
else
x = a;
CompareInt y;
if constexpr (is_decimal)
y = b.value;
else
y = static_cast(b);
if constexpr (_check_overflow)
{
bool overflow = false;
if constexpr (sizeof(A) > sizeof(CompareInt))
overflow |= (static_cast(x) != a);
if constexpr (sizeof(B) > sizeof(CompareInt))
overflow |= (static_cast(y) != b);
if constexpr (is_unsigned_v)
overflow |= (x < 0);
if constexpr (is_unsigned_v)
overflow |= (y < 0);
if constexpr (scale_left)
overflow |= common::mulOverflow(x, scale, x);
if constexpr (scale_right)
overflow |= common::mulOverflow(y, scale, y);
if (overflow)
throw Exception(ErrorCodes::DECIMAL_OVERFLOW, "Can't compare decimal number due to overflow");
}
else
{
if constexpr (scale_left)
x = common::mulIgnoreOverflow(x, scale);
if constexpr (scale_right)
y = common::mulIgnoreOverflow(y, scale);
}
return Op::apply(x, y);
}
template
static void NO_INLINE vectorVector(const ArrayA & a, const ArrayB & b, PaddedPODArray & c,
CompareInt scale)
{
size_t size = a.size();
const A * a_pos = a.data();
const B * b_pos = b.data();
UInt8 * c_pos = c.data();
const A * a_end = a_pos + size;
while (a_pos < a_end)
{
*c_pos = apply(*a_pos, *b_pos, scale);
++a_pos;
++b_pos;
++c_pos;
}
}
template
static void NO_INLINE vectorConstant(const ArrayA & a, B b, PaddedPODArray & c, CompareInt scale)
{
size_t size = a.size();
const A * a_pos = a.data();
UInt8 * c_pos = c.data();
const A * a_end = a_pos + size;
while (a_pos < a_end)
{
*c_pos = apply(*a_pos, b, scale);
++a_pos;
++c_pos;
}
}
template
static void NO_INLINE constantVector(A a, const ArrayB & b, PaddedPODArray & c, CompareInt scale)
{
size_t size = b.size();
const B * b_pos = b.data();
UInt8 * c_pos = c.data();
const B * b_end = b_pos + size;
while (b_pos < b_end)
{
*c_pos = apply(a, *b_pos, scale);
++b_pos;
++c_pos;
}
}
};
}