Added support for use_awaitable

Co-authored-by: ruben <rubenperez038@gmail.com>
Co-authored-by: Alex Hodges <alexander.hodges11@live.co.uk>
This commit is contained in:
Richard Hodges 2020-05-21 10:50:16 +02:00 committed by ruben
parent 76f8123e37
commit 3c420b8683
58 changed files with 2495 additions and 1808 deletions

View File

@ -16,24 +16,18 @@ matrix:
fast_finish: true
build_script:
- mkdir C:\boost_1_73_0
- git clone https://github.com/anarthal/boost-unix-mirror.git boost-latest
- cd boost-latest
- .\bootstrap.bat --prefix=C:\boost_1_73_0
- .\b2 --prefix=C:\boost_1_73_0 --with-system --with-context --with-coroutine --with-date_time -d0 install
- cd ..
- cp ci\ca-cert.pem C:\
- cp ci\server-cert.pem C:\
- cp ci\server-key.pem C:\
- cp ci\windows-ci.cnf C:\
- 'echo !include C:\\windows-ci.cnf >> "C:\Program Files\MySQL\MySQL Server 5.7\my.ini"'
- net stop MySQL57 && net start MySQL57
- set PATH=C:\Program Files\MySQL\MySQL Server 5.7\bin;C:\Libraries\boost_1_71_0\lib64-msvc-14.2;C:\Libraries\boost_1_71_0\lib32-msvc-14.2;%PATH%
- set PATH=C:\Program Files\MySQL\MySQL Server 5.7\bin;C:\Libraries\boost_1_73_0\lib64-msvc-14.2;C:\Libraries\boost_1_73_0\lib32-msvc-14.2;%PATH%
- mysqladmin --user=root password -pPassword12! ""
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" %PLATFORM%
- mkdir build
- cd build
- cmake -G Ninja -DBOOST_ROOT=C:\Libraries\boost_1_71_0 -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl ..
- cmake -G Ninja -DBOOST_ROOT=C:\Libraries\boost_1_73_0 -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl ..
- cmake --build . -j
test_script:

View File

@ -48,6 +48,18 @@ language: cpp
matrix:
include:
- name: linux_clang10_x64_debug_mysql
<<: *__linux_mysql_defaults
compiler: clang
env:
- CMAKE_BUILD_TYPE=Debug
- DATABASE=mysql
- USE_COVERAGE=1
- CMAKE_CXX_FLAGS="-stdlib=libc++ -DBOOST_ASIO_DISABLE_CONCEPTS"
- CMAKE_OPTIONS="-DCMAKE_C_COMPILER=clang-10 -DCMAKE_CXX_COMPILER=clang++-10 -DCMAKE_CXX_STANDARD=20"
install:
- sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"
- sudo apt install clang-10 libc++-10-dev libc++abi-10-dev
- name: linux_gcc_x64_debug_mysql
<<: *__linux_mysql_defaults
compiler: gcc

View File

@ -34,7 +34,6 @@ Finally, link your target against the **Boost_mysql** interface library, and you
- C++17 capable compiler (tested with gcc 7.4, clang 7.0, Apple clang 11.0, MSVC 19.25).
- Boost 1.70 or higher. The following Boost libraries are used:
- Boost.Asio (and in consequence, Boost.System).
- Boost.Beast (implementation dependency, we are working in removing it).
- Boost.Lexical_Cast.
- Boost.Endian.
- OpenSSL.

View File

@ -53,7 +53,6 @@ Technical debt
Review named_param
Take fetch_many() algorithm out into network_algorithms (e.g. read_many_rows)
Concept checking for StreamType
See if we can drop the dependence on beast
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?

View File

@ -65,6 +65,7 @@ cmake -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE \
$(if [ $USE_VALGRIND ]; then echo -DBOOST_MYSQL_VALGRIND_TESTS=ON; fi) \
$(if [ $USE_COVERAGE ]; then echo -DBOOST_MYSQL_COVERAGE=ON; fi) \
$(if [ $HAS_SHA256 ]; then echo -DBOOST_MYSQL_SHA256_TESTS=ON; fi) \
-DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS" \
$CMAKE_OPTIONS \
..
make -j6 CTEST_OUTPUT_ON_FAILURE=1 all test

View File

@ -78,7 +78,8 @@ EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH = ../include
INCLUDE_FILE_PATTERNS =
PREDEFINED = BOOST_MYSQL_DOXYGEN
PREDEFINED = BOOST_MYSQL_DOXYGEN \
"BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(ct, sig)=auto"
SKIP_FUNCTION_MACROS = NO
# Source browsing

View File

@ -37,7 +37,6 @@
* - C++17 capable compiler (tested with gcc 7.4, clang 7.0, Apple clang 11.0, MSVC 19.25).
* - Boost 1.70 or higher. The following Boost libraries are used:
* - Boost.Asio (and in consequence, Boost.System).
* - Boost.Beast (implementation dependency, we are working in removing it).
* - Boost.Lexical_Cast.
* - Boost.Endian.
* - OpenSSL.
@ -88,6 +87,11 @@
* results, using coroutines.
* \include query_async_coroutines.cpp
*
* \subsection query_async_coroutinescpp20 Query, asynchronous with C++20 coroutines
* This example demonstrates issuing text queries to the server and reading
* results, using C++20 coroutines (boost::asio::use_awaitable).
* \include query_async_coroutinescpp20.cpp
*
* \subsection prepared_statements Prepared statements
* This example demonstrates preparing statements, executing them
* and reading back the results. It employs synchronous functions.

View File

@ -49,6 +49,7 @@ set(MYSQL_EXAMPLES
query_sync
query_async_callbacks
query_async_coroutines
query_async_coroutinescpp20
query_async_futures
metadata
prepared_statements

View File

@ -6,7 +6,7 @@
//
#include "boost/mysql/mysql.hpp"
#include <boost/asio/io_service.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/system/system_error.hpp>
#include <iostream>

View File

@ -6,7 +6,7 @@
//
#include "boost/mysql/mysql.hpp"
#include <boost/asio/io_service.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/system/system_error.hpp>
#include <iostream>

View File

@ -6,7 +6,7 @@
//
#include "boost/mysql/mysql.hpp"
#include <boost/asio/io_service.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/system/system_error.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/asio/yield.hpp>
@ -29,7 +29,8 @@ using boost::mysql::owning_row;
* 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.
* asynchronous models, and thus may be used with callbacks, Boost stackful
* coroutines, C++20 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

View File

@ -6,7 +6,7 @@
//
#include "boost/mysql/mysql.hpp"
#include <boost/asio/io_service.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/system/system_error.hpp>
#include <boost/asio/spawn.hpp>
#include <iostream>
@ -26,7 +26,8 @@ using boost::mysql::error_info;
* 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.
* asynchronous models, and thus may be used with callbacks, Boost stackful
* coroutines, C++20 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
@ -91,7 +92,7 @@ void main_impl(int argc, char** argv)
* (see https://www.boost.org/doc/libs/1_72_0/doc/html/boost_asio/reference/spawn.html).
*
* The coroutine will actually start running when we call io_context::run().
* It will suspend every time we call one of the asyncrhonous functions, saving
* It will suspend every time we call one of the asynchronous functions, saving
* all information it needs for resuming. When the asynchronous operation completes,
* the coroutine will resume in the point it was left.
*

View File

@ -0,0 +1,214 @@
//
// Copyright (c) 2019-2020 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)
//
#include "boost/mysql/mysql.hpp"
#include <boost/asio/io_context.hpp>
#include <boost/system/system_error.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/use_awaitable.hpp>
#include <boost/asio/awaitable.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/use_future.hpp>
#include <iostream>
using boost::mysql::error_code;
using boost::mysql::error_info;
#ifdef BOOST_ASIO_HAS_CO_AWAIT
/**
* For this example, we will be using the 'boost_mysql_examples' database.
* You can get this database by running db_setup.sql.
* This example assumes you are connecting to a localhost MySQL server.
*
* This example uses asynchronous functions with C++20 coroutines
* (boost::asio::use_awaitable).
*
* 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, Boost stackful
* coroutines, C++20 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<StreamType>).
*
* 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)
{
std::cout << "Employee '"
<< employee.values()[0] << " " // first_name (type std::string_view)
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
}
/**
* A boost::asio::io_context plus a thread that calls context.run().
* We encapsulate this here to ensure correct shutdown even in case of
* error (exception), when we should first reset the work guard, to
* stop the io_context, and then join the thread. Failing to do so
* may cause your application to not stop (if the work guard is not
* reset) or to terminate badly (if the thread is not joined).
*/
class application
{
boost::asio::io_context ctx_;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> guard_;
std::thread runner_;
public:
application(): guard_(ctx_.get_executor()), runner_([this] { ctx_.run(); }) {}
application(const application&) = delete;
application(application&&) = delete;
application& operator=(const application&) = delete;
application& operator=(application&&) = delete;
~application()
{
guard_.reset();
runner_.join();
}
boost::asio::io_context& context() { return ctx_; }
};
/**
* Our coroutine. It must have a return type of boost::asio::awaitable<T>.
* Our coroutine does not communicate any result back, so T=void.
* Remember that you do not have to explicitly create any awaitable<void> in
* your function. Instead, the return type is fed to std::coroutine_traits
* to determine the semantics of the coroutine, like the promise type.
* Asio already takes care of all this for us.
*
* The coroutine will suspend every time we call one of the asynchronous functions, saving
* all information it needs for resuming. When the asynchronous operation completes,
* the coroutine will resume in the point it was left.
*
* The return type of an asynchronous operation that uses boost::asio::use_awaitable
* as completion token is a boost::asio::awaitable<T>, where T
* is the second argument to the handler signature for the asynchronous operation.
* For example, connection::query has a handler
* signature of void(error_code, resultset<Stream>), so async_query will return
* a boost::asio::awaitable<boost::mysql::resultset<Stream>>. The return type of
* calling co_await on such a expression would be a boost::mysql::resultset<Stream>.
* If any of the asyncrhonous operations fail, an exception will be raised
* within the coroutine.
*/
boost::asio::awaitable<void> start_query(
boost::asio::executor ex,
const boost::asio::ip::tcp::endpoint& ep,
const boost::mysql::connection_params& params
)
{
boost::mysql::tcp_connection conn (ex);
// Connect to server
co_await conn.async_connect(ep, params, boost::asio::use_awaitable);
// Issue the query to the server. Note that async_query returns a
// boost::asio::awaitable<boost::mysql::tcp_resultset>
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
boost::mysql::tcp_resultset result =
co_await conn.async_query(sql, boost::asio::use_awaitable);
/**
* Get all rows in the resultset. We will employ resultset::async_fetch_one(),
* which returns a single row at every call. The returned row is a pointer
* to memory owned by the resultset, and is re-used for each row. Thus, returned
* 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* row =
co_await result.async_fetch_one(boost::asio::use_awaitable))
{
print_employee(*row);
}
// Notify the MySQL server we want to quit, then close the underlying connection.
co_await conn.async_close(boost::asio::use_awaitable);
}
void main_impl(int argc, char** argv)
{
if (argc != 3)
{
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
exit(1);
}
// io_context plus runner thread
application app;
// Connection parameters
boost::asio::ip::tcp::endpoint ep (
boost::asio::ip::address_v4::loopback(), // host
boost::mysql::default_port // port
);
boost::mysql::connection_params params (
argv[1], // username
argv[2], // password
"boost_mysql_examples" // database to use; leave empty or omit the parameter for no database
);
/**
* The entry point. We spawn a thread of execution to run our
* coroutine using boost::asio::co_spawn. We pass in a function returning
* a boost::asio::awaitable<void>, as required.
*
* We pass in a callback to co_spawn. It will be called when
* the coroutine completes, with an exception_ptr if there was any error
* during execution. We use a promise to wait for the coroutine completion
* and transmit any raised exception.
*/
auto executor = app.context().get_executor();
std::promise<void> prom;
boost::asio::co_spawn(executor, [executor, ep, params] {
return start_query(executor, ep, params);
}, [&prom](std::exception_ptr err) {
prom.set_exception(std::move(err));
});
prom.get_future().get();
}
#else
void main_impl(int, char**)
{
std::cout << "Sorry, your compiler does not support C++20 coroutines" << std::endl;
}
#endif
int main(int argc, char** argv)
{
try
{
main_impl(argc, argv);
}
catch (const boost::system::system_error& err)
{
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
return 1;
}
catch (const std::exception& err)
{
std::cerr << "Error: " << err.what() << std::endl;
return 1;
}
}

View File

@ -6,7 +6,7 @@
//
#include "boost/mysql/mysql.hpp"
#include <boost/asio/io_service.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/system/system_error.hpp>
#include <boost/asio/use_future.hpp>
#include <iostream>
@ -27,7 +27,8 @@ using boost::asio::use_future;
* 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.
* asynchronous models, and thus may be used with callbacks, Boost stackful
* coroutines, C++20 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

View File

@ -6,7 +6,7 @@
//
#include "boost/mysql/mysql.hpp"
#include <boost/asio/io_service.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/system/system_error.hpp>
#include <iostream>

View File

@ -6,7 +6,7 @@
//
#include "boost/mysql/mysql.hpp"
#include <boost/asio/io_service.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/system/system_error.hpp>
#include <iostream>

View File

@ -11,7 +11,6 @@
#include "boost/mysql/detail/protocol/channel.hpp"
#include "boost/mysql/detail/protocol/protocol_types.hpp"
#include "boost/mysql/detail/network_algorithms/handshake.hpp"
#include "boost/mysql/detail/auxiliar/async_result_macro.hpp"
#include "boost/mysql/error.hpp"
#include "boost/mysql/resultset.hpp"
#include "boost/mysql/prepared_statement.hpp"
@ -82,6 +81,12 @@ public:
{
}
/// The executor type associated to this object.
using executor_type = typename Stream::executor_type;
/// Retrieves the executor associated to this object.
executor_type get_executor() { return next_layer_.get_executor(); }
/// Retrieves the underlying Stream object.
Stream& next_layer() { return next_layer_; }
@ -114,8 +119,8 @@ public:
* \details The strings pointed to by params should be kept alive by the caller
* until the operation completes, as no copy is made by the library.
*/
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, handshake_signature)
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(handshake_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, handshake_signature)
async_handshake(const connection_params& params, CompletionToken&& token, error_info* info = nullptr);
/**
@ -141,8 +146,8 @@ public:
using query_signature = void(error_code, resultset<Stream>);
/// Executes a SQL text query (async version).
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, query_signature)
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(query_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, query_signature)
async_query(std::string_view query_string, CompletionToken&& token, error_info* info=nullptr);
/**
@ -165,8 +170,8 @@ public:
using prepare_statement_signature = void(error_code, prepared_statement<Stream>);
/// Prepares a statement (async version).
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, prepare_statement_signature)
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(prepare_statement_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, prepare_statement_signature)
async_prepare_statement(std::string_view statement, CompletionToken&& token, error_info* info=nullptr);
/**
@ -196,8 +201,8 @@ public:
* \brief Notifies the MySQL server that we want to end the session and quit the connection
* (async version).
*/
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, quit_signature)
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(quit_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, quit_signature)
async_quit(CompletionToken&& token, error_info* info=nullptr);
};
@ -247,8 +252,8 @@ public:
* \details The strings pointed to by params should be kept alive by the caller
* until the operation completes, as no copy is made by the library.
*/
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, connect_signature)
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(connect_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, connect_signature)
async_connect(const endpoint_type& endpoint, const connection_params& params,
CompletionToken&& token, error_info* output_info=nullptr);
@ -270,8 +275,8 @@ public:
using close_signature = void(error_code);
/// Closes the connection (async version).
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, close_signature)
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(close_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, close_signature)
async_close(CompletionToken&& token, error_info* info=nullptr);
};

View File

@ -1,20 +0,0 @@
//
// Copyright (c) 2019-2020 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_DETAIL_AUXILIAR_ASYNC_RESULT_MACRO_HPP
#define BOOST_MYSQL_DETAIL_AUXILIAR_ASYNC_RESULT_MACRO_HPP
#include <boost/asio/async_result.hpp>
#ifdef BOOST_MYSQL_DOXYGEN
#define BOOST_MYSQL_INITFN_RESULT_TYPE(ct, sig) DEDUCED
#else
#define BOOST_MYSQL_INITFN_RESULT_TYPE(ct, sig) \
BOOST_ASIO_INITFN_RESULT_TYPE(ct, sig)
#endif
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_AUXILIAR_ASYNC_RESULT_MACRO_HPP_ */

