From a35671a7efe57ffde4541cbe52f683ee3db2c6c8 Mon Sep 17 00:00:00 2001 From: ruben Date: Sun, 3 May 2020 15:23:40 +0100 Subject: [PATCH] Improved text deserializ error handling Added more tests for text deserialization Corrected some previous tests Added code to handle some corner cases more gracefully Refactored --- TODO.txt | 9 +- .../protocol/impl/text_deserialization.ipp | 160 ++++++++++++------ .../detail/protocol/text_deserialization.cpp | 93 ++++++++-- 3 files changed, 197 insertions(+), 65 deletions(-) diff --git a/TODO.txt b/TODO.txt index 1cf514ef..243665cc 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,5 +1,13 @@ Sanitize Test zero dates + Refactor text deserialization constants + Add text deserialization error tests + Refactor binary deserialization constants + Add binary deserialization error tests + Float: nan, inf, signalling nan + See if we have any trouble with user input in binary serialization + Test with an unknown protocol type + Random input tests Better docs Wandbox Breaking up the tutorial in pieces @@ -38,7 +46,6 @@ Other possible features Status flags accessors in resultset (for OK_Packet) Technical debt Change channel read to make less syscalls - Random input tests Test dashboard Sanitizers Integ tests for different default auth plugins diff --git a/include/boost/mysql/detail/protocol/impl/text_deserialization.ipp b/include/boost/mysql/detail/protocol/impl/text_deserialization.ipp index f0e95395..7be78f51 100644 --- a/include/boost/mysql/detail/protocol/impl/text_deserialization.ipp +++ b/include/boost/mysql/detail/protocol/impl/text_deserialization.ipp @@ -17,25 +17,71 @@ namespace boost { namespace mysql { namespace detail { +// Text protocol deserialization constants +namespace textc { + +constexpr unsigned max_decimals = 6u; + +constexpr std::size_t year_sz = 4; +constexpr std::size_t month_sz = 2; +constexpr std::size_t day_sz = 2; +constexpr std::size_t hours_min_sz = 2; // in TIME, it may be longer +constexpr std::size_t mins_sz = 2; +constexpr std::size_t secs_sz = 2; + +constexpr std::size_t date_sz = year_sz + month_sz + day_sz + 2; // delimiters +constexpr std::size_t time_min_sz = hours_min_sz + mins_sz + secs_sz + 2; // delimiters +constexpr std::size_t time_max_sz = time_min_sz + max_decimals + 3; // sign, period, hour extra character +constexpr std::size_t datetime_min_sz = date_sz + time_min_sz + 1; // delimiter + +constexpr unsigned max_hour = 838; +constexpr unsigned max_min = 59; +constexpr unsigned max_sec = 59; +constexpr unsigned max_micro = 999999; + +} // textc + +inline unsigned sanitize_decimals(unsigned decimals) +{ + return std::min(decimals, textc::max_decimals); +} + +// Computes the meaning of the parsed microsecond number, taking into +// account decimals (85 with 2 decimals means 850000us) +inline unsigned compute_micros(unsigned parsed_micros, unsigned decimals) +{ + return parsed_micros * static_cast(std::pow(10, textc::max_decimals - decimals)); +} + inline errc deserialize_text_value_impl( std::string_view from, date& to ) { - constexpr std::size_t size = 4 + 2 + 2 + 2; // year, month, day, separators - if (from.size() != size) + // Size check + if (from.size() != textc::date_sz) return errc::protocol_value_error; - unsigned year, month, day; - char buffer [size + 1] {}; + + // Copy to a NULL-terminated buffer + char buffer [textc::date_sz + 1] {}; memcpy(buffer, from.data(), from.size()); + + // Parse individual components + unsigned year, month, day; int parsed = sscanf(buffer, "%4u-%2u-%2u", &year, &month, &day); if (parsed != 3) return errc::protocol_value_error; + + // Verify date validity ::date::year_month_day result (::date::year(year)/::date::month(month)/::date::day(day)); if (!result.ok()) return errc::protocol_value_error; + + // Range check if (result > max_date || result < min_date) return errc::protocol_value_error; + + // Done to = result; return errc::ok; } @@ -46,28 +92,46 @@ inline errc deserialize_text_value_impl( unsigned decimals ) { + using namespace textc; + + // Adjust decimals + decimals = sanitize_decimals(decimals); + // size check - constexpr std::size_t min_size = 2 + 2 + 2 + 2; // hours, mins, seconds, no micros - constexpr std::size_t max_size = min_size + 1 + 1 + 7; // hour extra character, sign and micros - decimals = std::min(decimals, 6u); - if (from.size() < min_size || from.size() > max_size) + std::size_t actual_min_size = time_min_sz + (decimals ? decimals + 1 : 0); + std::size_t actual_max_size = actual_min_size + 1 + 1; // hour extra character and sign + assert(actual_max_size <= time_max_sz); + if (from.size() < actual_min_size || from.size() > actual_max_size) return errc::protocol_value_error; + // Copy to NULL-terminated buffer + char buffer [time_max_sz + 1] {}; + memcpy(buffer, from.data(), from.size()); + + // Sign + bool is_negative = from[0] == '-'; + const char* first = is_negative ? buffer + 1 : buffer; + // Parse it - int hours; - unsigned minutes, seconds, micros = 0; - char buffer [max_size + 1] {}; - memcpy(buffer, from.data(), from.size()); - int parsed = decimals ? sscanf(buffer, "%4d:%2u:%2u.%6u", &hours, &minutes, &seconds, µs) : // sign adds 1 char - sscanf(buffer, "%4d:%2u:%2u", &hours, &minutes, &seconds); - if ((decimals && parsed != 4) || (!decimals && parsed != 3)) - return errc::protocol_value_error; - micros *= static_cast(std::pow(10, 6 - decimals)); - hours = std::abs(hours); - bool is_negative = from[0] == '-'; + unsigned hours, minutes, seconds; + unsigned micros = 0; + char extra_char; + if (decimals) + { + int parsed = sscanf(first, "%3u:%2u:%2u.%6u%c", &hours, &minutes, &seconds, µs, &extra_char); + if (parsed != 4) + return errc::protocol_value_error; + micros = compute_micros(micros, decimals); + } + else + { + int parsed = sscanf(first, "%3u:%2u:%2u%c", &hours, &minutes, &seconds, &extra_char); + if (parsed != 3) + return errc::protocol_value_error; + } - // Range check for minutes and seconds (hours may be greater than 24) - if (minutes >= 60 || seconds >= 60) + // Range check + if (hours > max_hour || minutes > max_min || seconds > max_sec || micros > max_micro) return errc::protocol_value_error; // Sum it @@ -78,10 +142,7 @@ inline errc deserialize_text_value_impl( res = -res; } - // Range check - if (res > max_time || res < min_time) - return errc::protocol_value_error; - + // Done to = res; return errc::ok; } @@ -92,30 +153,35 @@ inline errc deserialize_text_value_impl( unsigned decimals ) { + using namespace textc; + + // Sanitize decimals + decimals = sanitize_decimals(decimals); + // Length check - constexpr std::size_t min_size = 4 + 5*2 + 5; // year, month, day, hour, minute, seconds, separators - decimals = std::min(decimals, 6u); - std::size_t expected_size = min_size + (decimals ? decimals + 1 : 0); + std::size_t expected_size = datetime_min_sz + (decimals ? decimals + 1 : 0); if (from.size() != expected_size) return errc::protocol_value_error; - // Date part - date dt; - auto err = deserialize_text_value_impl(from.substr(0, 10), dt); + // Parse date + date d; + auto err = deserialize_text_value_impl(from.substr(0, date_sz), d); if (err != errc::ok) return err; // Time of day part time time_of_day; - err = deserialize_text_value_impl(from.substr(11), time_of_day, decimals); + err = deserialize_text_value_impl(from.substr(date_sz + 1), time_of_day, decimals); if (err != errc::ok) return err; + + // Range check constexpr auto max_time_of_day = std::chrono::hours(24) - std::chrono::microseconds(1); if (time_of_day < std::chrono::seconds(0) || time_of_day > max_time_of_day) return errc::protocol_value_error; // Sum it up - to = dt + time_of_day; + to = d + time_of_day; return errc::ok; } @@ -141,30 +207,16 @@ inline errc deserialize_text_value_impl(std::string_view from, std::string_view& template errc deserialize_text_value_to_variant(std::string_view from, value& to, Args&&... args) { - T value; - auto err = deserialize_text_value_impl(from, value, std::forward(args)...); - if (err == errc::ok) - { - to = value; - } - return err; + return deserialize_text_value_impl(from, to.emplace(), std::forward(args)...); } inline bool is_next_field_null( - deserialization_context& ctx + const deserialization_context& ctx ) { - int1 type_byte; - errc err = deserialize(type_byte, ctx); - if (err == errc::ok) - { - if (type_byte.value == 0xfb) - { - return true; // it was null, do not rewind - } - ctx.rewind(1); // it was not null, rewind (this byte is part of the actual message) - } - return false; + if (!ctx.enough_size(1)) + return false; + return *ctx.first() == 0xfb; } } // detail @@ -232,9 +284,9 @@ boost::mysql::error_code boost::mysql::detail::deserialize_text_row( output.resize(fields.size()); for (std::vector::size_type i = 0; i < fields.size(); ++i) { - bool is_null = is_next_field_null(ctx); - if (is_null) + if (is_next_field_null(ctx)) { + ctx.advance(1); output[i] = nullptr; } else diff --git a/test/unit/detail/protocol/text_deserialization.cpp b/test/unit/detail/protocol/text_deserialization.cpp index 2980a380..2f0948a9 100644 --- a/test/unit/detail/protocol/text_deserialization.cpp +++ b/test/unit/detail/protocol/text_deserialization.cpp @@ -216,10 +216,13 @@ INSTANTIATE_TEST_SUITE_P(DATETIME, DeserializeTextValueTest, Values( text_value_testcase("6_decimals_hms", "2010-02-15 02:05:30.000000", makedt(2010, 2, 15, 2, 5, 30), protocol_field_type::datetime, 0, 6), text_value_testcase("6_decimals_hmsu", "2010-02-15 02:05:30.002395", makedt(2010, 2, 15, 2, 5, 30, 2395), protocol_field_type::datetime, 0, 6), text_value_testcase("6_decimals_min", "1000-01-01 00:00:00.000000", makedt(1000, 1, 1), protocol_field_type::datetime, 0, 6), - text_value_testcase("6_decimals_max", "9999-12-31 23:59:59.999999", makedt(9999, 12, 31, 23, 59, 59, 999999), protocol_field_type::datetime, 0, 6) + text_value_testcase("6_decimals_max", "9999-12-31 23:59:59.999999", makedt(9999, 12, 31, 23, 59, 59, 999999), protocol_field_type::datetime, 0, 6), + + // not a real case, we cap decimals to 6 + text_value_testcase("7_decimals", "2010-02-15 02:05:30.002395", makedt(2010, 2, 15, 2, 5, 30, 2395), protocol_field_type::datetime, 0, 7) ), test_name_generator); -// Right now, timestamps are deserialized as DATETIMEs. TODO: update this when we consider time zones +// Right now, timestamps are deserialized as DATETIMEs INSTANTIATE_TEST_SUITE_P(TIMESTAMP, DeserializeTextValueTest, Values( text_value_testcase("0_decimals", "2010-02-15 02:05:30", makedt(2010, 2, 15, 2, 5, 30), protocol_field_type::timestamp), text_value_testcase("6_decimals", "2010-02-15 02:05:30.085670", makedt(2010, 2, 15, 2, 5, 30, 85670), protocol_field_type::timestamp, 0, 6), @@ -237,6 +240,7 @@ INSTANTIATE_TEST_SUITE_P(TIME, DeserializeTextValueTest, Values( text_value_testcase("0_decimals_negative_hms", "-14:51:23", -maket(14, 51, 23), protocol_field_type::time), text_value_testcase("0_decimals_min", "-838:59:59", -maket(838, 59, 59), protocol_field_type::time), text_value_testcase("0_decimals_zero", "00:00:00", maket(0, 0, 0), protocol_field_type::time), + text_value_testcase("0_decimals_negative_h0", "-00:51:23", -maket(0, 51, 23), protocol_field_type::time), text_value_testcase("1_decimals_positive_hms", "14:51:23.0", maket(14, 51, 23), protocol_field_type::time, 0, 1), text_value_testcase("1_decimals_positive_hmsu", "14:51:23.5", maket(14, 51, 23, 500000), protocol_field_type::time, 0, 1), @@ -245,6 +249,7 @@ INSTANTIATE_TEST_SUITE_P(TIME, DeserializeTextValueTest, Values( text_value_testcase("1_decimals_negative_hmsu", "-14:51:23.5", -maket(14, 51, 23, 500000), protocol_field_type::time, 0, 1), text_value_testcase("1_decimals_min", "-838:59:58.9", -maket(838, 59, 58, 900000), protocol_field_type::time, 0, 1), text_value_testcase("1_decimals_zero", "00:00:00.0", maket(0, 0, 0), protocol_field_type::time, 0, 1), + text_value_testcase("1_decimals_negative_h0", "-00:51:23.1", -maket(0, 51, 23, 100000), protocol_field_type::time, 0, 1), text_value_testcase("2_decimals_positive_hms", "14:51:23.00", maket(14, 51, 23), protocol_field_type::time, 0, 2), text_value_testcase("2_decimals_positive_hmsu", "14:51:23.52", maket(14, 51, 23, 520000), protocol_field_type::time, 0, 2), @@ -253,6 +258,7 @@ INSTANTIATE_TEST_SUITE_P(TIME, DeserializeTextValueTest, Values( text_value_testcase("2_decimals_negative_hmsu", "-14:51:23.50", -maket(14, 51, 23, 500000), protocol_field_type::time, 0, 2), text_value_testcase("2_decimals_min", "-838:59:58.99", -maket(838, 59, 58, 990000), protocol_field_type::time, 0, 2), text_value_testcase("2_decimals_zero", "00:00:00.00", maket(0, 0, 0), protocol_field_type::time, 0, 2), + text_value_testcase("2_decimals_negative_h0", "-00:51:23.12", -maket(0, 51, 23, 120000), protocol_field_type::time, 0, 2), text_value_testcase("3_decimals_positive_hms", "14:51:23.000", maket(14, 51, 23), protocol_field_type::time, 0, 3), text_value_testcase("3_decimals_positive_hmsu", "14:51:23.501", maket(14, 51, 23, 501000), protocol_field_type::time, 0, 3), @@ -261,6 +267,7 @@ INSTANTIATE_TEST_SUITE_P(TIME, DeserializeTextValueTest, Values( text_value_testcase("3_decimals_negative_hmsu", "-14:51:23.003", -maket(14, 51, 23, 3000), protocol_field_type::time, 0, 3), text_value_testcase("3_decimals_min", "-838:59:58.999", -maket(838, 59, 58, 999000), protocol_field_type::time, 0, 3), text_value_testcase("3_decimals_zero", "00:00:00.000", maket(0, 0, 0), protocol_field_type::time, 0, 3), + text_value_testcase("3_decimals_negative_h0", "-00:51:23.123", -maket(0, 51, 23, 123000), protocol_field_type::time, 0, 3), text_value_testcase("4_decimals_positive_hms", "14:51:23.0000", maket(14, 51, 23), protocol_field_type::time, 0, 4), text_value_testcase("4_decimals_positive_hmsu", "14:51:23.5017", maket(14, 51, 23, 501700), protocol_field_type::time, 0, 4), @@ -269,6 +276,7 @@ INSTANTIATE_TEST_SUITE_P(TIME, DeserializeTextValueTest, Values( text_value_testcase("4_decimals_negative_hmsu", "-14:51:23.0038", -maket(14, 51, 23, 3800), protocol_field_type::time, 0, 4), text_value_testcase("4_decimals_min", "-838:59:58.9999", -maket(838, 59, 58, 999900), protocol_field_type::time, 0, 4), text_value_testcase("4_decimals_zero", "00:00:00.0000", maket(0, 0, 0), protocol_field_type::time, 0, 4), + text_value_testcase("4_decimals_negative_h0", "-00:51:23.1234", -maket(0, 51, 23, 123400), protocol_field_type::time, 0, 4), text_value_testcase("5_decimals_positive_hms", "14:51:23.00000", maket(14, 51, 23), protocol_field_type::time, 0, 5), text_value_testcase("5_decimals_positive_hmsu", "14:51:23.50171", maket(14, 51, 23, 501710), protocol_field_type::time, 0, 5), @@ -277,6 +285,7 @@ INSTANTIATE_TEST_SUITE_P(TIME, DeserializeTextValueTest, Values( text_value_testcase("5_decimals_negative_hmsu", "-14:51:23.00009", -maket(14, 51, 23, 90), protocol_field_type::time, 0, 5), text_value_testcase("5_decimals_min", "-838:59:58.99999", -maket(838, 59, 58, 999990), protocol_field_type::time, 0, 5), text_value_testcase("5_decimals_zero", "00:00:00.00000", maket(0, 0, 0), protocol_field_type::time, 0, 5), + text_value_testcase("5_decimals_negative_h0", "-00:51:23.12345", -maket(0, 51, 23, 123450), protocol_field_type::time, 0, 5), text_value_testcase("6_decimals_positive_hms", "14:51:23.000000", maket(14, 51, 23), protocol_field_type::time, 0, 6), text_value_testcase("6_decimals_positive_hmsu", "14:51:23.501717", maket(14, 51, 23, 501717), protocol_field_type::time, 0, 6), @@ -285,6 +294,7 @@ INSTANTIATE_TEST_SUITE_P(TIME, DeserializeTextValueTest, Values( text_value_testcase("6_decimals_negative_hmsu", "-14:51:23.900000", -maket(14, 51, 23, 900000), protocol_field_type::time, 0, 6), text_value_testcase("6_decimals_min", "-838:59:58.999999", -maket(838, 59, 58, 999999), protocol_field_type::time, 0, 6), text_value_testcase("6_decimals_zero", "00:00:00.000000", maket(0, 0, 0), protocol_field_type::time, 0, 6), + text_value_testcase("6_decimals_negative_h0", "-00:51:23.123456", -maket(0, 51, 23, 123456), protocol_field_type::time, 0, 6), // This is not a real case - we cap anything above 6 decimals to 6 text_value_testcase("7_decimals", "14:51:23.501717", maket(14, 51, 23, 501717), protocol_field_type::time, 0, 7) @@ -303,17 +313,17 @@ struct err_text_value_testcase : named_param std::string name; std::string_view from; protocol_field_type type; - unsigned decimals; std::uint16_t flags; + unsigned decimals; errc expected_err; err_text_value_testcase(std::string&& name, std::string_view from, protocol_field_type type, - unsigned decimals=0, std::uint16_t flags=0, errc expected_err=errc::protocol_value_error) : + std::uint16_t flags=0, unsigned decimals=0, errc expected_err=errc::protocol_value_error) : name(std::move(name)), from(from), type(type), - decimals(decimals), flags(flags), + decimals(decimals), expected_err(expected_err) { } @@ -369,7 +379,7 @@ std::vector make_int32_err_cases( { // Unsigned integers behaviour for negative inputs are determined by what iostreams // do (accepting it and overflowing) - return make_int_err_cases(t, "-2147483649", "2147483648", "-2147483649", "4294967296"); + return make_int_err_cases(t, "-2147483649", "2147483648", "-4294967296", "4294967296"); } INSTANTIATE_TEST_SUITE_P(TINYINT, DeserializeTextValueErrorTest, ValuesIn( @@ -398,7 +408,7 @@ std::vector make_int64_err_cases( t, "-9223372036854775809", "9223372036854775808", - "-9223372036854775809", + "-18446744073709551616", "18446744073709551616" ); } @@ -479,6 +489,13 @@ std::vector make_datetime_err_cases( err_text_value_testcase("no_decimals_4", "2020-05-02 23:01:00 ", t, 0, 4), err_text_value_testcase("no_decimals_5", "2020-05-02 23:01:00 ", t, 0, 5), err_text_value_testcase("no_decimals_6", "2020-05-02 23:01:00 ", t, 0, 6), + err_text_value_testcase("trailing_0", "2020-05-02 23:01:0p", t, 0, 0), + err_text_value_testcase("trailing_1", "2020-05-02 23:01:00.p", t, 0, 1), + err_text_value_testcase("trailing_2", "2020-05-02 23:01:00.1p", t, 0, 2), + err_text_value_testcase("trailing_3", "2020-05-02 23:01:00.12p", t, 0, 3), + err_text_value_testcase("trailing_4", "2020-05-02 23:01:00.123p", t, 0, 4), + err_text_value_testcase("trailing_5", "2020-05-02 23:01:00.1234p", t, 0, 5), + err_text_value_testcase("trailing_6", "2020-05-02 23:01:00.12345p", t, 0, 6), err_text_value_testcase("bad_delimiter", "2020-05-02 23-01-00", t), err_text_value_testcase("missing_1gp_0", "2020-05-02 23:01: ", t), err_text_value_testcase("missing_2gp_0", "2020-05-02 23: ", t), @@ -490,11 +507,11 @@ std::vector make_datetime_err_cases( err_text_value_testcase("invalid_hour2", "2020-05-02 240:2:20", t), err_text_value_testcase("negative_hour", "2020-05-02 -2:20:20", t), err_text_value_testcase("invalid_min", "2020-05-02 22:60:20", t), - err_text_value_testcase("nagetive_min", "2020-05-02 22:-1:20", t), + err_text_value_testcase("negative_min", "2020-05-02 22:-1:20", t), err_text_value_testcase("invalid_sec", "2020-05-02 22:06:60", t), - err_text_value_testcase("nagetive_sec", "2020-05-02 22:06:-1", t), + err_text_value_testcase("negative_sec", "2020-05-02 22:06:-1", t), err_text_value_testcase("negative_micro", "2020-05-02 22:06:01.-1", t, 0, 2), - err_text_value_testcase("lt_min", "0900-01-01 00:00:00.00", t, 0, 2), + err_text_value_testcase("lt_min", "0090-01-01 00:00:00.00", t, 0, 2), err_text_value_testcase("gt_max", "10000-01-01 00:00:00.00", t, 0, 2) }; } @@ -509,6 +526,62 @@ INSTANTIATE_TEST_SUITE_P(TIMESTAMP, DeserializeTextValueErrorTest, ValuesIn( make_datetime_err_cases(protocol_field_type::timestamp) ), test_name_generator); +INSTANTIATE_TEST_SUITE_P(TIME, DeserializeTextValueErrorTest, Values( + err_text_value_testcase("empty", "", protocol_field_type::time), + err_text_value_testcase("not_numbers", "abjkjdb67", protocol_field_type::time), + err_text_value_testcase("too_short_0", "1:20:20", protocol_field_type::time), + err_text_value_testcase("too_short_1", "1:20:20.1", protocol_field_type::time, 0, 1), + err_text_value_testcase("too_short_2", "01:20:20.1", protocol_field_type::time, 0, 2), + err_text_value_testcase("too_short_3", "01:20:20.12", protocol_field_type::time, 0, 3), + err_text_value_testcase("too_short_4", "01:20:20.123", protocol_field_type::time, 0, 4), + err_text_value_testcase("too_short_5", "01:20:20.1234", protocol_field_type::time, 0, 5), + err_text_value_testcase("too_short_6", "01:20:20.12345", protocol_field_type::time, 0, 6), + err_text_value_testcase("too_long_0", "-9999:40:40", protocol_field_type::time, 0, 0), + err_text_value_testcase("too_long_1", "-9999:40:40.1", protocol_field_type::time, 0, 1), + err_text_value_testcase("too_long_2", "-9999:40:40.12", protocol_field_type::time, 0, 2), + err_text_value_testcase("too_long_3", "-9999:40:40.123", protocol_field_type::time, 0, 3), + err_text_value_testcase("too_long_4", "-9999:40:40.1234", protocol_field_type::time, 0, 4), + err_text_value_testcase("too_long_5", "-9999:40:40.12345", protocol_field_type::time, 0, 5), + err_text_value_testcase("too_long_6", "-9999:40:40.123456", protocol_field_type::time, 0, 6), + err_text_value_testcase("extra_long", "-99999999:40:40.12345678", protocol_field_type::time, 0, 6), + err_text_value_testcase("extra_long2", "99999999999:40:40", protocol_field_type::time, 0, 6), + err_text_value_testcase("decimals_0", "01:20:20.1", protocol_field_type::time, 0, 0), + err_text_value_testcase("no_decimals_1", "01:20:20 ", protocol_field_type::time, 0, 1), + err_text_value_testcase("no_decimals_2", "01:20:20 ", protocol_field_type::time, 0, 2), + err_text_value_testcase("no_decimals_3", "01:20:20 ", protocol_field_type::time, 0, 3), + err_text_value_testcase("no_decimals_4", "01:20:20 ", protocol_field_type::time, 0, 4), + err_text_value_testcase("no_decimals_5", "01:20:20 ", protocol_field_type::time, 0, 5), + err_text_value_testcase("no_decimals_6", "01:20:20 ", protocol_field_type::time, 0, 6), + err_text_value_testcase("bad_delimiter", "01-20-20", protocol_field_type::time), + err_text_value_testcase("missing_1gp_0", "23:01: ", protocol_field_type::time), + err_text_value_testcase("missing_2gp_0", "23: ", protocol_field_type::time), + err_text_value_testcase("missing_1gp_1", "23:01:.9 ", protocol_field_type::time, 0, 1), + err_text_value_testcase("missing_2gp_1", "23:.9 ", protocol_field_type::time, 0, 1), + err_text_value_testcase("invalid_min", "22:60:20", protocol_field_type::time), + err_text_value_testcase("negative_min", "22:-1:20", protocol_field_type::time), + err_text_value_testcase("invalid_sec", "22:06:60", protocol_field_type::time), + err_text_value_testcase("negative_sec", "22:06:-1", protocol_field_type::time), + err_text_value_testcase("invalid_micro_1", "22:06:01.99", protocol_field_type::time, 0, 1), + err_text_value_testcase("invalid_micro_2", "22:06:01.999", protocol_field_type::time, 0, 2), + err_text_value_testcase("invalid_micro_3", "22:06:01.9999", protocol_field_type::time, 0, 3), + err_text_value_testcase("invalid_micro_4", "22:06:01.99999", protocol_field_type::time, 0, 4), + err_text_value_testcase("invalid_micro_5", "22:06:01.999999", protocol_field_type::time, 0, 5), + err_text_value_testcase("invalid_micro_6", "22:06:01.9999999", protocol_field_type::time, 0, 6), + err_text_value_testcase("negative_micro", "22:06:01.-1", protocol_field_type::time, 0, 2), + err_text_value_testcase("lt_min", "-900:00:00.00", protocol_field_type::time, 0, 2), + err_text_value_testcase("gt_max", "900:00:00.00", protocol_field_type::time, 0, 2), + err_text_value_testcase("invalid_sign", "x670:00:00.00", protocol_field_type::time, 0, 2), + err_text_value_testcase("null_char", makesv("20:00:\00.00"), protocol_field_type::time, 0, 2), + err_text_value_testcase("trailing_0", "22:06:01k", protocol_field_type::time, 0, 0), + err_text_value_testcase("trailing_1", "22:06:01.1k", protocol_field_type::time, 0, 1), + err_text_value_testcase("trailing_2", "22:06:01.12k", protocol_field_type::time, 0, 2), + err_text_value_testcase("trailing_3", "22:06:01.123k", protocol_field_type::time, 0, 3), + err_text_value_testcase("trailing_4", "22:06:01.1234k", protocol_field_type::time, 0, 4), + err_text_value_testcase("trailing_5", "22:06:01.12345k", protocol_field_type::time, 0, 5), + err_text_value_testcase("trailing_6", "22:06:01.123456k", protocol_field_type::time, 0, 6), + err_text_value_testcase("double_sign", "--22:06:01.123456", protocol_field_type::time, 0, 6) +), test_name_generator); + // All cases, row struct DeserializeTextRowTest : public Test {