mirror of
https://github.com/boostorg/mysql.git
synced 2026-02-15 13:12:21 +00:00
Refactored binary protocol magic numbers
Fixed wider ranges for DATE and DATETIME Removed old binary (de)serialization functions
This commit is contained in:
23
TODO.txt
23
TODO.txt
@@ -1,23 +1,13 @@
|
||||
Sanitize
|
||||
Refactor binary deserialization tests
|
||||
serialize_binary_value(), get_binary_value_size() instead of global serialize for floats and times
|
||||
Move serialization tests to just binary serialization tests
|
||||
Verify that deserialization test fixture works
|
||||
Add float, double, times error tests in serialization.cpp
|
||||
Refactor binary deserialization constants
|
||||
Change text row to use parameterized tests
|
||||
Refactor deserialize_binary_value to have similar implementation to the text one
|
||||
Better resilience of deserialization
|
||||
Make float inf and nan output NULL?
|
||||
Lift range check on dates, datetimes, times
|
||||
Make invalid dates and datetimes output NULL
|
||||
Integ tests for zero and invalid dates
|
||||
Complete DATETIME and TIME error tests
|
||||
Complete DATETIME and TIME deserialize_binary_value error tests
|
||||
Complete serialize_binary_value tests
|
||||
Change deserialize_text_value to allow zero and invalid dates/datetimes
|
||||
Integ tests for zero and invalid dates
|
||||
Remove deserialization test error fixture
|
||||
Change text row to use parameterized tests
|
||||
See if we have any trouble with user input in binary serialization: make assertions
|
||||
Test with an unknown protocol type
|
||||
Random input tests
|
||||
Better docs
|
||||
Wandbox
|
||||
Breaking up the tutorial in pieces
|
||||
Explaining the different overloads and async methods available
|
||||
Try to include Readme instead of copying it
|
||||
@@ -54,6 +44,7 @@ Other possible features
|
||||
Lower C++ std requirements
|
||||
Status flags accessors in resultset (for OK_Packet)
|
||||
Technical debt
|
||||
Random input tests
|
||||
Change channel read to make less syscalls
|
||||
Test dashboard
|
||||
Sanitizers
|
||||
|
||||
@@ -74,8 +74,8 @@ constexpr std::uint8_t auth_more_data_header = 0x01;
|
||||
constexpr std::string_view fast_auth_complete_challenge = "\3";
|
||||
|
||||
// Column flags
|
||||
namespace column_flags
|
||||
{
|
||||
namespace column_flags {
|
||||
|
||||
constexpr std::uint16_t not_null = 1; // Field can't be NULL.
|
||||
constexpr std::uint16_t pri_key = 2; // Field is part of a primary key.
|
||||
constexpr std::uint16_t unique_key = 4; // Field is part of a unique key.
|
||||
@@ -92,16 +92,73 @@ constexpr std::uint16_t no_default_value = 4096; // Field doesn't have defau
|
||||
constexpr std::uint16_t on_update_now = 8192; // Field is set to NOW on UPDATE.
|
||||
constexpr std::uint16_t part_key = 16384; // Intern; Part of some key.
|
||||
constexpr std::uint16_t num = 32768; // Field is num (for clients)
|
||||
}
|
||||
|
||||
} // column_flags
|
||||
|
||||
// Prepared statements
|
||||
namespace cursor_types
|
||||
{
|
||||
namespace cursor_types {
|
||||
|
||||
constexpr std::uint8_t no_cursor = 0;
|
||||
constexpr std::uint8_t read_only = 1;
|
||||
constexpr std::uint8_t for_update = 2;
|
||||
constexpr std::uint8_t scrollable = 4;
|
||||
}
|
||||
|
||||
} // cursor_types
|
||||
|
||||
// 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
|
||||
|
||||
// Binary protocol (de)serialization constants
|
||||
namespace binc {
|
||||
|
||||
constexpr std::size_t length_sz = 1; // length byte, for date, datetime and time
|
||||
constexpr std::size_t year_sz = 2;
|
||||
constexpr std::size_t month_sz = 1;
|
||||
constexpr std::size_t date_day_sz = 1;
|
||||
constexpr std::size_t time_days_sz = 4;
|
||||
constexpr std::size_t hours_sz = 1;
|
||||
constexpr std::size_t mins_sz = 1;
|
||||
constexpr std::size_t secs_sz = 1;
|
||||
constexpr std::size_t micros_sz = 4;
|
||||
constexpr std::size_t time_sign_sz = 1;
|
||||
|
||||
constexpr std::size_t date_sz = year_sz + month_sz + date_day_sz; // does not include length
|
||||
|
||||
constexpr std::size_t datetime_d_sz = date_sz;
|
||||
constexpr std::size_t datetime_dhms_sz = datetime_d_sz + hours_sz + mins_sz + secs_sz;
|
||||
constexpr std::size_t datetime_dhmsu_sz = datetime_dhms_sz + micros_sz;
|
||||
|
||||
constexpr std::size_t time_dhms_sz = time_sign_sz + time_days_sz + hours_sz + mins_sz + secs_sz;
|
||||
constexpr std::size_t time_dhmsu_sz = time_dhms_sz + micros_sz;
|
||||
|
||||
constexpr std::size_t max_days = 34; // equivalent to the 839 hours, in the broken format
|
||||
constexpr unsigned max_hour = 23;
|
||||
constexpr unsigned max_min = 59;
|
||||
constexpr unsigned max_sec = 59;
|
||||
constexpr unsigned max_micro = 999999;
|
||||
|
||||
} // binc
|
||||
|
||||
|
||||
} // detail
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <variant>
|
||||
#include "boost/mysql/detail/protocol/serialization.hpp"
|
||||
#include "boost/mysql/detail/protocol/null_bitmap_traits.hpp"
|
||||
#include "boost/mysql/detail/protocol/constants.hpp"
|
||||
#include "boost/mysql/detail/auxiliar/tmp.hpp"
|
||||
|
||||
namespace boost {
|
||||
@@ -62,9 +63,8 @@ errc deserialize_binary_value_to_variant_float(
|
||||
if (!ctx.enough_size(sizeof(T)))
|
||||
return errc::incomplete_message;
|
||||
|
||||
T v;
|
||||
|
||||
// Endianness conversion. Boost.Endian support for floats start at 1.71
|
||||
T v;
|
||||
#if BOOST_ENDIAN_BIG_BYTE
|
||||
char buf [sizeof(T)];
|
||||
std::memcpy(buf, ctx.first(), sizeof(T));
|
||||
@@ -73,59 +73,78 @@ errc deserialize_binary_value_to_variant_float(
|
||||
#else
|
||||
std::memcpy(&v, ctx.first(), sizeof(T));
|
||||
#endif
|
||||
ctx.advance(sizeof(T));
|
||||
|
||||
// Nans and infs not allowed in SQL
|
||||
if (std::isnan(v) || std::isinf(v))
|
||||
{
|
||||
//output = nullptr;
|
||||
return errc::protocol_value_error;
|
||||
}
|
||||
else
|
||||
{
|
||||
output = v;
|
||||
}
|
||||
|
||||
// Done
|
||||
ctx.advance(sizeof(T));
|
||||
output = v;
|
||||
return errc::ok;
|
||||
}
|
||||
|
||||
// Time types
|
||||
inline errc deserialize_binary_ymd(
|
||||
deserialization_context& ctx,
|
||||
::date::year_month_day& output
|
||||
)
|
||||
{
|
||||
int2 year;
|
||||
int1 month;
|
||||
int1 day;
|
||||
|
||||
auto err = deserialize_fields(ctx, year, month, day);
|
||||
if (err != errc::ok)
|
||||
return err;
|
||||
|
||||
output = ::date::year_month_day (
|
||||
::date::year(year.value),
|
||||
::date::month(month.value),
|
||||
::date::day(day.value)
|
||||
);
|
||||
|
||||
return errc::ok;
|
||||
}
|
||||
|
||||
inline errc deserialize_binary_value_to_variant_date(
|
||||
deserialization_context& ctx,
|
||||
value& output
|
||||
) noexcept
|
||||
{
|
||||
int1 length;
|
||||
int2 year;
|
||||
int1 month;
|
||||
int1 day;
|
||||
|
||||
// Deserialize length
|
||||
int1 length;
|
||||
auto err = deserialize(length, ctx);
|
||||
if (err != errc::ok)
|
||||
return err;
|
||||
|
||||
// A zero date. This is represented in C++ as a NULL
|
||||
if (length.value < 4)
|
||||
// Check for zero dates, represented in C++ as a NULL
|
||||
if (length.value < binc::date_sz)
|
||||
{
|
||||
output = nullptr;
|
||||
return errc::ok;
|
||||
}
|
||||
|
||||
// Deserialize rest of fields
|
||||
err = deserialize_fields(ctx, year, month, day);
|
||||
::date::year_month_day ymd;
|
||||
err = deserialize_binary_ymd(ctx, ymd);
|
||||
if (err != errc::ok)
|
||||
return err;
|
||||
|
||||
::date::year_month_day ymd (::date::year(year.value), ::date::month(month.value), ::date::day(day.value));
|
||||
if (!ymd.ok()) // invalid date. We represent this as a NULL
|
||||
// Check for invalid dates, represented as NULL in C++
|
||||
if (!ymd.ok())
|
||||
{
|
||||
output = nullptr;
|
||||
return errc::ok;
|
||||
}
|
||||
|
||||
// Range check
|
||||
date d (ymd);
|
||||
if (d < min_date || d > max_date)
|
||||
return errc::protocol_value_error;
|
||||
|
||||
output = d;
|
||||
// Convert to sys_days (date)
|
||||
output = static_cast<date>(ymd);
|
||||
return errc::ok;
|
||||
}
|
||||
|
||||
@@ -134,58 +153,72 @@ inline errc deserialize_binary_value_to_variant_datetime(
|
||||
value& output
|
||||
) noexcept
|
||||
{
|
||||
int1 length;
|
||||
int2 year;
|
||||
int1 month;
|
||||
int1 day;
|
||||
int1 hours;
|
||||
int1 minutes;
|
||||
int1 seconds;
|
||||
int4 micros;
|
||||
using namespace binc;
|
||||
|
||||
// Deserialize length
|
||||
int1 length;
|
||||
auto err = deserialize(length, ctx);
|
||||
if (err != errc::ok)
|
||||
return err;
|
||||
|
||||
// A zero datetime, represented as NULL in C++
|
||||
if (length.value < 4)
|
||||
// Check for zero datetimes, represented as NULL in C++
|
||||
if (length.value < datetime_d_sz)
|
||||
{
|
||||
output = nullptr;
|
||||
return errc::ok;
|
||||
}
|
||||
|
||||
// Date part
|
||||
err = deserialize_fields(ctx, year, month, day);
|
||||
// Deserialize date
|
||||
::date::year_month_day ymd;
|
||||
err = deserialize_binary_ymd(ctx, ymd);
|
||||
if (err != errc::ok)
|
||||
return err;
|
||||
::date::year_month_day ymd (::date::year(year.value), ::date::month(month.value), ::date::day(day.value));
|
||||
|
||||
// Check for invalid dates, represented in C++ as NULL
|
||||
if (!ymd.ok())
|
||||
{
|
||||
output = nullptr;
|
||||
return errc::ok;
|
||||
}
|
||||
|
||||
// Rest of fields
|
||||
if (length.value >= 7)
|
||||
// If the DATETIME contains no value for these fields, they are zero
|
||||
int1 hours (0);
|
||||
int1 minutes (0);
|
||||
int1 seconds (0);
|
||||
int4 micros (0);
|
||||
|
||||
// Hours, minutes, seconds
|
||||
if (length.value >= datetime_dhms_sz)
|
||||
{
|
||||
err = deserialize_fields(ctx, hours, minutes, seconds);
|
||||
if (err != errc::ok)
|
||||
return err;
|
||||
}
|
||||
if (length.value >= 11)
|
||||
|
||||
// Microseconds
|
||||
if (length.value >= datetime_dhmsu_sz)
|
||||
{
|
||||
err = deserialize(micros, ctx);
|
||||
if (err != errc::ok)
|
||||
return err;
|
||||
}
|
||||
|
||||
// TODO: range check
|
||||
// Range check
|
||||
if (hours.value > max_hour ||
|
||||
minutes.value > max_min ||
|
||||
seconds.value > max_sec ||
|
||||
micros.value > max_micro)
|
||||
{
|
||||
return errc::protocol_value_error;
|
||||
}
|
||||
|
||||
// Compose the final datetime. Doing time of day and date separately to avoid overflow
|
||||
auto time_of_day_part = std::chrono::hours(hours.value) + std::chrono::minutes(minutes.value) +
|
||||
std::chrono::seconds(seconds.value) + std::chrono::microseconds(micros.value);
|
||||
output = date(ymd) + time_of_day_part;
|
||||
auto time_of_day =
|
||||
std::chrono::hours(hours.value) +
|
||||
std::chrono::minutes(minutes.value) +
|
||||
std::chrono::seconds(seconds.value) +
|
||||
std::chrono::microseconds(micros.value);
|
||||
output = static_cast<date>(ymd) + time_of_day;
|
||||
return errc::ok;
|
||||
}
|
||||
|
||||
@@ -194,12 +227,15 @@ inline errc deserialize_binary_value_to_variant_time(
|
||||
value& output
|
||||
) noexcept
|
||||
{
|
||||
// Length
|
||||
using namespace binc;
|
||||
|
||||
// Deserialize length
|
||||
int1 length;
|
||||
auto err = deserialize(length, ctx);
|
||||
if (err != errc::ok)
|
||||
return err;
|
||||
|
||||
// If the TIME contains no value for these fields, they are zero
|
||||
int1 is_negative (0);
|
||||
int4 days (0);
|
||||
int1 hours (0);
|
||||
@@ -207,7 +243,8 @@ inline errc deserialize_binary_value_to_variant_time(
|
||||
int1 seconds(0);
|
||||
int4 microseconds(0);
|
||||
|
||||
if (length.value >= 8)
|
||||
// Sign, days, hours, minutes, seconds
|
||||
if (length.value >= time_dhms_sz)
|
||||
{
|
||||
err = deserialize_fields(
|
||||
ctx,
|
||||
@@ -220,20 +257,33 @@ inline errc deserialize_binary_value_to_variant_time(
|
||||
if (err != errc::ok)
|
||||
return err;
|
||||
}
|
||||
if (length.value >= 12)
|
||||
|
||||
// Microseconds
|
||||
if (length.value >= time_dhmsu_sz)
|
||||
{
|
||||
err = deserialize(microseconds, ctx);
|
||||
if (err != errc::ok)
|
||||
return err;
|
||||
}
|
||||
|
||||
output = (is_negative.value ? -1 : 1) * (
|
||||
// Range check
|
||||
if (days.value > max_days ||
|
||||
hours.value > max_hour ||
|
||||
minutes.value > max_min ||
|
||||
seconds.value > max_sec ||
|
||||
microseconds.value > max_micro)
|
||||
{
|
||||
return errc::protocol_value_error;
|
||||
}
|
||||
|
||||
// Compose the final time
|
||||
output = time((is_negative.value ? -1 : 1) * (
|
||||
::date::days(days.value) +
|
||||
std::chrono::hours(hours.value) +
|
||||
std::chrono::minutes(minutes.value) +
|
||||
std::chrono::seconds(seconds.value) +
|
||||
std::chrono::microseconds(microseconds.value)
|
||||
);
|
||||
));
|
||||
return errc::ok;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,54 +8,13 @@
|
||||
#ifndef INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_IMPL_BINARY_SERIALIZATION_IPP_
|
||||
#define INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_IMPL_BINARY_SERIALIZATION_IPP_
|
||||
|
||||
#include "boost/mysql/detail/protocol/constants.hpp"
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
// Does not add the length prefix byte
|
||||
inline void serialize_binary_ymd(
|
||||
const ::date::year_month_day& ymd,
|
||||
serialization_context& ctx
|
||||
) noexcept
|
||||
{
|
||||
serialize_fields(
|
||||
ctx,
|
||||
int2(static_cast<std::uint16_t>(static_cast<int>(ymd.year()))),
|
||||
int1(static_cast<std::uint8_t>(static_cast<unsigned>(ymd.month()))),
|
||||
int1(static_cast<std::uint8_t>(static_cast<unsigned>(ymd.day())))
|
||||
);
|
||||
}
|
||||
|
||||
struct broken_datetime
|
||||
{
|
||||
::date::year_month_day ymd;
|
||||
::date::time_of_day<std::chrono::microseconds> tod;
|
||||
|
||||
broken_datetime(const datetime& input) noexcept :
|
||||
ymd(::date::floor<::date::days>(input)),
|
||||
tod(input - ::date::floor<::date::days>(input))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct broken_time
|
||||
{
|
||||
::date::days days;
|
||||
std::chrono::hours hours;
|
||||
std::chrono::minutes minutes;
|
||||
std::chrono::seconds seconds;
|
||||
std::chrono::microseconds microseconds;
|
||||
|
||||
broken_time(const time& input) noexcept :
|
||||
days(std::chrono::duration_cast<::date::days>(input)),
|
||||
hours(std::chrono::duration_cast<std::chrono::hours>(input % ::date::days(1))),
|
||||
minutes(std::chrono::duration_cast<std::chrono::minutes>(input % std::chrono::hours(1))),
|
||||
seconds(std::chrono::duration_cast<std::chrono::seconds>(input % std::chrono::minutes(1))),
|
||||
microseconds(input % std::chrono::seconds(1))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Floating point types
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_floating_point_v<T>>
|
||||
serialize_binary_value_impl(
|
||||
@@ -74,13 +33,32 @@ serialize_binary_value_impl(
|
||||
#endif
|
||||
}
|
||||
|
||||
// Time types
|
||||
|
||||
// Does not add the length prefix byte
|
||||
inline void serialize_binary_ymd(
|
||||
const ::date::year_month_day& ymd,
|
||||
serialization_context& ctx
|
||||
) noexcept
|
||||
{
|
||||
serialize_fields(
|
||||
ctx,
|
||||
int2(static_cast<std::uint16_t>(static_cast<int>(ymd.year()))),
|
||||
int1(static_cast<std::uint8_t>(static_cast<unsigned>(ymd.month()))),
|
||||
int1(static_cast<std::uint8_t>(static_cast<unsigned>(ymd.day())))
|
||||
);
|
||||
}
|
||||
|
||||
inline void serialize_binary_value_impl(
|
||||
const date& input,
|
||||
serialization_context& ctx
|
||||
)
|
||||
{
|
||||
serialize(int1(4), ctx); // length byte
|
||||
serialize_binary_ymd(::date::year_month_day (input), ctx);
|
||||
::date::year_month_day ymd (input);
|
||||
assert(ymd.ok());
|
||||
|
||||
serialize(int1(binc::date_sz), ctx);
|
||||
serialize_binary_ymd(ymd, ctx);
|
||||
}
|
||||
|
||||
inline void serialize_binary_value_impl(
|
||||
@@ -88,16 +66,21 @@ inline void serialize_binary_value_impl(
|
||||
serialization_context& ctx
|
||||
)
|
||||
{
|
||||
broken_datetime brokendt (input);
|
||||
auto micros = static_cast<std::uint32_t>(brokendt.tod.subseconds().count());
|
||||
serialize(int1(11), ctx);
|
||||
serialize_binary_ymd(brokendt.ymd, ctx);
|
||||
// Break datetime
|
||||
auto days = ::date::floor<::date::days>(input);
|
||||
::date::year_month_day ymd (days);
|
||||
::date::time_of_day<std::chrono::microseconds> tod (input - days);
|
||||
assert(ymd.ok());
|
||||
|
||||
// Serialize
|
||||
serialize(int1(binc::datetime_dhmsu_sz), ctx);
|
||||
serialize_binary_ymd(ymd, ctx);
|
||||
serialize_fields(
|
||||
ctx,
|
||||
int1(static_cast<std::uint8_t>(brokendt.tod.hours().count())),
|
||||
int1(static_cast<std::uint8_t>(brokendt.tod.minutes().count())),
|
||||
int1(static_cast<std::uint8_t>(brokendt.tod.seconds().count())),
|
||||
int4(micros)
|
||||
int1(static_cast<std::uint8_t>(tod.hours().count())),
|
||||
int1(static_cast<std::uint8_t>(tod.minutes().count())),
|
||||
int1(static_cast<std::uint8_t>(tod.seconds().count())),
|
||||
int4(static_cast<std::uint32_t>(tod.subseconds().count()))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -106,18 +89,24 @@ inline void serialize_binary_value_impl(
|
||||
serialization_context& ctx
|
||||
)
|
||||
{
|
||||
broken_time broken (input);
|
||||
// Break time
|
||||
auto days = std::chrono::duration_cast<::date::days>(input);
|
||||
auto hours = std::chrono::duration_cast<std::chrono::hours>(input % ::date::days(1));
|
||||
auto minutes = std::chrono::duration_cast<std::chrono::minutes>(input % std::chrono::hours(1));
|
||||
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(input % std::chrono::minutes(1));
|
||||
auto microseconds = input % std::chrono::seconds(1);
|
||||
int1 is_negative (input.count() < 0 ? 1 : 0);
|
||||
auto micros = static_cast<std::uint32_t>(std::abs(broken.microseconds.count()));
|
||||
|
||||
// Serialize
|
||||
serialize_fields(
|
||||
ctx,
|
||||
int1(12), // length
|
||||
int1(binc::time_dhmsu_sz),
|
||||
is_negative,
|
||||
int4(static_cast<std::uint32_t>(std::abs(broken.days.count()))),
|
||||
int1(static_cast<std::uint8_t>(std::abs(broken.hours.count()))),
|
||||
int1(static_cast<std::uint8_t>(std::abs(broken.minutes.count()))),
|
||||
int1(static_cast<std::uint8_t>(std::abs(broken.seconds.count()))),
|
||||
int4(micros)
|
||||
int4(static_cast<std::uint32_t>(std::abs(days.count()))),
|
||||
int1(static_cast<std::uint8_t>(std::abs(hours.count()))),
|
||||
int1(static_cast<std::uint8_t>(std::abs(minutes.count()))),
|
||||
int1(static_cast<std::uint8_t>(std::abs(seconds.count()))),
|
||||
int4(static_cast<std::uint32_t>(std::abs(microseconds.count())))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -133,9 +122,9 @@ struct size_visitor
|
||||
std::size_t operator()(T) noexcept { return sizeof(T); }
|
||||
|
||||
std::size_t operator()(std::string_view v) noexcept { return get_size(string_lenenc(v), ctx); }
|
||||
std::size_t operator()(const date&) noexcept { return 5; }
|
||||
std::size_t operator()(const datetime&) noexcept { return 12; }
|
||||
std::size_t operator()(const time&) noexcept { return 13; }
|
||||
std::size_t operator()(const date&) noexcept { return binc::date_sz + binc::length_sz; }
|
||||
std::size_t operator()(const datetime&) noexcept { return binc::datetime_dhmsu_sz + binc::length_sz; }
|
||||
std::size_t operator()(const time&) noexcept { return binc::time_dhmsu_sz + binc::length_sz; }
|
||||
std::size_t operator()(std::nullptr_t) noexcept { return 0; }
|
||||
};
|
||||
|
||||
|
||||
@@ -11,36 +11,12 @@
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <boost/lexical_cast/try_lexical_convert.hpp>
|
||||
#include <date/date.h>
|
||||
#include "boost/mysql/detail/protocol/constants.hpp"
|
||||
|
||||
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);
|
||||
@@ -78,11 +54,10 @@ inline errc deserialize_text_value_impl(
|
||||
return errc::protocol_value_error;
|
||||
|
||||
// Range check
|
||||
if (result > max_date || result < min_date)
|
||||
to = result;
|
||||
if (to < min_date || to > max_date)
|
||||
return errc::protocol_value_error;
|
||||
|
||||
// Done
|
||||
to = result;
|
||||
return errc::ok;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
// Max/min
|
||||
constexpr date min_date = ::date::day(1)/::date::January/::date::year(100); // some implementations support less than the official
|
||||
constexpr date min_date = ::date::day(1)/::date::January/::date::year(0); // slightly more flexible than official min
|
||||
constexpr date max_date = ::date::day(31)/::date::December/::date::year(9999);
|
||||
constexpr datetime min_datetime = min_date;
|
||||
constexpr datetime max_datetime = max_date + std::chrono::hours(24) - std::chrono::microseconds(1);
|
||||
|
||||
@@ -133,9 +133,13 @@ INSTANTIATE_TEST_SUITE_P(DATE, DeserializeBinaryValueErrorTest, Values(
|
||||
err_binary_value_testcase("empty", {}, protocol_field_type::date, errc::incomplete_message),
|
||||
err_binary_value_testcase("incomplete_year", {0x04, 0xff},
|
||||
protocol_field_type::date, errc::incomplete_message),
|
||||
err_binary_value_testcase("year_gt_max", {0x04, 0x10, 0x27, 0x03, 0x1c}, // year 10000
|
||||
err_binary_value_testcase("no_month_day", {0x04, 0x09, 0x27},
|
||||
protocol_field_type::date, errc::incomplete_message),
|
||||
err_binary_value_testcase("no_day", {0x04, 0x09, 0x27, 0x01},
|
||||
protocol_field_type::date, errc::incomplete_message),
|
||||
err_binary_value_testcase("gt_max", {0x04, 0x10, 0x27, 0x0c, 0x1f}, // year 10000
|
||||
protocol_field_type::date),
|
||||
err_binary_value_testcase("year_lt_min", {0x04, 0x63, 0x00, 0x03, 0x1c}, // year 99
|
||||
err_binary_value_testcase("protocol_max", {0x04, 0xff, 0xff, 0x0c, 0x1f},
|
||||
protocol_field_type::date)
|
||||
));
|
||||
|
||||
|
||||
@@ -138,8 +138,8 @@ INSTANTIATE_TEST_SUITE_P(DOUBLE, DeserializeBinaryValueTest, Values(
|
||||
INSTANTIATE_TEST_SUITE_P(DATE, DeserializeBinaryValueTest, ::testing::Values(
|
||||
binary_value_testcase("regular", {0x04, 0xda, 0x07, 0x03, 0x1c},
|
||||
makedate(2010, 3, 28), protocol_field_type::date),
|
||||
binary_value_testcase("min", {0x04, 0xe8, 0x03, 0x01, 0x01},
|
||||
makedate(1000, 1, 1), protocol_field_type::date),
|
||||
binary_value_testcase("min", {0x04, 0x00, 0x00, 0x01, 0x01},
|
||||
makedate(0, 1, 1), protocol_field_type::date),
|
||||
binary_value_testcase("max", {0x04, 0x0f, 0x27, 0x0c, 0x1f},
|
||||
makedate(9999, 12, 31), protocol_field_type::date)
|
||||
), test_name_generator);
|
||||
|
||||
@@ -166,13 +166,14 @@ INSTANTIATE_TEST_SUITE_P(DATE, DeserializeTextValueErrorTest, Values(
|
||||
err_text_value_testcase("too_few_groups", "2020-00005", protocol_field_type::date),
|
||||
err_text_value_testcase("incomplete_year", "999-05-005", protocol_field_type::date),
|
||||
err_text_value_testcase("null_value", makesv("2020-05-\02"), protocol_field_type::date),
|
||||
err_text_value_testcase("lt_min", "0099-05-02", protocol_field_type::date),
|
||||
err_text_value_testcase("lt_min", "-001-05-02", protocol_field_type::date),
|
||||
err_text_value_testcase("gt_max", "10000-05-2", protocol_field_type::date),
|
||||
err_text_value_testcase("long_month", "2010-005-2", protocol_field_type::date),
|
||||
err_text_value_testcase("long_day", "2010-5-002", protocol_field_type::date),
|
||||
err_text_value_testcase("negative_year", "-999-05-02", protocol_field_type::date),
|
||||
err_text_value_testcase("negative_month", "2010--5-02", protocol_field_type::date),
|
||||
err_text_value_testcase("negative_day", "2010-05-- 2", protocol_field_type::date)
|
||||
err_text_value_testcase("negative_day", "2010-05--2", protocol_field_type::date),
|
||||
err_text_value_testcase("hex", "ffff-ff-ff", protocol_field_type::date)
|
||||
), test_name_generator);
|
||||
|
||||
std::vector<err_text_value_testcase> make_datetime_err_cases(
|
||||
@@ -223,7 +224,7 @@ std::vector<err_text_value_testcase> make_datetime_err_cases(
|
||||
err_text_value_testcase("invalid_sec", "2020-05-02 22:06:60", 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", "0090-01-01 00:00:00.00", t, 0, 2),
|
||||
err_text_value_testcase("lt_min", "-001-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)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -188,9 +188,8 @@ INSTANTIATE_TEST_SUITE_P(DOUBLE, DeserializeTextValueTest, ValuesIn(
|
||||
INSTANTIATE_TEST_SUITE_P(DATE, DeserializeTextValueTest, Values(
|
||||
text_value_testcase("regular_date", "2019-02-28", makedate(2019, 2, 28), protocol_field_type::date),
|
||||
text_value_testcase("leap_year", "1788-02-29", makedate(1788, 2, 29), protocol_field_type::date),
|
||||
text_value_testcase("min", "1000-01-01", makedate(1000, 1, 1), protocol_field_type::date),
|
||||
text_value_testcase("max", "9999-12-31", makedate(9999, 12, 31), protocol_field_type::date),
|
||||
text_value_testcase("unofficial_min", "0100-01-01", makedate(100, 1, 1), protocol_field_type::date)
|
||||
text_value_testcase("min", "0000-01-01", makedate(0, 1, 1), protocol_field_type::date),
|
||||
text_value_testcase("max", "9999-12-31", makedate(9999, 12, 31), protocol_field_type::date)
|
||||
), test_name_generator);
|
||||
|
||||
std::vector<text_value_testcase> make_datetime_cases(
|
||||
|
||||
Reference in New Issue
Block a user