View File

@ -1,65 +0,0 @@
//
// Copyright (c) 2019-2020 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_DETAIL_AUXILIAR_CHECK_COMPLETION_TOKEN_HPP
#define BOOST_MYSQL_DETAIL_AUXILIAR_CHECK_COMPLETION_TOKEN_HPP
#include <type_traits>
#include <boost/asio/async_result.hpp>
#include "boost/mysql/error.hpp"
namespace boost {
namespace mysql {
namespace detail {
template <typename HandlerSignature>
struct get_handler_arg;
template <typename T>
struct get_handler_arg<void(error_code, T)>
{
using type = T;
};
template <>
struct get_handler_arg<void(error_code)>
{
using type = void;
};
template <typename HandlerType, typename HandlerArg>
constexpr bool is_handler_signature_ok()
{
if constexpr (std::is_same_v<HandlerArg, void>)
{
return std::is_invocable_v<HandlerType, error_code>;
}
else
{
return std::is_invocable_v<HandlerType, error_code, HandlerArg>;
}
}
template <typename CompletionToken, typename HandlerSignature>
constexpr void check_completion_token()
{
using handler_type = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using handler_arg = typename detail::get_handler_arg<HandlerSignature>::type;
static_assert(
is_handler_signature_ok<handler_type, handler_arg>(),
"Invalid CompletionToken type. Check that CompletionToken fullfills the CompletionToken "
"requirements or that the callback signature you passed in is correct"
);
}
} // detail
} // mysql
} // boost
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_AUXILIAR_CHECK_COMPLETION_TOKEN_HPP_ */

View File

@ -21,10 +21,10 @@ void close_connection(
error_info& info
);
using close_connection_signature = empty_signature;
using close_connection_signature = void(error_code);
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, close_connection_signature)
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, close_connection_signature)
async_close_connection(
channel<StreamType>& chan,
CompletionToken&& token,

View File

@ -22,10 +22,10 @@ void close_statement(
error_info& info
);
using close_signature = empty_signature;
using close_signature = void(error_code);
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, close_signature)
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, close_signature)
async_close_statement(
channel<StreamType>& chan,
std::uint32_t statement_id,

View File

@ -23,11 +23,6 @@ using deserialize_row_fn = error_code (*)(
std::vector<value>&
);
using empty_signature = void(error_code);
template <typename T>
using r_handler_signature = void(error_code, T);
} // detail
} // mysql
} // boost

View File

@ -24,10 +24,10 @@ void connect(
error_info& info
);
using connect_signature = empty_signature;
using connect_signature = void(error_code);
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, connect_signature)
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, connect_signature)
async_connect(
channel<StreamType>& chan,
const typename StreamType::endpoint_type& endpoint,

View File

@ -30,7 +30,7 @@ template <typename StreamType>
using execute_generic_signature = void(error_code, resultset<StreamType>);
template <typename StreamType, typename Serializable, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, execute_generic_signature<StreamType>)
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, execute_generic_signature<StreamType>)
async_execute_generic(
deserialize_row_fn deserializer,
channel<StreamType>& chan,

View File

@ -27,7 +27,7 @@ void execute_query(
);
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, execute_generic_signature<StreamType>)
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, execute_generic_signature<StreamType>)
async_execute_query(
channel<StreamType>& chan,
std::string_view query,

View File

@ -29,7 +29,7 @@ void execute_statement(
);
template <typename StreamType, typename ForwardIterator, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, execute_generic_signature<StreamType>)
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, execute_generic_signature<StreamType>)
async_execute_statement(
channel<StreamType>& chan,
std::uint32_t statement_id,

View File

@ -27,10 +27,10 @@ void handshake(
error_info& info
);
using handshake_signature = empty_signature;
using handshake_signature = void(error_code);
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, handshake_signature)
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, handshake_signature)
async_handshake(
channel<StreamType>& channel,
const connection_params& params,

View File

@ -29,8 +29,57 @@ void boost::mysql::detail::close_connection(
}
}
namespace boost {
namespace mysql {
namespace detail {
template<class StreamType>
struct close_connection_op : boost::asio::coroutine
{
channel<StreamType>& chan_;
error_info* output_info_;
close_connection_op(channel<StreamType>& chan, error_info* output_info) :
chan_(chan),
output_info_(output_info)
{
}
template<class Self>
void operator()(
Self& self,
error_code err = {}
)
{
error_code close_err;
BOOST_ASIO_CORO_REENTER(*this)
{
if (!chan_.next_layer().is_open())
{
BOOST_ASIO_CORO_YIELD boost::asio::post(std::move(self));
self.complete(error_code());
BOOST_ASIO_CORO_YIELD break;
}
// We call close regardless of the quit outcome
// There are no async versions of shutdown or close
BOOST_ASIO_CORO_YIELD async_quit_connection(
chan_,
std::move(self),
output_info_
);
close_err = chan_.close();
self.complete(err ? err : close_err);
}
}
};
}
}
}
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
boost::mysql::detail::close_connection_signature
)
@ -40,38 +89,10 @@ boost::mysql::detail::async_close_connection(
error_info* info
)
{
struct op : async_op<StreamType, CompletionToken, close_connection_signature, op>
{
using async_op<StreamType, CompletionToken, close_connection_signature, op>::async_op;
void operator()(
error_code err,
bool cont=true
)
{
error_code close_err;
BOOST_ASIO_CORO_REENTER(*this)
{
if (!this->get_channel().next_layer().is_open())
{
this->complete(cont, error_code());
BOOST_ASIO_CORO_YIELD break;
}
// We call close regardless of the quit outcome
// There are no async versions of shutdown or close
BOOST_ASIO_CORO_YIELD async_quit_connection(
this->get_channel(),
std::move(*this),
this->get_output_info()
);
close_err = this->get_channel().close();
this->complete(cont, err ? err : close_err);
}
}
};
return op::initiate(std::forward<CompletionToken>(token), chan, info);
return boost::asio::async_compose<
CompletionToken,
close_connection_signature
>(close_connection_op<StreamType>{chan, info}, token, chan);
}

View File

@ -30,7 +30,7 @@ void boost::mysql::detail::close_statement(
}
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
boost::mysql::detail::close_signature
)

View File

@ -10,6 +10,74 @@
#include "boost/mysql/detail/network_algorithms/handshake.hpp"
namespace boost {
namespace mysql {
namespace detail {
template<class StreamType>
struct connect_op : boost::asio::coroutine
{
using endpoint_type = typename StreamType::endpoint_type;
channel<StreamType>& chan_;
error_info* output_info_;
const endpoint_type& ep_; // No need for a copy, as we will call it in the first operator() call
connection_params params_;
connect_op(
channel<StreamType>& chan,
error_info* output_info,
const endpoint_type& ep,
const connection_params& params
) :
chan_(chan),
output_info_(output_info),
ep_(ep),
params_(params)
{
}
template<class Self>
void operator()(
Self& self,
error_code code = {}
)
{
BOOST_ASIO_CORO_REENTER(*this)
{
// Physical connect
BOOST_ASIO_CORO_YIELD chan_.next_layer().async_connect(ep_, std::move(self));
if (code)
{
chan_.close();
if (output_info_)
{
output_info_->set_message("Physical connect failed");
}
self.complete(code);
BOOST_ASIO_CORO_YIELD break;
}
// Handshake
BOOST_ASIO_CORO_YIELD async_handshake(
chan_,
params_,
std::move(self),
output_info_
);
if (code)
{
chan_.close();
}
self.complete(code);
}
}
};
} // detail
} // mysql
} // boost
template <typename StreamType>
void boost::mysql::detail::connect(
channel<StreamType>& chan,
@ -34,7 +102,7 @@ void boost::mysql::detail::connect(
}
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
boost::mysql::detail::connect_signature
)
@ -46,65 +114,8 @@ boost::mysql::detail::async_connect(
error_info* info
)
{
using endpoint_type = typename StreamType::endpoint_type;
struct op : async_op<StreamType, CompletionToken, connect_signature, op>
{
const endpoint_type& ep_; // No need for a copy, as we will call it in the first operator() call
connection_params params_;
op(
boost::asio::async_completion<CompletionToken, connect_signature>& completion,
channel<StreamType>& chan,
error_info* output_info,
const endpoint_type& ep,
const connection_params& params
) :
async_op<StreamType, CompletionToken, connect_signature, op>(completion, chan, output_info),
ep_(ep),
params_(params)
{
}
void operator()(
error_code code,
bool cont=true
)
{
BOOST_ASIO_CORO_REENTER(*this)
{
// Physical connect
BOOST_ASIO_CORO_YIELD this->get_channel().next_layer().async_connect(
ep_,
std::move(*this)
);
if (code)
{
this->get_channel().close();
if (this->get_output_info())
{
this->get_output_info()->set_message("Physical connect failed");
}
this->complete(cont, code);
BOOST_ASIO_CORO_YIELD break;
}
// Handshake
BOOST_ASIO_CORO_YIELD async_handshake(
this->get_channel(),
params_,
std::move(*this),
this->get_output_info()
);
if (code)
{
this->get_channel().close();
}
this->complete(cont, code);
}
}
};
return op::initiate(std::forward<CompletionToken>(token), chan, info, endpoint, params);
return boost::asio::async_compose<CompletionToken, connect_signature>(
connect_op<StreamType>{chan, info, endpoint, params}, token, chan);
}
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_CONNECT_HPP_ */

View File

@ -14,31 +14,25 @@ namespace boost {
namespace mysql {
namespace detail {
template <typename StreamType>
class execute_processor
{
deserialize_row_fn deserializer_;
channel<StreamType>& channel_;
capabilities caps_;
bytestring buffer_;
std::size_t field_count_ {};
ok_packet ok_packet_;
std::vector<field_metadata> fields_;
std::vector<bytestring> field_buffers_;
public:
execute_processor(deserialize_row_fn deserializer, channel<StreamType>& chan):
deserializer_(deserializer), channel_(chan) {};
execute_processor(deserialize_row_fn deserializer, capabilities caps):
deserializer_(deserializer), caps_(caps) {};
template <typename Serializable>
void process_request(
const Serializable& request
)
{
// Serialize the request
capabilities caps = channel_.current_capabilities();
serialize_message(request, caps, buffer_);
// Prepare the channel
channel_.reset_sequence_number();
serialize_message(request, caps_, buffer_);
}
void process_response(
@ -49,7 +43,7 @@ public:
// Response may be: ok_packet, err_packet, local infile request (not implemented)
// If it is none of this, then the message type itself is the beginning of
// a length-encoded int containing the field count
deserialization_context ctx (boost::asio::buffer(buffer_), channel_.current_capabilities());
deserialization_context ctx (boost::asio::buffer(buffer_), caps_);
std::uint8_t msg_type;
std::tie(err, msg_type) = deserialize_message_type(ctx);
if (err)
@ -101,7 +95,7 @@ public:
error_code process_field_definition()
{
column_definition_packet field_definition;
deserialization_context ctx (boost::asio::buffer(buffer_), channel_.current_capabilities());
deserialization_context ctx (boost::asio::buffer(buffer_), caps_);
auto err = deserialize_message(ctx, field_definition);
if (err)
return err;
@ -114,12 +108,13 @@ public:
return error_code();
}
resultset<StreamType> create_resultset() &&
template <typename StreamType>
resultset<StreamType> create_resultset(channel<StreamType>& chan) &&
{
if (field_count_ == 0)
{
return resultset<StreamType>(
channel_,
chan,
std::move(buffer_),
ok_packet_
);
@ -127,19 +122,99 @@ public:
else
{
return resultset<StreamType>(
channel_,
chan,
resultset_metadata(std::move(field_buffers_), std::move(fields_)),
deserializer_
);
}
}
auto& get_channel() { return channel_; }
auto& get_buffer() { return buffer_; }
bytestring& get_buffer() { return buffer_; }
std::size_t field_count() const noexcept { return field_count_; }
};
template<class StreamType>
struct execute_generic_op : boost::asio::coroutine
{
channel<StreamType>& chan_;
error_info* output_info_;
std::shared_ptr<execute_processor> processor_;
std::uint64_t remaining_fields_ {0};
execute_generic_op(
channel<StreamType>& chan,
error_info* output_info,
std::shared_ptr<execute_processor>&& processor
) :
chan_(chan),
output_info_(output_info),
processor_(std::move(processor))
{
}
template<class Self>
void operator()(
Self& self,
error_code err = {}
)
{
// Error checking
if (err)
{
self.complete(err, resultset<StreamType>());
return;
}
// Non-error path
error_info info;
BOOST_ASIO_CORO_REENTER(*this)
{
chan_.reset_sequence_number();
// The request message has already been composed in the ctor. Send it
BOOST_ASIO_CORO_YIELD chan_.async_write(processor_->get_buffer(), std::move(self));
// Read the response
BOOST_ASIO_CORO_YIELD chan_.async_read(processor_->get_buffer(), std::move(self));
// Response may be: ok_packet, err_packet, local infile request
// (not implemented), or response with fields
processor_->process_response(err, info);
if (err)
{
conditional_assign(output_info_, std::move(info));
self.complete(err, resultset<StreamType>());
BOOST_ASIO_CORO_YIELD break;
}
remaining_fields_ = processor_->field_count();
// Read all of the field definitions
while (remaining_fields_ > 0)
{
// Read the field definition packet
BOOST_ASIO_CORO_YIELD chan_.async_read(processor_->get_buffer(), std::move(self));
// Process the message
err = processor_->process_field_definition();
if (err)
{
self.complete(err, resultset<StreamType>());
BOOST_ASIO_CORO_YIELD break;
}
remaining_fields_--;
}
// No EOF packet is expected here, as we require deprecate EOF capabilities
self.complete(
error_code(),
resultset<StreamType>(std::move(*processor_).create_resultset(chan_))
);
}
}
};
} // detail
} // mysql
} // boost
@ -155,8 +230,9 @@ void boost::mysql::detail::execute_generic(
)
{
// Compose a com_query message, reset seq num
execute_processor<StreamType> processor (deserializer, channel);
execute_processor processor (deserializer, channel.current_capabilities());
processor.process_request(request);
channel.reset_sequence_number();
// Send it
channel.write(boost::asio::buffer(processor.get_buffer()), err);
@ -188,12 +264,11 @@ void boost::mysql::detail::execute_generic(
}
// No EOF packet is expected here, as we require deprecate EOF capabilities
output = std::move(processor).create_resultset();
output = std::move(processor).create_resultset(channel);
}
template <typename StreamType, typename Serializable, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
boost::mysql::detail::execute_generic_signature<StreamType>
)
@ -205,88 +280,16 @@ boost::mysql::detail::async_execute_generic(
error_info* info
)
{
using handler_signature = execute_generic_signature<StreamType>;
using resultset_type = resultset<StreamType>;
struct op : async_op<StreamType, CompletionToken, handler_signature, op>
{
std::shared_ptr<execute_processor<StreamType>> processor_;
std::uint64_t remaining_fields_ {0};
op(
boost::asio::async_completion<CompletionToken, handler_signature>& initiator,
channel<StreamType>& chan,
error_info* output_info,
deserialize_row_fn deserializer,
const Serializable& request
) :
async_op<StreamType, CompletionToken, handler_signature, op>(initiator, chan, output_info),
processor_(std::make_shared<execute_processor<StreamType>>(deserializer, chan))
{
processor_->process_request(request);
}
void operator()(
error_code err,
bool cont=true
)
{
// Error checking
if (err)
{
this->complete(cont, err, resultset_type());
return;
}
// Non-error path
error_info info;
BOOST_ASIO_CORO_REENTER(*this)
{
// The request message has already been composed in the ctor. Send it
BOOST_ASIO_CORO_YIELD this->async_write(processor_->get_buffer());
// Read the response
BOOST_ASIO_CORO_YIELD this->async_read(processor_->get_buffer());
// Response may be: ok_packet, err_packet, local infile request (not implemented), or response with fields
processor_->process_response(err, info);
if (err)
{
conditional_assign(this->get_output_info(), std::move(info));
this->complete(cont, err, resultset_type());
BOOST_ASIO_CORO_YIELD break;
}
remaining_fields_ = processor_->field_count();
// Read all of the field definitions
while (remaining_fields_ > 0)
{
// Read the field definition packet
BOOST_ASIO_CORO_YIELD this->async_read(processor_->get_buffer());
// Process the message
err = processor_->process_field_definition();
if (err)
{
this->complete(cont, err, resultset_type());
BOOST_ASIO_CORO_YIELD break;
}
remaining_fields_--;
}
// No EOF packet is expected here, as we require deprecate EOF capabilities
this->complete(
cont,
error_code(),
resultset_type(std::move(*processor_).create_resultset())
);
}
}
};
return op::initiate(std::forward<CompletionToken>(token),
chan, info, deserializer, request);
auto processor = std::make_shared<execute_processor>(deserializer, chan.current_capabilities());
processor->process_request(request);
return boost::asio::async_compose<
CompletionToken,
execute_generic_signature<StreamType>
>(
execute_generic_op<StreamType>(chan, info, std::move(processor)),
token,
chan
);
}

View File

@ -33,7 +33,7 @@ void boost::mysql::detail::execute_query(
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
boost::mysql::detail::execute_generic_signature<StreamType>
)

View File

@ -58,7 +58,7 @@ void boost::mysql::detail::execute_statement(
}
template <typename StreamType, typename ForwardIterator, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
boost::mysql::detail::execute_generic_signature<StreamType>
)

