Fix bugs in DateTime64 parsing

This commit is contained in:
Alexey Milovidov 2020-06-22 23:54:36 +03:00
parent 75357ab98d
commit 4ace4b4c75

View File

@ -275,8 +275,11 @@ ReturnType readIntTextImpl(T & x, ReadBuffer & buf)
switch (*buf.position()) switch (*buf.position())
{ {
case '+': case '+':
{
break; break;
}
case '-': case '-':
{
if constexpr (is_signed_v<T>) if constexpr (is_signed_v<T>)
negative = true; negative = true;
else else
@ -287,6 +290,7 @@ ReturnType readIntTextImpl(T & x, ReadBuffer & buf)
return ReturnType(false); return ReturnType(false);
} }
break; break;
}
case '0': [[fallthrough]]; case '0': [[fallthrough]];
case '1': [[fallthrough]]; case '1': [[fallthrough]];
case '2': [[fallthrough]]; case '2': [[fallthrough]];
@ -297,20 +301,27 @@ ReturnType readIntTextImpl(T & x, ReadBuffer & buf)
case '7': [[fallthrough]]; case '7': [[fallthrough]];
case '8': [[fallthrough]]; case '8': [[fallthrough]];
case '9': case '9':
{
if constexpr (check_overflow == ReadIntTextCheckOverflow::CHECK_OVERFLOW) if constexpr (check_overflow == ReadIntTextCheckOverflow::CHECK_OVERFLOW)
{ {
// perform relativelly slow overflow check only when number of decimal digits so far is close to the max for given type. /// Perform relativelly slow overflow check only when
if (buf.count() - initial_pos >= std::numeric_limits<T>::max_digits10) /// number of decimal digits so far is close to the max for given type.
/// Example: 20 * 10 will overflow Int8.
if (buf.count() - initial_pos + 1 >= std::numeric_limits<T>::max_digits10)
{ {
if (common::mulOverflow(res, static_cast<decltype(res)>(10), res) T signed_res = res;
|| common::addOverflow(res, static_cast<decltype(res)>(*buf.position() - '0'), res)) if (common::mulOverflow<T>(signed_res, 10, signed_res)
|| common::addOverflow<T>(signed_res, (*buf.position() - '0'), signed_res))
return ReturnType(false); return ReturnType(false);
res = signed_res;
break; break;
} }
} }
res *= 10; res *= 10;
res += *buf.position() - '0'; res += *buf.position() - '0';
break; break;
}
default: default:
goto end; goto end;
} }
@ -318,7 +329,23 @@ ReturnType readIntTextImpl(T & x, ReadBuffer & buf)
} }
end: end:
x = negative ? -res : res; if (!negative)
{
x = res;
}
else
{
if constexpr (check_overflow == ReadIntTextCheckOverflow::CHECK_OVERFLOW)
{
x = res;
if (common::mulOverflow<T>(x, -1, x))
return ReturnType(false);
}
else
{
x = -res;
}
}
return ReturnType(true); return ReturnType(true);
} }
@ -665,9 +692,7 @@ inline ReturnType readDateTimeTextImpl(DateTime64 & datetime64, UInt32 scale, Re
buf.ignore(1); // skip separator buf.ignore(1); // skip separator
const auto pos_before_fractional = buf.count(); const auto pos_before_fractional = buf.count();
if (!tryReadIntText<ReadIntTextCheckOverflow::CHECK_OVERFLOW>(c.fractional, buf)) if (!tryReadIntText<ReadIntTextCheckOverflow::CHECK_OVERFLOW>(c.fractional, buf))
{
return ReturnType(false); return ReturnType(false);
}
// Adjust fractional part to the scale, since decimalFromComponents knows nothing // Adjust fractional part to the scale, since decimalFromComponents knows nothing
// about convention of ommiting trailing zero on fractional part // about convention of ommiting trailing zero on fractional part
@ -676,13 +701,15 @@ inline ReturnType readDateTimeTextImpl(DateTime64 & datetime64, UInt32 scale, Re
// If scale is 3, but we read '12', promote fractional part to '120'. // If scale is 3, but we read '12', promote fractional part to '120'.
// And vice versa: if we read '1234', denote it to '123'. // And vice versa: if we read '1234', denote it to '123'.
const auto fractional_length = static_cast<Int32>(buf.count() - pos_before_fractional); const auto fractional_length = static_cast<Int32>(buf.count() - pos_before_fractional);
if (const auto adjust_scale = static_cast<Int32>(scale) - fractional_length; adjust_scale > 0)
const auto adjust_scale = static_cast<Int32>(scale) - fractional_length;
if (adjust_scale > 0)
{ {
c.fractional *= common::exp10_i64(adjust_scale); c.fractional *= common::exp10_i64(adjust_scale);
} }
else if (adjust_scale < 0) else if (adjust_scale < 0)
{ {
c.fractional /= common::exp10_i64(-1 * adjust_scale); c.fractional /= common::exp10_i64(-adjust_scale);
} }
} }