From 113d9fe474073995cef592c7498c35c542fc3bde Mon Sep 17 00:00:00 2001 From: Meng Xin Date: Fri, 16 Sep 2022 13:34:59 +0800 Subject: [PATCH] This is an automated cherry-pick of #5799 Signed-off-by: ti-chi-bot --- dbms/src/Common/MyTime.cpp | 178 ++++- dbms/src/Common/MyTime.h | 18 + dbms/src/Common/tests/gtest_mytime.cpp | 8 +- dbms/src/Functions/FunctionsDateTime.h | 12 + dbms/src/Functions/FunctionsTiDBConversion.h | 49 +- .../Functions/tests/gtest_tidb_conversion.cpp | 697 ++++++++++++++++++ tests/fullstack-test/expr/cast_as_time.test | 80 +- 7 files changed, 1033 insertions(+), 9 deletions(-) diff --git a/dbms/src/Common/MyTime.cpp b/dbms/src/Common/MyTime.cpp index 279462c9875..3b75e013df1 100644 --- a/dbms/src/Common/MyTime.cpp +++ b/dbms/src/Common/MyTime.cpp @@ -503,7 +503,16 @@ int MyTimeBase::weekDay() const bool checkTimeValid(Int32 year, Int32 month, Int32 day, Int32 hour, Int32 minute, Int32 second) { +<<<<<<< HEAD if (year > 9999 || month < 1 || month > 12 || day < 1 || day > 31 || hour > 23 || minute > 59 || second > 59) +======= + return month != 0 && day != 0 && checkTimeValidAllowMonthAndDayZero(year, month, day, hour, minute, second); +} + +bool checkTimeValidAllowMonthAndDayZero(Int32 year, Int32 month, Int32 day, Int32 hour, Int32 minute, Int32 second) +{ + if (year > 9999 || month < 0 || month > 12 || day < 0 || day > 31 || hour > 23 || minute > 59 || second > 59) +>>>>>>> 720bfc1787 (fix that the result of expression cast(Real/Decimal)AsTime is inconsistent with TiDB (#5799)) { return false; } @@ -518,7 +527,123 @@ bool checkTimeValid(Int32 year, Int32 month, Int32 day, Int32 hour, Int32 minute return day <= (is_leap_year ? 29 : 28); } +<<<<<<< HEAD std::pair parseMyDateTimeAndJudgeIsDate(const String & str, int8_t fsp, bool needCheckTimeValid) +======= +bool noNeedCheckTime(Int32, Int32, Int32, Int32, Int32, Int32) +{ + return true; +} + +// Return true if the time is invalid. +inline bool getDatetime(const Int64 & num, MyDateTime & result) +{ + UInt64 ymd = num / 1000000; + UInt64 hms = num - ymd * 1000000; + + UInt64 year = ymd / 10000; + ymd %= 10000; + UInt64 month = ymd / 100; + UInt64 day = ymd % 100; + + UInt64 hour = hms / 10000; + hms %= 10000; + UInt64 minute = hms / 100; + UInt64 second = hms % 100; + + if (toCoreTimeChecked(year, month, day, hour, minute, second, 0, result)) + { + return true; + } + return !result.isValid(true, false); +} + +// Convert a integer number to DateTime and return true if the result is NULL. +// If number is invalid(according to SQL_MODE), return NULL and handle the error with DAGContext. +// This function may throw exception. +inline bool numberToDateTime(Int64 number, MyDateTime & result, bool allowZeroDate) +{ + MyDateTime datetime(0); + if (number == 0) + { + if (allowZeroDate) + { + result = datetime; + return false; + } + return true; + } + + // datetime type + if (number >= 10000101000000) + { + return getDatetime(number, result); + } + + // check MMDD + if (number < 101) + { + return true; + } + + // check YYMMDD: 2000-2069 + if (number <= 69 * 10000 + 1231) + { + number = (number + 20000000) * 1000000; + return getDatetime(number, result); + } + + if (number < 70 * 10000 + 101) + { + return true; + } + + // check YYMMDD + if (number <= 991231) + { + number = (number + 19000000) * 1000000; + return getDatetime(number, result); + } + + // check hour/min/second + if (number <= 99991231) + { + number *= 1000000; + return getDatetime(number, result); + } + + // check MMDDHHMMSS + if (number < 101000000) + { + return true; + } + + // check YYMMDDhhmmss: 2000-2069 + if (number <= 69 * 10000000000 + 1231235959) + { + number += 20000000000000; + return getDatetime(number, result); + } + + // check YYYYMMDDhhmmss + if (number < 70 * 10000000000 + 101000000) + { + return true; + } + + // check YYMMDDHHMMSS + if (number <= 991231235959) + { + number += 19000000000000; + return getDatetime(number, result); + } + + return getDatetime(number, result); +} + +// isFloat is true means that the input string is float format like "1212.111" +std::pair parseMyDateTimeAndJudgeIsDate(const String & str, int8_t fsp, CheckTimeFunc checkTimeFunc, bool isFloat) +>>>>>>> 720bfc1787 (fix that the result of expression cast(Real/Decimal)AsTime is inconsistent with TiDB (#5799)) { // Since we only use DateLUTImpl as parameter placeholder of AddSecondsImpl::execute // and it's costly to construct a DateLUTImpl, a shared static instance is enough. @@ -543,7 +668,7 @@ std::pair parseMyDateTimeAndJudgeIsDate(const String & str, int8_t return seps.size() > 5 || (seps.size() == 1 && seps[0].size() > 4); }; - if (!frac_str.empty()) + if (!frac_str.empty() && !isFloat) { if (!no_absorb(seps)) { @@ -577,6 +702,24 @@ std::pair parseMyDateTimeAndJudgeIsDate(const String & str, int8_t case 1: { size_t l = seps[0].size(); + if (isFloat) + { + MyDateTime date_time(0); + if (seps[0] == "0") + { + return {date_time.toPackedUInt(), is_date}; + } + if (numberToDateTime(std::stoll(seps[0]), date_time)) + { + return {Field(), is_date}; + } + std::tie(year, month, day, hour, minute, second) = std::tuple(date_time.year, date_time.month, date_time.day, date_time.hour, date_time.minute, date_time.second); + if (l >= 9 && l <= 14) + { + hhmmss = true; + } + break; + } switch (l) { case 14: // YYYYMMDDHHMMSS @@ -720,7 +863,7 @@ std::pair parseMyDateTimeAndJudgeIsDate(const String & str, int8_t // If str is sepereated by delimiters, the first one is year, and if the year is 2 digit, // we should adjust it. // TODO: adjust year is very complex, now we only consider the simplest way. - if (seps[0].size() == 2) + if (seps[0].size() <= 2 && !isFloat) { if (year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 && second == 0 && frac_str.empty()) { @@ -768,7 +911,11 @@ std::pair parseMyDateTimeAndJudgeIsDate(const String & str, int8_t } } +<<<<<<< HEAD if (needCheckTimeValid && !checkTimeValid(year, month, day, hour, minute, second)) +======= + if (!checkTimeFunc(year, month, day, hour, minute, second)) +>>>>>>> 720bfc1787 (fix that the result of expression cast(Real/Decimal)AsTime is inconsistent with TiDB (#5799)) { throw Exception("Wrong datetime format"); } @@ -811,9 +958,20 @@ std::pair parseMyDateTimeAndJudgeIsDate(const String & str, int8_t } // TODO: support parse time from float string +<<<<<<< HEAD Field parseMyDateTime(const String & str, int8_t fsp, bool needCheckTimeValid) { return parseMyDateTimeAndJudgeIsDate(str, fsp, needCheckTimeValid).first; +======= +Field parseMyDateTime(const String & str, int8_t fsp, CheckTimeFunc checkTimeFunc) +{ + return parseMyDateTimeAndJudgeIsDate(str, fsp, checkTimeFunc).first; +} + +Field parseMyDateTimeFromFloat(const String & str, int8_t fsp, CheckTimeFunc checkTimeFunc) +{ + return parseMyDateTimeAndJudgeIsDate(str, fsp, checkTimeFunc, true).first; +>>>>>>> 720bfc1787 (fix that the result of expression cast(Real/Decimal)AsTime is inconsistent with TiDB (#5799)) } String MyDateTime::toString(int fsp) const @@ -1037,7 +1195,7 @@ void MyTimeBase::check(bool allow_zero_in_date, bool allow_invalid_date) const } } - if (year >= 9999 || month > 12) + if (year > 9999 || month > 12) { throw TiFlashException("Incorrect time value", Errors::Types::WrongValue); } @@ -1049,10 +1207,22 @@ void MyTimeBase::check(bool allow_zero_in_date, bool allow_invalid_date) const static auto is_leap_year = [](UInt16 _year) { return ((_year % 4 == 0) && (_year % 100 != 0)) || (_year % 400 == 0); }; +<<<<<<< HEAD max_day = max_days_in_month[month - 1]; // NOLINT if (month == 2 && is_leap_year(year)) +======= + if (allow_zero_in_date && month == 0) +>>>>>>> 720bfc1787 (fix that the result of expression cast(Real/Decimal)AsTime is inconsistent with TiDB (#5799)) { - max_day = 29; + max_day = 31; + } + else + { + max_day = max_days_in_month[month - 1]; // NOLINT + if (month == 2 && is_leap_year(year)) + { + max_day = 29; + } } } if (day > max_day) diff --git a/dbms/src/Common/MyTime.h b/dbms/src/Common/MyTime.h index 85aa4f20a5a..966ef45bcb1 100644 --- a/dbms/src/Common/MyTime.h +++ b/dbms/src/Common/MyTime.h @@ -128,6 +128,8 @@ struct MyDate : public MyTimeBase } }; +bool numberToDateTime(Int64 number, MyDateTime & result, bool allowZeroDate = true); + struct MyDateTimeFormatter { std::vector> formatters; @@ -159,8 +161,24 @@ struct MyDateTimeParser std::vector parsers; }; +<<<<<<< HEAD Field parseMyDateTime(const String & str, int8_t fsp = 6, bool needCheckTimeValid = false); std::pair parseMyDateTimeAndJudgeIsDate(const String & str, int8_t fsp = 6, bool needCheckTimeValid = false); +======= +bool checkTimeValid(Int32 year, Int32 month, Int32 day, Int32 hour, Int32 minute, Int32 second); +bool checkTimeValidAllowMonthAndDayZero(Int32 year, Int32 month, Int32 day, Int32 hour, Int32 minute, Int32 second); +bool noNeedCheckTime(Int32, Int32, Int32, Int32, Int32, Int32); + +using CheckTimeFunc = std::function; + +static const int8_t DefaultFsp = 6; +static bool DefaultIsFloat = false; +static CheckTimeFunc DefaultCheckTimeFunc = noNeedCheckTime; + +Field parseMyDateTime(const String & str, int8_t fsp = DefaultFsp, CheckTimeFunc checkTimeFunc = DefaultCheckTimeFunc); +Field parseMyDateTimeFromFloat(const String & str, int8_t fsp = DefaultFsp, CheckTimeFunc checkTimeFunc = DefaultCheckTimeFunc); +std::pair parseMyDateTimeAndJudgeIsDate(const String & str, int8_t fsp = DefaultFsp, CheckTimeFunc checkTimeFunc = DefaultCheckTimeFunc, bool isFloat = DefaultIsFloat); +>>>>>>> 720bfc1787 (fix that the result of expression cast(Real/Decimal)AsTime is inconsistent with TiDB (#5799)) void convertTimeZone(UInt64 from_time, UInt64 & to_time, const DateLUTImpl & time_zone_from, const DateLUTImpl & time_zone_to, bool throw_exception = false); diff --git a/dbms/src/Common/tests/gtest_mytime.cpp b/dbms/src/Common/tests/gtest_mytime.cpp index 679a2752125..b0b1e062e03 100644 --- a/dbms/src/Common/tests/gtest_mytime.cpp +++ b/dbms/src/Common/tests/gtest_mytime.cpp @@ -57,19 +57,23 @@ class TestMyTime : public testing::Test } } - static void checkNumberToMyDateTime(const Int64 & input, const MyDateTime & expected, bool expect_error, DAGContext * ctx) + static void checkNumberToMyDateTime(const Int64 & input, const MyDateTime & expected, bool expect_error, DAGContext *) { if (expect_error) { MyDateTime datetime(0, 0, 0, 0, 0, 0, 0); +<<<<<<< HEAD EXPECT_THROW({ numberToDateTime(input, datetime, ctx); }, TiFlashException) << "Original time number: " << input; +======= + EXPECT_TRUE(numberToDateTime(input, datetime)); +>>>>>>> 720bfc1787 (fix that the result of expression cast(Real/Decimal)AsTime is inconsistent with TiDB (#5799)) return; } try { MyDateTime source(0, 0, 0, 0, 0, 0, 0); - numberToDateTime(input, source, ctx); + numberToDateTime(input, source); EXPECT_EQ(source.year, expected.year) << "Original time number: " << input; EXPECT_EQ(source.month, expected.month) << "Original time number: " << input; EXPECT_EQ(source.day, expected.day) << "Original time number: " << input; diff --git a/dbms/src/Functions/FunctionsDateTime.h b/dbms/src/Functions/FunctionsDateTime.h index 6083335ace6..543a581f36a 100644 --- a/dbms/src/Functions/FunctionsDateTime.h +++ b/dbms/src/Functions/FunctionsDateTime.h @@ -1007,7 +1007,11 @@ struct AddSecondsImpl // TODO: need do these in vector mode in the future static inline String execute(String str, Int64 delta, const DateLUTImpl & time_zone) { +<<<<<<< HEAD Field packed_uint_value = parseMyDateTime(str); +======= + Field packed_uint_value = parseMyDateTime(str, 6, checkTimeValid); +>>>>>>> 720bfc1787 (fix that the result of expression cast(Real/Decimal)AsTime is inconsistent with TiDB (#5799)) UInt64 packed_uint = packed_uint_value.template safeGet(); UInt64 result = AddSecondsImpl::execute(packed_uint, delta, time_zone); MyDateTime myDateTime(result); @@ -1091,7 +1095,11 @@ struct AddDaysImpl static inline String execute(String str, Int64 delta, const DateLUTImpl & time_zone) { +<<<<<<< HEAD auto value_and_is_date = parseMyDateTimeAndJudgeIsDate(str, 6, true); +======= + auto value_and_is_date = parseMyDateTimeAndJudgeIsDate(str, 6, checkTimeValid); +>>>>>>> 720bfc1787 (fix that the result of expression cast(Real/Decimal)AsTime is inconsistent with TiDB (#5799)) Field packed_uint_value = value_and_is_date.first; bool is_date = value_and_is_date.second; UInt64 packed_uint = packed_uint_value.template safeGet(); @@ -1159,7 +1167,11 @@ struct AddMonthsImpl static inline String execute(String str, Int64 delta, const DateLUTImpl & time_zone) { +<<<<<<< HEAD auto value_and_is_date = parseMyDateTimeAndJudgeIsDate(str, 6, true); +======= + auto value_and_is_date = parseMyDateTimeAndJudgeIsDate(str, 6, checkTimeValid); +>>>>>>> 720bfc1787 (fix that the result of expression cast(Real/Decimal)AsTime is inconsistent with TiDB (#5799)) Field packed_uint_value = value_and_is_date.first; bool is_date = value_and_is_date.second; UInt64 packed_uint = packed_uint_value.template safeGet(); diff --git a/dbms/src/Functions/FunctionsTiDBConversion.h b/dbms/src/Functions/FunctionsTiDBConversion.h index fde9cbca1d4..d944e6aec2f 100644 --- a/dbms/src/Functions/FunctionsTiDBConversion.h +++ b/dbms/src/Functions/FunctionsTiDBConversion.h @@ -1240,7 +1240,7 @@ struct TiDBConvertToTime size_t result, bool, const tipb::FieldType &, - const Context & context) + const Context &) { size_t size = block.getByPosition(arguments[0]).column->size(); auto col_to = ColumnUInt64::create(size, 0); @@ -1280,7 +1280,14 @@ struct TiDBConvertToTime size_t string_size = next_offset - current_offset - 1; StringRef string_ref(&(*chars)[current_offset], string_size); String string_value = string_ref.toString(); +<<<<<<< HEAD try +======= + + Field packed_uint_value = parseMyDateTime(string_value, to_fsp, checkTimeValidAllowMonthAndDayZero); + + if (packed_uint_value.isNull()) +>>>>>>> 720bfc1787 (fix that the result of expression cast(Real/Decimal)AsTime is inconsistent with TiDB (#5799)) { Field packed_uint_value = parseMyDateTime(string_value, to_fsp); UInt64 packed_uint = packed_uint_value.template safeGet(); @@ -1361,7 +1368,14 @@ struct TiDBConvertToTime for (size_t i = 0; i < size; i++) { +<<<<<<< HEAD try +======= + MyDateTime datetime(0, 0, 0, 0, 0, 0, 0); + bool is_null = numberToDateTime(vec_from[i], datetime, false); + + if (is_null) +>>>>>>> 720bfc1787 (fix that the result of expression cast(Real/Decimal)AsTime is inconsistent with TiDB (#5799)) { MyDateTime datetime(0, 0, 0, 0, 0, 0, 0); bool is_null = numberToDateTime(vec_from[i], datetime, context.getDAGContext()); @@ -1402,9 +1416,13 @@ struct TiDBConvertToTime // Convert to string and then parse to time String value_str = toString(value); - if (value_str == "0") + Field packed_uint_value = parseMyDateTimeFromFloat(value_str, to_fsp, noNeedCheckTime); + + if (packed_uint_value.isNull()) { + // Fill NULL if cannot parse (*vec_null_map_to)[i] = 1; +<<<<<<< HEAD } else { @@ -1429,6 +1447,22 @@ struct TiDBConvertToTime (*vec_null_map_to)[i] = 1; context.getDAGContext()->handleInvalidTime("Invalid time value: '" + value_str + "'", Errors::Types::WrongValue); } +======= + vec_to[i] = 0; + continue; + } + + UInt64 packed_uint = packed_uint_value.template safeGet(); + MyDateTime datetime(packed_uint); + if constexpr (std::is_same_v) + { + MyDate date(datetime.year, datetime.month, datetime.day); + vec_to[i] = date.toPackedUInt(); + } + else + { + vec_to[i] = packed_uint; +>>>>>>> 720bfc1787 (fix that the result of expression cast(Real/Decimal)AsTime is inconsistent with TiDB (#5799)) } } } @@ -1441,7 +1475,14 @@ struct TiDBConvertToTime for (size_t i = 0; i < size; i++) { String value_str = vec_from[i].toString(type.getScale()); +<<<<<<< HEAD try +======= + + Field value = parseMyDateTimeFromFloat(value_str, to_fsp, noNeedCheckTime); + + if (value.getType() == Field::Types::Null) +>>>>>>> 720bfc1787 (fix that the result of expression cast(Real/Decimal)AsTime is inconsistent with TiDB (#5799)) { Field value = parseMyDateTime(value_str, to_fsp); MyDateTime datetime(value.template safeGet()); @@ -1553,6 +1594,7 @@ struct TiDBConvertToDuration } }; +<<<<<<< HEAD inline bool getDatetime(const Int64 & num, MyDateTime & result, DAGContext * ctx) { UInt64 ymd = num / 1000000; @@ -1662,6 +1704,9 @@ inline bool numberToDateTime(Int64 number, MyDateTime & result, DAGContext * ctx return getDatetime(number, result, ctx); } +======= +template +>>>>>>> 720bfc1787 (fix that the result of expression cast(Real/Decimal)AsTime is inconsistent with TiDB (#5799)) class ExecutableFunctionTiDBCast : public IExecutableFunction { public: diff --git a/dbms/src/Functions/tests/gtest_tidb_conversion.cpp b/dbms/src/Functions/tests/gtest_tidb_conversion.cpp index 887e896168f..f366fbda00d 100644 --- a/dbms/src/Functions/tests/gtest_tidb_conversion.cpp +++ b/dbms/src/Functions/tests/gtest_tidb_conversion.cpp @@ -49,6 +49,215 @@ const UInt64 MAX_UINT64 = std::numeric_limits::max(); class TestTidbConversion : public DB::tests::FunctionTest { +<<<<<<< HEAD +======= +public: + template + void testNotOnlyNull(const Input & input, const Output & output) + { + static_assert(!IsDecimal && !std::is_same_v); + auto inner_test = [&](bool is_const) { + ASSERT_COLUMN_EQ( + is_const ? createConstColumn>(1, output) : createColumn>({output}), + executeFunction( + func_name, + {is_const ? createConstColumn>(1, input) : createColumn>({input}), + createCastTypeConstColumn(fmt::format("Nullable({})", TypeName::get()))})); + }; + inner_test(true); + inner_test(false); + } + + template + typename std::enable_if, void>::type testNotOnlyNull(const Input & input, const DecimalField & output, const std::tuple & meta) + { + auto inner_test = [&](bool is_const) { + ASSERT_COLUMN_EQ( + is_const ? createConstColumn>(meta, 1, output) : createColumn>(meta, {output}), + executeFunction( + func_name, + {is_const ? createConstColumn>(1, input) : createColumn>({input}), + createCastTypeConstColumn(fmt::format("Nullable(Decimal({},{}))", std::get<0>(meta), std::get<1>(meta)))})); + }; + inner_test(true); + inner_test(false); + } + + template + typename std::enable_if, void>::type testNotOnlyNull(const DecimalField & input, const MyDateTime & output, int fraction) + { + auto meta = std::make_tuple(19, input.getScale()); + auto inner_test = [&](bool is_const) { + ASSERT_COLUMN_EQ( + is_const ? createDateTimeColumnConst(1, output, fraction) : createDateTimeColumn({output}, fraction), + executeFunction( + func_name, + {is_const ? createConstColumn>(meta, 1, input) : createColumn>(meta, {input}), + createCastTypeConstColumn(fmt::format("Nullable(MyDateTime({}))", fraction))})); + }; + inner_test(true); + inner_test(false); + } + + template + typename std::enable_if, void>::type testNotOnlyNull(const Input & input, const MyDateTime & output, int fraction) + { + auto inner_test = [&](bool is_const) { + ASSERT_COLUMN_EQ( + is_const ? createDateTimeColumnConst(1, output, fraction) : createDateTimeColumn({output}, fraction), + executeFunction( + func_name, + {is_const ? createConstColumn>(1, input) : createColumn>({input}), + createCastTypeConstColumn(fmt::format("Nullable(MyDateTime({}))", fraction))})); + }; + inner_test(true); + inner_test(false); + } + + template + void testThrowException(const Input & input) + { + static_assert(!IsDecimal && !std::is_same_v); + auto inner_test = [&](bool is_const) { + ASSERT_THROW( + executeFunction( + func_name, + {is_const ? createConstColumn>(1, input) : createColumn>({input}), + createCastTypeConstColumn(fmt::format("Nullable({})", TypeName::get()))}), + TiFlashException); + }; + inner_test(true); + inner_test(false); + } + + template + typename std::enable_if, void>::type testThrowException(const Input & input, const std::tuple & meta) + { + auto inner_test = [&](bool is_const) { + ASSERT_THROW( + executeFunction( + func_name, + {is_const ? createConstColumn>(1, input) : createColumn>({input}), + createCastTypeConstColumn(fmt::format("Nullable(Decimal({},{}))", std::get<0>(meta), std::get<1>(meta)))}), + TiFlashException); + }; + inner_test(true); + inner_test(false); + } + + template + typename std::enable_if, void>::type testThrowException(const Input & input, int fraction) + { + auto inner_test = [&](bool is_const) { + ASSERT_THROW( + executeFunction( + func_name, + {is_const ? createConstColumn>(1, input) : createColumn>({input}), + createCastTypeConstColumn(fmt::format("Nullable(MyDateTime({}))", fraction))}), + TiFlashException); + }; + inner_test(true); + inner_test(false); + } + + template + typename std::enable_if, void>::type testReturnNull(const Input & input, int fraction) + { + auto inner_test = [&](bool is_const) { + ASSERT_COLUMN_EQ( + is_const ? createDateTimeColumnConst(1, {}, fraction) : createDateTimeColumn({{}}, fraction), + executeFunction( + func_name, + {is_const ? createConstColumn>(1, input) : createColumn>({input}), + createCastTypeConstColumn(fmt::format("Nullable(MyDateTime({}))", fraction))})); + }; + inner_test(true); + inner_test(false); + } + + template + typename std::enable_if, void>::type testReturnNull(const DecimalField & input, const std::tuple & meta, int fraction) + { + auto inner_test = [&](bool is_const) { + ASSERT_COLUMN_EQ( + is_const ? createDateTimeColumnConst(1, {}, fraction) : createDateTimeColumn({{}}, fraction), + executeFunction( + func_name, + {is_const ? createConstColumn>(meta, 1, input) : createColumn>(meta, {input}), + createCastTypeConstColumn(fmt::format("Nullable(MyDateTime({}))", fraction))})); + }; + inner_test(true); + inner_test(false); + } + + template + void testOnlyNull() + { + std::vector nulls = { + createOnlyNullColumnConst(1), + createOnlyNullColumn(1), + createColumn>({{}}), + createConstColumn>(1, {})}; + + auto inner_test = [&](const ColumnWithTypeAndName & null_one) { + if constexpr (IsDecimal) + { + auto precision = 0; + if constexpr (std::is_same_v) + { + precision = 9; + } + else if constexpr (std::is_same_v) + { + precision = 18; + } + else if constexpr (std::is_same_v) + { + precision = 38; + } + else + { + static_assert(std::is_same_v); + precision = 65; + } + auto meta = std::make_tuple(precision, 0); + auto res = null_one.column->isColumnConst() + ? createConstColumn>(meta, 1, std::optional>{}) + : createColumn>(meta, {std::optional>{}}); + ASSERT_COLUMN_EQ( + res, + executeFunction( + func_name, + {null_one, + createCastTypeConstColumn(fmt::format("Nullable(Decimal({},0))", precision))})); + } + else if constexpr (std::is_same_v) + { + auto res = null_one.column->isColumnConst() ? createDateTimeColumnConst(1, {}, 6) : createDateTimeColumn({{}}, 6); + ASSERT_COLUMN_EQ( + res, + executeFunction( + func_name, + {null_one, + createCastTypeConstColumn("Nullable(MyDateTime(6))")})); + } + else + { + auto res = null_one.column->isColumnConst() ? createConstColumn>(1, {}) : createColumn>({{}}); + ASSERT_COLUMN_EQ( + res, + executeFunction( + func_name, + {null_one, + createCastTypeConstColumn(fmt::format("Nullable({})", TypeName::get()))})); + } + }; + for (const auto & null_one : nulls) + { + inner_test(null_one); + } + } +>>>>>>> 720bfc1787 (fix that the result of expression cast(Real/Decimal)AsTime is inconsistent with TiDB (#5799)) }; using DecimalField32 = DecimalField; @@ -731,8 +940,321 @@ try ASSERT_THROW( executeFunction(func_name, {createColumn>({{}, -20211026160859}), +<<<<<<< HEAD createCastTypeConstColumn("Nullable(MyDateTime(6))")}), TiFlashException); +======= + createCastTypeConstColumn("Nullable(MyDateTime(6))")})); +} +CATCH + +TEST_F(TestTidbConversion, castRealAsInt) +try +{ + testOnlyNull(); + testOnlyNull(); + testOnlyNull(); + testOnlyNull(); + + testNotOnlyNull(0, 0); + testThrowException(MAX_FLOAT32); + testNotOnlyNull(MIN_FLOAT32, 0); + testNotOnlyNull(12.213f, 12); + testNotOnlyNull(-12.213f, -12); + testNotOnlyNull(12.513f, 13); + testNotOnlyNull(-12.513f, -13); + + testNotOnlyNull(0, 0); + testThrowException(MAX_FLOAT32); + testNotOnlyNull(MIN_FLOAT32, 0); + testNotOnlyNull(12.213f, 12); + testThrowException(-12.213f); + testNotOnlyNull(12.513f, 13); + testThrowException(-12.513f); + + testNotOnlyNull(0, 0); + testThrowException(MAX_FLOAT64); + testNotOnlyNull(MIN_FLOAT64, 0); + testNotOnlyNull(12.213, 12); + testNotOnlyNull(-12.213, -12); + testNotOnlyNull(12.513, 13); + testNotOnlyNull(-12.513, -13); + + testNotOnlyNull(0, 0); + testThrowException(MAX_FLOAT64); + testNotOnlyNull(MIN_FLOAT64, 0); + testNotOnlyNull(12.213, 12); + testThrowException(-12.213); + testNotOnlyNull(12.513, 13); + testNotOnlyNull(-12.513, -13); +} +CATCH + +TEST_F(TestTidbConversion, castRealAsReal) +try +{ + testOnlyNull(); + testOnlyNull(); + + testNotOnlyNull(0, 0); + testNotOnlyNull(12.213, 12.213000297546387); + testNotOnlyNull(-12.213, -12.213000297546387); + testNotOnlyNull(MIN_FLOAT32, MIN_FLOAT32); + testNotOnlyNull(MAX_FLOAT32, MAX_FLOAT32); + + testNotOnlyNull(0, 0); + testNotOnlyNull(12.213, 12.213); + testNotOnlyNull(-12.213, -12.213); + testNotOnlyNull(MIN_FLOAT64, MIN_FLOAT64); + testNotOnlyNull(MAX_FLOAT64, MAX_FLOAT64); +} +CATCH + +TEST_F(TestTidbConversion, castRealAsString) +try +{ + testOnlyNull(); + testOnlyNull(); + + // TODO add tests after non-expected results fixed + + testNotOnlyNull(0, "0"); + testNotOnlyNull(12.213, "12.213"); + testNotOnlyNull(-12.213, "-12.213"); + // tiflash: 3.4028235e38 + // tidb: 340282350000000000000000000000000000000 + // mysql: 3.40282e38 + // testNotOnlyNull(MAX_FLOAT32, "3.4028235e38"); + // tiflash: 1.1754944e-38 + // tidb: 0.000000000000000000000000000000000000011754944 + // mysql: 1.17549e-38 + // testNotOnlyNull(MIN_FLOAT32, "1.1754944e-38"); + + testNotOnlyNull(0, "0"); + testNotOnlyNull(12.213, "12.213"); + testNotOnlyNull(-12.213, "-12.213"); + // tiflash: 1.7976931348623157e308 + // tidb: 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + // mysql: 1.7976931348623157e308 + // testNotOnlyNull(MAX_FLOAT64, "1.7976931348623157e308"); + // tiflash: 2.2250738585072014e-308 + // tidb: 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014 + // mysql: 2.2250738585072014e-308 + // testNotOnlyNull(MIN_FLOAT64, "2.2250738585072014e-308"); +} +CATCH + +TEST_F(TestTidbConversion, castRealAsDecimal) +try +{ + testOnlyNull(); + testOnlyNull(); + testOnlyNull(); + testOnlyNull(); + testOnlyNull(); + testOnlyNull(); + testOnlyNull(); + testOnlyNull(); + + // TODO fix: + // for tidb, cast(12.213f as decimal(x, x)) throw warnings: Truncated incorrect DECIMAL value: '-12.21300029754638. + // tiflash is same as mysql, don't throw warnings. + + testNotOnlyNull(0, DecimalField32(0, 0), std::make_tuple(9, 0)); + testNotOnlyNull(12.213f, DecimalField32(12213, 3), std::make_tuple(9, 3)); + testNotOnlyNull(-12.213f, DecimalField32(-12213, 3), std::make_tuple(9, 3)); + testThrowException(MAX_FLOAT32, std::make_tuple(9, 0)); + testNotOnlyNull(MIN_FLOAT64, DecimalField32(0, 9), std::make_tuple(9, 9)); + + testNotOnlyNull(0, DecimalField64(0, 0), std::make_tuple(18, 0)); + testNotOnlyNull(12.213f, DecimalField64(12213, 3), std::make_tuple(18, 3)); + testNotOnlyNull(-12.213f, DecimalField64(-12213, 3), std::make_tuple(18, 3)); + testThrowException(MAX_FLOAT32, std::make_tuple(18, 0)); + testNotOnlyNull(MIN_FLOAT64, DecimalField64(0, 18), std::make_tuple(18, 18)); + + testNotOnlyNull(0, DecimalField128(0, 0), std::make_tuple(38, 0)); + testNotOnlyNull(12.213f, DecimalField128(12213, 3), std::make_tuple(38, 3)); + testNotOnlyNull(-12.213f, DecimalField128(-12213, 3), std::make_tuple(38, 3)); + testThrowException(MAX_FLOAT32, std::make_tuple(38, 0)); + testNotOnlyNull(MIN_FLOAT64, DecimalField128(0, 30), std::make_tuple(38, 30)); + + testNotOnlyNull(0, DecimalField256(static_cast(0), 0), std::make_tuple(65, 0)); + testNotOnlyNull(12.213f, DecimalField256(static_cast(12213), 3), std::make_tuple(65, 3)); + testNotOnlyNull(-12.213f, DecimalField256(static_cast(-12213), 3), std::make_tuple(65, 3)); + // TODO add test after bug fixed + // ERROR 1105 (HY000): other error for mpp stream: Cannot convert a non-finite number to an integer. + // testNotOnlyNull(MAX_FLOAT32, DecimalField256(Int256("340282346638528860000000000000000000000"), 0), std::make_tuple(65, 0)); + testNotOnlyNull(MIN_FLOAT64, DecimalField256(static_cast(0), 30), std::make_tuple(65, 30)); + + testNotOnlyNull(0, DecimalField32(0, 0), std::make_tuple(9, 0)); + testNotOnlyNull(12.213, DecimalField32(12213, 3), std::make_tuple(9, 3)); + testNotOnlyNull(-12.213, DecimalField32(-12213, 3), std::make_tuple(9, 3)); + testThrowException(MAX_FLOAT64, std::make_tuple(9, 0)); + testNotOnlyNull(MIN_FLOAT64, DecimalField32(0, 9), std::make_tuple(9, 9)); + + testNotOnlyNull(0, DecimalField64(0, 0), std::make_tuple(18, 0)); + testNotOnlyNull(12.213, DecimalField64(12213, 3), std::make_tuple(18, 3)); + testNotOnlyNull(-12.213, DecimalField64(-12213, 3), std::make_tuple(18, 3)); + testThrowException(MAX_FLOAT64, std::make_tuple(18, 0)); + testNotOnlyNull(MIN_FLOAT64, DecimalField64(0, 18), std::make_tuple(18, 18)); + + testNotOnlyNull(0, DecimalField128(0, 0), std::make_tuple(38, 0)); + testNotOnlyNull(12.213, DecimalField128(12213, 3), std::make_tuple(38, 3)); + testNotOnlyNull(-12.213, DecimalField128(-12213, 3), std::make_tuple(38, 3)); + testThrowException(MAX_FLOAT64, std::make_tuple(38, 0)); + testNotOnlyNull(MIN_FLOAT64, DecimalField128(0, 30), std::make_tuple(38, 30)); + + testNotOnlyNull(0, DecimalField256(static_cast(0), 0), std::make_tuple(65, 0)); + testNotOnlyNull(12.213, DecimalField256(static_cast(12213), 3), std::make_tuple(65, 3)); + testNotOnlyNull(-12.213, DecimalField256(static_cast(-12213), 3), std::make_tuple(65, 3)); + testThrowException(MAX_FLOAT64, std::make_tuple(65, 0)); + testNotOnlyNull(MIN_FLOAT64, DecimalField256(static_cast(0), 30), std::make_tuple(65, 30)); + + + // test round + // TODO fix: + // in default mode + // for round test, tidb throw warnings: Truncated incorrect DECIMAL value: xxx + // tiflash is same as mysql, don't throw warnings. + DAGContext * dag_context = context.getDAGContext(); + UInt64 ori_flags = dag_context->getFlags(); + dag_context->addFlag(TiDBSQLFlags::TRUNCATE_AS_WARNING); + dag_context->clearWarnings(); + + testNotOnlyNull(12.213f, DecimalField32(1221, 2), std::make_tuple(9, 2)); + testNotOnlyNull(-12.213f, DecimalField32(-1221, 2), std::make_tuple(9, 2)); + testNotOnlyNull(12.215f, DecimalField32(1222, 2), std::make_tuple(9, 2)); + testNotOnlyNull(-12.215f, DecimalField32(-1222, 2), std::make_tuple(9, 2)); + + testNotOnlyNull(12.213f, DecimalField64(1221, 2), std::make_tuple(18, 2)); + testNotOnlyNull(-12.213f, DecimalField64(-1221, 2), std::make_tuple(18, 2)); + testNotOnlyNull(12.215f, DecimalField64(1222, 2), std::make_tuple(18, 2)); + testNotOnlyNull(-12.215f, DecimalField64(-1222, 2), std::make_tuple(18, 2)); + + testNotOnlyNull(12.213f, DecimalField128(1221, 2), std::make_tuple(38, 2)); + testNotOnlyNull(-12.213f, DecimalField128(-1221, 2), std::make_tuple(38, 2)); + testNotOnlyNull(12.215f, DecimalField128(1222, 2), std::make_tuple(38, 2)); + testNotOnlyNull(-12.215f, DecimalField128(-1222, 2), std::make_tuple(38, 2)); + + testNotOnlyNull(12.213f, DecimalField256(static_cast(1221), 2), std::make_tuple(65, 2)); + testNotOnlyNull(-12.213f, DecimalField256(static_cast(-1221), 2), std::make_tuple(65, 2)); + testNotOnlyNull(12.215f, DecimalField256(static_cast(1222), 2), std::make_tuple(65, 2)); + testNotOnlyNull(-12.215f, DecimalField256(static_cast(-1222), 2), std::make_tuple(65, 2)); + + testNotOnlyNull(12.213, DecimalField32(1221, 2), std::make_tuple(9, 2)); + testNotOnlyNull(-12.213, DecimalField32(-1221, 2), std::make_tuple(9, 2)); + testNotOnlyNull(12.215, DecimalField32(1222, 2), std::make_tuple(9, 2)); + testNotOnlyNull(-12.215, DecimalField32(-1222, 2), std::make_tuple(9, 2)); + + testNotOnlyNull(12.213, DecimalField64(1221, 2), std::make_tuple(18, 2)); + testNotOnlyNull(-12.213, DecimalField64(-1221, 2), std::make_tuple(18, 2)); + testNotOnlyNull(12.215, DecimalField64(1222, 2), std::make_tuple(18, 2)); + testNotOnlyNull(-12.215, DecimalField64(-1222, 2), std::make_tuple(18, 2)); + + testNotOnlyNull(12.213, DecimalField128(1221, 2), std::make_tuple(38, 2)); + testNotOnlyNull(-12.213, DecimalField128(-1221, 2), std::make_tuple(38, 2)); + testNotOnlyNull(12.215, DecimalField128(1222, 2), std::make_tuple(38, 2)); + testNotOnlyNull(-12.215, DecimalField128(-1222, 2), std::make_tuple(38, 2)); + + testNotOnlyNull(12.213, DecimalField256(static_cast(1221), 2), std::make_tuple(65, 2)); + testNotOnlyNull(-12.213, DecimalField256(static_cast(-1221), 2), std::make_tuple(65, 2)); + testNotOnlyNull(12.215, DecimalField256(static_cast(1222), 2), std::make_tuple(65, 2)); + testNotOnlyNull(-12.215, DecimalField256(static_cast(-1222), 2), std::make_tuple(65, 2)); + + // Not compatible with MySQL/TiDB. + // MySQL/TiDB: 34028199169636080000000000000000000000.00 + // TiFlash: 34028199169636079590747176440761942016.00 + testNotOnlyNull(3.40282e+37f, DecimalField256(Decimal256(Int256("3402819916963607959074717644076194201600")), 2), std::make_tuple(50, 2)); + // MySQL/TiDB: 34028200000000000000000000000000000000.00 + // TiFlash: 34028200000000004441521809130870213181.44 + testNotOnlyNull(3.40282e+37, DecimalField256(Decimal256(Int256("3402820000000000444152180913087021318144")), 2), std::make_tuple(50, 2)); + + // MySQL/TiDB: 123.12345886230469000000 + // TiFlash: 123.12345886230470197248 + testNotOnlyNull(123.123456789123456789f, DecimalField256(Decimal256(Int256("12312345886230470197248")), 20), std::make_tuple(50, 20)); + // MySQL/TiDB: 123.12345886230469000000 + // TiFlash: 123.12345678912344293376 + testNotOnlyNull(123.123456789123456789, DecimalField256(Decimal256(Int256("12312345678912344293376")), 20), std::make_tuple(50, 20)); + + dag_context->setFlags(ori_flags); + dag_context->clearWarnings(); +} +CATCH + +TEST_F(TestTidbConversion, castRealAsTime) +try +{ + testOnlyNull(); + testOnlyNull(); + + // TODO add tests after non-expected results fixed + testReturnNull(12.213, 6); + testReturnNull(-12.213, 6); + testReturnNull(MAX_FLOAT32, 6); + testReturnNull(MIN_FLOAT32, 6); + + testNotOnlyNull(0, {0, 0, 0, 0, 0, 0, 0}, 6); + testNotOnlyNull(111, {2000, 1, 11, 0, 0, 0, 0}, 6); + testReturnNull(-111, 6); + testNotOnlyNull(111.1, {2000, 1, 11, 0, 0, 0, 0}, 6); + + testReturnNull(12.213, 6); + testReturnNull(-12.213, 6); + testReturnNull(MAX_FLOAT64, 6); + testReturnNull(MIN_FLOAT64, 6); + testReturnNull(1.1, 6); + testReturnNull(48.1, 6); + testReturnNull(100.1, 6); + testReturnNull(1301.11, 6); + testReturnNull(1131.111, 6); + testReturnNull(100001111.111, 6); + testReturnNull(20121212121260.1111111, 6); + testReturnNull(20121212126012.1111111, 6); + testReturnNull(20121212241212.1111111, 6); + testNotOnlyNull(111, {2000, 1, 11, 0, 0, 0, 0}, 6); + testReturnNull(-111, 6); + + testNotOnlyNull(0, {0, 0, 0, 0, 0, 0, 0}, 6); + testNotOnlyNull(20210201, {2021, 2, 1, 0, 0, 0, 0}, 6); + testNotOnlyNull(20210201.1, {2021, 2, 1, 0, 0, 0, 0}, 6); + testNotOnlyNull(20210000.1, {2021, 0, 0, 0, 0, 0, 0}, 6); + testNotOnlyNull(120012.1, {2012, 0, 12, 0, 0, 0, 0}, 6); + testNotOnlyNull(121200.1, {2012, 12, 00, 0, 0, 0, 0}, 6); + testNotOnlyNull(101.1, {2000, 1, 1, 0, 0, 0, 0}, 6); + testNotOnlyNull(111.1, {2000, 1, 11, 0, 0, 0, 0}, 6); + testNotOnlyNull(1122.1, {2000, 11, 22, 0, 0, 0, 0}, 6); + testNotOnlyNull(31212.111, {2003, 12, 12, 0, 0, 0, 0}, 6); + testNotOnlyNull(121212.1111, {2012, 12, 12, 0, 0, 0, 0}, 6); + testNotOnlyNull(1121212.111111, {112, 12, 12, 0, 0, 0, 0}, 6); + testNotOnlyNull(11121212.111111, {1112, 12, 12, 0, 0, 0, 0}, 6); + testNotOnlyNull(99991111.1111111, {9999, 11, 11, 0, 0, 0, 0}, 6); + testNotOnlyNull(1212121212.111111, {2000, 12, 12, 12, 12, 12, 111111}, 6); +} +CATCH + +TEST_F(TestTidbConversion, castDecimalAsReal) +try +{ + testReturnNull(DecimalField64(11, 1), std::make_tuple(19, 1), 6); + testReturnNull(DecimalField64(481, 1), std::make_tuple(19, 1), 6); + testReturnNull(DecimalField64(1001, 1), std::make_tuple(19, 1), 6); + testReturnNull(DecimalField64(130111, 2), std::make_tuple(19, 2), 6); + testReturnNull(DecimalField64(1131111, 3), std::make_tuple(19, 3), 6); + testReturnNull(DecimalField64(100001111111, 3), std::make_tuple(19, 3), 6); + testReturnNull(DecimalField64(12121212126011111, 5), std::make_tuple(19, 6), 6); + testReturnNull(DecimalField64(121212126012111111, 5), std::make_tuple(19, 4), 6); + testReturnNull(DecimalField64(12121224121211111, 5), std::make_tuple(19, 4), 6); + + testNotOnlyNull(DecimalField64(1011, 1), {2000, 1, 1, 0, 0, 0, 0}, 6); + testNotOnlyNull(DecimalField64(1111, 1), {2000, 1, 11, 0, 0, 0, 0}, 6); + testNotOnlyNull(DecimalField64(11221, 1), {2000, 11, 22, 0, 0, 0, 0}, 6); + testNotOnlyNull(DecimalField64(31212111, 3), {2003, 12, 12, 0, 0, 0, 0}, 6); + testNotOnlyNull(DecimalField64(30000111, 3), {2003, 0, 0, 0, 0, 0, 0}, 6); + testNotOnlyNull(DecimalField64(1212121111, 4), {2012, 12, 12, 0, 0, 0, 0}, 6); + testNotOnlyNull(DecimalField64(1121212111111, 6), {112, 12, 12, 0, 0, 0, 0}, 6); + testNotOnlyNull(DecimalField64(11121212111111, 6), {1112, 12, 12, 0, 0, 0, 0}, 6); + testNotOnlyNull(DecimalField64(99991111111111, 6), {9999, 11, 11, 0, 0, 0, 0}, 6); +>>>>>>> 720bfc1787 (fix that the result of expression cast(Real/Decimal)AsTime is inconsistent with TiDB (#5799)) } CATCH @@ -993,5 +1515,180 @@ try } CATCH +<<<<<<< HEAD +======= +TEST_F(TestTidbConversion, castTimeAsDuration) +try +{ + const auto to_type_1 = std::make_shared(5); // from_fsp < to_fsp + const auto to_type_2 = std::make_shared(4); // from_fsp == to_fsp + const auto to_type_3 = std::make_shared(2); // from_fsp > to_fsp + // cast datetime to duration + const auto datetime_type_ptr = std::make_shared(4); + MyDateTime date(2021, 10, 26, 0, 0, 0, 0); + MyDateTime datetime(2021, 10, 26, 11, 11, 11, 0); + MyDateTime datetime_frac1(2021, 10, 26, 11, 11, 11, 111100); + MyDateTime datetime_frac2(2021, 10, 26, 11, 11, 11, 123500); + MyDateTime datetime_frac3(2021, 10, 26, 11, 11, 11, 999900); + + auto col_datetime = ColumnUInt64::create(); + col_datetime->insert(Field(date.toPackedUInt())); + col_datetime->insert(Field(datetime.toPackedUInt())); + col_datetime->insert(Field(datetime_frac1.toPackedUInt())); + col_datetime->insert(Field(datetime_frac2.toPackedUInt())); + col_datetime->insert(Field(datetime_frac3.toPackedUInt())); + + auto ctn_datetime = ColumnWithTypeAndName(std::move(col_datetime), datetime_type_ptr, "datetime"); + ColumnWithTypeAndName datetime_output1( + createColumn({(0 * 3600 + 0 * 60 + 0) * 1000000000L + 000000000L, + (11 * 3600 + 11 * 60 + 11) * 1000000000L + 000000000L, + (11 * 3600 + 11 * 60 + 11) * 1000000000L + 111100000L, + (11 * 3600 + 11 * 60 + 11) * 1000000000L + 123500000L, + (11 * 3600 + 11 * 60 + 11) * 1000000000L + 999900000L}) + .column, + to_type_1, + "datetime_output1"); + ColumnWithTypeAndName datetime_output2( + createColumn({(0 * 3600 + 0 * 60 + 0) * 1000000000L + 000000000L, + (11 * 3600 + 11 * 60 + 11) * 1000000000L + 000000000L, + (11 * 3600 + 11 * 60 + 11) * 1000000000L + 111100000L, + (11 * 3600 + 11 * 60 + 11) * 1000000000L + 123500000L, + (11 * 3600 + 11 * 60 + 11) * 1000000000L + 999900000L}) + .column, + to_type_2, + "datetime_output2"); + + ColumnWithTypeAndName datetime_output3( + createColumn({(0 * 3600 + 0 * 60 + 0) * 1000000000L + 000000000L, + (11 * 3600 + 11 * 60 + 11) * 1000000000L + 000000000L, + (11 * 3600 + 11 * 60 + 11) * 1000000000L + 110000000L, + (11 * 3600 + 11 * 60 + 11) * 1000000000L + 120000000L, + (11 * 3600 + 11 * 60 + 12) * 1000000000L + 000000000L}) + .column, + to_type_3, + "datetime_output3"); + + + ASSERT_COLUMN_EQ(datetime_output1, executeFunction(func_name, {ctn_datetime, createCastTypeConstColumn(to_type_1->getName())})); + ASSERT_COLUMN_EQ(datetime_output2, executeFunction(func_name, {ctn_datetime, createCastTypeConstColumn(to_type_2->getName())})); + ASSERT_COLUMN_EQ(datetime_output3, executeFunction(func_name, {ctn_datetime, createCastTypeConstColumn(to_type_3->getName())})); + + + // Test Const + ColumnWithTypeAndName input_const(createConstColumn(1, datetime_frac2.toPackedUInt()).column, datetime_type_ptr, "input_const"); + ColumnWithTypeAndName output1_const(createConstColumn(1, (11 * 3600 + 11 * 60 + 11) * 1000000000L + 123500000L).column, to_type_1, "output1_const"); + ColumnWithTypeAndName output2_const(createConstColumn(1, (11 * 3600 + 11 * 60 + 11) * 1000000000L + 123500000L).column, to_type_2, "output2_const"); + ColumnWithTypeAndName output3_const(createConstColumn(1, (11 * 3600 + 11 * 60 + 11) * 1000000000L + 120000000L).column, to_type_3, "output3_const"); + + ASSERT_COLUMN_EQ(output1_const, executeFunction(func_name, {input_const, createCastTypeConstColumn(to_type_1->getName())})); + ASSERT_COLUMN_EQ(output2_const, executeFunction(func_name, {input_const, createCastTypeConstColumn(to_type_2->getName())})); + ASSERT_COLUMN_EQ(output3_const, executeFunction(func_name, {input_const, createCastTypeConstColumn(to_type_3->getName())})); + + // Test Nullable + ColumnWithTypeAndName input_nullable( + createColumn>({datetime_frac1.toPackedUInt(), + {}, + datetime_frac2.toPackedUInt(), + {}, + datetime_frac3.toPackedUInt()}) + .column, + makeNullable(datetime_type_ptr), + "input_nullable"); + ColumnWithTypeAndName output1_nullable( + createColumn>({(11 * 3600 + 11 * 60 + 11) * 1000000000L + 111100000L, + {}, + (11 * 3600 + 11 * 60 + 11) * 1000000000L + 123500000L, + {}, + (11 * 3600 + 11 * 60 + 11) * 1000000000L + 999900000L}) + .column, + makeNullable(to_type_1), + "output1_output"); + ColumnWithTypeAndName output2_nullable( + createColumn>({(11 * 3600 + 11 * 60 + 11) * 1000000000L + 111100000L, + {}, + (11 * 3600 + 11 * 60 + 11) * 1000000000L + 123500000L, + {}, + (11 * 3600 + 11 * 60 + 11) * 1000000000L + 999900000L}) + .column, + makeNullable(to_type_2), + "output2_output"); + ColumnWithTypeAndName output3_nullable( + createColumn>({(11 * 3600 + 11 * 60 + 11) * 1000000000L + 110000000L, + {}, + (11 * 3600 + 11 * 60 + 11) * 1000000000L + 120000000L, + {}, + (11 * 3600 + 11 * 60 + 12) * 1000000000L + 000000000L}) + .column, + makeNullable(to_type_3), + "output3_output"); + + ASSERT_COLUMN_EQ(output1_nullable, executeFunction(func_name, {input_nullable, createCastTypeConstColumn(makeNullable(to_type_1)->getName())})); + ASSERT_COLUMN_EQ(output2_nullable, executeFunction(func_name, {input_nullable, createCastTypeConstColumn(makeNullable(to_type_2)->getName())})); + ASSERT_COLUMN_EQ(output3_nullable, executeFunction(func_name, {input_nullable, createCastTypeConstColumn(makeNullable(to_type_3)->getName())})); +} +CATCH + +// for https://github.com/pingcap/tics/issues/3595 +TEST_F(TestTidbConversion, castStringAsDateTime3595) +try +{ + DAGContext * dag_context = context.getDAGContext(); + dag_context->addFlag(TiDBSQLFlags::TRUNCATE_AS_WARNING); + auto to_datetime_column = createConstColumn(1, "Nullable(MyDateTime(6))"); + ColumnWithTypeAndName expect_datetime_column( + createColumn>({{}}).column, + makeNullable(std::make_shared(6)), + "result"); + auto to_date_column = createConstColumn(1, "Nullable(MyDate)"); + ColumnWithTypeAndName expect_date_column( + createColumn>({{}}).column, + makeNullable(std::make_shared()), + "result"); + + auto from_column = createColumn({"08:45:16"}); + auto vector_result = executeFunction("tidb_cast", {from_column, to_datetime_column}); + for (size_t i = 0; i < from_column.column->size(); i++) + { + ASSERT_COLUMN_EQ(expect_datetime_column, vector_result); + } + vector_result = executeFunction("tidb_cast", {from_column, to_date_column}); + for (size_t i = 0; i < from_column.column->size(); i++) + { + ASSERT_COLUMN_EQ(expect_date_column, vector_result); + } + + auto from_decimal_column = createColumn(std::make_tuple(9, 3), {"102310.023"}); + vector_result = executeFunction("tidb_cast", {from_decimal_column, to_datetime_column}); + for (size_t i = 0; i < from_decimal_column.column->size(); i++) + { + ASSERT_COLUMN_EQ(expect_datetime_column, vector_result); + } + vector_result = executeFunction("tidb_cast", {from_decimal_column, to_date_column}); + for (size_t i = 0; i < from_decimal_column.column->size(); i++) + { + ASSERT_COLUMN_EQ(expect_date_column, vector_result); + } + + auto from_float_column = createColumn({102310.023}); + vector_result = executeFunction("tidb_cast", {from_float_column, to_datetime_column}); + for (size_t i = 0; i < from_float_column.column->size(); i++) + { + ASSERT_COLUMN_EQ(expect_datetime_column, vector_result); + } + vector_result = executeFunction("tidb_cast", {from_float_column, to_date_column}); + for (size_t i = 0; i < from_float_column.column->size(); i++) + { + ASSERT_COLUMN_EQ(expect_date_column, vector_result); + } + + ASSERT_COLUMN_EQ( + createDateTimeColumn({{{2012, 0, 0, 0, 0, 0, 0}}}, 6), + executeFunction(func_name, + {createColumn>({"20120000"}), + createCastTypeConstColumn("Nullable(MyDateTime(6))")})); +} +CATCH + +>>>>>>> 720bfc1787 (fix that the result of expression cast(Real/Decimal)AsTime is inconsistent with TiDB (#5799)) } // namespace } // namespace DB::tests diff --git a/tests/fullstack-test/expr/cast_as_time.test b/tests/fullstack-test/expr/cast_as_time.test index 4905b0d9136..94c1d7e9c66 100644 --- a/tests/fullstack-test/expr/cast_as_time.test +++ b/tests/fullstack-test/expr/cast_as_time.test @@ -31,4 +31,82 @@ mysql> set @@tidb_isolation_read_engines='tiflash';select * from test.t where ca | a | +----------------------------+ | 2020-12-08 11:11:11.999999 | -+----------------------------+ \ No newline at end of file +<<<<<<< HEAD ++----------------------------+ +======= ++----------------------------+ + +mysql> drop table if exists test.t +mysql> create table test.t(a varchar(20), b decimal(20,6), c float, d int) +mysql> insert into test.t values('08:45:16', 201302.2345, 200132.2345, 201302) +mysql> alter table test.t set tiflash replica 1 location labels 'rack', 'host', 'abc' + +func> wait_table test t + +mysql> set @@tidb_isolation_read_engines='tiflash';select cast(a as datetime) ad, cast(b as datetime) bd, cast(c as datetime) cd, cast(d as datetime) dd from test.t ++------+------+------+------+ +| ad | bd | cd | dd | ++------+------+------+------+ +| NULL | NULL | NULL | NULL | ++------+------+------+------+ + +mysql> set @@tidb_isolation_read_engines='tiflash';select cast(a as date) ad, cast(b as date) bd, cast(c as date) cd, cast(d as date) dd from test.t ++------+------+------+------+ +| ad | bd | cd | dd | ++------+------+------+------+ +| NULL | NULL | NULL | NULL | ++------+------+------+------+ + +mysql> drop table if exists test.t + +mysql> create table test.t(d1 double, f float, d2 decimal(24,8)) +mysql> alter table test.t set tiflash replica 1 location labels 'rack', 'host', 'abc' + +func> wait_table test t + +mysql> insert into test.t values(0, 0, 0) +mysql> set @@tidb_isolation_read_engines='tiflash';select cast(111.1 as datetime) from test.t ++-------------------------+ +| cast(111.1 as datetime) | ++-------------------------+ +| 2000-01-11 00:00:00 | ++-------------------------+ +mysql> set @@tidb_isolation_read_engines='tiflash';select cast(1311.1 as datetime) from test.t ++--------------------------+ +| cast(1311.1 as datetime) | ++--------------------------+ +| NULL | ++--------------------------+ +mysql> set @@tidb_isolation_read_engines='tiflash';select cast(d1 as datetime), cast(d2 as datetime), cast(f as datetime) from test.t ++----------------------+----------------------+---------------------+ +| cast(d1 as datetime) | cast(d2 as datetime) | cast(f as datetime) | ++----------------------+----------------------+---------------------+ +| 0000-00-00 00:00:00 | 0000-00-00 00:00:00 | 0000-00-00 00:00:00 | ++----------------------+----------------------+---------------------+ + +mysql> insert into test.t values(111.1, 1122.1, 31212.111) +mysql> insert into test.t values(121212.1111, 1121212.111111, 11121212.111111) +mysql> insert into test.t values(120012.1111, 1121200.111111, 11120000.111111) +mysql> insert into test.t values(99991111.1111111, 101.1111111, 20121212121212.1111111) +mysql> insert into test.t values(NULL, NULL, NULL) +mysql> insert into test.t values(1.1, 48.1, 100.1) +mysql> insert into test.t values(1301.11, 1131.111, 100001111.111) +mysql> insert into test.t values(20121212121260.1111111, 20121212126012.1111111, 20121212241212.1111111) +mysql> set @@tidb_isolation_read_engines='tiflash';set tidb_enforce_mpp=1;select cast(d1 as datetime), cast(f as datetime), cast(d2 as datetime) from test.t ++----------------------+---------------------+----------------------+ +| cast(d1 as datetime) | cast(f as datetime) | cast(d2 as datetime) | ++----------------------+---------------------+----------------------+ +| 0000-00-00 00:00:00 | 0000-00-00 00:00:00 | 0000-00-00 00:00:00 | +| 2000-01-11 00:00:00 | 2000-11-22 00:00:00 | 2003-12-12 00:00:00 | +| 2012-12-12 00:00:00 | 0112-12-12 00:00:00 | 1112-12-12 00:00:00 | +| 2012-00-12 00:00:00 | 0112-12-00 00:00:00 | 1112-00-00 00:00:00 | +| 9999-11-11 00:00:00 | 2000-01-01 00:00:00 | 2012-12-12 12:12:12 | +| NULL | NULL | NULL | +| NULL | NULL | NULL | +| NULL | NULL | NULL | +| NULL | NULL | NULL | ++----------------------+---------------------+----------------------+ + +mysql> drop table if exists test.t + +>>>>>>> 720bfc1787 (fix that the result of expression cast(Real/Decimal)AsTime is inconsistent with TiDB (#5799))