View File

@ -239,6 +239,105 @@ public:
}
};
template<class StreamType>
struct handshake_op : boost::asio::coroutine
{
channel<StreamType>& chan_;
error_info* output_info_;
handshake_processor processor_;
auth_result auth_state_ {auth_result::invalid};
handshake_op(
channel<StreamType>& channel,
error_info* output_info,
const connection_params& params
) :
chan_(channel),
output_info_(output_info),
processor_(params)
{
}
template<class Self>
void complete(Self& self, error_code code, error_info&& info)
{
chan_.set_current_capabilities(processor_.negotiated_capabilities());
conditional_assign(output_info_, std::move(info));
self.complete(code);
}
template<class Self>
void operator()(
Self& self,
error_code err = {}
)
{
// Error checking
if (err)
{
complete(self, err, error_info());
return;
}
// Non-error path
error_info info;
BOOST_ASIO_CORO_REENTER(*this)
{
// Read server greeting
BOOST_ASIO_CORO_YIELD chan_.async_read(chan_.shared_buffer(), std::move(self));
// Process server greeting
err = processor_.process_handshake(chan_.shared_buffer(), info);
if (err)
{
complete(self, err, std::move(info));
BOOST_ASIO_CORO_YIELD break;
}
// SSL
if (processor_.use_ssl())
{
// Send SSL request
processor_.compose_ssl_request(chan_.shared_buffer());
BOOST_ASIO_CORO_YIELD chan_.async_write(chan_.shared_buffer(), std::move(self));
// SSL handshake
BOOST_ASIO_CORO_YIELD chan_.async_ssl_handshake(std::move(self));
}
// Compose and send handshake response
processor_.compose_handshake_response(chan_.shared_buffer());
BOOST_ASIO_CORO_YIELD chan_.async_write(chan_.shared_buffer(), std::move(self));
while (auth_state_ != auth_result::complete)
{
// Receive response
BOOST_ASIO_CORO_YIELD chan_.async_read(chan_.shared_buffer(), std::move(self));
// Process it
err = processor_.process_handshake_server_response(
chan_.shared_buffer(),
auth_state_,
info
);
if (err)
{
complete(self, err, std::move(info));
BOOST_ASIO_CORO_YIELD break;
}
if (auth_state_ == auth_result::send_more_data)
{
// We received an auth switch response and we have the response ready to be sent
BOOST_ASIO_CORO_YIELD chan_.async_write(chan_.shared_buffer(), std::move(self));
}
}
complete(self, error_code(), error_info());
}
}
};
} // detail
} // mysql
} // boost
@ -311,7 +410,7 @@ void boost::mysql::detail::handshake(
}
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
boost::mysql::detail::handshake_signature
)
@ -322,102 +421,14 @@ boost::mysql::detail::async_handshake(
error_info* info
)
{
struct op : async_op<StreamType, CompletionToken, handshake_signature, op>
{
handshake_processor processor_;
error_info info_;
auth_result auth_state_ {auth_result::invalid};
op(
boost::asio::async_completion<CompletionToken, handshake_signature>& completion,
channel<StreamType>& channel,
error_info* output_info,
const connection_params& params
) :
async_op<StreamType, CompletionToken, handshake_signature, op>(completion, channel, output_info),
processor_(params)
{
}
void complete(bool cont, error_code code, error_info&& info = {})
{
this->get_channel().set_current_capabilities(processor_.negotiated_capabilities());
conditional_assign(this->get_output_info(), std::move(info));
async_op<StreamType, CompletionToken, handshake_signature, op>::complete(cont, code);
}
void operator()(
error_code err,
bool cont=true
)
{
// Error checking
if (err)
{
complete(cont, err);
return;
}
// Non-error path
error_info info;
BOOST_ASIO_CORO_REENTER(*this)
{
// Read server greeting
BOOST_ASIO_CORO_YIELD this->async_read();
// Process server greeting
err = processor_.process_handshake(this->get_channel().shared_buffer(), info);
if (err)
{
complete(cont, err, std::move(info));
BOOST_ASIO_CORO_YIELD break;
}
// SSL
if (processor_.use_ssl())
{
// Send SSL request
processor_.compose_ssl_request(this->get_channel().shared_buffer());
BOOST_ASIO_CORO_YIELD this->async_write();
// SSL handshake
BOOST_ASIO_CORO_YIELD this->get_channel().async_ssl_handshake(std::move(*this));
}
// Compose and send handshake response
processor_.compose_handshake_response(this->get_channel().shared_buffer());
BOOST_ASIO_CORO_YIELD this->async_write();
while (auth_state_ != auth_result::complete)
{
// Receive response
BOOST_ASIO_CORO_YIELD this->async_read();
// Process it
err = processor_.process_handshake_server_response(
this->get_channel().shared_buffer(),
auth_state_,
info
);
if (err)
{
complete(cont, err, std::move(info));
BOOST_ASIO_CORO_YIELD break;
}
if (auth_state_ == auth_result::send_more_data)
{
// We received an auth switch response and we have the response ready to be sent
BOOST_ASIO_CORO_YIELD this->async_write();
}
}
complete(cont, error_code());
}
}
};
return op::initiate(std::forward<CompletionToken>(token), chan, info, params);
return boost::asio::async_compose<
CompletionToken,
boost::mysql::detail::handshake_signature
>(
handshake_op<StreamType>(chan, info, params),
token,
chan
);
}

View File

@ -59,6 +59,74 @@ public:
}
};
template<typename StreamType>
struct prepare_statement_op : boost::asio::coroutine
{
prepare_statement_processor<StreamType> processor_;
error_info* output_info_;
unsigned remaining_meta_ {0};
prepare_statement_op(
channel<StreamType>& chan,
error_info* output_info,
std::string_view statement
) :
processor_(chan),
output_info_(output_info)
{
processor_.process_request(statement);
}
template<class Self>
void operator()(
Self& self,
error_code err = {}
)
{
// Error checking
if (err)
{
self.complete(err, prepared_statement<StreamType>());
return;
}
// Regular coroutine body; if there has been an error, we don't get here
error_info info;
channel<StreamType>& chan = processor_.get_channel();
BOOST_ASIO_CORO_REENTER(*this)
{
// Write message (already serialized at this point)
BOOST_ASIO_CORO_YIELD chan.async_write(processor_.get_buffer(), std::move(self));
// Read response
BOOST_ASIO_CORO_YIELD chan.async_read(processor_.get_buffer(), std::move(self));
// Process response
processor_.process_response(err, info);
if (err)
{
detail::conditional_assign(output_info_, std::move(info));
self.complete(err, prepared_statement<StreamType>());
BOOST_ASIO_CORO_YIELD break;
}
// Server sends now one packet per parameter and field.
// We ignore these for now.
remaining_meta_ = processor_.get_num_metadata_packets();
for (; remaining_meta_ > 0; --remaining_meta_)
{
BOOST_ASIO_CORO_YIELD chan.async_read(processor_.get_buffer(), std::move(self));
}
// Compose response
self.complete(
err,
prepared_statement<StreamType>(processor_.get_channel(), processor_.get_response())
);
}
}
};
} // detail
} // mysql
} // boost
@ -105,7 +173,7 @@ void boost::mysql::detail::prepare_statement(
}
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
boost::mysql::detail::prepare_statement_signature<StreamType>
)
@ -116,77 +184,14 @@ boost::mysql::detail::async_prepare_statement(
error_info* info
)
{
using handler_signature = prepare_statement_signature<StreamType>;
using stmt_type = prepared_statement<StreamType>;
struct op : async_op<StreamType, CompletionToken, handler_signature, op>
{
prepare_statement_processor<StreamType> processor_;
unsigned remaining_meta_;
op(
boost::asio::async_completion<CompletionToken, handler_signature>& completion,
channel<StreamType>& chan,
error_info* output_info,
std::string_view statement
) :
async_op<StreamType, CompletionToken, handler_signature, op>(completion, chan, output_info),
processor_(chan),
remaining_meta_(0)
{
processor_.process_request(statement);
}
void operator()(
error_code err,
bool cont=true
)
{
// Error checking
if (err)
{
this->complete(cont, err, stmt_type());
return;
}
// Regular coroutine body; if there has been an error, we don't get here
error_info info;
BOOST_ASIO_CORO_REENTER(*this)
{
// Write message (already serialized at this point)
BOOST_ASIO_CORO_YIELD this->async_write(processor_.get_buffer());
// Read response
BOOST_ASIO_CORO_YIELD this->async_read(processor_.get_buffer());
// Process response
processor_.process_response(err, info);
if (err)
{
detail::conditional_assign(this->get_output_info(), std::move(info));
this->complete(cont, err, stmt_type());
BOOST_ASIO_CORO_YIELD break;
}
// Server sends now one packet per parameter and field.
// We ignore these for now.
remaining_meta_ = processor_.get_num_metadata_packets();
for (; remaining_meta_ > 0; --remaining_meta_)
{
BOOST_ASIO_CORO_YIELD this->async_read(processor_.get_buffer());
}
// Compose response
this->complete(
cont,
err,
stmt_type(processor_.get_channel(), processor_.get_response())
);
}
}
};
return op::initiate(std::forward<CompletionToken>(token), chan, info, statement);
return boost::asio::async_compose<
CompletionToken,
prepare_statement_signature<StreamType>
>(
prepare_statement_op<StreamType>{chan, info, statement},
token,
chan
);
}
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_PREPARE_STATEMENT_HPP_ */

View File

@ -37,11 +37,11 @@ void boost::mysql::detail::quit_connection(
)
{
compose_quit(chan);
chan.write(boost::asio::buffer(chan.shared_buffer()), code);
chan.write(chan.shared_buffer(), code);
}
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
boost::mysql::detail::quit_connection_signature
)
@ -53,7 +53,7 @@ boost::mysql::detail::async_quit_connection(
{
compose_quit(chan);
return chan.async_write(
boost::asio::buffer(chan.shared_buffer()),
chan.shared_buffer(),
std::forward<CompletionToken>(token)
);
}

View File

@ -59,6 +59,75 @@ inline read_row_result process_read_message(
}
}
template<class StreamType>
struct read_row_op : boost::asio::coroutine
{
channel<StreamType>& chan_;
error_info* output_info_;
deserialize_row_fn deserializer_;
const std::vector<field_metadata>& meta_;
bytestring& buffer_;
std::vector<value>& output_values_;
ok_packet& output_ok_packet_;
read_row_op(
channel<StreamType>& chan,
error_info* output_info,
deserialize_row_fn deserializer,
const std::vector<field_metadata>& meta,
bytestring& buffer,
std::vector<value>& output_values,
ok_packet& output_ok_packet
) :
chan_(chan),
output_info_(output_info),
deserializer_(deserializer),
meta_(meta),
buffer_(buffer),
output_values_(output_values),
output_ok_packet_(output_ok_packet)
{
}
template<class Self>
void operator()(
Self& self,
error_code err = {}
)
{
error_info info;
read_row_result result = read_row_result::error;
// Error checking
if (err)
{
self.complete(err, result);
return;
}
// Normal path
BOOST_ASIO_CORO_REENTER(*this)
{
// Read the message
BOOST_ASIO_CORO_YIELD chan_.async_read(buffer_, std::move(self));
// Process it
result = process_read_message(
deserializer_,
chan_.current_capabilities(),
meta_,
buffer_,
output_values_,
output_ok_packet_,
err,
info
);
detail::conditional_assign(output_info_, std::move(info));
self.complete(err, result);
}
}
};
} // detail
} // mysql
} // boost
@ -93,9 +162,8 @@ boost::mysql::detail::read_row_result boost::mysql::detail::read_row(
);
}
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
boost::mysql::detail::read_row_signature
)
@ -110,81 +178,28 @@ boost::mysql::detail::async_read_row(
error_info* output_info
)
{
struct op : async_op<StreamType, CompletionToken, read_row_signature, op>
{
deserialize_row_fn deserializer_;
const std::vector<field_metadata>& meta_;
bytestring& buffer_;
std::vector<value>& output_values_;
ok_packet& output_ok_packet_;
return boost::asio::async_compose<CompletionToken, read_row_signature>(
read_row_op(
chan,
output_info,
deserializer,
meta,
buffer,
output_values,
output_ok_packet
), token, chan
);
op(
boost::asio::async_completion<CompletionToken, read_row_signature>& completion,
channel<StreamType>& chan,
error_info* output_info,
deserialize_row_fn deserializer,
const std::vector<field_metadata>& meta,
bytestring& buffer,
std::vector<value>& output_values,
ok_packet& output_ok_packet
) :
async_op<StreamType, CompletionToken, read_row_signature, op>(completion, chan, output_info),
deserializer_(deserializer),
meta_(meta),
buffer_(buffer),
output_values_(output_values),
output_ok_packet_(output_ok_packet)
{
}
void operator()(
error_code err,
bool cont=true
)
{
error_info info;
read_row_result result = read_row_result::error;
// Error checking
if (err)
{
this->complete(cont, err, result);
return;
}
// Normal path
BOOST_ASIO_CORO_REENTER(*this)
{
// Read the message
BOOST_ASIO_CORO_YIELD this->async_read(buffer_);
// Process it
result = process_read_message(
deserializer_,
this->get_channel().current_capabilities(),
meta_,
buffer_,
output_values_,
output_ok_packet_,
err,
info
);
detail::conditional_assign(this->get_output_info(), std::move(info));
this->complete(cont, err, result);
}
}
};
return op::initiate(
std::forward<CompletionToken>(token),
chan,
output_info,
deserializer,
meta,
buffer,
output_values,
output_ok_packet
);
// return op::initiate(
// std::forward<CompletionToken>(token),
// chan,
// output_info,
// deserializer,
// meta,
// buffer,
// output_values,
// output_ok_packet
// );
}
#endif /* INCLUDE_MYSQL_IMPL_NETWORK_ALGORITHMS_READ_TEXT_ROW_IPP_ */

View File

