Skip to content

Commit

Permalink
fix: error data input for date(CAST(value AS DATETIME)) causing high …
Browse files Browse the repository at this point in the history
…TiFlash sys CPU (pingcap#5477)

close pingcap#5097
  • Loading branch information
xzhangxian1008 committed Nov 16, 2022
1 parent 83ac860 commit caadc41
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 136 deletions.
62 changes: 36 additions & 26 deletions dbms/src/Common/MyTime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,23 @@ int32_t adjustYear(int32_t year)
return year;
}

void scanTimeArgs(const std::vector<String> & seps, std::initializer_list<int *> && list)
bool scanTimeArgs(const std::vector<String> & seps, std::initializer_list<int *> && list)
{
int i = 0;
for (auto * ptr : list)
try
{
*ptr = std::stoi(seps[i]);
i++;
for (auto * ptr : list)
{
*ptr = std::stoi(seps[i]);
i++;
}
}
catch (std::exception & e)
{
return false;
}

return true;
}

// find index of fractional point.
Expand Down Expand Up @@ -669,7 +678,7 @@ std::pair<Field, bool> parseMyDateTimeAndJudgeIsDate(const String & str, int8_t
}
default:
{
throw TiFlashException("Wrong datetime format: " + str, Errors::Types::WrongValue);
return {Field(), is_date};
}
}
if (l == 5 || l == 6 || l == 8)
Expand Down Expand Up @@ -719,40 +728,44 @@ std::pair<Field, bool> parseMyDateTimeAndJudgeIsDate(const String & str, int8_t
}
if (truncated_or_incorrect)
{
throw TiFlashException("Datetime truncated: " + str, Errors::Types::Truncated);
return {Field(), is_date};
}
break;
}
case 3:
{
// YYYY-MM-DD
scanTimeArgs(seps, {&year, &month, &day});
if (!scanTimeArgs(seps, {&year, &month, &day}))
return {Field(), is_date};
is_date = true;
break;
}
case 4:
{
// YYYY-MM-DD HH
scanTimeArgs(seps, {&year, &month, &day, &hour});
if (!scanTimeArgs(seps, {&year, &month, &day, &hour}))
return {Field(), is_date};
break;
}
case 5:
{
// YYYY-MM-DD HH-MM
scanTimeArgs(seps, {&year, &month, &day, &hour, &minute});
if (!scanTimeArgs(seps, {&year, &month, &day, &hour, &minute}))
return {Field(), is_date};
break;
}
case 6:
{
// We don't have fractional seconds part.
// YYYY-MM-DD HH-MM-SS
scanTimeArgs(seps, {&year, &month, &day, &hour, &minute, &second});
if (!scanTimeArgs(seps, {&year, &month, &day, &hour, &minute, &second}))
return {Field(), is_date};
hhmmss = true;
break;
}
default:
{
throw Exception("Wrong datetime format");
return {Field(), is_date};
}
}

