2
0
mirror of https://github.com/boostorg/mysql.git synced 2026-02-15 13:12:21 +00:00
Files
mysql/test/unit/detail/protocol/binary_deserialization_value.cpp
ruben f5a17cdf7e Now text protocol supports invalid dates
Changed text protocol to support invalid dates and datetimes
Added tests
Now months greater than 12, days greater than 31, years greater than
9999 are always rejected
2020-05-11 13:21:29 +01:00

284 lines
14 KiB
C++

//
// Copyright (c) 2019-2020 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#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()
using namespace boost::mysql::detail;
using namespace boost::mysql::test;
using namespace testing;
using boost::mysql::value;
using boost::mysql::error_code;
using boost::mysql::errc;
namespace
{
using boost::mysql::operator<<;
struct binary_value_testcase : named_param
{
std::string name;
std::vector<std::uint8_t> from;
value expected;
protocol_field_type type;
std::uint16_t flags;
template <typename T>
binary_value_testcase(
std::string name,
std::vector<std::uint8_t> from,
T&& expected_value,
protocol_field_type type,
std::uint16_t flags=0
):
name(std::move(name)),
from(std::move(from)),
expected(std::forward<T>(expected_value)),
type(type),
flags(flags)
{
}
};
struct DeserializeBinaryValueTest : public TestWithParam<binary_value_testcase> {};
TEST_P(DeserializeBinaryValueTest, CorrectFormat_SetsOutputValueReturnsTrue)
{
using boost::mysql::operator<<;
column_definition_packet coldef;
coldef.type = GetParam().type;
coldef.flags.value = GetParam().flags;
boost::mysql::field_metadata meta (coldef);
value actual_value;
const auto& buffer = GetParam().from;
deserialization_context ctx (buffer.data(), buffer.data() + buffer.size(), capabilities());
auto err = deserialize_binary_value(ctx, meta, actual_value);
EXPECT_EQ(err, errc::ok);
EXPECT_EQ(actual_value, GetParam().expected);
EXPECT_EQ(ctx.first(), buffer.data() + buffer.size()); // all bytes consumed
}
INSTANTIATE_TEST_SUITE_P(StringTypes, DeserializeBinaryValueTest, Values(
binary_value_testcase("varchar", {0x04, 0x74, 0x65, 0x73, 0x74}, "test", protocol_field_type::var_string),
binary_value_testcase("char", {0x04, 0x74, 0x65, 0x73, 0x74}, "test", protocol_field_type::string),
binary_value_testcase("varbinary", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
protocol_field_type::var_string, column_flags::binary),
binary_value_testcase("binary", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
protocol_field_type::string, column_flags::binary),
binary_value_testcase("text_blob", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
protocol_field_type::blob, column_flags::blob),
binary_value_testcase("enum", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
protocol_field_type::string, column_flags::enum_),
binary_value_testcase("set", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
protocol_field_type::string, column_flags::set),
binary_value_testcase("bit", {0x02, 0x02, 0x01}, "\2\1", protocol_field_type::bit),
binary_value_testcase("decimal", {0x02, 0x31, 0x30}, "10", protocol_field_type::newdecimal),
binary_value_testcase("geomtry", {0x04, 0x74, 0x65, 0x73, 0x74}, "test", protocol_field_type::geometry)
), test_name_generator);
// Note: these employ regular integer deserialization functions, which have
// already been tested in serialization.cpp
INSTANTIATE_TEST_SUITE_P(IntTypes, DeserializeBinaryValueTest, Values(
binary_value_testcase("tinyint_unsigned", {0x14}, std::uint32_t(20),
protocol_field_type::tiny, column_flags::unsigned_),
binary_value_testcase("tinyint_signed", {0xec}, std::int32_t(-20), protocol_field_type::tiny),
binary_value_testcase("smallint_unsigned", {0x14, 0x00}, std::uint32_t(20),
protocol_field_type::short_, column_flags::unsigned_),
binary_value_testcase("smallint_signed", {0xec, 0xff}, std::int32_t(-20), protocol_field_type::short_),
binary_value_testcase("mediumint_unsigned", {0x14, 0x00, 0x00, 0x00}, std::uint32_t(20),
protocol_field_type::int24, column_flags::unsigned_),
binary_value_testcase("mediumint_signed", {0xec, 0xff, 0xff, 0xff}, std::int32_t(-20), protocol_field_type::int24),
binary_value_testcase("int_unsigned", {0x14, 0x00, 0x00, 0x00}, std::uint32_t(20),
protocol_field_type::long_, column_flags::unsigned_),
binary_value_testcase("int_signed", {0xec, 0xff, 0xff, 0xff}, std::int32_t(-20), protocol_field_type::long_),
binary_value_testcase("bigint_unsigned", {0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, std::uint64_t(20),
protocol_field_type::longlong, column_flags::unsigned_),
binary_value_testcase("bigint_signed", {0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, std::int64_t(-20),
protocol_field_type::longlong),
binary_value_testcase("year", {0xe3, 0x07}, std::uint32_t(2019),
protocol_field_type::year, column_flags::unsigned_)
), test_name_generator);
INSTANTIATE_TEST_SUITE_P(FLOAT, DeserializeBinaryValueTest, Values(
binary_value_testcase("fractional_negative", {0x66, 0x66, 0x86, 0xc0},
-4.2f, protocol_field_type::float_),
binary_value_testcase("fractional_positive", {0x66, 0x66, 0x86, 0x40},
4.2f, protocol_field_type::float_),
binary_value_testcase("positive_exp_positive_fractional", {0x01, 0x2d, 0x88, 0x61},
3.14e20f, protocol_field_type::float_),
binary_value_testcase("zero", {0x00, 0x00, 0x00, 0x00},
0.0f, protocol_field_type::float_)
), test_name_generator);
INSTANTIATE_TEST_SUITE_P(DOUBLE, DeserializeBinaryValueTest, Values(
binary_value_testcase("fractional_negative", {0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x10, 0xc0},
-4.2, protocol_field_type::double_),
binary_value_testcase("fractional_positive", {0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x10, 0x40},
4.2, protocol_field_type::double_),
binary_value_testcase("positive_exp_positive_fractional", {0xce, 0x46, 0x3c, 0x76, 0x9c, 0x68, 0x90, 0x69},
3.14e200, protocol_field_type::double_),
binary_value_testcase("zero", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
0.0, protocol_field_type::double_)
), test_name_generator);
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, 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),
binary_value_testcase("zero", {0x00},
nullptr, protocol_field_type::date),
binary_value_testcase("zero_full_length", {0x04, 0x00, 0x00, 0x00, 0x00},
nullptr, protocol_field_type::date),
binary_value_testcase("zero_month", {0x04, 0x00, 0x00, 0x00, 0x01},
nullptr, protocol_field_type::date),
binary_value_testcase("zero_day", {0x04, 0x00, 0x00, 0x01, 0x00},
nullptr, protocol_field_type::date),
binary_value_testcase("zero_month_day_nonzero_year", {0x04, 0x01, 0x00, 0x00, 0x00},
nullptr, protocol_field_type::date),
binary_value_testcase("invalid_date", {0x04, 0x00, 0x00, 11, 31},
nullptr, protocol_field_type::date)
), test_name_generator);
std::vector<binary_value_testcase> make_datetime_cases(
protocol_field_type type
)
{
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},
makedt(2010, 1, 1, 20, 0, 0, 0), type },
{ "date_m", {0x07, 0xda, 0x07, 0x01, 0x01, 0x00, 0x01, 0x00},
makedt(2010, 1, 1, 0, 1, 0, 0), type },
{ "date_hm", {0x07, 0xda, 0x07, 0x01, 0x01, 0x03, 0x02, 0x00},
makedt(2010, 1, 1, 3, 2, 0, 0), type },
{ "date_s", {0x07, 0xda, 0x07, 0x01, 0x01, 0x00, 0x00, 0x01},
makedt(2010, 1, 1, 0, 0, 1, 0), type },
{ "date_ms", {0x07, 0xda, 0x07, 0x01, 0x01, 0x00, 0x3b, 0x01},
makedt(2010, 1, 1, 0, 59, 1, 0), type },
{ "date_hs", {0x07, 0xda, 0x07, 0x01, 0x01, 0x05, 0x00, 0x01},
makedt(2010, 1, 1, 5, 0, 1, 0), type },
{ "date_hms", {0x07, 0xda, 0x07, 0x01, 0x01, 0x17, 0x01, 0x3b},
makedt(2010, 1, 1, 23, 1, 59, 0), type },
{ "date_u", {0x0b, 0xda, 0x07, 0x01, 0x01, 0x00, 0x00, 0x00, 0x78, 0xd4, 0x03, 0x00},
makedt(2010, 1, 1, 0, 0, 0, 251000), type },
{ "date_hu", {0x0b, 0xda, 0x07, 0x01, 0x01, 0x17, 0x00, 0x00, 0x56, 0xc3, 0x0e, 0x00},
makedt(2010, 1, 1, 23, 0, 0, 967510), type },
{ "date_mu", {0x0b, 0xda, 0x07, 0x01, 0x01, 0x00, 0x01, 0x00, 0x56, 0xc3, 0x0e, 0x00},
makedt(2010, 1, 1, 0, 1, 0, 967510), type },
{ "date_hmu", {0x0b, 0xda, 0x07, 0x01, 0x01, 0x17, 0x01, 0x00, 0x56, 0xc3, 0x0e, 0x00},
makedt(2010, 1, 1, 23, 1, 0, 967510), type },
{ "date_su", {0x0b, 0xda, 0x07, 0x01, 0x01, 0x00, 0x00, 0x3b, 0x56, 0xc3, 0x0e, 0x00},
makedt(2010, 1, 1, 0, 0, 59, 967510), type },
{ "date_msu", {0x0b, 0xda, 0x07, 0x01, 0x01, 0x00, 0x01, 0x3b, 0x56, 0xc3, 0x0e, 0x00},
makedt(2010, 1, 1, 0, 1, 59, 967510), type },
{ "date_hsu", {0x0b, 0xda, 0x07, 0x01, 0x01, 0x17, 0x00, 0x3b, 0x56, 0xc3, 0x0e, 0x00},
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 },
};
// Create all the casuistic for datetimes with invalid dates. For all possible lengths,
// try invalid date, zero month, zero day, zero date
constexpr struct
{
const char* name;
std::uint8_t length;
} lengths [] = {
{ "d", 4 },
{ "hms", 7 },
{ "hmsu", 11 }
};
constexpr struct
{
const char* name;
void (*invalidator)(bytestring&);
} why_is_invalid [] = {
{ "zeros", [](bytestring& b) { std::memset(b.data() + 1, 0, b.size() - 1); } },
{ "invalid_date", [](bytestring& b) { b[3] = 11; b[4] = 31; } },
{ "zero_month", [](bytestring& b) { b[3] = 0; } },
{ "zero_day", [](bytestring& b) { b[4] = 0; } },
{ "zero_month_day", [](bytestring& b) { std::memset(b.data()+1, 0, 4); } },
};
// Template datetime
bytestring regular {0x0b, 0xda, 0x07, 0x01, 0x01, 0x17, 0x01, 0x3b, 0x56, 0xc3, 0x0e, 0x00};
for (const auto& why: why_is_invalid)
{
for (const auto& len: lengths)
{
std::string name = stringize(why.name, "_", len.name);
bytestring buffer (regular);
buffer[0] = std::uint8_t(len.length);
buffer.resize(len.length + 1);
why.invalidator(buffer);
res.emplace_back(std::move(name), std::move(buffer), nullptr, type);
}
}
return res;
}
INSTANTIATE_TEST_SUITE_P(DATETIME, DeserializeBinaryValueTest, ValuesIn(
make_datetime_cases(protocol_field_type::datetime)
), test_name_generator);
INSTANTIATE_TEST_SUITE_P(TIMESTAMP, DeserializeBinaryValueTest, ValuesIn(
make_datetime_cases(protocol_field_type::timestamp)
), test_name_generator);
INSTANTIATE_TEST_SUITE_P(TIME, DeserializeBinaryValueTest, Values(
binary_value_testcase("zero", {0x00},
maket(0, 0, 0), protocol_field_type::time),
binary_value_testcase("positive_d", {0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
maket(48, 0, 0, 0), protocol_field_type::time),
binary_value_testcase("positive_h", {0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00},
maket(21, 0, 0, 0), protocol_field_type::time),
binary_value_testcase("positive_m", {0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00},
maket(0, 40, 0), protocol_field_type::time),
binary_value_testcase("positive_s", {0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15},
maket(0, 0, 21), protocol_field_type::time),
binary_value_testcase("positive_u", {0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0xe5, 0x04, 0x00},
maket(0, 0, 0, 321000), protocol_field_type::time),
binary_value_testcase("positive_hmsu", {0x0c, 0x00, 0x22, 0x00, 0x00, 0x00, 0x16, 0x3b, 0x3a, 0x58, 0x3e, 0x0f, 0x00},
maket(838, 59, 58, 999000), protocol_field_type::time),
binary_value_testcase("negative_d", {0x08, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
-maket(48, 0, 0, 0), protocol_field_type::time),
binary_value_testcase("negative_h", {0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00},
-maket(21, 0, 0, 0), protocol_field_type::time),
binary_value_testcase("negative_m", {0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00},
-maket(0, 40, 0), protocol_field_type::time),
binary_value_testcase("negative_s", {0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15},
-maket(0, 0, 21), protocol_field_type::time),
binary_value_testcase("negative_u", {0x0c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0xe5, 0x04, 0x00},
-maket(0, 0, 0, 321000), protocol_field_type::time),
binary_value_testcase("negative_hmsu", {0x0c, 0x01, 0x22, 0x00, 0x00, 0x00, 0x16, 0x3b, 0x3a, 0x58, 0x3e, 0x0f, 0x00},
-maket(838, 59, 58, 999000), protocol_field_type::time),
binary_value_testcase("negative_sign_not_one", {0x0c, 0x03, 0x22, 0x00, 0x00, 0x00, 0x16, 0x3b, 0x3a, 0x58, 0x3e, 0x0f, 0x00},
-maket(838, 59, 58, 999000), protocol_field_type::time)
), test_name_generator);
} // anon namespace