@ -28,7 +28,7 @@ template <typename StreamType>
using prepare_statement_signature = void(error_code, prepared_statement<StreamType>);
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, prepare_statement_signature<StreamType>)
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, prepare_statement_signature<StreamType>)
async_prepare_statement(
channel<StreamType>& chan,
std::string_view statement,

View File

@ -21,10 +21,10 @@ void quit_connection(
error_info& info
);
using quit_connection_signature = empty_signature;
using quit_connection_signature = void(error_code);
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, quit_connection_signature)
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, quit_connection_signature)
async_quit_connection(
channel<StreamType>& chan,
CompletionToken&& token,

View File

@ -39,7 +39,7 @@ read_row_result read_row(
using read_row_signature = void(error_code, read_row_result);
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, read_row_signature)
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, read_row_signature)
async_read_row(
deserialize_row_fn deserializer,
channel<StreamType>& channel,

View File

@ -16,7 +16,6 @@
#include <boost/asio/ssl/stream.hpp>
#include <boost/asio/ssl/context.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/beast/core/async_base.hpp>
#include <array>
#include <optional>
@ -65,27 +64,45 @@ class channel
template <typename BufferSeq, typename CompletionToken>
auto async_write_impl(BufferSeq&& buff, CompletionToken&& token);
struct read_op;
struct write_op;
public:
channel(Stream& stream): stream_(stream) {}
// Executor
using executor_type = typename Stream::executor_type;
executor_type get_executor() { return stream_.get_executor(); }
// Reading
void read(bytestring& buffer, error_code& code);
using read_signature = void(error_code);
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, read_signature)
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, read_signature)
async_read(bytestring& buffer, CompletionToken&& token);
// Writing
void write(boost::asio::const_buffer buffer, error_code& code);
void write(const bytestring& buffer, error_code& code)
{
write(boost::asio::buffer(buffer), code);
}
using write_signature = void(error_code);
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, write_signature)
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, write_signature)
async_write(boost::asio::const_buffer buffer, CompletionToken&& token);
template <typename CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, write_signature)
async_write(const bytestring& buffer, CompletionToken&& token)
{
return async_write(boost::asio::buffer(buffer), std::forward<CompletionToken>(token));
}
// SSL
bool ssl_active() const noexcept { return ssl_block_.has_value(); }
@ -94,7 +111,7 @@ public:
using ssl_handshake_signature = void(error_code);
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, ssl_handshake_signature)
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, ssl_handshake_signature)
async_ssl_handshake(CompletionToken&& token);
// Closing (only available for sockets)
@ -117,81 +134,6 @@ public:
bytestring& shared_buffer() noexcept { return shared_buff_; }
};
template <
typename Stream,
typename CompletionToken,
typename HandlerSignature
>
using async_op_base = boost::beast::async_base<
BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature),
typename Stream::executor_type
>;
// The base for async operations involving a channel
// Defined here so it can also be used for channel function implementation
template <
typename Stream,
typename CompletionToken,
typename HandlerSignature,
typename Derived // CRTP
>
class async_op :
public boost::asio::coroutine,
public async_op_base<Stream, CompletionToken, HandlerSignature>
{
channel<Stream>& channel_;
error_info* output_info_;
using base_type = async_op_base<Stream, CompletionToken, HandlerSignature>;
public:
using async_completion_type = boost::asio::async_completion<CompletionToken, HandlerSignature>;
async_op(async_completion_type& initiator, channel<Stream>& chan, error_info* output_info) :
base_type(std::move(initiator.completion_handler), chan.next_layer().get_executor()),
channel_(chan),
output_info_(output_info)
{
}
channel<Stream>& get_channel() noexcept { return channel_; }
error_info* get_output_info() noexcept { return output_info_; }
template <typename... Args>
static auto initiate(
CompletionToken&& token,
channel<Stream>& chan,
error_info* output_info,
Args&&... args
)
{
async_completion_type completion (token);
Derived op (completion, chan, output_info, std::forward<Args>(args)...);
op(error_code());
return completion.result.get();
}
// Reads from channel against the channel internal buffer, using itself as
void async_read() { async_read(channel_.shared_buffer()); }
void async_read(bytestring& buff)
{
channel_.async_read(
buff,
std::move(static_cast<Derived&>(*this))
);
}
void async_write(const bytestring& buff)
{
channel_.async_write(
boost::asio::buffer(buff),
std::move(static_cast<Derived&>(*this))
);
}
void async_write() { async_write(channel_.shared_buffer()); }
};
} // detail
} // mysql
} // boost

View File

@ -10,6 +10,7 @@
#include <boost/asio/read.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/compose.hpp>
#include <cassert>
#include "boost/mysql/detail/protocol/common_messages.hpp"
#include "boost/mysql/detail/protocol/constants.hpp"
@ -229,10 +230,77 @@ void boost::mysql::detail::channel<Stream>::write(
} while (transferred_size < bufsize);
}
template<class Stream>
struct boost::mysql::detail::channel<Stream>::read_op
: boost::asio::coroutine
{
channel<Stream>& chan_;
bytestring& buffer_;
std::size_t total_transferred_size_ = 0;
read_op(
channel<Stream>& chan,
bytestring& buffer
) :
chan_(chan),
buffer_(buffer)
{
}
template<class Self>
void operator()(
Self& self,
error_code code = {},
std::size_t bytes_transferred=0
)
{
// Error checking
if (code)
{
self.complete(code);
return;
}
// Non-error path
std::uint32_t size_to_read = 0;
BOOST_ASIO_CORO_REENTER(*this)
{
do
{
BOOST_ASIO_CORO_YIELD chan_.async_read_impl(
boost::asio::buffer(chan_.header_buffer_),
std::move(self)
);
valgrind_make_mem_defined(boost::asio::buffer(chan_.header_buffer_));
code = chan_.process_header_read(size_to_read);
if (code)
{
self.complete(code);
BOOST_ASIO_CORO_YIELD break;
}
buffer_.resize(buffer_.size() + size_to_read);
BOOST_ASIO_CORO_YIELD chan_.async_read_impl(
boost::asio::buffer(buffer_.data() + total_transferred_size_, size_to_read),
std::move(self)
);
valgrind_make_mem_defined(
boost::asio::buffer(buffer_.data() + total_transferred_size_, bytes_transferred)
);
total_transferred_size_ += bytes_transferred;
} while (bytes_transferred == MAX_PACKET_SIZE);
self.complete(error_code());
}
}
};
template <typename Stream>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
typename boost::mysql::detail::channel<Stream>::read_signature
)
@ -241,80 +309,78 @@ boost::mysql::detail::channel<Stream>::async_read(
CompletionToken&& token
)
{
struct op : async_op<Stream, CompletionToken, read_signature, op>
{
bytestring& buffer_;
std::size_t total_transferred_size_ = 0;
op(
boost::asio::async_completion<CompletionToken, read_signature>& completion,
channel<Stream>& chan,
error_info* output_info,
bytestring& buffer
) :
async_op<Stream, CompletionToken, read_signature, op>(completion, chan, output_info),
buffer_(buffer)
{
}
void operator()(
error_code code,
std::size_t bytes_transferred=0,
bool cont=true
)
{
// Error checking
if (code)
{
this->complete(cont, code);
return;
}
// Non-error path
std::uint32_t size_to_read = 0;
channel<Stream>& chan = this->get_channel();
BOOST_ASIO_CORO_REENTER(*this)
{
do
{
BOOST_ASIO_CORO_YIELD chan.async_read_impl(
boost::asio::buffer(chan.header_buffer_),
std::move(*this)
);
valgrind_make_mem_defined(boost::asio::buffer(chan.header_buffer_));
code = chan.process_header_read(size_to_read);
if (code)
{
this->complete(cont, code);
BOOST_ASIO_CORO_YIELD break;
}
buffer_.resize(buffer_.size() + size_to_read);
BOOST_ASIO_CORO_YIELD chan.async_read_impl(
boost::asio::buffer(buffer_.data() + total_transferred_size_, size_to_read),
std::move(*this)
);
valgrind_make_mem_defined(
boost::asio::buffer(buffer_.data() + total_transferred_size_, bytes_transferred)
);
total_transferred_size_ += bytes_transferred;
} while (bytes_transferred == MAX_PACKET_SIZE);
this->complete(cont, error_code());
}
}
};
buffer.clear();
return op::initiate(std::forward<CompletionToken>(token), *this, nullptr, buffer);
return boost::asio::async_compose<
CompletionToken,
typename boost::mysql::detail::channel<Stream>::read_signature
>(
read_op(*this, buffer),
token,
*this
);
}
template<typename Stream>
struct boost::mysql::detail::channel<Stream>::write_op
: boost::asio::coroutine
{
channel<Stream>& chan_;
boost::asio::const_buffer buffer_;
std::size_t total_transferred_size_ = 0;
write_op(
channel<Stream>& chan,
boost::asio::const_buffer buffer
) :
chan_(chan),
buffer_(buffer)
{
}
template<class Self>
void operator()(
Self& self,
error_code code = {},
std::size_t bytes_transferred=0
)
{
// Error handling
if (code)
{
self.complete(code);
return;
}
// Non-error path
std::uint32_t size_to_write;
BOOST_ASIO_CORO_REENTER(*this)
{
// Force write the packet header on an empty packet, at least.
do
{
size_to_write = compute_size_to_write(buffer_.size(), total_transferred_size_);
chan_.process_header_write(size_to_write);
BOOST_ASIO_CORO_YIELD chan_.async_write_impl(
std::array<boost::asio::const_buffer, 2> {
boost::asio::buffer(chan_.header_buffer_),
boost::asio::buffer(buffer_ + total_transferred_size_, size_to_write)
},
std::move(self)
);
total_transferred_size_ += (bytes_transferred - 4); // header size
} while (total_transferred_size_ < buffer_.size());
self.complete(error_code());
}
}
};
template <typename Stream>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
typename boost::mysql::detail::channel<Stream>::write_signature
)
@ -323,65 +389,14 @@ boost::mysql::detail::channel<Stream>::async_write(
CompletionToken&& token
)
{
struct op : async_op<Stream, CompletionToken, write_signature, op>
{
boost::asio::const_buffer buffer_;
std::size_t total_transferred_size_ = 0;
op(
boost::asio::async_completion<CompletionToken, write_signature>& completion,
channel<Stream>& chan,
error_info* output_info,
boost::asio::const_buffer buffer
) :
async_op<Stream, CompletionToken, write_signature, op>(completion, chan, output_info),
buffer_(buffer)
{
}
void operator()(
error_code code,
std::size_t bytes_transferred=0,
bool cont=true
)
{
// Error handling
if (code)
{
this->complete(cont, code);
return;
}
// Non-error path
std::uint32_t size_to_write;
channel<Stream>& chan = this->get_channel();
BOOST_ASIO_CORO_REENTER(*this)
{
// Force write the packet header on an empty packet, at least.
do
{
size_to_write = compute_size_to_write(buffer_.size(), total_transferred_size_);
chan.process_header_write(size_to_write);
BOOST_ASIO_CORO_YIELD chan.async_write_impl(
std::array<boost::asio::const_buffer, 2> {
boost::asio::buffer(chan.header_buffer_),
boost::asio::buffer(buffer_ + total_transferred_size_, size_to_write)
},
std::move(*this)
);
total_transferred_size_ += (bytes_transferred - 4); // header size
} while (total_transferred_size_ < buffer_.size());
this->complete(cont, error_code());
}
}
};
return op::initiate(std::forward<CompletionToken>(token), *this, nullptr, buffer);
return boost::asio::async_compose<
CompletionToken,
typename boost::mysql::detail::channel<Stream>::write_signature
>(
write_op(*this, buffer),
token,
*this
);
}
template <typename Stream>
@ -395,7 +410,7 @@ void boost::mysql::detail::channel<Stream>::ssl_handshake(
template <typename Stream>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
typename boost::mysql::detail::channel<Stream>::ssl_handshake_signature
)

View File