Expand Down Expand Up @@ -809,7 +822,7 @@ std::pair<Field, bool> parseMyDateTimeAndJudgeIsDate(const String & str, int8_t

if (needCheckTimeValid && !checkTimeValid(year, month, day, hour, minute, second))
{
throw Exception("Wrong datetime format");
return {Field(), is_date};
}

MyDateTime result(year, month, day, hour, minute, second, micro_second);
Expand All @@ -818,7 +831,7 @@ std::pair<Field, bool> parseMyDateTimeAndJudgeIsDate(const String & str, int8_t
{
if (!hhmmss)
{
throw TiFlashException("Invalid datetime value: " + str, Errors::Types::WrongValue);
return {Field(), is_date};
}
if (!tz_hour.empty())
{
Expand All @@ -832,7 +845,7 @@ std::pair<Field, bool> parseMyDateTimeAndJudgeIsDate(const String & str, int8_t
if (delta_hour > 14 || delta_minute > 59 || (delta_hour == 14 && delta_minute != 0)
|| (tz_sign == "-" && delta_hour == 0 && delta_minute == 0))
{
throw TiFlashException("Invalid datetime value: " + str, Errors::Types::WrongValue);
return {Field(), is_date};
}
// by default, if the temporal string literal does not contain timezone information, it will be in the timezone
// specified by the time_zone system variable. However, if the timezone is specified in the string literal, we
Expand Down Expand Up @@ -1074,29 +1087,27 @@ size_t maxFormattedDateTimeStringLength(const String & format)
return std::max<size_t>(result, 1);
}

void MyTimeBase::check(bool allow_zero_in_date, bool allow_invalid_date) const
bool MyTimeBase::isValid(bool allow_zero_in_date, bool allow_invalid_date) const
{
if (!(year == 0 && month == 0 && day == 0))
{
if (!allow_zero_in_date && (month == 0 || day == 0))
{
throw TiFlashException(
fmt::format("Incorrect datetime value: {0:04d}-{1:02d}-{2:02d}", year, month, day),
Errors::Types::WrongValue);
return false;
}
}

if (year >= 9999 || month > 12)
{
throw TiFlashException("Incorrect time value", Errors::Types::WrongValue);
return false;
}

UInt8 max_day = 31;
if (!allow_invalid_date)
{
if (month < 1)
{
throw TiFlashException(fmt::format("Incorrect time value: month {}", month), Errors::Types::WrongValue);
return false;
}
constexpr static UInt8 max_days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static auto is_leap_year = [](UInt16 _year) {
Expand All @@ -1110,23 +1121,22 @@ void MyTimeBase::check(bool allow_zero_in_date, bool allow_invalid_date) const
}
if (day > max_day)
{
throw TiFlashException(
fmt::format("Incorrect datetime value: {0:04d}-{1:02d}-{2:02d}", year, month, day),
Errors::Types::WrongValue);
return false;
}

if (hour < 0 || hour >= 24)
{
throw TiFlashException("Incorrect datetime value", Errors::Types::WrongValue);
return false;
}
if (minute >= 60)
{
throw TiFlashException("Incorrect datetime value", Errors::Types::WrongValue);
return false;
}
if (second >= 60)
{
throw TiFlashException("Incorrect datetime value", Errors::Types::WrongValue);
return false;
}
return true;
}

bool toCoreTimeChecked(const UInt64 & year, const UInt64 & month, const UInt64 & day, const UInt64 & hour, const UInt64 & minute, const UInt64 & second, const UInt64 & microsecond, MyDateTime & result)
Expand Down
11 changes: 7 additions & 4 deletions dbms/src/Common/MyTime.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ struct MyTimeBase
std::tuple<int, int> calcWeek(UInt32 mode) const;

// Check validity of time under specified SQL_MODE.
// May throw exception.
void check(bool allow_zero_in_date, bool allow_invalid_date) const;
// return false if time is invalid
bool isValid(bool allow_zero_in_date, bool allow_invalid_date) const;
};

struct MyDateTime : public MyTimeBase
Expand Down Expand Up @@ -181,8 +181,11 @@ struct MyDateTimeParser
std::vector<ParserCallback> parsers;
};

Field parseMyDateTime(const String & str, int8_t fsp = 6, bool needCheckTimeValid = false);
std::pair<Field, bool> parseMyDateTimeAndJudgeIsDate(const String & str, int8_t fsp = 6, bool needCheckTimeValid = false);
static int8_t default_fsp = 6;
static bool default_needCheckTimeValid = false;

Field parseMyDateTime(const String & str, int8_t fsp = default_fsp, bool needCheckTimeValid = default_needCheckTimeValid);
std::pair<Field, bool> parseMyDateTimeAndJudgeIsDate(const String & str, int8_t fsp = default_fsp, bool needCheckTimeValid = default_needCheckTimeValid);

void convertTimeZone(UInt64 from_time, UInt64 & to_time, const DateLUTImpl & time_zone_from, const DateLUTImpl & time_zone_to, bool throw_exception = false);

Expand Down
2 changes: 1 addition & 1 deletion dbms/src/Common/tests/gtest_mytime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class TestMyTime : public testing::Test
if (expect_error)
{
MyDateTime datetime(0, 0, 0, 0, 0, 0, 0);
EXPECT_THROW({ numberToDateTime(input, datetime, ctx); }, TiFlashException) << "Original time number: " << input;
EXPECT_TRUE(numberToDateTime(input, datetime, ctx));
return;
}

Expand Down
Loading

0 comments on commit caadc41

Please sign in to comment.