From 9ce931acf34e9ac1d5fd9e72a52ef8ebcd7b120d Mon Sep 17 00:00:00 2001 From: Meng Xin Date: Wed, 16 Feb 2022 19:08:09 +0800 Subject: [PATCH 1/3] This is an automated cherry-pick of #4046 Signed-off-by: ti-chi-bot --- dbms/src/Common/MyTime.cpp | 858 +++++++++- .../Functions/tests/gtest_tidb_conversion.cpp | 1461 +++++++++++++++++ 2 files changed, 2315 insertions(+), 4 deletions(-) create mode 100644 dbms/src/Functions/tests/gtest_tidb_conversion.cpp diff --git a/dbms/src/Common/MyTime.cpp b/dbms/src/Common/MyTime.cpp index b249d2db136..6fbe8537853 100644 --- a/dbms/src/Common/MyTime.cpp +++ b/dbms/src/Common/MyTime.cpp @@ -53,7 +53,8 @@ bool isValidSeperator(char c, int previous_parts) if (isPunctuation(c)) return true; - return previous_parts == 2 && (c == ' ' || c == 'T'); + // for https://github.com/pingcap/tics/issues/4036 + return previous_parts == 2 && (c == 'T' || isWhitespaceASCII(c)); } std::vector parseDateFormat(String format) @@ -496,8 +497,8 @@ Field parseMyDateTime(const String & str, int8_t fsp) bool truncated_or_incorrect = false; - // noAbsorb tests if can absorb FSP or TZ - auto noAbsorb = [](const std::vector & seps) { + // no_absorb tests if can absorb FSP or TZ + auto no_absorb = [](const std::vector & seps) { // if we have more than 5 parts (i.e. 6), the tailing part can't be absorbed // or if we only have 1 part, but its length is longer than 4, then it is at least YYMMD, in this case, FSP can // not be absorbed, and it will be handled later, and the leading sign prevents TZ from being absorbed, because @@ -507,7 +508,7 @@ Field parseMyDateTime(const String & str, int8_t fsp) if (!frac_str.empty()) { - if (!noAbsorb(seps)) + if (!no_absorb(seps)) { seps.push_back(frac_str); frac_str = ""; @@ -518,7 +519,11 @@ Field parseMyDateTime(const String & str, int8_t fsp) { // if tz_sign is empty, it's sure that the string literal contains timezone (e.g., 2010-10-10T10:10:10Z), // therefore we could safely skip this branch. +<<<<<<< HEAD if (!noAbsorb(seps) && !(tz_minute != "" && tz_sep == "")) +======= + if (!no_absorb(seps) && !(!tz_minute.empty() && tz_sep.empty())) +>>>>>>> 745bcce2a5 (fix date format identifies '\n' as invalid separator (#4046)) { // we can't absorb timezone if there is no separate between tz_hour and tz_minute if (!tz_hour.empty()) @@ -535,6 +540,7 @@ Field parseMyDateTime(const String & str, int8_t fsp) switch (seps.size()) { +<<<<<<< HEAD // No delimiter case 1: { @@ -643,38 +649,173 @@ Field parseMyDateTime(const String & str, int8_t fsp) { throw TiFlashException("Datetime truncated: " + str, Errors::Types::Truncated); } +======= + // No delimiter + case 1: + { + size_t l = seps[0].size(); + switch (l) + { + case 14: // YYYYMMDDHHMMSS + { + std::sscanf(seps[0].c_str(), "%4d%2d%2d%2d%2d%2d", &year, &month, &day, &hour, &minute, &second); //NOLINT + hhmmss = true; + break; + } + case 12: // YYMMDDHHMMSS + { + std::sscanf(seps[0].c_str(), "%2d%2d%2d%2d%2d%2d", &year, &month, &day, &hour, &minute, &second); //NOLINT + year = adjustYear(year); + hhmmss = true; + break; + } + case 11: // YYMMDDHHMMS + { + std::sscanf(seps[0].c_str(), "%2d%2d%2d%2d%2d%1d", &year, &month, &day, &hour, &minute, &second); //NOLINT + year = adjustYear(year); + hhmmss = true; + break; + } + case 10: // YYMMDDHHMM + { + std::sscanf(seps[0].c_str(), "%2d%2d%2d%2d%2d", &year, &month, &day, &hour, &minute); //NOLINT + year = adjustYear(year); +>>>>>>> 745bcce2a5 (fix date format identifies '\n' as invalid separator (#4046)) break; } case 3: { +<<<<<<< HEAD // YYYY-MM-DD scanTimeArgs(seps, {&year, &month, &day}); +======= + std::sscanf(seps[0].c_str(), "%2d%2d%2d%2d%1d", &year, &month, &day, &hour, &minute); //NOLINT + year = adjustYear(year); +>>>>>>> 745bcce2a5 (fix date format identifies '\n' as invalid separator (#4046)) break; } case 4: { +<<<<<<< HEAD // YYYY-MM-DD HH scanTimeArgs(seps, {&year, &month, &day, &hour}); +======= + std::sscanf(seps[0].c_str(), "%4d%2d%2d", &year, &month, &day); //NOLINT +>>>>>>> 745bcce2a5 (fix date format identifies '\n' as invalid separator (#4046)) break; } case 5: { +<<<<<<< HEAD // YYYY-MM-DD HH-MM scanTimeArgs(seps, {&year, &month, &day, &hour, &minute}); +======= + std::sscanf(seps[0].c_str(), "%2d%2d%2d%1d", &year, &month, &day, &hour); //NOLINT + year = adjustYear(year); +>>>>>>> 745bcce2a5 (fix date format identifies '\n' as invalid separator (#4046)) break; } case 6: { +<<<<<<< HEAD // We don't have fractional seconds part. // YYYY-MM-DD HH-MM-SS scanTimeArgs(seps, {&year, &month, &day, &hour, &minute, &second}); hhmmss = true; +======= + std::sscanf(seps[0].c_str(), "%2d%2d%2d", &year, &month, &day); //NOLINT + year = adjustYear(year); +>>>>>>> 745bcce2a5 (fix date format identifies '\n' as invalid separator (#4046)) break; } default: { throw Exception("Wrong datetime format"); } +<<<<<<< HEAD +======= + if (l == 5 || l == 6 || l == 8) + { + // YYMMDD or YYYYMMDD + // We must handle float => string => datetime, the difference is that fractional + // part of float type is discarded directly, while fractional part of string type + // is parsed to HH:MM:SS. + int ret = 0; + switch (frac_str.size()) + { + case 0: + ret = 1; + is_date = true; + break; + case 1: + case 2: + { + ret = std::sscanf(frac_str.c_str(), "%2d ", &hour); //NOLINT + break; + } + case 3: + case 4: + { + ret = std::sscanf(frac_str.c_str(), "%2d%2d ", &hour, &minute); //NOLINT + break; + } + default: + { + ret = std::sscanf(frac_str.c_str(), "%2d%2d%2d ", &hour, &minute, &second); //NOLINT + break; + } + } + truncated_or_incorrect = (ret == 0); + } + if (l == 9 || l == 10) + { + if (frac_str.empty()) + { + second = 0; + } + else + { + truncated_or_incorrect = (std::sscanf(frac_str.c_str(), "%2d ", &second) == 0); //NOLINT + } + } + if (truncated_or_incorrect) + { + throw TiFlashException("Datetime truncated: " + str, Errors::Types::Truncated); + } + break; + } + case 3: + { + // YYYY-MM-DD + scanTimeArgs(seps, {&year, &month, &day}); + is_date = true; + break; + } + case 4: + { + // YYYY-MM-DD HH + scanTimeArgs(seps, {&year, &month, &day, &hour}); + break; + } + case 5: + { + // YYYY-MM-DD HH-MM + scanTimeArgs(seps, {&year, &month, &day, &hour, &minute}); + break; + } + case 6: + { + // We don't have fractional seconds part. + // YYYY-MM-DD HH-MM-SS + scanTimeArgs(seps, {&year, &month, &day, &hour, &minute, &second}); + hhmmss = true; + break; + } + default: + { + throw Exception("Wrong datetime format"); + } +>>>>>>> 745bcce2a5 (fix date format identifies '\n' as invalid separator (#4046)) } // If str is sepereated by delimiters, the first one is year, and if the year is 2 digit, @@ -951,8 +1092,15 @@ void MyTimeBase::check(bool allow_zero_in_date, bool allow_invalid_date) const if (!allow_invalid_date) { constexpr static UInt8 max_days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +<<<<<<< HEAD static auto is_leap_year = [](UInt16 _year) { return ((_year % 4 == 0) && (_year % 100 != 0)) || (_year % 400 == 0); }; max_day = max_days_in_month[month - 1]; +======= + static auto is_leap_year = [](UInt16 _year) { + return ((_year % 4 == 0) && (_year % 100 != 0)) || (_year % 400 == 0); + }; + max_day = max_days_in_month[month - 1]; // NOLINT +>>>>>>> 745bcce2a5 (fix date format identifies '\n' as invalid separator (#4046)) if (month == 2 && is_leap_year(year)) { max_day = 29; @@ -1217,4 +1365,706 @@ MyDateTimeFormatter::MyDateTimeFormatter(const String & layout) } } +<<<<<<< HEAD +======= +struct MyDateTimeParser::Context +{ + // Some state for `mysqlTimeFix` + uint32_t state = 0; + static constexpr uint32_t ST_DAY_OF_YEAR = 0x01; + static constexpr uint32_t ST_MERIDIEM = 0x02; + static constexpr uint32_t ST_HOUR_0_23 = 0x04; + static constexpr uint32_t ST_HOUR_1_12 = 0x08; + + int32_t day_of_year = 0; + // 0 - invalid, 1 - am, 2 - pm + int32_t meridiem = 0; + + // The input string view + const StringRef view; + // The pos we are parsing from + size_t pos = 0; + + explicit Context(StringRef view_) + : view(std::move(view_)) + {} +}; + +// Try to parse digits with number of `limit` starting from view[pos] +// Return if success. +// Return <0, _> if fail. +static std::tuple parseNDigits(const StringRef & view, const size_t pos, const size_t limit) +{ + size_t step = 0; + int32_t num = 0; + while (step < limit && (pos + step) < view.size && isNumericASCII(view.data[pos + step])) + { + num = num * 10 + (view.data[pos + step] - '0'); + step += 1; + } + return std::make_tuple(step, num); +} + +static std::tuple parseYearNDigits(const StringRef & view, const size_t pos, const size_t limit) +{ + // Try to parse a "year" within `limit` digits + size_t step = 0; + int32_t year = 0; + std::tie(step, year) = parseNDigits(view, pos, limit); + if (step == 0) + return std::make_tuple(step, 0); + else if (step <= 2) + year = adjustYear(year); + return std::make_tuple(step, year); +} + +enum class ParseState +{ + NORMAL = 0, // Parsing + FAIL = 1, // Fail to parse + END_OF_FILE = 2, // The end of input +}; + +//"%r": Time, 12-hour (hh:mm:ss followed by AM or PM) +static bool parseTime12Hour(MyDateTimeParser::Context & ctx, MyTimeBase & time) +{ + // Use temp_pos instead of changing `ctx.pos` directly in case of parsing failure + size_t temp_pos = ctx.pos; + auto check_if_end = [&temp_pos, &ctx]() -> ParseState { + // To the end + if (temp_pos == ctx.view.size) + return ParseState::END_OF_FILE; + return ParseState::NORMAL; + }; + auto skip_whitespaces = [&temp_pos, &ctx, &check_if_end]() -> ParseState { + while (temp_pos < ctx.view.size && isWhitespaceASCII(ctx.view.data[temp_pos])) + ++temp_pos; + return check_if_end(); + }; + auto parse_sep = [&temp_pos, &ctx, &skip_whitespaces]() -> ParseState { + if (skip_whitespaces() == ParseState::END_OF_FILE) + return ParseState::END_OF_FILE; + // parse ":" + if (ctx.view.data[temp_pos] != ':') + return ParseState::FAIL; + temp_pos += 1; // move forward + return ParseState::NORMAL; + }; + auto try_parse = [&]() -> ParseState { + ParseState state = ParseState::NORMAL; + /// Note that we should update `time` as soon as possible, or we + /// can not get correct result for incomplete input like "12:13" + /// that is less than "hh:mm:ssAM" + + // hh + size_t step = 0; + int32_t hour = 0; + if (state = skip_whitespaces(); state != ParseState::NORMAL) + return state; + std::tie(step, hour) = parseNDigits(ctx.view, temp_pos, 2); + if (step == 0 || hour > 12 || hour == 0) + return ParseState::FAIL; + // Handle special case: 12:34:56 AM -> 00:34:56 + // For PM, we will add 12 it later + if (hour == 12) + hour = 0; + time.hour = hour; + temp_pos += step; // move forward + + if (state = parse_sep(); state != ParseState::NORMAL) + return state; + + int32_t minute = 0; + if (state = skip_whitespaces(); state != ParseState::NORMAL) + return state; + std::tie(step, minute) = parseNDigits(ctx.view, temp_pos, 2); + if (step == 0 || minute > 59) + return ParseState::FAIL; + time.minute = minute; + temp_pos += step; // move forward + + if (state = parse_sep(); state != ParseState::NORMAL) + return state; + + int32_t second = 0; + if (state = skip_whitespaces(); state != ParseState::NORMAL) + return state; + std::tie(step, second) = parseNDigits(ctx.view, temp_pos, 2); + if (step == 0 || second > 59) + return ParseState::FAIL; + time.second = second; + temp_pos += step; // move forward + + int meridiem = 0; // 0 - invalid, 1 - am, 2 - pm + if (state = skip_whitespaces(); state != ParseState::NORMAL) + return state; + // "AM"/"PM" must be parsed as a single element + // "11:13:56a" is an invalid input for "%r". + if (auto size_to_end = ctx.view.size - temp_pos; size_to_end < 2) + return ParseState::FAIL; + if (toLowerIfAlphaASCII(ctx.view.data[temp_pos]) == 'a') + meridiem = 1; + else if (toLowerIfAlphaASCII(ctx.view.data[temp_pos]) == 'p') + meridiem = 2; + + if (toLowerIfAlphaASCII(ctx.view.data[temp_pos + 1]) != 'm') + meridiem = 0; + switch (meridiem) + { + case 0: + return ParseState::FAIL; + case 1: + break; + case 2: + time.hour += 12; + break; + } + temp_pos += 2; // move forward + return ParseState::NORMAL; + }; + if (auto state = try_parse(); state == ParseState::FAIL) + return false; + // Other state, forward the `ctx.pos` and return true + ctx.pos = temp_pos; + return true; +} + +//"%T": Time, 24-hour (hh:mm:ss) +static bool parseTime24Hour(MyDateTimeParser::Context & ctx, MyTimeBase & time) +{ + // Use temp_pos instead of changing `ctx.pos` directly in case of parsing failure + size_t temp_pos = ctx.pos; + auto check_if_end = [&temp_pos, &ctx]() -> ParseState { + // To the end + if (temp_pos == ctx.view.size) + return ParseState::END_OF_FILE; + return ParseState::NORMAL; + }; + auto skip_whitespaces = [&temp_pos, &ctx, &check_if_end]() -> ParseState { + while (temp_pos < ctx.view.size && isWhitespaceASCII(ctx.view.data[temp_pos])) + ++temp_pos; + return check_if_end(); + }; + auto parse_sep = [&temp_pos, &ctx, &skip_whitespaces]() -> ParseState { + if (skip_whitespaces() == ParseState::END_OF_FILE) + return ParseState::END_OF_FILE; + // parse ":" + if (ctx.view.data[temp_pos] != ':') + return ParseState::FAIL; + temp_pos += 1; // move forward + return ParseState::NORMAL; + }; + auto try_parse = [&]() -> ParseState { + ParseState state = ParseState::NORMAL; + /// Note that we should update `time` as soon as possible, or we + /// can not get correct result for incomplete input like "12:13" + /// that is less than "hh:mm:ss" + + // hh + size_t step = 0; + int32_t hour = 0; + if (state = skip_whitespaces(); state != ParseState::NORMAL) + return state; + std::tie(step, hour) = parseNDigits(ctx.view, temp_pos, 2); + if (step == 0 || hour > 23) + return ParseState::FAIL; + time.hour = hour; + temp_pos += step; // move forward + + if (state = parse_sep(); state != ParseState::NORMAL) + return state; + + int32_t minute = 0; + if (state = skip_whitespaces(); state != ParseState::NORMAL) + return state; + std::tie(step, minute) = parseNDigits(ctx.view, temp_pos, 2); + if (step == 0 || minute > 59) + return ParseState::FAIL; + time.minute = minute; + temp_pos += step; // move forward + + if (state = parse_sep(); state != ParseState::NORMAL) + return state; + + int32_t second = 0; + if (state = skip_whitespaces(); state != ParseState::NORMAL) + return state; + std::tie(step, second) = parseNDigits(ctx.view, temp_pos, 2); + if (step == 0 || second > 59) + return ParseState::FAIL; + time.second = second; + temp_pos += step; // move forward + + return ParseState::NORMAL; + }; + if (auto state = try_parse(); state == ParseState::FAIL) + return false; + // Other state, forward the `ctx.pos` and return true + ctx.pos = temp_pos; + return true; +} + +// Refer: https://github.com/pingcap/tidb/blob/v5.0.1/types/time.go#L2946 +MyDateTimeParser::MyDateTimeParser(String format_) + : format(std::move(format_)) +{ + // Ignore all prefix white spaces (TODO: handle unicode space?) + size_t format_pos = 0; + while (format_pos < format.size() && isWhitespaceASCII(format[format_pos])) + format_pos++; + + bool in_pattern_match = false; + while (format_pos < format.size()) + { + char x = format[format_pos]; + if (in_pattern_match) + { + switch (x) + { + case 'b': + { + //"%b": Abbreviated month name (Jan..Dec) + parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { + size_t step = 0; + auto v = removePrefix(ctx.view, ctx.pos); + for (size_t p = 0; p < 12; p++) + { + if (startsWithCI(v, abbrev_month_names[p])) + { + time.month = p + 1; + step = abbrev_month_names[p].size(); + break; + } + } + if (step == 0) + return false; + ctx.pos += step; + return true; + }); + break; + } + case 'm': + //"%m": Month, numeric (00..12) + [[fallthrough]]; + case 'c': + { + //"%c": Month, numeric (0..12) + parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { + // To be compatible with TiDB & MySQL, first try to take two digit and parse it as `num` + auto [step, month] = parseNDigits(ctx.view, ctx.pos, 2); + // Then check whether num is valid month + // Note that 0 is valid when sql_mode does not contain NO_ZERO_IN_DATE,NO_ZERO_DATE + if (step == 0 || month > 12) + return false; + time.month = month; + ctx.pos += step; + return true; + }); + break; + } + case 'd': //"%d": Day of the month, numeric (00..31) + [[fallthrough]]; + case 'e': //"%e": Day of the month, numeric (0..31) + { + parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { + auto [step, day] = parseNDigits(ctx.view, ctx.pos, 2); + if (step == 0 || day > 31) + return false; + time.day = day; + ctx.pos += step; + return true; + }); + break; + } + case 'f': + { + //"%f": Microseconds (000000..999999) + parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { + auto [step, ms] = parseNDigits(ctx.view, ctx.pos, 6); + // Empty string is a valid input + if (step == 0) + { + time.micro_second = 0; + return true; + } + // The suffix '0' can be ignored. + // "9" means 900000 + for (size_t i = step; i < 6; i++) + { + ms *= 10; + } + time.micro_second = ms; + ctx.pos += step; + return true; + }); + break; + } + case 'k': + //"%k": Hour (0..23) + [[fallthrough]]; + case 'H': + { + //"%H": Hour (00..23) + parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { + auto [step, hour] = parseNDigits(ctx.view, ctx.pos, 2); + if (step == 0 || hour > 23) + return false; + ctx.state |= MyDateTimeParser::Context::ST_HOUR_0_23; + time.hour = hour; + ctx.pos += step; + return true; + }); + break; + } + case 'l': + //"%l": Hour (1..12) + [[fallthrough]]; + case 'I': + //"%I": Hour (01..12) + [[fallthrough]]; + case 'h': + { + //"%h": Hour (01..12) + parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { + auto [step, hour] = parseNDigits(ctx.view, ctx.pos, 2); + if (step == 0 || hour <= 0 || hour > 12) + return false; + ctx.state |= MyDateTimeParser::Context::ST_HOUR_1_12; + time.hour = hour; + ctx.pos += step; + return true; + }); + break; + } + case 'i': + { + //"%i": Minutes, numeric (00..59) + parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { + auto [step, num] = parseNDigits(ctx.view, ctx.pos, 2); + if (step == 0 || num > 59) + return false; + time.minute = num; + ctx.pos += step; + return true; + }); + break; + } + case 'j': + { + //"%j": Day of year (001..366) + parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase &) -> bool { + auto [step, num] = parseNDigits(ctx.view, ctx.pos, 3); + if (step == 0 || num == 0 || num > 366) + return false; + ctx.state |= MyDateTimeParser::Context::ST_DAY_OF_YEAR; + ctx.day_of_year = num; + ctx.pos += step; + return true; + }); + break; + } + case 'M': + { + //"%M": Month name (January..December) + parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { + auto v = removePrefix(ctx.view, ctx.pos); + size_t step = 0; + for (size_t p = 0; p < 12; p++) + { + if (startsWithCI(v, month_names[p])) + { + time.month = p + 1; + step = month_names[p].size(); + break; + } + } + if (step == 0) + return false; + ctx.pos += step; + return true; + }); + break; + } + case 'S': + //"%S": Seconds (00..59) + [[fallthrough]]; + case 's': + { + //"%s": Seconds (00..59) + parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { + auto [step, second] = parseNDigits(ctx.view, ctx.pos, 2); + if (step == 0 || second > 59) + return false; + time.second = second; + ctx.pos += step; + return true; + }); + break; + } + case 'p': + { + //"%p": AM or PM + parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase &) -> bool { + // Check the offset that will visit + if (ctx.view.size - ctx.pos < 2) + return false; + + int meridiem = 0; // 0 - invalid, 1 - am, 2 - pm + if (toLowerIfAlphaASCII(ctx.view.data[ctx.pos]) == 'a') + meridiem = 1; + else if (toLowerIfAlphaASCII(ctx.view.data[ctx.pos]) == 'p') + meridiem = 2; + + if (toLowerIfAlphaASCII(ctx.view.data[ctx.pos + 1]) != 'm') + meridiem = 0; + + if (meridiem == 0) + return false; + + ctx.state |= MyDateTimeParser::Context::ST_MERIDIEM; + ctx.meridiem = meridiem; + ctx.pos += 2; + return true; + }); + break; + } + case 'r': + { + //"%r": Time, 12-hour (hh:mm:ss followed by AM or PM) + parsers.emplace_back(parseTime12Hour); + break; + } + case 'T': + { + //"%T": Time, 24-hour (hh:mm:ss) + parsers.emplace_back(parseTime24Hour); + break; + } + case 'Y': + { + //"%Y": Year, numeric, four digits + parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { + auto [step, year] = parseYearNDigits(ctx.view, ctx.pos, 4); + if (step == 0) + return false; + time.year = year; + ctx.pos += step; + return true; + }); + break; + } + case 'y': + { + //"%y": Year, numeric, two digits. Deprecated since MySQL 5.7.5 + parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { + auto [step, year] = parseYearNDigits(ctx.view, ctx.pos, 2); + if (step == 0) + return false; + time.year = year; + ctx.pos += step; + return true; + }); + break; + } + case '#': + { + //"%#": Skip all numbers + parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase &) -> bool { + // TODO: Does ASCII numeric the same with unicode numeric? + size_t temp_pos = ctx.pos; + while (temp_pos < ctx.view.size && isNumericASCII(ctx.view.data[temp_pos])) + temp_pos++; + ctx.pos = temp_pos; + return true; + }); + break; + } + case '.': + { + //"%.": Skip all punctation characters + parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase &) -> bool { + // TODO: Does ASCII punctuation the same with unicode punctuation? + size_t temp_pos = ctx.pos; + while (temp_pos < ctx.view.size && isPunctuation(ctx.view.data[temp_pos])) + temp_pos++; + ctx.pos = temp_pos; + return true; + }); + break; + } + case '@': + { + //"%@": Skip all alpha characters + parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase &) -> bool { + // TODO: Does ASCII alpha the same with unicode alpha? + size_t temp_pos = ctx.pos; + while (temp_pos < ctx.view.size && isAlphaASCII(ctx.view.data[temp_pos])) + temp_pos++; + ctx.pos = temp_pos; + return true; + }); + break; + } + case '%': + { + //"%%": A literal % character + parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase &) -> bool { +#if 0 + if (ctx.view.data[ctx.pos] != '%') + return false; + ctx.pos++; + return true; +#else + // FIXME: Ignored by now, both tidb 5.0.0 and mariadb 10.3.14 can not handle it + std::ignore = ctx; + return false; +#endif + }); + break; + } + default: + throw Exception( + "Unknown date format pattern, [format=" + format + "] [pattern=" + x + "] [pos=" + DB::toString(format_pos) + "]", + ErrorCodes::BAD_ARGUMENTS); + } + // end the state of pattern match + in_pattern_match = false; + // move format_pos forward + format_pos++; + continue; + } + + if (x == '%') + { + in_pattern_match = true; + // move format_pos forward + format_pos++; + } + else + { + // Ignore whitespace for literal forwarding (TODO: handle unicode space?) + while (format_pos < format.size() && isWhitespaceASCII(format[format_pos])) + format_pos++; + // Move forward ctx.view with a sequence of literal `format[format_pos:span_end]` + size_t span_end = format_pos; + while (span_end < format.size() && format[span_end] != '%' && !isWhitespaceASCII(format[span_end])) + ++span_end; + const size_t span_size = span_end - format_pos; + if (span_size > 0) + { + StringRef format_view{format.data() + format_pos, span_size}; + parsers.emplace_back([format_view](MyDateTimeParser::Context & ctx, MyTimeBase &) { + assert(format_view.size > 0); + if (format_view.size == 1) + { + // Shortcut for only 1 char + if (ctx.view.data[ctx.pos] != format_view.data[0]) + return false; + ctx.pos += 1; + return true; + } + // Try best to match input as most literal as possible + auto v = removePrefix(ctx.view, ctx.pos); + size_t v_step = 0; + for (size_t format_step = 0; format_step < format_view.size; ++format_step) + { + // Ignore prefix whitespace for input + while (v_step < v.size && isWhitespaceASCII(v.data[v_step])) + ++v_step; + if (v_step == v.size) // To the end + break; + // Try to match literal + if (v.data[v_step] != format_view.data[format_step]) + return false; + ++v_step; + } + ctx.pos += v_step; + return true; + }); + } + // move format_pos forward + format_pos = span_end; + } + } +} + +bool mysqlTimeFix(const MyDateTimeParser::Context & ctx, MyTimeBase & my_time) +{ + // TODO: Implement the function that converts day of year to yy:mm:dd + if (ctx.state & MyDateTimeParser::Context::ST_DAY_OF_YEAR) + { + // %j Day of year (001..366) set + throw Exception("%j set, parsing day of year is not implemented", ErrorCodes::NOT_IMPLEMENTED); + } + + if (ctx.state & MyDateTimeParser::Context::ST_MERIDIEM) + { + // %H (00..23) set, should not set AM/PM + if (ctx.state & MyDateTimeParser::Context::ST_HOUR_0_23) + return false; + if (my_time.hour == 0) + return false; + if (my_time.hour == 12) + { + // 12 is a special hour. + if (ctx.meridiem == 1) // AM + my_time.hour = 0; + else if (ctx.meridiem == 2) // PM + my_time.hour = 12; + return true; + } + if (ctx.meridiem == 2) // PM + my_time.hour += 12; + } + else + { + // %h (01..12) set + if ((ctx.state & MyDateTimeParser::Context::ST_HOUR_1_12) && my_time.hour == 12) + my_time.hour = 0; // why? + } + return true; +} + +std::optional MyDateTimeParser::parseAsPackedUInt(const StringRef & str_view) const +{ + MyTimeBase my_time{0, 0, 0, 0, 0, 0, 0}; + MyDateTimeParser::Context ctx(str_view); + + // TODO: can we return warnings to TiDB? + for (const auto & f : parsers) + { + // Ignore all prefix white spaces before each pattern match (TODO: handle unicode space?) + while (ctx.pos < str_view.size && isWhitespaceASCII(str_view.data[ctx.pos])) + ctx.pos++; + // To the end of input, exit (successfully) even if there is more patterns to match + if (ctx.pos == ctx.view.size) + break; + + if (!f(ctx, my_time)) + { +#ifndef NDEBUG + LOG_TRACE(&Poco::Logger::get("MyDateTimeParser"), + "parse error, [str=" << ctx.view.toString() << "] [format=" << format << "] [parse_pos=" << ctx.pos << "]"); +#endif + return std::nullopt; + } + + // `ctx.pos` > `ctx.view.size` after callback, must be something wrong + if (unlikely(ctx.pos > ctx.view.size)) + { + throw Exception(String(__PRETTY_FUNCTION__) + ": parse error, pos overflow. [str=" + ctx.view.toString() + "] [format=" + format + + "] [parse_pos=" + DB::toString(ctx.pos) + "] [size=" + DB::toString(ctx.view.size) + "]"); + } + } + // Extra characters at the end of date are ignored, but a warning should be reported at this case + // if (ctx.pos < ctx.view.size) {} + + // Handle the var in `ctx` + if (!mysqlTimeFix(ctx, my_time)) + return std::nullopt; + + return my_time.toPackedUInt(); +} + +>>>>>>> 745bcce2a5 (fix date format identifies '\n' as invalid separator (#4046)) } // namespace DB diff --git a/dbms/src/Functions/tests/gtest_tidb_conversion.cpp b/dbms/src/Functions/tests/gtest_tidb_conversion.cpp new file mode 100644 index 00000000000..e9690fd2191 --- /dev/null +++ b/dbms/src/Functions/tests/gtest_tidb_conversion.cpp @@ -0,0 +1,1461 @@ +#include + +#include "Columns/ColumnsNumber.h" +#include "Core/ColumnWithTypeAndName.h" +#include "DataTypes/DataTypeMyDateTime.h" +#include "DataTypes/DataTypeMyDuration.h" +#include "DataTypes/DataTypeNullable.h" +#include "DataTypes/DataTypesNumber.h" +#include "Functions/FunctionHelpers.h" +#include "TestUtils/FunctionTestUtils.h" +#include "common/types.h" +#include "gtest/gtest.h" + +namespace DB::tests +{ +namespace +{ +auto getDatetimeColumn(bool single_field = false) +{ + MyDateTime datetime(2021, 10, 26, 16, 8, 59, 0); + MyDateTime datetime_frac(2021, 10, 26, 16, 8, 59, 123456); + + auto col_datetime = ColumnUInt64::create(); + col_datetime->insert(Field(datetime.toPackedUInt())); + if (!single_field) + col_datetime->insert(Field(datetime_frac.toPackedUInt())); + return col_datetime; +} + +auto createCastTypeConstColumn(String str) +{ + return createConstColumn(1, str); +} + +const std::string func_name = "tidb_cast"; + +const Int8 MAX_INT8 = std::numeric_limits::max(); +const Int8 MIN_INT8 = std::numeric_limits::min(); +const Int16 MAX_INT16 = std::numeric_limits::max(); +const Int16 MIN_INT16 = std::numeric_limits::min(); +const Int32 MAX_INT32 = std::numeric_limits::max(); +const Int32 MIN_INT32 = std::numeric_limits::min(); +const Int64 MAX_INT64 = std::numeric_limits::max(); +const Int64 MIN_INT64 = std::numeric_limits::min(); +const UInt8 MAX_UINT8 = std::numeric_limits::max(); +const UInt16 MAX_UINT16 = std::numeric_limits::max(); +const UInt32 MAX_UINT32 = std::numeric_limits::max(); +const UInt64 MAX_UINT64 = std::numeric_limits::max(); + +const Float32 MAX_FLOAT32 = std::numeric_limits::max(); +const Float32 MIN_FLOAT32 = std::numeric_limits::min(); +const Float64 MAX_FLOAT64 = std::numeric_limits::max(); +const Float64 MIN_FLOAT64 = std::numeric_limits::min(); + +class TestTidbConversion : public DB::tests::FunctionTest +{ +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 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 + 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); + } + } +}; + +using DecimalField32 = DecimalField; +using DecimalField64 = DecimalField; +using DecimalField128 = DecimalField; +using DecimalField256 = DecimalField; + +TEST_F(TestTidbConversion, castIntAsInt) +try +{ + /// null only cases + ASSERT_COLUMN_EQ( + createColumn>({{}}), + executeFunction(func_name, + {createOnlyNullColumn(1), + createCastTypeConstColumn("Nullable(UInt64)")})); + ASSERT_COLUMN_EQ( + createColumn>({{}}), + executeFunction(func_name, + {createOnlyNullColumn(1), + createCastTypeConstColumn("Nullable(Int64)")})); + + /// const cases + // uint8/16/32/64 -> uint64, no overflow + ASSERT_COLUMN_EQ( + createConstColumn(1, MAX_UINT8), + executeFunction(func_name, + {createConstColumn(1, MAX_UINT8), + createCastTypeConstColumn("UInt64")})); + ASSERT_COLUMN_EQ( + createConstColumn(1, MAX_UINT16), + executeFunction(func_name, + {createConstColumn(1, MAX_UINT16), + createCastTypeConstColumn("UInt64")})); + ASSERT_COLUMN_EQ( + createConstColumn(1, MAX_UINT32), + executeFunction(func_name, + {createConstColumn(1, MAX_UINT32), + createCastTypeConstColumn("UInt64")})); + ASSERT_COLUMN_EQ( + createConstColumn(1, MAX_UINT64), + executeFunction(func_name, + {createConstColumn(1, MAX_UINT64), + createCastTypeConstColumn("UInt64")})); + // int8/16/32/64 -> uint64, no overflow + ASSERT_COLUMN_EQ( + createConstColumn(1, MAX_INT8), + executeFunction(func_name, + {createConstColumn(1, MAX_INT8), + createCastTypeConstColumn("UInt64")})); + ASSERT_COLUMN_EQ( + createConstColumn(1, MAX_INT16), + executeFunction(func_name, + {createConstColumn(1, MAX_INT16), + createCastTypeConstColumn("UInt64")})); + ASSERT_COLUMN_EQ( + createConstColumn(1, MAX_INT32), + executeFunction(func_name, + {createConstColumn(1, MAX_INT32), + createCastTypeConstColumn("UInt64")})); + ASSERT_COLUMN_EQ( + createConstColumn(1, MAX_INT64), + executeFunction(func_name, + {createConstColumn(1, MAX_INT64), + createCastTypeConstColumn("UInt64")})); + // uint8/16/32 -> int64, no overflow + ASSERT_COLUMN_EQ( + createConstColumn(1, MAX_UINT8), + executeFunction(func_name, + {createConstColumn(1, MAX_UINT8), + createCastTypeConstColumn("Int64")})); + ASSERT_COLUMN_EQ( + createConstColumn(1, MAX_UINT16), + executeFunction(func_name, + {createConstColumn(1, MAX_UINT16), + createCastTypeConstColumn("Int64")})); + ASSERT_COLUMN_EQ( + createConstColumn(1, MAX_UINT32), + executeFunction(func_name, + {createConstColumn(1, MAX_UINT32), + createCastTypeConstColumn("Int64")})); + // uint64 -> int64, will overflow + ASSERT_COLUMN_EQ( + createConstColumn(1, -1), + executeFunction(func_name, + {createConstColumn(1, MAX_UINT64), + createCastTypeConstColumn("Int64")})); + // int8/16/32/64 -> int64, no overflow + ASSERT_COLUMN_EQ( + createConstColumn(1, MAX_INT8), + executeFunction(func_name, + {createConstColumn(1, MAX_INT8), + createCastTypeConstColumn("Int64")})); + ASSERT_COLUMN_EQ( + createConstColumn(1, MAX_INT16), + executeFunction(func_name, + {createConstColumn(1, MAX_INT16), + createCastTypeConstColumn("Int64")})); + ASSERT_COLUMN_EQ( + createConstColumn(1, MAX_INT32), + executeFunction(func_name, + {createConstColumn(1, MAX_INT32), + createCastTypeConstColumn("Int64")})); + ASSERT_COLUMN_EQ( + createConstColumn(1, MAX_INT64), + executeFunction(func_name, + {createConstColumn(1, MAX_INT64), + createCastTypeConstColumn("Int64")})); + + /// normal cases + // uint8/16/32/64 -> uint64, no overflow + ASSERT_COLUMN_EQ( + createColumn>({0, 1, MAX_UINT8, {}}), + executeFunction(func_name, + {createColumn>({0, 1, MAX_UINT8, {}}), + createCastTypeConstColumn("Nullable(UInt64)")})); + ASSERT_COLUMN_EQ( + createColumn>({0, 1, MAX_UINT16, {}}), + executeFunction(func_name, + {createColumn>({0, 1, MAX_UINT16, {}}), + createCastTypeConstColumn("Nullable(UInt64)")})); + ASSERT_COLUMN_EQ( + createColumn>({0, 1, MAX_UINT32, {}}), + executeFunction(func_name, + {createColumn>({0, 1, MAX_UINT32, {}}), + createCastTypeConstColumn("Nullable(UInt64)")})); + ASSERT_COLUMN_EQ( + createColumn>({0, 1, MAX_UINT64, {}}), + executeFunction(func_name, + {createColumn>({0, 1, MAX_UINT64, {}}), + createCastTypeConstColumn("Nullable(UInt64)")})); + // int8/16/32/64 -> uint64, no overflow + ASSERT_COLUMN_EQ( + createColumn>({0, MAX_INT8, MAX_UINT64, MAX_UINT64 - MAX_INT8, {}}), + executeFunction(func_name, + {createColumn>({0, MAX_INT8, -1, MIN_INT8, {}}), + createCastTypeConstColumn("Nullable(UInt64)")})); + ASSERT_COLUMN_EQ( + createColumn>({0, MAX_INT16, MAX_UINT64, MAX_UINT64 - MAX_INT16, {}}), + executeFunction(func_name, + {createColumn>({0, MAX_INT16, -1, MIN_INT16, {}}), + createCastTypeConstColumn("Nullable(UInt64)")})); + ASSERT_COLUMN_EQ( + createColumn>({0, MAX_INT32, MAX_UINT64, MAX_UINT64 - MAX_INT32, {}}), + executeFunction(func_name, + {createColumn>({0, MAX_INT32, -1, MIN_INT32, {}}), + createCastTypeConstColumn("Nullable(UInt64)")})); + ASSERT_COLUMN_EQ( + createColumn>({0, MAX_INT64, MAX_UINT64, MAX_UINT64 - MAX_INT64, {}}), + executeFunction(func_name, + {createColumn>({0, MAX_INT64, -1, MIN_INT64, {}}), + createCastTypeConstColumn("Nullable(UInt64)")})); + // uint8/16/32 -> int64, no overflow + ASSERT_COLUMN_EQ( + createColumn>({0, MAX_INT8, MAX_UINT8, {}}), + executeFunction(func_name, + {createColumn>({0, MAX_INT8, MAX_UINT8, {}}), + createCastTypeConstColumn("Nullable(Int64)")})); + ASSERT_COLUMN_EQ( + createColumn>({0, MAX_INT16, MAX_UINT16, {}}), + executeFunction(func_name, + {createColumn>({0, MAX_INT16, MAX_UINT16, {}}), + createCastTypeConstColumn("Nullable(Int64)")})); + ASSERT_COLUMN_EQ( + createColumn>({0, MAX_INT32, MAX_UINT32, {}}), + executeFunction(func_name, + {createColumn>({0, MAX_INT32, MAX_UINT32, {}}), + createCastTypeConstColumn("Nullable(Int64)")})); + // uint64 -> int64, overflow may happen + ASSERT_COLUMN_EQ( + createColumn>({0, MAX_INT64, -1, {}}), + executeFunction(func_name, + {createColumn>({0, MAX_INT64, MAX_UINT64, {}}), + createCastTypeConstColumn("Nullable(Int64)")})); + // int8/16/32/64 -> int64, no overflow + ASSERT_COLUMN_EQ( + createColumn>({0, MAX_INT8, -1, MIN_INT8, {}}), + executeFunction(func_name, + {createColumn>({0, MAX_INT8, -1, MIN_INT8, {}}), + createCastTypeConstColumn("Nullable(Int64)")})); + ASSERT_COLUMN_EQ( + createColumn>({0, MAX_INT16, -1, MIN_INT16, {}}), + executeFunction(func_name, + {createColumn>({0, MAX_INT16, -1, MIN_INT16, {}}), + createCastTypeConstColumn("Nullable(Int64)")})); + ASSERT_COLUMN_EQ( + createColumn>({0, MAX_INT32, -1, MIN_INT32, {}}), + executeFunction(func_name, + {createColumn>({0, MAX_INT32, -1, MIN_INT32, {}}), + createCastTypeConstColumn("Nullable(Int64)")})); + ASSERT_COLUMN_EQ( + createColumn>({0, MAX_INT64, -1, MIN_INT64, {}}), + executeFunction(func_name, + {createColumn>({0, MAX_INT64, -1, MIN_INT64, {}}), + createCastTypeConstColumn("Nullable(Int64)")})); +} +CATCH + +TEST_F(TestTidbConversion, castIntAsReal) +try +{ + // uint64/int64 -> float64, may be not precise + ASSERT_COLUMN_EQ( + createColumn>( + {1234567890.0, + 123456789012345680.0, + 0.0, + {}}), + executeFunction(func_name, + {createColumn>( + {1234567890, // this is fine + 123456789012345678, // but this cannot be represented precisely in the IEEE 754 64-bit float format + 0, + {}}), + createCastTypeConstColumn("Nullable(Float64)")})); + ASSERT_COLUMN_EQ( + createColumn>( + {1234567890.0, + 123456789012345680.0, + 0.0, + {}}), + executeFunction(func_name, + {createColumn>( + {1234567890, // this is fine + 123456789012345678, // but this cannot be represented precisely in the IEEE 754 64-bit float format + 0, + {}}), + createCastTypeConstColumn("Nullable(Float64)")})); + // uint32/16/8 and int32/16/8 -> float64, precise + ASSERT_COLUMN_EQ( + createColumn>({MAX_UINT32, 0, {}}), + executeFunction(func_name, + {createColumn>({MAX_UINT32, 0, {}}), + createCastTypeConstColumn("Nullable(Float64)")})); + ASSERT_COLUMN_EQ( + createColumn>({MAX_UINT16, 0, {}}), + executeFunction(func_name, + {createColumn>({MAX_UINT16, 0, {}}), + createCastTypeConstColumn("Nullable(Float64)")})); + ASSERT_COLUMN_EQ( + createColumn>({MAX_UINT8, 0, {}}), + executeFunction(func_name, + {createColumn>({MAX_UINT8, 0, {}}), + createCastTypeConstColumn("Nullable(Float64)")})); + ASSERT_COLUMN_EQ( + createColumn>({MAX_INT32, MIN_INT32, 0, {}}), + executeFunction(func_name, + {createColumn>({MAX_INT32, MIN_INT32, 0, {}}), + createCastTypeConstColumn("Nullable(Float64)")})); + ASSERT_COLUMN_EQ( + createColumn>({MAX_INT16, MIN_INT16, 0, {}}), + executeFunction(func_name, + {createColumn>({MAX_INT16, MIN_INT16, 0, {}}), + createCastTypeConstColumn("Nullable(Float64)")})); + ASSERT_COLUMN_EQ( + createColumn>({MAX_INT8, MIN_INT8, 0, {}}), + executeFunction(func_name, + {createColumn>({MAX_INT8, MIN_INT8, 0, {}}), + createCastTypeConstColumn("Nullable(Float64)")})); +} +CATCH + +TEST_F(TestTidbConversion, castIntAsString) +try +{ + /// null only cases + ASSERT_COLUMN_EQ( + createColumn>({{}}), + executeFunction(func_name, + {createOnlyNullColumn(1), + createCastTypeConstColumn("Nullable(String)")})); + + /// const cases + // uint64/32/16/8 -> string + ASSERT_COLUMN_EQ( + createConstColumn(1, "18446744073709551615"), + executeFunction(func_name, + {createConstColumn(1, MAX_UINT64), + createCastTypeConstColumn("String")})); + ASSERT_COLUMN_EQ( + createConstColumn(1, "4294967295"), + executeFunction(func_name, + {createConstColumn(1, MAX_UINT32), + createCastTypeConstColumn("String")})); + ASSERT_COLUMN_EQ( + createConstColumn(1, "65535"), + executeFunction(func_name, + {createConstColumn(1, MAX_UINT16), + createCastTypeConstColumn("String")})); + ASSERT_COLUMN_EQ( + createConstColumn(1, "255"), + executeFunction(func_name, + {createConstColumn(1, MAX_UINT8), + createCastTypeConstColumn("String")})); + // int64/32/16/8 -> string + ASSERT_COLUMN_EQ( + createConstColumn(1, "9223372036854775807"), + executeFunction(func_name, + {createConstColumn(1, MAX_INT64), + createCastTypeConstColumn("String")})); + ASSERT_COLUMN_EQ( + createConstColumn(1, "2147483647"), + executeFunction(func_name, + {createConstColumn(1, MAX_INT32), + createCastTypeConstColumn("String")})); + ASSERT_COLUMN_EQ( + createConstColumn(1, "32767"), + executeFunction(func_name, + {createConstColumn(1, MAX_INT16), + createCastTypeConstColumn("String")})); + ASSERT_COLUMN_EQ( + createConstColumn(1, "127"), + executeFunction(func_name, + {createConstColumn(1, MAX_INT8), + createCastTypeConstColumn("String")})); + + /// normal cases + // uint64/32/16/8 -> string + ASSERT_COLUMN_EQ( + createColumn>({"18446744073709551615", "0", {}}), + executeFunction(func_name, + {createColumn>({MAX_UINT64, 0, {}}), + createCastTypeConstColumn("Nullable(String)")})); + ASSERT_COLUMN_EQ( + createColumn>({"4294967295", "0", {}}), + executeFunction(func_name, + {createColumn>({MAX_UINT32, 0, {}}), + createCastTypeConstColumn("Nullable(String)")})); + ASSERT_COLUMN_EQ( + createColumn>({"65535", "0", {}}), + executeFunction(func_name, + {createColumn>({MAX_UINT16, 0, {}}), + createCastTypeConstColumn("Nullable(String)")})); + ASSERT_COLUMN_EQ( + createColumn>({"255", "0", {}}), + executeFunction(func_name, + {createColumn>({MAX_UINT8, 0, {}}), + createCastTypeConstColumn("Nullable(String)")})); + // int64/32/16/8 -> string + ASSERT_COLUMN_EQ( + createColumn>({"9223372036854775807", "-9223372036854775808", "0", {}}), + executeFunction(func_name, + {createColumn>({MAX_INT64, MIN_INT64, 0, {}}), + createCastTypeConstColumn("Nullable(String)")})); + ASSERT_COLUMN_EQ( + createColumn>({"2147483647", "-2147483648", "0", {}}), + executeFunction(func_name, + {createColumn>({MAX_INT32, MIN_INT32, 0, {}}), + createCastTypeConstColumn("Nullable(String)")})); + ASSERT_COLUMN_EQ( + createColumn>({"32767", "-32768", "0", {}}), + executeFunction(func_name, + {createColumn>({MAX_INT16, MIN_INT16, 0, {}}), + createCastTypeConstColumn("Nullable(String)")})); + ASSERT_COLUMN_EQ( + createColumn>({"127", "-128", "0", {}}), + executeFunction(func_name, + {createColumn>({MAX_INT8, MIN_INT8, 0, {}}), + createCastTypeConstColumn("Nullable(String)")})); +} +CATCH + +TEST_F(TestTidbConversion, castIntAsDecimal) +try +{ + // int8 -> decimal32/64/128/256 + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(9, 0), + {DecimalField32(MAX_INT8, 0), DecimalField32(MIN_INT8, 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_INT8, MIN_INT8, {}}), + createCastTypeConstColumn("Nullable(Decimal(9,0))")})); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(18, 0), + {DecimalField64(MAX_INT8, 0), DecimalField64(MIN_INT8, 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_INT8, MIN_INT8, {}}), + createCastTypeConstColumn("Nullable(Decimal(18,0))")})); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(38, 0), + {DecimalField128(MAX_INT8, 0), DecimalField128(MIN_INT8, 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_INT8, MIN_INT8, {}}), + createCastTypeConstColumn("Nullable(Decimal(38,0))")})); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(65, 0), + {DecimalField256(static_cast(MAX_INT8), 0), DecimalField256(static_cast(MIN_INT8), 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_INT8, MIN_INT8, {}}), + createCastTypeConstColumn("Nullable(Decimal(65,0))")})); + // int16 -> decimal32/64/128/256 + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(9, 0), + {DecimalField32(MAX_INT16, 0), DecimalField32(MIN_INT16, 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_INT16, MIN_INT16, {}}), + createCastTypeConstColumn("Nullable(Decimal(9,0))")})); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(18, 0), + {DecimalField64(MAX_INT16, 0), DecimalField64(MIN_INT16, 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_INT16, MIN_INT16, {}}), + createCastTypeConstColumn("Nullable(Decimal(18,0))")})); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(38, 0), + {DecimalField128(MAX_INT16, 0), DecimalField128(MIN_INT16, 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_INT16, MIN_INT16, {}}), + createCastTypeConstColumn("Nullable(Decimal(38,0))")})); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(65, 0), + {DecimalField256(static_cast(MAX_INT16), 0), DecimalField256(static_cast(MIN_INT16), 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_INT16, MIN_INT16, {}}), + createCastTypeConstColumn("Nullable(Decimal(65,0))")})); + // int32 -> decimal32/64/128/256 + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(9, 0), + {DecimalField32(999999999, 0), DecimalField32(-999999999, 0), {}}), + executeFunction(func_name, + {createColumn>({999999999, -999999999, {}}), + createCastTypeConstColumn("Nullable(Decimal(9,0))")})); + ASSERT_THROW(executeFunction(func_name, + {createColumn>({1000000000, -1000000000, {}}), + createCastTypeConstColumn("Nullable(Decimal(9,0))")}), + TiFlashException); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(18, 0), + {DecimalField64(MAX_INT32, 0), DecimalField64(MIN_INT32, 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_INT32, MIN_INT32, {}}), + createCastTypeConstColumn("Nullable(Decimal(18,0))")})); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(38, 0), + {DecimalField128(MAX_INT32, 0), DecimalField128(MIN_INT32, 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_INT32, MIN_INT32, {}}), + createCastTypeConstColumn("Nullable(Decimal(38,0))")})); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(65, 0), + {DecimalField256(static_cast(MAX_INT32), 0), DecimalField256(static_cast(MIN_INT32), 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_INT32, MIN_INT32, {}}), + createCastTypeConstColumn("Nullable(Decimal(65,0))")})); + // int64 -> decimal32/64/128/256 + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(9, 0), + {DecimalField32(999999999, 0), DecimalField32(-999999999, 0), {}}), + executeFunction(func_name, + {createColumn>({999999999, -999999999, {}}), + createCastTypeConstColumn("Nullable(Decimal(9,0))")})); + ASSERT_THROW(executeFunction(func_name, + {createColumn>({1000000000, -1000000000, {}}), + createCastTypeConstColumn("Nullable(Decimal(9,0))")}), + TiFlashException); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(18, 0), + {DecimalField64(999999999999999999, 0), DecimalField64(-999999999999999999, 0), {}}), + executeFunction(func_name, + {createColumn>({999999999999999999, -999999999999999999, {}}), + createCastTypeConstColumn("Nullable(Decimal(18,0))")})); + ASSERT_THROW(executeFunction(func_name, + {createColumn>({1000000000000000000, -1000000000000000000, {}}), + createCastTypeConstColumn("Nullable(Decimal(18,0))")}), + TiFlashException); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(38, 0), + {DecimalField128(MAX_INT64, 0), DecimalField128(MIN_INT64, 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_INT64, MIN_INT64, {}}), + createCastTypeConstColumn("Nullable(Decimal(38,0))")})); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(65, 0), + {DecimalField256(static_cast(MAX_INT64), 0), DecimalField256(static_cast(MIN_INT64), 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_INT64, MIN_INT64, {}}), + createCastTypeConstColumn("Nullable(Decimal(65,0))")})); + // uint8 -> decimal32/64/128/256 + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(9, 0), + {DecimalField32(MAX_UINT8, 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_UINT8, {}}), + createCastTypeConstColumn("Nullable(Decimal(9,0))")})); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(18, 0), + {DecimalField64(MAX_UINT8, 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_UINT8, {}}), + createCastTypeConstColumn("Nullable(Decimal(18,0))")})); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(38, 0), + {DecimalField128(MAX_UINT8, 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_UINT8, {}}), + createCastTypeConstColumn("Nullable(Decimal(38,0))")})); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(65, 0), + {DecimalField256(static_cast(MAX_UINT8), 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_UINT8, {}}), + createCastTypeConstColumn("Nullable(Decimal(65,0))")})); + // uint16 -> decimal32/64/128/256 + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(9, 0), + {DecimalField32(MAX_UINT16, 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_UINT16, {}}), + createCastTypeConstColumn("Nullable(Decimal(9,0))")})); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(18, 0), + {DecimalField64(MAX_UINT16, 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_UINT16, {}}), + createCastTypeConstColumn("Nullable(Decimal(18,0))")})); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(38, 0), + {DecimalField128(MAX_UINT16, 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_UINT16, {}}), + createCastTypeConstColumn("Nullable(Decimal(38,0))")})); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(65, 0), + {DecimalField256(static_cast(MAX_UINT16), 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_UINT16, {}}), + createCastTypeConstColumn("Nullable(Decimal(65,0))")})); + // uint32 -> decimal32/64/128/256 + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(9, 0), + {DecimalField32(999999999, 0), {}}), + executeFunction(func_name, + {createColumn>({999999999, {}}), + createCastTypeConstColumn("Nullable(Decimal(9,0))")})); + ASSERT_THROW(executeFunction(func_name, + {createColumn>({1000000000, {}}), + createCastTypeConstColumn("Nullable(Decimal(9,0))")}), + TiFlashException); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(18, 0), + {DecimalField64(MAX_UINT32, 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_UINT32, {}}), + createCastTypeConstColumn("Nullable(Decimal(18,0))")})); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(38, 0), + {DecimalField128(MAX_UINT32, 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_UINT32, {}}), + createCastTypeConstColumn("Nullable(Decimal(38,0))")})); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(65, 0), + {DecimalField256(static_cast(MAX_UINT32), 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_UINT32, {}}), + createCastTypeConstColumn("Nullable(Decimal(65,0))")})); + // uint64 -> decimal32/64/128/256 + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(9, 0), + {DecimalField32(999999999, 0), {}}), + executeFunction(func_name, + {createColumn>({999999999, {}}), + createCastTypeConstColumn("Nullable(Decimal(9,0))")})); + ASSERT_THROW(executeFunction(func_name, + {createColumn>({1000000000, {}}), + createCastTypeConstColumn("Nullable(Decimal(9,0))")}), + TiFlashException); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(18, 0), + {DecimalField64(999999999999999999, 0), {}}), + executeFunction(func_name, + {createColumn>({999999999999999999, {}}), + createCastTypeConstColumn("Nullable(Decimal(18,0))")})); + ASSERT_THROW(executeFunction(func_name, + {createColumn>({1000000000000000000, {}}), + createCastTypeConstColumn("Nullable(Decimal(18,0))")}), + TiFlashException); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(38, 0), + {DecimalField128(MAX_INT64, 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_INT64, {}}), + createCastTypeConstColumn("Nullable(Decimal(38,0))")})); + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(65, 0), + {DecimalField256(static_cast(MAX_INT64), 0), {}}), + executeFunction(func_name, + {createColumn>({MAX_INT64, {}}), + createCastTypeConstColumn("Nullable(Decimal(65,0))")})); + + ASSERT_THROW(executeFunction(func_name, + {createColumn>({9999}), createCastTypeConstColumn("Nullable(Decimal(4, 1))")}), + TiFlashException); + + ASSERT_THROW(executeFunction(func_name, + {createColumn>({-9999}), createCastTypeConstColumn("Nullable(Decimal(4, 1))")}), + TiFlashException); + + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(4, 1), + {DecimalField32(static_cast(9990), 1)}), + executeFunction(func_name, + {createColumn>({999}), createCastTypeConstColumn("Nullable(Decimal(4, 1))")})); + + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(4, 1), + {DecimalField32(static_cast(-9990), 1)}), + executeFunction(func_name, + {createColumn>({-999}), createCastTypeConstColumn("Nullable(Decimal(4, 1))")})); + + DAGContext * dag_context = context.getDAGContext(); + UInt64 ori_flags = dag_context->getFlags(); + dag_context->addFlag(TiDBSQLFlags::OVERFLOW_AS_WARNING); + dag_context->clearWarnings(); + + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(4, 1), + {DecimalField32(static_cast(9999), 1)}), + executeFunction(func_name, + {createColumn>({9999}), createCastTypeConstColumn("Nullable(Decimal(4, 1))")})); + + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(4, 1), + {DecimalField32(static_cast(-9999), 1)}), + executeFunction(func_name, + {createColumn>({-9999}), createCastTypeConstColumn("Nullable(Decimal(4, 1))")})); + + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(2, 2), + {DecimalField32(static_cast(99), 2)}), + executeFunction(func_name, + {createColumn>({9999}), createCastTypeConstColumn("Nullable(Decimal(2, 2))")})); + + ASSERT_COLUMN_EQ( + createColumn>( + std::make_tuple(2, 2), + {DecimalField32(static_cast(-99), 2)}), + executeFunction(func_name, + {createColumn>({-9999}), createCastTypeConstColumn("Nullable(Decimal(2, 2))")})); + + dag_context->setFlags(ori_flags); +} +CATCH + +TEST_F(TestTidbConversion, castIntAsTime) +try +{ + ASSERT_COLUMN_EQ( + createDateTimeColumn({{}, {{2021, 10, 26, 16, 8, 59, 0}}}, 6), + executeFunction(func_name, + {createColumn>({{}, 20211026160859}), + createCastTypeConstColumn("Nullable(MyDateTime(6))")})); + ASSERT_COLUMN_EQ( + createDateTimeColumn({{}, {{2021, 10, 26, 16, 8, 59, 0}}}, 6), + executeFunction(func_name, + {createColumn>({{}, 20211026160859}), + createCastTypeConstColumn("Nullable(MyDateTime(6))")})); + ASSERT_THROW( + executeFunction(func_name, + {createColumn>({MAX_UINT8}), + createCastTypeConstColumn("Nullable(MyDateTime(6))")}), + TiFlashException); + ASSERT_THROW( + executeFunction(func_name, + {createColumn>({MAX_UINT16}), + createCastTypeConstColumn("Nullable(MyDateTime(6))")}), + TiFlashException); + ASSERT_THROW( + executeFunction(func_name, + {createColumn>({MAX_UINT32}), + createCastTypeConstColumn("Nullable(MyDateTime(6))")}), + TiFlashException); + ASSERT_COLUMN_EQ( + createDateTimeColumn({{}}, 6), + executeFunction(func_name, + {createColumn>({0}), + createCastTypeConstColumn("Nullable(MyDateTime(6))")})); + ASSERT_THROW( + executeFunction(func_name, + {createColumn>({{}, -20211026160859}), + createCastTypeConstColumn("Nullable(MyDateTime(6))")}), + TiFlashException); +} +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)); + + dag_context->setFlags(ori_flags); + dag_context->clearWarnings(); +} +CATCH + +TEST_F(TestTidbConversion, castRealAsTime) +try +{ + testOnlyNull(); + testOnlyNull(); + + // TODO add tests after non-expected results fixed + + // mysql: null, warning. + // tiflash: null, no warning. + // tidb: 0000-00-00 00:00:00 + // testThrowException(0, 6); + testThrowException(12.213, 6); + testThrowException(-12.213, 6); + testThrowException(MAX_FLOAT32, 6); + testThrowException(MIN_FLOAT32, 6); + // mysql: 2000-01-11 00:00:00 + // tiflash / tidb: null, warnings + // testNotOnlyNull(111, {2000, 1, 11, 0, 0, 0, 0}, 6); + testThrowException(-111, 6); + // mysql: 2000-01-11 00:00:00 + // tiflash / tidb: null, warnings + // testNotOnlyNull(111.1, {2000, 1, 11, 0, 0, 0, 0}, 6); + + // mysql: null, warning. + // tiflash: null, no warning. + // tidb: 0000-00-00 00:00:00 + // testThrowException(0, 6); + testThrowException(12.213, 6); + testThrowException(-12.213, 6); + testThrowException(MAX_FLOAT64, 6); + testThrowException(MIN_FLOAT64, 6); + // mysql: 2000-01-11 00:00:00 + // tiflash / tidb: null, warnings + // testNotOnlyNull(111, {2000, 1, 11, 0, 0, 0, 0}, 6); + testThrowException(-111, 6); + // mysql: 2000-01-11 00:00:00 + // tiflash / tidb: null, warnings + // testNotOnlyNull(111.1, {2000, 1, 11, 0, 0, 0, 0}, 6); + testNotOnlyNull(20210201, {2021, 2, 1, 0, 0, 0, 0}, 6); + // mysql: 2021-02-01 00:00:00 + // tiflash / tidb: 2021-02-01 01:00:00 + // testNotOnlyNull(20210201.1, {2021, 2, 1, 0, 0, 0, 0}, 6); +} +CATCH + +TEST_F(TestTidbConversion, castTimeAsReal) +try +{ + const auto data_type_ptr = std::make_shared(6); + const Float64 datetime_float = 20211026160859; + const Float64 datetime_frac_float = 20211026160859.125; + + // cast datetime to float + auto col_datetime1 = getDatetimeColumn(); + auto ctn_datetime1 = ColumnWithTypeAndName(std::move(col_datetime1), data_type_ptr, "datetime"); + ASSERT_COLUMN_EQ( + createColumn({datetime_float, datetime_frac_float}), + executeFunction(func_name, + {ctn_datetime1, + createCastTypeConstColumn("Float64")})); + + // cast datetime to nullable float + auto col_datetime2 = getDatetimeColumn(); + auto ctn_datetime2 = ColumnWithTypeAndName(std::move(col_datetime2), data_type_ptr, "datetime"); + ASSERT_COLUMN_EQ( + createColumn>({datetime_float, datetime_frac_float}), + executeFunction(func_name, + {ctn_datetime2, + createCastTypeConstColumn("Nullable(Float64)")})); + + // cast nullable datetime to nullable float + auto col_datetime3 = getDatetimeColumn(); + auto datetime3_null_map = ColumnUInt8::create(2, 0); + datetime3_null_map->getData()[1] = 1; + auto col_datetime3_nullable = ColumnNullable::create(std::move(col_datetime3), std::move(datetime3_null_map)); + auto ctn_datetime3_nullable = ColumnWithTypeAndName(std::move(col_datetime3_nullable), makeNullable(data_type_ptr), "datetime"); + ASSERT_COLUMN_EQ( + createColumn>({datetime_float, {}}), + executeFunction(func_name, + {ctn_datetime3_nullable, + createCastTypeConstColumn("Nullable(Float64)")})); + + // cast const datetime to float + auto col_datetime4_const = ColumnConst::create(getDatetimeColumn(true), 1); + auto ctn_datetime4_const = ColumnWithTypeAndName(std::move(col_datetime4_const), data_type_ptr, "datetime"); + ASSERT_COLUMN_EQ( + createConstColumn(1, datetime_float), + executeFunction(func_name, + {ctn_datetime4_const, + createCastTypeConstColumn("Float64")})); + + // cast nullable const datetime to float + auto col_datetime5 = getDatetimeColumn(true); + auto datetime5_null_map = ColumnUInt8::create(1, 0); + auto col_datetime5_nullable = ColumnNullable::create(std::move(col_datetime5), std::move(datetime5_null_map)); + auto col_datetime5_nullable_const = ColumnConst::create(std::move(col_datetime5_nullable), 1); + auto ctn_datetime5_nullable_const = ColumnWithTypeAndName(std::move(col_datetime5_nullable_const), makeNullable(data_type_ptr), "datetime"); + ASSERT_COLUMN_EQ( + createConstColumn>(1, datetime_float), + executeFunction(func_name, + {ctn_datetime5_nullable_const, + createCastTypeConstColumn("Nullable(Float64)")})); +} +CATCH + +TEST_F(TestTidbConversion, castDurationAsDuration) +try +{ + const auto from_type = std::make_shared(3); + const auto to_type_1 = std::make_shared(5); // from_fsp < to_fsp + const auto to_type_2 = std::make_shared(3); // from_fsp == to_fsp + const auto to_type_3 = std::make_shared(2); // from_fsp < to_fsp + + ColumnWithTypeAndName input( + createColumn({(20 * 3600 + 20 * 60 + 20) * 1000000000L + 555000000L, + -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 555000000L, + (20 * 3600 + 20 * 60 + 20) * 1000000000L + 554000000L, + -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 554000000L, + (20 * 3600 + 20 * 60 + 20) * 1000000000L + 999000000L, + -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 999000000L}) + .column, + from_type, + "input"); + + ColumnWithTypeAndName output1(input.column, to_type_1, "output1"); + ColumnWithTypeAndName output2(input.column, to_type_2, "output2"); + ColumnWithTypeAndName output3( + createColumn({(20 * 3600 + 20 * 60 + 20) * 1000000000L + 560000000L, + -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 560000000L, + (20 * 3600 + 20 * 60 + 20) * 1000000000L + 550000000L, + -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 550000000L, + (20 * 3600 + 20 * 60 + 21) * 1000000000L + 000000000L, + -(20 * 3600 + 20 * 60 + 21) * 1000000000L - 000000000L}) + .column, + to_type_3, + "output3"); + + ASSERT_COLUMN_EQ(output1, executeFunction(func_name, {input, createCastTypeConstColumn(to_type_1->getName())})); + ASSERT_COLUMN_EQ(output2, executeFunction(func_name, {input, createCastTypeConstColumn(to_type_2->getName())})); + ASSERT_COLUMN_EQ(output3, executeFunction(func_name, {input, createCastTypeConstColumn(to_type_3->getName())})); + + // Test Nullable + ColumnWithTypeAndName input_nullable( + createColumn>({(20 * 3600 + 20 * 60 + 20) * 1000000000L + 555000000L, + -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 555000000L, + {}, + (20 * 3600 + 20 * 60 + 20) * 1000000000L + 554000000L, + -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 554000000L, + {}, + (20 * 3600 + 20 * 60 + 20) * 1000000000L + 999000000L, + -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 999000000L}) + .column, + makeNullable(input.type), + "input_nullable"); + ColumnWithTypeAndName output1_nullable(input_nullable.column, makeNullable(to_type_1), "output1_nullable"); + ColumnWithTypeAndName output2_nullable(input_nullable.column, makeNullable(to_type_2), "output2_nullable"); + ColumnWithTypeAndName output3_nullable( + createColumn>({(20 * 3600 + 20 * 60 + 20) * 1000000000L + 560000000L, + -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 560000000L, + {}, + (20 * 3600 + 20 * 60 + 20) * 1000000000L + 550000000L, + -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 550000000L, + {}, + (20 * 3600 + 20 * 60 + 21) * 1000000000L + 000000000L, + -(20 * 3600 + 20 * 60 + 21) * 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())})); + + // Test Const + ColumnWithTypeAndName input_const(createConstColumn(1, (20 * 3600 + 20 * 60 + 20) * 1000000000L + 999000000L).column, from_type, "input_const"); + ColumnWithTypeAndName output1_const(input_const.column, to_type_1, "output1_const"); + ColumnWithTypeAndName output2_const(input_const.column, to_type_2, "output2_const"); + ColumnWithTypeAndName output3_const(createConstColumn(1, (20 * 3600 + 20 * 60 + 21) * 1000000000L + 000000000L).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())})); +} +CATCH + +TEST_F(TestTidbConversion, StrToDateTypeTest) +try +{ + // Arg1 is ColumnVector, Arg2 is ColumnVector + auto arg1_column = createColumn>({{}, "1/12/2020", "00:59:60 ", "1/12/2020"}); + auto arg2_column = createColumn>({"%d/%c/%Y", {}, "%H:%i:%S ", "%d/%c/%Y"}); + ColumnWithTypeAndName result_column( + createColumn>({{}, {}, {}, MyDateTime{2020, 12, 1, 0, 0, 0, 0}.toPackedUInt()}).column, + makeNullable(std::make_shared(0)), + "result"); + ASSERT_COLUMN_EQ(result_column, executeFunction("strToDateDatetime", arg1_column, arg2_column)); + + // Arg1 is ColumnConst(ColumnNullable(non-null value)), Arg2 is ColumnVector + arg1_column = createConstColumn>(2, {"1/12/2020"}); + arg2_column = createColumn>({"%d/%c/%Y", "%d/%c/%Y"}); + result_column = ColumnWithTypeAndName( + createColumn>({MyDateTime{2020, 12, 1, 0, 0, 0, 0}.toPackedUInt(), MyDateTime{2020, 12, 1, 0, 0, 0, 0}.toPackedUInt()}).column, + makeNullable(std::make_shared(0)), + "result"); + ASSERT_COLUMN_EQ(result_column, executeFunction("strToDateDatetime", arg1_column, arg2_column)); + + // Arg1 is ColumnConst(ColumnNullable(null value)), Arg2 is ColumnVector + arg1_column = createConstColumn>(2, {}); + arg2_column = createColumn>({"%d/%c/%Y", "%d/%c/%Y"}); + result_column = ColumnWithTypeAndName( + createConstColumn>(2, {}).column, + makeNullable(std::make_shared(0)), + "result"); + ASSERT_COLUMN_EQ(result_column, executeFunction("strToDateDatetime", arg1_column, arg2_column)); + + // Arg1 is ColumnVector, Arg2 is ColumnConst(ColumnNullable(non-null value)) + arg1_column = createColumn>({"1/12/2020", "1/12/2020"}); + arg2_column = createConstColumn>(2, "%d/%c/%Y"); + result_column = ColumnWithTypeAndName( + createColumn>({MyDateTime{2020, 12, 1, 0, 0, 0, 0}.toPackedUInt(), MyDateTime{2020, 12, 1, 0, 0, 0, 0}.toPackedUInt()}).column, + makeNullable(std::make_shared(0)), + "result"); + ASSERT_COLUMN_EQ(result_column, executeFunction("strToDateDatetime", arg1_column, arg2_column)); + + // Arg1 is ColumnConst(ColumnNullable(non-null value)), Arg2 is ColumnConst(ColumnNullable(non-null value)) + arg1_column = createConstColumn>(2, "1/12/2020"); + arg2_column = createConstColumn>(2, "%d/%c/%Y"); + result_column = ColumnWithTypeAndName( + createConstColumn>(2, {MyDateTime{2020, 12, 1, 0, 0, 0, 0}.toPackedUInt()}).column, + makeNullable(std::make_shared(0)), + "result"); + ASSERT_COLUMN_EQ(result_column, executeFunction("strToDateDatetime", arg1_column, arg2_column)); + + // Arg1 is ColumnConst(ColumnNullable(null value)), Arg2 is ColumnConst(ColumnNullable(non-null value)) + arg1_column = createConstColumn>(2, {}); + arg2_column = createConstColumn>(2, "%d/%c/%Y"); + result_column = ColumnWithTypeAndName( + createConstColumn>(2, {}).column, + makeNullable(std::make_shared(0)), + "result"); + ASSERT_COLUMN_EQ(result_column, executeFunction("strToDateDatetime", arg1_column, arg2_column)); + + // Arg1 is ColumnVector, Arg2 is ColumnConst(ColumnNullable(null value)) + arg1_column = createColumn>({"1/12/2020", "1/12/2020"}); + arg2_column = createConstColumn>(2, {}); + result_column = ColumnWithTypeAndName( + createConstColumn>(2, {}).column, + makeNullable(std::make_shared(0)), + "result"); + ASSERT_COLUMN_EQ(result_column, executeFunction("strToDateDatetime", arg1_column, arg2_column)); + + // Arg1 is ColumnConst(ColumnNullable(non-null value)), Arg2 is ColumnConst(ColumnNullable(null value)) + arg1_column = createConstColumn>(2, {"1/12/2020"}); + arg2_column = createConstColumn>(2, {}); + result_column = ColumnWithTypeAndName( + createConstColumn>(2, {}).column, + makeNullable(std::make_shared(0)), + "result"); + ASSERT_COLUMN_EQ(result_column, executeFunction("strToDateDatetime", arg1_column, arg2_column)); + + // Arg1 is ColumnConst(ColumnNullable(null value)), Arg2 is ColumnConst(ColumnNullable(null value)) + arg1_column = createConstColumn>(2, {}); + arg2_column = createConstColumn>(2, {}); + result_column = ColumnWithTypeAndName( + createConstColumn>(2, {}).column, + makeNullable(std::make_shared(0)), + "result"); + ASSERT_COLUMN_EQ(result_column, executeFunction("strToDateDatetime", arg1_column, arg2_column)); +} +CATCH + +// for https://github.com/pingcap/tics/issues/4036 +TEST_F(TestTidbConversion, castStringAsDateTime) +try +{ + auto input = std::vector{"2012-12-12 12:12:12", "2012-12-12\t12:12:12", "2012-12-12\n12:12:12", "2012-12-12\v12:12:12", "2012-12-12\f12:12:12", "2012-12-12\r12:12:12"}; + auto to_column = createConstColumn(1, "MyDateTime(6)"); + + // vector + auto from_column = createColumn(input); + UInt64 except_packed = MyDateTime(2012, 12, 12, 12, 12, 12, 0).toPackedUInt(); + auto vector_result = executeFunction("tidb_cast", {from_column, to_column}); + for (size_t i = 0; i < input.size(); i++) + { + ASSERT_EQ(except_packed, vector_result.column.get()->get64(i)); + } + + // const + auto const_from_column = createConstColumn(1, "2012-12-12\n12:12:12"); + auto const_result = executeFunction("tidb_cast", {from_column, to_column}); + ASSERT_EQ(except_packed, const_result.column.get()->get64(0)); + + // nullable + auto nullable_from_column = createColumn>({"2012-12-12 12:12:12", "2012-12-12\t12:12:12", "2012-12-12\n12:12:12", "2012-12-12\v12:12:12", "2012-12-12\f12:12:12", "2012-12-12\r12:12:12"}); + auto nullable_result = executeFunction("tidb_cast", {from_column, to_column}); + for (size_t i = 0; i < input.size(); i++) + { + ASSERT_EQ(except_packed, nullable_result.column.get()->get64(i)); + } +} +CATCH + +} // namespace +} // namespace DB::tests From 88acd7c8a8d993e81515cae551c4981573e131c0 Mon Sep 17 00:00:00 2001 From: Meng Xin Date: Wed, 15 Jun 2022 20:20:18 +0800 Subject: [PATCH 2/3] Update MyTime.cpp resolve conflict --- dbms/src/Common/MyTime.cpp | 855 +------------------------------------ 1 file changed, 3 insertions(+), 852 deletions(-) diff --git a/dbms/src/Common/MyTime.cpp b/dbms/src/Common/MyTime.cpp index 6fbe8537853..d7ec5ec7a68 100644 --- a/dbms/src/Common/MyTime.cpp +++ b/dbms/src/Common/MyTime.cpp @@ -497,8 +497,8 @@ Field parseMyDateTime(const String & str, int8_t fsp) bool truncated_or_incorrect = false; - // no_absorb tests if can absorb FSP or TZ - auto no_absorb = [](const std::vector & seps) { + // noAbsorb tests if can absorb FSP or TZ + auto noAbsorb = [](const std::vector & seps) { // if we have more than 5 parts (i.e. 6), the tailing part can't be absorbed // or if we only have 1 part, but its length is longer than 4, then it is at least YYMMD, in this case, FSP can // not be absorbed, and it will be handled later, and the leading sign prevents TZ from being absorbed, because @@ -508,7 +508,7 @@ Field parseMyDateTime(const String & str, int8_t fsp) if (!frac_str.empty()) { - if (!no_absorb(seps)) + if (!noAbsorb(seps)) { seps.push_back(frac_str); frac_str = ""; @@ -519,11 +519,7 @@ Field parseMyDateTime(const String & str, int8_t fsp) { // if tz_sign is empty, it's sure that the string literal contains timezone (e.g., 2010-10-10T10:10:10Z), // therefore we could safely skip this branch. -<<<<<<< HEAD if (!noAbsorb(seps) && !(tz_minute != "" && tz_sep == "")) -======= - if (!no_absorb(seps) && !(!tz_minute.empty() && tz_sep.empty())) ->>>>>>> 745bcce2a5 (fix date format identifies '\n' as invalid separator (#4046)) { // we can't absorb timezone if there is no separate between tz_hour and tz_minute if (!tz_hour.empty()) @@ -540,7 +536,6 @@ Field parseMyDateTime(const String & str, int8_t fsp) switch (seps.size()) { -<<<<<<< HEAD // No delimiter case 1: { @@ -649,173 +644,38 @@ Field parseMyDateTime(const String & str, int8_t fsp) { throw TiFlashException("Datetime truncated: " + str, Errors::Types::Truncated); } -======= - // No delimiter - case 1: - { - size_t l = seps[0].size(); - switch (l) - { - case 14: // YYYYMMDDHHMMSS - { - std::sscanf(seps[0].c_str(), "%4d%2d%2d%2d%2d%2d", &year, &month, &day, &hour, &minute, &second); //NOLINT - hhmmss = true; - break; - } - case 12: // YYMMDDHHMMSS - { - std::sscanf(seps[0].c_str(), "%2d%2d%2d%2d%2d%2d", &year, &month, &day, &hour, &minute, &second); //NOLINT - year = adjustYear(year); - hhmmss = true; - break; - } - case 11: // YYMMDDHHMMS - { - std::sscanf(seps[0].c_str(), "%2d%2d%2d%2d%2d%1d", &year, &month, &day, &hour, &minute, &second); //NOLINT - year = adjustYear(year); - hhmmss = true; - break; - } - case 10: // YYMMDDHHMM - { - std::sscanf(seps[0].c_str(), "%2d%2d%2d%2d%2d", &year, &month, &day, &hour, &minute); //NOLINT - year = adjustYear(year); ->>>>>>> 745bcce2a5 (fix date format identifies '\n' as invalid separator (#4046)) break; } case 3: { -<<<<<<< HEAD // YYYY-MM-DD scanTimeArgs(seps, {&year, &month, &day}); -======= - std::sscanf(seps[0].c_str(), "%2d%2d%2d%2d%1d", &year, &month, &day, &hour, &minute); //NOLINT - year = adjustYear(year); ->>>>>>> 745bcce2a5 (fix date format identifies '\n' as invalid separator (#4046)) break; } case 4: { -<<<<<<< HEAD // YYYY-MM-DD HH scanTimeArgs(seps, {&year, &month, &day, &hour}); -======= - std::sscanf(seps[0].c_str(), "%4d%2d%2d", &year, &month, &day); //NOLINT ->>>>>>> 745bcce2a5 (fix date format identifies '\n' as invalid separator (#4046)) break; } case 5: { -<<<<<<< HEAD // YYYY-MM-DD HH-MM scanTimeArgs(seps, {&year, &month, &day, &hour, &minute}); -======= - std::sscanf(seps[0].c_str(), "%2d%2d%2d%1d", &year, &month, &day, &hour); //NOLINT - year = adjustYear(year); ->>>>>>> 745bcce2a5 (fix date format identifies '\n' as invalid separator (#4046)) break; } case 6: { -<<<<<<< HEAD // We don't have fractional seconds part. // YYYY-MM-DD HH-MM-SS scanTimeArgs(seps, {&year, &month, &day, &hour, &minute, &second}); hhmmss = true; -======= - std::sscanf(seps[0].c_str(), "%2d%2d%2d", &year, &month, &day); //NOLINT - year = adjustYear(year); ->>>>>>> 745bcce2a5 (fix date format identifies '\n' as invalid separator (#4046)) break; } default: { throw Exception("Wrong datetime format"); } -<<<<<<< HEAD -======= - if (l == 5 || l == 6 || l == 8) - { - // YYMMDD or YYYYMMDD - // We must handle float => string => datetime, the difference is that fractional - // part of float type is discarded directly, while fractional part of string type - // is parsed to HH:MM:SS. - int ret = 0; - switch (frac_str.size()) - { - case 0: - ret = 1; - is_date = true; - break; - case 1: - case 2: - { - ret = std::sscanf(frac_str.c_str(), "%2d ", &hour); //NOLINT - break; - } - case 3: - case 4: - { - ret = std::sscanf(frac_str.c_str(), "%2d%2d ", &hour, &minute); //NOLINT - break; - } - default: - { - ret = std::sscanf(frac_str.c_str(), "%2d%2d%2d ", &hour, &minute, &second); //NOLINT - break; - } - } - truncated_or_incorrect = (ret == 0); - } - if (l == 9 || l == 10) - { - if (frac_str.empty()) - { - second = 0; - } - else - { - truncated_or_incorrect = (std::sscanf(frac_str.c_str(), "%2d ", &second) == 0); //NOLINT - } - } - if (truncated_or_incorrect) - { - throw TiFlashException("Datetime truncated: " + str, Errors::Types::Truncated); - } - break; - } - case 3: - { - // YYYY-MM-DD - scanTimeArgs(seps, {&year, &month, &day}); - is_date = true; - break; - } - case 4: - { - // YYYY-MM-DD HH - scanTimeArgs(seps, {&year, &month, &day, &hour}); - break; - } - case 5: - { - // YYYY-MM-DD HH-MM - scanTimeArgs(seps, {&year, &month, &day, &hour, &minute}); - break; - } - case 6: - { - // We don't have fractional seconds part. - // YYYY-MM-DD HH-MM-SS - scanTimeArgs(seps, {&year, &month, &day, &hour, &minute, &second}); - hhmmss = true; - break; - } - default: - { - throw Exception("Wrong datetime format"); - } ->>>>>>> 745bcce2a5 (fix date format identifies '\n' as invalid separator (#4046)) } // If str is sepereated by delimiters, the first one is year, and if the year is 2 digit, @@ -1092,15 +952,8 @@ void MyTimeBase::check(bool allow_zero_in_date, bool allow_invalid_date) const if (!allow_invalid_date) { constexpr static UInt8 max_days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; -<<<<<<< HEAD static auto is_leap_year = [](UInt16 _year) { return ((_year % 4 == 0) && (_year % 100 != 0)) || (_year % 400 == 0); }; max_day = max_days_in_month[month - 1]; -======= - static auto is_leap_year = [](UInt16 _year) { - return ((_year % 4 == 0) && (_year % 100 != 0)) || (_year % 400 == 0); - }; - max_day = max_days_in_month[month - 1]; // NOLINT ->>>>>>> 745bcce2a5 (fix date format identifies '\n' as invalid separator (#4046)) if (month == 2 && is_leap_year(year)) { max_day = 29; @@ -1365,706 +1218,4 @@ MyDateTimeFormatter::MyDateTimeFormatter(const String & layout) } } -<<<<<<< HEAD -======= -struct MyDateTimeParser::Context -{ - // Some state for `mysqlTimeFix` - uint32_t state = 0; - static constexpr uint32_t ST_DAY_OF_YEAR = 0x01; - static constexpr uint32_t ST_MERIDIEM = 0x02; - static constexpr uint32_t ST_HOUR_0_23 = 0x04; - static constexpr uint32_t ST_HOUR_1_12 = 0x08; - - int32_t day_of_year = 0; - // 0 - invalid, 1 - am, 2 - pm - int32_t meridiem = 0; - - // The input string view - const StringRef view; - // The pos we are parsing from - size_t pos = 0; - - explicit Context(StringRef view_) - : view(std::move(view_)) - {} -}; - -// Try to parse digits with number of `limit` starting from view[pos] -// Return if success. -// Return <0, _> if fail. -static std::tuple parseNDigits(const StringRef & view, const size_t pos, const size_t limit) -{ - size_t step = 0; - int32_t num = 0; - while (step < limit && (pos + step) < view.size && isNumericASCII(view.data[pos + step])) - { - num = num * 10 + (view.data[pos + step] - '0'); - step += 1; - } - return std::make_tuple(step, num); -} - -static std::tuple parseYearNDigits(const StringRef & view, const size_t pos, const size_t limit) -{ - // Try to parse a "year" within `limit` digits - size_t step = 0; - int32_t year = 0; - std::tie(step, year) = parseNDigits(view, pos, limit); - if (step == 0) - return std::make_tuple(step, 0); - else if (step <= 2) - year = adjustYear(year); - return std::make_tuple(step, year); -} - -enum class ParseState -{ - NORMAL = 0, // Parsing - FAIL = 1, // Fail to parse - END_OF_FILE = 2, // The end of input -}; - -//"%r": Time, 12-hour (hh:mm:ss followed by AM or PM) -static bool parseTime12Hour(MyDateTimeParser::Context & ctx, MyTimeBase & time) -{ - // Use temp_pos instead of changing `ctx.pos` directly in case of parsing failure - size_t temp_pos = ctx.pos; - auto check_if_end = [&temp_pos, &ctx]() -> ParseState { - // To the end - if (temp_pos == ctx.view.size) - return ParseState::END_OF_FILE; - return ParseState::NORMAL; - }; - auto skip_whitespaces = [&temp_pos, &ctx, &check_if_end]() -> ParseState { - while (temp_pos < ctx.view.size && isWhitespaceASCII(ctx.view.data[temp_pos])) - ++temp_pos; - return check_if_end(); - }; - auto parse_sep = [&temp_pos, &ctx, &skip_whitespaces]() -> ParseState { - if (skip_whitespaces() == ParseState::END_OF_FILE) - return ParseState::END_OF_FILE; - // parse ":" - if (ctx.view.data[temp_pos] != ':') - return ParseState::FAIL; - temp_pos += 1; // move forward - return ParseState::NORMAL; - }; - auto try_parse = [&]() -> ParseState { - ParseState state = ParseState::NORMAL; - /// Note that we should update `time` as soon as possible, or we - /// can not get correct result for incomplete input like "12:13" - /// that is less than "hh:mm:ssAM" - - // hh - size_t step = 0; - int32_t hour = 0; - if (state = skip_whitespaces(); state != ParseState::NORMAL) - return state; - std::tie(step, hour) = parseNDigits(ctx.view, temp_pos, 2); - if (step == 0 || hour > 12 || hour == 0) - return ParseState::FAIL; - // Handle special case: 12:34:56 AM -> 00:34:56 - // For PM, we will add 12 it later - if (hour == 12) - hour = 0; - time.hour = hour; - temp_pos += step; // move forward - - if (state = parse_sep(); state != ParseState::NORMAL) - return state; - - int32_t minute = 0; - if (state = skip_whitespaces(); state != ParseState::NORMAL) - return state; - std::tie(step, minute) = parseNDigits(ctx.view, temp_pos, 2); - if (step == 0 || minute > 59) - return ParseState::FAIL; - time.minute = minute; - temp_pos += step; // move forward - - if (state = parse_sep(); state != ParseState::NORMAL) - return state; - - int32_t second = 0; - if (state = skip_whitespaces(); state != ParseState::NORMAL) - return state; - std::tie(step, second) = parseNDigits(ctx.view, temp_pos, 2); - if (step == 0 || second > 59) - return ParseState::FAIL; - time.second = second; - temp_pos += step; // move forward - - int meridiem = 0; // 0 - invalid, 1 - am, 2 - pm - if (state = skip_whitespaces(); state != ParseState::NORMAL) - return state; - // "AM"/"PM" must be parsed as a single element - // "11:13:56a" is an invalid input for "%r". - if (auto size_to_end = ctx.view.size - temp_pos; size_to_end < 2) - return ParseState::FAIL; - if (toLowerIfAlphaASCII(ctx.view.data[temp_pos]) == 'a') - meridiem = 1; - else if (toLowerIfAlphaASCII(ctx.view.data[temp_pos]) == 'p') - meridiem = 2; - - if (toLowerIfAlphaASCII(ctx.view.data[temp_pos + 1]) != 'm') - meridiem = 0; - switch (meridiem) - { - case 0: - return ParseState::FAIL; - case 1: - break; - case 2: - time.hour += 12; - break; - } - temp_pos += 2; // move forward - return ParseState::NORMAL; - }; - if (auto state = try_parse(); state == ParseState::FAIL) - return false; - // Other state, forward the `ctx.pos` and return true - ctx.pos = temp_pos; - return true; -} - -//"%T": Time, 24-hour (hh:mm:ss) -static bool parseTime24Hour(MyDateTimeParser::Context & ctx, MyTimeBase & time) -{ - // Use temp_pos instead of changing `ctx.pos` directly in case of parsing failure - size_t temp_pos = ctx.pos; - auto check_if_end = [&temp_pos, &ctx]() -> ParseState { - // To the end - if (temp_pos == ctx.view.size) - return ParseState::END_OF_FILE; - return ParseState::NORMAL; - }; - auto skip_whitespaces = [&temp_pos, &ctx, &check_if_end]() -> ParseState { - while (temp_pos < ctx.view.size && isWhitespaceASCII(ctx.view.data[temp_pos])) - ++temp_pos; - return check_if_end(); - }; - auto parse_sep = [&temp_pos, &ctx, &skip_whitespaces]() -> ParseState { - if (skip_whitespaces() == ParseState::END_OF_FILE) - return ParseState::END_OF_FILE; - // parse ":" - if (ctx.view.data[temp_pos] != ':') - return ParseState::FAIL; - temp_pos += 1; // move forward - return ParseState::NORMAL; - }; - auto try_parse = [&]() -> ParseState { - ParseState state = ParseState::NORMAL; - /// Note that we should update `time` as soon as possible, or we - /// can not get correct result for incomplete input like "12:13" - /// that is less than "hh:mm:ss" - - // hh - size_t step = 0; - int32_t hour = 0; - if (state = skip_whitespaces(); state != ParseState::NORMAL) - return state; - std::tie(step, hour) = parseNDigits(ctx.view, temp_pos, 2); - if (step == 0 || hour > 23) - return ParseState::FAIL; - time.hour = hour; - temp_pos += step; // move forward - - if (state = parse_sep(); state != ParseState::NORMAL) - return state; - - int32_t minute = 0; - if (state = skip_whitespaces(); state != ParseState::NORMAL) - return state; - std::tie(step, minute) = parseNDigits(ctx.view, temp_pos, 2); - if (step == 0 || minute > 59) - return ParseState::FAIL; - time.minute = minute; - temp_pos += step; // move forward - - if (state = parse_sep(); state != ParseState::NORMAL) - return state; - - int32_t second = 0; - if (state = skip_whitespaces(); state != ParseState::NORMAL) - return state; - std::tie(step, second) = parseNDigits(ctx.view, temp_pos, 2); - if (step == 0 || second > 59) - return ParseState::FAIL; - time.second = second; - temp_pos += step; // move forward - - return ParseState::NORMAL; - }; - if (auto state = try_parse(); state == ParseState::FAIL) - return false; - // Other state, forward the `ctx.pos` and return true - ctx.pos = temp_pos; - return true; -} - -// Refer: https://github.com/pingcap/tidb/blob/v5.0.1/types/time.go#L2946 -MyDateTimeParser::MyDateTimeParser(String format_) - : format(std::move(format_)) -{ - // Ignore all prefix white spaces (TODO: handle unicode space?) - size_t format_pos = 0; - while (format_pos < format.size() && isWhitespaceASCII(format[format_pos])) - format_pos++; - - bool in_pattern_match = false; - while (format_pos < format.size()) - { - char x = format[format_pos]; - if (in_pattern_match) - { - switch (x) - { - case 'b': - { - //"%b": Abbreviated month name (Jan..Dec) - parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { - size_t step = 0; - auto v = removePrefix(ctx.view, ctx.pos); - for (size_t p = 0; p < 12; p++) - { - if (startsWithCI(v, abbrev_month_names[p])) - { - time.month = p + 1; - step = abbrev_month_names[p].size(); - break; - } - } - if (step == 0) - return false; - ctx.pos += step; - return true; - }); - break; - } - case 'm': - //"%m": Month, numeric (00..12) - [[fallthrough]]; - case 'c': - { - //"%c": Month, numeric (0..12) - parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { - // To be compatible with TiDB & MySQL, first try to take two digit and parse it as `num` - auto [step, month] = parseNDigits(ctx.view, ctx.pos, 2); - // Then check whether num is valid month - // Note that 0 is valid when sql_mode does not contain NO_ZERO_IN_DATE,NO_ZERO_DATE - if (step == 0 || month > 12) - return false; - time.month = month; - ctx.pos += step; - return true; - }); - break; - } - case 'd': //"%d": Day of the month, numeric (00..31) - [[fallthrough]]; - case 'e': //"%e": Day of the month, numeric (0..31) - { - parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { - auto [step, day] = parseNDigits(ctx.view, ctx.pos, 2); - if (step == 0 || day > 31) - return false; - time.day = day; - ctx.pos += step; - return true; - }); - break; - } - case 'f': - { - //"%f": Microseconds (000000..999999) - parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { - auto [step, ms] = parseNDigits(ctx.view, ctx.pos, 6); - // Empty string is a valid input - if (step == 0) - { - time.micro_second = 0; - return true; - } - // The suffix '0' can be ignored. - // "9" means 900000 - for (size_t i = step; i < 6; i++) - { - ms *= 10; - } - time.micro_second = ms; - ctx.pos += step; - return true; - }); - break; - } - case 'k': - //"%k": Hour (0..23) - [[fallthrough]]; - case 'H': - { - //"%H": Hour (00..23) - parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { - auto [step, hour] = parseNDigits(ctx.view, ctx.pos, 2); - if (step == 0 || hour > 23) - return false; - ctx.state |= MyDateTimeParser::Context::ST_HOUR_0_23; - time.hour = hour; - ctx.pos += step; - return true; - }); - break; - } - case 'l': - //"%l": Hour (1..12) - [[fallthrough]]; - case 'I': - //"%I": Hour (01..12) - [[fallthrough]]; - case 'h': - { - //"%h": Hour (01..12) - parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { - auto [step, hour] = parseNDigits(ctx.view, ctx.pos, 2); - if (step == 0 || hour <= 0 || hour > 12) - return false; - ctx.state |= MyDateTimeParser::Context::ST_HOUR_1_12; - time.hour = hour; - ctx.pos += step; - return true; - }); - break; - } - case 'i': - { - //"%i": Minutes, numeric (00..59) - parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { - auto [step, num] = parseNDigits(ctx.view, ctx.pos, 2); - if (step == 0 || num > 59) - return false; - time.minute = num; - ctx.pos += step; - return true; - }); - break; - } - case 'j': - { - //"%j": Day of year (001..366) - parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase &) -> bool { - auto [step, num] = parseNDigits(ctx.view, ctx.pos, 3); - if (step == 0 || num == 0 || num > 366) - return false; - ctx.state |= MyDateTimeParser::Context::ST_DAY_OF_YEAR; - ctx.day_of_year = num; - ctx.pos += step; - return true; - }); - break; - } - case 'M': - { - //"%M": Month name (January..December) - parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { - auto v = removePrefix(ctx.view, ctx.pos); - size_t step = 0; - for (size_t p = 0; p < 12; p++) - { - if (startsWithCI(v, month_names[p])) - { - time.month = p + 1; - step = month_names[p].size(); - break; - } - } - if (step == 0) - return false; - ctx.pos += step; - return true; - }); - break; - } - case 'S': - //"%S": Seconds (00..59) - [[fallthrough]]; - case 's': - { - //"%s": Seconds (00..59) - parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { - auto [step, second] = parseNDigits(ctx.view, ctx.pos, 2); - if (step == 0 || second > 59) - return false; - time.second = second; - ctx.pos += step; - return true; - }); - break; - } - case 'p': - { - //"%p": AM or PM - parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase &) -> bool { - // Check the offset that will visit - if (ctx.view.size - ctx.pos < 2) - return false; - - int meridiem = 0; // 0 - invalid, 1 - am, 2 - pm - if (toLowerIfAlphaASCII(ctx.view.data[ctx.pos]) == 'a') - meridiem = 1; - else if (toLowerIfAlphaASCII(ctx.view.data[ctx.pos]) == 'p') - meridiem = 2; - - if (toLowerIfAlphaASCII(ctx.view.data[ctx.pos + 1]) != 'm') - meridiem = 0; - - if (meridiem == 0) - return false; - - ctx.state |= MyDateTimeParser::Context::ST_MERIDIEM; - ctx.meridiem = meridiem; - ctx.pos += 2; - return true; - }); - break; - } - case 'r': - { - //"%r": Time, 12-hour (hh:mm:ss followed by AM or PM) - parsers.emplace_back(parseTime12Hour); - break; - } - case 'T': - { - //"%T": Time, 24-hour (hh:mm:ss) - parsers.emplace_back(parseTime24Hour); - break; - } - case 'Y': - { - //"%Y": Year, numeric, four digits - parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { - auto [step, year] = parseYearNDigits(ctx.view, ctx.pos, 4); - if (step == 0) - return false; - time.year = year; - ctx.pos += step; - return true; - }); - break; - } - case 'y': - { - //"%y": Year, numeric, two digits. Deprecated since MySQL 5.7.5 - parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase & time) -> bool { - auto [step, year] = parseYearNDigits(ctx.view, ctx.pos, 2); - if (step == 0) - return false; - time.year = year; - ctx.pos += step; - return true; - }); - break; - } - case '#': - { - //"%#": Skip all numbers - parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase &) -> bool { - // TODO: Does ASCII numeric the same with unicode numeric? - size_t temp_pos = ctx.pos; - while (temp_pos < ctx.view.size && isNumericASCII(ctx.view.data[temp_pos])) - temp_pos++; - ctx.pos = temp_pos; - return true; - }); - break; - } - case '.': - { - //"%.": Skip all punctation characters - parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase &) -> bool { - // TODO: Does ASCII punctuation the same with unicode punctuation? - size_t temp_pos = ctx.pos; - while (temp_pos < ctx.view.size && isPunctuation(ctx.view.data[temp_pos])) - temp_pos++; - ctx.pos = temp_pos; - return true; - }); - break; - } - case '@': - { - //"%@": Skip all alpha characters - parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase &) -> bool { - // TODO: Does ASCII alpha the same with unicode alpha? - size_t temp_pos = ctx.pos; - while (temp_pos < ctx.view.size && isAlphaASCII(ctx.view.data[temp_pos])) - temp_pos++; - ctx.pos = temp_pos; - return true; - }); - break; - } - case '%': - { - //"%%": A literal % character - parsers.emplace_back([](MyDateTimeParser::Context & ctx, MyTimeBase &) -> bool { -#if 0 - if (ctx.view.data[ctx.pos] != '%') - return false; - ctx.pos++; - return true; -#else - // FIXME: Ignored by now, both tidb 5.0.0 and mariadb 10.3.14 can not handle it - std::ignore = ctx; - return false; -#endif - }); - break; - } - default: - throw Exception( - "Unknown date format pattern, [format=" + format + "] [pattern=" + x + "] [pos=" + DB::toString(format_pos) + "]", - ErrorCodes::BAD_ARGUMENTS); - } - // end the state of pattern match - in_pattern_match = false; - // move format_pos forward - format_pos++; - continue; - } - - if (x == '%') - { - in_pattern_match = true; - // move format_pos forward - format_pos++; - } - else - { - // Ignore whitespace for literal forwarding (TODO: handle unicode space?) - while (format_pos < format.size() && isWhitespaceASCII(format[format_pos])) - format_pos++; - // Move forward ctx.view with a sequence of literal `format[format_pos:span_end]` - size_t span_end = format_pos; - while (span_end < format.size() && format[span_end] != '%' && !isWhitespaceASCII(format[span_end])) - ++span_end; - const size_t span_size = span_end - format_pos; - if (span_size > 0) - { - StringRef format_view{format.data() + format_pos, span_size}; - parsers.emplace_back([format_view](MyDateTimeParser::Context & ctx, MyTimeBase &) { - assert(format_view.size > 0); - if (format_view.size == 1) - { - // Shortcut for only 1 char - if (ctx.view.data[ctx.pos] != format_view.data[0]) - return false; - ctx.pos += 1; - return true; - } - // Try best to match input as most literal as possible - auto v = removePrefix(ctx.view, ctx.pos); - size_t v_step = 0; - for (size_t format_step = 0; format_step < format_view.size; ++format_step) - { - // Ignore prefix whitespace for input - while (v_step < v.size && isWhitespaceASCII(v.data[v_step])) - ++v_step; - if (v_step == v.size) // To the end - break; - // Try to match literal - if (v.data[v_step] != format_view.data[format_step]) - return false; - ++v_step; - } - ctx.pos += v_step; - return true; - }); - } - // move format_pos forward - format_pos = span_end; - } - } -} - -bool mysqlTimeFix(const MyDateTimeParser::Context & ctx, MyTimeBase & my_time) -{ - // TODO: Implement the function that converts day of year to yy:mm:dd - if (ctx.state & MyDateTimeParser::Context::ST_DAY_OF_YEAR) - { - // %j Day of year (001..366) set - throw Exception("%j set, parsing day of year is not implemented", ErrorCodes::NOT_IMPLEMENTED); - } - - if (ctx.state & MyDateTimeParser::Context::ST_MERIDIEM) - { - // %H (00..23) set, should not set AM/PM - if (ctx.state & MyDateTimeParser::Context::ST_HOUR_0_23) - return false; - if (my_time.hour == 0) - return false; - if (my_time.hour == 12) - { - // 12 is a special hour. - if (ctx.meridiem == 1) // AM - my_time.hour = 0; - else if (ctx.meridiem == 2) // PM - my_time.hour = 12; - return true; - } - if (ctx.meridiem == 2) // PM - my_time.hour += 12; - } - else - { - // %h (01..12) set - if ((ctx.state & MyDateTimeParser::Context::ST_HOUR_1_12) && my_time.hour == 12) - my_time.hour = 0; // why? - } - return true; -} - -std::optional MyDateTimeParser::parseAsPackedUInt(const StringRef & str_view) const -{ - MyTimeBase my_time{0, 0, 0, 0, 0, 0, 0}; - MyDateTimeParser::Context ctx(str_view); - - // TODO: can we return warnings to TiDB? - for (const auto & f : parsers) - { - // Ignore all prefix white spaces before each pattern match (TODO: handle unicode space?) - while (ctx.pos < str_view.size && isWhitespaceASCII(str_view.data[ctx.pos])) - ctx.pos++; - // To the end of input, exit (successfully) even if there is more patterns to match - if (ctx.pos == ctx.view.size) - break; - - if (!f(ctx, my_time)) - { -#ifndef NDEBUG - LOG_TRACE(&Poco::Logger::get("MyDateTimeParser"), - "parse error, [str=" << ctx.view.toString() << "] [format=" << format << "] [parse_pos=" << ctx.pos << "]"); -#endif - return std::nullopt; - } - - // `ctx.pos` > `ctx.view.size` after callback, must be something wrong - if (unlikely(ctx.pos > ctx.view.size)) - { - throw Exception(String(__PRETTY_FUNCTION__) + ": parse error, pos overflow. [str=" + ctx.view.toString() + "] [format=" + format - + "] [parse_pos=" + DB::toString(ctx.pos) + "] [size=" + DB::toString(ctx.view.size) + "]"); - } - } - // Extra characters at the end of date are ignored, but a warning should be reported at this case - // if (ctx.pos < ctx.view.size) {} - - // Handle the var in `ctx` - if (!mysqlTimeFix(ctx, my_time)) - return std::nullopt; - - return my_time.toPackedUInt(); -} - ->>>>>>> 745bcce2a5 (fix date format identifies '\n' as invalid separator (#4046)) } // namespace DB From 3cae097db41e80819d41822d4bf56fe0dd6fc6f2 Mon Sep 17 00:00:00 2001 From: Meng Xin Date: Wed, 15 Jun 2022 20:20:57 +0800 Subject: [PATCH 3/3] Delete gtest_tidb_conversion.cpp this version not have gtest_tidb_conversion.cpp --- .../Functions/tests/gtest_tidb_conversion.cpp | 1461 ----------------- 1 file changed, 1461 deletions(-) delete mode 100644 dbms/src/Functions/tests/gtest_tidb_conversion.cpp diff --git a/dbms/src/Functions/tests/gtest_tidb_conversion.cpp b/dbms/src/Functions/tests/gtest_tidb_conversion.cpp deleted file mode 100644 index e9690fd2191..00000000000 --- a/dbms/src/Functions/tests/gtest_tidb_conversion.cpp +++ /dev/null @@ -1,1461 +0,0 @@ -#include - -#include "Columns/ColumnsNumber.h" -#include "Core/ColumnWithTypeAndName.h" -#include "DataTypes/DataTypeMyDateTime.h" -#include "DataTypes/DataTypeMyDuration.h" -#include "DataTypes/DataTypeNullable.h" -#include "DataTypes/DataTypesNumber.h" -#include "Functions/FunctionHelpers.h" -#include "TestUtils/FunctionTestUtils.h" -#include "common/types.h" -#include "gtest/gtest.h" - -namespace DB::tests -{ -namespace -{ -auto getDatetimeColumn(bool single_field = false) -{ - MyDateTime datetime(2021, 10, 26, 16, 8, 59, 0); - MyDateTime datetime_frac(2021, 10, 26, 16, 8, 59, 123456); - - auto col_datetime = ColumnUInt64::create(); - col_datetime->insert(Field(datetime.toPackedUInt())); - if (!single_field) - col_datetime->insert(Field(datetime_frac.toPackedUInt())); - return col_datetime; -} - -auto createCastTypeConstColumn(String str) -{ - return createConstColumn(1, str); -} - -const std::string func_name = "tidb_cast"; - -const Int8 MAX_INT8 = std::numeric_limits::max(); -const Int8 MIN_INT8 = std::numeric_limits::min(); -const Int16 MAX_INT16 = std::numeric_limits::max(); -const Int16 MIN_INT16 = std::numeric_limits::min(); -const Int32 MAX_INT32 = std::numeric_limits::max(); -const Int32 MIN_INT32 = std::numeric_limits::min(); -const Int64 MAX_INT64 = std::numeric_limits::max(); -const Int64 MIN_INT64 = std::numeric_limits::min(); -const UInt8 MAX_UINT8 = std::numeric_limits::max(); -const UInt16 MAX_UINT16 = std::numeric_limits::max(); -const UInt32 MAX_UINT32 = std::numeric_limits::max(); -const UInt64 MAX_UINT64 = std::numeric_limits::max(); - -const Float32 MAX_FLOAT32 = std::numeric_limits::max(); -const Float32 MIN_FLOAT32 = std::numeric_limits::min(); -const Float64 MAX_FLOAT64 = std::numeric_limits::max(); -const Float64 MIN_FLOAT64 = std::numeric_limits::min(); - -class TestTidbConversion : public DB::tests::FunctionTest -{ -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 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 - 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); - } - } -}; - -using DecimalField32 = DecimalField; -using DecimalField64 = DecimalField; -using DecimalField128 = DecimalField; -using DecimalField256 = DecimalField; - -TEST_F(TestTidbConversion, castIntAsInt) -try -{ - /// null only cases - ASSERT_COLUMN_EQ( - createColumn>({{}}), - executeFunction(func_name, - {createOnlyNullColumn(1), - createCastTypeConstColumn("Nullable(UInt64)")})); - ASSERT_COLUMN_EQ( - createColumn>({{}}), - executeFunction(func_name, - {createOnlyNullColumn(1), - createCastTypeConstColumn("Nullable(Int64)")})); - - /// const cases - // uint8/16/32/64 -> uint64, no overflow - ASSERT_COLUMN_EQ( - createConstColumn(1, MAX_UINT8), - executeFunction(func_name, - {createConstColumn(1, MAX_UINT8), - createCastTypeConstColumn("UInt64")})); - ASSERT_COLUMN_EQ( - createConstColumn(1, MAX_UINT16), - executeFunction(func_name, - {createConstColumn(1, MAX_UINT16), - createCastTypeConstColumn("UInt64")})); - ASSERT_COLUMN_EQ( - createConstColumn(1, MAX_UINT32), - executeFunction(func_name, - {createConstColumn(1, MAX_UINT32), - createCastTypeConstColumn("UInt64")})); - ASSERT_COLUMN_EQ( - createConstColumn(1, MAX_UINT64), - executeFunction(func_name, - {createConstColumn(1, MAX_UINT64), - createCastTypeConstColumn("UInt64")})); - // int8/16/32/64 -> uint64, no overflow - ASSERT_COLUMN_EQ( - createConstColumn(1, MAX_INT8), - executeFunction(func_name, - {createConstColumn(1, MAX_INT8), - createCastTypeConstColumn("UInt64")})); - ASSERT_COLUMN_EQ( - createConstColumn(1, MAX_INT16), - executeFunction(func_name, - {createConstColumn(1, MAX_INT16), - createCastTypeConstColumn("UInt64")})); - ASSERT_COLUMN_EQ( - createConstColumn(1, MAX_INT32), - executeFunction(func_name, - {createConstColumn(1, MAX_INT32), - createCastTypeConstColumn("UInt64")})); - ASSERT_COLUMN_EQ( - createConstColumn(1, MAX_INT64), - executeFunction(func_name, - {createConstColumn(1, MAX_INT64), - createCastTypeConstColumn("UInt64")})); - // uint8/16/32 -> int64, no overflow - ASSERT_COLUMN_EQ( - createConstColumn(1, MAX_UINT8), - executeFunction(func_name, - {createConstColumn(1, MAX_UINT8), - createCastTypeConstColumn("Int64")})); - ASSERT_COLUMN_EQ( - createConstColumn(1, MAX_UINT16), - executeFunction(func_name, - {createConstColumn(1, MAX_UINT16), - createCastTypeConstColumn("Int64")})); - ASSERT_COLUMN_EQ( - createConstColumn(1, MAX_UINT32), - executeFunction(func_name, - {createConstColumn(1, MAX_UINT32), - createCastTypeConstColumn("Int64")})); - // uint64 -> int64, will overflow - ASSERT_COLUMN_EQ( - createConstColumn(1, -1), - executeFunction(func_name, - {createConstColumn(1, MAX_UINT64), - createCastTypeConstColumn("Int64")})); - // int8/16/32/64 -> int64, no overflow - ASSERT_COLUMN_EQ( - createConstColumn(1, MAX_INT8), - executeFunction(func_name, - {createConstColumn(1, MAX_INT8), - createCastTypeConstColumn("Int64")})); - ASSERT_COLUMN_EQ( - createConstColumn(1, MAX_INT16), - executeFunction(func_name, - {createConstColumn(1, MAX_INT16), - createCastTypeConstColumn("Int64")})); - ASSERT_COLUMN_EQ( - createConstColumn(1, MAX_INT32), - executeFunction(func_name, - {createConstColumn(1, MAX_INT32), - createCastTypeConstColumn("Int64")})); - ASSERT_COLUMN_EQ( - createConstColumn(1, MAX_INT64), - executeFunction(func_name, - {createConstColumn(1, MAX_INT64), - createCastTypeConstColumn("Int64")})); - - /// normal cases - // uint8/16/32/64 -> uint64, no overflow - ASSERT_COLUMN_EQ( - createColumn>({0, 1, MAX_UINT8, {}}), - executeFunction(func_name, - {createColumn>({0, 1, MAX_UINT8, {}}), - createCastTypeConstColumn("Nullable(UInt64)")})); - ASSERT_COLUMN_EQ( - createColumn>({0, 1, MAX_UINT16, {}}), - executeFunction(func_name, - {createColumn>({0, 1, MAX_UINT16, {}}), - createCastTypeConstColumn("Nullable(UInt64)")})); - ASSERT_COLUMN_EQ( - createColumn>({0, 1, MAX_UINT32, {}}), - executeFunction(func_name, - {createColumn>({0, 1, MAX_UINT32, {}}), - createCastTypeConstColumn("Nullable(UInt64)")})); - ASSERT_COLUMN_EQ( - createColumn>({0, 1, MAX_UINT64, {}}), - executeFunction(func_name, - {createColumn>({0, 1, MAX_UINT64, {}}), - createCastTypeConstColumn("Nullable(UInt64)")})); - // int8/16/32/64 -> uint64, no overflow - ASSERT_COLUMN_EQ( - createColumn>({0, MAX_INT8, MAX_UINT64, MAX_UINT64 - MAX_INT8, {}}), - executeFunction(func_name, - {createColumn>({0, MAX_INT8, -1, MIN_INT8, {}}), - createCastTypeConstColumn("Nullable(UInt64)")})); - ASSERT_COLUMN_EQ( - createColumn>({0, MAX_INT16, MAX_UINT64, MAX_UINT64 - MAX_INT16, {}}), - executeFunction(func_name, - {createColumn>({0, MAX_INT16, -1, MIN_INT16, {}}), - createCastTypeConstColumn("Nullable(UInt64)")})); - ASSERT_COLUMN_EQ( - createColumn>({0, MAX_INT32, MAX_UINT64, MAX_UINT64 - MAX_INT32, {}}), - executeFunction(func_name, - {createColumn>({0, MAX_INT32, -1, MIN_INT32, {}}), - createCastTypeConstColumn("Nullable(UInt64)")})); - ASSERT_COLUMN_EQ( - createColumn>({0, MAX_INT64, MAX_UINT64, MAX_UINT64 - MAX_INT64, {}}), - executeFunction(func_name, - {createColumn>({0, MAX_INT64, -1, MIN_INT64, {}}), - createCastTypeConstColumn("Nullable(UInt64)")})); - // uint8/16/32 -> int64, no overflow - ASSERT_COLUMN_EQ( - createColumn>({0, MAX_INT8, MAX_UINT8, {}}), - executeFunction(func_name, - {createColumn>({0, MAX_INT8, MAX_UINT8, {}}), - createCastTypeConstColumn("Nullable(Int64)")})); - ASSERT_COLUMN_EQ( - createColumn>({0, MAX_INT16, MAX_UINT16, {}}), - executeFunction(func_name, - {createColumn>({0, MAX_INT16, MAX_UINT16, {}}), - createCastTypeConstColumn("Nullable(Int64)")})); - ASSERT_COLUMN_EQ( - createColumn>({0, MAX_INT32, MAX_UINT32, {}}), - executeFunction(func_name, - {createColumn>({0, MAX_INT32, MAX_UINT32, {}}), - createCastTypeConstColumn("Nullable(Int64)")})); - // uint64 -> int64, overflow may happen - ASSERT_COLUMN_EQ( - createColumn>({0, MAX_INT64, -1, {}}), - executeFunction(func_name, - {createColumn>({0, MAX_INT64, MAX_UINT64, {}}), - createCastTypeConstColumn("Nullable(Int64)")})); - // int8/16/32/64 -> int64, no overflow - ASSERT_COLUMN_EQ( - createColumn>({0, MAX_INT8, -1, MIN_INT8, {}}), - executeFunction(func_name, - {createColumn>({0, MAX_INT8, -1, MIN_INT8, {}}), - createCastTypeConstColumn("Nullable(Int64)")})); - ASSERT_COLUMN_EQ( - createColumn>({0, MAX_INT16, -1, MIN_INT16, {}}), - executeFunction(func_name, - {createColumn>({0, MAX_INT16, -1, MIN_INT16, {}}), - createCastTypeConstColumn("Nullable(Int64)")})); - ASSERT_COLUMN_EQ( - createColumn>({0, MAX_INT32, -1, MIN_INT32, {}}), - executeFunction(func_name, - {createColumn>({0, MAX_INT32, -1, MIN_INT32, {}}), - createCastTypeConstColumn("Nullable(Int64)")})); - ASSERT_COLUMN_EQ( - createColumn>({0, MAX_INT64, -1, MIN_INT64, {}}), - executeFunction(func_name, - {createColumn>({0, MAX_INT64, -1, MIN_INT64, {}}), - createCastTypeConstColumn("Nullable(Int64)")})); -} -CATCH - -TEST_F(TestTidbConversion, castIntAsReal) -try -{ - // uint64/int64 -> float64, may be not precise - ASSERT_COLUMN_EQ( - createColumn>( - {1234567890.0, - 123456789012345680.0, - 0.0, - {}}), - executeFunction(func_name, - {createColumn>( - {1234567890, // this is fine - 123456789012345678, // but this cannot be represented precisely in the IEEE 754 64-bit float format - 0, - {}}), - createCastTypeConstColumn("Nullable(Float64)")})); - ASSERT_COLUMN_EQ( - createColumn>( - {1234567890.0, - 123456789012345680.0, - 0.0, - {}}), - executeFunction(func_name, - {createColumn>( - {1234567890, // this is fine - 123456789012345678, // but this cannot be represented precisely in the IEEE 754 64-bit float format - 0, - {}}), - createCastTypeConstColumn("Nullable(Float64)")})); - // uint32/16/8 and int32/16/8 -> float64, precise - ASSERT_COLUMN_EQ( - createColumn>({MAX_UINT32, 0, {}}), - executeFunction(func_name, - {createColumn>({MAX_UINT32, 0, {}}), - createCastTypeConstColumn("Nullable(Float64)")})); - ASSERT_COLUMN_EQ( - createColumn>({MAX_UINT16, 0, {}}), - executeFunction(func_name, - {createColumn>({MAX_UINT16, 0, {}}), - createCastTypeConstColumn("Nullable(Float64)")})); - ASSERT_COLUMN_EQ( - createColumn>({MAX_UINT8, 0, {}}), - executeFunction(func_name, - {createColumn>({MAX_UINT8, 0, {}}), - createCastTypeConstColumn("Nullable(Float64)")})); - ASSERT_COLUMN_EQ( - createColumn>({MAX_INT32, MIN_INT32, 0, {}}), - executeFunction(func_name, - {createColumn>({MAX_INT32, MIN_INT32, 0, {}}), - createCastTypeConstColumn("Nullable(Float64)")})); - ASSERT_COLUMN_EQ( - createColumn>({MAX_INT16, MIN_INT16, 0, {}}), - executeFunction(func_name, - {createColumn>({MAX_INT16, MIN_INT16, 0, {}}), - createCastTypeConstColumn("Nullable(Float64)")})); - ASSERT_COLUMN_EQ( - createColumn>({MAX_INT8, MIN_INT8, 0, {}}), - executeFunction(func_name, - {createColumn>({MAX_INT8, MIN_INT8, 0, {}}), - createCastTypeConstColumn("Nullable(Float64)")})); -} -CATCH - -TEST_F(TestTidbConversion, castIntAsString) -try -{ - /// null only cases - ASSERT_COLUMN_EQ( - createColumn>({{}}), - executeFunction(func_name, - {createOnlyNullColumn(1), - createCastTypeConstColumn("Nullable(String)")})); - - /// const cases - // uint64/32/16/8 -> string - ASSERT_COLUMN_EQ( - createConstColumn(1, "18446744073709551615"), - executeFunction(func_name, - {createConstColumn(1, MAX_UINT64), - createCastTypeConstColumn("String")})); - ASSERT_COLUMN_EQ( - createConstColumn(1, "4294967295"), - executeFunction(func_name, - {createConstColumn(1, MAX_UINT32), - createCastTypeConstColumn("String")})); - ASSERT_COLUMN_EQ( - createConstColumn(1, "65535"), - executeFunction(func_name, - {createConstColumn(1, MAX_UINT16), - createCastTypeConstColumn("String")})); - ASSERT_COLUMN_EQ( - createConstColumn(1, "255"), - executeFunction(func_name, - {createConstColumn(1, MAX_UINT8), - createCastTypeConstColumn("String")})); - // int64/32/16/8 -> string - ASSERT_COLUMN_EQ( - createConstColumn(1, "9223372036854775807"), - executeFunction(func_name, - {createConstColumn(1, MAX_INT64), - createCastTypeConstColumn("String")})); - ASSERT_COLUMN_EQ( - createConstColumn(1, "2147483647"), - executeFunction(func_name, - {createConstColumn(1, MAX_INT32), - createCastTypeConstColumn("String")})); - ASSERT_COLUMN_EQ( - createConstColumn(1, "32767"), - executeFunction(func_name, - {createConstColumn(1, MAX_INT16), - createCastTypeConstColumn("String")})); - ASSERT_COLUMN_EQ( - createConstColumn(1, "127"), - executeFunction(func_name, - {createConstColumn(1, MAX_INT8), - createCastTypeConstColumn("String")})); - - /// normal cases - // uint64/32/16/8 -> string - ASSERT_COLUMN_EQ( - createColumn>({"18446744073709551615", "0", {}}), - executeFunction(func_name, - {createColumn>({MAX_UINT64, 0, {}}), - createCastTypeConstColumn("Nullable(String)")})); - ASSERT_COLUMN_EQ( - createColumn>({"4294967295", "0", {}}), - executeFunction(func_name, - {createColumn>({MAX_UINT32, 0, {}}), - createCastTypeConstColumn("Nullable(String)")})); - ASSERT_COLUMN_EQ( - createColumn>({"65535", "0", {}}), - executeFunction(func_name, - {createColumn>({MAX_UINT16, 0, {}}), - createCastTypeConstColumn("Nullable(String)")})); - ASSERT_COLUMN_EQ( - createColumn>({"255", "0", {}}), - executeFunction(func_name, - {createColumn>({MAX_UINT8, 0, {}}), - createCastTypeConstColumn("Nullable(String)")})); - // int64/32/16/8 -> string - ASSERT_COLUMN_EQ( - createColumn>({"9223372036854775807", "-9223372036854775808", "0", {}}), - executeFunction(func_name, - {createColumn>({MAX_INT64, MIN_INT64, 0, {}}), - createCastTypeConstColumn("Nullable(String)")})); - ASSERT_COLUMN_EQ( - createColumn>({"2147483647", "-2147483648", "0", {}}), - executeFunction(func_name, - {createColumn>({MAX_INT32, MIN_INT32, 0, {}}), - createCastTypeConstColumn("Nullable(String)")})); - ASSERT_COLUMN_EQ( - createColumn>({"32767", "-32768", "0", {}}), - executeFunction(func_name, - {createColumn>({MAX_INT16, MIN_INT16, 0, {}}), - createCastTypeConstColumn("Nullable(String)")})); - ASSERT_COLUMN_EQ( - createColumn>({"127", "-128", "0", {}}), - executeFunction(func_name, - {createColumn>({MAX_INT8, MIN_INT8, 0, {}}), - createCastTypeConstColumn("Nullable(String)")})); -} -CATCH - -TEST_F(TestTidbConversion, castIntAsDecimal) -try -{ - // int8 -> decimal32/64/128/256 - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(9, 0), - {DecimalField32(MAX_INT8, 0), DecimalField32(MIN_INT8, 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_INT8, MIN_INT8, {}}), - createCastTypeConstColumn("Nullable(Decimal(9,0))")})); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(18, 0), - {DecimalField64(MAX_INT8, 0), DecimalField64(MIN_INT8, 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_INT8, MIN_INT8, {}}), - createCastTypeConstColumn("Nullable(Decimal(18,0))")})); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(38, 0), - {DecimalField128(MAX_INT8, 0), DecimalField128(MIN_INT8, 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_INT8, MIN_INT8, {}}), - createCastTypeConstColumn("Nullable(Decimal(38,0))")})); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(65, 0), - {DecimalField256(static_cast(MAX_INT8), 0), DecimalField256(static_cast(MIN_INT8), 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_INT8, MIN_INT8, {}}), - createCastTypeConstColumn("Nullable(Decimal(65,0))")})); - // int16 -> decimal32/64/128/256 - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(9, 0), - {DecimalField32(MAX_INT16, 0), DecimalField32(MIN_INT16, 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_INT16, MIN_INT16, {}}), - createCastTypeConstColumn("Nullable(Decimal(9,0))")})); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(18, 0), - {DecimalField64(MAX_INT16, 0), DecimalField64(MIN_INT16, 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_INT16, MIN_INT16, {}}), - createCastTypeConstColumn("Nullable(Decimal(18,0))")})); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(38, 0), - {DecimalField128(MAX_INT16, 0), DecimalField128(MIN_INT16, 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_INT16, MIN_INT16, {}}), - createCastTypeConstColumn("Nullable(Decimal(38,0))")})); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(65, 0), - {DecimalField256(static_cast(MAX_INT16), 0), DecimalField256(static_cast(MIN_INT16), 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_INT16, MIN_INT16, {}}), - createCastTypeConstColumn("Nullable(Decimal(65,0))")})); - // int32 -> decimal32/64/128/256 - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(9, 0), - {DecimalField32(999999999, 0), DecimalField32(-999999999, 0), {}}), - executeFunction(func_name, - {createColumn>({999999999, -999999999, {}}), - createCastTypeConstColumn("Nullable(Decimal(9,0))")})); - ASSERT_THROW(executeFunction(func_name, - {createColumn>({1000000000, -1000000000, {}}), - createCastTypeConstColumn("Nullable(Decimal(9,0))")}), - TiFlashException); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(18, 0), - {DecimalField64(MAX_INT32, 0), DecimalField64(MIN_INT32, 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_INT32, MIN_INT32, {}}), - createCastTypeConstColumn("Nullable(Decimal(18,0))")})); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(38, 0), - {DecimalField128(MAX_INT32, 0), DecimalField128(MIN_INT32, 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_INT32, MIN_INT32, {}}), - createCastTypeConstColumn("Nullable(Decimal(38,0))")})); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(65, 0), - {DecimalField256(static_cast(MAX_INT32), 0), DecimalField256(static_cast(MIN_INT32), 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_INT32, MIN_INT32, {}}), - createCastTypeConstColumn("Nullable(Decimal(65,0))")})); - // int64 -> decimal32/64/128/256 - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(9, 0), - {DecimalField32(999999999, 0), DecimalField32(-999999999, 0), {}}), - executeFunction(func_name, - {createColumn>({999999999, -999999999, {}}), - createCastTypeConstColumn("Nullable(Decimal(9,0))")})); - ASSERT_THROW(executeFunction(func_name, - {createColumn>({1000000000, -1000000000, {}}), - createCastTypeConstColumn("Nullable(Decimal(9,0))")}), - TiFlashException); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(18, 0), - {DecimalField64(999999999999999999, 0), DecimalField64(-999999999999999999, 0), {}}), - executeFunction(func_name, - {createColumn>({999999999999999999, -999999999999999999, {}}), - createCastTypeConstColumn("Nullable(Decimal(18,0))")})); - ASSERT_THROW(executeFunction(func_name, - {createColumn>({1000000000000000000, -1000000000000000000, {}}), - createCastTypeConstColumn("Nullable(Decimal(18,0))")}), - TiFlashException); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(38, 0), - {DecimalField128(MAX_INT64, 0), DecimalField128(MIN_INT64, 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_INT64, MIN_INT64, {}}), - createCastTypeConstColumn("Nullable(Decimal(38,0))")})); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(65, 0), - {DecimalField256(static_cast(MAX_INT64), 0), DecimalField256(static_cast(MIN_INT64), 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_INT64, MIN_INT64, {}}), - createCastTypeConstColumn("Nullable(Decimal(65,0))")})); - // uint8 -> decimal32/64/128/256 - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(9, 0), - {DecimalField32(MAX_UINT8, 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_UINT8, {}}), - createCastTypeConstColumn("Nullable(Decimal(9,0))")})); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(18, 0), - {DecimalField64(MAX_UINT8, 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_UINT8, {}}), - createCastTypeConstColumn("Nullable(Decimal(18,0))")})); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(38, 0), - {DecimalField128(MAX_UINT8, 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_UINT8, {}}), - createCastTypeConstColumn("Nullable(Decimal(38,0))")})); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(65, 0), - {DecimalField256(static_cast(MAX_UINT8), 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_UINT8, {}}), - createCastTypeConstColumn("Nullable(Decimal(65,0))")})); - // uint16 -> decimal32/64/128/256 - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(9, 0), - {DecimalField32(MAX_UINT16, 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_UINT16, {}}), - createCastTypeConstColumn("Nullable(Decimal(9,0))")})); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(18, 0), - {DecimalField64(MAX_UINT16, 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_UINT16, {}}), - createCastTypeConstColumn("Nullable(Decimal(18,0))")})); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(38, 0), - {DecimalField128(MAX_UINT16, 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_UINT16, {}}), - createCastTypeConstColumn("Nullable(Decimal(38,0))")})); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(65, 0), - {DecimalField256(static_cast(MAX_UINT16), 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_UINT16, {}}), - createCastTypeConstColumn("Nullable(Decimal(65,0))")})); - // uint32 -> decimal32/64/128/256 - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(9, 0), - {DecimalField32(999999999, 0), {}}), - executeFunction(func_name, - {createColumn>({999999999, {}}), - createCastTypeConstColumn("Nullable(Decimal(9,0))")})); - ASSERT_THROW(executeFunction(func_name, - {createColumn>({1000000000, {}}), - createCastTypeConstColumn("Nullable(Decimal(9,0))")}), - TiFlashException); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(18, 0), - {DecimalField64(MAX_UINT32, 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_UINT32, {}}), - createCastTypeConstColumn("Nullable(Decimal(18,0))")})); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(38, 0), - {DecimalField128(MAX_UINT32, 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_UINT32, {}}), - createCastTypeConstColumn("Nullable(Decimal(38,0))")})); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(65, 0), - {DecimalField256(static_cast(MAX_UINT32), 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_UINT32, {}}), - createCastTypeConstColumn("Nullable(Decimal(65,0))")})); - // uint64 -> decimal32/64/128/256 - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(9, 0), - {DecimalField32(999999999, 0), {}}), - executeFunction(func_name, - {createColumn>({999999999, {}}), - createCastTypeConstColumn("Nullable(Decimal(9,0))")})); - ASSERT_THROW(executeFunction(func_name, - {createColumn>({1000000000, {}}), - createCastTypeConstColumn("Nullable(Decimal(9,0))")}), - TiFlashException); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(18, 0), - {DecimalField64(999999999999999999, 0), {}}), - executeFunction(func_name, - {createColumn>({999999999999999999, {}}), - createCastTypeConstColumn("Nullable(Decimal(18,0))")})); - ASSERT_THROW(executeFunction(func_name, - {createColumn>({1000000000000000000, {}}), - createCastTypeConstColumn("Nullable(Decimal(18,0))")}), - TiFlashException); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(38, 0), - {DecimalField128(MAX_INT64, 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_INT64, {}}), - createCastTypeConstColumn("Nullable(Decimal(38,0))")})); - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(65, 0), - {DecimalField256(static_cast(MAX_INT64), 0), {}}), - executeFunction(func_name, - {createColumn>({MAX_INT64, {}}), - createCastTypeConstColumn("Nullable(Decimal(65,0))")})); - - ASSERT_THROW(executeFunction(func_name, - {createColumn>({9999}), createCastTypeConstColumn("Nullable(Decimal(4, 1))")}), - TiFlashException); - - ASSERT_THROW(executeFunction(func_name, - {createColumn>({-9999}), createCastTypeConstColumn("Nullable(Decimal(4, 1))")}), - TiFlashException); - - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(4, 1), - {DecimalField32(static_cast(9990), 1)}), - executeFunction(func_name, - {createColumn>({999}), createCastTypeConstColumn("Nullable(Decimal(4, 1))")})); - - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(4, 1), - {DecimalField32(static_cast(-9990), 1)}), - executeFunction(func_name, - {createColumn>({-999}), createCastTypeConstColumn("Nullable(Decimal(4, 1))")})); - - DAGContext * dag_context = context.getDAGContext(); - UInt64 ori_flags = dag_context->getFlags(); - dag_context->addFlag(TiDBSQLFlags::OVERFLOW_AS_WARNING); - dag_context->clearWarnings(); - - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(4, 1), - {DecimalField32(static_cast(9999), 1)}), - executeFunction(func_name, - {createColumn>({9999}), createCastTypeConstColumn("Nullable(Decimal(4, 1))")})); - - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(4, 1), - {DecimalField32(static_cast(-9999), 1)}), - executeFunction(func_name, - {createColumn>({-9999}), createCastTypeConstColumn("Nullable(Decimal(4, 1))")})); - - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(2, 2), - {DecimalField32(static_cast(99), 2)}), - executeFunction(func_name, - {createColumn>({9999}), createCastTypeConstColumn("Nullable(Decimal(2, 2))")})); - - ASSERT_COLUMN_EQ( - createColumn>( - std::make_tuple(2, 2), - {DecimalField32(static_cast(-99), 2)}), - executeFunction(func_name, - {createColumn>({-9999}), createCastTypeConstColumn("Nullable(Decimal(2, 2))")})); - - dag_context->setFlags(ori_flags); -} -CATCH - -TEST_F(TestTidbConversion, castIntAsTime) -try -{ - ASSERT_COLUMN_EQ( - createDateTimeColumn({{}, {{2021, 10, 26, 16, 8, 59, 0}}}, 6), - executeFunction(func_name, - {createColumn>({{}, 20211026160859}), - createCastTypeConstColumn("Nullable(MyDateTime(6))")})); - ASSERT_COLUMN_EQ( - createDateTimeColumn({{}, {{2021, 10, 26, 16, 8, 59, 0}}}, 6), - executeFunction(func_name, - {createColumn>({{}, 20211026160859}), - createCastTypeConstColumn("Nullable(MyDateTime(6))")})); - ASSERT_THROW( - executeFunction(func_name, - {createColumn>({MAX_UINT8}), - createCastTypeConstColumn("Nullable(MyDateTime(6))")}), - TiFlashException); - ASSERT_THROW( - executeFunction(func_name, - {createColumn>({MAX_UINT16}), - createCastTypeConstColumn("Nullable(MyDateTime(6))")}), - TiFlashException); - ASSERT_THROW( - executeFunction(func_name, - {createColumn>({MAX_UINT32}), - createCastTypeConstColumn("Nullable(MyDateTime(6))")}), - TiFlashException); - ASSERT_COLUMN_EQ( - createDateTimeColumn({{}}, 6), - executeFunction(func_name, - {createColumn>({0}), - createCastTypeConstColumn("Nullable(MyDateTime(6))")})); - ASSERT_THROW( - executeFunction(func_name, - {createColumn>({{}, -20211026160859}), - createCastTypeConstColumn("Nullable(MyDateTime(6))")}), - TiFlashException); -} -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)); - - dag_context->setFlags(ori_flags); - dag_context->clearWarnings(); -} -CATCH - -TEST_F(TestTidbConversion, castRealAsTime) -try -{ - testOnlyNull(); - testOnlyNull(); - - // TODO add tests after non-expected results fixed - - // mysql: null, warning. - // tiflash: null, no warning. - // tidb: 0000-00-00 00:00:00 - // testThrowException(0, 6); - testThrowException(12.213, 6); - testThrowException(-12.213, 6); - testThrowException(MAX_FLOAT32, 6); - testThrowException(MIN_FLOAT32, 6); - // mysql: 2000-01-11 00:00:00 - // tiflash / tidb: null, warnings - // testNotOnlyNull(111, {2000, 1, 11, 0, 0, 0, 0}, 6); - testThrowException(-111, 6); - // mysql: 2000-01-11 00:00:00 - // tiflash / tidb: null, warnings - // testNotOnlyNull(111.1, {2000, 1, 11, 0, 0, 0, 0}, 6); - - // mysql: null, warning. - // tiflash: null, no warning. - // tidb: 0000-00-00 00:00:00 - // testThrowException(0, 6); - testThrowException(12.213, 6); - testThrowException(-12.213, 6); - testThrowException(MAX_FLOAT64, 6); - testThrowException(MIN_FLOAT64, 6); - // mysql: 2000-01-11 00:00:00 - // tiflash / tidb: null, warnings - // testNotOnlyNull(111, {2000, 1, 11, 0, 0, 0, 0}, 6); - testThrowException(-111, 6); - // mysql: 2000-01-11 00:00:00 - // tiflash / tidb: null, warnings - // testNotOnlyNull(111.1, {2000, 1, 11, 0, 0, 0, 0}, 6); - testNotOnlyNull(20210201, {2021, 2, 1, 0, 0, 0, 0}, 6); - // mysql: 2021-02-01 00:00:00 - // tiflash / tidb: 2021-02-01 01:00:00 - // testNotOnlyNull(20210201.1, {2021, 2, 1, 0, 0, 0, 0}, 6); -} -CATCH - -TEST_F(TestTidbConversion, castTimeAsReal) -try -{ - const auto data_type_ptr = std::make_shared(6); - const Float64 datetime_float = 20211026160859; - const Float64 datetime_frac_float = 20211026160859.125; - - // cast datetime to float - auto col_datetime1 = getDatetimeColumn(); - auto ctn_datetime1 = ColumnWithTypeAndName(std::move(col_datetime1), data_type_ptr, "datetime"); - ASSERT_COLUMN_EQ( - createColumn({datetime_float, datetime_frac_float}), - executeFunction(func_name, - {ctn_datetime1, - createCastTypeConstColumn("Float64")})); - - // cast datetime to nullable float - auto col_datetime2 = getDatetimeColumn(); - auto ctn_datetime2 = ColumnWithTypeAndName(std::move(col_datetime2), data_type_ptr, "datetime"); - ASSERT_COLUMN_EQ( - createColumn>({datetime_float, datetime_frac_float}), - executeFunction(func_name, - {ctn_datetime2, - createCastTypeConstColumn("Nullable(Float64)")})); - - // cast nullable datetime to nullable float - auto col_datetime3 = getDatetimeColumn(); - auto datetime3_null_map = ColumnUInt8::create(2, 0); - datetime3_null_map->getData()[1] = 1; - auto col_datetime3_nullable = ColumnNullable::create(std::move(col_datetime3), std::move(datetime3_null_map)); - auto ctn_datetime3_nullable = ColumnWithTypeAndName(std::move(col_datetime3_nullable), makeNullable(data_type_ptr), "datetime"); - ASSERT_COLUMN_EQ( - createColumn>({datetime_float, {}}), - executeFunction(func_name, - {ctn_datetime3_nullable, - createCastTypeConstColumn("Nullable(Float64)")})); - - // cast const datetime to float - auto col_datetime4_const = ColumnConst::create(getDatetimeColumn(true), 1); - auto ctn_datetime4_const = ColumnWithTypeAndName(std::move(col_datetime4_const), data_type_ptr, "datetime"); - ASSERT_COLUMN_EQ( - createConstColumn(1, datetime_float), - executeFunction(func_name, - {ctn_datetime4_const, - createCastTypeConstColumn("Float64")})); - - // cast nullable const datetime to float - auto col_datetime5 = getDatetimeColumn(true); - auto datetime5_null_map = ColumnUInt8::create(1, 0); - auto col_datetime5_nullable = ColumnNullable::create(std::move(col_datetime5), std::move(datetime5_null_map)); - auto col_datetime5_nullable_const = ColumnConst::create(std::move(col_datetime5_nullable), 1); - auto ctn_datetime5_nullable_const = ColumnWithTypeAndName(std::move(col_datetime5_nullable_const), makeNullable(data_type_ptr), "datetime"); - ASSERT_COLUMN_EQ( - createConstColumn>(1, datetime_float), - executeFunction(func_name, - {ctn_datetime5_nullable_const, - createCastTypeConstColumn("Nullable(Float64)")})); -} -CATCH - -TEST_F(TestTidbConversion, castDurationAsDuration) -try -{ - const auto from_type = std::make_shared(3); - const auto to_type_1 = std::make_shared(5); // from_fsp < to_fsp - const auto to_type_2 = std::make_shared(3); // from_fsp == to_fsp - const auto to_type_3 = std::make_shared(2); // from_fsp < to_fsp - - ColumnWithTypeAndName input( - createColumn({(20 * 3600 + 20 * 60 + 20) * 1000000000L + 555000000L, - -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 555000000L, - (20 * 3600 + 20 * 60 + 20) * 1000000000L + 554000000L, - -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 554000000L, - (20 * 3600 + 20 * 60 + 20) * 1000000000L + 999000000L, - -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 999000000L}) - .column, - from_type, - "input"); - - ColumnWithTypeAndName output1(input.column, to_type_1, "output1"); - ColumnWithTypeAndName output2(input.column, to_type_2, "output2"); - ColumnWithTypeAndName output3( - createColumn({(20 * 3600 + 20 * 60 + 20) * 1000000000L + 560000000L, - -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 560000000L, - (20 * 3600 + 20 * 60 + 20) * 1000000000L + 550000000L, - -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 550000000L, - (20 * 3600 + 20 * 60 + 21) * 1000000000L + 000000000L, - -(20 * 3600 + 20 * 60 + 21) * 1000000000L - 000000000L}) - .column, - to_type_3, - "output3"); - - ASSERT_COLUMN_EQ(output1, executeFunction(func_name, {input, createCastTypeConstColumn(to_type_1->getName())})); - ASSERT_COLUMN_EQ(output2, executeFunction(func_name, {input, createCastTypeConstColumn(to_type_2->getName())})); - ASSERT_COLUMN_EQ(output3, executeFunction(func_name, {input, createCastTypeConstColumn(to_type_3->getName())})); - - // Test Nullable - ColumnWithTypeAndName input_nullable( - createColumn>({(20 * 3600 + 20 * 60 + 20) * 1000000000L + 555000000L, - -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 555000000L, - {}, - (20 * 3600 + 20 * 60 + 20) * 1000000000L + 554000000L, - -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 554000000L, - {}, - (20 * 3600 + 20 * 60 + 20) * 1000000000L + 999000000L, - -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 999000000L}) - .column, - makeNullable(input.type), - "input_nullable"); - ColumnWithTypeAndName output1_nullable(input_nullable.column, makeNullable(to_type_1), "output1_nullable"); - ColumnWithTypeAndName output2_nullable(input_nullable.column, makeNullable(to_type_2), "output2_nullable"); - ColumnWithTypeAndName output3_nullable( - createColumn>({(20 * 3600 + 20 * 60 + 20) * 1000000000L + 560000000L, - -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 560000000L, - {}, - (20 * 3600 + 20 * 60 + 20) * 1000000000L + 550000000L, - -(20 * 3600 + 20 * 60 + 20) * 1000000000L - 550000000L, - {}, - (20 * 3600 + 20 * 60 + 21) * 1000000000L + 000000000L, - -(20 * 3600 + 20 * 60 + 21) * 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())})); - - // Test Const - ColumnWithTypeAndName input_const(createConstColumn(1, (20 * 3600 + 20 * 60 + 20) * 1000000000L + 999000000L).column, from_type, "input_const"); - ColumnWithTypeAndName output1_const(input_const.column, to_type_1, "output1_const"); - ColumnWithTypeAndName output2_const(input_const.column, to_type_2, "output2_const"); - ColumnWithTypeAndName output3_const(createConstColumn(1, (20 * 3600 + 20 * 60 + 21) * 1000000000L + 000000000L).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())})); -} -CATCH - -TEST_F(TestTidbConversion, StrToDateTypeTest) -try -{ - // Arg1 is ColumnVector, Arg2 is ColumnVector - auto arg1_column = createColumn>({{}, "1/12/2020", "00:59:60 ", "1/12/2020"}); - auto arg2_column = createColumn>({"%d/%c/%Y", {}, "%H:%i:%S ", "%d/%c/%Y"}); - ColumnWithTypeAndName result_column( - createColumn>({{}, {}, {}, MyDateTime{2020, 12, 1, 0, 0, 0, 0}.toPackedUInt()}).column, - makeNullable(std::make_shared(0)), - "result"); - ASSERT_COLUMN_EQ(result_column, executeFunction("strToDateDatetime", arg1_column, arg2_column)); - - // Arg1 is ColumnConst(ColumnNullable(non-null value)), Arg2 is ColumnVector - arg1_column = createConstColumn>(2, {"1/12/2020"}); - arg2_column = createColumn>({"%d/%c/%Y", "%d/%c/%Y"}); - result_column = ColumnWithTypeAndName( - createColumn>({MyDateTime{2020, 12, 1, 0, 0, 0, 0}.toPackedUInt(), MyDateTime{2020, 12, 1, 0, 0, 0, 0}.toPackedUInt()}).column, - makeNullable(std::make_shared(0)), - "result"); - ASSERT_COLUMN_EQ(result_column, executeFunction("strToDateDatetime", arg1_column, arg2_column)); - - // Arg1 is ColumnConst(ColumnNullable(null value)), Arg2 is ColumnVector - arg1_column = createConstColumn>(2, {}); - arg2_column = createColumn>({"%d/%c/%Y", "%d/%c/%Y"}); - result_column = ColumnWithTypeAndName( - createConstColumn>(2, {}).column, - makeNullable(std::make_shared(0)), - "result"); - ASSERT_COLUMN_EQ(result_column, executeFunction("strToDateDatetime", arg1_column, arg2_column)); - - // Arg1 is ColumnVector, Arg2 is ColumnConst(ColumnNullable(non-null value)) - arg1_column = createColumn>({"1/12/2020", "1/12/2020"}); - arg2_column = createConstColumn>(2, "%d/%c/%Y"); - result_column = ColumnWithTypeAndName( - createColumn>({MyDateTime{2020, 12, 1, 0, 0, 0, 0}.toPackedUInt(), MyDateTime{2020, 12, 1, 0, 0, 0, 0}.toPackedUInt()}).column, - makeNullable(std::make_shared(0)), - "result"); - ASSERT_COLUMN_EQ(result_column, executeFunction("strToDateDatetime", arg1_column, arg2_column)); - - // Arg1 is ColumnConst(ColumnNullable(non-null value)), Arg2 is ColumnConst(ColumnNullable(non-null value)) - arg1_column = createConstColumn>(2, "1/12/2020"); - arg2_column = createConstColumn>(2, "%d/%c/%Y"); - result_column = ColumnWithTypeAndName( - createConstColumn>(2, {MyDateTime{2020, 12, 1, 0, 0, 0, 0}.toPackedUInt()}).column, - makeNullable(std::make_shared(0)), - "result"); - ASSERT_COLUMN_EQ(result_column, executeFunction("strToDateDatetime", arg1_column, arg2_column)); - - // Arg1 is ColumnConst(ColumnNullable(null value)), Arg2 is ColumnConst(ColumnNullable(non-null value)) - arg1_column = createConstColumn>(2, {}); - arg2_column = createConstColumn>(2, "%d/%c/%Y"); - result_column = ColumnWithTypeAndName( - createConstColumn>(2, {}).column, - makeNullable(std::make_shared(0)), - "result"); - ASSERT_COLUMN_EQ(result_column, executeFunction("strToDateDatetime", arg1_column, arg2_column)); - - // Arg1 is ColumnVector, Arg2 is ColumnConst(ColumnNullable(null value)) - arg1_column = createColumn>({"1/12/2020", "1/12/2020"}); - arg2_column = createConstColumn>(2, {}); - result_column = ColumnWithTypeAndName( - createConstColumn>(2, {}).column, - makeNullable(std::make_shared(0)), - "result"); - ASSERT_COLUMN_EQ(result_column, executeFunction("strToDateDatetime", arg1_column, arg2_column)); - - // Arg1 is ColumnConst(ColumnNullable(non-null value)), Arg2 is ColumnConst(ColumnNullable(null value)) - arg1_column = createConstColumn>(2, {"1/12/2020"}); - arg2_column = createConstColumn>(2, {}); - result_column = ColumnWithTypeAndName( - createConstColumn>(2, {}).column, - makeNullable(std::make_shared(0)), - "result"); - ASSERT_COLUMN_EQ(result_column, executeFunction("strToDateDatetime", arg1_column, arg2_column)); - - // Arg1 is ColumnConst(ColumnNullable(null value)), Arg2 is ColumnConst(ColumnNullable(null value)) - arg1_column = createConstColumn>(2, {}); - arg2_column = createConstColumn>(2, {}); - result_column = ColumnWithTypeAndName( - createConstColumn>(2, {}).column, - makeNullable(std::make_shared(0)), - "result"); - ASSERT_COLUMN_EQ(result_column, executeFunction("strToDateDatetime", arg1_column, arg2_column)); -} -CATCH - -// for https://github.com/pingcap/tics/issues/4036 -TEST_F(TestTidbConversion, castStringAsDateTime) -try -{ - auto input = std::vector{"2012-12-12 12:12:12", "2012-12-12\t12:12:12", "2012-12-12\n12:12:12", "2012-12-12\v12:12:12", "2012-12-12\f12:12:12", "2012-12-12\r12:12:12"}; - auto to_column = createConstColumn(1, "MyDateTime(6)"); - - // vector - auto from_column = createColumn(input); - UInt64 except_packed = MyDateTime(2012, 12, 12, 12, 12, 12, 0).toPackedUInt(); - auto vector_result = executeFunction("tidb_cast", {from_column, to_column}); - for (size_t i = 0; i < input.size(); i++) - { - ASSERT_EQ(except_packed, vector_result.column.get()->get64(i)); - } - - // const - auto const_from_column = createConstColumn(1, "2012-12-12\n12:12:12"); - auto const_result = executeFunction("tidb_cast", {from_column, to_column}); - ASSERT_EQ(except_packed, const_result.column.get()->get64(0)); - - // nullable - auto nullable_from_column = createColumn>({"2012-12-12 12:12:12", "2012-12-12\t12:12:12", "2012-12-12\n12:12:12", "2012-12-12\v12:12:12", "2012-12-12\f12:12:12", "2012-12-12\r12:12:12"}); - auto nullable_result = executeFunction("tidb_cast", {from_column, to_column}); - for (size_t i = 0; i < input.size(); i++) - { - ASSERT_EQ(except_packed, nullable_result.column.get()->get64(i)); - } -} -CATCH - -} // namespace -} // namespace DB::tests