@ -14,7 +14,6 @@
#include "boost/mysql/detail/network_algorithms/quit_connection.hpp"
#include "boost/mysql/detail/network_algorithms/close_connection.hpp"
#include "boost/mysql/detail/network_algorithms/connect.hpp"
#include "boost/mysql/detail/auxiliar/check_completion_token.hpp"
#include <boost/asio/buffer.hpp>
template <typename Stream>
@ -39,8 +38,9 @@ void boost::mysql::connection<Stream>::handshake(
}
template <typename Stream>
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(
typename boost::mysql::connection<Stream>::handshake_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
typename boost::mysql::connection<Stream>::handshake_signature
)
@ -51,7 +51,6 @@ boost::mysql::connection<Stream>::async_handshake(
)
{
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, handshake_signature>();
return detail::async_handshake(
channel_,
params,
@ -87,8 +86,9 @@ boost::mysql::resultset<Stream> boost::mysql::connection<Stream>::query(
}
template <typename Stream>
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(
typename boost::mysql::connection<Stream>::query_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
typename boost::mysql::connection<Stream>::query_signature
)
@ -99,7 +99,6 @@ boost::mysql::connection<Stream>::async_query(
)
{
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, query_signature>();
return detail::async_execute_query(
channel_,
query_string,
@ -134,8 +133,9 @@ boost::mysql::prepared_statement<Stream> boost::mysql::connection<Stream>::prepa
}
template <typename Stream>
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(
typename boost::mysql::connection<Stream>::prepare_statement_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
typename boost::mysql::connection<Stream>::prepare_statement_signature
)
@ -146,7 +146,6 @@ boost::mysql::connection<Stream>::async_prepare_statement(
)
{
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, prepare_statement_signature>();
return detail::async_prepare_statement(
channel_,
statement,
@ -174,8 +173,9 @@ void boost::mysql::connection<Stream>::quit()
}
template <typename Stream>
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(
typename boost::mysql::connection<Stream>::quit_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
typename boost::mysql::connection<Stream>::quit_signature
)
@ -185,7 +185,6 @@ boost::mysql::connection<Stream>::async_quit(
)
{
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, quit_signature>();
return detail::async_quit_connection(
channel_,
std::forward<CompletionToken>(token),
@ -218,8 +217,9 @@ void boost::mysql::socket_connection<Stream>::connect(
}
template <typename Stream>
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(
typename boost::mysql::socket_connection<Stream>::connect_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
typename boost::mysql::socket_connection<Stream>::connect_signature
)
@ -231,7 +231,6 @@ boost::mysql::socket_connection<Stream>::async_connect(
)
{
detail::conditional_clear(output_info);
detail::check_completion_token<CompletionToken, connect_signature>();
return detail::async_connect(
this->get_channel(),
endpoint,
@ -260,8 +259,9 @@ void boost::mysql::socket_connection<Stream>::close()
}
template <typename Stream>
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(
typename boost::mysql::socket_connection<Stream>::close_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
typename boost::mysql::socket_connection<Stream>::close_signature
)
@ -271,7 +271,6 @@ boost::mysql::socket_connection<Stream>::async_close(
)
{
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, close_signature>();
return detail::async_close_connection(
this->get_channel(),
std::forward<CompletionToken>(token),

View File

@ -11,8 +11,7 @@
#include "boost/mysql/detail/network_algorithms/execute_statement.hpp"
#include "boost/mysql/detail/network_algorithms/close_statement.hpp"
#include "boost/mysql/detail/auxiliar/stringize.hpp"
#include "boost/mysql/detail/auxiliar/check_completion_token.hpp"
#include <boost/beast/core/bind_handler.hpp>
#include <boost/asio/bind_executor.hpp>
template <typename Stream>
template <typename ForwardIterator>
@ -40,7 +39,7 @@ boost::mysql::resultset<Stream> boost::mysql::prepared_statement<Stream>::execut
ForwardIterator params_last,
error_code& err,
error_info& info
) const
)
{
assert(valid());
@ -70,7 +69,7 @@ template <typename ForwardIterator>
boost::mysql::resultset<Stream> boost::mysql::prepared_statement<Stream>::execute(
ForwardIterator params_first,
ForwardIterator params_last
) const
)
{
detail::error_block blk;
auto res = execute(params_first, params_last, blk.err, blk.info);
@ -79,8 +78,9 @@ boost::mysql::resultset<Stream> boost::mysql::prepared_statement<Stream>::execut
}
template <typename StreamType>
template <typename ForwardIterator, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
template <typename ForwardIterator, BOOST_ASIO_COMPLETION_TOKEN_FOR(
typename boost::mysql::prepared_statement<StreamType>::execute_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
typename boost::mysql::prepared_statement<StreamType>::execute_signature
)
@ -89,10 +89,9 @@ boost::mysql::prepared_statement<StreamType>::async_execute(
ForwardIterator params_last,
CompletionToken&& token,
error_info* info
) const
)
{
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, execute_signature>();
// Check we got passed the right number of params
error_code err;
@ -101,26 +100,48 @@ boost::mysql::prepared_statement<StreamType>::async_execute(
if (err)
{
detail::conditional_assign(info, std::move(nonnull_info));
boost::asio::async_completion<CompletionToken, execute_signature> completion (token);
boost::asio::post(boost::beast::bind_front_handler(
std::move(completion.completion_handler),
err,
resultset<StreamType>()
));
return completion.result.get();
}
else
{
// Actually execute the statement
return detail::async_execute_statement(
*channel_,
stmt_msg_.statement_id.value,
params_first,
params_last,
std::forward<CompletionToken>(token),
info
);
}
auto initiation = [](auto&& handler, error_code err, error_info* info,
prepared_statement<StreamType>& stmt, ForwardIterator params_first,
ForwardIterator params_last) {
if (err)
{
auto executor = boost::asio::get_associated_executor(
handler,
stmt.next_layer().get_executor()
);
boost::asio::post(boost::asio::bind_executor(
executor,
[handler = std::forward<decltype(handler)>(handler), err] () mutable {
std::forward<decltype(handler)>(handler)(err, resultset<StreamType>());
}
));
}
else
{
// Actually execute the statement
detail::async_execute_statement(
*stmt.channel_,
stmt.stmt_msg_.statement_id.value,
params_first,
params_last,
std::forward<decltype(handler)>(handler),
info
);
}
};
return boost::asio::async_initiate<CompletionToken, execute_signature>(
initiation,
token,
err,
info,
std::ref(*this),
params_first,
params_last
);
}
template <typename StreamType>
@ -144,8 +165,9 @@ void boost::mysql::prepared_statement<StreamType>::close()
}
template <typename StreamType>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(
typename boost::mysql::prepared_statement<StreamType>::close_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
typename boost::mysql::prepared_statement<StreamType>::close_signature
)
@ -156,7 +178,6 @@ boost::mysql::prepared_statement<StreamType>::async_close(
{
assert(valid());
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, close_signature>();
return detail::async_close_statement(
*channel_,
id(),

View File

@ -9,8 +9,8 @@
#define BOOST_MYSQL_IMPL_RESULTSET_HPP
#include "boost/mysql/detail/network_algorithms/read_row.hpp"
#include "boost/mysql/detail/auxiliar/check_completion_token.hpp"
#include <boost/asio/coroutine.hpp>
#include <boost/asio/bind_executor.hpp>
#include <cassert>
#include <limits>
@ -122,9 +122,61 @@ std::vector<boost::mysql::owning_row> boost::mysql::resultset<StreamType>::fetch
return fetch_many(std::numeric_limits<std::size_t>::max());
}
template<typename StreamType>
struct boost::mysql::resultset<StreamType>::fetch_one_op
: boost::asio::coroutine
{
resultset<StreamType>& resultset_;
error_info* output_info_;
fetch_one_op(
resultset<StreamType>& obj,
error_info* output_info
) :
resultset_(obj),
output_info_(output_info)
{
}
template<class Self>
void operator()(
Self& self,
error_code err = {},
detail::read_row_result result=detail::read_row_result::error
)
{
BOOST_ASIO_CORO_REENTER(*this)
{
if (resultset_.complete())
{
// ensure return as if by post
BOOST_ASIO_CORO_YIELD boost::asio::post(std::move(self));
self.complete(error_code(), nullptr);
BOOST_ASIO_CORO_YIELD break;
}
BOOST_ASIO_CORO_YIELD detail::async_read_row(
resultset_.deserializer_,
*resultset_.channel_,
resultset_.meta_.fields(),
resultset_.buffer_,
resultset_.current_row_.values(),
resultset_.ok_packet_,
std::move(self),
output_info_
);
resultset_.eof_received_ = result == detail::read_row_result::eof;
self.complete(
err,
result == detail::read_row_result::row ? &resultset_.current_row_ : nullptr
);
}
}
};
template <typename StreamType>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(
typename boost::mysql::resultset<StreamType>::fetch_one_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
typename boost::mysql::resultset<StreamType>::fetch_one_signature
)
@ -133,83 +185,35 @@ boost::mysql::resultset<StreamType>::async_fetch_one(
error_info* info
)
{
struct op: detail::async_op<StreamType, CompletionToken, fetch_one_signature, op>
{
resultset<StreamType>& resultset_;
op(
boost::asio::async_completion<CompletionToken, fetch_one_signature>& completion,
detail::channel<StreamType>& chan,
error_info* output_info,
resultset<StreamType>& obj
):
detail::async_op<StreamType, CompletionToken, fetch_one_signature, op>(completion, chan, output_info),
resultset_(obj)
{
};
void operator()(
error_code err,
detail::read_row_result result=detail::read_row_result::error,
bool cont=true
)
{
BOOST_ASIO_CORO_REENTER(*this)
{
if (resultset_.complete())
{
this->complete(cont, error_code(), nullptr);
BOOST_ASIO_CORO_YIELD break;
}
BOOST_ASIO_CORO_YIELD detail::async_read_row(
resultset_.deserializer_,
*resultset_.channel_,
resultset_.meta_.fields(),
resultset_.buffer_,
resultset_.current_row_.values(),
resultset_.ok_packet_,
std::move(*this),
this->get_output_info()
);
resultset_.eof_received_ = result == detail::read_row_result::eof;
this->complete(
cont,
err,
result == detail::read_row_result::row ? &resultset_.current_row_ : nullptr
);
}
}
};
assert(valid());
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, fetch_one_signature>();
return op::initiate(std::forward<CompletionToken>(token), *channel_, info, *this);
return boost::asio::async_compose<CompletionToken, fetch_one_signature>(
fetch_one_op(*this, info),
token,
*this
);
}
template <typename StreamType>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
typename boost::mysql::resultset<StreamType>::fetch_many_signature
)
boost::mysql::resultset<StreamType>::async_fetch_many(
std::size_t count,
CompletionToken&& token,
error_info* info
)
template<typename StreamType>
struct boost::mysql::resultset<StreamType>::fetch_many_op
: boost::asio::coroutine
{
struct op_impl
struct impl_struct
{
resultset<StreamType>& parent_resultset;
std::vector<owning_row> rows;
detail::bytestring buffer;
std::vector<value> values;
std::size_t remaining;
error_info* output_info;
bool cont {false};
op_impl(resultset<StreamType>& obj, std::size_t count):
impl_struct(resultset<StreamType>& obj, error_info* output_info, std::size_t count):
parent_resultset(obj),
remaining(count)
remaining(count),
output_info(output_info)
{
};
@ -222,71 +226,107 @@ boost::mysql::resultset<StreamType>::async_fetch_many(
}
};
struct op : detail::async_op<StreamType, CompletionToken, fetch_many_signature, op>
std::shared_ptr<impl_struct> impl_;
fetch_many_op(
resultset<StreamType>& obj,
error_info* output_info,
std::size_t count
) :
impl_(std::make_shared<impl_struct>(obj, output_info, count))
{
std::shared_ptr<op_impl> impl_;
op(
boost::asio::async_completion<CompletionToken, fetch_many_signature>& completion,
detail::channel<StreamType>& chan,
error_info* output_info,
resultset<StreamType>& obj,
std::size_t count
) :
detail::async_op<StreamType, CompletionToken, fetch_many_signature, op>(
completion, chan, output_info),
impl_(std::make_shared<op_impl>(obj, count))
{
};
void operator()(
error_code err,
detail::read_row_result result=detail::read_row_result::error,
bool cont=true
)
{
BOOST_ASIO_CORO_REENTER(*this)
{
while (!impl_->parent_resultset.complete() && impl_->remaining > 0)
{
BOOST_ASIO_CORO_YIELD detail::async_read_row(
impl_->parent_resultset.deserializer_,
this->get_channel(),
impl_->parent_resultset.meta_.fields(),
impl_->buffer,
impl_->values,
impl_->parent_resultset.ok_packet_,
std::move(*this),
this->get_output_info()
);
if (result == detail::read_row_result::error)
{
this->complete(cont, err, std::move(impl_->rows));
BOOST_ASIO_CORO_YIELD break;
}
else if (result == detail::read_row_result::eof)
{
impl_->parent_resultset.eof_received_ = true;
}
else
{
impl_->row_received();
}
}
this->complete(cont, err, std::move(impl_->rows));
}
}
};
template <typename Self>
auto bind_handler(Self& self, error_code err)
{
auto executor = boost::asio::get_associated_executor(
self,
impl_->parent_resultset.get_executor()
);
return boost::asio::bind_executor(
executor,
std::bind(std::move(self), err, detail::read_row_result::eof)
);
}
template<class Self>
void operator()(
Self& self,
error_code err = {},
detail::read_row_result result=detail::read_row_result::error
)
{
impl_struct& impl = *impl_;
BOOST_ASIO_CORO_REENTER(*this)
{
while (!impl.parent_resultset.complete() && impl.remaining > 0)
{
impl.cont = true;
BOOST_ASIO_CORO_YIELD detail::async_read_row(
impl.parent_resultset.deserializer_,
*impl.parent_resultset.channel_,
impl.parent_resultset.meta_.fields(),
impl.buffer,
impl.values,
impl.parent_resultset.ok_packet_,
std::move(self),
impl.output_info
);
if (result == detail::read_row_result::error)
{
self.complete(err, std::move(impl.rows));
BOOST_ASIO_CORO_YIELD break;
}
else if (result == detail::read_row_result::eof)
{
impl.parent_resultset.eof_received_ = true;
}
else
{
impl.row_received();
}
}
if (!impl.cont)
{
// Ensure we call handler as if dispatched using post
// through the correct executor
BOOST_ASIO_CORO_YIELD
boost::asio::post(bind_handler(self, err));
}
self.complete(err, std::move(impl.rows));
}
}
};
template <typename StreamType>
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(
typename boost::mysql::resultset<StreamType>::fetch_many_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
typename boost::mysql::resultset<StreamType>::fetch_many_signature
)
boost::mysql::resultset<StreamType>::async_fetch_many(
std::size_t count,
CompletionToken&& token,
error_info* info
)
{
assert(valid());
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, fetch_many_signature>();
return op::initiate(std::forward<CompletionToken>(token), *channel_, info, *this, count);
return boost::asio::async_compose<CompletionToken, fetch_many_signature>(
fetch_many_op(*this, info, count),
token,
*this
);
}
template <typename StreamType>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(
typename boost::mysql::resultset<StreamType>::fetch_all_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
typename boost::mysql::resultset<StreamType>::fetch_all_signature
)

View File

@ -11,7 +11,6 @@
#include "boost/mysql/resultset.hpp"
#include "boost/mysql/detail/protocol/channel.hpp"
#include "boost/mysql/detail/protocol/prepared_statement_messages.hpp"
#include "boost/mysql/detail/auxiliar/async_result_macro.hpp"
#include <optional>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/local/stream_protocol.hpp>
@ -77,6 +76,12 @@ public:
prepared_statement(detail::channel<Stream>& chan, const detail::com_stmt_prepare_ok_packet& msg) noexcept:
channel_(&chan), stmt_msg_(msg) {}
/// The executor type associated to this object.
using executor_type = typename Stream::executor_type;
/// Retrieves the executor associated to this object.
executor_type get_executor() { assert(channel_); return channel_->get_executor(); }
/// Retrieves the stream object associated with the underlying connection.
Stream& next_layer() noexcept { assert(channel_); return channel_->next_layer(); }
@ -99,14 +104,14 @@ public:
* Use no_statement_params to execute a statement with no params.
*/
template <typename Collection>
resultset<Stream> execute(const Collection& params, error_code& err, error_info& info) const
resultset<Stream> execute(const Collection& params, error_code& err, error_info& info)
{
return execute(std::begin(params), std::end(params), err, info);
}
/// Executes a statement (collection, sync with exceptions code version).
template <typename Collection>
resultset<Stream> execute(const Collection& params) const
resultset<Stream> execute(const Collection& params)
{
return execute(std::begin(params), std::end(params));
}
@ -119,9 +124,9 @@ public:
* \details It is **not** necessary to keep the collection of parameters or the
* values they may point to alive.
*/
template <typename Collection, typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, execute_signature)
async_execute(const Collection& params, CompletionToken&& token, error_info* info=nullptr) const
template <typename Collection, BOOST_ASIO_COMPLETION_TOKEN_FOR(execute_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, execute_signature)
async_execute(const Collection& params, CompletionToken&& token, error_info* info=nullptr)
{
return async_execute(
std::begin(params),
@ -140,21 +145,21 @@ public:
*/
template <typename ForwardIterator>
resultset<Stream> execute(ForwardIterator params_first, ForwardIterator params_last,
error_code&, error_info&) const;
error_code&, error_info&);
/// Executes a statement (iterator, sync with exceptions version).
template <typename ForwardIterator>
resultset<Stream> execute(ForwardIterator params_first, ForwardIterator params_last) const;
resultset<Stream> execute(ForwardIterator params_first, ForwardIterator params_last);
/**
* \brief Executes a statement (iterator, async version).
* \details The sequence [params_first, params_last) and the values that may be pointed
* by the elements of the sequence need **not** be kept alive.
*/
template <typename ForwardIterator, typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, execute_signature)
template <typename ForwardIterator, BOOST_ASIO_COMPLETION_TOKEN_FOR(execute_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, execute_signature)
async_execute(ForwardIterator params_first, ForwardIterator params_last,
CompletionToken&& token, error_info* info=nullptr) const;
CompletionToken&& token, error_info* info=nullptr);
/**
* \brief Closes a prepared statement, deallocating it from the server (sync with error code version).
@ -177,8 +182,8 @@ public:
using close_signature = void(error_code);
/// Closes a prepared statement, deallocating it from the server (async version).
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, close_signature)
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(close_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, close_signature)
async_close(CompletionToken&& token, error_info* info=nullptr);
};

View File

@ -14,7 +14,6 @@
#include "boost/mysql/detail/protocol/channel.hpp"
#include "boost/mysql/detail/auxiliar/bytestring.hpp"
#include "boost/mysql/detail/network_algorithms/common.hpp" // deserialize_row_fn
#include "boost/mysql/detail/auxiliar/async_result_macro.hpp"
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/local/stream_protocol.hpp>
#include <cassert>
@ -75,7 +74,12 @@ class resultset
detail::bytestring buffer_;
detail::ok_packet ok_packet_;
bool eof_received_ {false};
public:
struct fetch_one_op;
struct fetch_many_op;
struct fetch_many_op_impl;
public:
/// Default constructor.
resultset(): channel_(nullptr) {};
@ -85,6 +89,12 @@ public:
resultset(channel_type& channel, detail::bytestring&& buffer, const detail::ok_packet& ok_pack):
channel_(&channel), buffer_(std::move(buffer)), ok_packet_(ok_pack), eof_received_(true) {};
/// The executor type associated to the object.
using executor_type = typename channel_type::executor_type;
/// Retrieves the executor associated to the object.
executor_type get_executor() { assert(channel_); return channel_->get_executor(); }
/// Retrieves the stream object associated with the underlying connection.
StreamType& next_layer() noexcept { assert(channel_); return channel_->next_layer(); }
@ -138,24 +148,24 @@ public:
using fetch_one_signature = void(error_code, const row*);
/// Fetchs a single row (async version).
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, fetch_one_signature)
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(fetch_one_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, fetch_one_signature)
async_fetch_one(CompletionToken&& token, error_info* info=nullptr);
/// Handler signature for resultset::async_fetch_many.
using fetch_many_signature = void(error_code, std::vector<owning_row>);
/// Fetches at most count rows (async version).
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, fetch_many_signature)
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(fetch_many_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, fetch_many_signature)
async_fetch_many(std::size_t count, CompletionToken&& token, error_info* info=nullptr);
/// Handler signature for resultset::async_fetch_all.
using fetch_all_signature = void(error_code, std::vector<owning_row>);
/// Fetches all available rows (async version).
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, fetch_all_signature)
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(fetch_all_signature) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, fetch_all_signature)
async_fetch_all(CompletionToken&& token, error_info* info=nullptr);
/**

View File

@ -82,8 +82,13 @@ find_package(Boost REQUIRED COMPONENTS coroutine)
add_executable(
mysql_integrationtests
integration/network_functions/network_functions_impl.cpp
integration/network_functions/sync.cpp
integration/network_functions/async_callback.cpp
integration/network_functions/async_coroutine.cpp
integration/network_functions/async_coroutinecpp20.cpp
integration/network_functions/async_future.cpp
integration/metadata_validator.cpp
integration/network_functions.cpp
integration/connect.cpp
integration/handshake.cpp
integration/query.cpp

View File

@ -552,27 +552,27 @@ INSTANTIATE_TEST_SUITE_P(YEAR, DatabaseTypesTest, Values(
INSTANTIATE_TEST_SUITE_P(STRING, DatabaseTypesTest, Values(
database_types_testcase("types_string", "field_char", "regular", "test_char", field_type::char_),
database_types_testcase("types_string", "field_char", "utf8", u8"\u00F1", field_type::char_),
database_types_testcase("types_string", "field_char", "utf8", "\xc3\xb1", field_type::char_),
database_types_testcase("types_string", "field_char", "empty", "", field_type::char_),
database_types_testcase("types_string", "field_varchar", "regular", "test_varchar", field_type::varchar),
database_types_testcase("types_string", "field_varchar", "utf8", u8"\u00D1", field_type::varchar),
database_types_testcase("types_string", "field_varchar", "utf8", "\xc3\x91", field_type::varchar),
database_types_testcase("types_string", "field_varchar", "empty", "", field_type::varchar),
database_types_testcase("types_string", "field_tinytext", "regular", "test_tinytext", field_type::text),
database_types_testcase("types_string", "field_tinytext", "utf8", u8"\u00e1", field_type::text),
database_types_testcase("types_string", "field_tinytext", "utf8", "\xc3\xa1", field_type::text),
database_types_testcase("types_string", "field_tinytext", "empty", "", field_type::text),
database_types_testcase("types_string", "field_text", "regular", "test_text", field_type::text),
database_types_testcase("types_string", "field_text", "utf8", u8"\u00e9", field_type::text),
database_types_testcase("types_string", "field_text", "utf8", "\xc3\xa9", field_type::text),
database_types_testcase("types_string", "field_text", "empty", "", field_type::text),
database_types_testcase("types_string", "field_mediumtext", "regular", "test_mediumtext", field_type::text),
database_types_testcase("types_string", "field_mediumtext", "utf8", u8"\u00ed", field_type::text),
database_types_testcase("types_string", "field_mediumtext", "utf8", "\xc3\xad", field_type::text),
database_types_testcase("types_string", "field_mediumtext", "empty", "", field_type::text),
database_types_testcase("types_string", "field_longtext", "regular", "test_longtext", field_type::text),
database_types_testcase("types_string", "field_longtext", "utf8", u8"\u00f3", field_type::text),
database_types_testcase("types_string", "field_longtext", "utf8", "\xc3\xb3", field_type::text),
database_types_testcase("types_string", "field_longtext", "empty", "", field_type::text),
database_types_testcase("types_string", "field_enum", "regular", "red", field_type::enum_),

View File

@ -1,806 +0,0 @@
//
// Copyright (c) 2019-2020 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)
//
#include "network_functions.hpp"
#include <boost/asio/spawn.hpp>
#include <boost/asio/use_future.hpp>
#include <boost/asio/local/stream_protocol.hpp>
#include <future>
using namespace boost::mysql::test;
using boost::mysql::connection_params;
using boost::mysql::error_info;
using boost::mysql::error_code;
using boost::mysql::detail::make_error_code;
using boost::mysql::errc;
using boost::mysql::value;
using boost::mysql::row;
using boost::mysql::owning_row;
using boost::asio::yield_context;
using boost::asio::use_future;
namespace
{
// A non-empty error_info to verify that we correctly clear the message
error_info make_initial_error_info()
{
return error_info("Error info not cleared correctly");
}
error_code make_initial_error_code()
{
return make_error_code(errc::no);
}
template <typename Stream>
class sync_errc : public network_functions<Stream>
{
template <typename Callable>
static auto impl(Callable&& cb) {
using R = decltype(cb(std::declval<error_code&>(), std::declval<error_info&>()));
network_result<R> res (make_initial_error_code(), make_initial_error_info());
res.value = cb(res.err, *res.info);
return res;
}
public:
using connection_type = typename network_functions<Stream>::connection_type;
using prepared_statement_type = typename network_functions<Stream>::prepared_statement_type;
using resultset_type = typename network_functions<Stream>::resultset_type;
const char* name() const override { return "sync_errc"; }
network_result<no_result> connect(
connection_type& conn,
const typename Stream::endpoint_type& ep,
const connection_params& params
) override
{
return impl([&](error_code& code, error_info& info) {
conn.connect(ep, params, code, info);
return no_result();
});
}
network_result<no_result> handshake(
connection_type& conn,
const connection_params& params
) override
{
return impl([&](error_code& code, error_info& info) {
conn.handshake(params, code, info);
return no_result();
});
}
network_result<resultset_type> query(
connection_type& conn,
std::string_view query
) override
{
return impl([&](error_code& code, error_info& info) {
return conn.query(query, code, info);
});
}
network_result<prepared_statement_type> prepare_statement(
connection_type& conn,
std::string_view statement
) override
{
return impl([&conn, statement](error_code& err, error_info& info) {
return conn.prepare_statement(statement, err, info);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
value_list_it params_first,
value_list_it params_last
) override
{
return impl([=, &stmt](error_code& err, error_info& info) {
return stmt.execute(params_first, params_last, err, info);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
const std::vector<value>& values
) override
{
return impl([&stmt, &values](error_code& err, error_info& info) {
return stmt.execute(values, err, info);
});
}
network_result<no_result> close_statement(
prepared_statement_type& stmt
) override
{
return impl([&](error_code& code, error_info& info) {
stmt.close(code, info);
return no_result();
});
}
network_result<const row*> fetch_one(
resultset_type& r
) override
{
return impl([&](error_code& code, error_info& info) {
return r.fetch_one(code, info);
});
}
network_result<std::vector<owning_row>> fetch_many(
resultset_type& r,
std::size_t count
) override
{
return impl([&](error_code& code, error_info& info) {
return r.fetch_many(count, code, info);
});
}
network_result<std::vector<owning_row>> fetch_all(
resultset_type& r
) override
{
return impl([&](error_code& code, error_info& info) {
return r.fetch_all(code, info);
});
}
network_result<no_result> quit(
connection_type& conn
) override
{
return impl([&](error_code& code, error_info& info) {
conn.quit(code, info);
return no_result();
});
}
network_result<no_result> close(
connection_type& conn
) override
{
return impl([&](error_code& code, error_info& info) {
conn.close(code, info);
return no_result();
});
}
};
template <typename Stream>
class sync_exc : public network_functions<Stream>
{
template <typename Callable>
static auto impl(Callable&& cb) {
using R = decltype(cb());
network_result<R> res;
try
{
res.value = cb();
}
catch (const boost::system::system_error& err)
{
res.err = err.code();
res.info = error_info(err.what());
}
return res;
}
public:
using connection_type = typename network_functions<Stream>::connection_type;
using prepared_statement_type = typename network_functions<Stream>::prepared_statement_type;
using resultset_type = typename network_functions<Stream>::resultset_type;
const char* name() const override { return "sync_exc"; }
network_result<no_result> connect(
connection_type& conn,
const typename Stream::endpoint_type& ep,
const connection_params& params
) override
{
return impl([&] {
conn.connect(ep, params);
return no_result();
});
}
network_result<no_result> handshake(
connection_type& conn,
const connection_params& params
) override
{
return impl([&] {
conn.handshake(params);
return no_result();
});
}
network_result<resultset_type> query(
connection_type& conn,
std::string_view query
) override
{
return impl([&] {
return conn.query(query);
});
}
network_result<prepared_statement_type> prepare_statement(
connection_type& conn,
std::string_view statement
) override
{
return impl([&conn, statement] {
return conn.prepare_statement(statement);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
value_list_it params_first,
value_list_it params_last
) override
{
return impl([&]{
return stmt.execute(params_first, params_last);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
const std::vector<value>& values
) override
{
return impl([&stmt, &values] {
return stmt.execute(values);
});
}
network_result<no_result> close_statement(
prepared_statement_type& stmt
) override
{
return impl([&] {
stmt.close();
return no_result();
});
}
network_result<const row*> fetch_one(
resultset_type& r
) override
{
return impl([&] {
return r.fetch_one();
});
}
network_result<std::vector<owning_row>> fetch_many(
resultset_type& r,
std::size_t count
) override
{
return impl([&] {
return r.fetch_many(count);
});
}
network_result<std::vector<owning_row>> fetch_all(
resultset_type& r
) override
{
return impl([&] {
return r.fetch_all();
});
}
network_result<no_result> quit(
connection_type& conn
) override
{
return impl([&] {
conn.quit();
return no_result();
});
}
network_result<no_result> close(
connection_type& conn
) override
{
return impl([&] {
conn.close();
return no_result();
});
}
};
template <typename Stream>
class async_callback : public network_functions<Stream>
{
// This allows for two versions of the tests: one where we pass a
// non-nullptr error_info* to the initiating function, and another
// one where we pass nullptr.
bool use_errinfo_;
template <typename R, typename Callable>
network_result<R> impl(Callable&& cb)
{
struct handler
{
std::promise<network_result<R>>& prom;
error_info* info; // nullptr for !use_errinfo_
// For operations with a return type
void operator()(error_code code, R retval)
{
if (info)
{
prom.set_value(network_result<R>(code, std::move(*info), std::move(retval)));
}
else
{
prom.set_value(network_result<R>(code, std::move(retval)));
}
}
// For operations without a return type (R=no_result)
void operator()(error_code code)
{
if (info)
{
prom.set_value(network_result<R>(code, std::move(*info)));
}
else
{
prom.set_value(network_result<R>(code));
}
}
};
std::promise<network_result<R>> prom;
error_info info = make_initial_error_info();
error_info* infoptr = use_errinfo_ ? &info : nullptr;
cb(handler{prom, infoptr}, infoptr);
return prom.get_future().get();
}
public:
using connection_type = typename network_functions<Stream>::connection_type;
using prepared_statement_type = typename network_functions<Stream>::prepared_statement_type;
using resultset_type = typename network_functions<Stream>::resultset_type;
async_callback(bool use_errinfo): use_errinfo_(use_errinfo) {}
const char* name() const override
{
return use_errinfo_ ? "async_callback_errinfo" : "async_callback_noerrinfo";
}
network_result<no_result> connect(
connection_type& conn,
const typename Stream::endpoint_type& ep,
const connection_params& params
) override
{
return impl<no_result>([&](auto&& token, error_info* info) {
return conn.async_connect(ep, params, std::forward<decltype(token)>(token), info);
});
}
network_result<no_result> handshake(
connection_type& conn,
const connection_params& params
) override
{
return impl<no_result>([&](auto&& token, error_info* info) {
return conn.async_handshake(params, std::forward<decltype(token)>(token), info);
});
}
network_result<resultset_type> query(
connection_type& conn,
std::string_view query
) override
{
return impl<resultset_type>([&](auto&& token, error_info* info) {
return conn.async_query(query, std::forward<decltype(token)>(token), info);
});
}
network_result<prepared_statement_type> prepare_statement(
connection_type& conn,
std::string_view statement
) override
{
return impl<prepared_statement_type>([&conn, statement](auto&& token, error_info* info) {
return conn.async_prepare_statement(statement, std::forward<decltype(token)>(token), info);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
value_list_it params_first,
value_list_it params_last
) override
{
return impl<resultset_type>([&](auto&& token, error_info* info) {
return stmt.async_execute(params_first, params_last, std::forward<decltype(token)>(token), info);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
const std::vector<value>& values
) override
{
return impl<resultset_type>([&](auto&& token, error_info* info) {
return stmt.async_execute(values, std::forward<decltype(token)>(token), info);
});
}
network_result<no_result> close_statement(
prepared_statement_type& stmt
) override
{
return impl<no_result>([&](auto&& token, error_info* info) {
return stmt.async_close(std::forward<decltype(token)>(token), info);
});
}
network_result<const row*> fetch_one(
resultset_type& r
) override
{
return impl<const row*>([&](auto&& token, error_info* info) {
return r.async_fetch_one(std::forward<decltype(token)>(token), info);
});
}
network_result<std::vector<owning_row>> fetch_many(
resultset_type& r,
std::size_t count
) override
{
return impl<std::vector<owning_row>>([&](auto&& token, error_info* info) {
return r.async_fetch_many(count, std::forward<decltype(token)>(token), info);
});
}
network_result<std::vector<owning_row>> fetch_all(
resultset_type& r
) override
{
return impl<std::vector<owning_row>>([&](auto&& token, error_info* info) {
return r.async_fetch_all(std::forward<decltype(token)>(token), info);
});
}
network_result<no_result> quit(
connection_type& conn
) override
{
return impl<no_result>([&](auto&& token, error_info* info) {
return conn.async_quit(std::forward<decltype(token)>(token), info);
});
}
network_result<no_result> close(
connection_type& conn
) override
{
return impl<no_result>([&](auto&& token, error_info* info) {
return conn.async_close(std::forward<decltype(token)>(token), info);
});
}
};
template <typename Stream>
class async_coroutine : public network_functions<Stream>
{
bool use_errinfo_;
template <typename IoObj, typename Callable>
auto impl(IoObj& obj, Callable&& cb) {
using R = decltype(cb(
std::declval<yield_context>(),
std::declval<error_info*>()
));
std::promise<network_result<R>> prom;
boost::asio::spawn(obj.next_layer().get_executor(), [&, this](yield_context yield) {
error_code ec = make_initial_error_code();
error_info info = make_initial_error_info();
R result = cb(yield[ec], use_errinfo_ ? &info: nullptr);
if (use_errinfo_)
{
prom.set_value(network_result<R>(ec, std::move(info), std::move(result)));
}
else
{
prom.set_value(network_result<R>(ec, std::move(result)));
}
});
return prom.get_future().get();
}
public:
using connection_type = typename network_functions<Stream>::connection_type;
using prepared_statement_type = typename network_functions<Stream>::prepared_statement_type;
using resultset_type = typename network_functions<Stream>::resultset_type;
async_coroutine(bool use_errinfo): use_errinfo_(use_errinfo) {}
const char* name() const override
{
return use_errinfo_ ? "async_coroutine_errinfo" : "async_coroutine_noerrinfo";
}
network_result<no_result> connect(
connection_type& conn,
const typename Stream::endpoint_type& ep,
const connection_params& params
) override
{
return impl(conn, [&](yield_context yield, error_info* info) {
conn.async_connect(ep, params, yield, info);
return no_result();
});
}
network_result<no_result> handshake(
connection_type& conn,
const connection_params& params
) override
{
return impl(conn, [&](yield_context yield, error_info* info) {
conn.async_handshake(params, yield, info);
return no_result();
});
}
network_result<resultset_type> query(
connection_type& conn,
std::string_view query
) override
{
return impl(conn, [&](yield_context yield, error_info* info) {
return conn.async_query(query, yield, info);
});
}
network_result<prepared_statement_type> prepare_statement(
connection_type& conn,
std::string_view statement
) override
{
return impl(conn, [&](yield_context yield, error_info* info) {
return conn.async_prepare_statement(statement, yield, info);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
value_list_it params_first,
value_list_it params_last
) override
{
return impl(stmt, [&](yield_context yield, error_info* info) {
return stmt.async_execute(params_first, params_last, yield, info);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
const std::vector<value>& values
) override
{
return impl(stmt, [&](yield_context yield, error_info* info) {
return stmt.async_execute(values, yield, info);
});
}
network_result<no_result> close_statement(
prepared_statement_type& stmt
) override
{
return impl(stmt, [&](yield_context yield, error_info* info) {
stmt.async_close(yield, info);
return no_result();
});
}
network_result<const row*> fetch_one(
resultset_type& r
) override
{
return impl(r, [&](yield_context yield, error_info* info) {
return r.async_fetch_one(yield, info);
});
}
network_result<std::vector<owning_row>> fetch_many(
resultset_type& r,
std::size_t count
) override
{
return impl(r, [&](yield_context yield, error_info* info) {
return r.async_fetch_many(count, yield, info);
});
}
network_result<std::vector<owning_row>> fetch_all(
resultset_type& r
) override
{
return impl(r, [&](yield_context yield, error_info* info) {
return r.async_fetch_all(yield, info);
});
}
network_result<no_result> quit(
connection_type& conn
) override
{
return impl(conn, [&](yield_context yield, error_info* info) {
conn.async_quit(yield, info);
return no_result();
});
}
network_result<no_result> close(
connection_type& conn
) override
{
return impl(conn, [&](yield_context yield, error_info* info) {
conn.async_close(yield, info);
return no_result();
});
}
};
template <typename Stream>
class async_future : public network_functions<Stream>
{
template <typename Callable>
static auto impl(Callable&& cb) {
using R = decltype(cb().get()); // Callable returns a future<R>
std::future<R> fut = cb();
try
{
// error_info is not available here, so we skip validation
return network_result<R>(
error_code(),
fut.get()
);
}
catch (const boost::system::system_error& err)
{
// error_info is not available here, so we skip validation
return network_result<R>(err.code());
}
}
template <typename Callable>
static network_result<no_result> impl_no_result(Callable&& cb) {
std::future<void> fut = cb();
try
{
// error_info is not available here, so we skip validation
fut.get();
return network_result<no_result>(error_code());
}
catch (const boost::system::system_error& err)
{
// error_info is not available here, so we skip validation
return network_result<no_result>(err.code());
}
}
public:
using connection_type = typename network_functions<Stream>::connection_type;
using prepared_statement_type = typename network_functions<Stream>::prepared_statement_type;
using resultset_type = typename network_functions<Stream>::resultset_type;
const char* name() const override { return "async_future_noerrinfo"; }
network_result<no_result> connect(
connection_type& conn,
const typename Stream::endpoint_type& ep,
const connection_params& params
) override
{
return impl_no_result([&] {
return conn.async_connect(ep, params, use_future);
});
}
network_result<no_result> handshake(
connection_type& conn,
const connection_params& params
) override
{
return impl_no_result([&] {
return conn.async_handshake(params, use_future);
});
}
network_result<resultset_type> query(
connection_type& conn,
std::string_view query
) override
{
return impl([&] {
return conn.async_query(query, use_future);
});
}
network_result<prepared_statement_type> prepare_statement(
connection_type& conn,
std::string_view statement
) override
{
return impl([&]{
return conn.async_prepare_statement(statement, use_future);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
value_list_it params_first,
value_list_it params_last
) override
{
return impl([&]{
return stmt.async_execute(params_first, params_last, use_future);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
const std::vector<value>& values
) override
{
return impl([&] {
return stmt.async_execute(values, use_future);
});
}
network_result<no_result> close_statement(
prepared_statement_type& stmt
) override
{
return impl_no_result([&] {
return stmt.async_close(use_future);
});
}
network_result<const row*> fetch_one(
resultset_type& r
) override
{
return impl([&] {
return r.async_fetch_one(use_future);
});
}
network_result<std::vector<owning_row>> fetch_many(
resultset_type& r,
std::size_t count
) override
{
return impl([&] {
return r.async_fetch_many(count, use_future);
});
}
network_result<std::vector<owning_row>> fetch_all(
resultset_type& r
) override
{
return impl([&] {
return r.async_fetch_all(use_future);
});
}
network_result<no_result> quit(
connection_type& conn
) override
{
return impl_no_result([&] {
return conn.async_quit(use_future);
});
}
network_result<no_result> close(
connection_type& conn
) override
{
return impl_no_result([&] {
return conn.async_close(use_future);
});
}
};
}
// Visible stuff
template <typename Stream>
boost::mysql::test::network_function_array<Stream>
boost::mysql::test::make_all_network_functions()
{
static sync_errc<Stream> sync_errc_obj;
static sync_exc<Stream> sync_exc_obj;
static async_callback<Stream> async_callback_errinfo_obj (true);
static async_callback<Stream> async_callback_noerrinfo_obj (false);
static async_coroutine<Stream> async_coroutine_errinfo_obj (true);
static async_coroutine<Stream> async_coroutine_noerrinfo_obj (false);
static async_future<Stream> async_future_obj;
return {
&sync_errc_obj,
&sync_exc_obj,
&async_callback_errinfo_obj,
&async_callback_noerrinfo_obj,
&async_coroutine_errinfo_obj,
&async_coroutine_noerrinfo_obj,
&async_future_obj
};
}
template boost::mysql::test::network_function_array<boost::asio::ip::tcp::socket>
boost::mysql::test::make_all_network_functions();
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
template boost::mysql::test::network_function_array<boost::asio::local::stream_protocol::socket>
boost::mysql::test::make_all_network_functions();
#endif

View File

@ -137,10 +137,7 @@ public:
};
template <typename Stream>
using network_function_array = std::array<network_functions<Stream>*, 7>;
template <typename Stream>
network_function_array<Stream> make_all_network_functions();
std::vector<network_functions<Stream>*> make_all_network_functions();
} // test
} // mysql

View File

@ -0,0 +1,232 @@
//
// Copyright (c) 2019-2020 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)
//
#include "network_functions_impl.hpp"
#include <boost/asio/use_future.hpp>
using namespace boost::mysql::test;
using boost::mysql::connection_params;
using boost::mysql::error_code;
using boost::mysql::error_info;
using boost::mysql::errc;
using boost::mysql::value;
using boost::mysql::row;
using boost::mysql::owning_row;
namespace
{
class handler_call_tracker
{
int call_count_ {};
std::thread::id calling_thread_ {};
public:
handler_call_tracker() = default;
void register_call()
{
++call_count_;
calling_thread_ = std::this_thread::get_id();
}
int call_count() const { return call_count_; }
std::thread::id calling_thread() const { return calling_thread_; }
};
template <typename Stream>
class async_callback : public network_functions<Stream>
{
// This allows for two versions of the tests: one where we pass a
// non-nullptr error_info* to the initiating function, and another
// one where we pass nullptr.
bool use_errinfo_;
template <typename R, typename Callable>
network_result<R> impl(Callable&& cb)
{
struct handler
{
std::promise<network_result<R>>& prom;
error_info* info; // nullptr for !use_errinfo_
handler_call_tracker& call_tracker_;
// For operations with a return type
void operator()(error_code code, R retval)
{
call_tracker_.register_call();
if (info)
{
prom.set_value(network_result<R>(code, std::move(*info), std::move(retval)));
}
else
{
prom.set_value(network_result<R>(code, std::move(retval)));
}
}
// For operations without a return type (R=no_result)
void operator()(error_code code)
{
call_tracker_.register_call();
if (info)
{
prom.set_value(network_result<R>(code, std::move(*info)));
}
else
{
prom.set_value(network_result<R>(code));
}
}
};
handler_call_tracker call_tracker;
std::promise<network_result<R>> prom;
error_info info ("error_info not cleared properly");
error_info* infoptr = use_errinfo_ ? &info : nullptr;
cb(handler{prom, infoptr, call_tracker}, infoptr);
auto res = prom.get_future().get();
EXPECT_EQ(call_tracker.call_count(), 1); // we call handler exactly once
EXPECT_NE(call_tracker.calling_thread(), std::this_thread::get_id()); // handler runs in the io_context thread
return res;
}
public:
using connection_type = typename network_functions<Stream>::connection_type;
using prepared_statement_type = typename network_functions<Stream>::prepared_statement_type;
using resultset_type = typename network_functions<Stream>::resultset_type;
async_callback(bool use_errinfo): use_errinfo_(use_errinfo) {}
const char* name() const override
{
return use_errinfo_ ? "async_callback_errinfo" : "async_callback_noerrinfo";
}
network_result<no_result> connect(
connection_type& conn,
const typename Stream::endpoint_type& ep,
const connection_params& params
) override
{
return impl<no_result>([&](auto&& token, error_info* info) {
return conn.async_connect(ep, params, std::forward<decltype(token)>(token), info);
});
}
network_result<no_result> handshake(
connection_type& conn,
const connection_params& params
) override
{
return impl<no_result>([&](auto&& token, error_info* info) {
return conn.async_handshake(params, std::forward<decltype(token)>(token), info);
});
}
network_result<resultset_type> query(
connection_type& conn,
std::string_view query
) override
{
return impl<resultset_type>([&](auto&& token, error_info* info) {
return conn.async_query(query, std::forward<decltype(token)>(token), info);
});
}
network_result<prepared_statement_type> prepare_statement(
connection_type& conn,
std::string_view statement
) override
{
return impl<prepared_statement_type>([&conn, statement](auto&& token, error_info* info) {
return conn.async_prepare_statement(statement, std::forward<decltype(token)>(token), info);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
value_list_it params_first,
value_list_it params_last
) override
{
return impl<resultset_type>([&](auto&& token, error_info* info) {
return stmt.async_execute(params_first, params_last, std::forward<decltype(token)>(token), info);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
const std::vector<value>& values
) override
{
return impl<resultset_type>([&](auto&& token, error_info* info) {
return stmt.async_execute(values, std::forward<decltype(token)>(token), info);
});
}
network_result<no_result> close_statement(
prepared_statement_type& stmt
) override
{
return impl<no_result>([&](auto&& token, error_info* info) {
return stmt.async_close(std::forward<decltype(token)>(token), info);
});
}
network_result<const row*> fetch_one(
resultset_type& r
) override
{
return impl<const row*>([&](auto&& token, error_info* info) {
return r.async_fetch_one(std::forward<decltype(token)>(token), info);
});
}
network_result<std::vector<owning_row>> fetch_many(
resultset_type& r,
std::size_t count
) override
{
return impl<std::vector<owning_row>>([&](auto&& token, error_info* info) {
return r.async_fetch_many(count, std::forward<decltype(token)>(token), info);
});
}
network_result<std::vector<owning_row>> fetch_all(
resultset_type& r
) override
{
return impl<std::vector<owning_row>>([&](auto&& token, error_info* info) {
return r.async_fetch_all(std::forward<decltype(token)>(token), info);
});
}
network_result<no_result> quit(
connection_type& conn
) override
{
return impl<no_result>([&](auto&& token, error_info* info) {
return conn.async_quit(std::forward<decltype(token)>(token), info);
});
}
network_result<no_result> close(
connection_type& conn
) override
{
return impl<no_result>([&](auto&& token, error_info* info) {
return conn.async_close(std::forward<decltype(token)>(token), info);
});
}
};
} // anon namespace
// Visible stuff
template <typename Stream>
network_functions<Stream>* boost::mysql::test::async_callback_errinfo_functions()
{
static async_callback<Stream> res (true);
return &res;
}
template <typename Stream>
network_functions<Stream>* boost::mysql::test::async_callback_noerrinfo_functions()
{
static async_callback<Stream> res (false);
return &res;
}
BOOST_MYSQL_INSTANTIATE_NETWORK_FUNCTIONS(async_callback_errinfo_functions)
BOOST_MYSQL_INSTANTIATE_NETWORK_FUNCTIONS(async_callback_noerrinfo_functions)

View File

@ -0,0 +1,196 @@
//
// Copyright (c) 2019-2020 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)
//
#include "network_functions_impl.hpp"
#include <boost/asio/spawn.hpp>
#include <future>
using namespace boost::mysql::test;
using boost::mysql::connection_params;
using boost::mysql::error_code;
using boost::mysql::error_info;
using boost::mysql::errc;
using boost::mysql::value;
using boost::mysql::row;
using boost::mysql::owning_row;
using boost::asio::yield_context;
namespace
{
template <typename Stream>
class async_coroutine : public network_functions<Stream>
{
bool use_errinfo_;
template <typename IoObj, typename Callable>
auto impl(IoObj& obj, Callable&& cb) {
using R = decltype(cb(
std::declval<yield_context>(),
std::declval<error_info*>()
));
std::promise<network_result<R>> prom;
boost::asio::spawn(obj.get_executor(), [&, this](yield_context yield) {
error_code ec = boost::mysql::detail::make_error_code(errc::no);
error_info info ("error_info not cleared properly");
R result = cb(yield[ec], use_errinfo_ ? &info: nullptr);
if (use_errinfo_)
{
prom.set_value(network_result<R>(ec, std::move(info), std::move(result)));
}
else
{
prom.set_value(network_result<R>(ec, std::move(result)));
}
});
return prom.get_future().get();
}
public:
using connection_type = typename network_functions<Stream>::connection_type;
using prepared_statement_type = typename network_functions<Stream>::prepared_statement_type;
using resultset_type = typename network_functions<Stream>::resultset_type;
async_coroutine(bool use_errinfo): use_errinfo_(use_errinfo) {}
const char* name() const override
{
return use_errinfo_ ? "async_coroutine_errinfo" : "async_coroutine_noerrinfo";
}
network_result<no_result> connect(
connection_type& conn,
const typename Stream::endpoint_type& ep,
const connection_params& params
) override
{
return impl(conn, [&](yield_context yield, error_info* info) {
conn.async_connect(ep, params, yield, info);
return no_result();
});
}
network_result<no_result> handshake(
connection_type& conn,
const connection_params& params
) override
{
return impl(conn, [&](yield_context yield, error_info* info) {
conn.async_handshake(params, yield, info);
return no_result();
});
}
network_result<resultset_type> query(
connection_type& conn,
std::string_view query
) override
{
return impl(conn, [&](yield_context yield, error_info* info) {
return conn.async_query(query, yield, info);
});
}
network_result<prepared_statement_type> prepare_statement(
connection_type& conn,
std::string_view statement
) override
{
return impl(conn, [&](yield_context yield, error_info* info) {
return conn.async_prepare_statement(statement, yield, info);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
value_list_it params_first,
value_list_it params_last
) override
{
return impl(stmt, [&](yield_context yield, error_info* info) {
return stmt.async_execute(params_first, params_last, yield, info);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
const std::vector<value>& values
) override
{
return impl(stmt, [&](yield_context yield, error_info* info) {
return stmt.async_execute(values, yield, info);
});
}
network_result<no_result> close_statement(
prepared_statement_type& stmt
) override
{
return impl(stmt, [&](yield_context yield, error_info* info) {
stmt.async_close(yield, info);
return no_result();
});
}
network_result<const row*> fetch_one(
resultset_type& r
) override
{
return impl(r, [&](yield_context yield, error_info* info) {
return r.async_fetch_one(yield, info);
});
}
network_result<std::vector<owning_row>> fetch_many(
resultset_type& r,
std::size_t count
) override
{
return impl(r, [&](yield_context yield, error_info* info) {
return r.async_fetch_many(count, yield, info);
});
}
network_result<std::vector<owning_row>> fetch_all(
resultset_type& r
) override
{
return impl(r, [&](yield_context yield, error_info* info) {
return r.async_fetch_all(yield, info);
});
}
network_result<no_result> quit(
connection_type& conn
) override
{
return impl(conn, [&](yield_context yield, error_info* info) {
conn.async_quit(yield, info);
return no_result();
});
}
network_result<no_result> close(
connection_type& conn
) override
{
return impl(conn, [&](yield_context yield, error_info* info) {
conn.async_close(yield, info);
return no_result();
});
}
};
} // anon namespace
// Visible stuff
template <typename Stream>
network_functions<Stream>* boost::mysql::test::async_coroutine_errinfo_functions()
{
static async_coroutine<Stream> res (true);
return &res;
}
template <typename Stream>
network_functions<Stream>* boost::mysql::test::async_coroutine_noerrinfo_functions()
{
static async_coroutine<Stream> res (false);
return &res;
}
BOOST_MYSQL_INSTANTIATE_NETWORK_FUNCTIONS(async_coroutine_errinfo_functions)
BOOST_MYSQL_INSTANTIATE_NETWORK_FUNCTIONS(async_coroutine_noerrinfo_functions)

View File

@ -0,0 +1,241 @@
//
// Copyright (c) 2019-2020 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)
//
#include "network_functions_impl.hpp"
#include <boost/asio/use_awaitable.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <future>
#ifdef BOOST_ASIO_HAS_CO_AWAIT
using namespace boost::mysql::test;
using boost::mysql::connection_params;
using boost::mysql::error_code;
using boost::mysql::error_info;
using boost::mysql::errc;
using boost::mysql::value;
using boost::mysql::row;
using boost::mysql::owning_row;
using boost::asio::use_awaitable;
namespace
{
template <typename Stream>
class async_coroutinecpp20 : public network_functions<Stream>
{
bool use_errinfo_;
template <typename IoObj, typename Callable>
auto impl(IoObj& obj, Callable&& cb) {
using R = typename decltype(cb(std::declval<error_info*>()))::value_type;
std::promise<network_result<R>> prom;
boost::asio::co_spawn(obj.get_executor(), [&, this] () -> boost::asio::awaitable<void> {
error_info info ("error_info not cleared properly");
try
{
R result = co_await cb(use_errinfo_ ? &info : nullptr);
if (use_errinfo_)
{
prom.set_value(network_result<R>(error_code(), std::move(info), std::move(result)));
}
else
{
prom.set_value(network_result<R>(error_code(), std::move(result)));
}
}
catch (const boost::system::system_error& err)
{
if (use_errinfo_)
{
prom.set_value(network_result<R>(err.code(), std::move(info)));
}
else
{
prom.set_value(network_result<R>(err.code()));
}
}
}, boost::asio::detached);
return prom.get_future().get();
}
template <typename IoObj, typename Callable>
auto impl_no_result(IoObj& obj, Callable&& cb) {
std::promise<network_result<no_result>> prom;
boost::asio::co_spawn(obj.get_executor(), [&, this] () -> boost::asio::awaitable<void> {
error_info info ("error_info not cleared properly");
try
{
co_await cb(use_errinfo_ ? &info : nullptr);
if (use_errinfo_)
{
prom.set_value(network_result<no_result>(error_code(), std::move(info)));
}
else
{
prom.set_value(network_result<no_result>(error_code()));
}
}
catch (const boost::system::system_error& err)
{
if (use_errinfo_)
{
prom.set_value(network_result<no_result>(err.code(), std::move(info)));
}
else
{
prom.set_value(network_result<no_result>(err.code()));
}
}
}, boost::asio::detached);
return prom.get_future().get();
}
public:
using connection_type = typename network_functions<Stream>::connection_type;
using prepared_statement_type = typename network_functions<Stream>::prepared_statement_type;
using resultset_type = typename network_functions<Stream>::resultset_type;
async_coroutinecpp20(bool use_errinfo): use_errinfo_(use_errinfo) {}
const char* name() const override
{
return use_errinfo_ ? "async_coroutinecpp20_errinfo" : "async_coroutinecpp20_noerrinfo";
}
network_result<no_result> connect(
connection_type& conn,
const typename Stream::endpoint_type& ep,
const connection_params& params
) override
{
return impl_no_result(conn, [&](error_info* info) {
return conn.async_connect(ep, params, use_awaitable, info);
});
}
network_result<no_result> handshake(
connection_type& conn,
const connection_params& params
) override
{
return impl_no_result(conn, [&](error_info* info) {
return conn.async_handshake(params, use_awaitable, info);
});
}
network_result<resultset_type> query(
connection_type& conn,
std::string_view query
) override
{
return impl(conn, [&](error_info* info) {
return conn.async_query(query, use_awaitable, info);
});
}
network_result<prepared_statement_type> prepare_statement(
connection_type& conn,
std::string_view statement
) override
{
return impl(conn, [&](error_info* info) {
return conn.async_prepare_statement(statement, use_awaitable, info);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
value_list_it params_first,
value_list_it params_last
) override
{
return impl(stmt, [&](error_info* info) {
return stmt.async_execute(params_first, params_last, use_awaitable, info);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
const std::vector<value>& values
) override
{
return impl(stmt, [&](error_info* info) {
return stmt.async_execute(values, use_awaitable, info);
});
}
network_result<no_result> close_statement(
prepared_statement_type& stmt
) override
{
return impl_no_result(stmt, [&](error_info* info) {
return stmt.async_close(use_awaitable, info);
});
}
network_result<const row*> fetch_one(
resultset_type& r
) override
{
return impl(r, [&](error_info* info) {
return r.async_fetch_one(use_awaitable, info);
});
}
network_result<std::vector<owning_row>> fetch_many(
resultset_type& r,
std::size_t count
) override
{
return impl(r, [&](error_info* info) {
return r.async_fetch_many(count, use_awaitable, info);
});
}
network_result<std::vector<owning_row>> fetch_all(
resultset_type& r
) override
{
return impl(r, [&](error_info* info) {
return r.async_fetch_all(use_awaitable, info);
});
}
network_result<no_result> quit(
connection_type& conn
) override
{
return impl_no_result(conn, [&](error_info* info) {
return conn.async_quit(use_awaitable, info);
});
}
network_result<no_result> close(
connection_type& conn
) override
{
return impl_no_result(conn, [&](error_info* info) {
return conn.async_close(use_awaitable, info);
});
}
};
} // anon namespace
// Visible stuff
// Visible stuff
template <typename Stream>
network_functions<Stream>* boost::mysql::test::async_coroutinecpp20_errinfo_functions()
{
static async_coroutinecpp20<Stream> res (true);
return &res;
}
template <typename Stream>
network_functions<Stream>* boost::mysql::test::async_coroutinecpp20_noerrinfo_functions()
{
static async_coroutinecpp20<Stream> res (false);
return &res;
}
BOOST_MYSQL_INSTANTIATE_NETWORK_FUNCTIONS(async_coroutinecpp20_errinfo_functions)
BOOST_MYSQL_INSTANTIATE_NETWORK_FUNCTIONS(async_coroutinecpp20_noerrinfo_functions)
#endif // BOOST_ASIO_HAS_CO_AWAIT

View File

@ -0,0 +1,184 @@
//
// Copyright (c) 2019-2020 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)
//
#include "network_functions_impl.hpp"
#include <boost/asio/use_future.hpp>
using namespace boost::mysql::test;
using boost::mysql::connection_params;
using boost::mysql::error_code;
using boost::mysql::error_info;
using boost::mysql::errc;
using boost::mysql::value;
using boost::mysql::row;
using boost::mysql::owning_row;
using boost::asio::use_future;
namespace
{
template <typename Stream>
class async_future : public network_functions<Stream>
{
template <typename Callable>
static auto impl(Callable&& cb) {
using R = decltype(cb().get()); // Callable returns a future<R>
std::future<R> fut = cb();
try
{
// error_info is not available here, so we skip validation
return network_result<R>(
error_code(),
fut.get()
);
}
catch (const boost::system::system_error& err)
{
// error_info is not available here, so we skip validation
return network_result<R>(err.code());
}
}
template <typename Callable>
static network_result<no_result> impl_no_result(Callable&& cb) {
std::future<void> fut = cb();
try
{
// error_info is not available here, so we skip validation
fut.get();
return network_result<no_result>(error_code());
}
catch (const boost::system::system_error& err)
{
// error_info is not available here, so we skip validation
return network_result<no_result>(err.code());
}
}
public:
using connection_type = typename network_functions<Stream>::connection_type;
using prepared_statement_type = typename network_functions<Stream>::prepared_statement_type;
using resultset_type = typename network_functions<Stream>::resultset_type;
const char* name() const override { return "async_future_noerrinfo"; }
network_result<no_result> connect(
connection_type& conn,
const typename Stream::endpoint_type& ep,
const connection_params& params
) override
{
return impl_no_result([&] {
return conn.async_connect(ep, params, use_future);
});
}
network_result<no_result> handshake(
connection_type& conn,
const connection_params& params
) override
{
return impl_no_result([&] {
return conn.async_handshake(params, use_future);
});
}
network_result<resultset_type> query(
connection_type& conn,
std::string_view query
) override
{
return impl([&] {
return conn.async_query(query, use_future);
});
}
network_result<prepared_statement_type> prepare_statement(
connection_type& conn,
std::string_view statement
) override
{
return impl([&]{
return conn.async_prepare_statement(statement, use_future);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
value_list_it params_first,
value_list_it params_last
) override
{
return impl([&]{
return stmt.async_execute(params_first, params_last, use_future);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
const std::vector<value>& values
) override
{
return impl([&] {
return stmt.async_execute(values, use_future);
});
}
network_result<no_result> close_statement(
prepared_statement_type& stmt
) override
{
return impl_no_result([&] {
return stmt.async_close(use_future);
});
}
network_result<const row*> fetch_one(
resultset_type& r
) override
{
return impl([&] {
return r.async_fetch_one(use_future);
});
}
network_result<std::vector<owning_row>> fetch_many(
resultset_type& r,
std::size_t count
) override
{
return impl([&] {
return r.async_fetch_many(count, use_future);
});
}
network_result<std::vector<owning_row>> fetch_all(
resultset_type& r
) override
{
return impl([&] {
return r.async_fetch_all(use_future);
});
}
network_result<no_result> quit(
connection_type& conn
) override
{
return impl_no_result([&] {
return conn.async_quit(use_future);
});
}
network_result<no_result> close(
connection_type& conn
) override
{
return impl_no_result([&] {
return conn.async_close(use_future);
});
}
};
} // anon namespace
// Visible stuff
template <typename Stream>
network_functions<Stream>* boost::mysql::test::async_future_functions()
{
static async_future<Stream> res;
return &res;
}
BOOST_MYSQL_INSTANTIATE_NETWORK_FUNCTIONS(async_future_functions)

View File

@ -0,0 +1,36 @@
//
// Copyright (c) 2019-2020 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)
//
#include "network_functions_impl.hpp"
#include <boost/asio/use_awaitable.hpp> // for BOOST_ASIO_HAS_CO_AWAIT
template <typename Stream>
std::vector<boost::mysql::test::network_functions<Stream>*>
boost::mysql::test::make_all_network_functions()
{
return {
sync_errc_functions<Stream>(),
sync_exc_functions<Stream>(),
async_callback_errinfo_functions<Stream>(),
async_callback_noerrinfo_functions<Stream>(),
async_coroutine_errinfo_functions<Stream>(),
async_coroutine_noerrinfo_functions<Stream>(),
async_future_functions<Stream>(),
#ifdef BOOST_ASIO_HAS_CO_AWAIT
async_coroutinecpp20_errinfo_functions<Stream>(),
async_coroutinecpp20_noerrinfo_functions<Stream>()
#endif
};
}
template std::vector<boost::mysql::test::network_functions<boost::asio::ip::tcp::socket>*>
boost::mysql::test::make_all_network_functions();
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
template std::vector<boost::mysql::test::network_functions<boost::asio::local::stream_protocol::socket>*>
boost::mysql::test::make_all_network_functions();
#endif

View File

@ -0,0 +1,48 @@
//
// Copyright (c) 2019-2020 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 TEST_INTEGRATION_NETWORK_FUNCTIONS_NETWORK_FUNCTIONS_IMPL_HPP_
#define TEST_INTEGRATION_NETWORK_FUNCTIONS_NETWORK_FUNCTIONS_IMPL_HPP_
#include "../network_functions.hpp"
#include <boost/asio/local/stream_protocol.hpp>
namespace boost {
namespace mysql {
namespace test {
template <typename Stream> network_functions<Stream>* sync_errc_functions();
template <typename Stream> network_functions<Stream>* sync_exc_functions();
template <typename Stream> network_functions<Stream>* async_callback_errinfo_functions();
template <typename Stream> network_functions<Stream>* async_callback_noerrinfo_functions();
template <typename Stream> network_functions<Stream>* async_coroutine_errinfo_functions();
template <typename Stream> network_functions<Stream>* async_coroutine_noerrinfo_functions();
template <typename Stream> network_functions<Stream>* async_coroutinecpp20_errinfo_functions();
template <typename Stream> network_functions<Stream>* async_coroutinecpp20_noerrinfo_functions();
template <typename Stream> network_functions<Stream>* async_future_functions();
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
#define BOOST_MYSQL_INSTANTIATE_NETWORK_FUNCTIONS_UNIX(fun) \
template boost::mysql::test::network_functions<boost::asio::local::stream_protocol::socket>* \
boost::mysql::test::fun<boost::asio::local::stream_protocol::socket>();
#else
#define BOOST_MYSQL_INSTANTIATE_NETWORK_FUNCTIONS_UNIX(fun)
#endif
#define BOOST_MYSQL_INSTANTIATE_NETWORK_FUNCTIONS(fun) \
template boost::mysql::test::network_functions<boost::asio::ip::tcp::socket>* \
boost::mysql::test::fun<boost::asio::ip::tcp::socket>(); \
BOOST_MYSQL_INSTANTIATE_NETWORK_FUNCTIONS_UNIX(fun)
}
}
}
#endif

View File

@ -0,0 +1,313 @@
//
// Copyright (c) 2019-2020 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)
//
// Both sync_errc and sync_exc are implemented here, as the resulting
// template instantiations are almost identical
#include "network_functions_impl.hpp"
using namespace boost::mysql::test;
using boost::mysql::connection_params;
using boost::mysql::error_code;
using boost::mysql::error_info;
using boost::mysql::errc;
using boost::mysql::value;
using boost::mysql::row;
using boost::mysql::owning_row;
namespace
{
template <typename Stream>
class sync_errc : public network_functions<Stream>
{
template <typename Callable>
static auto impl(Callable&& cb) {
using R = decltype(cb(std::declval<error_code&>(), std::declval<error_info&>()));
network_result<R> res (
boost::mysql::detail::make_error_code(errc::no),
error_info("error_info not cleared properly")
);
res.value = cb(res.err, *res.info);
return res;
}
public:
using connection_type = typename network_functions<Stream>::connection_type;
using prepared_statement_type = typename network_functions<Stream>::prepared_statement_type;
using resultset_type = typename network_functions<Stream>::resultset_type;
const char* name() const override { return "sync_errc"; }
network_result<no_result> connect(
connection_type& conn,
const typename Stream::endpoint_type& ep,
const connection_params& params
) override
{
return impl([&](error_code& code, error_info& info) {
conn.connect(ep, params, code, info);
return no_result();
});
}
network_result<no_result> handshake(
connection_type& conn,
const connection_params& params
) override
{
return impl([&](error_code& code, error_info& info) {
conn.handshake(params, code, info);
return no_result();
});
}
network_result<resultset_type> query(
connection_type& conn,
std::string_view query
) override
{
return impl([&](error_code& code, error_info& info) {
return conn.query(query, code, info);
});
}
network_result<prepared_statement_type> prepare_statement(
connection_type& conn,
std::string_view statement
) override
{
return impl([&conn, statement](error_code& err, error_info& info) {
return conn.prepare_statement(statement, err, info);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
value_list_it params_first,
value_list_it params_last
) override
{
return impl([=, &stmt](error_code& err, error_info& info) {
return stmt.execute(params_first, params_last, err, info);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
const std::vector<value>& values
) override
{
return impl([&stmt, &values](error_code& err, error_info& info) {
return stmt.execute(values, err, info);
});
}
network_result<no_result> close_statement(
prepared_statement_type& stmt
) override
{
return impl([&](error_code& code, error_info& info) {
stmt.close(code, info);
return no_result();
});
}
network_result<const row*> fetch_one(
resultset_type& r
) override
{
return impl([&](error_code& code, error_info& info) {
return r.fetch_one(code, info);
});
}
network_result<std::vector<owning_row>> fetch_many(
resultset_type& r,
std::size_t count
) override
{
return impl([&](error_code& code, error_info& info) {
return r.fetch_many(count, code, info);
});
}
network_result<std::vector<owning_row>> fetch_all(
resultset_type& r
) override
{
return impl([&](error_code& code, error_info& info) {
return r.fetch_all(code, info);
});
}
network_result<no_result> quit(
connection_type& conn
) override
{
return impl([&](error_code& code, error_info& info) {
conn.quit(code, info);
return no_result();
});
}
network_result<no_result> close(
connection_type& conn
) override
{
return impl([&](error_code& code, error_info& info) {
conn.close(code, info);
return no_result();
});
}
static sync_errc obj;
};
template <typename Stream>
class sync_exc : public network_functions<Stream>
{
template <typename Callable>
static auto impl(Callable&& cb) {
using R = decltype(cb());
network_result<R> res;
try
{
res.value = cb();
}
catch (const boost::system::system_error& err)
{
res.err = err.code();
res.info = error_info(err.what());
}
return res;
}
public:
using connection_type = typename network_functions<Stream>::connection_type;
using prepared_statement_type = typename network_functions<Stream>::prepared_statement_type;
using resultset_type = typename network_functions<Stream>::resultset_type;
const char* name() const override { return "sync_exc"; }
network_result<no_result> connect(
connection_type& conn,
const typename Stream::endpoint_type& ep,
const connection_params& params
) override
{
return impl([&] {
conn.connect(ep, params);
return no_result();
});
}
network_result<no_result> handshake(
connection_type& conn,
const connection_params& params
) override
{
return impl([&] {
conn.handshake(params);
return no_result();
});
}
network_result<resultset_type> query(
connection_type& conn,
std::string_view query
) override
{
return impl([&] {
return conn.query(query);
});
}
network_result<prepared_statement_type> prepare_statement(
connection_type& conn,
std::string_view statement
) override
{
return impl([&conn, statement] {
return conn.prepare_statement(statement);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
value_list_it params_first,
value_list_it params_last
) override
{
return impl([&]{
return stmt.execute(params_first, params_last);
});
}
network_result<resultset_type> execute_statement(
prepared_statement_type& stmt,
const std::vector<value>& values
) override
{
return impl([&stmt, &values] {
return stmt.execute(values);
});
}
network_result<no_result> close_statement(
prepared_statement_type& stmt
) override
{
return impl([&] {
stmt.close();
return no_result();
});
}
network_result<const row*> fetch_one(
resultset_type& r
) override
{
return impl([&] {
return r.fetch_one();
});
}
network_result<std::vector<owning_row>> fetch_many(
resultset_type& r,
std::size_t count
) override
{
return impl([&] {
return r.fetch_many(count);
});
}
network_result<std::vector<owning_row>> fetch_all(
resultset_type& r
) override
{
return impl([&] {
return r.fetch_all();
});
}
network_result<no_result> quit(
connection_type& conn
) override
{
return impl([&] {
conn.quit();
return no_result();
});
}
network_result<no_result> close(
connection_type& conn
) override
{
return impl([&] {
conn.close();
return no_result();
});
}
};
} // anon namespace
// Visible stuff
template <typename Stream>
network_functions<Stream>* boost::mysql::test::sync_errc_functions()
{
static sync_errc<Stream> res;
return &res;
}
template <typename Stream>
network_functions<Stream>* boost::mysql::test::sync_exc_functions()
{
static sync_exc<Stream> res;
return &res;
}
BOOST_MYSQL_INSTANTIATE_NETWORK_FUNCTIONS(sync_errc_functions)
BOOST_MYSQL_INSTANTIATE_NETWORK_FUNCTIONS(sync_exc_functions)