mirror of
https://github.com/boostorg/mysql.git
synced 2025-05-12 14:11:41 +00:00
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:
parent
76f8123e37
commit
3c420b8683
@ -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:
|
||||
|
12
.travis.yml
12
.travis.yml
@ -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
|
||||
|
@ -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.
|
||||
|
1
TODO.txt
1
TODO.txt
@ -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?
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -49,6 +49,7 @@ set(MYSQL_EXAMPLES
|
||||
query_sync
|
||||
query_async_callbacks
|
||||
query_async_coroutines
|
||||
query_async_coroutinescpp20
|
||||
query_async_futures
|
||||
metadata
|
||||
prepared_statements
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
*
|
||||
|
214
example/query_async_coroutinescpp20.cpp
Normal file
214
example/query_async_coroutinescpp20.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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_ */
|
@ -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_ */
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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_ */
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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>
|
||||
)
|
||||
|
@ -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>
|
||||
)
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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_ */
|
||||
|
@ -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)
|
||||
);
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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),
|
||||
|
@ -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(),
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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_),
|
||||
|
@ -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
|
@ -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
|
||||
|
232
test/integration/network_functions/async_callback.cpp
Normal file
232
test/integration/network_functions/async_callback.cpp
Normal 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)
|
||||
|
196
test/integration/network_functions/async_coroutine.cpp
Normal file
196
test/integration/network_functions/async_coroutine.cpp
Normal 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)
|
241
test/integration/network_functions/async_coroutinecpp20.cpp
Normal file
241
test/integration/network_functions/async_coroutinecpp20.cpp
Normal 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
|
184
test/integration/network_functions/async_future.cpp
Normal file
184
test/integration/network_functions/async_future.cpp
Normal 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)
|
@ -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
|
@ -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
|
313
test/integration/network_functions/sync.cpp
Normal file
313
test/integration/network_functions/sync.cpp
Normal 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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user