This commit is contained in:
Michael Kolupaev 2024-11-20 15:11:53 -08:00 committed by GitHub
commit 74d8617628
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 58 additions and 2 deletions

View File

@ -32,9 +32,48 @@ using FunctionNegate = FunctionUnaryArithmetic<NegateImpl, NameNegate, true>;
template <> struct FunctionUnaryArithmeticMonotonicity<NameNegate>
{
static bool has() { return true; }
static IFunction::Monotonicity get(const Field &, const Field &)
static IFunction::Monotonicity get(const Field & left, const Field & right)
{
return { .is_monotonic = true, .is_positive = false, .is_strict = true };
/// negate(UInt64) -> Int64:
/// * monotonically decreases on [0, 2^63] (no overflow),
/// * then jumps up from -2^63 to 2^63-1, then
/// * monotonically decreases on [2^63+1, 2^64-1] (with overflow).
/// Similarly for UInt128 and UInt256.
/// Note: we currently don't handle the corner case -UINT64_MIN == UINT64_MIN, and similar for floats and wide signed ints.
/// (This implementation seems overcomplicated and not very correct, maybe there's a better way to do it,
/// maybe by using the actual IDataType instead of two field types.)
/// We don't know the data type, assume nonmonotonic.
if (left.isNull() && right.isNull())
return { .is_monotonic = false, .is_positive = false, .is_strict = true };
auto which_half_if_unsigned_or_infinity = [](const Field & f, int half_if_null, bool & is_unsigned) -> int
{
is_unsigned = true;
switch (f.getType())
{
case Field::Types::UInt64: return (f.safeGet<UInt64>() > 1ul << 63) ? +1 : -1;
case Field::Types::UInt128: return (f.safeGet<UInt128>() > UInt128(1) << 127) ? +1 : -1;
case Field::Types::UInt256: return (f.safeGet<UInt256>() > UInt256(1) << 255) ? +1 : -1;
default: break;
}
is_unsigned = false;
if (f.isPositiveInfinity())
return +1;
if (f.isNegativeInfinity())
return -1;
if (f.isNull())
return half_if_null;
return 0;
};
bool left_is_unsigned, right_is_unsigned;
int left_half = which_half_if_unsigned_or_infinity(left, -1, left_is_unsigned);
int right_half = which_half_if_unsigned_or_infinity(right, +1, right_is_unsigned);
bool is_monotonic = true;
if (left_is_unsigned || right_is_unsigned)
is_monotonic = left_half * right_half >= 0; /// either both values in the same half or unexpected signed-unsigned mix
return { .is_monotonic = is_monotonic, .is_positive = false, .is_strict = true };
}
};

View File

@ -0,0 +1,3 @@
42
42
42

View File

@ -0,0 +1,14 @@
create table a (x UInt64) engine MergeTree order by x;
insert into a values (12345678901234567890), (42);
select * from a where -x = -42;
drop table a;
create table a (x UInt128) engine MergeTree order by x;
insert into a values (170141183460469231731687303715884105828), (42);
select * from a where -x = -42;
drop table a;
create table a (x UInt256) engine MergeTree order by x;
insert into a values (57896044618658097711785492504343953926634992332820282019728792003956564820068), (42);
select * from a where -x = -42;
drop table a;