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