mirror of
https://github.com/boostorg/mysql.git
synced 2026-02-15 13:12:21 +00:00
Fixed bug in binary deserialization of invalid datetimes
Added tests to prove it works
This commit is contained in:
@@ -168,24 +168,14 @@ inline errc deserialize_binary_value_to_variant_datetime(
|
||||
if (err != errc::ok)
|
||||
return err;
|
||||
|
||||
// Check for zero datetimes, represented as NULL in C++
|
||||
if (length.value < datetime_d_sz)
|
||||
// Deserialize date. If the DATETIME does not contain these values,
|
||||
// they are supposed to be zero (invalid date)
|
||||
::date::year_month_day ymd (::date::year(0), ::date::month(0), ::date::day(0));
|
||||
if (length.value >= datetime_d_sz)
|
||||
{
|
||||
output = nullptr;
|
||||
return errc::ok;
|
||||
}
|
||||
|
||||
// Deserialize date
|
||||
::date::year_month_day ymd;
|
||||
err = deserialize_binary_ymd(ctx, ymd);
|
||||
if (err != errc::ok)
|
||||
return err;
|
||||
|
||||
// Check for invalid dates, represented in C++ as NULL
|
||||
if (!ymd.ok())
|
||||
{
|
||||
output = nullptr;
|
||||
return errc::ok;
|
||||
err = deserialize_binary_ymd(ctx, ymd);
|
||||
if (err != errc::ok)
|
||||
return err;
|
||||
}
|
||||
|
||||
// If the DATETIME contains no value for these fields, they are zero
|
||||
@@ -210,6 +200,15 @@ inline errc deserialize_binary_value_to_variant_datetime(
|
||||
return err;
|
||||
}
|
||||
|
||||
// Check for invalid dates, represented in C++ as NULL.
|
||||
// Note: we do the check here to ensure we consume all the bytes
|
||||
// associated to this datetime
|
||||
if (!ymd.ok())
|
||||
{
|
||||
output = nullptr;
|
||||
return errc::ok;
|
||||
}
|
||||
|
||||
// Range check
|
||||
date d (ymd);
|
||||
if (is_out_of_range(d) ||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "boost/mysql/detail/protocol/binary_deserialization.hpp"
|
||||
#include "boost/mysql/detail/auxiliar/stringize.hpp"
|
||||
#include "test_common.hpp"
|
||||
|
||||
// Tests for deserialize_binary_value()
|
||||
@@ -144,25 +145,19 @@ INSTANTIATE_TEST_SUITE_P(DATE, DeserializeBinaryValueTest, ::testing::Values(
|
||||
makedate(9999, 12, 31), protocol_field_type::date),
|
||||
binary_value_testcase("zero", {0x00},
|
||||
nullptr, protocol_field_type::date),
|
||||
binary_value_testcase("zero_invalid_month", {0x04, 0x00, 0x00, 13, 0x01},
|
||||
binary_value_testcase("invalid_month", {0x04, 0x00, 0x00, 13, 0x01},
|
||||
nullptr, protocol_field_type::date),
|
||||
binary_value_testcase("zero_invalid_month_max", {0x04, 0x00, 0x00, 0xff, 0x01},
|
||||
binary_value_testcase("invalid_month_max", {0x04, 0x00, 0x00, 0xff, 0x01},
|
||||
nullptr, protocol_field_type::date),
|
||||
binary_value_testcase("zero_invalid_day", {0x04, 0x00, 0x00, 0x01, 32},
|
||||
binary_value_testcase("invalid_month_min", {0x04, 0x00, 0x00, 0x00, 0x01},
|
||||
nullptr, protocol_field_type::date),
|
||||
binary_value_testcase("zero_invalid_day_max", {0x04, 0x00, 0x00, 0x01, 0xff},
|
||||
binary_value_testcase("invalid_day", {0x04, 0x00, 0x00, 0x01, 32},
|
||||
nullptr, protocol_field_type::date),
|
||||
binary_value_testcase("zero_invalid_date", {0x04, 0x00, 0x00, 31, 4},
|
||||
binary_value_testcase("invalid_day_max", {0x04, 0x00, 0x00, 0x01, 0xff},
|
||||
nullptr, protocol_field_type::date),
|
||||
binary_value_testcase("nonzero_invalid_month", {0x04, 0xe4, 0x07, 13, 0x01},
|
||||
binary_value_testcase("invalid_day_min", {0x04, 0x00, 0x00, 0x01, 0x00},
|
||||
nullptr, protocol_field_type::date),
|
||||
binary_value_testcase("nonzero_invalid_month_max", {0x04, 0xe4, 0x07, 0xff, 0x01},
|
||||
nullptr, protocol_field_type::date),
|
||||
binary_value_testcase("nonzero_invalid_day", {0x04, 0xe4, 0x07, 0x01, 32},
|
||||
nullptr, protocol_field_type::date),
|
||||
binary_value_testcase("nonzero_invalid_day_max", {0x04, 0xe4, 0x07, 0x01, 0xff},
|
||||
nullptr, protocol_field_type::date),
|
||||
binary_value_testcase("nonzero_invalid_date", {0x04, 0xe4, 0x07, 31, 4},
|
||||
binary_value_testcase("invalid_date", {0x04, 0x00, 0x00, 31, 4},
|
||||
nullptr, protocol_field_type::date)
|
||||
), test_name_generator);
|
||||
|
||||
@@ -170,7 +165,7 @@ std::vector<binary_value_testcase> make_datetime_cases(
|
||||
protocol_field_type type
|
||||
)
|
||||
{
|
||||
return {
|
||||
std::vector<binary_value_testcase> res {
|
||||
{ "only_date", {0x04, 0xda, 0x07, 0x01, 0x01},
|
||||
makedt(2010, 1, 1), type },
|
||||
{ "date_h", {0x07, 0xda, 0x07, 0x01, 0x01, 0x14, 0x00, 0x00},
|
||||
@@ -203,7 +198,64 @@ std::vector<binary_value_testcase> make_datetime_cases(
|
||||
makedt(2010, 1, 1, 23, 0, 59, 967510), type },
|
||||
{ "date_hmsu", {0x0b, 0xda, 0x07, 0x01, 0x01, 0x17, 0x01, 0x3b, 0x56, 0xc3, 0x0e, 0x00},
|
||||
makedt(2010, 1, 1, 23, 1, 59, 967510), type },
|
||||
{ "zeros", {0x00}, nullptr, type },
|
||||
{ "zeros_d", {0x04, 0x00, 0x00, 0x00, 0x00}, nullptr, type },
|
||||
{ "zeros_hms", {0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, nullptr, type },
|
||||
{ "zeros_hmsu", {0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, nullptr, type },
|
||||
};
|
||||
|
||||
// Create all the casuistic for invalid datetimes. We consider three factors:
|
||||
// which member is invalid, why is it invalid, and which length does the datetime have.
|
||||
constexpr struct
|
||||
{
|
||||
const char* name;
|
||||
std::size_t pos;
|
||||
std::uint8_t invalid_value;
|
||||
} what_is_invalid [] = {
|
||||
{ "month", 3, 13 },
|
||||
{ "day", 4, 32 }
|
||||
};
|
||||
|
||||
constexpr struct
|
||||
{
|
||||
const char* name;
|
||||
int value;
|
||||
} why_is_invalid [] = {
|
||||
{ "zero", 0 },
|
||||
{ "protocolmax", 0xff },
|
||||
{ "gtmax", -1 } // means "look inside what_is_invalid"
|
||||
};
|
||||
|
||||
constexpr struct
|
||||
{
|
||||
const char* name;
|
||||
std::uint8_t length;
|
||||
} lengths [] = {
|
||||
{ "d", 4 },
|
||||
{ "hms", 7 },
|
||||
{ "hmsu", 11 }
|
||||
};
|
||||
|
||||
bytestring regular {0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
for (const auto& what: what_is_invalid)
|
||||
{
|
||||
for (const auto& why: why_is_invalid)
|
||||
{
|
||||
for (const auto& len: lengths)
|
||||
{
|
||||
std::string name = stringize("invalid_", what.name, "_", why.name, "_", len.name);
|
||||
std::uint8_t invalid_value = why.value == -1 ? what.invalid_value : why.value;
|
||||
bytestring buffer (regular);
|
||||
buffer[what.pos] = invalid_value;
|
||||
buffer[0] = std::uint8_t(len.length);
|
||||
buffer.resize(len.length + 1);
|
||||
res.emplace_back(std::move(name), std::move(buffer), nullptr, type);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(DATETIME, DeserializeBinaryValueTest, ValuesIn(
|
||||
|
||||
Reference in New Issue
Block a user