diff --git a/include/boost/mysql/connection.hpp b/include/boost/mysql/connection.hpp index 93b388ea..0bfbb2c1 100644 --- a/include/boost/mysql/connection.hpp +++ b/include/boost/mysql/connection.hpp @@ -14,14 +14,10 @@ #include #include -#include #include -#include -#include +#include +#include #include -#include -#include -#include #include #endif @@ -128,13 +124,16 @@ public: executor_type get_executor() { return get_channel().get_executor(); } /// The `Stream` type this connection is using. - using next_layer_type = Stream; + using stream_type = Stream; /// Retrieves the underlying Stream object. - Stream& next_layer() { return get_channel().next_layer(); } + Stream& stream() { return get_channel().next_layer(); } /// Retrieves the underlying Stream object. - const Stream& next_layer() const { return get_channel().next_layer(); } + const Stream& stream() const { return get_channel().next_layer(); } + + using statement_type = statement; + using resultset_type = resultset; /** * \brief Returns whether the connection uses SSL or not. @@ -341,7 +340,7 @@ public: * before calling any function that involves communication with the server over this * connection. Otherwise, the results are undefined. */ - void query(boost::string_view query_string, resultset_base& result, error_code&, error_info&); + void query(boost::string_view query_string, resultset& result, error_code&, error_info&); /** * \brief Executes a SQL text query (sync with exceptions version). @@ -351,7 +350,7 @@ public: * before calling any function that involves communication with the server over this * connection. Otherwise, the results are undefined. */ - void query(boost::string_view query_string, resultset_base& result); + void query(boost::string_view query_string, resultset& result); /** * \brief Executes a SQL text query (async without [reflink error_info] version). @@ -372,7 +371,7 @@ public: BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) async_query( boost::string_view query_string, - resultset_base& result, + resultset& result, CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) ) { @@ -398,7 +397,7 @@ public: BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) async_query( boost::string_view query_string, - resultset_base& result, + resultset& result, error_info& output_info, CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) ); @@ -409,7 +408,7 @@ public: * Prepared statements are only valid while the connection object on which * this function was called is alive and open. */ - void prepare_statement(boost::string_view statement, statement_base& result, error_code&, error_info&); + void prepare_statement(boost::string_view stmt, statement& result, error_code&, error_info&); /** * \brief Prepares a statement (sync with exceptions version). @@ -417,7 +416,7 @@ public: * Prepared statements are only valid while the connection object on which * this function was called is alive and open. */ - void prepare_statement(boost::string_view statement, statement_base& result); + void prepare_statement(boost::string_view stmt, statement& result); /** * \brief Prepares a statement (async without [reflink error_info] version). @@ -435,12 +434,12 @@ public: > BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) async_prepare_statement( - boost::string_view statement, - statement_base& result, + boost::string_view stmt, + statement& result, CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) ) { - return async_prepare_statement(statement, result, shared_info(), std::forward(token)); + return async_prepare_statement(stmt, result, shared_info(), std::forward(token)); } /** @@ -459,471 +458,8 @@ public: > BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) async_prepare_statement( - boost::string_view statement, - statement_base& result, - error_info& output_info, - CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) - ); - - - /** - * \brief Executes a statement (collection, sync with error code version). - * \details - * FieldViewCollection should meet the [reflink FieldViewCollection] requirements. - * - * After this function has returned, you should read the entire resultset_base - * before calling any function that involves communication with the server over this - * connection. Otherwise, the results are undefined. - */ - template > - void execute_statement( - const statement_base& statement, - const FieldViewCollection& params, - resultset_base& result, - error_code& err, - error_info& info - ) - { - return execute_statement(make_execute_params(statement, params), result, err, info); - } - - /** - * \brief Executes a statement (collection, sync with exceptions version). - * \details - * FieldViewCollection should meet the [reflink FieldViewCollection] requirements. - * - * After this function has returned, you should read the entire resultset_base - * before calling any function that involves communication with the server over this - * connection. Otherwise, the results are undefined. - */ - template > - void execute_statement( - const statement_base& statement, - const FieldViewCollection& params, - resultset_base& result - ) - { - return execute_statement(make_execute_params(statement, params), result); - } - - /** - * \brief Executes a statement (collection, - * async without [reflink error_info] version). - * \details - * FieldViewCollection should meet the [reflink FieldViewCollection] requirements. - * - * After this operation completes, you should read the entire resultset_base - * before calling any function that involves communication with the server over this - * connection. Otherwise, the results are undefined. - * It is __not__ necessary to keep the collection of parameters or the - * values they may point to alive after the initiating function returns. - * - * The handler signature for this operation is - * `void(boost::mysql::error_code, boost::mysql::resultset_base)`. - */ - template< - class FieldViewCollection, - BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) - CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type), - class EnableIf = detail::enable_if_field_view_collection - > - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) - async_execute_statement( - const statement_base& statement, - const FieldViewCollection& params, - resultset_base& result, - CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) - ) - { - return async_execute_statement( - make_execute_params(statement, params), - result, - std::forward(token) - ); - } - - /** - * \brief Executes a statement (collection, - * async with [reflink error_info] version). - * \details - * FieldViewCollection should meet the [reflink FieldViewCollection] requirements. - * - * After this operation completes, you should read the entire resultset_base - * before calling any function that involves communication with the server over this - * connection. Otherwise, the results are undefined. - * It is __not__ necessary to keep the collection of parameters or the - * values they may point to alive after the initiating function returns. - * - * The handler signature for this operation is - * `void(boost::mysql::error_code, boost::mysql::resultset_base)`. - */ - template < - class FieldViewCollection, - BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) - CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type), - class EnableIf = detail::enable_if_field_view_collection - > - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) - async_execute_statement( - const statement_base& statement, - const FieldViewCollection& params, - resultset_base& result, - error_info& output_info, - CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) - ) - { - return async_execute_statement( - make_execute_params(statement, params), - result, - output_info, - std::forward(token) - ); - } - - - /** - * \brief Executes a statement (`execute_params`, sync with error code version). - * \details - * FieldViewFwdIterator should meet the [reflink FieldViewFwdIterator] requirements. - * The range \\[`params.first()`, `params.last()`) will be used as parameters for - * statement execution. They should be a valid iterator range. - * - * After this function has returned, you should read the entire resultset_base - * before calling any function that involves communication with the server over this - * connection. Otherwise, the results are undefined. - */ - template - void execute_statement( - const execute_params& params, - resultset_base& result, - error_code& ec, - error_info& info - ); - - /** - * \brief Executes a statement (`execute_params`, sync with exceptions version). - * \details - * FieldViewFwdIterator should meet the [reflink FieldViewFwdIterator] requirements. - * The range \\[`params.first()`, `params.last()`) will be used as parameters for - * statement execution. They should be a valid iterator range. - * - * After this function has returned, you should read the entire resultset_base - * before calling any function that involves communication with the server over this - * connection. Otherwise, the results are undefined. - */ - template - void execute_statement( - const execute_params& params, - resultset_base& result - ); - - /** - * \brief Executes a statement (`execute_params`, - * async without [reflink error_info] version). - * \details - * FieldViewFwdIterator should meet the [reflink FieldViewFwdIterator] requirements. - * The range \\[`params.first()`, `params.last()`) will be used as parameters for - * statement execution. They should be a valid iterator range. - * - * After this operation completes, you should read the entire resultset_base - * before calling any function that involves communication with the server over this - * connection. Otherwise, the results are undefined. - * - * It is __not__ necessary to keep the objects in - * \\[`params.first()`, `params.last()`) or the - * values they may point to alive after the initiating function returns. - * - * The handler signature for this operation is - * `void(boost::mysql::error_code, boost::mysql::resultset_base)`. - */ - template < - class FieldViewFwdIterator, - BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) - CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) - > - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) - async_execute_statement( - const execute_params& params, - resultset_base& result, - CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) - ) - { - return async_execute(params, result, shared_info(), std::forward(token)); - } - - /** - * \brief Executes a statement (`execute_params`, - * async with [reflink error_info] version). - * \details - * FieldViewFwdIterator should meet the [reflink FieldViewFwdIterator] requirements. - * The range \\[`params.first()`, `params.last()`) will be used as parameters for - * statement execution. They should be a valid iterator range. - * - * After this operation completes, you should read the entire resultset_base - * before calling any function that involves communication with the server over this - * connection. Otherwise, the results are undefined. - * - * It is __not__ necessary to keep the objects in - * \\[`params.first()`, `params.last()`) or the - * values they may point to alive after the initiating function returns. - * - * The handler signature for this operation is - * `void(boost::mysql::error_code, boost::mysql::resultset_base)`. - */ - template < - class FieldViewFwdIterator, - BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) - CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) - > - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) - async_execute_statement( - const execute_params& params, - resultset_base& result, - error_info& output_info, - CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) - ); - - - /** - * \brief Closes a prepared statement, deallocating it from the server - (sync with error code version). - * \details - * After calling this function, no further functions may be called on this prepared - * statement, other than assignment. Failing to do so results in undefined behavior. - */ - void close_statement(const statement_base&, error_code&, error_info&); - - /** - * \brief Closes a prepared statement, deallocating it from the server - (sync with exceptions version). - * \details - * After calling this function, no further functions may be called on this prepared - * statement, other than assignment. Failing to do so results in undefined behavior. - */ - void close_statement(const statement_base&); - - /** - * \brief Closes a prepared statement, deallocating it from the server - (async without [reflink error_info] version). - * \details - * After the operation completes, no further functions may be called on this prepared - * statement, other than assignment. Failing to do so results in undefined behavior. - * - * The handler signature for this operation is `void(boost::mysql::error_code)`. - */ - template < - BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) - CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) - > - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) - async_close_statement( - const statement_base& stmt, - CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) - ) - { - return async_close_statement(stmt, shared_info(), std::forward(token)); - } - - /** - * \brief Closes a prepared statement, deallocating it from the server - (async with [reflink error_info] version). - * \details - * After the operation completes, no further functions may be called on this prepared - * statement, other than assignment. Failing to do so results in undefined behavior. - * - * The handler signature for this operation is `void(boost::mysql::error_code)`. - */ - template < - BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) - CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) - > - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) - async_close_statement( - const statement_base& stmt, - error_info& output_info, - CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) - ); - - - /** - * \brief Reads a single row (sync with error code version). - * \details Returns `true` if a row was read successfully, `false` if - * there was an error or there were no more rows to read. Calling - * this function on a complete resultset_base always returns `false`. - * - * If the operation succeeds and returns `true`, the new row will be - * read against `output`, possibly reusing its memory. If the operation - * succeeds but returns `false`, `output` will be set to the empty row - * (as if [refmem row clear] was called). If the operation fails, - * `output` is left in a valid but undetrmined state. - */ - bool read_one_row(resultset_base& resultset_base, row_view& output, error_code& err, error_info& info); - - /** - * \brief Reads a single row (sync with exceptions version). - * \details Returns `true` if a row was read successfully, `false` if - * there was an error or there were no more rows to read. Calling - * this function on a complete resultset_base always returns `false`. - * - * If the operation succeeds and returns `true`, the new row will be - * read against `output`, possibly reusing its memory. If the operation - * succeeds but returns `false`, `output` will be set to the empty row - * (as if [refmem row clear] was called). If the operation fails, - * `output` is left in a valid but undetrmined state. - */ - bool read_one_row(resultset_base& resultset_base, row_view& output); - - /** - * \brief Reads a single row (async without [reflink error_info] version). - * \details Completes with `true` if a row was read successfully, and with `false` if - * there was an error or there were no more rows to read. Calling - * this function on a complete resultset_base always returns `false`. - * - * If the operation succeeds and completes with `true`, the new row will be - * read against `output`, possibly reusing its memory. If the operation - * succeeds but completes with `false`, `output` will be set to the empty row - * (as if [refmem row clear] was called). If the operation fails, - * `output` is left in a valid but undetrmined state. - * - * The handler signature for this operation is - * `void(boost::mysql::error_code, bool)`. - */ - template < - BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, bool)) - CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) - > - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, bool)) - async_read_one_row( - resultset_base& resultset_base, - row_view& output, - CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) - ) - { - return async_read_one_row(resultset_base, output, shared_info(), std::forward(token)); - } - - /** - * \brief Reads a single row (async with [reflink error_info] version). - * \details Completes with `true` if a row was read successfully, and with `false` if - * there was an error or there were no more rows to read. Calling - * this function on a complete resultset_base always returns `false`. - * - * If the operation succeeds and completes with `true`, the new row will be - * read against `output`, possibly reusing its memory. If the operation - * succeeds but completes with `false`, `output` will be set to the empty row - * (as if [refmem row clear] was called). If the operation fails, - * `output` is left in a valid but undetrmined state. - * - * The handler signature for this operation is - * `void(boost::mysql::error_code, bool)`. - */ - template < - BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, bool)) - CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) - > - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, bool)) - async_read_one_row( - resultset_base& resultset_base, - row_view& output, - error_info& output_info, - CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) - ); - - void read_some_rows(resultset_base& resultset_base, rows_view& output, error_code& err, error_info& info); - void read_some_rows(resultset_base& resultset_base, rows_view& output); - - /** - * \brief Reads several rows, up to a maximum - * (async without [reflink error_info] version). - * \details - * The handler signature for this operation is - * `void(boost::mysql::error_code, std::vector)`. - */ - template < - BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) - CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) - > - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) - async_read_some_rows( - resultset_base& resultset_base, - rows_view& output, - CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) - ) - { - return async_read_some_rows(resultset_base, output, shared_info(), std::forward(token)); - } - - /** - * \brief Reads several rows, up to a maximum - * (async with [reflink error_info] version). - * \details - * The handler signature for this operation is - * `void(boost::mysql::error_code, std::vector)`. - */ - template < - BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) - CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) - > - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) - async_read_some_rows( - resultset_base& resultset_base, - rows_view& output, - error_info& output_info, - CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) - ); - - /// Reads all available rows (sync with error code version). - void read_all_rows(resultset_base& resultset_base, rows_view& output, error_code& err, error_info& info); - - /// Reads all available rows (sync with exceptions version). - void read_all_rows(resultset_base& resultset_base, rows_view& output); - - /** - * \brief Reads all available rows (async without [reflink error_info] version). - * \details - * The handler signature for this operation is - * `void(boost::mysql::error_code, std::vector)`. - */ - template < - BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) - CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) - > - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) - async_read_all_rows( - resultset_base& resultset_base, - rows_view& output, - CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) - ) - { - return async_read_all_rows(resultset_base, output, shared_info(), std::forward(token)); - } - - /** - * \brief Reads all available rows (async with [reflink error_info] version). - * \details - * The handler signature for this operation is - * `void(boost::mysql::error_code, std::vector)`. - */ - template < - BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) - CompletionToken - BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) - > - BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) - async_read_all_rows( - resultset_base& resultset_base, - rows_view& output, + boost::string_view stmt, + statement& result, error_info& output_info, CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) ); @@ -1074,16 +610,6 @@ public: error_info& output_info, CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) ); - - /// Rebinds the connection type to another executor. - template - struct rebind_executor - { - /// The connection type when rebound to the specified executor. - using other = connection< - typename Stream:: template rebind_executor::other - >; - }; }; /// The default TCP port for the MySQL protocol. diff --git a/include/boost/mysql/detail/network_algorithms/close_statement.hpp b/include/boost/mysql/detail/network_algorithms/close_statement.hpp index eebe6ce4..559977c3 100644 --- a/include/boost/mysql/detail/network_algorithms/close_statement.hpp +++ b/include/boost/mysql/detail/network_algorithms/close_statement.hpp @@ -10,7 +10,7 @@ #include #include - +#include namespace boost { namespace mysql { @@ -19,7 +19,7 @@ namespace detail { template void close_statement( channel& chan, - std::uint32_t statement_id, + statement_base& stmt, error_code& code, error_info& info ); @@ -28,7 +28,7 @@ template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) async_close_statement( channel& chan, - std::uint32_t statement_id, + statement_base& stmt, 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 ee09e9f3..65c766df 100644 --- a/include/boost/mysql/detail/network_algorithms/execute_statement.hpp +++ b/include/boost/mysql/detail/network_algorithms/execute_statement.hpp @@ -9,6 +9,7 @@ #define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_EXECUTE_STATEMENT_HPP #include +#include #include #include #include @@ -20,6 +21,7 @@ namespace detail { template void execute_statement( channel& channel, + const statement_base& stmt, const execute_params& params, resultset_base& output, error_code& err, @@ -30,6 +32,7 @@ template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) async_execute_statement( channel& chan, + const statement_base& stmt, const execute_params& params, resultset_base& output, error_info& info, 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 38aa39c5..c4ff45b7 100644 --- a/include/boost/mysql/detail/network_algorithms/impl/close_statement.hpp +++ b/include/boost/mysql/detail/network_algorithms/impl/close_statement.hpp @@ -13,22 +13,80 @@ #include #include +namespace boost { +namespace mysql { +namespace detail { + +template +struct close_statement_op : boost::asio::coroutine +{ + channel& chan_; + statement_base& stmt_; + + close_statement_op( + channel& chan, + statement_base& stmt + ) noexcept : + chan_(chan), + stmt_(stmt) + { + } + + template + void operator()( + Self& self, + error_code err = {} + ) + { + // Error checking + if (err) + { + self.complete(err); + return; + } + + // Regular coroutine body; if there has been an error, we don't get here + BOOST_ASIO_CORO_REENTER(*this) + { + // Write message (already serialized at this point) + BOOST_ASIO_CORO_YIELD chan_.async_write( + chan_.shared_buffer(), + chan_.shared_sequence_number(), + std::move(self) + ); + + // Mark the statement as invalid + stmt_.reset(); + + // Complete + self.complete(error_code()); + } + } +}; + +} // detail +} // mysql +} // boost + template void boost::mysql::detail::close_statement( channel& chan, - std::uint32_t statement_id, + statement_base& stmt, error_code& code, error_info& ) { // Compose the close message - com_stmt_close_packet packet {statement_id}; + com_stmt_close_packet packet {stmt.id()}; // Serialize it serialize_message(packet, chan.current_capabilities(), chan.shared_buffer()); // Send it. No response is sent back chan.write(boost::asio::buffer(chan.shared_buffer()), chan.shared_sequence_number(), code); + + // Mark the statement as invalid + stmt.reset(); } template @@ -38,22 +96,22 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( ) boost::mysql::detail::async_close_statement( channel& chan, - std::uint32_t statement_id, + statement_base& stmt, CompletionToken&& token, error_info& ) { // Compose the close message - com_stmt_close_packet packet {statement_id}; + com_stmt_close_packet packet {stmt.id()}; // Serialize it serialize_message(packet, chan.current_capabilities(), chan.shared_buffer()); - // Send it. No response is sent back - return chan.async_write( - boost::asio::buffer(chan.shared_buffer()), - chan.shared_sequence_number(), - std::forward(token) + // Trigger the async op + return boost::asio::async_compose( + close_statement_op(chan, stmt), + token, + chan ); } 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 829a0843..7268792e 100644 --- a/include/boost/mysql/detail/network_algorithms/impl/execute_generic.hpp +++ b/include/boost/mysql/detail/network_algorithms/impl/execute_generic.hpp @@ -48,13 +48,14 @@ public: { }; - template + template void process_request( const Serializable& request, + channel& chan, resultset_encoding encoding ) { - output_.reset(encoding); + output_.reset(encoding, &chan); serialize_message(request, caps_, write_buffer_); } @@ -236,7 +237,7 @@ void boost::mysql::detail::execute_generic( channel.shared_buffer(), channel.current_capabilities() ); - processor.process_request(request, encoding); + processor.process_request(request, encoding, channel); // Send it channel.write(channel.shared_buffer(), processor.sequence_number(), err); @@ -296,7 +297,7 @@ boost::mysql::detail::async_execute_generic( channel.shared_buffer(), channel.current_capabilities() ); - processor.process_request(request, encoding); + processor.process_request(request, encoding, channel); return boost::asio::async_compose( execute_generic_op(channel, processor), token, 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 e79304f7..b6031057 100644 --- a/include/boost/mysql/detail/network_algorithms/impl/execute_statement.hpp +++ b/include/boost/mysql/detail/network_algorithms/impl/execute_statement.hpp @@ -10,6 +10,7 @@ #pragma once +#include #include #include #include @@ -23,11 +24,12 @@ namespace detail { template com_stmt_execute_packet make_stmt_execute_packet( - const execute_params& params + const execute_params& params, + const statement_base& stmt ) { return com_stmt_execute_packet { - params.statement_id(), + stmt.id(), std::uint8_t(0), // flags std::uint32_t(1), // iteration count std::uint8_t(1), // new params flag: set @@ -44,6 +46,7 @@ com_stmt_execute_packet make_stmt_execute_packet( template void boost::mysql::detail::execute_statement( channel& chan, + const statement_base& stmt, const execute_params& params, resultset_base& output, error_code& err, @@ -53,7 +56,7 @@ void boost::mysql::detail::execute_statement( execute_generic( resultset_encoding::binary, chan, - make_stmt_execute_packet(params), + make_stmt_execute_packet(params, stmt), output, err, info @@ -67,6 +70,7 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( ) boost::mysql::detail::async_execute_statement( channel& chan, + const statement_base& stmt, const execute_params& params, resultset_base& output, error_info& info, @@ -76,7 +80,7 @@ boost::mysql::detail::async_execute_statement( return async_execute_generic( resultset_encoding::binary, chan, - make_stmt_execute_packet(params), + make_stmt_execute_packet(params, stmt), output, info, std::forward(token) 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 8a553393..15df33c3 100644 --- a/include/boost/mysql/detail/network_algorithms/impl/prepare_statement.hpp +++ b/include/boost/mysql/detail/network_algorithms/impl/prepare_statement.hpp @@ -51,7 +51,7 @@ public: serialize_message(packet, caps_, write_buffer_); } - void process_response(boost::asio::const_buffer message, error_code& err) + void process_response(boost::asio::const_buffer message, void* channel, error_code& err) { deserialization_context ctx (message, caps_); std::uint8_t msg_type = 0; @@ -71,7 +71,7 @@ public: { com_stmt_prepare_ok_packet response; err = deserialize_message(ctx, response); - output_ = statement_base(response); + output_.reset(channel, response); remaining_meta_ = response.num_columns + response.num_params; } } @@ -83,7 +83,7 @@ public: template struct prepare_statement_op : boost::asio::coroutine { - channel chan_; + channel& chan_; prepare_statement_processor processor_; prepare_statement_op( @@ -119,7 +119,7 @@ struct prepare_statement_op : boost::asio::coroutine BOOST_ASIO_CORO_YIELD chan_.async_read_one(chan_.shared_sequence_number(), std::move(self)); // Process response - processor_.process_response(read_message, err); + processor_.process_response(read_message, &chan_, err); if (err) { self.complete(err); @@ -182,7 +182,7 @@ void boost::mysql::detail::prepare_statement( return; // Process response - processor.process_response(read_buffer, err); + processor.process_response(read_buffer, &channel, err); if (err) return; diff --git a/include/boost/mysql/impl/connection.hpp b/include/boost/mysql/impl/connection.hpp index 7160f848..28c5a108 100644 --- a/include/boost/mysql/impl/connection.hpp +++ b/include/boost/mysql/impl/connection.hpp @@ -18,11 +18,6 @@ #include #include #include -#include -#include -#include -#include -#include #include #include #include @@ -129,7 +124,7 @@ boost::mysql::connection::async_handshake( template void boost::mysql::connection::query( boost::string_view query_string, - resultset_base& result, + resultset& result, error_code& err, error_info& info ) @@ -141,7 +136,7 @@ void boost::mysql::connection::query( template void boost::mysql::connection::query( boost::string_view query_string, - resultset_base& result + resultset& result ) { detail::error_block blk; @@ -159,7 +154,7 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( ) boost::mysql::connection::async_query( boost::string_view query_string, - resultset_base& result, + resultset& result, error_info& output_info, CompletionToken&& token ) @@ -178,24 +173,24 @@ boost::mysql::connection::async_query( // Prepare statement template void boost::mysql::connection::prepare_statement( - boost::string_view statement, - statement_base& output, + boost::string_view stmt, + statement& output, error_code& err, error_info& info ) { detail::clear_errors(err, info); - detail::prepare_statement(get_channel(), statement, output, err, info); + detail::prepare_statement(get_channel(), stmt, output, err, info); } template void boost::mysql::connection::prepare_statement( - boost::string_view statement, - statement_base& output + boost::string_view stmt, + statement& output ) { detail::error_block blk; - detail::prepare_statement(get_channel(), statement, output, blk.err, blk.info); + detail::prepare_statement(get_channel(), stmt, output, blk.err, blk.info); blk.check(); } @@ -208,8 +203,8 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( void(boost::mysql::error_code) ) boost::mysql::connection::async_prepare_statement( - boost::string_view statement, - statement_base& output, + boost::string_view stmt, + statement& output, error_info& output_info, CompletionToken&& token ) @@ -217,269 +212,7 @@ boost::mysql::connection::async_prepare_statement( output_info.clear(); return detail::async_prepare_statement( get_channel(), - statement, - output, - output_info, - std::forward(token) - ); -} - - -// Execute statement -template -template -void boost::mysql::connection::execute_statement( - const execute_params& params, - resultset_base& result, - error_code& err, - error_info& info -) -{ - detail::clear_errors(err, info); - detail::execute_statement( - get_channel(), - params, - result, - err, - info - ); -} - -template -template -void boost::mysql::connection::execute_statement( - const execute_params& params, - resultset_base& result -) -{ - detail::error_block blk; - detail::execute_statement( - get_channel(), - params, - result, - blk.err, - blk.info - ); - blk.check(); -} - - -template -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( - CompletionToken, - void(boost::mysql::error_code) -) -boost::mysql::connection::async_execute_statement( - const execute_params& params, - resultset_base& result, - error_info& output_info, - CompletionToken&& token -) -{ - output_info.clear(); - detail::async_execute_statement( - get_channel(), - params, - result, - output_info, - std::forward(token) - ); -} - -// Close statement -template -void boost::mysql::connection::close_statement( - const statement_base& stmt, - error_code& code, - error_info& info -) -{ - detail::clear_errors(code, info); - detail::close_statement(get_channel(), stmt.id(), code, info); -} - -template -void boost::mysql::connection::close_statement( - const statement_base& stmt -) -{ - detail::error_block blk; - detail::close_statement(get_channel(), stmt.id(), blk.err, blk.info); - blk.check(); -} - - -template -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( - CompletionToken, - void(boost::mysql::error_code) -) -boost::mysql::connection::async_close_statement( - const statement_base& stmt, - error_info& output_info, - CompletionToken&& token -) -{ - output_info.clear(); - return detail::async_close_statement( - get_channel(), - stmt.id(), - std::forward(token), - output_info - ); -} - -// Read one row -template -bool boost::mysql::connection::read_one_row( - resultset_base& result, - row_view& output, - error_code& err, - error_info& info -) -{ - detail::clear_errors(err, info); - detail::read_one_row(get_channel(), result, output, err, info); -} - - -template -bool boost::mysql::connection::read_one_row( - resultset_base& result, - row_view& output -) -{ - detail::error_block blk; - bool res = detail::read_one_row(get_channel(), result, output, blk.err, blk.info); - blk.check(); - return res; -} - -template -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( - CompletionToken, - void(boost::mysql::error_code, bool) -) -boost::mysql::connection::async_read_one_row( - resultset_base& result, - row_view& output, - error_info& output_info, - CompletionToken&& token -) -{ - output_info.clear(); - return detail::async_read_one_row( - get_channel(), - result, - output, - output_info, - std::forward(token) - ); -} - -// Read some rows -template -void boost::mysql::connection::read_some_rows( - resultset_base& result, - rows_view& output, - error_code& err, - error_info& info -) -{ - detail::clear_errors(err, info); - detail::read_some_rows(get_channel(), result, output, err, info); -} - - -template -void boost::mysql::connection::read_some_rows( - resultset_base& result, - rows_view& output -) -{ - detail::error_block blk; - detail::read_some_rows(get_channel(), result, output, blk.err, blk.info); - blk.check(); -} - -template -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( - CompletionToken, - void(boost::mysql::error_code) -) -boost::mysql::connection::async_read_some_rows( - resultset_base& result, - rows_view& output, - error_info& output_info, - CompletionToken&& token -) -{ - output_info.clear(); - return detail::async_read_some_rows( - get_channel(), - result, - output, - output_info, - std::forward(token) - ); -} - -// Read all rows -template -void boost::mysql::connection::read_all_rows( - resultset_base& result, - rows_view& output, - error_code& err, - error_info& info -) -{ - detail::clear_errors(err, info); - detail::read_all_rows(get_channel(), result, output, err, info); -} - - -template -void boost::mysql::connection::read_all_rows( - resultset_base& result, - rows_view& output -) -{ - detail::error_block blk; - detail::read_all_rows(get_channel(), result, output, blk.err, blk.info); - blk.check(); -} - -template -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( - CompletionToken, - void(boost::mysql::error_code) -) -boost::mysql::connection::async_read_all_rows( - resultset_base& result, - rows_view& output, - error_info& output_info, - CompletionToken&& token -) -{ - output_info.clear(); - return detail::async_read_all_rows( - get_channel(), - result, + stmt, output, output_info, std::forward(token) diff --git a/include/boost/mysql/impl/resultset.hpp b/include/boost/mysql/impl/resultset.hpp new file mode 100644 index 00000000..bbe82eea --- /dev/null +++ b/include/boost/mysql/impl/resultset.hpp @@ -0,0 +1,160 @@ +// +// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_MYSQL_IMPL_RESULTSET_HPP +#define BOOST_MYSQL_IMPL_RESULTSET_HPP + +#pragma once + +#include +#include +#include +#include + +// Read one row +template +bool boost::mysql::resultset::read_one( + row_view& output, + error_code& err, + error_info& info +) +{ + detail::clear_errors(err, info); + detail::read_one_row(get_channel(), *this, output, err, info); +} + + +template +bool boost::mysql::resultset::read_one( + row_view& output +) +{ + detail::error_block blk; + bool res = detail::read_one_row(get_channel(), *this, output, blk.err, blk.info); + blk.check(); + return res; +} + +template +template +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( + CompletionToken, + void(boost::mysql::error_code, bool) +) +boost::mysql::resultset::async_read_one( + row_view& output, + error_info& output_info, + CompletionToken&& token +) +{ + output_info.clear(); + return detail::async_read_one_row( + get_channel(), + *this, + output, + output_info, + std::forward(token) + ); +} + +// Read some rows +template +void boost::mysql::resultset::read_some( + rows_view& output, + error_code& err, + error_info& info +) +{ + detail::clear_errors(err, info); + detail::read_some_rows(get_channel(), *this, output, err, info); +} + + +template +void boost::mysql::resultset::read_some( + rows_view& output +) +{ + detail::error_block blk; + detail::read_some_rows(get_channel(), *this, output, blk.err, blk.info); + blk.check(); +} + +template +template +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( + CompletionToken, + void(boost::mysql::error_code) +) +boost::mysql::resultset::async_read_some( + rows_view& output, + error_info& output_info, + CompletionToken&& token +) +{ + output_info.clear(); + return detail::async_read_some_rows( + get_channel(), + *this, + output, + output_info, + std::forward(token) + ); +} + +// Read all rows +template +void boost::mysql::resultset::read_all( + rows_view& output, + error_code& err, + error_info& info +) +{ + detail::clear_errors(err, info); + detail::read_all_rows(get_channel(), *this, output, err, info); +} + + +template +void boost::mysql::resultset::read_all( + rows_view& output +) +{ + detail::error_block blk; + detail::read_all_rows(get_channel(), *this, output, blk.err, blk.info); + blk.check(); +} + +template +template +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( + CompletionToken, + void(boost::mysql::error_code) +) +boost::mysql::resultset::async_read_all( + rows_view& output, + error_info& output_info, + CompletionToken&& token +) +{ + output_info.clear(); + return detail::async_read_all_rows( + get_channel(), + *this, + output, + output_info, + std::forward(token) + ); +} + +#endif diff --git a/include/boost/mysql/impl/statement.hpp b/include/boost/mysql/impl/statement.hpp new file mode 100644 index 00000000..cc3de894 --- /dev/null +++ b/include/boost/mysql/impl/statement.hpp @@ -0,0 +1,128 @@ +// +// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_MYSQL_IMPL_STATEMENT_HPP +#define BOOST_MYSQL_IMPL_STATEMENT_HPP + +#pragma once + +#include +#include +#include + + +// Execute statement +template +template +void boost::mysql::statement::execute( + const execute_params& params, + resultset& result, + error_code& err, + error_info& info +) +{ + detail::clear_errors(err, info); + detail::execute_statement( + get_channel(), + *this, + params, + result, + err, + info + ); +} + +template +template +void boost::mysql::statement::execute( + const execute_params& params, + resultset& result +) +{ + detail::error_block blk; + detail::execute_statement( + get_channel(), + *this, + params, + result, + blk.err, + blk.info + ); + blk.check(); +} + + +template +template +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( + CompletionToken, + void(boost::mysql::error_code) +) +boost::mysql::statement::async_execute( + const execute_params& params, + resultset& result, + error_info& output_info, + CompletionToken&& token +) +{ + output_info.clear(); + detail::async_execute_statement( + get_channel(), + *this, + params, + result, + output_info, + std::forward(token) + ); +} + +// Close statement +template +void boost::mysql::statement::close( + error_code& code, + error_info& info +) +{ + detail::clear_errors(code, info); + detail::close_statement(get_channel(), *this, code, info); +} + +template +void boost::mysql::statement::close() +{ + detail::error_block blk; + detail::close_statement(get_channel(), *this, blk.err, blk.info); + blk.check(); +} + + +template +template +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( + CompletionToken, + void(boost::mysql::error_code) +) +boost::mysql::statement::async_close( + error_info& output_info, + CompletionToken&& token +) +{ + output_info.clear(); + return detail::async_close_statement( + get_channel(), + *this, + std::forward(token), + output_info + ); +} + + +#endif diff --git a/include/boost/mysql/resultset.hpp b/include/boost/mysql/resultset.hpp new file mode 100644 index 00000000..3a37b81c --- /dev/null +++ b/include/boost/mysql/resultset.hpp @@ -0,0 +1,221 @@ +// +// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_MYSQL_RESULTSET_HPP +#define BOOST_MYSQL_RESULTSET_HPP + + +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace mysql { + +template +class resultset : public resultset_base +{ +public: + resultset() = default; + resultset(const resultset&) = delete; + resultset(resultset&& other) noexcept : resultset_base(std::move(other)) { other.reset(); } + resultset& operator=(const resultset&) = delete; + resultset& operator=(resultset&& rhs) noexcept { swap(rhs); rhs.reset(); return *this; } + ~resultset() = default; + + using executor_type = typename Stream::executor_type; + + executor_type get_executor() { return get_channel().get_executor(); } + + /** + * \brief Reads a single row (sync with error code version). + * \details Returns `true` if a row was read successfully, `false` if + * there was an error or there were no more rows to read. Calling + * this function on a complete resultset_base always returns `false`. + * + * If the operation succeeds and returns `true`, the new row will be + * read against `output`, possibly reusing its memory. If the operation + * succeeds but returns `false`, `output` will be set to the empty row + * (as if [refmem row clear] was called). If the operation fails, + * `output` is left in a valid but undetrmined state. + */ + bool read_one(row_view& output, error_code& err, error_info& info); + + /** + * \brief Reads a single row (sync with exceptions version). + * \details Returns `true` if a row was read successfully, `false` if + * there was an error or there were no more rows to read. Calling + * this function on a complete resultset_base always returns `false`. + * + * If the operation succeeds and returns `true`, the new row will be + * read against `output`, possibly reusing its memory. If the operation + * succeeds but returns `false`, `output` will be set to the empty row + * (as if [refmem row clear] was called). If the operation fails, + * `output` is left in a valid but undetrmined state. + */ + bool read_one(row_view& output); + + /** + * \brief Reads a single row (async without [reflink error_info] version). + * \details Completes with `true` if a row was read successfully, and with `false` if + * there was an error or there were no more rows to read. Calling + * this function on a complete resultset_base always returns `false`. + * + * If the operation succeeds and completes with `true`, the new row will be + * read against `output`, possibly reusing its memory. If the operation + * succeeds but completes with `false`, `output` will be set to the empty row + * (as if [refmem row clear] was called). If the operation fails, + * `output` is left in a valid but undetrmined state. + * + * The handler signature for this operation is + * `void(boost::mysql::error_code, bool)`. + */ + template < + BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, bool)) + CompletionToken + BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) + > + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, bool)) + async_read_one( + row_view& output, + CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) + ) + { + return async_read_one(output, get_channel().shared_info(), std::forward(token)); + } + + /** + * \brief Reads a single row (async with [reflink error_info] version). + * \details Completes with `true` if a row was read successfully, and with `false` if + * there was an error or there were no more rows to read. Calling + * this function on a complete resultset_base always returns `false`. + * + * If the operation succeeds and completes with `true`, the new row will be + * read against `output`, possibly reusing its memory. If the operation + * succeeds but completes with `false`, `output` will be set to the empty row + * (as if [refmem row clear] was called). If the operation fails, + * `output` is left in a valid but undetrmined state. + * + * The handler signature for this operation is + * `void(boost::mysql::error_code, bool)`. + */ + template < + BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, bool)) + CompletionToken + BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) + > + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, bool)) + async_read_one( + row_view& output, + error_info& output_info, + CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) + ); + + void read_some(rows_view& output, error_code& err, error_info& info); + void read_some(rows_view& output); + + /** + * \brief Reads several rows, up to a maximum + * (async without [reflink error_info] version). + * \details + * The handler signature for this operation is + * `void(boost::mysql::error_code, std::vector)`. + */ + template < + BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) + CompletionToken + BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) + > + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) + async_read_some( + rows_view& output, + CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) + ) + { + return async_read_some(output, get_channel().shared_info(), std::forward(token)); + } + + /** + * \brief Reads several rows, up to a maximum + * (async with [reflink error_info] version). + * \details + * The handler signature for this operation is + * `void(boost::mysql::error_code, std::vector)`. + */ + template < + BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) + CompletionToken + BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) + > + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) + async_read_some( + rows_view& output, + error_info& output_info, + CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) + ); + + /// Reads all available rows (sync with error code version). + void read_all(rows_view& output, error_code& err, error_info& info); + + /// Reads all available rows (sync with exceptions version). + void read_all(rows_view& output); + + /** + * \brief Reads all available rows (async without [reflink error_info] version). + * \details + * The handler signature for this operation is + * `void(boost::mysql::error_code, std::vector)`. + */ + template < + BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) + CompletionToken + BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) + > + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) + async_read_all( + rows_view& output, + CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) + ) + { + return async_read_all(output, get_channel().shared_info(), std::forward(token)); + } + + /** + * \brief Reads all available rows (async with [reflink error_info] version). + * \details + * The handler signature for this operation is + * `void(boost::mysql::error_code, std::vector)`. + */ + template < + BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) + CompletionToken + BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) + > + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) + async_read_all( + rows_view& output, + error_info& output_info, + CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) + ); + +private: + detail::channel& get_channel() noexcept + { + assert(valid()); + return *static_cast*>(channel_ptr()); + } +}; + +} // mysql +} // boost + +#include + +#endif diff --git a/include/boost/mysql/resultset_base.hpp b/include/boost/mysql/resultset_base.hpp index 30ca0701..2bf143d4 100644 --- a/include/boost/mysql/resultset_base.hpp +++ b/include/boost/mysql/resultset_base.hpp @@ -71,7 +71,7 @@ class resultset_base boost::string_view info() const noexcept { assert(has_data_); return info_; } }; - bool valid_ {false}; + void* channel_ {nullptr}; std::uint8_t seqnum_ {}; detail::resultset_encoding encoding_ { detail::resultset_encoding::text }; std::vector meta_; @@ -84,23 +84,12 @@ public: #ifndef BOOST_MYSQL_DOXYGEN // Private, do not use. TODO: hide these - resultset_base(std::vector&& meta, detail::resultset_encoding encoding) noexcept: - valid_(true), - encoding_(encoding), - meta_(std::move(meta)) - { - }; - resultset_base(const detail::ok_packet& ok_pack): - valid_(true), - ok_packet_(ok_pack) - { - }; - void reset( + void* channel, detail::resultset_encoding encoding - ) + ) noexcept { - valid_ = true; + channel_ = channel; seqnum_ = 0; encoding_ = encoding; meta_.clear(); @@ -109,7 +98,7 @@ public: void complete(const detail::ok_packet& ok_pack) { - assert(valid_); + assert(valid()); ok_packet_.assign(ok_pack); } @@ -137,7 +126,7 @@ public: * Calling any member function on an invalid resultset_base, * other than assignment, results in undefined behavior. */ - bool valid() const noexcept { return valid_; } + bool valid() const noexcept { return channel_ != nullptr; } /// \brief Returns whether the resultset_base has been completely read or not. /// \details See [link mysql.resultsets.complete this section] for more info. @@ -179,6 +168,10 @@ public: * until the resultset_base object is destroyed. */ boost::string_view info() const noexcept { return ok_packet_.info(); } +protected: + void* channel_ptr() noexcept { return channel_; } + void reset() noexcept { reset(nullptr, detail::resultset_encoding::text); } + void swap(resultset_base& other) noexcept { std::swap(*this, other); } }; } // mysql diff --git a/include/boost/mysql/statement.hpp b/include/boost/mysql/statement.hpp new file mode 100644 index 00000000..7c6c9e90 --- /dev/null +++ b/include/boost/mysql/statement.hpp @@ -0,0 +1,327 @@ +// +// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_MYSQL_STATEMENT_HPP +#define BOOST_MYSQL_STATEMENT_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace mysql { + +template +class statement : public statement_base +{ +public: + statement() = default; + statement(const statement&) = delete; + statement(statement&& other) noexcept : statement_base(other) { other.reset(); } + statement& operator=(const statement&) = delete; + statement& operator=(statement&& rhs) noexcept { swap(std::move(rhs)); return *this; } + ~statement() = default; + + using executor_type = typename Stream::executor_type; + + executor_type get_executor() { return get_channel().get_executor(); } + + /** + * \brief Executes a statement (collection, sync with error code version). + * \details + * FieldViewCollection should meet the [reflink FieldViewCollection] requirements. + * + * After this function has returned, you should read the entire resultset_base + * before calling any function that involves communication with the server over this + * connection. Otherwise, the results are undefined. + */ + template > + void execute( + const FieldViewCollection& params, + resultset& result, + error_code& err, + error_info& info + ) + { + return execute(make_execute_params(params), result, err, info); + } + + /** + * \brief Executes a statement (collection, sync with exceptions version). + * \details + * FieldViewCollection should meet the [reflink FieldViewCollection] requirements. + * + * After this function has returned, you should read the entire resultset_base + * before calling any function that involves communication with the server over this + * connection. Otherwise, the results are undefined. + */ + template > + void execute( + const FieldViewCollection& params, + resultset& result + ) + { + return execute_statement(make_execute_params(params), result); + } + + /** + * \brief Executes a statement (collection, + * async without [reflink error_info] version). + * \details + * FieldViewCollection should meet the [reflink FieldViewCollection] requirements. + * + * After this operation completes, you should read the entire resultset_base + * before calling any function that involves communication with the server over this + * connection. Otherwise, the results are undefined. + * It is __not__ necessary to keep the collection of parameters or the + * values they may point to alive after the initiating function returns. + * + * The handler signature for this operation is + * `void(boost::mysql::error_code, boost::mysql::resultset_base)`. + */ + template< + class FieldViewCollection, + BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) + CompletionToken + BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type), + class EnableIf = detail::enable_if_field_view_collection + > + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) + async_execute( + const FieldViewCollection& params, + resultset& result, + CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) + ) + { + return async_execute_statement( + make_execute_params(params), + result, + std::forward(token) + ); + } + + /** + * \brief Executes a statement (collection, + * async with [reflink error_info] version). + * \details + * FieldViewCollection should meet the [reflink FieldViewCollection] requirements. + * + * After this operation completes, you should read the entire resultset_base + * before calling any function that involves communication with the server over this + * connection. Otherwise, the results are undefined. + * It is __not__ necessary to keep the collection of parameters or the + * values they may point to alive after the initiating function returns. + * + * The handler signature for this operation is + * `void(boost::mysql::error_code, boost::mysql::resultset_base)`. + */ + template < + class FieldViewCollection, + BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) + CompletionToken + BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type), + class EnableIf = detail::enable_if_field_view_collection + > + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) + async_execute( + const FieldViewCollection& params, + resultset& result, + error_info& output_info, + CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) + ) + { + return async_execute_statement( + make_execute_params(params), + result, + output_info, + std::forward(token) + ); + } + + + /** + * \brief Executes a statement (`execute_params`, sync with error code version). + * \details + * FieldViewFwdIterator should meet the [reflink FieldViewFwdIterator] requirements. + * The range \\[`params.first()`, `params.last()`) will be used as parameters for + * statement execution. They should be a valid iterator range. + * + * After this function has returned, you should read the entire resultset_base + * before calling any function that involves communication with the server over this + * connection. Otherwise, the results are undefined. + */ + template + void execute( + const execute_params& params, + resultset& result, + error_code& ec, + error_info& info + ); + + /** + * \brief Executes a statement (`execute_params`, sync with exceptions version). + * \details + * FieldViewFwdIterator should meet the [reflink FieldViewFwdIterator] requirements. + * The range \\[`params.first()`, `params.last()`) will be used as parameters for + * statement execution. They should be a valid iterator range. + * + * After this function has returned, you should read the entire resultset_base + * before calling any function that involves communication with the server over this + * connection. Otherwise, the results are undefined. + */ + template + void execute( + const execute_params& params, + resultset& result + ); + + /** + * \brief Executes a statement (`execute_params`, + * async without [reflink error_info] version). + * \details + * FieldViewFwdIterator should meet the [reflink FieldViewFwdIterator] requirements. + * The range \\[`params.first()`, `params.last()`) will be used as parameters for + * statement execution. They should be a valid iterator range. + * + * After this operation completes, you should read the entire resultset_base + * before calling any function that involves communication with the server over this + * connection. Otherwise, the results are undefined. + * + * It is __not__ necessary to keep the objects in + * \\[`params.first()`, `params.last()`) or the + * values they may point to alive after the initiating function returns. + * + * The handler signature for this operation is + * `void(boost::mysql::error_code, boost::mysql::resultset_base)`. + */ + template < + class FieldViewFwdIterator, + BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) + CompletionToken + BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) + > + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) + async_execute( + const execute_params& params, + resultset& result, + CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) + ) + { + return async_execute(params, result, get_channel().shared_info(), std::forward(token)); + } + + /** + * \brief Executes a statement (`execute_params`, + * async with [reflink error_info] version). + * \details + * FieldViewFwdIterator should meet the [reflink FieldViewFwdIterator] requirements. + * The range \\[`params.first()`, `params.last()`) will be used as parameters for + * statement execution. They should be a valid iterator range. + * + * After this operation completes, you should read the entire resultset_base + * before calling any function that involves communication with the server over this + * connection. Otherwise, the results are undefined. + * + * It is __not__ necessary to keep the objects in + * \\[`params.first()`, `params.last()`) or the + * values they may point to alive after the initiating function returns. + * + * The handler signature for this operation is + * `void(boost::mysql::error_code, boost::mysql::resultset_base)`. + */ + template < + class FieldViewFwdIterator, + BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) + CompletionToken + BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) + > + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) + async_execute( + const execute_params& params, + resultset& result, + error_info& output_info, + CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) + ); + + + /** + * \brief Closes a prepared statement, deallocating it from the server + (sync with error code version). + * \details + * After calling this function, no further functions may be called on this prepared + * statement, other than assignment. Failing to do so results in undefined behavior. + */ + void close(error_code&, error_info&); + + /** + * \brief Closes a prepared statement, deallocating it from the server + (sync with exceptions version). + * \details + * After calling this function, no further functions may be called on this prepared + * statement, other than assignment. Failing to do so results in undefined behavior. + */ + void close(); + + /** + * \brief Closes a prepared statement, deallocating it from the server + (async without [reflink error_info] version). + * \details + * After the operation completes, no further functions may be called on this prepared + * statement, other than assignment. Failing to do so results in undefined behavior. + * + * The handler signature for this operation is `void(boost::mysql::error_code)`. + */ + template < + BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) + CompletionToken + BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) + > + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) + async_close( + CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) + ) + { + return async_close(get_channel().shared_info(), std::forward(token)); + } + + /** + * \brief Closes a prepared statement, deallocating it from the server + (async with [reflink error_info] version). + * \details + * After the operation completes, no further functions may be called on this prepared + * statement, other than assignment. Failing to do so results in undefined behavior. + * + * The handler signature for this operation is `void(boost::mysql::error_code)`. + */ + template < + BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) + CompletionToken + BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type) + > + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code)) + async_close( + error_info& output_info, + CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) + ); + +private: + detail::channel& get_channel() noexcept + { + assert(valid()); + return *static_cast*>(channel_ptr()); + } +}; + +} // mysql +} // boost + +#include + +#endif diff --git a/include/boost/mysql/statement_base.hpp b/include/boost/mysql/statement_base.hpp index 74ab0f4b..4de45b1f 100644 --- a/include/boost/mysql/statement_base.hpp +++ b/include/boost/mysql/statement_base.hpp @@ -41,8 +41,6 @@ constexpr std::array no_statement_params {}; */ class statement_base { - bool valid_ {false}; - detail::com_stmt_prepare_ok_packet stmt_msg_; public: /** * \brief Default constructor. @@ -52,8 +50,16 @@ public: #ifndef BOOST_MYSQL_DOXYGEN // Private. Do not use. TODO: hide this - statement_base(const detail::com_stmt_prepare_ok_packet& msg) noexcept: - valid_(true), stmt_msg_(msg) {} + void reset( + void* channel, + const detail::com_stmt_prepare_ok_packet& msg + ) noexcept + { + channel_ = channel; + stmt_msg_ = msg; + } + + void reset() noexcept { channel_ = nullptr; } #endif /** @@ -61,7 +67,7 @@ public: * \details Calling any function other than assignment on an statement for which * this function returns `false` results in undefined behavior. */ - bool valid() const noexcept { return valid_; } + bool valid() const noexcept { return channel_ != nullptr; } /// Returns a server-side identifier for the statement (unique in a per-connection basis). std::uint32_t id() const noexcept { assert(valid()); return stmt_msg_.statement_id; } @@ -69,6 +75,12 @@ public: /// Returns the number of parameters that should be provided when executing the statement. unsigned num_params() const noexcept { assert(valid()); return stmt_msg_.num_params; } +protected: + void* channel_ptr() noexcept { return channel_; } + void swap(statement_base& other) noexcept { std::swap(*this, other); } +private: + void* channel_ {nullptr}; + detail::com_stmt_prepare_ok_packet stmt_msg_; }; } // mysql diff --git a/include/boost/mysql/tcp.hpp b/include/boost/mysql/tcp.hpp index d5799750..66568018 100644 --- a/include/boost/mysql/tcp.hpp +++ b/include/boost/mysql/tcp.hpp @@ -17,6 +17,9 @@ namespace mysql { /// A connection to MySQL over a TCP socket. using tcp_connection = connection; +using tcp_statement = typename tcp_connection::statement_type; +using tcp_resultset = typename tcp_connection::resultset_type; + } // mysql } // boost diff --git a/include/boost/mysql/tcp_ssl.hpp b/include/boost/mysql/tcp_ssl.hpp index 5ca3a42b..75dc1fd7 100644 --- a/include/boost/mysql/tcp_ssl.hpp +++ b/include/boost/mysql/tcp_ssl.hpp @@ -17,6 +17,8 @@ namespace mysql { /// A connection to MySQL over a TCP socket using TLS. using tcp_ssl_connection = connection>; +using tcp_ssl_statement = typename tcp_ssl_connection::statement_type; +using tcp_ssl_resultset = typename tcp_ssl_connection::resultset_type; } // mysql } // boost diff --git a/include/boost/mysql/unix.hpp b/include/boost/mysql/unix.hpp index 62f72c0f..b7b04113 100644 --- a/include/boost/mysql/unix.hpp +++ b/include/boost/mysql/unix.hpp @@ -18,6 +18,8 @@ namespace mysql { /// A connection to MySQL over a UNIX domain socket. using unix_connection = connection; +using unix_statement = typename unix_connection::statement_type; +using unix_resultset = typename unix_connection::resultset_type; #endif diff --git a/include/boost/mysql/unix_ssl.hpp b/include/boost/mysql/unix_ssl.hpp index e2539703..8382a71c 100644 --- a/include/boost/mysql/unix_ssl.hpp +++ b/include/boost/mysql/unix_ssl.hpp @@ -19,6 +19,8 @@ namespace mysql { /// A connection to MySQL over a UNIX domain socket over TLS. using unix_ssl_connection = connection>; +using unix_ssl_statement = typename unix_ssl_connection::statement_type; +using unix_ssl_resultset = typename unix_ssl_connection::resultset_type; #endif