/* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "decimal.h" #include "plugin/x/client/mysqlxclient/xdatetime.h" #include "plugin/x/client/mysqlxclient/xdecimal.h" #include "plugin/x/client/mysqlxclient/xrow.h" #include "plugin/x/client/xrow_impl.h" #include "plugin/x/ngs/include/ngs/protocol/page_pool.h" #include "plugin/x/ngs/include/ngs/protocol/protocol_protobuf.h" #include "plugin/x/protocol/encoders/encoding_xrow.h" #include "unittest/gunit/xplugin/xpl/protobuf_message.h" namespace protocol { namespace test { using ::xpl::test::message_with_header_from_buffer; template void assert_row_getter(const Expected_value_type &expected_value, const Method_type &method, const std::string &buffer) { Expected_value_type value; const bool result = method(buffer, &value); ASSERT_TRUE(result); ASSERT_EQ(expected_value, value); } class Row_builder_testsuite : public testing::Test { public: std::string get_buffer_as_string() { std::string result; auto page = m_buffer.m_front; while (page) { result += std::string(reinterpret_cast(page->m_begin_data), page->get_used_bytes()); page = page->m_next_page; } return result; } ngs::Memory_block_pool m_memory_block_pool{{20, k_minimum_page_size}}; Encoding_pool m_pool{10, &m_memory_block_pool}; Encoding_buffer m_buffer{&m_pool}; XMessage_encoder m_encoder{&m_buffer}; XRow_encoder m_row{&m_encoder}; }; TEST_F(Row_builder_testsuite, row_start) { m_row.begin_row(); m_row.field_null(); m_row.field_null(); ASSERT_EQ(2u, m_row.get_num_fields()); m_row.begin_row(); m_row.end_row(); ASSERT_EQ(0u, m_row.get_num_fields()); } TEST_F(Row_builder_testsuite, row_msg_size) { m_row.begin_row(); m_row.field_null(); m_row.end_row(); auto row_data = get_buffer_as_string(); // 1 byte for msg tag + 1 byte for field header + 1 byte // for field value (NULL) ASSERT_EQ(7, row_data.length()); ASSERT_EQ(3u, row_data[0]); ASSERT_EQ(0u, row_data[1]); ASSERT_EQ(0u, row_data[2]); ASSERT_EQ(0u, row_data[3]); m_row.begin_row(); m_row.field_null(); m_row.field_null(); m_row.end_row(); // offset of the size is 7 (3 bytes for prev msg + 4 for its size) auto two_row_data = get_buffer_as_string(); // 1 byte for msg tag + 2*(1 byte for field header + 1 byte // for field value (NULL)) ASSERT_EQ(16, two_row_data.length()); ASSERT_EQ(5u, two_row_data[7]); ASSERT_EQ(0u, two_row_data[8]); ASSERT_EQ(0u, two_row_data[9]); ASSERT_EQ(0u, two_row_data[10]); } TEST_F(Row_builder_testsuite, row_abort) { m_row.begin_row(); m_row.field_null(); m_row.field_null(); m_row.abort_row(); m_row.end_row(); auto row_data = get_buffer_as_string(); ASSERT_EQ(0u, row_data.length()); } TEST_F(Row_builder_testsuite, fields_qty) { m_row.begin_row(); ASSERT_EQ(0u, m_row.get_num_fields()); m_row.field_null(); m_row.field_null(); ASSERT_EQ(2u, m_row.get_num_fields()); m_row.field_unsigned_longlong(0); m_row.field_float(0.0f); m_row.field_float(0.0f); ASSERT_EQ(5u, m_row.get_num_fields()); m_row.end_row(); } TEST_F(Row_builder_testsuite, null_field) { m_row.begin_row(); m_row.field_null(); m_row.end_row(); std::unique_ptr row( message_with_header_from_buffer( get_buffer_as_string())); ASSERT_EQ(1, row->field_size()); ASSERT_EQ(0u, row->field(0).length()); } TEST_F(Row_builder_testsuite, unsigned64_field) { std::string *buffer; int idx = 0; m_row.begin_row(); m_row.field_unsigned_longlong(0); m_row.field_unsigned_longlong(500); m_row.field_unsigned_longlong(10000000); m_row.field_unsigned_longlong(0x7fffffffffffffffLL); m_row.field_unsigned_longlong(1); m_row.field_unsigned_longlong(0xffffffffffffffffLL); m_row.end_row(); std::unique_ptr row( message_with_header_from_buffer( get_buffer_as_string())); buffer = row->mutable_field(idx++); assert_row_getter(static_cast(0), &xcl::row_decoder::buffer_to_u64, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(500, &xcl::row_decoder::buffer_to_u64, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(10000000, &xcl::row_decoder::buffer_to_u64, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(0x7fffffffffffffffULL, &xcl::row_decoder::buffer_to_u64, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(1, &xcl::row_decoder::buffer_to_u64, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(0xffffffffffffffffULL, &xcl::row_decoder::buffer_to_u64, *buffer); } TEST_F(Row_builder_testsuite, signed64_field) { std::string *buffer; int idx = 0; m_row.begin_row(); m_row.field_signed_longlong(0); m_row.field_signed_longlong(-500); m_row.field_signed_longlong(-10000000); m_row.field_signed_longlong(0x7fffffffffffffffLL); m_row.field_signed_longlong(-1); m_row.end_row(); std::unique_ptr row( message_with_header_from_buffer( get_buffer_as_string())); buffer = row->mutable_field(idx++); assert_row_getter(0, &xcl::row_decoder::buffer_to_s64, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(-500, &xcl::row_decoder::buffer_to_s64, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(-10000000, &xcl::row_decoder::buffer_to_s64, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(0x7fffffffffffffffLL, &xcl::row_decoder::buffer_to_s64, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(-1, &xcl::row_decoder::buffer_to_s64, *buffer); } TEST_F(Row_builder_testsuite, float_field) { std::string *buffer; int idx = 0; m_row.begin_row(); m_row.field_float(0.0f); m_row.field_float(0.0001f); m_row.field_float(-10000000.1f); m_row.field_float(9999.91992f); m_row.field_float(std::numeric_limits::min()); m_row.field_float(std::numeric_limits::max()); m_row.end_row(); std::unique_ptr row( message_with_header_from_buffer( get_buffer_as_string())); buffer = row->mutable_field(idx++); assert_row_getter(0.0f, &xcl::row_decoder::buffer_to_float, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(0.0001f, &xcl::row_decoder::buffer_to_float, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(-10000000.1f, &xcl::row_decoder::buffer_to_float, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(9999.91992f, &xcl::row_decoder::buffer_to_float, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(std::numeric_limits::min(), &xcl::row_decoder::buffer_to_float, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(std::numeric_limits::max(), &xcl::row_decoder::buffer_to_float, *buffer); } TEST_F(Row_builder_testsuite, double_field) { std::string *buffer; int idx = 0; m_row.begin_row(); m_row.field_double(0.0); m_row.field_double(0.0001); m_row.field_double(-10000000.1); m_row.field_double(9999.91992); m_row.field_double(std::numeric_limits::min()); m_row.field_double(std::numeric_limits::max()); m_row.end_row(); std::unique_ptr row( message_with_header_from_buffer( get_buffer_as_string())); buffer = row->mutable_field(idx++); assert_row_getter(0.0, &xcl::row_decoder::buffer_to_double, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(0.0001, &xcl::row_decoder::buffer_to_double, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(-10000000.1, &xcl::row_decoder::buffer_to_double, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(9999.91992, &xcl::row_decoder::buffer_to_double, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(std::numeric_limits::min(), &xcl::row_decoder::buffer_to_double, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(std::numeric_limits::max(), &xcl::row_decoder::buffer_to_double, *buffer); } TEST_F(Row_builder_testsuite, string_field) { std::string *buffer; int idx = 0; const char *pstr; size_t len; const char *const STR1 = "ABBABABBBAAA-09-0900--==0,\0\0\0\0\0"; m_row.begin_row(); m_row.field_string("", 0); m_row.field_string(STR1, strlen(STR1)); m_row.end_row(); std::unique_ptr row( message_with_header_from_buffer( get_buffer_as_string())); buffer = row->mutable_field(idx++); ASSERT_TRUE(xcl::row_decoder::buffer_to_string(*buffer, &pstr, &len)); ASSERT_STREQ("", pstr); ASSERT_EQ(len, 0u); buffer = row->mutable_field(idx++); ASSERT_TRUE(xcl::row_decoder::buffer_to_string(*buffer, &pstr, &len)); for (size_t i = 0; i < len; i++) { ASSERT_EQ(STR1[i], pstr[i]); } ASSERT_EQ(len, strlen(STR1)); } TEST_F(Row_builder_testsuite, date_field) { std::string *buffer; int idx = 0; MYSQL_TIME time; time.year = 2006; time.month = 3; time.day = 24; m_row.begin_row(); m_row.field_date(&time); m_row.end_row(); std::unique_ptr row( message_with_header_from_buffer( get_buffer_as_string())); buffer = row->mutable_field(idx++); xcl::DateTime xtime; bool has_time = false; ASSERT_TRUE(xcl::row_decoder::buffer_to_datetime(*buffer, &xtime, has_time)); ASSERT_EQ(time.year, xtime.year()); ASSERT_EQ(time.month, xtime.month()); ASSERT_EQ(time.day, xtime.day()); } TEST_F(Row_builder_testsuite, time_field) { std::string *buffer; int idx = 0; MYSQL_TIME time; time.neg = false; time.hour = 12; time.minute = 0; time.second = 0; time.second_part = 999999; MYSQL_TIME time2; time2.neg = false; time2.hour = 0; time2.minute = 0; time2.second = 0; time2.second_part = 0; MYSQL_TIME time3; time3.neg = true; time3.hour = 811; time3.minute = 0; time3.second = 0; time3.second_part = 0; m_row.begin_row(); m_row.field_time(&time); m_row.field_time(&time2); m_row.field_time(&time3); m_row.end_row(); std::unique_ptr row{ message_with_header_from_buffer( get_buffer_as_string())}; buffer = row->mutable_field(idx++); xcl::Time xtime; ASSERT_TRUE(xcl::row_decoder::buffer_to_time(*buffer, &xtime)); ASSERT_TRUE(false == xtime.is_negate()); ASSERT_EQ(time.hour, xtime.hour()); ASSERT_EQ(time.minute, xtime.minutes()); ASSERT_EQ(time.second, xtime.seconds()); ASSERT_EQ(time.second_part, xtime.useconds()); buffer = row->mutable_field(idx++); ASSERT_TRUE(xcl::row_decoder::buffer_to_time(*buffer, &xtime)); ASSERT_TRUE(false == xtime.is_negate()); ASSERT_EQ(time2.hour, xtime.hour()); ASSERT_EQ(time2.minute, xtime.minutes()); ASSERT_EQ(time2.second, xtime.seconds()); ASSERT_EQ(time2.second_part, xtime.useconds()); buffer = row->mutable_field(idx++); ASSERT_TRUE(xcl::row_decoder::buffer_to_time(*buffer, &xtime)); ASSERT_TRUE(true == xtime.is_negate()); ASSERT_EQ(time3.hour, xtime.hour()); ASSERT_EQ(time3.minute, xtime.minutes()); ASSERT_EQ(time3.second, xtime.seconds()); ASSERT_EQ(time3.second_part, xtime.useconds()); } TEST_F(Row_builder_testsuite, datetime_field) { std::string *buffer; int idx = 0; MYSQL_TIME time; time.year = 2016; time.month = 12; time.day = 24; time.hour = 13; time.minute = 55; time.second = 55; time.second_part = 999999; time.time_type = MYSQL_TIMESTAMP_DATETIME; MYSQL_TIME time2; time2.year = 2000; time2.month = 1; time2.day = 1; time2.hour = 0; time2.minute = 0; time2.second = 0; time2.second_part = 0; time2.time_type = MYSQL_TIMESTAMP_DATETIME; m_row.begin_row(); m_row.field_datetime(&time); m_row.field_datetime(&time2); m_row.end_row(); std::unique_ptr row{ message_with_header_from_buffer( get_buffer_as_string())}; buffer = row->mutable_field(idx++); xcl::DateTime xtime; bool has_time = true; ASSERT_TRUE(xcl::row_decoder::buffer_to_datetime(*buffer, &xtime, has_time)); ASSERT_EQ(time.year, xtime.year()); ASSERT_EQ(time.month, xtime.month()); ASSERT_EQ(time.day, xtime.day()); ASSERT_EQ(time.hour, xtime.hour()); ASSERT_EQ(time.minute, xtime.minutes()); ASSERT_EQ(time.second, xtime.seconds()); ASSERT_EQ(time.second_part, xtime.useconds()); buffer = row->mutable_field(idx++); ASSERT_TRUE(xcl::row_decoder::buffer_to_datetime(*buffer, &xtime, has_time)); ASSERT_EQ(time2.year, xtime.year()); ASSERT_EQ(time2.month, xtime.month()); ASSERT_EQ(time2.day, xtime.day()); ASSERT_EQ(time2.hour, xtime.hour()); ASSERT_EQ(time2.minute, xtime.minutes()); ASSERT_EQ(time2.second, xtime.seconds()); ASSERT_EQ(time2.second_part, xtime.useconds()); } TEST_F(Row_builder_testsuite, decimal_field) { std::string *buffer; int idx = 0; xcl::Decimal xdecimal; m_row.begin_row(); decimal_digit_t arr1[] = {1, 0}; decimal_t dec1 = {1, 1, 2, 1, arr1}; m_row.field_decimal(&dec1); decimal_digit_t arr2[] = {1, 0}; decimal_t dec2 = {1, 1, 2, 0, arr2}; m_row.field_decimal(&dec2); m_row.end_row(); std::unique_ptr row{ message_with_header_from_buffer( get_buffer_as_string())}; buffer = row->mutable_field(idx++); ASSERT_TRUE(xcl::row_decoder::buffer_to_decimal(*buffer, &xdecimal)); ASSERT_EQ("-1.0", xdecimal.to_string()); buffer = row->mutable_field(idx++); ASSERT_TRUE(xcl::row_decoder::buffer_to_decimal(*buffer, &xdecimal)); ASSERT_EQ("1.0", xdecimal.to_string()); } TEST_F(Row_builder_testsuite, set_field) { std::string *buffer; int idx = 0; m_row.begin_row(); const char *const str_abcd = "A,B,C,D"; const char *const str_a = "A"; m_row.field_set(str_abcd, strlen(str_abcd)); m_row.field_set("", 0); // empty SET case m_row.field_set(str_a, strlen(str_a)); m_row.end_row(); std::unique_ptr row( message_with_header_from_buffer( get_buffer_as_string())); buffer = row->mutable_field(idx++); std::set elems; std::string elems_string; ASSERT_TRUE(xcl::row_decoder::buffer_to_string_set(*buffer, &elems_string)); ASSERT_STREQ("A,B,C,D", elems_string.c_str()); ASSERT_TRUE(xcl::row_decoder::buffer_to_set(*buffer, &elems)); ASSERT_EQ(1u, elems.count("A")); ASSERT_EQ(1u, elems.count("B")); ASSERT_EQ(1u, elems.count("C")); ASSERT_EQ(1u, elems.count("D")); ASSERT_EQ(4u, elems.size()); buffer = row->mutable_field(idx++); xcl::row_decoder::buffer_to_set(*buffer, &elems); ASSERT_EQ(true, elems.empty()); ASSERT_TRUE(xcl::row_decoder::buffer_to_string_set(*buffer, &elems_string)); ASSERT_STREQ("", elems_string.c_str()); buffer = row->mutable_field(idx++); xcl::row_decoder::buffer_to_set(*buffer, &elems); ASSERT_EQ(1u, elems.size()); ASSERT_EQ(1u, elems.count("A")); ASSERT_TRUE(xcl::row_decoder::buffer_to_string_set(*buffer, &elems_string)); ASSERT_STREQ("A", elems_string.c_str()); } TEST_F(Row_builder_testsuite, bit_field) { std::string *buffer; int idx = 0; m_row.begin_row(); m_row.field_bit("\x00", 1); m_row.field_bit("\x01", 1); m_row.field_bit("\xff\x00", 2); m_row.field_bit("\x00\x00\x00\x00\x00\x00\x00\x00", 8); m_row.field_bit("\xff\xff\xff\xff\xff\xff\xff\xff", 8); m_row.end_row(); std::unique_ptr row{ message_with_header_from_buffer( get_buffer_as_string())}; buffer = row->mutable_field(idx++); assert_row_getter(0x0u, &xcl::row_decoder::buffer_to_u64, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(0x1u, &xcl::row_decoder::buffer_to_u64, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(0xff00u, &xcl::row_decoder::buffer_to_u64, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(0x0000000000000000ULL, &xcl::row_decoder::buffer_to_u64, *buffer); buffer = row->mutable_field(idx++); assert_row_getter(0xffffffffffffffffULL, &xcl::row_decoder::buffer_to_u64, *buffer); } TEST_F(Row_builder_testsuite, datetime_content_type_set) { xcl::XRow_impl::Metadata metadata; xcl::XRow_impl::Metadata::value_type metadata_row; metadata_row.type = xcl::Column_type::DATETIME; metadata_row.length = 19; metadata_row.has_content_type = true; metadata_row.content_type = static_cast(Mysqlx::Resultset::DATETIME); metadata.push_back(metadata_row); xcl::Context context; ::testing::StrictMock row_mock(&metadata, &context); MYSQL_TIME time; time.year = 2016; time.month = 12; time.day = 24; time.hour = 13; time.minute = 55; time.second = 55; time.second_part = 999999; time.time_type = MYSQL_TIMESTAMP_DATETIME; m_row.begin_row(); m_row.field_datetime(&time); m_row.end_row(); std::unique_ptr row{ message_with_header_from_buffer( get_buffer_as_string())}; row_mock.set_row(std::move(row)); xcl::DateTime result; row_mock.get_datetime(0, &result); EXPECT_TRUE(result.has_time()); EXPECT_EQ(result.year(), time.year); EXPECT_EQ(result.month(), time.month); EXPECT_EQ(result.day(), time.day); EXPECT_EQ(result.hour(), time.hour); EXPECT_EQ(result.minutes(), time.minute); EXPECT_EQ(result.seconds(), time.second); EXPECT_EQ(result.useconds(), time.second_part); } TEST_F(Row_builder_testsuite, datetime_content_type_not_set_and_has_time_part) { xcl::XRow_impl::Metadata metadata; xcl::XRow_impl::Metadata::value_type metadata_row; metadata_row.type = xcl::Column_type::DATETIME; metadata_row.length = 19; metadata_row.has_content_type = false; metadata.push_back(metadata_row); xcl::Context context; ::testing::StrictMock row_mock(&metadata, &context); MYSQL_TIME time; time.year = 2016; time.month = 12; time.day = 24; time.hour = 13; time.minute = 55; time.second = 55; time.second_part = 0; time.time_type = MYSQL_TIMESTAMP_DATETIME; m_row.begin_row(); m_row.field_datetime(&time); m_row.end_row(); std::unique_ptr row{ message_with_header_from_buffer( get_buffer_as_string())}; row_mock.set_row(std::move(row)); xcl::DateTime result; row_mock.get_datetime(0, &result); EXPECT_EQ(row_mock.get_number_of_fields(), 1); EXPECT_TRUE(result.has_time()); EXPECT_EQ(result.year(), time.year); EXPECT_EQ(result.month(), time.month); EXPECT_EQ(result.day(), time.day); EXPECT_EQ(result.hour(), time.hour); EXPECT_EQ(result.minutes(), time.minute); EXPECT_EQ(result.seconds(), time.second); EXPECT_EQ(result.useconds(), time.second_part); } TEST_F(Row_builder_testsuite, datetime_content_type_not_set_and_not_contains_time_part) { xcl::XRow_impl::Metadata metadata; xcl::XRow_impl::Metadata::value_type metadata_row; metadata_row.type = xcl::Column_type::DATETIME; metadata_row.length = 10; metadata_row.has_content_type = false; metadata.push_back(metadata_row); xcl::Context context; ::testing::StrictMock row_mock(&metadata, &context); MYSQL_TIME time; time.year = 2016; time.month = 12; time.day = 24; time.hour = 0; time.minute = 0; time.second = 0; time.second_part = 0; time.time_type = MYSQL_TIMESTAMP_DATE; m_row.begin_row(); m_row.field_datetime(&time); m_row.end_row(); std::unique_ptr row{ message_with_header_from_buffer( get_buffer_as_string())}; row_mock.set_row(std::move(row)); xcl::DateTime result; row_mock.get_datetime(0, &result); EXPECT_EQ(result.year(), time.year); EXPECT_EQ(result.month(), time.month); EXPECT_EQ(result.day(), time.day); EXPECT_FALSE(result.has_time()); EXPECT_EQ(result.hour(), 0xFF); EXPECT_EQ(result.minutes(), 0xFF); EXPECT_EQ(result.seconds(), 0xFF); EXPECT_EQ(result.useconds(), 0xFFFFFF); } } // namespace test } // namespace protocol