From 8c46059db21ae8e5cfcb575bdf683cfc6b8e5659 Mon Sep 17 00:00:00 2001 From: ruben Date: Thu, 6 Feb 2020 00:22:07 +0000 Subject: [PATCH] Added resultset tests for binary protocol Split resultset tests out of query tests Parameterized resultset tests so they are run with both text and binary protocol Fixed bug in binary deserialization (we were not skipping the message type byte in deserialize_binary_row) --- include/mysql/impl/binary_deserialization.ipp | 7 +- test/CMakeLists.txt | 1 + test/integration/integration_test_common.hpp | 39 ++ test/integration/query.cpp | 476 ----------------- test/integration/resultset.cpp | 493 ++++++++++++++++++ test/unit/binary_deserialization.cpp | 28 +- 6 files changed, 553 insertions(+), 491 deletions(-) create mode 100644 test/integration/resultset.cpp diff --git a/include/mysql/impl/binary_deserialization.ipp b/include/mysql/impl/binary_deserialization.ipp index 6236b463..96ca4d97 100644 --- a/include/mysql/impl/binary_deserialization.ipp +++ b/include/mysql/impl/binary_deserialization.ipp @@ -122,7 +122,12 @@ inline mysql::error_code mysql::detail::deserialize_binary_row( std::vector& output ) { - // Packet header is already read + // Skip packet header (it is not part of the message in the binary + // protocol but it is in the text protocol, so we include it for homogeneity) + // The caller will have checked we have this byte already for us + assert(ctx.enough_size(1)); + ctx.advance(1); + // Number of fields auto num_fields = meta.size(); output.resize(num_fields); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b7dbd4ee..aa425318 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -60,6 +60,7 @@ add_executable( integration/query.cpp integration/prepare_statement.cpp integration/execute_statement.cpp + integration/resultset.cpp integration/query_types.cpp ) target_link_libraries( diff --git a/test/integration/integration_test_common.hpp b/test/integration/integration_test_common.hpp index 9a42e335..e04bc2b7 100644 --- a/test/integration/integration_test_common.hpp +++ b/test/integration/integration_test_common.hpp @@ -9,6 +9,7 @@ #include #include #include "test_common.hpp" +#include "metadata_validator.hpp" namespace mysql { @@ -141,6 +142,44 @@ struct IntegTest : testing::Test ASSERT_EQ(errc, error_code()); EXPECT_EQ(info, error_info()); } + + using resultset_type = mysql::resultset; + + void validate_eof( + const resultset_type& result, + int affected_rows=0, + int warnings=0, + int last_insert=0, + std::string_view info="" + ) + { + EXPECT_TRUE(result.valid()); + EXPECT_TRUE(result.complete()); + EXPECT_EQ(result.affected_rows(), affected_rows); + EXPECT_EQ(result.warning_count(), warnings); + EXPECT_EQ(result.last_insert_id(), last_insert); + EXPECT_EQ(result.info(), info); + } + + void validate_2fields_meta( + const std::vector& fields, + const std::string& table + ) const + { + validate_meta(fields, { + meta_validator(table, "id", field_type::int_), + meta_validator(table, "field_varchar", field_type::varchar) + }); + } + + void validate_2fields_meta( + const resultset_type& result, + const std::string& table + ) const + { + validate_2fields_meta(result.fields(), table); + } + }; struct IntegTestAfterHandshake : IntegTest diff --git a/test/integration/query.cpp b/test/integration/query.cpp index 3166d769..925b0270 100644 --- a/test/integration/query.cpp +++ b/test/integration/query.cpp @@ -28,43 +28,6 @@ namespace struct QueryTest : public mysql::test::IntegTestAfterHandshake { - using resultset_type = mysql::resultset; - - void validate_eof( - const resultset_type& result, - int affected_rows=0, - int warnings=0, - int last_insert=0, - std::string_view info="" - ) - { - EXPECT_TRUE(result.valid()); - EXPECT_TRUE(result.complete()); - EXPECT_EQ(result.affected_rows(), affected_rows); - EXPECT_EQ(result.warning_count(), warnings); - EXPECT_EQ(result.last_insert_id(), last_insert); - EXPECT_EQ(result.info(), info); - } - - void validate_2fields_meta( - const std::vector& fields, - const std::string& table - ) const - { - validate_meta(fields, { - meta_validator(table, "id", field_type::int_), - meta_validator(table, "field_varchar", field_type::varchar) - }); - } - - void validate_2fields_meta( - const resultset_type& result, - const std::string& table - ) const - { - validate_2fields_meta(result.fields(), table); - } - auto make_query_initiator(const char* sql) { return [this, sql](auto&& cb) { @@ -207,445 +170,6 @@ TEST_F(QueryTest, QueryAsync_SelectQueryFailed) ); } - -// FetchOne, sync errc -TEST_F(QueryTest, FetchOneSyncErrc_NoResults) -{ - auto result = conn.query("SELECT * FROM empty_table"); - EXPECT_TRUE(result.valid()); - EXPECT_FALSE(result.complete()); - EXPECT_EQ(result.fields().size(), 2); - - // Already in the end of the resultset, we receive the EOF - const mysql::row* row = result.fetch_one(errc, info); - validate_no_error(); - EXPECT_EQ(row, nullptr); - validate_eof(result); - - // Fetching again just returns null - reset_errors(); - row = result.fetch_one(errc, info); - validate_no_error(); - EXPECT_EQ(row, nullptr); - validate_eof(result); -} - -TEST_F(QueryTest, FetchOneSyncErrc_OneRow) -{ - auto result = conn.query("SELECT * FROM one_row_table"); - EXPECT_TRUE(result.valid()); - EXPECT_FALSE(result.complete()); - EXPECT_EQ(result.fields().size(), 2); - - // Fetch only row - const mysql::row* row = result.fetch_one(errc, info); - validate_no_error(); - ASSERT_NE(row, nullptr); - validate_2fields_meta(result, "one_row_table"); - EXPECT_EQ(row->values(), makevalues(1, "f0")); - EXPECT_FALSE(result.complete()); - - // Fetch next: end of resultset - reset_errors(); - row = result.fetch_one(errc, info); - validate_no_error(); - ASSERT_EQ(row, nullptr); - validate_eof(result); -} - -TEST_F(QueryTest, FetchOneSyncErrc_TwoRows) -{ - auto result = conn.query("SELECT * FROM two_rows_table"); - EXPECT_TRUE(result.valid()); - EXPECT_FALSE(result.complete()); - EXPECT_EQ(result.fields().size(), 2); - - // Fetch first row - const mysql::row* row = result.fetch_one(errc, info); - validate_no_error(); - ASSERT_NE(row, nullptr); - validate_2fields_meta(result, "two_rows_table"); - EXPECT_EQ(row->values(), makevalues(1, "f0")); - EXPECT_FALSE(result.complete()); - - // Fetch next row - reset_errors(); - row = result.fetch_one(errc, info); - validate_no_error(); - ASSERT_NE(row, nullptr); - validate_2fields_meta(result, "two_rows_table"); - EXPECT_EQ(row->values(), makevalues(2, "f1")); - EXPECT_FALSE(result.complete()); - - // Fetch next: end of resultset - reset_errors(); - row = result.fetch_one(errc, info); - validate_no_error(); - ASSERT_EQ(row, nullptr); - validate_eof(result); -} - -// There seems to be no real case where fetch can fail (other than net fails) - -// FetchOne, sync exc -TEST_F(QueryTest, FetchOneSyncExc_TwoRows) -{ - auto result = conn.query("SELECT * FROM two_rows_table"); - EXPECT_TRUE(result.valid()); - EXPECT_FALSE(result.complete()); - EXPECT_EQ(result.fields().size(), 2); - - // Fetch first row - const mysql::row* row = result.fetch_one(); - ASSERT_NE(row, nullptr); - validate_2fields_meta(result, "two_rows_table"); - EXPECT_EQ(row->values(), makevalues(1, "f0")); - EXPECT_FALSE(result.complete()); - - // Fetch next row - row = result.fetch_one(); - ASSERT_NE(row, nullptr); - validate_2fields_meta(result, "two_rows_table"); - EXPECT_EQ(row->values(), makevalues(2, "f1")); - EXPECT_FALSE(result.complete()); - - // Fetch next: end of resultset - row = result.fetch_one(); - ASSERT_EQ(row, nullptr); - validate_eof(result); -} - -// FetchOne, async -TEST_F(QueryTest, FetchOneAsync_NoResults) -{ - auto result = conn.query("SELECT * FROM empty_table"); - - // Already in the end of the resultset, we receive the EOF - auto [info, row] = result.async_fetch_one(net::use_future).get(); - EXPECT_EQ(info, error_info()); - EXPECT_EQ(row, nullptr); - validate_eof(result); - - // Fetching again just returns null - std::tie(info, row) = result.async_fetch_one(net::use_future).get(); - EXPECT_EQ(info, error_info()); - EXPECT_EQ(row, nullptr); - validate_eof(result); -} - -TEST_F(QueryTest, FetchOneAsync_OneRow) -{ - auto result = conn.query("SELECT * FROM one_row_table"); - - // Fetch only row - auto [info, row] = result.async_fetch_one(net::use_future).get(); - EXPECT_EQ(info, error_info()); - ASSERT_NE(row, nullptr); - EXPECT_EQ(row->values(), makevalues(1, "f0")); - EXPECT_FALSE(result.complete()); - - // Fetch next: end of resultset - reset_errors(); - std::tie(info, row) = result.async_fetch_one(net::use_future).get(); - EXPECT_EQ(info, error_info()); - ASSERT_EQ(row, nullptr); - validate_eof(result); -} - -TEST_F(QueryTest, FetchOneAsync_TwoRows) -{ - auto result = conn.query("SELECT * FROM two_rows_table"); - - // Fetch first row - auto [info, row] = result.async_fetch_one(net::use_future).get(); - EXPECT_EQ(info, error_info()); - ASSERT_NE(row, nullptr); - EXPECT_EQ(row->values(), makevalues(1, "f0")); - EXPECT_FALSE(result.complete()); - - // Fetch next row - reset_errors(); - std::tie(info, row) = result.async_fetch_one(net::use_future).get(); - EXPECT_EQ(info, error_info()); - ASSERT_NE(row, nullptr); - EXPECT_EQ(row->values(), makevalues(2, "f1")); - EXPECT_FALSE(result.complete()); - - // Fetch next: end of resultset - reset_errors(); - std::tie(info, row) = result.async_fetch_one(net::use_future).get(); - EXPECT_EQ(info, error_info()); - ASSERT_EQ(row, nullptr); - validate_eof(result); -} - -// FetchMany, sync errc -TEST_F(QueryTest, FetchManySyncErrc_NoResults) -{ - auto result = conn.query("SELECT * FROM empty_table"); - - // Fetch many, but there are no results - auto rows = result.fetch_many(10, errc, info); - validate_no_error(); - EXPECT_TRUE(rows.empty()); - EXPECT_TRUE(result.complete()); - validate_eof(result); - - // Fetch again, should return OK and empty - reset_errors(); - rows = result.fetch_many(10, errc, info); - ASSERT_EQ(errc, error_code()); - EXPECT_EQ(info, error_info()); - EXPECT_TRUE(rows.empty()); - EXPECT_TRUE(result.complete()); - validate_eof(result); -} - -TEST_F(QueryTest, FetchManySyncErrc_MoreRowsThanCount) -{ - auto result = conn.query("SELECT * FROM three_rows_table"); - - // Fetch 2, one remaining - auto rows = result.fetch_many(2, errc, info); - validate_no_error(); - EXPECT_FALSE(result.complete()); - EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); - - // Fetch another two (completes the resultset) - reset_errors(); - rows = result.fetch_many(2, errc, info); - validate_no_error(); - EXPECT_TRUE(result.complete()); - validate_eof(result); - EXPECT_EQ(rows, (makerows(2, 3, "f2"))); -} - -TEST_F(QueryTest, FetchManySyncErrc_LessRowsThanCount) -{ - auto result = conn.query("SELECT * FROM two_rows_table"); - - // Fetch 3, resultset exhausted - auto rows = result.fetch_many(3, errc, info); - validate_no_error(); - EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); - validate_eof(result); -} - -TEST_F(QueryTest, FetchManySyncErrc_SameRowsAsCount) -{ - auto result = conn.query("SELECT * FROM two_rows_table"); - - // Fetch 2, 0 remaining but resultset not exhausted - auto rows = result.fetch_many(2, errc, info); - validate_no_error(); - EXPECT_FALSE(result.complete()); - EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); - - // Fetch again, exhausts the resultset - reset_errors(); - rows = result.fetch_many(2, errc, info); - validate_no_error(); - EXPECT_EQ(rows.size(), 0); - validate_eof(result); -} - -TEST_F(QueryTest, FetchManySyncErrc_CountEqualsOne) -{ - auto result = conn.query("SELECT * FROM one_row_table"); - - // Fetch 1, 1 remaining - auto rows = result.fetch_many(1, errc, info); - validate_no_error(); - EXPECT_FALSE(result.complete()); - EXPECT_EQ(rows, (makerows(2, 1, "f0"))); -} - -// FetchMany, sync exc -TEST_F(QueryTest, FetchManySyncExc_MoreRowsThanCount) -{ - auto result = conn.query("SELECT * FROM three_rows_table"); - - // Fetch 2, one remaining - auto rows = result.fetch_many(2); - EXPECT_FALSE(result.complete()); - EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); - - // Fetch another two (completes the resultset) - rows = result.fetch_many(2); - validate_eof(result); - EXPECT_EQ(rows, (makerows(2, 3, "f2"))); - - // Fetching another time returns empty - rows = result.fetch_many(2); - EXPECT_EQ(rows.size(), 0); -} - -// FetchMany, async -TEST_F(QueryTest, FetchManyAsync_NoResults) -{ - auto result = conn.query("SELECT * FROM empty_table"); - - // Fetch many, but there are no results - auto [info, rows] = result.async_fetch_many(10, net::use_future).get(); - EXPECT_EQ(info, error_info()); - EXPECT_TRUE(rows.empty()); - EXPECT_TRUE(result.complete()); - validate_eof(result); - - // Fetch again, should return OK and empty - reset_errors(); - std::tie(info, rows) = result.async_fetch_many(10, net::use_future).get(); - EXPECT_EQ(info, error_info()); - EXPECT_TRUE(rows.empty()); - EXPECT_TRUE(result.complete()); - validate_eof(result); -} - -TEST_F(QueryTest, FetchManyAsync_MoreRowsThanCount) -{ - auto result = conn.query("SELECT * FROM three_rows_table"); - - // Fetch 2, one remaining - auto [info, rows] = result.async_fetch_many(2, net::use_future).get(); - EXPECT_EQ(info, error_info()); - EXPECT_FALSE(result.complete()); - EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); - - // Fetch another two (completes the resultset) - std::tie(info, rows) = result.async_fetch_many(2, net::use_future).get(); - EXPECT_EQ(info, error_info()); - EXPECT_TRUE(result.complete()); - validate_eof(result); - EXPECT_EQ(rows, (makerows(2, 3, "f2"))); -} - -TEST_F(QueryTest, FetchManyAsync_LessRowsThanCount) -{ - auto result = conn.query("SELECT * FROM two_rows_table"); - - // Fetch 3, resultset exhausted - auto [info, rows] = result.async_fetch_many(3, net::use_future).get(); - EXPECT_EQ(info, error_info()); - EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); - validate_eof(result); -} - -TEST_F(QueryTest, FetchManyAsync_SameRowsAsCount) -{ - auto result = conn.query("SELECT * FROM two_rows_table"); - - // Fetch 2, 0 remaining but resultset not exhausted - auto [info, rows] = result.async_fetch_many(2, net::use_future).get(); - EXPECT_EQ(info, error_info()); - EXPECT_FALSE(result.complete()); - EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); - - // Fetch again, exhausts the resultset - reset_errors(); - std::tie(info, rows) = result.async_fetch_many(2, net::use_future).get(); - EXPECT_EQ(info, error_info()); - EXPECT_EQ(rows.size(), 0); - validate_eof(result); -} - -TEST_F(QueryTest, FetchManyAsync_CountEqualsOne) -{ - auto result = conn.query("SELECT * FROM one_row_table"); - - // Fetch 1, 1 remaining - auto [info, rows] = result.async_fetch_many(1, net::use_future).get(); - EXPECT_EQ(info, error_info()); - EXPECT_FALSE(result.complete()); - EXPECT_EQ(rows, (makerows(2, 1, "f0"))); -} - -// FetchAll, sync errc -TEST_F(QueryTest, FetchAllSyncErrc_NoResults) -{ - auto result = conn.query("SELECT * FROM empty_table"); - - // Fetch many, but there are no results - auto rows = result.fetch_all(errc, info); - validate_no_error(); - EXPECT_TRUE(rows.empty()); - EXPECT_TRUE(result.complete()); - - // Fetch again, should return OK and empty - reset_errors(); - rows = result.fetch_all(errc, info); - validate_no_error(); - EXPECT_TRUE(rows.empty()); - validate_eof(result); -} - -TEST_F(QueryTest, FetchAllSyncErrc_OneRow) -{ - auto result = conn.query("SELECT * FROM one_row_table"); - - auto rows = result.fetch_all(errc, info); - validate_no_error(); - EXPECT_TRUE(result.complete()); - EXPECT_EQ(rows, (makerows(2, 1, "f0"))); -} - -TEST_F(QueryTest, FetchAllSyncErrc_SeveralRows) -{ - auto result = conn.query("SELECT * FROM two_rows_table"); - - auto rows = result.fetch_all(errc, info); - validate_no_error(); - validate_eof(result); - EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); -} - -// FetchAll, sync exc -TEST_F(QueryTest, FetchAllSyncExc_SeveralRows) -{ - auto result = conn.query("SELECT * FROM two_rows_table"); - auto rows = result.fetch_all(); - validate_eof(result); - EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); -} - -// FetchAll, async -TEST_F(QueryTest, FetchAllAsync_NoResults) -{ - auto result = conn.query("SELECT * FROM empty_table"); - - // Fetch many, but there are no results - auto [info, rows] = result.async_fetch_all(net::use_future).get(); - EXPECT_EQ(info, error_info()); - EXPECT_TRUE(rows.empty()); - EXPECT_TRUE(result.complete()); - - // Fetch again, should return OK and empty - reset_errors(); - std::tie(info, rows) = result.async_fetch_all(net::use_future).get(); - EXPECT_EQ(info, error_info()); - EXPECT_TRUE(rows.empty()); - validate_eof(result); -} - -TEST_F(QueryTest, FetchAllAsync_OneRow) -{ - auto result = conn.query("SELECT * FROM one_row_table"); - - auto [info, rows] = result.async_fetch_all(net::use_future).get(); - EXPECT_EQ(info, error_info()); - EXPECT_TRUE(result.complete()); - EXPECT_EQ(rows, (makerows(2, 1, "f0"))); -} - -TEST_F(QueryTest, FetchAllAsync_SeveralRows) -{ - auto result = conn.query("SELECT * FROM two_rows_table"); - - auto [info, rows] = result.async_fetch_all(net::use_future).get(); - EXPECT_EQ(info, error_info()); - validate_eof(result); - EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); -} - // Some system-level query tests TEST_F(QueryTest, QueryAndFetch_AliasedTableAndField_MetadataCorrect) { diff --git a/test/integration/resultset.cpp b/test/integration/resultset.cpp new file mode 100644 index 00000000..f61b18ae --- /dev/null +++ b/test/integration/resultset.cpp @@ -0,0 +1,493 @@ +/* + * resultset.cpp + * + * Created on: Feb 5, 2020 + * Author: ruben + */ + +#include "integration_test_common.hpp" +#include + +using namespace mysql::test; +using mysql::detail::make_error_code; +using mysql::test::meta_validator; +using mysql::test::validate_meta; +using mysql::field_metadata; +using mysql::field_type; +using mysql::error_code; +using mysql::error_info; +namespace net = boost::asio; + +namespace +{ + +struct ResultsetTestParam : named_param +{ + std::string name; + std::function generate_resultset; + + template + ResultsetTestParam(std::string name, Callable&& cb): + name(std::move(name)), generate_resultset(std::forward(cb)) {} +}; + +struct ResultsetTest : public IntegTestAfterHandshake, public testing::WithParamInterface +{ +}; + +// FetchOne, sync errc +TEST_P(ResultsetTest, FetchOneSyncErrc_NoResults) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM empty_table"); + EXPECT_TRUE(result.valid()); + EXPECT_FALSE(result.complete()); + EXPECT_EQ(result.fields().size(), 2); + + // Already in the end of the resultset, we receive the EOF + const mysql::row* row = result.fetch_one(errc, info); + validate_no_error(); + EXPECT_EQ(row, nullptr); + validate_eof(result); + + // Fetching again just returns null + reset_errors(); + row = result.fetch_one(errc, info); + validate_no_error(); + EXPECT_EQ(row, nullptr); + validate_eof(result); +} + +TEST_P(ResultsetTest, FetchOneSyncErrc_OneRow) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM one_row_table"); + EXPECT_TRUE(result.valid()); + EXPECT_FALSE(result.complete()); + EXPECT_EQ(result.fields().size(), 2); + + // Fetch only row + const mysql::row* row = result.fetch_one(errc, info); + validate_no_error(); + ASSERT_NE(row, nullptr); + validate_2fields_meta(result, "one_row_table"); + EXPECT_EQ(row->values(), makevalues(1, "f0")); + EXPECT_FALSE(result.complete()); + + // Fetch next: end of resultset + reset_errors(); + row = result.fetch_one(errc, info); + validate_no_error(); + ASSERT_EQ(row, nullptr); + validate_eof(result); +} + +TEST_P(ResultsetTest, FetchOneSyncErrc_TwoRows) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM two_rows_table"); + EXPECT_TRUE(result.valid()); + EXPECT_FALSE(result.complete()); + EXPECT_EQ(result.fields().size(), 2); + + // Fetch first row + const mysql::row* row = result.fetch_one(errc, info); + validate_no_error(); + ASSERT_NE(row, nullptr); + validate_2fields_meta(result, "two_rows_table"); + EXPECT_EQ(row->values(), makevalues(1, "f0")); + EXPECT_FALSE(result.complete()); + + // Fetch next row + reset_errors(); + row = result.fetch_one(errc, info); + validate_no_error(); + ASSERT_NE(row, nullptr); + validate_2fields_meta(result, "two_rows_table"); + EXPECT_EQ(row->values(), makevalues(2, "f1")); + EXPECT_FALSE(result.complete()); + + // Fetch next: end of resultset + reset_errors(); + row = result.fetch_one(errc, info); + validate_no_error(); + ASSERT_EQ(row, nullptr); + validate_eof(result); +} + +// There seems to be no real case where fetch can fail (other than net fails) + +// FetchOne, sync exc +TEST_P(ResultsetTest, FetchOneSyncExc_TwoRows) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM two_rows_table"); + EXPECT_TRUE(result.valid()); + EXPECT_FALSE(result.complete()); + EXPECT_EQ(result.fields().size(), 2); + + // Fetch first row + const mysql::row* row = result.fetch_one(); + ASSERT_NE(row, nullptr); + validate_2fields_meta(result, "two_rows_table"); + EXPECT_EQ(row->values(), makevalues(1, "f0")); + EXPECT_FALSE(result.complete()); + + // Fetch next row + row = result.fetch_one(); + ASSERT_NE(row, nullptr); + validate_2fields_meta(result, "two_rows_table"); + EXPECT_EQ(row->values(), makevalues(2, "f1")); + EXPECT_FALSE(result.complete()); + + // Fetch next: end of resultset + row = result.fetch_one(); + ASSERT_EQ(row, nullptr); + validate_eof(result); +} + +// FetchOne, async +TEST_P(ResultsetTest, FetchOneAsync_NoResults) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM empty_table"); + + // Already in the end of the resultset, we receive the EOF + auto [info, row] = result.async_fetch_one(net::use_future).get(); + EXPECT_EQ(info, error_info()); + EXPECT_EQ(row, nullptr); + validate_eof(result); + + // Fetching again just returns null + std::tie(info, row) = result.async_fetch_one(net::use_future).get(); + EXPECT_EQ(info, error_info()); + EXPECT_EQ(row, nullptr); + validate_eof(result); +} + +TEST_P(ResultsetTest, FetchOneAsync_OneRow) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM one_row_table"); + + // Fetch only row + auto [info, row] = result.async_fetch_one(net::use_future).get(); + EXPECT_EQ(info, error_info()); + ASSERT_NE(row, nullptr); + EXPECT_EQ(row->values(), makevalues(1, "f0")); + EXPECT_FALSE(result.complete()); + + // Fetch next: end of resultset + reset_errors(); + std::tie(info, row) = result.async_fetch_one(net::use_future).get(); + EXPECT_EQ(info, error_info()); + ASSERT_EQ(row, nullptr); + validate_eof(result); +} + +TEST_P(ResultsetTest, FetchOneAsync_TwoRows) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM two_rows_table"); + + // Fetch first row + auto [info, row] = result.async_fetch_one(net::use_future).get(); + EXPECT_EQ(info, error_info()); + ASSERT_NE(row, nullptr); + EXPECT_EQ(row->values(), makevalues(1, "f0")); + EXPECT_FALSE(result.complete()); + + // Fetch next row + reset_errors(); + std::tie(info, row) = result.async_fetch_one(net::use_future).get(); + EXPECT_EQ(info, error_info()); + ASSERT_NE(row, nullptr); + EXPECT_EQ(row->values(), makevalues(2, "f1")); + EXPECT_FALSE(result.complete()); + + // Fetch next: end of resultset + reset_errors(); + std::tie(info, row) = result.async_fetch_one(net::use_future).get(); + EXPECT_EQ(info, error_info()); + ASSERT_EQ(row, nullptr); + validate_eof(result); +} + +// FetchMany, sync errc +TEST_P(ResultsetTest, FetchManySyncErrc_NoResults) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM empty_table"); + + // Fetch many, but there are no results + auto rows = result.fetch_many(10, errc, info); + validate_no_error(); + EXPECT_TRUE(rows.empty()); + EXPECT_TRUE(result.complete()); + validate_eof(result); + + // Fetch again, should return OK and empty + reset_errors(); + rows = result.fetch_many(10, errc, info); + ASSERT_EQ(errc, error_code()); + EXPECT_EQ(info, error_info()); + EXPECT_TRUE(rows.empty()); + EXPECT_TRUE(result.complete()); + validate_eof(result); +} + +TEST_P(ResultsetTest, FetchManySyncErrc_MoreRowsThanCount) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM three_rows_table"); + + // Fetch 2, one remaining + auto rows = result.fetch_many(2, errc, info); + validate_no_error(); + EXPECT_FALSE(result.complete()); + EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); + + // Fetch another two (completes the resultset) + reset_errors(); + rows = result.fetch_many(2, errc, info); + validate_no_error(); + EXPECT_TRUE(result.complete()); + validate_eof(result); + EXPECT_EQ(rows, (makerows(2, 3, "f2"))); +} + +TEST_P(ResultsetTest, FetchManySyncErrc_LessRowsThanCount) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM two_rows_table"); + + // Fetch 3, resultset exhausted + auto rows = result.fetch_many(3, errc, info); + validate_no_error(); + EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); + validate_eof(result); +} + +TEST_P(ResultsetTest, FetchManySyncErrc_SameRowsAsCount) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM two_rows_table"); + + // Fetch 2, 0 remaining but resultset not exhausted + auto rows = result.fetch_many(2, errc, info); + validate_no_error(); + EXPECT_FALSE(result.complete()); + EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); + + // Fetch again, exhausts the resultset + reset_errors(); + rows = result.fetch_many(2, errc, info); + validate_no_error(); + EXPECT_EQ(rows.size(), 0); + validate_eof(result); +} + +TEST_P(ResultsetTest, FetchManySyncErrc_CountEqualsOne) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM one_row_table"); + + // Fetch 1, 1 remaining + auto rows = result.fetch_many(1, errc, info); + validate_no_error(); + EXPECT_FALSE(result.complete()); + EXPECT_EQ(rows, (makerows(2, 1, "f0"))); +} + +// FetchMany, sync exc +TEST_P(ResultsetTest, FetchManySyncExc_MoreRowsThanCount) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM three_rows_table"); + + // Fetch 2, one remaining + auto rows = result.fetch_many(2); + EXPECT_FALSE(result.complete()); + EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); + + // Fetch another two (completes the resultset) + rows = result.fetch_many(2); + validate_eof(result); + EXPECT_EQ(rows, (makerows(2, 3, "f2"))); + + // Fetching another time returns empty + rows = result.fetch_many(2); + EXPECT_EQ(rows.size(), 0); +} + +// FetchMany, async +TEST_P(ResultsetTest, FetchManyAsync_NoResults) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM empty_table"); + + // Fetch many, but there are no results + auto [info, rows] = result.async_fetch_many(10, net::use_future).get(); + EXPECT_EQ(info, error_info()); + EXPECT_TRUE(rows.empty()); + EXPECT_TRUE(result.complete()); + validate_eof(result); + + // Fetch again, should return OK and empty + reset_errors(); + std::tie(info, rows) = result.async_fetch_many(10, net::use_future).get(); + EXPECT_EQ(info, error_info()); + EXPECT_TRUE(rows.empty()); + EXPECT_TRUE(result.complete()); + validate_eof(result); +} + +TEST_P(ResultsetTest, FetchManyAsync_MoreRowsThanCount) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM three_rows_table"); + + // Fetch 2, one remaining + auto [info, rows] = result.async_fetch_many(2, net::use_future).get(); + EXPECT_EQ(info, error_info()); + EXPECT_FALSE(result.complete()); + EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); + + // Fetch another two (completes the resultset) + std::tie(info, rows) = result.async_fetch_many(2, net::use_future).get(); + EXPECT_EQ(info, error_info()); + EXPECT_TRUE(result.complete()); + validate_eof(result); + EXPECT_EQ(rows, (makerows(2, 3, "f2"))); +} + +TEST_P(ResultsetTest, FetchManyAsync_LessRowsThanCount) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM two_rows_table"); + + // Fetch 3, resultset exhausted + auto [info, rows] = result.async_fetch_many(3, net::use_future).get(); + EXPECT_EQ(info, error_info()); + EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); + validate_eof(result); +} + +TEST_P(ResultsetTest, FetchManyAsync_SameRowsAsCount) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM two_rows_table"); + + // Fetch 2, 0 remaining but resultset not exhausted + auto [info, rows] = result.async_fetch_many(2, net::use_future).get(); + EXPECT_EQ(info, error_info()); + EXPECT_FALSE(result.complete()); + EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); + + // Fetch again, exhausts the resultset + reset_errors(); + std::tie(info, rows) = result.async_fetch_many(2, net::use_future).get(); + EXPECT_EQ(info, error_info()); + EXPECT_EQ(rows.size(), 0); + validate_eof(result); +} + +TEST_P(ResultsetTest, FetchManyAsync_CountEqualsOne) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM one_row_table"); + + // Fetch 1, 1 remaining + auto [info, rows] = result.async_fetch_many(1, net::use_future).get(); + EXPECT_EQ(info, error_info()); + EXPECT_FALSE(result.complete()); + EXPECT_EQ(rows, (makerows(2, 1, "f0"))); +} + +// FetchAll, sync errc +TEST_P(ResultsetTest, FetchAllSyncErrc_NoResults) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM empty_table"); + + // Fetch many, but there are no results + auto rows = result.fetch_all(errc, info); + validate_no_error(); + EXPECT_TRUE(rows.empty()); + EXPECT_TRUE(result.complete()); + + // Fetch again, should return OK and empty + reset_errors(); + rows = result.fetch_all(errc, info); + validate_no_error(); + EXPECT_TRUE(rows.empty()); + validate_eof(result); +} + +TEST_P(ResultsetTest, FetchAllSyncErrc_OneRow) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM one_row_table"); + + auto rows = result.fetch_all(errc, info); + validate_no_error(); + EXPECT_TRUE(result.complete()); + EXPECT_EQ(rows, (makerows(2, 1, "f0"))); +} + +TEST_P(ResultsetTest, FetchAllSyncErrc_SeveralRows) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM two_rows_table"); + + auto rows = result.fetch_all(errc, info); + validate_no_error(); + validate_eof(result); + EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); +} + +// FetchAll, sync exc +TEST_P(ResultsetTest, FetchAllSyncExc_SeveralRows) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM two_rows_table"); + auto rows = result.fetch_all(); + validate_eof(result); + EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); +} + +// FetchAll, async +TEST_P(ResultsetTest, FetchAllAsync_NoResults) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM empty_table"); + + // Fetch many, but there are no results + auto [info, rows] = result.async_fetch_all(net::use_future).get(); + EXPECT_EQ(info, error_info()); + EXPECT_TRUE(rows.empty()); + EXPECT_TRUE(result.complete()); + + // Fetch again, should return OK and empty + reset_errors(); + std::tie(info, rows) = result.async_fetch_all(net::use_future).get(); + EXPECT_EQ(info, error_info()); + EXPECT_TRUE(rows.empty()); + validate_eof(result); +} + +TEST_P(ResultsetTest, FetchAllAsync_OneRow) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM one_row_table"); + + auto [info, rows] = result.async_fetch_all(net::use_future).get(); + EXPECT_EQ(info, error_info()); + EXPECT_TRUE(result.complete()); + EXPECT_EQ(rows, (makerows(2, 1, "f0"))); +} + +TEST_P(ResultsetTest, FetchAllAsync_SeveralRows) +{ + auto result = GetParam().generate_resultset(conn, "SELECT * FROM two_rows_table"); + + auto [info, rows] = result.async_fetch_all(net::use_future).get(); + EXPECT_EQ(info, error_info()); + validate_eof(result); + EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1"))); +} + +mysql::tcp_resultset make_text_resultset(mysql::tcp_connection& conn, std::string_view query) +{ + return conn.query(query); +} + +mysql::tcp_resultset make_binary_resultset(mysql::tcp_connection& conn, std::string_view query) +{ + return conn.prepare_statement(query).execute(mysql::no_statement_params); +} + + +INSTANTIATE_TEST_SUITE_P(Default, ResultsetTest, testing::Values( + ResultsetTestParam("text", &make_text_resultset), + ResultsetTestParam("binary", &make_binary_resultset) +), test_name_generator); + +} + diff --git a/test/unit/binary_deserialization.cpp b/test/unit/binary_deserialization.cpp index 5f2157b2..40205994 100644 --- a/test/unit/binary_deserialization.cpp +++ b/test/unit/binary_deserialization.cpp @@ -175,20 +175,20 @@ TEST_P(DeserializeBinaryRowTest, CorrectFormat_SetsOutputValueReturnsTrue) } INSTANTIATE_TEST_SUITE_P(Default, DeserializeBinaryRowTest, testing::Values( - BinaryRowParam("one_value", {0x00, 0x14}, makevalues(std::int32_t(20)), {protocol_field_type::tiny}), - BinaryRowParam("one_null", {0x04}, makevalues(nullptr), {protocol_field_type::tiny}), - BinaryRowParam("two_values", {0x00, 0x03, 0x6d, 0x69, 0x6e, 0x6d, 0x07}, + BinaryRowParam("one_value", {0x00, 0x00, 0x14}, makevalues(std::int32_t(20)), {protocol_field_type::tiny}), + BinaryRowParam("one_null", {0x00, 0x04}, makevalues(nullptr), {protocol_field_type::tiny}), + BinaryRowParam("two_values", {0x00, 0x00, 0x03, 0x6d, 0x69, 0x6e, 0x6d, 0x07}, makevalues("min", std::int32_t(1901)), {protocol_field_type::var_string, protocol_field_type::short_}), - BinaryRowParam("one_value_one_null", {0x08, 0x03, 0x6d, 0x61, 0x78}, + BinaryRowParam("one_value_one_null", {0x00, 0x08, 0x03, 0x6d, 0x61, 0x78}, makevalues("max", nullptr), {protocol_field_type::var_string, protocol_field_type::tiny}), - BinaryRowParam("two_nulls", {0x0c}, + BinaryRowParam("two_nulls", {0x00, 0x0c}, makevalues(nullptr, nullptr), {protocol_field_type::tiny, protocol_field_type::tiny}), - BinaryRowParam("six_nulls", {0xfc}, std::vector(6, value(nullptr)), + BinaryRowParam("six_nulls", {0x00, 0xfc}, std::vector(6, value(nullptr)), std::vector(6, protocol_field_type::tiny)), - BinaryRowParam("seven_nulls", {0xfc, 0x01}, std::vector(7, value(nullptr)), + BinaryRowParam("seven_nulls", {0x00, 0xfc, 0x01}, std::vector(7, value(nullptr)), std::vector(7, protocol_field_type::tiny)), BinaryRowParam("several_values", { - 0x90, 0x00, 0xfd, 0x14, 0x00, 0xc3, 0xf5, 0x48, + 0x00, 0x90, 0x00, 0xfd, 0x14, 0x00, 0xc3, 0xf5, 0x48, 0x40, 0x02, 0x61, 0x62, 0x04, 0xe2, 0x07, 0x0a, 0x05, 0x71, 0x99, 0x6d, 0xe2, 0x93, 0x4d, 0xf5, 0x3d @@ -250,15 +250,15 @@ TEST_P(DeserializeBinaryRowErrorTest, ErrorCondition_ReturnsErrorCode) } INSTANTIATE_TEST_SUITE_P(Default, DeserializeBinaryRowErrorTest, testing::Values( - BinaryRowErrorParam("no_space_null_bitmap_1", {}, Error::incomplete_message, {protocol_field_type::tiny}), - BinaryRowErrorParam("no_space_null_bitmap_2", {0xfc}, Error::incomplete_message, + BinaryRowErrorParam("no_space_null_bitmap_1", {0x00}, Error::incomplete_message, {protocol_field_type::tiny}), + BinaryRowErrorParam("no_space_null_bitmap_2", {0x00, 0xfc}, Error::incomplete_message, std::vector(7, protocol_field_type::tiny)), - BinaryRowErrorParam("no_space_value_single", {0x00}, Error::incomplete_message, {protocol_field_type::tiny}), - BinaryRowErrorParam("no_space_value_last", {0x00, 0x01}, Error::incomplete_message, + BinaryRowErrorParam("no_space_value_single", {0x00, 0x00}, Error::incomplete_message, {protocol_field_type::tiny}), + BinaryRowErrorParam("no_space_value_last", {0x00, 0x00, 0x01}, Error::incomplete_message, std::vector(2, protocol_field_type::tiny)), - BinaryRowErrorParam("no_space_value_middle", {0x00, 0x01}, Error::incomplete_message, + BinaryRowErrorParam("no_space_value_middle", {0x00, 0x00, 0x01}, Error::incomplete_message, std::vector(3, protocol_field_type::tiny)), - BinaryRowErrorParam("extra_bytes", {0x00, 0x01, 0x02}, Error::extra_bytes, {protocol_field_type::tiny}) + BinaryRowErrorParam("extra_bytes", {0x00, 0x00, 0x01, 0x02}, Error::extra_bytes, {protocol_field_type::tiny}) ), test_name_generator);