diff --git a/TODO.txt b/TODO.txt index ab6ccd89..c6a4baec 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,3 +1,10 @@ +Change sigs + Add tests with error_info*=nullptr + Make tests pass a non-empty error_info + Change async_fetch_one to not use coroutines + Take fetch_many() algorithm out into network_algorithms (e.g. read_many_rows) + clear_errors() function to clear error_info and error_code at once + Concept checking for async completion tokens Multiresultset Text protocol Binary protocol (stored procedures) @@ -26,16 +33,12 @@ Technical debt Review async initiations See if async_initiate is applicable See if we can drop the dependence on beast - See if we can just declare the functions as auto - Test things with futures Review close() of the stream on error Review valid() for moved-from resultsets (should it return always true?) Force the same number of values in each row as in fields() CMake exporting? Integration test for network errors (e.g. host unreachable) - Take fetch_many() algorithm out into network_algorithms (e.g. read_many_rows) Test zero dates - clear_errors() function to clear error_info and error_code at once Rework deserialize_row_fn to allow cursors Test prepared statement binding to procedure out params More thorough testing for several NULLs in integration testing diff --git a/examples/query_async_callbacks.cpp b/examples/query_async_callbacks.cpp index 4da8ed2b..a33345b5 100644 --- a/examples/query_async_callbacks.cpp +++ b/examples/query_async_callbacks.cpp @@ -8,7 +8,6 @@ using boost::mysql::error_code; using boost::mysql::error_info; -using boost::mysql::async_handler_arg; using boost::mysql::tcp_resultset; using boost::mysql::owning_row; @@ -22,6 +21,23 @@ using boost::mysql::owning_row; * This example assumes you are already familiar with the basic concepts * of mysql-asio (tcp_connection, resultset, rows, values). If you are not, * please have a look to the query_sync.cpp example. + * + * In this library, all asynchronous operations follow Boost.Asio universal + * asynchronous models, and thus may be used with callbacks, coroutines or futures. + * The handler signature is always one of: + * - void(error_code): for operations that do not have a "return type" (e.g. handshake) + * - void(error_code, T): for operations that have a "return type" (e.g. query, for which + * T = resultset). + * + * All asynchronous operations accept a last optional error_info* parameter. error_info + * contains additional diagnostic information returned by the server. If you + * pass a non-nullptr value, it will be populated in case of error if any extra information + * is available. + * + * Design note: handler signatures in Boost.Asio should have two parameters, at + * most, and the first one should be an error_code - otherwise some of the asynchronous + * features (e.g. coroutines) won't work. This is why error_info is not part of any + * of the handler signatures. */ void print_employee(const boost::mysql::row& employee) @@ -47,11 +63,12 @@ void die_on_error( class application { - boost::asio::ip::tcp::endpoint ep; - boost::mysql::connection_params conn_params; - boost::asio::io_context ctx; - boost::mysql::tcp_connection connection; - boost::mysql::tcp_resultset resultset; + boost::asio::ip::tcp::endpoint ep; // Physical endpoint to connect to + boost::mysql::connection_params conn_params; // MySQL credentials and other connection config + boost::asio::io_context ctx; // boost::asio context + boost::mysql::tcp_connection connection; // Represents the connection to the MySQL server + boost::mysql::tcp_resultset resultset; // A result from a query + boost::mysql::error_info additional_info; // Will be populated with additional information about any errors public: application(const char* username, const char* password) : ep (boost::asio::ip::address_v4::loopback(), boost::mysql::default_port), @@ -66,59 +83,53 @@ public: { connection.next_layer().async_connect(ep, [this](error_code err) { die_on_error(err); - connection.async_handshake(conn_params, [this](error_code err, const error_info& info) { - die_on_error(err, info); + connection.async_handshake(conn_params, [this](error_code err) { + die_on_error(err, additional_info); query_employees(); - }); + }, &additional_info); }); } void query_employees() { const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'"; - connection.async_query(sql, [this](error_code err, - async_handler_arg&& result - ) { - die_on_error(err, result.error()); - resultset = std::move(result.get()); - resultset.async_fetch_all([this](error_code err, - const async_handler_arg>& rows) { - die_on_error(err, rows.error()); - for (const auto& employee: rows.get()) + connection.async_query(sql, [this](error_code err, tcp_resultset&& result) { + die_on_error(err, additional_info); + resultset = std::move(result); + resultset.async_fetch_all([this](error_code err, const std::vector& rows) { + die_on_error(err, additional_info); + for (const auto& employee: rows) { print_employee(employee); } update_slacker(); - }); - }); + }, &additional_info); + }, &additional_info); } void update_slacker() { const char* sql = "UPDATE employee SET salary = 15000 WHERE last_name = 'Slacker'"; - connection.async_query(sql, [this](error_code err, - async_handler_arg&& result) { - die_on_error(err, result.error()); - assert(result.get().fields().size() == 0); + connection.async_query(sql, [this](error_code err, tcp_resultset&& result) { + die_on_error(err, additional_info); + assert(result.fields().size() == 0); query_intern(); - }); + }, &additional_info); } void query_intern() { const char* sql = "SELECT salary FROM employee WHERE last_name = 'Slacker'"; - connection.async_query(sql, [this](error_code err, - async_handler_arg&& result) { - die_on_error(err, result.error()); - resultset = std::move(result.get()); - resultset.async_fetch_all([](error_code err, - const async_handler_arg>& rows) { - die_on_error(err, rows.error()); - assert(rows.get().size() == 1); - [[maybe_unused]] auto salary = std::get(rows.get()[0].values()[0]); + connection.async_query(sql, [this](error_code err, tcp_resultset&& result) { + die_on_error(err, additional_info); + resultset = std::move(result); + resultset.async_fetch_all([this](error_code err, const std::vector& rows) { + die_on_error(err, additional_info); + assert(rows.size() == 1); + [[maybe_unused]] auto salary = std::get(rows[0].values()[0]); assert(salary == 15000); - }); - }); + }, &additional_info); + }, &additional_info); } auto& context() { return ctx; } diff --git a/examples/query_async_coroutines.cpp b/examples/query_async_coroutines.cpp index e00f637e..64be1754 100644 --- a/examples/query_async_coroutines.cpp +++ b/examples/query_async_coroutines.cpp @@ -18,6 +18,23 @@ using boost::mysql::error_info; * This example assumes you are already familiar with the basic concepts * of mysql-asio (tcp_connection, resultset, rows, values). If you are not, * please have a look to the query_sync.cpp example. + * + * In this library, all asynchronous operations follow Boost.Asio universal + * asynchronous models, and thus may be used with callbacks, coroutines or futures. + * The handler signature is always one of: + * - void(error_code): for operations that do not have a "return type" (e.g. handshake) + * - void(error_code, T): for operations that have a "return type" (e.g. query, for which + * T = resultset). + * + * All asynchronous operations accept a last optional error_info* parameter. error_info + * contains additional diagnostic information returned by the server. If you + * pass a non-nullptr value, it will be populated in case of error if any extra information + * is available. + * + * Design note: handler signatures in Boost.Asio should have two parameters, at + * most, and the first one should be an error_code - otherwise some of the asynchronous + * features (e.g. coroutines) won't work. This is why error_info is not part of any + * of the handler signatures. */ void print_employee(const boost::mysql::row& employee) @@ -74,38 +91,29 @@ void main_impl(int argc, char** argv) * the coroutine will resume in the point it was left. * * The return type of a coroutine is the second argument to the handler signature - * for the asynchronous operation. For example, connection::connect has a handler - * signature of void(error_code, error_info), so the coroutine return type is error_info. + * for the asynchronous operation. For example, connection::query has a handler + * signature of void(error_code, resultset), so the coroutine return + * type is resultset. * - * Coroutines are limited to returning a single argument, so all handler signatures - * in boost::mysql are limited to two arguments. In boost::mysql, coroutines may return: - * - error_info. Provides additional information in case of error. - * - async_handler_arg. A combination of a value of type T and an error_info. - * Used by functions like connection::async_query(), which has to transmit - * a resultset as a return value, in addition to the error_info. */ boost::asio::spawn(ctx.get_executor(), [&conn, ep, params](boost::asio::yield_context yield) { - // This error_code will be filled if an operation fails. We will check it - // for every operation we perform. + // This error_code and error_info will be filled if an + // operation fails. We will check them for every operation we perform. boost::mysql::error_code ec; + boost::mysql::error_info additional_info; // TCP connect conn.next_layer().async_connect(ep, yield[ec]); check_error(ec); - // MySQL handshake. Note that if the operation would fail, - // the returned error_info would contain additional information about what happened - boost::mysql::error_info errinfo = conn.async_handshake(params, yield[ec]); - check_error(ec, errinfo); + // MySQL handshake + conn.async_handshake(params, yield[ec], &additional_info); + check_error(ec, additional_info); - // Issue the query to the server. This returns an async_handler_arg, - // which contains an error_info and a tcp_resultset. Call async_handler_arg::error() - // to obtain the error_info, which will contain additional info in case of error. - // async_handler_arg::get() returns the actual resultset. + // Issue the query to the server const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'"; - boost::mysql::async_handler_arg result = - conn.async_query(sql, yield[ec]); - check_error(ec, result.error()); // The error_info + boost::mysql::tcp_resultset result = conn.async_query(sql, yield[ec], &additional_info); + check_error(ec, additional_info); /** * Get all rows in the resultset. We will employ resultset::async_fetch_one(), @@ -116,11 +124,10 @@ void main_impl(int argc, char** argv) */ while (true) { - boost::mysql::async_handler_arg row = - result.get().async_fetch_one(yield[ec]); - check_error(ec, row.error()); - if (!row.get()) break; // No more rows available - print_employee(*row.get()); + const boost::mysql::row* row = result.async_fetch_one(yield[ec], &additional_info); + check_error(ec, additional_info); + if (!row) break; // No more rows available + print_employee(*row); } }); diff --git a/examples/query_async_futures.cpp b/examples/query_async_futures.cpp index a4408dca..ef7c707e 100644 --- a/examples/query_async_futures.cpp +++ b/examples/query_async_futures.cpp @@ -7,7 +7,6 @@ #include using boost::mysql::error_code; -using boost::mysql::error_info; using boost::asio::use_future; /** @@ -20,6 +19,23 @@ using boost::asio::use_future; * This example assumes you are already familiar with the basic concepts * of mysql-asio (tcp_connection, resultset, rows, values). If you are not, * please have a look to the query_sync.cpp example. + * + * In this library, all asynchronous operations follow Boost.Asio universal + * asynchronous models, and thus may be used with callbacks, coroutines or futures. + * The handler signature is always one of: + * - void(error_code): for operations that do not have a "return type" (e.g. handshake) + * - void(error_code, T): for operations that have a "return type" (e.g. query, for which + * T = resultset). + * + * All asynchronous operations accept a last optional error_info* parameter. error_info + * contains additional diagnostic information returned by the server. If you + * pass a non-nullptr value, it will be populated in case of error if any extra information + * is available. + * + * Design note: handler signatures in Boost.Asio should have two parameters, at + * most, and the first one should be an error_code - otherwise some of the asynchronous + * features (e.g. coroutines) won't work. This is why error_info is not part of any + * of the handler signatures. */ void print_employee(const boost::mysql::row& employee) @@ -89,27 +105,14 @@ void main_impl(int argc, char** argv) * Perform the MySQL handshake. Calling async_handshake triggers the * operation, and calling future::get() blocks the current thread until * it completes. get() will throw an exception if the operation fails. - * - * For compatibility with other async methods, futures may return an - * error_info object. However, this would only contain information - * in case of error, and in that case get() would throw. Thus, - * the returned error_info is always empty. */ - std::future fut2 = - conn.async_handshake(params, use_future); - fut2.get(); + fut = conn.async_handshake(params, use_future); + fut.get(); - - /** - * Issue the query to the server. The returned value is an async_handler_arg, - * which is a resultset plus an error_info, which is also empty. - */ + // Issue the query to the server const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'"; - std::future> resultset_fut = - conn.async_query(sql, use_future); - - // First get() is for the future, second is for the async_handler_arg - boost::mysql::tcp_resultset result = resultset_fut.get().get(); + std::future resultset_fut = conn.async_query(sql, use_future); + boost::mysql::tcp_resultset result = resultset_fut.get(); /** * Get all rows in the resultset. We will employ resultset::async_fetch_one(), @@ -118,7 +121,7 @@ void main_impl(int argc, char** argv) * rows remain valid until the next call to async_fetch_one(). When no more * rows are available, async_fetch_one returns nullptr. */ - while (const boost::mysql::row* current_row = result.async_fetch_one(use_future).get().get()) + while (const boost::mysql::row* current_row = result.async_fetch_one(use_future).get()) { print_employee(*current_row); } diff --git a/include/boost/mysql/async_handler_arg.hpp b/include/boost/mysql/async_handler_arg.hpp deleted file mode 100644 index 465fa463..00000000 --- a/include/boost/mysql/async_handler_arg.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef INCLUDE_BOOST_MYSQL_ASYNC_HANDLER_ARG_HPP_ -#define INCLUDE_BOOST_MYSQL_ASYNC_HANDLER_ARG_HPP_ - -#include "boost/mysql/error.hpp" -#include - -namespace boost { -namespace mysql { - -/** - * \brief An error_info plus another type. - * \details This type is intended to be used as second argument in - * an asynchronous handler signature, in cases where both an error_info - * and another type must be transmitted. - * - * In order for Boost.Asio universal asynchronous primitives to work, all - * handler signatures should have two arguments, at most, and the first one - * should be an error_code. Concretely, stackful coroutines (boost::asio::yield_context) - * do NOT support handlers with more than two arguments. However, many handlers - * in this library need to transmit three arguments: an error_code, an error_info - * and another type (e.g. a resultset, a prepared_statement...). This class is - * intended to be used as second argument in these handlers. It is similar to - * a std::pair, but accessor names make more sense. - * - * This class is NOT intended to be created by the user - the library - * will pass it to your asynchronous handlers. - * - * async_handler_arg supports default cnstruction, copy and move operations as - * long as T supports them. - */ -template -class async_handler_arg -{ - error_info err_; - T value_; -public: - /// The type T. - using value_type = T; - - /// Default constructor. - constexpr async_handler_arg() = default; - - // Private, do not use - constexpr async_handler_arg(error_info&& info): - err_(std::move(info)) {} - - // Private, do not use - template - constexpr async_handler_arg(Args&&... args): - value_(std::forward(args)...) {} - - /// Retrieves the stored error_info. - const error_info& error() const noexcept { return err_; } - - /// Retrieves the stored value (const version). - const T& get() const & noexcept { return value_; } - - /// Retrieves the stored value (non-const version). - T& get() & noexcept { return value_; } - - /// Retrieves the stored value (rvalue version). - T&& get() && noexcept { return std::move(value_); } -}; - - -} // mysql -} // boost - - - -#endif /* INCLUDE_BOOST_MYSQL_ASYNC_HANDLER_ARG_HPP_ */ diff --git a/include/boost/mysql/connection.hpp b/include/boost/mysql/connection.hpp index bfb2b627..14b3b003 100644 --- a/include/boost/mysql/connection.hpp +++ b/include/boost/mysql/connection.hpp @@ -6,7 +6,6 @@ #include "boost/mysql/detail/protocol/protocol_types.hpp" #include "boost/mysql/error.hpp" #include "boost/mysql/resultset.hpp" -#include "boost/mysql/async_handler_arg.hpp" #include "boost/mysql/prepared_statement.hpp" #include @@ -94,7 +93,7 @@ public: void handshake(const connection_params& params); /// Handler signature for handshake. - using handshake_signature = void(error_code, error_info); + using handshake_signature = void(error_code); /** * \brief Performs the MySQL-level handshake (asynchronous version). @@ -103,7 +102,7 @@ public: */ template BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, handshake_signature) - async_handshake(const connection_params& params, CompletionToken&& token); + async_handshake(const connection_params& params, CompletionToken&& token, error_info* info = nullptr); /** * \brief Executes a SQL text query (sync with error code version). @@ -125,12 +124,12 @@ public: resultset query(std::string_view query_string); /// Handler signature for query. - using query_signature = void(error_code, async_handler_arg>); + using query_signature = void(error_code, resultset); /// Executes a SQL text query (async version). template BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, query_signature) - async_query(std::string_view query_string, CompletionToken&& token); + async_query(std::string_view query_string, CompletionToken&& token, error_info* info=nullptr); /** * \brief Prepares a statement in the server (sync with error code version). @@ -149,12 +148,12 @@ public: prepared_statement prepare_statement(std::string_view statement); /// Handler signature for prepare_statement. - using prepare_statement_signature = void(error_code, async_handler_arg>); + using prepare_statement_signature = void(error_code, prepared_statement); /// Prepares a statement (async version). template BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, prepare_statement_signature) - async_prepare_statement(std::string_view statement, CompletionToken&& token); + async_prepare_statement(std::string_view statement, CompletionToken&& token, error_info* info=nullptr); }; /// A connection to MySQL over TCP. diff --git a/include/boost/mysql/detail/network_algorithms/close_statement.hpp b/include/boost/mysql/detail/network_algorithms/close_statement.hpp index fdd22a93..f559cc71 100644 --- a/include/boost/mysql/detail/network_algorithms/close_statement.hpp +++ b/include/boost/mysql/detail/network_algorithms/close_statement.hpp @@ -22,7 +22,8 @@ BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, close_signature) async_close_statement( channel& chan, std::uint32_t statement_id, - CompletionToken&& token + CompletionToken&& token, + error_info* info ); } // detail diff --git a/include/boost/mysql/detail/network_algorithms/common.hpp b/include/boost/mysql/detail/network_algorithms/common.hpp index bddf0646..bcbd66b8 100644 --- a/include/boost/mysql/detail/network_algorithms/common.hpp +++ b/include/boost/mysql/detail/network_algorithms/common.hpp @@ -4,7 +4,6 @@ #include #include #include "boost/mysql/error.hpp" -#include "boost/mysql/async_handler_arg.hpp" #include "boost/mysql/metadata.hpp" #include "boost/mysql/detail/protocol/channel.hpp" #include "boost/mysql/detail/protocol/common_messages.hpp" @@ -19,10 +18,10 @@ using deserialize_row_fn = error_code (*)( std::vector& ); -using empty_signature = void(error_code, error_info); +using empty_signature = void(error_code); template -using r_handler_signature = void(error_code, async_handler_arg); +using r_handler_signature = void(error_code, T); } // detail } // mysql diff --git a/include/boost/mysql/detail/network_algorithms/execute_generic.hpp b/include/boost/mysql/detail/network_algorithms/execute_generic.hpp index a7f20fff..58e061b1 100644 --- a/include/boost/mysql/detail/network_algorithms/execute_generic.hpp +++ b/include/boost/mysql/detail/network_algorithms/execute_generic.hpp @@ -20,7 +20,7 @@ void execute_generic( ); template -using execute_generic_signature = r_handler_signature>; +using execute_generic_signature = void(error_code, resultset); template BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, execute_generic_signature) @@ -28,7 +28,8 @@ async_execute_generic( deserialize_row_fn deserializer, channel& chan, const Serializable& request, - CompletionToken&& token + CompletionToken&& token, + error_info* info ); } // detail diff --git a/include/boost/mysql/detail/network_algorithms/execute_query.hpp b/include/boost/mysql/detail/network_algorithms/execute_query.hpp index 734b228c..a531dcbc 100644 --- a/include/boost/mysql/detail/network_algorithms/execute_query.hpp +++ b/include/boost/mysql/detail/network_algorithms/execute_query.hpp @@ -24,7 +24,8 @@ BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, execute_generic_signature& chan, std::string_view query, - CompletionToken&& token + CompletionToken&& token, + error_info* info ); } diff --git a/include/boost/mysql/detail/network_algorithms/execute_statement.hpp b/include/boost/mysql/detail/network_algorithms/execute_statement.hpp index 119a9400..fdaddf4d 100644 --- a/include/boost/mysql/detail/network_algorithms/execute_statement.hpp +++ b/include/boost/mysql/detail/network_algorithms/execute_statement.hpp @@ -28,7 +28,8 @@ async_execute_statement( std::uint32_t statement_id, ForwardIterator params_begin, ForwardIterator params_end, - CompletionToken&& token + CompletionToken&& token, + error_info* info ); } // detail diff --git a/include/boost/mysql/detail/network_algorithms/handshake.hpp b/include/boost/mysql/detail/network_algorithms/handshake.hpp index 05a31672..c7a0e6ad 100644 --- a/include/boost/mysql/detail/network_algorithms/handshake.hpp +++ b/include/boost/mysql/detail/network_algorithms/handshake.hpp @@ -34,7 +34,8 @@ BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, handshake_signature) async_handshake( channel& channel, const handshake_params& params, - CompletionToken&& token + CompletionToken&& token, + error_info* info ); } // detail diff --git a/include/boost/mysql/detail/network_algorithms/impl/close_statement.hpp b/include/boost/mysql/detail/network_algorithms/impl/close_statement.hpp index d7161c0f..71e45359 100644 --- a/include/boost/mysql/detail/network_algorithms/impl/close_statement.hpp +++ b/include/boost/mysql/detail/network_algorithms/impl/close_statement.hpp @@ -1,8 +1,6 @@ #ifndef INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_CLOSE_STATEMENT_HPP_ #define INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_CLOSE_STATEMENT_HPP_ -#include -#include #include "boost/mysql/detail/protocol/prepared_statement_messages.hpp" template @@ -32,7 +30,8 @@ BOOST_ASIO_INITFN_RESULT_TYPE( boost::mysql::detail::async_close_statement( channel& chan, std::uint32_t statement_id, - CompletionToken&& token + CompletionToken&& token, + error_info* ) { // Compose the close message @@ -43,34 +42,10 @@ boost::mysql::detail::async_close_statement( // Send it. No response is sent back chan.reset_sequence_number(); - - auto initiation = []( - auto&& completion_handler, - channel& chan - ) - { - // Executor to use - auto executor = boost::asio::get_associated_executor( - completion_handler, - chan.next_layer().get_executor() - ); - - // Preserve the executor to use - chan.async_write( - boost::asio::buffer(chan.shared_buffer()), - boost::asio::bind_executor( - executor, - std::bind( - std::forward(completion_handler), - std::placeholders::_1, - error_info() - ) - ) - ); - }; - - return boost::asio::async_initiate( - initiation, token, chan); + return chan.async_write( + boost::asio::buffer(chan.shared_buffer()), + std::forward(token) + ); } diff --git a/include/boost/mysql/detail/network_algorithms/impl/execute_generic.hpp b/include/boost/mysql/detail/network_algorithms/impl/execute_generic.hpp index 69a546e1..e02bd233 100644 --- a/include/boost/mysql/detail/network_algorithms/impl/execute_generic.hpp +++ b/include/boost/mysql/detail/network_algorithms/impl/execute_generic.hpp @@ -187,14 +187,14 @@ boost::mysql::detail::async_execute_generic( deserialize_row_fn deserializer, channel& chan, const Serializable& request, - CompletionToken&& token + CompletionToken&& token, + error_info* info ) { - using HandlerSignature = boost::mysql::detail::execute_generic_signature; + using HandlerSignature = execute_generic_signature; using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature); using BaseType = boost::beast::async_base; using ResultsetType = resultset; - using HandlerArg = async_handler_arg; boost::asio::async_completion initiator(token); @@ -202,15 +202,18 @@ boost::mysql::detail::async_execute_generic( { std::shared_ptr> processor_; std::uint64_t remaining_fields_ {0}; + error_info* output_info_; Op( HandlerType&& handler, deserialize_row_fn deserializer, channel& channel, - const Serializable& request + const Serializable& request, + error_info* output_info ): BaseType(std::move(handler), channel.next_layer().get_executor()), - processor_(std::make_shared>(deserializer, channel)) + processor_(std::make_shared>(deserializer, channel)), + output_info_(output_info) { processor_->process_request(request); } @@ -230,7 +233,7 @@ boost::mysql::detail::async_execute_generic( ); if (err) { - this->complete(cont, err, HandlerArg()); + this->complete(cont, err, ResultsetType()); yield break; } @@ -241,7 +244,7 @@ boost::mysql::detail::async_execute_generic( ); if (err) { - this->complete(cont, err, HandlerArg()); + this->complete(cont, err, ResultsetType()); yield break; } @@ -249,7 +252,8 @@ boost::mysql::detail::async_execute_generic( processor_->process_response(err, info); if (err) { - this->complete(cont, err, HandlerArg(std::move(info))); + conditional_assign(output_info_, std::move(info)); + this->complete(cont, err, ResultsetType()); yield break; } remaining_fields_ = processor_->field_count(); @@ -270,7 +274,7 @@ boost::mysql::detail::async_execute_generic( if (err) { - this->complete(cont, err, HandlerArg()); + this->complete(cont, err, ResultsetType()); yield break; } @@ -281,7 +285,7 @@ boost::mysql::detail::async_execute_generic( this->complete( cont, error_code(), - HandlerArg(std::move(*processor_).create_resultset()) + ResultsetType(std::move(*processor_).create_resultset()) ); } } @@ -291,7 +295,8 @@ boost::mysql::detail::async_execute_generic( std::move(initiator.completion_handler), deserializer, chan, - request + request, + info )(error_code(), false); return initiator.result.get(); } diff --git a/include/boost/mysql/detail/network_algorithms/impl/execute_query.hpp b/include/boost/mysql/detail/network_algorithms/impl/execute_query.hpp index 47e8c165..f657aaf2 100644 --- a/include/boost/mysql/detail/network_algorithms/impl/execute_query.hpp +++ b/include/boost/mysql/detail/network_algorithms/impl/execute_query.hpp @@ -14,7 +14,14 @@ void boost::mysql::detail::execute_query( ) { com_query_packet request { string_eof(query) }; - execute_generic(&deserialize_text_row, channel, request, output, err, info); + execute_generic( + &deserialize_text_row, + channel, + request, + output, + err, + info + ); } @@ -26,11 +33,18 @@ BOOST_ASIO_INITFN_RESULT_TYPE( boost::mysql::detail::async_execute_query( channel& chan, std::string_view query, - CompletionToken&& token + CompletionToken&& token, + error_info* info ) { com_query_packet request { string_eof(query) }; - return async_execute_generic(&deserialize_text_row, chan, request, std::forward(token)); + return async_execute_generic( + &deserialize_text_row, + chan, + request, + std::forward(token), + info + ); } #include diff --git a/include/boost/mysql/detail/network_algorithms/impl/execute_statement.hpp b/include/boost/mysql/detail/network_algorithms/impl/execute_statement.hpp index ccf7e948..3a3e1994 100644 --- a/include/boost/mysql/detail/network_algorithms/impl/execute_statement.hpp +++ b/include/boost/mysql/detail/network_algorithms/impl/execute_statement.hpp @@ -40,8 +40,14 @@ void boost::mysql::detail::execute_statement( error_info& info ) { - auto request = make_stmt_execute_packet(statement_id, params_begin, params_end); - execute_generic(&deserialize_binary_row, chan, request, output, err, info); + execute_generic( + &deserialize_binary_row, + chan, + make_stmt_execute_packet(statement_id, params_begin, params_end), + output, + err, + info + ); } template @@ -54,11 +60,17 @@ boost::mysql::detail::async_execute_statement( std::uint32_t statement_id, ForwardIterator params_begin, ForwardIterator params_end, - CompletionToken&& token + CompletionToken&& token, + error_info* info ) { - auto request = make_stmt_execute_packet(statement_id, params_begin, params_end); - return async_execute_generic(&deserialize_binary_row, chan, request, std::forward(token)); + return async_execute_generic( + &deserialize_binary_row, + chan, + make_stmt_execute_packet(statement_id, params_begin, params_end), + std::forward(token), + info + ); } diff --git a/include/boost/mysql/detail/network_algorithms/impl/handshake.hpp b/include/boost/mysql/detail/network_algorithms/impl/handshake.hpp index 0f239787..be42b3e8 100644 --- a/include/boost/mysql/detail/network_algorithms/impl/handshake.hpp +++ b/include/boost/mysql/detail/network_algorithms/impl/handshake.hpp @@ -283,7 +283,8 @@ BOOST_ASIO_INITFN_RESULT_TYPE( boost::mysql::detail::async_handshake( channel& chan, const handshake_params& params, - CompletionToken&& token + CompletionToken&& token, + error_info* info ) { using HandlerSignature = handshake_signature; @@ -297,22 +298,26 @@ boost::mysql::detail::async_handshake( channel& channel_; handshake_processor processor_; error_info info_; + error_info* output_info_; Op( HandlerType&& handler, channel& channel, - const handshake_params& params + const handshake_params& params, + error_info* output_info ): BaseType(std::move(handler), channel.next_layer().get_executor()), channel_(channel), - processor_(params) + processor_(params), + output_info_(output_info) { } void complete(bool cont, error_code code) { channel_.set_current_capabilities(processor_.negotiated_capabilities()); - BaseType::complete(cont, code, std::move(info_)); + conditional_assign(output_info_, std::move(info_)); + BaseType::complete(cont, code); } void operator()( @@ -357,7 +362,6 @@ boost::mysql::detail::async_handshake( // Process it err = processor_.process_handshake_server_response(channel_.shared_buffer(), auth_complete, info_); - if (auth_complete) err.clear(); if (err || auth_complete) { complete(cont, err); @@ -396,7 +400,8 @@ boost::mysql::detail::async_handshake( Op( std::move(initiator.completion_handler), chan, - params + params, + info )(error_code(), false); return initiator.result.get(); } diff --git a/include/boost/mysql/detail/network_algorithms/impl/prepare_statement.hpp b/include/boost/mysql/detail/network_algorithms/impl/prepare_statement.hpp index ceb96f10..c9928dfd 100644 --- a/include/boost/mysql/detail/network_algorithms/impl/prepare_statement.hpp +++ b/include/boost/mysql/detail/network_algorithms/impl/prepare_statement.hpp @@ -102,14 +102,14 @@ BOOST_ASIO_INITFN_RESULT_TYPE( boost::mysql::detail::async_prepare_statement( channel& chan, std::string_view statement, - CompletionToken&& token + CompletionToken&& token, + error_info* info ) { using HandlerSignature = prepare_statement_signature; using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature); using BaseType = boost::beast::async_base; using PreparedStatementType = prepared_statement; - using HandlerArg = async_handler_arg; boost::asio::async_completion initiator(token); @@ -117,15 +117,18 @@ boost::mysql::detail::async_prepare_statement( { prepare_statement_processor processor_; unsigned remaining_meta_; + error_info* output_info_; Op( HandlerType&& handler, channel& channel, - std::string_view statement + std::string_view statement, + error_info* output_info ): BaseType(std::move(handler), channel.next_layer().get_executor()), processor_(channel), - remaining_meta_(0) + remaining_meta_(0), + output_info_(output_info) { processor_.process_request(statement); } @@ -145,7 +148,7 @@ boost::mysql::detail::async_prepare_statement( ); if (err) { - this->complete(cont, err, HandlerArg()); + this->complete(cont, err, PreparedStatementType()); yield break; } @@ -156,7 +159,7 @@ boost::mysql::detail::async_prepare_statement( ); if (err) { - this->complete(cont, err, HandlerArg()); + this->complete(cont, err, PreparedStatementType()); yield break; } @@ -164,7 +167,8 @@ boost::mysql::detail::async_prepare_statement( processor_.process_response(err, info); if (err) { - this->complete(cont, err, HandlerArg(std::move(info))); + detail::conditional_assign(output_info_, std::move(info)); + this->complete(cont, err, PreparedStatementType()); yield break; } @@ -179,7 +183,7 @@ boost::mysql::detail::async_prepare_statement( ); if (err) { - this->complete(cont, err, HandlerArg()); + this->complete(cont, err, PreparedStatementType()); yield break; } } @@ -188,10 +192,7 @@ boost::mysql::detail::async_prepare_statement( this->complete( cont, err, - HandlerArg(PreparedStatementType( - processor_.get_channel(), - processor_.get_response()) - ) + PreparedStatementType(processor_.get_channel(), processor_.get_response()) ); } } @@ -200,7 +201,8 @@ boost::mysql::detail::async_prepare_statement( Op( std::move(initiator.completion_handler), chan, - statement + statement, + info )(error_code(), false); return initiator.result.get(); } diff --git a/include/boost/mysql/detail/network_algorithms/prepare_statement.hpp b/include/boost/mysql/detail/network_algorithms/prepare_statement.hpp index edd6ed44..e2529b39 100644 --- a/include/boost/mysql/detail/network_algorithms/prepare_statement.hpp +++ b/include/boost/mysql/detail/network_algorithms/prepare_statement.hpp @@ -18,14 +18,15 @@ void prepare_statement( ); template -using prepare_statement_signature = r_handler_signature>; +using prepare_statement_signature = void(error_code, prepared_statement); template BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, prepare_statement_signature) async_prepare_statement( channel& channel, std::string_view statement, - CompletionToken&& token + CompletionToken&& token, + error_info* info ); } // detail diff --git a/include/boost/mysql/impl/connection.hpp b/include/boost/mysql/impl/connection.hpp index 864914db..5af02c34 100644 --- a/include/boost/mysql/impl/connection.hpp +++ b/include/boost/mysql/impl/connection.hpp @@ -63,13 +63,16 @@ BOOST_ASIO_INITFN_RESULT_TYPE( ) boost::mysql::connection::async_handshake( const connection_params& params, - CompletionToken&& token + CompletionToken&& token, + error_info* info ) { + detail::conditional_clear(info); return detail::async_handshake( channel_, detail::to_handshake_params(params), - std::forward(token) + std::forward(token), + info ); } @@ -109,13 +112,16 @@ BOOST_ASIO_INITFN_RESULT_TYPE( ) boost::mysql::connection::async_query( std::string_view query_string, - CompletionToken&& token + CompletionToken&& token, + error_info* info ) { + detail::conditional_clear(info); return detail::async_execute_query( channel_, query_string, - std::forward(token) + std::forward(token), + info ); } @@ -154,10 +160,17 @@ BOOST_ASIO_INITFN_RESULT_TYPE( ) boost::mysql::connection::async_prepare_statement( std::string_view statement, - CompletionToken&& token + CompletionToken&& token, + error_info* info ) { - return detail::async_prepare_statement(channel_, statement, std::forward(token)); + detail::conditional_clear(info); + return detail::async_prepare_statement( + channel_, + statement, + std::forward(token), + info + ); } diff --git a/include/boost/mysql/impl/error.hpp b/include/boost/mysql/impl/error.hpp index 120134ae..0e2c4c08 100644 --- a/include/boost/mysql/impl/error.hpp +++ b/include/boost/mysql/impl/error.hpp @@ -61,6 +61,22 @@ inline void check_error_code(const error_code& code, const error_info& info) } } +inline void conditional_clear(error_info* info) noexcept +{ + if (info) + { + info->clear(); + } +} + +inline void conditional_assign(error_info* to, error_info&& from) +{ + if (to) + { + *to = std::move(from); + } +} + } // detail } // mysql } // boost diff --git a/include/boost/mysql/impl/prepared_statement.hpp b/include/boost/mysql/impl/prepared_statement.hpp index 84b9f37f..8bb41b21 100644 --- a/include/boost/mysql/impl/prepared_statement.hpp +++ b/include/boost/mysql/impl/prepared_statement.hpp @@ -81,38 +81,43 @@ BOOST_ASIO_INITFN_RESULT_TYPE( boost::mysql::prepared_statement::async_execute( ForwardIterator params_first, ForwardIterator params_last, - CompletionToken&& token + CompletionToken&& token, + error_info* info ) const { + detail::conditional_clear(info); + // Check we got passed the right number of params error_code err; - error_info info; - check_num_params(params_first, params_last, err, info); + error_info nonnull_info; + check_num_params(params_first, params_last, err, nonnull_info); if (err) { - using HandlerArg = async_handler_arg>; - using HandlerSignature = void(error_code, HandlerArg); - boost::asio::async_completion completion (token); + detail::conditional_assign(info, std::move(nonnull_info)); + boost::asio::async_completion completion (token); // TODO: is executor correctly preserved here? boost::asio::post( channel_->next_layer().get_executor(), boost::beast::bind_front_handler( std::move(completion.completion_handler), err, - HandlerArg(std::move(info)) + resultset() ) ); return completion.result.get(); } - - // Actually execute the statement - return detail::async_execute_statement( - *channel_, - stmt_msg_.statement_id.value, - params_first, - params_last, - std::forward(token) - ); + else + { + // Actually execute the statement + return detail::async_execute_statement( + *channel_, + stmt_msg_.statement_id.value, + params_first, + params_last, + std::forward(token), + info + ); + } } template @@ -144,11 +149,18 @@ BOOST_ASIO_INITFN_RESULT_TYPE( typename boost::mysql::prepared_statement::close_signature ) boost::mysql::prepared_statement::async_close( - CompletionToken&& token + CompletionToken&& token, + error_info* info ) { assert(valid()); - return detail::async_close_statement(*channel_, id(), std::forward(token)); + detail::conditional_clear(info); + return detail::async_close_statement( + *channel_, + id(), + std::forward(token), + info + ); } #endif /* INCLUDE_BOOST_MYSQL_IMPL_PREPARED_STATEMENT_HPP_ */ diff --git a/include/boost/mysql/impl/resultset.hpp b/include/boost/mysql/impl/resultset.hpp index 64b7ee19..3ce0d26e 100644 --- a/include/boost/mysql/impl/resultset.hpp +++ b/include/boost/mysql/impl/resultset.hpp @@ -126,10 +126,12 @@ BOOST_ASIO_INITFN_RESULT_TYPE( typename boost::mysql::resultset::fetch_one_signature ) boost::mysql::resultset::async_fetch_one( - CompletionToken&& token + CompletionToken&& token, + error_info* info ) { - using HandlerArg = async_handler_arg; + detail::conditional_clear(info); + using HandlerSignature = fetch_one_signature; using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature); using BaseType = boost::beast::async_base; @@ -137,13 +139,18 @@ boost::mysql::resultset::async_fetch_one( struct Op: BaseType, boost::asio::coroutine { resultset& resultset_; + error_info* output_info_; Op( HandlerType&& handler, - resultset& obj + resultset& obj, + error_info* output_info ): BaseType(std::move(handler), obj.channel_->next_layer().get_executor()), - resultset_(obj) {}; + resultset_(obj), + output_info_(output_info) + { + }; void operator()( error_code err, @@ -156,7 +163,7 @@ boost::mysql::resultset::async_fetch_one( { if (resultset_.complete()) { - this->complete(cont, error_code(), HandlerArg(nullptr)); + this->complete(cont, error_code(), nullptr); } else { @@ -170,12 +177,11 @@ boost::mysql::resultset::async_fetch_one( std::move(*this) ); resultset_.eof_received_ = result == detail::read_row_result::eof; + detail::conditional_assign(output_info_, std::move(info)); this->complete( cont, err, - result == detail::read_row_result::error ? HandlerArg(std::move(info)) : - result == detail::read_row_result::row ? HandlerArg(&resultset_.current_row_) : - HandlerArg(nullptr) + result == detail::read_row_result::row ? &resultset_.current_row_ : nullptr ); } } @@ -188,7 +194,8 @@ boost::mysql::resultset::async_fetch_one( Op( std::move(initiator.completion_handler), - *this + *this, + info )(error_code(), error_info(), detail::read_row_result::error, false); return initiator.result.get(); } @@ -201,10 +208,12 @@ BOOST_ASIO_INITFN_RESULT_TYPE( ) boost::mysql::resultset::async_fetch_many( std::size_t count, - CompletionToken&& token + CompletionToken&& token, + error_info* info ) { - using HandlerArg = async_handler_arg>; + detail::conditional_clear(info); + using HandlerSignature = fetch_many_signature; using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature); using BaseType = boost::beast::async_base; @@ -216,15 +225,20 @@ boost::mysql::resultset::async_fetch_many( detail::bytestring buffer; std::vector values; std::size_t remaining; + error_info* output_info_; - OpImpl(resultset& obj, std::size_t count): - parent_resultset(obj), remaining(count) {}; + OpImpl(resultset& obj, std::size_t count, error_info* output_info): + parent_resultset(obj), + remaining(count), + output_info_(output_info) + { + }; void row_received() { rows.emplace_back(std::move(values), std::move(buffer)); - values = std::vector{}; - buffer = detail::bytestring{}; + values = std::vector(); + buffer = detail::bytestring(); --remaining; } }; @@ -264,7 +278,8 @@ boost::mysql::resultset::async_fetch_many( ); if (result == detail::read_row_result::error) { - this->complete(cont, err, HandlerArg(std::move(info))); + detail::conditional_assign(impl_->output_info_, std::move(info)); + this->complete(cont, err, std::move(impl_->rows)); yield break; } else if (result == detail::read_row_result::eof) @@ -276,7 +291,7 @@ boost::mysql::resultset::async_fetch_many( impl_->row_received(); } } - this->complete(cont, err, HandlerArg(std::move(impl_->rows))); + this->complete(cont, err, std::move(impl_->rows)); } } }; @@ -287,7 +302,7 @@ boost::mysql::resultset::async_fetch_many( Op( std::move(initiator.completion_handler), - std::make_shared(*this, count) + std::make_shared(*this, count, info) )(error_code(), error_info(), detail::read_row_result::error, false); return initiator.result.get(); } @@ -299,12 +314,14 @@ BOOST_ASIO_INITFN_RESULT_TYPE( typename boost::mysql::resultset::fetch_all_signature ) boost::mysql::resultset::async_fetch_all( - CompletionToken&& token + CompletionToken&& token, + error_info* info ) { return async_fetch_many( std::numeric_limits::max(), - std::forward(token) + std::forward(token), + info ); } diff --git a/include/boost/mysql/prepared_statement.hpp b/include/boost/mysql/prepared_statement.hpp index dc41e6be..645d3b7a 100644 --- a/include/boost/mysql/prepared_statement.hpp +++ b/include/boost/mysql/prepared_statement.hpp @@ -2,7 +2,6 @@ #define INCLUDE_BOOST_MYSQL_PREPARED_STATEMENT_HPP_ #include "boost/mysql/resultset.hpp" -#include "boost/mysql/async_handler_arg.hpp" #include "boost/mysql/detail/protocol/channel.hpp" #include "boost/mysql/detail/protocol/prepared_statement_messages.hpp" #include @@ -93,7 +92,7 @@ public: } /// The handler signature for execute. - using execute_signature = void(error_code, async_handler_arg>); + using execute_signature = void(error_code, resultset); /** * \brief Executes a statement (collection, sync with exceptions code version). @@ -102,9 +101,14 @@ public: */ template BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, execute_signature) - async_execute(const Collection& params, CompletionToken&& token) const + async_execute(const Collection& params, CompletionToken&& token, error_info* info=nullptr) const { - return async_execute(std::begin(params), std::end(params), std::forward(token)); + return async_execute( + std::begin(params), + std::end(params), + std::forward(token), + info + ); } @@ -129,7 +133,8 @@ public: */ template BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, execute_signature) - async_execute(ForwardIterator params_first, ForwardIterator params_last, CompletionToken&& token) const; + async_execute(ForwardIterator params_first, ForwardIterator params_last, + CompletionToken&& token, error_info* info=nullptr) const; /** * \brief Closes a prepared statement, deallocating it from the server (sync with error code version). @@ -149,12 +154,12 @@ public: void close(); /// The handler signature for close. - using close_signature = void(error_code, error_info); + using close_signature = void(error_code); /// Closes a prepared statement, deallocating it from the server (async version). template BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, close_signature) - async_close(CompletionToken&& token); + async_close(CompletionToken&& token, error_info* info=nullptr); }; /// A prepared statement associated to a TCP connection to the MySQL server. diff --git a/include/boost/mysql/resultset.hpp b/include/boost/mysql/resultset.hpp index ad2c0b94..f41a195c 100644 --- a/include/boost/mysql/resultset.hpp +++ b/include/boost/mysql/resultset.hpp @@ -3,7 +3,6 @@ #include "boost/mysql/row.hpp" #include "boost/mysql/metadata.hpp" -#include "boost/mysql/async_handler_arg.hpp" #include "boost/mysql/detail/protocol/common_messages.hpp" #include "boost/mysql/detail/protocol/channel.hpp" #include "boost/mysql/detail/auxiliar/bytestring.hpp" @@ -120,28 +119,28 @@ public: std::vector fetch_all(); /// Handler signature for fetch_one. - using fetch_one_signature = void(error_code, async_handler_arg); + using fetch_one_signature = void(error_code, const row*); /// Fetchs a single row (async version). template BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, fetch_one_signature) - async_fetch_one(CompletionToken&& token); + async_fetch_one(CompletionToken&& token, error_info* info=nullptr); /// Handler signature for fetch_many. - using fetch_many_signature = void(error_code, async_handler_arg>); + using fetch_many_signature = void(error_code, std::vector); /// Fetches at most count rows (async version). template BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, fetch_many_signature) - async_fetch_many(std::size_t count, CompletionToken&& token); + async_fetch_many(std::size_t count, CompletionToken&& token, error_info* info=nullptr); /// Handler signature for fetch_all. - using fetch_all_signature = void(error_code, async_handler_arg>); + using fetch_all_signature = void(error_code, std::vector); /// Fetches all available rows (async version). template BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, fetch_all_signature) - async_fetch_all(CompletionToken&& token); + async_fetch_all(CompletionToken&& token, error_info* info=nullptr); /** * \brief Returns whether this object represents a valid resultset. diff --git a/test/integration/network_functions.cpp b/test/integration/network_functions.cpp index 1b49b13f..e6de496f 100644 --- a/test/integration/network_functions.cpp +++ b/test/integration/network_functions.cpp @@ -221,22 +221,24 @@ class async_callback : public network_functions template static network_result impl(Callable&& cb) { std::promise> prom; - cb([&prom](error_code code, auto retval) { + error_info info; + cb([&prom, &info](error_code code, R retval) { prom.set_value(network_result( code, - std::move(retval.error()), - std::move(retval.get()) + std::move(info), + std::move(retval) )); - }); + }, &info); return prom.get_future().get(); } template static network_result impl_no_result(Callable&& cb) { std::promise> prom; - cb([&prom](error_code code, error_info info) { + error_info info; + cb([&prom, &info](error_code code) { prom.set_value(network_result(code, std::move(info))); - }); + }, &info); return prom.get_future().get(); } public: @@ -246,8 +248,8 @@ public: const boost::mysql::connection_params& params ) override { - return impl_no_result([&](auto&& token) { - return conn.async_handshake(params, std::forward(token)); + return impl_no_result([&](auto&& token, error_info* info) { + return conn.async_handshake(params, std::forward(token), info); }); } network_result query( @@ -255,8 +257,8 @@ public: std::string_view query ) override { - return impl([&](auto&& token) { - return conn.async_query(query, std::forward(token)); + return impl([&](auto&& token, error_info* info) { + return conn.async_query(query, std::forward(token), info); }); } network_result prepare_statement( @@ -264,8 +266,8 @@ public: std::string_view statement ) override { - return impl([&conn, statement](auto&& token) { - return conn.async_prepare_statement(statement, std::forward(token)); + return impl([&conn, statement](auto&& token, error_info* info) { + return conn.async_prepare_statement(statement, std::forward(token), info); }); } network_result execute_statement( @@ -274,8 +276,8 @@ public: value_list_it params_last ) override { - return impl([&](auto&& token) { - return stmt.async_execute(params_first, params_last, std::forward(token)); + return impl([&](auto&& token, error_info* info) { + return stmt.async_execute(params_first, params_last, std::forward(token), info); }); } network_result execute_statement( @@ -283,24 +285,24 @@ public: const std::vector& values ) override { - return impl([&](auto&& token) { - return stmt.async_execute(values, std::forward(token)); + return impl([&](auto&& token, error_info* info) { + return stmt.async_execute(values, std::forward(token), info); }); } network_result close_statement( tcp_prepared_statement& stmt ) override { - return impl_no_result([&](auto&& token) { - return stmt.async_close(std::forward(token)); + return impl_no_result([&](auto&& token, error_info* info) { + return stmt.async_close(std::forward(token), info); }); } network_result fetch_one( tcp_resultset& r ) override { - return impl([&](auto&& token) { - return r.async_fetch_one(std::forward(token)); + return impl([&](auto&& token, error_info* info) { + return r.async_fetch_one(std::forward(token), info); }); } network_result> fetch_many( @@ -308,16 +310,16 @@ public: std::size_t count ) override { - return impl>([&](auto&& token) { - return r.async_fetch_many(count, std::forward(token)); + return impl>([&](auto&& token, error_info* info) { + return r.async_fetch_many(count, std::forward(token), info); }); } network_result> fetch_all( tcp_resultset& r ) override { - return impl>([&](auto&& token) { - return r.async_fetch_all(std::forward(token)); + return impl>([&](auto&& token, error_info* info) { + return r.async_fetch_all(std::forward(token), info); }); } }; @@ -326,15 +328,19 @@ class async_coroutine : public network_functions { template static auto impl(IoObj& obj, Callable&& cb) { - using R = typename decltype(cb(std::declval()))::value_type; + using R = decltype(cb( + std::declval(), + std::declval() + )); std::promise> prom; boost::asio::spawn(obj.next_layer().get_executor(), [&](yield_context yield) { error_code ec; - auto result = cb(yield[ec]); + error_info info; + auto result = cb(yield[ec], &info); prom.set_value(network_result( ec, - std::move(result.error()), - std::move(result.get()) + std::move(info), + std::move(result) )); }); return prom.get_future().get(); @@ -346,7 +352,8 @@ class async_coroutine : public network_functions std::promise> prom; boost::asio::spawn(executor, [&](yield_context yield) { error_code ec; - error_info info = cb(yield[ec]); + error_info info; + cb(yield[ec], &info); prom.set_value(network_result(ec, std::move(info))); }); return prom.get_future().get(); @@ -358,8 +365,8 @@ public: const boost::mysql::connection_params& params ) override { - return impl_no_result(conn, [&](yield_context yield) { - return conn.async_handshake(params, yield); + return impl_no_result(conn, [&](yield_context yield, error_info* info) { + return conn.async_handshake(params, yield, info); }); } network_result query( @@ -367,8 +374,8 @@ public: std::string_view query ) override { - return impl(conn, [&](yield_context yield) { - return conn.async_query(query, yield); + return impl(conn, [&](yield_context yield, error_info* info) { + return conn.async_query(query, yield, info); }); } network_result prepare_statement( @@ -376,8 +383,8 @@ public: std::string_view statement ) override { - return impl(conn, [&](yield_context yield) { - return conn.async_prepare_statement(statement, yield); + return impl(conn, [&](yield_context yield, error_info* info) { + return conn.async_prepare_statement(statement, yield, info); }); } network_result execute_statement( @@ -386,8 +393,8 @@ public: value_list_it params_last ) override { - return impl(stmt, [&](yield_context yield) { - return stmt.async_execute(params_first, params_last, yield); + return impl(stmt, [&](yield_context yield, error_info* info) { + return stmt.async_execute(params_first, params_last, yield, info); }); } network_result execute_statement( @@ -395,24 +402,24 @@ public: const std::vector& values ) override { - return impl(stmt, [&](yield_context yield) { - return stmt.async_execute(values, yield); + return impl(stmt, [&](yield_context yield, error_info* info) { + return stmt.async_execute(values, yield, info); }); } network_result close_statement( tcp_prepared_statement& stmt ) override { - return impl_no_result(stmt, [&](yield_context yield) { - return stmt.async_close(yield); + return impl_no_result(stmt, [&](yield_context yield, error_info* info) { + return stmt.async_close(yield, info); }); } network_result fetch_one( tcp_resultset& r ) override { - return impl(r, [&](yield_context yield) { - return r.async_fetch_one(yield); + return impl(r, [&](yield_context yield, error_info* info) { + return r.async_fetch_one(yield, info); }); } network_result> fetch_many( @@ -420,34 +427,33 @@ public: std::size_t count ) override { - return impl(r, [&](yield_context yield) { - return r.async_fetch_many(count, yield); + return impl(r, [&](yield_context yield, error_info* info) { + return r.async_fetch_many(count, yield, info); }); } network_result> fetch_all( tcp_resultset& r ) override { - return impl(r, [&](yield_context yield) { - return r.async_fetch_all(yield); + return impl(r, [&](yield_context yield, error_info* info) { + return r.async_fetch_all(yield, info); }); } }; + class async_future : public network_functions { template static auto impl(Callable&& cb) { - // Callable returns a future> - using R = typename decltype(cb().get())::value_type; - std::future> fut = cb(); + using R = decltype(cb().get()); // Callable returns a future + std::future fut = cb(); try { - auto result = fut.get(); + // error_info is not available here, so we skip validation return network_result( error_code(), - std::move(result.error()), // the returned error_info should be empty - std::move(result.get()) + std::move(fut.get()) ); } catch (const boost::system::system_error& err) @@ -459,14 +465,16 @@ class async_future : public network_functions template static network_result impl_no_result(Callable&& cb) { - std::future fut = cb(); + std::future fut = cb(); try { - // This should be returning an empty error_info, so we validate that - return network_result(error_code(), fut.get()); + // error_info is not available here, so we skip validation + fut.get(); + return network_result(error_code()); } catch (const boost::system::system_error& err) { + // error_info is not available here, so we skip validation return network_result(err.code()); } }