Correct index comparisons in case constants cannot be precisely casted [#CLICKHOUSE-3002]

This commit is contained in:
Alexey Zatelepin 2017-05-23 17:38:14 +03:00 committed by alexey-milovidov
parent a77369d082
commit e9f8f99e06
5 changed files with 66 additions and 87 deletions

View File

@ -196,39 +196,4 @@ void FieldVisitorHash::operator() (const Array & x) const
applyVisitor(*this, elem);
}
UInt64 stringToDateOrDateTime(const String & s)
{
if (s.size() == strlen("YYYY-MM-DD"))
return stringToDate(s);
else
return stringToDateTime(s);
}
DayNum_t stringToDate(const String & s)
{
ReadBufferFromString in(s);
DayNum_t date{};
readDateText(date, in);
if (!in.eof())
throw Exception("String is too long for Date: " + s);
return date;
}
UInt64 stringToDateTime(const String & s)
{
ReadBufferFromString in(s);
time_t date_time{};
readDateTimeText(date_time, in);
if (!in.eof())
throw Exception("String is too long for DateTime: " + s);
return UInt64(date_time);
}
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <Core/Field.h>
#include <Functions/AccurateComparison.h>
#include <common/DateLUT.h>
@ -174,26 +175,12 @@ public:
};
/// Converts string with date or datetime (in format 'YYYY-MM-DD hh:mm:ss') to UInt64 containing numeric value of date (or datetime)
UInt64 stringToDateOrDateTime(const String & s);
/// Converts string with date to UInt16 (which is alias of DayNum_t) containing numeric value of date
DayNum_t stringToDate(const String & s);
/// Converts string with date to UInt64 containing numeric value of datetime
UInt64 stringToDateTime(const String & s);
/** More precise comparison, used for index.
* Differs from Field::operator< and Field::operator== in that it also compares values of different types.
* Comparison rules are same as in FunctionsComparison (to be consistent with expression evaluation in query).
* Except in cases when comparing signed and unsigned integers, which is unspecified behavior in FunctionsComparison,
* and when comparing integers and floats. Comparison is accurate here.
*/
class FieldVisitorAccurateEquals : public StaticVisitor<bool>
{
using Double128 = long double; /// Non portable. Must have 64 bit mantissa to provide accurate comparisons.
public:
bool operator() (const Null & l, const Null & r) const { return true; }
bool operator() (const Null & l, const UInt64 & r) const { return false; }
@ -205,30 +192,30 @@ public:
bool operator() (const UInt64 & l, const Null & r) const { return false; }
bool operator() (const UInt64 & l, const UInt64 & r) const { return l == r; }
bool operator() (const UInt64 & l, const Int64 & r) const { return r >= 0 && l == UInt64(r); }
bool operator() (const UInt64 & l, const Float64 & r) const { return Double128(l) == Double128(r); }
bool operator() (const UInt64 & l, const String & r) const { return l == stringToDateOrDateTime(r); }
bool operator() (const UInt64 & l, const Int64 & r) const { return accurate::equalsOp(l, r); }
bool operator() (const UInt64 & l, const Float64 & r) const { return accurate::equalsOp(l, r); }
bool operator() (const UInt64 & l, const String & r) const { return false; }
bool operator() (const UInt64 & l, const Array & r) const { return false; }
bool operator() (const UInt64 & l, const Tuple & r) const { return false; }
bool operator() (const Int64 & l, const Null & r) const { return false; }
bool operator() (const Int64 & l, const UInt64 & r) const { return l >= 0 && UInt64(l) == r; }
bool operator() (const Int64 & l, const UInt64 & r) const { return accurate::equalsOp(l, r); }
bool operator() (const Int64 & l, const Int64 & r) const { return l == r; }
bool operator() (const Int64 & l, const Float64 & r) const { return Double128(l) == Double128(r); }
bool operator() (const Int64 & l, const Float64 & r) const { return accurate::equalsOp(l, r); }
bool operator() (const Int64 & l, const String & r) const { return false; }
bool operator() (const Int64 & l, const Array & r) const { return false; }
bool operator() (const Int64 & l, const Tuple & r) const { return false; }
bool operator() (const Float64 & l, const Null & r) const { return false; }
bool operator() (const Float64 & l, const UInt64 & r) const { return Double128(l) == Double128(r); }
bool operator() (const Float64 & l, const Int64 & r) const { return Double128(l) == Double128(r); }
bool operator() (const Float64 & l, const UInt64 & r) const { return accurate::equalsOp(l, r); }
bool operator() (const Float64 & l, const Int64 & r) const { return accurate::equalsOp(l, r); }
bool operator() (const Float64 & l, const Float64 & r) const { return l == r; }
bool operator() (const Float64 & l, const String & r) const { return false; }
bool operator() (const Float64 & l, const Array & r) const { return false; }
bool operator() (const Float64 & l, const Tuple & r) const { return false; }
bool operator() (const String & l, const Null & r) const { return false; }
bool operator() (const String & l, const UInt64 & r) const { return stringToDateOrDateTime(l) == r; }
bool operator() (const String & l, const UInt64 & r) const { return false; }
bool operator() (const String & l, const Int64 & r) const { return false; }
bool operator() (const String & l, const Float64 & r) const { return false; }
bool operator() (const String & l, const String & r) const { return l == r; }
@ -254,8 +241,6 @@ public:
class FieldVisitorAccurateLess : public StaticVisitor<bool>
{
using Double128 = long double; /// Non portable. Must have 64 bit mantissa to provide accurate comparisons.
public:
bool operator() (const Null & l, const Null & r) const { return false; }
bool operator() (const Null & l, const UInt64 & r) const { return true; }
@ -267,30 +252,30 @@ public:
bool operator() (const UInt64 & l, const Null & r) const { return false; }
bool operator() (const UInt64 & l, const UInt64 & r) const { return l < r; }
bool operator() (const UInt64 & l, const Int64 & r) const { return r >= 0 && l < UInt64(r); }
bool operator() (const UInt64 & l, const Float64 & r) const { return Double128(l) < Double128(r); }
bool operator() (const UInt64 & l, const String & r) const { return l < stringToDateOrDateTime(r); }
bool operator() (const UInt64 & l, const Int64 & r) const { return accurate::lessOp(l, r); }
bool operator() (const UInt64 & l, const Float64 & r) const { return accurate::lessOp(l, r); }
bool operator() (const UInt64 & l, const String & r) const { return true; }
bool operator() (const UInt64 & l, const Array & r) const { return true; }
bool operator() (const UInt64 & l, const Tuple & r) const { return true; }
bool operator() (const Int64 & l, const Null & r) const { return false; }
bool operator() (const Int64 & l, const UInt64 & r) const { return l < 0 || UInt64(l) < r; }
bool operator() (const Int64 & l, const UInt64 & r) const { return accurate::lessOp(l, r); }
bool operator() (const Int64 & l, const Int64 & r) const { return l < r; }
bool operator() (const Int64 & l, const Float64 & r) const { return Double128(l) < Double128(r); }
bool operator() (const Int64 & l, const Float64 & r) const { return accurate::lessOp(l, r); }
bool operator() (const Int64 & l, const String & r) const { return true; }
bool operator() (const Int64 & l, const Array & r) const { return true; }
bool operator() (const Int64 & l, const Tuple & r) const { return true; }
bool operator() (const Float64 & l, const Null & r) const { return false; }
bool operator() (const Float64 & l, const UInt64 & r) const { return Double128(l) < Double128(r); }
bool operator() (const Float64 & l, const Int64 & r) const { return Double128(l) < Double128(r); }
bool operator() (const Float64 & l, const UInt64 & r) const { return accurate::lessOp(l, r); }
bool operator() (const Float64 & l, const Int64 & r) const { return accurate::lessOp(l, r); }
bool operator() (const Float64 & l, const Float64 & r) const { return l < r; }
bool operator() (const Float64 & l, const String & r) const { return true; }
bool operator() (const Float64 & l, const Array & r) const { return true; }
bool operator() (const Float64 & l, const Tuple & r) const { return true; }
bool operator() (const String & l, const Null & r) const { return false; }
bool operator() (const String & l, const UInt64 & r) const { return stringToDateOrDateTime(l) < r; }
bool operator() (const String & l, const UInt64 & r) const { return false; }
bool operator() (const String & l, const Int64 & r) const { return false; }
bool operator() (const String & l, const Float64 & r) const { return false; }
bool operator() (const String & l, const String & r) const { return l < r; }

View File

@ -30,12 +30,12 @@ using DB::UInt64;
// Case 1. Is pair of floats or pair of ints or pair of uints
template <typename A, typename B>
using is_safe_convervsion = std::integral_constant<bool, (std::is_floating_point<A>::value && std::is_floating_point<B>::value)
using is_safe_conversion = std::integral_constant<bool, (std::is_floating_point<A>::value && std::is_floating_point<B>::value)
|| (std::is_integral<A>::value && std::is_integral<B>::value && !(std::is_signed<A>::value ^ std::is_signed<B>::value))>;
template <typename A, typename B>
using bool_if_safe_convervsion = std::enable_if_t<is_safe_convervsion<A, B>::value, bool>;
using bool_if_safe_conversion = std::enable_if_t<is_safe_conversion<A, B>::value, bool>;
template <typename A, typename B>
using bool_if_not_safe_convervsion = std::enable_if_t<!is_safe_convervsion<A, B>::value, bool>;
using bool_if_not_safe_conversion = std::enable_if_t<!is_safe_conversion<A, B>::value, bool>;
/// Case 2. Are params IntXX and UIntYY ?
@ -144,13 +144,13 @@ inline bool_if_double_can_be_used<TAInt, TAFloat> equalsOpTmpl(TAFloat a, TAInt
template <typename A, typename B>
inline bool_if_not_safe_convervsion<A, B> greaterOp(A a, B b)
inline bool_if_not_safe_conversion<A, B> greaterOp(A a, B b)
{
return greaterOpTmpl(a, b);
}
template <typename A, typename B>
inline bool_if_safe_convervsion<A, B> greaterOp(A a, B b)
inline bool_if_safe_conversion<A, B> greaterOp(A a, B b)
{
return a > b;
}
@ -227,13 +227,13 @@ inline bool greaterOp<DB::UInt64, DB::Float32>(DB::UInt64 u, DB::Float32 f)
template <typename A, typename B>
inline bool_if_not_safe_convervsion<A, B> equalsOp(A a, B b)
inline bool_if_not_safe_conversion<A, B> equalsOp(A a, B b)
{
return equalsOpTmpl(a, b);
}
template <typename A, typename B>
inline bool_if_safe_convervsion<A, B> equalsOp(A a, B b)
inline bool_if_safe_conversion<A, B> equalsOp(A a, B b)
{
return a == b;
}
@ -288,52 +288,52 @@ inline bool equalsOp<DB::Int64, DB::Float32>(DB::Int64 u, DB::Float32 f)
template <typename A, typename B>
inline bool_if_not_safe_convervsion<A, B> notEqualsOp(A a, B b)
inline bool_if_not_safe_conversion<A, B> notEqualsOp(A a, B b)
{
return !equalsOp(a, b);
}
template <typename A, typename B>
inline bool_if_safe_convervsion<A, B> notEqualsOp(A a, B b)
inline bool_if_safe_conversion<A, B> notEqualsOp(A a, B b)
{
return a != b;
}
template <typename A, typename B>
inline bool_if_not_safe_convervsion<A, B> lessOp(A a, B b)
inline bool_if_not_safe_conversion<A, B> lessOp(A a, B b)
{
return greaterOp(b, a);
}
template <typename A, typename B>
inline bool_if_safe_convervsion<A, B> lessOp(A a, B b)
inline bool_if_safe_conversion<A, B> lessOp(A a, B b)
{
return a < b;
}
template <typename A, typename B>
inline bool_if_not_safe_convervsion<A, B> lessOrEqualsOp(A a, B b)
inline bool_if_not_safe_conversion<A, B> lessOrEqualsOp(A a, B b)
{
return !greaterOp(a, b);
}
template <typename A, typename B>
inline bool_if_safe_convervsion<A, B> lessOrEqualsOp(A a, B b)
inline bool_if_safe_conversion<A, B> lessOrEqualsOp(A a, B b)
{
return a <= b;
}
template <typename A, typename B>
inline bool_if_not_safe_convervsion<A, B> greaterOrEqualsOp(A a, B b)
inline bool_if_not_safe_conversion<A, B> greaterOrEqualsOp(A a, B b)
{
return !greaterOp(b, a);
}
template <typename A, typename B>
inline bool_if_safe_convervsion<A, B> greaterOrEqualsOp(A a, B b)
inline bool_if_safe_conversion<A, B> greaterOrEqualsOp(A a, B b)
{
return a >= b;
}

View File

@ -1,3 +1,5 @@
#include <Interpreters/convertFieldToType.h>
#include <IO/ReadBufferFromString.h>
#include <IO/ReadHelpers.h>
@ -12,9 +14,7 @@
#include <Functions/DataTypeTraits.h>
#include <Core/FieldVisitors.h>
#include <Interpreters/convertFieldToType.h>
#include <Functions/AccurateComparison.h>
namespace DB
{
@ -43,7 +43,7 @@ static Field convertNumericTypeImpl(const Field & from)
{
From value = from.get<From>();
if (static_cast<long double>(value) != static_cast<long double>(To(value)))
if (!accurate::equalsOp(value, To(value)))
return {};
return Field(typename NearestFieldType<To>::Type(value));
@ -64,6 +64,31 @@ static Field convertNumericType(const Field & from, const IDataType & type)
}
DayNum_t stringToDate(const String & s)
{
ReadBufferFromString in(s);
DayNum_t date{};
readDateText(date, in);
if (!in.eof())
throw Exception("String is too long for Date: " + s);
return date;
}
UInt64 stringToDateTime(const String & s)
{
ReadBufferFromString in(s);
time_t date_time{};
readDateTimeText(date_time, in);
if (!in.eof())
throw Exception("String is too long for DateTime: " + s);
return UInt64(date_time);
}
Field convertFieldToTypeImpl(const Field & src, const IDataType & type)
{
if (type.isNumeric())

View File

@ -484,7 +484,11 @@ bool PKCondition::atomFromAST(const ASTPtr & node, const Context & context, Bloc
if (atom_it == std::end(atom_map))
return false;
if (!is_set_const) /// Set args are already casted inside Set::createFromAST
bool cast_not_needed =
is_set_const /// Set args are already casted inside Set::createFromAST
|| (key_expr_type->behavesAsNumber() && const_type->behavesAsNumber()); /// Numbers are accurately compared without cast.
if (!cast_not_needed)
castValueToType(key_expr_type, const_value, const_type, node);
return atom_it->second(out, const_value, node);