diff --git a/include/mysql/error.hpp b/include/mysql/error.hpp index 4d65018a..7fd47bea 100644 --- a/include/mysql/error.hpp +++ b/include/mysql/error.hpp @@ -22,7 +22,8 @@ enum class Error : int sequence_number_mismatch, server_unsupported, protocol_value_error, - unknown_auth_plugin + unknown_auth_plugin, + wrong_num_params }; /// An alias for boost::system error codes. diff --git a/include/mysql/impl/error.hpp b/include/mysql/impl/error.hpp index e6db3008..33bd3a6e 100644 --- a/include/mysql/impl/error.hpp +++ b/include/mysql/impl/error.hpp @@ -35,6 +35,7 @@ inline const char* error_to_string(Error error) noexcept case Error::server_unsupported: return "The server does not implement the minimum features to be supported"; case Error::protocol_value_error: return "A field in a message had an unexpected value"; case Error::unknown_auth_plugin: return "The user employs an authentication plugin unknown to the client"; + case Error::wrong_num_params: return "The provided parameter count does not match the prepared statement parameter count"; #include "mysql/impl/server_error_descriptions.hpp" diff --git a/include/mysql/impl/network_algorithms/prepare_statement.ipp b/include/mysql/impl/network_algorithms/prepare_statement.ipp index 94ac323d..9494ccbc 100644 --- a/include/mysql/impl/network_algorithms/prepare_statement.ipp +++ b/include/mysql/impl/network_algorithms/prepare_statement.ipp @@ -29,21 +29,32 @@ void mysql::detail::prepare_statement( std::uint8_t msg_type = 0; std::tie(err, msg_type) = deserialize_message_type(ctx); if (err) return; - if (msg_type == 0) - { - com_stmt_prepare_ok_packet response; - err = deserialize_message(response, ctx); - if (err) return; - output = prepared_statement(channel, response); - } - else if (msg_type == error_packet_header) + + if (msg_type == error_packet_header) { err = process_error_packet(ctx, info); + return; } - else + else if (msg_type != 0) { err = make_error_code(Error::protocol_value_error); + return; } + + com_stmt_prepare_ok_packet response; + err = deserialize_message(response, ctx); + if (err) return; + + // Server sends now one packet per parameter and field. + // We ignore these for now. TODO: do sth useful with these + for (unsigned i = 0; i < response.num_columns.value + response.num_params.value; ++i) + { + channel.read(buff, err); + if (err) return; + } + + // Compose response + output = prepared_statement(channel, response); } diff --git a/include/mysql/impl/prepared_statement.ipp b/include/mysql/impl/prepared_statement.ipp index a0351880..7e280226 100644 --- a/include/mysql/impl/prepared_statement.ipp +++ b/include/mysql/impl/prepared_statement.ipp @@ -2,6 +2,7 @@ #define INCLUDE_MYSQL_IMPL_PREPARED_STATEMENT_IPP_ #include "mysql/impl/network_algorithms/execute_statement.hpp" +#include "mysql/impl/stringize.hpp" template template @@ -15,15 +16,29 @@ mysql::resultset mysql::prepared_statement::execute( assert(valid()); mysql::resultset res; - detail::execute_statement( - *channel_, - stmt_msg_.statement_id.value, - params_first, - params_last, - res, - err, - info - ); + + auto param_count = std::distance(params_first, params_last); + if (param_count != num_params()) + { + err = detail::make_error_code(Error::wrong_num_params); + info.set_message(detail::stringize( + "prepared_statement::execute: expected ", num_params(), " params, but got ", param_count)); + } + else + { + err.clear(); + info.clear(); + detail::execute_statement( + *channel_, + stmt_msg_.statement_id.value, + params_first, + params_last, + res, + err, + info + ); + } + return res; } diff --git a/include/mysql/impl/stringize.hpp b/include/mysql/impl/stringize.hpp new file mode 100644 index 00000000..093194f1 --- /dev/null +++ b/include/mysql/impl/stringize.hpp @@ -0,0 +1,25 @@ +#ifndef INCLUDE_MYSQL_IMPL_STRINGIZE_HPP_ +#define INCLUDE_MYSQL_IMPL_STRINGIZE_HPP_ + +#include +#include + +namespace mysql +{ +namespace detail +{ + +template +std::string stringize(const Types&... inputs) +{ + std::ostringstream ss; + (ss << ... << inputs); + return ss.str(); +} + +} +} + + + +#endif /* INCLUDE_MYSQL_IMPL_STRINGIZE_HPP_ */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f4b6455c..b7dbd4ee 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -59,6 +59,7 @@ add_executable( integration/handshake.cpp integration/query.cpp integration/prepare_statement.cpp + integration/execute_statement.cpp integration/query_types.cpp ) target_link_libraries( diff --git a/test/integration/execute_statement.cpp b/test/integration/execute_statement.cpp new file mode 100644 index 00000000..46389acb --- /dev/null +++ b/test/integration/execute_statement.cpp @@ -0,0 +1,56 @@ +/* + * execute_statement.cpp + * + * Created on: Feb 5, 2020 + * Author: ruben + */ + +#include "integration_test_common.hpp" +#include + +using namespace mysql::test; +using mysql::value; +using mysql::Error; + +namespace +{ + +struct ExecuteStatementTest : public IntegTestAfterHandshake +{ +}; + +TEST_F(ExecuteStatementTest, IteratorsSyncErrc_OkNoParams) +{ + std::forward_list params; + auto stmt = conn.prepare_statement("SELECT * FROM empty_table"); + auto result = stmt.execute(params.begin(), params.end(), errc, info); + validate_no_error(); + EXPECT_TRUE(result.valid()); +} + +TEST_F(ExecuteStatementTest, IteratorsSyncErrc_OkWithParams) +{ + std::forward_list params { value("item"), value(42) }; + auto stmt = conn.prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)"); + auto result = stmt.execute(params.begin(), params.end(), errc, info); + validate_no_error(); + EXPECT_TRUE(result.valid()); +} + +TEST_F(ExecuteStatementTest, IteratorsSyncErrc_Error) +{ + std::forward_list params { value("item") }; + auto stmt = conn.prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)"); + auto result = stmt.execute(params.begin(), params.end(), errc, info); + validate_sync_fail(Error::wrong_num_params, {"param", "2", "1", "statement", "execute"}); + EXPECT_FALSE(result.valid()); +} + +// prepared_statement::execute +// OK, no params +// OK, with params +// OK, select, insert, update, delete +// Error, wrong number of parameters +// Collection version + +}