2
0
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:
ruben
2020-05-10 17:02:12 +01:00
parent 55a959d8a6
commit 6c4364d246
2 changed files with 82 additions and 31 deletions

View File

@@ -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) ||

View File

@@ -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(