mirror of
https://github.com/boostorg/mysql.git
synced 2025-05-12 14:11:41 +00:00
as_netresult completion token for tests
Restructured handshake integration tests Removed er_connection infrastructure Reworked network_result Greatly simplified integration tests
This commit is contained in:
parent
0060494170
commit
a8c992dd95
@ -381,7 +381,7 @@ public:
|
||||
BOOST_MYSQL_DECL bool ssl_active() const;
|
||||
BOOST_MYSQL_DECL bool backslash_escapes() const;
|
||||
BOOST_MYSQL_DECL system::result<character_set> current_character_set() const;
|
||||
BOOST_MYSQL_DECL diagnostics& shared_diag(); // TODO: get rid of this
|
||||
BOOST_MYSQL_DECL diagnostics& shared_diag();
|
||||
|
||||
engine& get_engine()
|
||||
{
|
||||
|
@ -1,124 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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_TEST_COMMON_INCLUDE_TEST_COMMON_NETFUN_HELPERS_HPP
|
||||
#define BOOST_MYSQL_TEST_COMMON_INCLUDE_TEST_COMMON_NETFUN_HELPERS_HPP
|
||||
|
||||
#include <boost/mysql/common_server_errc.hpp>
|
||||
#include <boost/mysql/error_code.hpp>
|
||||
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "test_common/create_diagnostics.hpp"
|
||||
#include "test_common/network_result.hpp"
|
||||
#include "test_common/tracker_executor.hpp"
|
||||
|
||||
// Helper functions and classes to implement netmakers
|
||||
// (the insfrastructure to run sync and async code as parameterized tests)
|
||||
// for both unit and integ tests
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
// Completion callback that saves result into a network_result
|
||||
// and binds it to an executor
|
||||
template <class R>
|
||||
class as_network_result
|
||||
{
|
||||
network_result<R>* netresult_;
|
||||
tracker_executor_result ex_;
|
||||
|
||||
void check_executor() const
|
||||
{
|
||||
BOOST_TEST(!is_initiation_function());
|
||||
BOOST_TEST(current_executor_id() == ex_.executor_id);
|
||||
}
|
||||
|
||||
public:
|
||||
as_network_result(network_result<R>& netresult, asio::any_io_executor exec)
|
||||
: netresult_(&netresult), ex_(create_tracker_executor(std::move(exec)))
|
||||
{
|
||||
}
|
||||
|
||||
using executor_type = asio::any_io_executor;
|
||||
asio::any_io_executor get_executor() const { return ex_.ex; }
|
||||
|
||||
void operator()(error_code ec) const
|
||||
{
|
||||
check_executor();
|
||||
netresult_->err = ec;
|
||||
}
|
||||
|
||||
template <class Arg>
|
||||
void operator()(error_code ec, Arg&& arg) const
|
||||
{
|
||||
check_executor();
|
||||
netresult_->err = ec;
|
||||
netresult_->value = std::forward<Arg>(arg);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Fn, class... Args>
|
||||
auto invoke_polyfill(Fn fn, Args&&... args) ->
|
||||
typename std::enable_if<
|
||||
std::is_function<typename std::remove_pointer<Fn>::type>::value,
|
||||
decltype(fn(std::forward<Args>(args)...))>::type
|
||||
{
|
||||
return fn(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Pmem, class Obj, class... Args>
|
||||
auto invoke_polyfill(Pmem fn, Obj& obj, Args&&... args) ->
|
||||
typename std::enable_if<
|
||||
std::is_member_function_pointer<Pmem>::value,
|
||||
decltype((obj.*fn)(std::forward<Args>(args)...))>::type
|
||||
{
|
||||
return (obj.*fn)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class T, class... InvokeArgs>
|
||||
void invoke_and_assign(network_result<T>& output, InvokeArgs&&... args)
|
||||
{
|
||||
output.value = invoke_polyfill(std::forward<InvokeArgs>(args)...);
|
||||
}
|
||||
|
||||
template <class... InvokeArgs>
|
||||
void invoke_and_assign(network_result<void>&, InvokeArgs&&... args)
|
||||
{
|
||||
invoke_polyfill(std::forward<InvokeArgs>(args)...);
|
||||
}
|
||||
|
||||
template <class R>
|
||||
network_result<R> create_initial_netresult(bool with_diag = true)
|
||||
{
|
||||
network_result<R> res(boost::mysql::make_error_code(boost::mysql::common_server_errc::er_no));
|
||||
if (with_diag)
|
||||
res.diag = create_server_diag("diagnostics not cleared properly");
|
||||
return res;
|
||||
}
|
||||
|
||||
inline boost::asio::io_context& get_context(boost::asio::any_io_executor ex) noexcept
|
||||
{
|
||||
return static_cast<boost::asio::io_context&>(ex.context());
|
||||
}
|
||||
|
||||
inline void run_until_completion(boost::asio::any_io_executor ex)
|
||||
{
|
||||
auto& ctx = get_context(ex);
|
||||
ctx.restart();
|
||||
ctx.run();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -8,18 +8,52 @@
|
||||
#ifndef BOOST_MYSQL_TEST_COMMON_INCLUDE_TEST_COMMON_NETFUN_MAKER_HPP
|
||||
#define BOOST_MYSQL_TEST_COMMON_INCLUDE_TEST_COMMON_NETFUN_MAKER_HPP
|
||||
|
||||
#include <boost/mysql/common_server_errc.hpp>
|
||||
#include <boost/mysql/diagnostics.hpp>
|
||||
#include <boost/mysql/error_code.hpp>
|
||||
#include <boost/mysql/error_with_diagnostics.hpp>
|
||||
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/system/system_error.hpp>
|
||||
|
||||
#include "test_common/netfun_helpers.hpp"
|
||||
#include "test_common/tracker_executor.hpp"
|
||||
#include <type_traits>
|
||||
|
||||
#include "test_common/create_diagnostics.hpp"
|
||||
#include "test_common/network_result.hpp"
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
template <class Fn, class... Args>
|
||||
auto invoke_polyfill(Fn fn, Args&&... args) ->
|
||||
typename std::enable_if<
|
||||
std::is_function<typename std::remove_pointer<Fn>::type>::value,
|
||||
decltype(fn(std::forward<Args>(args)...))>::type
|
||||
{
|
||||
return fn(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Pmem, class Obj, class... Args>
|
||||
auto invoke_polyfill(Pmem fn, Obj& obj, Args&&... args) ->
|
||||
typename std::enable_if<
|
||||
std::is_member_function_pointer<Pmem>::value,
|
||||
decltype((obj.*fn)(std::forward<Args>(args)...))>::type
|
||||
{
|
||||
return (obj.*fn)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class T, class... InvokeArgs>
|
||||
void invoke_and_assign(network_result<T>& output, InvokeArgs&&... args)
|
||||
{
|
||||
output.value = invoke_polyfill(std::forward<InvokeArgs>(args)...);
|
||||
}
|
||||
|
||||
template <class... InvokeArgs>
|
||||
void invoke_and_assign(network_result<void>&, InvokeArgs&&... args)
|
||||
{
|
||||
invoke_polyfill(std::forward<InvokeArgs>(args)...);
|
||||
}
|
||||
|
||||
template <class R, class IOObject, class... Args>
|
||||
struct netfun_maker_impl
|
||||
{
|
||||
@ -29,8 +63,21 @@ struct netfun_maker_impl
|
||||
static signature sync_errc(Pfn fn)
|
||||
{
|
||||
return [fn](IOObject& obj, Args... args) {
|
||||
auto res = create_initial_netresult<R>();
|
||||
invoke_and_assign(res, fn, obj, std::forward<Args>(args)..., res.err, *res.diag);
|
||||
network_result<R> res{
|
||||
common_server_errc::er_no,
|
||||
create_server_diag("diagnostics not cleared properly")
|
||||
};
|
||||
invoke_and_assign(res, fn, obj, std::forward<Args>(args)..., res.err, res.diag);
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
template <class Pfn>
|
||||
static signature sync_errc_nodiag(Pfn fn)
|
||||
{
|
||||
return [fn](IOObject& obj, Args... args) {
|
||||
network_result<R> res{common_server_errc::er_no, create_server_diag("<diagnostics unavailable>")};
|
||||
invoke_and_assign(res, fn, obj, std::forward<Args>(args)..., res.err);
|
||||
return res;
|
||||
};
|
||||
}
|
||||
@ -58,118 +105,44 @@ struct netfun_maker_impl
|
||||
}
|
||||
|
||||
template <class Pfn>
|
||||
static signature sync_errc_noerrinfo(Pfn fn)
|
||||
static signature async_diag(Pfn fn)
|
||||
{
|
||||
return [fn](IOObject& obj, Args... args) {
|
||||
auto res = create_initial_netresult<R>(false);
|
||||
invoke_and_assign(res, fn, obj, std::forward<Args>(args)..., res.err);
|
||||
return res;
|
||||
diagnostics diag; // checks for clearing diag are performed by as_netresult
|
||||
return invoke_polyfill(fn, obj, std::forward<Args>(args)..., diag, as_netresult).run();
|
||||
};
|
||||
}
|
||||
|
||||
template <class Pfn>
|
||||
static signature async_errinfo(Pfn fn)
|
||||
static signature async_nodiag(Pfn fn)
|
||||
{
|
||||
return [fn](IOObject& obj, Args... args) {
|
||||
// Get the object's I/O executor
|
||||
auto ex = obj.get_executor();
|
||||
|
||||
// Create the initial result
|
||||
auto res = create_initial_netresult<R>();
|
||||
|
||||
{
|
||||
// Mark ourselves as initiating
|
||||
initiation_guard guard;
|
||||
|
||||
// Invoke the initiation function
|
||||
invoke_polyfill(
|
||||
fn,
|
||||
obj,
|
||||
std::forward<Args>(args)...,
|
||||
*res.diag,
|
||||
as_network_result<R>(res, ex)
|
||||
);
|
||||
}
|
||||
|
||||
// Run
|
||||
run_until_completion(ex);
|
||||
|
||||
// Done
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
template <class Pfn>
|
||||
static signature async_noerrinfo(Pfn fn)
|
||||
{
|
||||
return [fn](IOObject& obj, Args... args) {
|
||||
// Get the object's I/O executor
|
||||
auto ex = obj.get_executor();
|
||||
|
||||
// Create the initial result
|
||||
auto res = create_initial_netresult<R>(false);
|
||||
|
||||
{
|
||||
// Mark ourselves as initiating
|
||||
initiation_guard guard;
|
||||
|
||||
// Invoke the initiation function
|
||||
invoke_polyfill(fn, obj, std::forward<Args>(args)..., as_network_result<R>(res, ex));
|
||||
}
|
||||
|
||||
// Run until completion
|
||||
run_until_completion(ex);
|
||||
|
||||
// Done
|
||||
return res;
|
||||
return invoke_polyfill(fn, obj, std::forward<Args>(args)..., as_netresult).run();
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
template <class R, class Obj, class... Args>
|
||||
class netfun_maker_mem
|
||||
class netfun_maker
|
||||
{
|
||||
using impl = netfun_maker_impl<R, Obj&, Args...>;
|
||||
|
||||
public:
|
||||
using signature = std::function<network_result<R>(Obj&, Args...)>;
|
||||
using sig_sync_errc = R (Obj::*)(Args..., error_code&, diagnostics&);
|
||||
using sig_sync_errc_noerrinfo = R (Obj::*)(Args..., error_code&);
|
||||
using sig_sync_errc_nodiag = R (Obj::*)(Args..., error_code&);
|
||||
using sig_sync_errc_nodiag_old =
|
||||
error_code (Obj::*)(Args..., error_code&); // support old Asio signatures
|
||||
using sig_sync_exc = R (Obj::*)(Args...);
|
||||
using sig_async_errinfo = void (Obj::*)(Args..., diagnostics&, as_network_result<R>&&);
|
||||
using sig_async_noerrinfo = void (Obj::*)(Args..., as_network_result<R>&&);
|
||||
using sig_async_diag = runnable_network_result<R> (Obj::*)(Args..., diagnostics&, const as_netresult_t&);
|
||||
using sig_async_nodiag = runnable_network_result<R> (Obj::*)(Args..., const as_netresult_t&);
|
||||
|
||||
static signature sync_errc(sig_sync_errc pfn) { return impl::sync_errc(pfn); }
|
||||
static signature sync_errc_noerrinfo(sig_sync_errc_noerrinfo pfn)
|
||||
{
|
||||
return impl::sync_errc_noerrinfo(pfn);
|
||||
}
|
||||
static signature sync_errc_nodiag(sig_sync_errc_nodiag pfn) { return impl::sync_errc_nodiag(pfn); }
|
||||
static signature sync_errc_nodiag(sig_sync_errc_nodiag_old pfn) { return impl::sync_errc_nodiag(pfn); }
|
||||
static signature sync_exc(sig_sync_exc pfn) { return impl::sync_exc(pfn); }
|
||||
static signature async_errinfo(sig_async_errinfo pfn) { return impl::async_errinfo(pfn); }
|
||||
static signature async_noerrinfo(sig_async_noerrinfo pfn) { return impl::async_noerrinfo(pfn); }
|
||||
};
|
||||
|
||||
template <class R, class... Args>
|
||||
class netfun_maker_fn
|
||||
{
|
||||
using impl = netfun_maker_impl<R, Args...>;
|
||||
|
||||
public:
|
||||
using signature = std::function<network_result<R>(Args...)>;
|
||||
using sig_sync_errc = R (*)(Args..., error_code&, diagnostics&);
|
||||
using sig_sync_errc_noerrinfo = R (*)(Args..., error_code&);
|
||||
using sig_sync_exc = R (*)(Args...);
|
||||
using sig_async_errinfo = void (*)(Args..., diagnostics&, as_network_result<R>&&);
|
||||
using sig_async_noerrinfo = void (*)(Args..., as_network_result<R>&&);
|
||||
|
||||
static signature sync_errc(sig_sync_errc pfn) { return impl::sync_errc(pfn); }
|
||||
static signature sync_errc_noerrinfo(sig_sync_errc_noerrinfo pfn)
|
||||
{
|
||||
return impl::sync_errc_noerrinfo(pfn);
|
||||
}
|
||||
static signature sync_exc(sig_sync_exc pfn) { return impl::sync_exc(pfn); }
|
||||
static signature async_errinfo(sig_async_errinfo pfn) { return impl::async_errinfo(pfn); }
|
||||
static signature async_noerrinfo(sig_async_noerrinfo pfn) { return impl::async_noerrinfo(pfn); }
|
||||
static signature async_diag(sig_async_diag pfn) { return impl::async_diag(pfn); }
|
||||
static signature async_nodiag(sig_async_nodiag pfn) { return impl::async_nodiag(pfn); }
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
|
@ -8,15 +8,28 @@
|
||||
#ifndef BOOST_MYSQL_TEST_COMMON_INCLUDE_TEST_COMMON_NETWORK_RESULT_HPP
|
||||
#define BOOST_MYSQL_TEST_COMMON_INCLUDE_TEST_COMMON_NETWORK_RESULT_HPP
|
||||
|
||||
#include <boost/mysql/client_errc.hpp>
|
||||
#include <boost/mysql/common_server_errc.hpp>
|
||||
#include <boost/mysql/diagnostics.hpp>
|
||||
#include <boost/mysql/error_code.hpp>
|
||||
#include <boost/mysql/string_view.hpp>
|
||||
|
||||
#include <boost/optional/optional.hpp>
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/cancellation_signal.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/assert/source_location.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "test_common/create_diagnostics.hpp"
|
||||
#include "test_common/printing.hpp"
|
||||
#include "test_common/source_location.hpp"
|
||||
#include "test_common/tracker_executor.hpp"
|
||||
#include "test_common/validate_string_contains.hpp"
|
||||
|
||||
namespace boost {
|
||||
@ -27,111 +40,327 @@ struct no_result
|
||||
{
|
||||
};
|
||||
|
||||
struct network_result_base
|
||||
// network_result: system::result-like type with helper functions
|
||||
// for tests
|
||||
struct BOOST_ATTRIBUTE_NODISCARD network_result_base
|
||||
{
|
||||
error_code err;
|
||||
boost::optional<diagnostics> diag; // some network_function's don't provide this
|
||||
diagnostics diag;
|
||||
|
||||
network_result_base() = default;
|
||||
network_result_base(error_code ec) : err(ec) {}
|
||||
network_result_base(error_code ec, diagnostics&& diag) : err(ec), diag(std::move(diag)) {}
|
||||
|
||||
void validate_no_error() const
|
||||
void validate_no_error(source_location loc = BOOST_MYSQL_CURRENT_LOCATION) const
|
||||
{
|
||||
BOOST_TEST_CONTEXT("diagnostics= " << server_diag() << ", error_code=" << err.message())
|
||||
validate_error(error_code(), diagnostics(), loc);
|
||||
}
|
||||
|
||||
// Use for functions without a diagnostics& parameter
|
||||
void validate_no_error_nodiag(source_location loc = BOOST_MYSQL_CURRENT_LOCATION) const
|
||||
{
|
||||
validate_error(error_code(), create_server_diag("<diagnostics unavailable>"), loc);
|
||||
}
|
||||
|
||||
void validate_error(
|
||||
error_code expected_err,
|
||||
const diagnostics& expected_diag = {},
|
||||
source_location loc = BOOST_MYSQL_CURRENT_LOCATION
|
||||
) const
|
||||
{
|
||||
BOOST_TEST_CONTEXT("Called from " << loc)
|
||||
{
|
||||
BOOST_TEST_REQUIRE(err == error_code());
|
||||
if (diag)
|
||||
{
|
||||
BOOST_TEST(diag->server_message() == "");
|
||||
}
|
||||
BOOST_TEST(diag == expected_diag);
|
||||
BOOST_TEST_REQUIRE(err == expected_err);
|
||||
}
|
||||
}
|
||||
|
||||
void validate_error(
|
||||
common_server_errc expected_err,
|
||||
string_view expected_msg = {},
|
||||
source_location loc = BOOST_MYSQL_CURRENT_LOCATION
|
||||
)
|
||||
{
|
||||
validate_error(expected_err, create_server_diag(expected_msg), loc);
|
||||
}
|
||||
|
||||
void validate_error(
|
||||
client_errc expected_err,
|
||||
string_view expected_msg = {},
|
||||
source_location loc = BOOST_MYSQL_CURRENT_LOCATION
|
||||
)
|
||||
{
|
||||
validate_error(expected_err, create_client_diag(expected_msg), loc);
|
||||
}
|
||||
|
||||
// Use when the exact message isn't known, but some of its contents are
|
||||
void validate_error_contains(
|
||||
error_code expected_err,
|
||||
const std::vector<std::string>& pieces,
|
||||
source_location loc = BOOST_MYSQL_CURRENT_LOCATION
|
||||
)
|
||||
{
|
||||
BOOST_TEST_CONTEXT("Called from " << loc)
|
||||
{
|
||||
validate_string_contains(diag.server_message(), pieces);
|
||||
BOOST_TEST_REQUIRE(err == expected_err);
|
||||
}
|
||||
}
|
||||
|
||||
// Use when you don't care or can't determine the kind of error
|
||||
void validate_any_error(const std::vector<std::string>& expected_msg = {}) const
|
||||
void validate_any_error(source_location loc = BOOST_MYSQL_CURRENT_LOCATION) const
|
||||
{
|
||||
BOOST_TEST_CONTEXT("diagnostics= " << server_diag() << ", error_code=" << err.message())
|
||||
{
|
||||
BOOST_TEST_REQUIRE(err != error_code());
|
||||
if (diag)
|
||||
{
|
||||
validate_string_contains(diag->server_message(), expected_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void validate_error(error_code expected_errc, const std::vector<std::string>& expected_msg) const
|
||||
{
|
||||
BOOST_TEST_CONTEXT("diagnostics= " << server_diag() << ", error_code=" << err.message())
|
||||
{
|
||||
BOOST_TEST_REQUIRE(err == expected_errc);
|
||||
if (diag)
|
||||
{
|
||||
validate_string_contains(diag->server_message(), expected_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void validate_error_exact(error_code expected_err, const char* expected_msg = "")
|
||||
{
|
||||
BOOST_TEST_CONTEXT("diagnostics= " << server_diag() << ", error_code=" << err.message())
|
||||
{
|
||||
BOOST_TEST_REQUIRE(err == expected_err);
|
||||
if (diag)
|
||||
{
|
||||
BOOST_TEST(diag->server_message() == expected_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void validate_error_exact_client(error_code expected_err, const char* expected_msg = "")
|
||||
{
|
||||
BOOST_TEST_CONTEXT("diagnostics= " << client_diag() << ", error_code=" << err.message())
|
||||
{
|
||||
BOOST_TEST_REQUIRE(err == expected_err);
|
||||
if (diag)
|
||||
{
|
||||
BOOST_TEST(diag->client_message() == expected_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
string_view server_diag() const noexcept
|
||||
{
|
||||
return diag ? diag->server_message() : string_view("<unavailable>");
|
||||
}
|
||||
string_view client_diag() const noexcept
|
||||
{
|
||||
return diag ? diag->client_message() : string_view("<unavailable>");
|
||||
BOOST_TEST_CONTEXT("Called from " << loc) { BOOST_TEST_REQUIRE(err != error_code()); }
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct network_result : network_result_base
|
||||
template <class R>
|
||||
struct BOOST_ATTRIBUTE_NODISCARD network_result : network_result_base
|
||||
{
|
||||
using value_type = typename std::conditional<std::is_same<T, void>::value, no_result, T>::type;
|
||||
using value_type = typename std::conditional<std::is_same<R, void>::value, no_result, R>::type;
|
||||
value_type value;
|
||||
|
||||
network_result() = default;
|
||||
network_result(error_code ec, diagnostics&& info, value_type&& value = {})
|
||||
: network_result_base(ec, std::move(info)), value(std::move(value))
|
||||
{
|
||||
}
|
||||
network_result(error_code ec, value_type&& value = {}) : network_result_base(ec), value(std::move(value))
|
||||
network_result(error_code ec, diagnostics diag, value_type value = {})
|
||||
: network_result_base{ec, std::move(diag)}, value(std::move(value))
|
||||
{
|
||||
}
|
||||
|
||||
const value_type& get() const
|
||||
BOOST_ATTRIBUTE_NODISCARD
|
||||
value_type get(source_location loc = BOOST_MYSQL_CURRENT_LOCATION) const
|
||||
{
|
||||
validate_no_error();
|
||||
validate_no_error(loc);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
// Wraps a network_result and an executor. The result of as_netresult_t.
|
||||
template <class R>
|
||||
struct BOOST_ATTRIBUTE_NODISCARD runnable_network_result
|
||||
{
|
||||
struct impl_t
|
||||
{
|
||||
network_result<R> netres{
|
||||
common_server_errc::er_no,
|
||||
create_server_diag("network_result_v2 - diagnostics not cleared")
|
||||
};
|
||||
};
|
||||
|
||||
std::unique_ptr<impl_t> impl;
|
||||
|
||||
runnable_network_result() : impl(new impl_t) {}
|
||||
|
||||
network_result<R> run() &&
|
||||
{
|
||||
run_global_context();
|
||||
return std::move(impl->netres);
|
||||
}
|
||||
|
||||
void validate_no_error(source_location loc = BOOST_MYSQL_CURRENT_LOCATION) &&
|
||||
{
|
||||
std::move(*this).run().validate_no_error(loc);
|
||||
}
|
||||
|
||||
void validate_no_error_nodiag(source_location loc = BOOST_MYSQL_CURRENT_LOCATION) &&
|
||||
{
|
||||
std::move(*this).run().validate_no_error_nodiag(loc);
|
||||
}
|
||||
|
||||
void validate_error(
|
||||
error_code expected_err,
|
||||
const diagnostics& expected_diag = {},
|
||||
source_location loc = BOOST_MYSQL_CURRENT_LOCATION
|
||||
) &&
|
||||
{
|
||||
std::move(*this).run().validate_error(expected_err, expected_diag, loc);
|
||||
}
|
||||
|
||||
void validate_error(
|
||||
common_server_errc expected_err,
|
||||
string_view expected_msg = {},
|
||||
source_location loc = BOOST_MYSQL_CURRENT_LOCATION
|
||||
) &&
|
||||
{
|
||||
std::move(*this).run().validate_error(expected_err, expected_msg, loc);
|
||||
}
|
||||
|
||||
void validate_error(
|
||||
client_errc expected_err,
|
||||
string_view expected_msg = {},
|
||||
source_location loc = BOOST_MYSQL_CURRENT_LOCATION
|
||||
) &&
|
||||
{
|
||||
std::move(*this).run().validate_error(expected_err, expected_msg, loc);
|
||||
}
|
||||
|
||||
// Use when the exact message isn't known, but some of its contents are
|
||||
void validate_error_contains(
|
||||
error_code expected_err,
|
||||
const std::vector<std::string>& pieces,
|
||||
source_location loc = BOOST_MYSQL_CURRENT_LOCATION
|
||||
) &&
|
||||
{
|
||||
std::move(*this).run().validate_error_contains(expected_err, pieces, loc);
|
||||
}
|
||||
|
||||
// Use when you don't care or can't determine the kind of error
|
||||
void validate_any_error(source_location loc = BOOST_MYSQL_CURRENT_LOCATION) &&
|
||||
{
|
||||
std::move(*this).run().validate_any_error(loc);
|
||||
}
|
||||
|
||||
BOOST_ATTRIBUTE_NODISCARD
|
||||
typename network_result<R>::value_type get(source_location loc = BOOST_MYSQL_CURRENT_LOCATION) &&
|
||||
{
|
||||
return std::move(*this).run().get(loc);
|
||||
}
|
||||
};
|
||||
|
||||
struct as_netresult_t
|
||||
{
|
||||
asio::cancellation_slot slot;
|
||||
};
|
||||
|
||||
constexpr as_netresult_t as_netresult{};
|
||||
|
||||
namespace test_detail {
|
||||
|
||||
template <class Signature>
|
||||
struct as_netres_sig_to_rtype;
|
||||
|
||||
template <>
|
||||
struct as_netres_sig_to_rtype<void(error_code)>
|
||||
{
|
||||
using type = void;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct as_netres_sig_to_rtype<void(error_code, T)>
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <class R>
|
||||
class as_netres_handler
|
||||
{
|
||||
network_result<R>* target_;
|
||||
tracker_executor_result ex_;
|
||||
asio::cancellation_slot slot_;
|
||||
const diagnostics* diag_ptr;
|
||||
|
||||
void complete(error_code ec) const
|
||||
{
|
||||
// Check executor
|
||||
BOOST_TEST(!is_initiation_function());
|
||||
BOOST_TEST(current_executor_id() == ex_.executor_id);
|
||||
|
||||
// Assign error code and diagnostics
|
||||
target_->err = ec;
|
||||
if (diag_ptr)
|
||||
target_->diag = *diag_ptr;
|
||||
else
|
||||
target_->diag = create_server_diag("<diagnostics unavailable>");
|
||||
}
|
||||
|
||||
public:
|
||||
as_netres_handler(
|
||||
network_result<R>& netresult,
|
||||
const diagnostics* output_diag,
|
||||
asio::cancellation_slot slot
|
||||
)
|
||||
: target_(&netresult),
|
||||
ex_(create_tracker_executor(global_context_executor())),
|
||||
slot_(slot),
|
||||
diag_ptr(output_diag)
|
||||
{
|
||||
}
|
||||
|
||||
// Executor
|
||||
using executor_type = asio::any_io_executor;
|
||||
asio::any_io_executor get_executor() const { return ex_.ex; }
|
||||
|
||||
// Cancellation slot
|
||||
using cancellation_slot_type = asio::cancellation_slot;
|
||||
asio::cancellation_slot get_cancellation_slot() const noexcept { return slot_; }
|
||||
|
||||
void operator()(error_code ec) const { complete(ec); }
|
||||
|
||||
template <class Arg>
|
||||
void operator()(error_code ec, Arg&& arg) const
|
||||
{
|
||||
target_->value = std::forward<Arg>(arg);
|
||||
complete(ec);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace test_detail
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
namespace boost {
|
||||
namespace asio {
|
||||
|
||||
template <typename Signature>
|
||||
class async_result<mysql::test::as_netresult_t, Signature>
|
||||
{
|
||||
public:
|
||||
using R = typename mysql::test::test_detail::as_netres_sig_to_rtype<Signature>::type;
|
||||
using return_type = mysql::test::runnable_network_result<R>;
|
||||
|
||||
template <typename Initiation, typename... Args>
|
||||
static return_type initiate(Initiation&& initiation, mysql::test::as_netresult_t token, Args&&... args)
|
||||
{
|
||||
return do_initiate(std::move(initiation), token.slot, std::move(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
// initiate() is not allowed to inspect individual arguments
|
||||
template <typename Initiation, typename... Args>
|
||||
static return_type do_initiate(
|
||||
Initiation&& initiation,
|
||||
asio::cancellation_slot slot,
|
||||
mysql::diagnostics* diag,
|
||||
Args&&... args
|
||||
)
|
||||
{
|
||||
// Verify that we correctly set diagnostics in all cases
|
||||
*diag = mysql::test::create_server_diag("Diagnostics not cleared properly");
|
||||
|
||||
// Create the return type
|
||||
mysql::test::runnable_network_result<R> netres;
|
||||
|
||||
// Record that we're initiating
|
||||
mysql::test::initiation_guard guard;
|
||||
|
||||
// Actually call the initiation function
|
||||
std::move(initiation)(
|
||||
mysql::test::test_detail::as_netres_handler<R>(netres.impl->netres, diag, slot),
|
||||
diag,
|
||||
std::move(args)...
|
||||
);
|
||||
|
||||
return netres;
|
||||
}
|
||||
|
||||
// For functions without diagnostics
|
||||
template <typename Initiation, typename... Args>
|
||||
static return_type do_initiate(Initiation&& initiation, asio::cancellation_slot slot, Args&&... args)
|
||||
{
|
||||
// Create the return type
|
||||
mysql::test::runnable_network_result<R> netres;
|
||||
|
||||
// Record that we're initiating
|
||||
mysql::test::initiation_guard guard;
|
||||
|
||||
// Actually call the initiation function
|
||||
std::move(initiation)(
|
||||
mysql::test::test_detail::as_netres_handler<R>(netres.impl->netres, nullptr, slot),
|
||||
std::move(args)...
|
||||
);
|
||||
|
||||
return netres;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace asio
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
|
22
test/common/include/test_common/source_location.hpp
Normal file
22
test/common/include/test_common/source_location.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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_TEST_COMMON_INCLUDE_TEST_COMMON_SOURCE_LOCATION_HPP
|
||||
#define BOOST_MYSQL_TEST_COMMON_INCLUDE_TEST_COMMON_SOURCE_LOCATION_HPP
|
||||
|
||||
#include <boost/assert/source_location.hpp>
|
||||
|
||||
// boost::source_location triggers a bug on gcc < 8 when using PCHs
|
||||
// BOOST_CURRENT_LOCATION complains about a redefinition of __PRETTY_FUNCTION__
|
||||
// when used as default argument
|
||||
#if defined(BOOST_GCC) && BOOST_GCC < 80000
|
||||
#define BOOST_MYSQL_CURRENT_LOCATION ::boost::source_location(__FILE__, __LINE__, "")
|
||||
#else
|
||||
#define BOOST_MYSQL_CURRENT_LOCATION BOOST_CURRENT_LOCATION
|
||||
#endif
|
||||
|
||||
#endif
|
@ -47,6 +47,9 @@ struct initiation_guard
|
||||
};
|
||||
bool is_initiation_function();
|
||||
|
||||
asio::any_io_executor global_context_executor();
|
||||
void run_global_context();
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
@ -5,9 +5,11 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/asio/execution/blocking.hpp>
|
||||
#include <boost/asio/execution/relationship.hpp>
|
||||
#include <boost/asio/execution_context.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/require.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
@ -226,7 +228,7 @@ int boost::mysql::test::current_executor_id()
|
||||
|
||||
boost::mysql::test::initiation_guard::initiation_guard()
|
||||
{
|
||||
BOOST_TEST_REQUIRE(!g_is_running_initiation);
|
||||
BOOST_ASSERT(!g_is_running_initiation);
|
||||
g_is_running_initiation = true;
|
||||
}
|
||||
|
||||
@ -236,4 +238,14 @@ boost::mysql::test::initiation_guard::~initiation_guard()
|
||||
g_is_running_initiation = false;
|
||||
}
|
||||
|
||||
bool boost::mysql::test::is_initiation_function() { return g_is_running_initiation; }
|
||||
bool boost::mysql::test::is_initiation_function() { return g_is_running_initiation; }
|
||||
|
||||
static boost::asio::io_context g_ctx;
|
||||
|
||||
boost::asio::any_io_executor boost::mysql::test::global_context_executor() { return g_ctx.get_executor(); }
|
||||
|
||||
void boost::mysql::test::run_global_context()
|
||||
{
|
||||
g_ctx.restart();
|
||||
g_ctx.run();
|
||||
}
|
@ -10,17 +10,13 @@ add_executable(
|
||||
|
||||
# Utilities
|
||||
src/server_features.cpp
|
||||
src/get_endpoint.cpp
|
||||
src/metadata_validator.cpp
|
||||
src/er_network_variant.cpp
|
||||
src/sync_errc.cpp
|
||||
src/sync_exc.cpp
|
||||
src/async_callback.cpp
|
||||
src/async_coroutines.cpp
|
||||
src/async_coroutinescpp20.cpp
|
||||
src/spotchecks_helpers.cpp
|
||||
src/utils.cpp
|
||||
|
||||
# Actual tests
|
||||
test/spotchecks.cpp
|
||||
test/execution_requests.cpp
|
||||
test/crud.cpp
|
||||
test/handshake.cpp
|
||||
test/prepared_statements.cpp
|
||||
|
@ -22,17 +22,13 @@ run
|
||||
|
||||
# Utilities
|
||||
src/server_features.cpp
|
||||
src/get_endpoint.cpp
|
||||
src/metadata_validator.cpp
|
||||
src/er_network_variant.cpp
|
||||
src/sync_errc.cpp
|
||||
src/sync_exc.cpp
|
||||
src/async_callback.cpp
|
||||
src/async_coroutines.cpp
|
||||
src/async_coroutinescpp20.cpp
|
||||
src/spotchecks_helpers.cpp
|
||||
src/utils.cpp
|
||||
|
||||
# Actual tests
|
||||
test/spotchecks.cpp
|
||||
test/execution_requests.cpp
|
||||
test/crud.cpp
|
||||
test/handshake.cpp
|
||||
test/prepared_statements.cpp
|
||||
|
@ -246,8 +246,9 @@ INSERT INTO types_date VALUES
|
||||
("yregular_invalid_date_leap100", "1900-02-29")
|
||||
;
|
||||
|
||||
-- A bug in MySQL 5.x requires us to set this collation to binary to get the correct order
|
||||
CREATE TABLE types_datetime(
|
||||
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
||||
id VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL PRIMARY KEY,
|
||||
field_0 DATETIME(0),
|
||||
field_1 DATETIME(1),
|
||||
field_2 DATETIME(2),
|
||||
|
@ -5,6 +5,8 @@
|
||||
-- file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
--
|
||||
|
||||
USE boost_mysql_integtests;
|
||||
|
||||
-- Setup that requires the presence of SHA256 functionality
|
||||
-- This one is used for unknown authentication plugin tests
|
||||
DROP USER IF EXISTS 'sha2p_user'@'%';
|
||||
@ -18,5 +20,17 @@ CREATE USER 'csha2p_user'@'%' IDENTIFIED WITH 'caching_sha2_password';
|
||||
ALTER USER 'csha2p_user'@'%' IDENTIFIED BY 'csha2p_password';
|
||||
GRANT ALL PRIVILEGES ON boost_mysql_integtests.* TO 'csha2p_user'@'%';
|
||||
|
||||
-- User that uses caching_sha2_password plugin with an empty password
|
||||
DROP USER IF EXISTS 'csha2p_empty_password_user'@'%';
|
||||
CREATE USER 'csha2p_empty_password_user'@'%' IDENTIFIED WITH 'caching_sha2_password';
|
||||
ALTER USER 'csha2p_empty_password_user'@'%' IDENTIFIED BY '';
|
||||
GRANT ALL PRIVILEGES ON boost_mysql_integtests.* TO 'csha2p_empty_password_user'@'%';
|
||||
|
||||
|
||||
-- caching_sha2_password behaves differently on sha256 cache hit and miss.
|
||||
-- These tests require exclusive access to the cache, and lock this table
|
||||
-- to avoid race conditions (e.g. between concurrent b2 runs)
|
||||
DROP TABLE IF EXISTS sha256_mutex;
|
||||
CREATE TABLE sha256_mutex (dummy INT);
|
||||
|
||||
FLUSH PRIVILEGES;
|
@ -0,0 +1,40 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_ANY_CONNECTION_FIXTURE_HPP
|
||||
#define BOOST_MYSQL_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_ANY_CONNECTION_FIXTURE_HPP
|
||||
|
||||
#include <boost/mysql/any_connection.hpp>
|
||||
#include <boost/mysql/connect_params.hpp>
|
||||
|
||||
#include <boost/assert/source_location.hpp>
|
||||
|
||||
#include "test_common/source_location.hpp"
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
struct any_connection_fixture
|
||||
{
|
||||
any_connection conn;
|
||||
|
||||
any_connection_fixture() : any_connection_fixture(any_connection_params{}) {}
|
||||
any_connection_fixture(any_connection_params params);
|
||||
any_connection_fixture(asio::ssl::context& ssl_ctx);
|
||||
~any_connection_fixture();
|
||||
|
||||
void connect(const connect_params& params, source_location loc = BOOST_MYSQL_CURRENT_LOCATION);
|
||||
void connect(source_location loc = BOOST_MYSQL_CURRENT_LOCATION);
|
||||
void start_transaction(source_location loc = BOOST_MYSQL_CURRENT_LOCATION);
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -1,151 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_COMMON_HPP
|
||||
#define BOOST_MYSQL_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_COMMON_HPP
|
||||
|
||||
#include <boost/mysql/connect_params.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
#include <boost/mysql/metadata_collection_view.hpp>
|
||||
#include <boost/mysql/results.hpp>
|
||||
#include <boost/mysql/ssl_mode.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
#include <boost/asio/system_executor.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
#include "test_common/ci_server.hpp"
|
||||
#include "test_integration/er_connection.hpp"
|
||||
#include "test_integration/er_network_variant.hpp"
|
||||
#include "test_integration/metadata_validator.hpp"
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
inline connect_params default_connect_params(ssl_mode ssl = ssl_mode::enable)
|
||||
{
|
||||
connect_params res;
|
||||
res.server_address.emplace_host_and_port(get_hostname());
|
||||
res.username = integ_user;
|
||||
res.password = integ_passwd;
|
||||
res.database = integ_db;
|
||||
res.ssl = ssl;
|
||||
return res;
|
||||
}
|
||||
|
||||
struct network_fixture_base
|
||||
{
|
||||
handshake_params params{integ_user, integ_passwd, integ_db};
|
||||
boost::asio::io_context ctx;
|
||||
boost::asio::ssl::context ssl_ctx{boost::asio::ssl::context::tls_client};
|
||||
};
|
||||
|
||||
struct network_fixture : network_fixture_base
|
||||
{
|
||||
er_network_variant* var{};
|
||||
er_connection_ptr conn;
|
||||
|
||||
~network_fixture()
|
||||
{
|
||||
if (conn)
|
||||
{
|
||||
conn->sync_close();
|
||||
}
|
||||
}
|
||||
|
||||
void setup(er_network_variant& variant)
|
||||
{
|
||||
var = &variant;
|
||||
conn = var->create_connection(ctx.get_executor(), ssl_ctx);
|
||||
conn->set_metadata_mode(metadata_mode::full);
|
||||
}
|
||||
|
||||
void setup_and_physical_connect(er_network_variant& net)
|
||||
{
|
||||
setup(net);
|
||||
conn->physical_connect();
|
||||
}
|
||||
|
||||
void setup_and_connect(er_network_variant& net, ssl_mode m = ssl_mode::require)
|
||||
{
|
||||
setup(net);
|
||||
connect(m);
|
||||
}
|
||||
|
||||
void set_credentials(string_view user, string_view password)
|
||||
{
|
||||
params.set_username(user);
|
||||
params.set_password(password);
|
||||
}
|
||||
|
||||
// Verifies that we are or are not using SSL, depending on whether the stream supports it or not
|
||||
void validate_ssl(ssl_mode m = ssl_mode::require)
|
||||
{
|
||||
bool expected = (m == ssl_mode::require || m == ssl_mode::enable) && var->supports_ssl();
|
||||
BOOST_TEST(conn->uses_ssl() == expected);
|
||||
}
|
||||
|
||||
void handshake(ssl_mode m = ssl_mode::require)
|
||||
{
|
||||
assert(conn);
|
||||
params.set_ssl(m);
|
||||
conn->handshake(params).validate_no_error();
|
||||
validate_ssl(m);
|
||||
}
|
||||
|
||||
void connect(ssl_mode m = ssl_mode::require)
|
||||
{
|
||||
assert(conn);
|
||||
params.set_ssl(m);
|
||||
conn->connect(params).validate_no_error();
|
||||
validate_ssl(m);
|
||||
}
|
||||
|
||||
// Call this in the fixture setup of any test invoking write
|
||||
// operations on the database, to prevent race conditions,
|
||||
// make the testing environment more stable and speed up the tests
|
||||
void start_transaction()
|
||||
{
|
||||
results result;
|
||||
conn->execute("START TRANSACTION", result).get();
|
||||
}
|
||||
};
|
||||
|
||||
inline void validate_2fields_meta(const metadata_collection_view& fields, const std::string& table)
|
||||
{
|
||||
validate_meta(
|
||||
fields,
|
||||
{meta_validator(table, "id", column_type::int_),
|
||||
meta_validator(table, "field_varchar", column_type::varchar)}
|
||||
);
|
||||
}
|
||||
|
||||
inline void validate_eof(
|
||||
const execution_state& st,
|
||||
unsigned affected_rows = 0,
|
||||
unsigned warnings = 0,
|
||||
unsigned last_insert = 0,
|
||||
string_view info = ""
|
||||
)
|
||||
{
|
||||
BOOST_TEST_REQUIRE(st.complete());
|
||||
BOOST_TEST(st.affected_rows() == affected_rows);
|
||||
BOOST_TEST(st.warning_count() == warnings);
|
||||
BOOST_TEST(st.last_insert_id() == last_insert);
|
||||
BOOST_TEST(st.info() == info);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif /* TEST_INTEGRATION_INTEGRATION_TEST_COMMON_HPP_ */
|
@ -0,0 +1,82 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_CONNECT_PARAMS_BUILDER_HPP
|
||||
#define BOOST_MYSQL_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_CONNECT_PARAMS_BUILDER_HPP
|
||||
|
||||
#include <boost/mysql/any_address.hpp>
|
||||
#include <boost/mysql/connect_params.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
#include <boost/mysql/metadata_collection_view.hpp>
|
||||
#include <boost/mysql/ssl_mode.hpp>
|
||||
#include <boost/mysql/string_view.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "test_common/ci_server.hpp"
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
class connect_params_builder
|
||||
{
|
||||
handshake_params res_{integ_user, integ_passwd, integ_db};
|
||||
any_address addr_;
|
||||
|
||||
public:
|
||||
connect_params_builder() { addr_.emplace_host_and_port(get_hostname()); }
|
||||
|
||||
connect_params_builder& set_unix()
|
||||
{
|
||||
addr_.emplace_unix_path(default_unix_path);
|
||||
return *this;
|
||||
}
|
||||
|
||||
connect_params_builder& credentials(string_view username, string_view passwd)
|
||||
{
|
||||
res_.set_username(username);
|
||||
res_.set_password(passwd);
|
||||
return *this;
|
||||
}
|
||||
|
||||
connect_params_builder& database(string_view db)
|
||||
{
|
||||
res_.set_database(db);
|
||||
return *this;
|
||||
}
|
||||
|
||||
connect_params_builder& disable_ssl() { return ssl(ssl_mode::disable); }
|
||||
|
||||
connect_params_builder& ssl(ssl_mode ssl)
|
||||
{
|
||||
res_.set_ssl(ssl);
|
||||
return *this;
|
||||
}
|
||||
|
||||
connect_params_builder& multi_queries(bool v)
|
||||
{
|
||||
res_.set_multi_queries(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
connect_params_builder& collation(std::uint16_t v)
|
||||
{
|
||||
res_.set_connection_collation(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
handshake_params build_hparams() const { return res_; }
|
||||
|
||||
connect_params build();
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -1,81 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_ER_CONNECTION_HPP
|
||||
#define BOOST_MYSQL_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_ER_CONNECTION_HPP
|
||||
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/field_view.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
#include <boost/mysql/metadata_mode.hpp>
|
||||
#include <boost/mysql/results.hpp>
|
||||
#include <boost/mysql/row_view.hpp>
|
||||
#include <boost/mysql/rows_view.hpp>
|
||||
#include <boost/mysql/statement.hpp>
|
||||
#include <boost/mysql/static_execution_state.hpp>
|
||||
#include <boost/mysql/static_results.hpp>
|
||||
|
||||
#include <forward_list>
|
||||
#include <memory>
|
||||
|
||||
#include "test_common/network_result.hpp"
|
||||
#include "test_integration/static_rows.hpp"
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
using fv_list_it = std::forward_list<field_view>::const_iterator;
|
||||
|
||||
class er_network_variant;
|
||||
|
||||
class er_connection
|
||||
{
|
||||
public:
|
||||
virtual ~er_connection() {}
|
||||
virtual bool uses_ssl() const = 0;
|
||||
virtual bool is_open() const = 0;
|
||||
virtual void set_metadata_mode(metadata_mode v) = 0;
|
||||
virtual void physical_connect() = 0; // used by fixture setup functions
|
||||
virtual void sync_close() noexcept = 0; // used by fixture cleanup functions
|
||||
virtual er_network_variant& variant() const = 0;
|
||||
|
||||
virtual network_result<void> connect(const handshake_params&) = 0;
|
||||
virtual network_result<void> handshake(const handshake_params&) = 0;
|
||||
virtual network_result<statement> prepare_statement(string_view statement) = 0;
|
||||
virtual network_result<void> execute(string_view, results&) = 0;
|
||||
virtual network_result<void> execute(bound_statement_tuple<std::tuple<field_view, field_view>>, results&) = 0;
|
||||
virtual network_result<void> execute(bound_statement_iterator_range<fv_list_it>, results&) = 0;
|
||||
virtual network_result<void> start_execution(string_view, execution_state&) = 0;
|
||||
virtual network_result<void> start_execution(bound_statement_tuple<std::tuple<field_view, field_view>>, execution_state&) = 0;
|
||||
virtual network_result<void> start_execution(bound_statement_iterator_range<fv_list_it>, execution_state&) = 0;
|
||||
virtual network_result<void> close_statement(statement&) = 0;
|
||||
virtual network_result<void> read_resultset_head(execution_state& st) = 0;
|
||||
virtual network_result<rows_view> read_some_rows(execution_state& st) = 0;
|
||||
virtual network_result<void> ping() = 0;
|
||||
virtual network_result<void> reset_connection() = 0;
|
||||
virtual network_result<void> quit() = 0;
|
||||
virtual network_result<void> close() = 0;
|
||||
|
||||
#ifdef BOOST_MYSQL_CXX14
|
||||
using static_results_t = static_results<row_multifield, row_2fields, empty>;
|
||||
using static_state_t = static_execution_state<row_multifield, row_2fields, empty>;
|
||||
virtual network_result<void> execute(string_view, static_results_t&) = 0;
|
||||
virtual network_result<void> start_execution(string_view, static_state_t&) = 0;
|
||||
virtual network_result<void> read_resultset_head(static_state_t& st) = 0;
|
||||
virtual network_result<std::size_t> read_some_rows(static_state_t& st, boost::span<row_multifield>) = 0;
|
||||
virtual network_result<std::size_t> read_some_rows(static_state_t& st, boost::span<row_2fields>) = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
using er_connection_ptr = std::unique_ptr<er_connection>;
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -1,51 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_ER_NETWORK_VARIANT_HPP
|
||||
#define BOOST_MYSQL_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_ER_NETWORK_VARIANT_HPP
|
||||
|
||||
#include <boost/mysql/string_view.hpp>
|
||||
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
#include <boost/core/span.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
|
||||
#include "test_integration/er_connection.hpp"
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
class er_network_variant
|
||||
{
|
||||
public:
|
||||
virtual ~er_network_variant() {}
|
||||
virtual bool supports_ssl() const = 0;
|
||||
virtual bool is_unix_socket() const = 0;
|
||||
virtual bool supports_handshake() const = 0;
|
||||
virtual const char* stream_name() const = 0;
|
||||
virtual const char* variant_name() const = 0;
|
||||
std::string name() const { return std::string(stream_name()) + '_' + variant_name(); }
|
||||
virtual er_connection_ptr create_connection(boost::asio::any_io_executor, boost::asio::ssl::context&) = 0;
|
||||
};
|
||||
std::ostream& operator<<(std::ostream& os, const er_network_variant& var);
|
||||
|
||||
std::vector<std::reference_wrapper<er_network_variant>> all_variants();
|
||||
std::vector<std::reference_wrapper<er_network_variant>> all_variants_with_handshake();
|
||||
er_network_variant& get_network_variant(string_view name);
|
||||
std::vector<std::reference_wrapper<er_network_variant>> get_network_variants(
|
||||
boost::span<const string_view> names
|
||||
);
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -1,47 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_GET_ENDPOINT_HPP
|
||||
#define BOOST_MYSQL_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_GET_ENDPOINT_HPP
|
||||
|
||||
#include <boost/mysql/string_view.hpp>
|
||||
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/local/stream_protocol.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
template <class Protocol>
|
||||
struct endpoint_getter;
|
||||
|
||||
template <>
|
||||
struct endpoint_getter<boost::asio::ip::tcp>
|
||||
{
|
||||
boost::asio::ip::tcp::endpoint operator()();
|
||||
};
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
|
||||
template <>
|
||||
struct endpoint_getter<boost::asio::local::stream_protocol>
|
||||
{
|
||||
boost::asio::local::stream_protocol::endpoint operator()();
|
||||
};
|
||||
#endif
|
||||
|
||||
template <class Stream>
|
||||
typename Stream::lowest_layer_type::endpoint_type get_endpoint()
|
||||
{
|
||||
return endpoint_getter<typename Stream::lowest_layer_type::protocol_type>()();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -73,10 +73,8 @@ private:
|
||||
std::vector<flag_getter> ignore_flags_;
|
||||
};
|
||||
|
||||
void validate_meta(
|
||||
const metadata_collection_view& actual,
|
||||
const std::vector<meta_validator>& expected
|
||||
);
|
||||
void validate_meta(const metadata_collection_view& actual, const std::vector<meta_validator>& expected);
|
||||
void validate_2fields_meta(metadata_collection_view fields, string_view table);
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
|
@ -1,84 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_NETWORK_SAMPLES_HPP
|
||||
#define BOOST_MYSQL_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_NETWORK_SAMPLES_HPP
|
||||
|
||||
#include <boost/mysql/string_view.hpp>
|
||||
|
||||
#include <boost/test/data/monomorphic/fwd.hpp>
|
||||
#include <boost/test/data/size.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "test_integration/er_network_variant.hpp"
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
// A Boost.Test dataset of er_network_variant samples that are lazily computed.
|
||||
// Laziness is required because creating the variants requires access to
|
||||
// the enabled server features (and thus getenv), and that's not legal from global initializers.
|
||||
class network_samples
|
||||
{
|
||||
using col_t = std::vector<std::reference_wrapper<er_network_variant>>;
|
||||
using factory_t = std::function<col_t()>;
|
||||
|
||||
mutable bool created_{false};
|
||||
mutable col_t data_;
|
||||
factory_t fn_;
|
||||
|
||||
col_t& get() const
|
||||
{
|
||||
if (!created_)
|
||||
{
|
||||
data_ = fn_();
|
||||
created_ = true;
|
||||
}
|
||||
return data_;
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr int arity = 1;
|
||||
using iterator = col_t::const_iterator;
|
||||
|
||||
network_samples(factory_t fn) : fn_(std::move(fn)) {}
|
||||
network_samples(std::vector<string_view> names)
|
||||
: network_samples([names]() { return get_network_variants(names); })
|
||||
{
|
||||
}
|
||||
|
||||
static network_samples all() { return network_samples(&all_variants); }
|
||||
static network_samples all_with_handshake() { return network_samples(&all_variants_with_handshake); }
|
||||
|
||||
boost::unit_test::data::size_t size() const { return get().size(); }
|
||||
iterator begin() const { return get().begin(); }
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
namespace boost {
|
||||
namespace unit_test {
|
||||
namespace data {
|
||||
namespace monomorphic {
|
||||
|
||||
template <>
|
||||
struct is_dataset<mysql::test::network_samples> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace monomorphic
|
||||
} // namespace data
|
||||
} // namespace unit_test
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
203
test/integration/include/test_integration/spotchecks_helpers.hpp
Normal file
203
test/integration/include/test_integration/spotchecks_helpers.hpp
Normal file
@ -0,0 +1,203 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_SPOTCHECKS_HELPERS_HPP
|
||||
#define BOOST_MYSQL_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_SPOTCHECKS_HELPERS_HPP
|
||||
|
||||
#include <boost/mysql/any_connection.hpp>
|
||||
#include <boost/mysql/character_set.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/pipeline.hpp>
|
||||
#include <boost/mysql/results.hpp>
|
||||
#include <boost/mysql/statement.hpp>
|
||||
#include <boost/mysql/static_execution_state.hpp>
|
||||
#include <boost/mysql/static_results.hpp>
|
||||
#include <boost/mysql/tcp.hpp>
|
||||
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/core/span.hpp>
|
||||
|
||||
#include <iosfwd>
|
||||
|
||||
#include "test_common/netfun_maker.hpp"
|
||||
#include "test_common/tracker_executor.hpp"
|
||||
#include "test_integration/connect_params_builder.hpp"
|
||||
#include "test_integration/static_rows.hpp"
|
||||
#include "test_integration/tcp_connection_fixture.hpp"
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
#ifdef BOOST_MYSQL_CXX14
|
||||
using static_results_t = static_results<row_multifield, row_2fields, empty>;
|
||||
using static_state_t = static_execution_state<row_multifield, row_2fields, empty>;
|
||||
#endif
|
||||
|
||||
// Netmakers
|
||||
template <class Conn>
|
||||
struct netmakers_common
|
||||
{
|
||||
using prepare_statement = netfun_maker<statement, Conn, string_view>;
|
||||
using execute_query = netfun_maker<void, Conn, const string_view&, results&>;
|
||||
using execute_statement = netfun_maker<
|
||||
void,
|
||||
Conn,
|
||||
const bound_statement_tuple<std::tuple<int, int>>&,
|
||||
results&>;
|
||||
using start_execution = netfun_maker<void, Conn, const string_view&, execution_state&>;
|
||||
using close_statement = netfun_maker<void, Conn, const statement&>;
|
||||
using read_resultset_head = netfun_maker<void, Conn, execution_state&>;
|
||||
using read_some_rows = netfun_maker<rows_view, Conn, execution_state&>;
|
||||
using ping = netfun_maker<void, Conn>;
|
||||
using reset_connection = netfun_maker<void, Conn>;
|
||||
using close = netfun_maker<void, Conn>;
|
||||
|
||||
#ifdef BOOST_MYSQL_CXX14
|
||||
using execute_static = netfun_maker<void, Conn, const string_view&, static_results_t&>;
|
||||
using start_execution_static = netfun_maker<void, Conn, const string_view&, static_state_t&>;
|
||||
using read_resultset_head_static = netfun_maker<void, Conn, static_state_t&>;
|
||||
using read_some_rows_static_1 = netfun_maker<
|
||||
std::size_t,
|
||||
Conn,
|
||||
static_state_t&,
|
||||
boost::span<row_multifield>>;
|
||||
using read_some_rows_static_2 = netfun_maker<
|
||||
std::size_t,
|
||||
Conn,
|
||||
static_state_t&,
|
||||
boost::span<row_2fields>>;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct netmakers_connection : netmakers_common<tcp_connection>
|
||||
{
|
||||
using connect_stream = netfun_maker<void, asio::ip::tcp::socket, const asio::ip::tcp::endpoint&>;
|
||||
using handshake = netfun_maker<void, tcp_connection, const handshake_params&>;
|
||||
using connect = netfun_maker<
|
||||
void,
|
||||
tcp_connection,
|
||||
const asio::ip::tcp::endpoint&,
|
||||
const handshake_params&>;
|
||||
using quit = netfun_maker<void, tcp_connection>;
|
||||
};
|
||||
|
||||
struct netmakers_any : netmakers_common<any_connection>
|
||||
{
|
||||
using connect = netfun_maker<void, any_connection, const connect_params&>;
|
||||
using set_character_set = netfun_maker<void, any_connection, const character_set&>;
|
||||
using run_pipeline = netfun_maker<
|
||||
void,
|
||||
any_connection,
|
||||
const pipeline_request&,
|
||||
std::vector<stage_response>&>;
|
||||
};
|
||||
|
||||
struct network_functions_connection
|
||||
{
|
||||
using netmakers = netmakers_connection;
|
||||
|
||||
string_view name;
|
||||
netmakers::prepare_statement::signature prepare_statement;
|
||||
netmakers::execute_query::signature execute_query;
|
||||
netmakers::execute_statement::signature execute_statement;
|
||||
netmakers::start_execution::signature start_execution;
|
||||
netmakers::close_statement::signature close_statement;
|
||||
netmakers::read_resultset_head::signature read_resultset_head;
|
||||
netmakers::read_some_rows::signature read_some_rows;
|
||||
netmakers::ping::signature ping;
|
||||
netmakers::reset_connection::signature reset_connection;
|
||||
netmakers::close::signature close;
|
||||
#ifdef BOOST_MYSQL_CXX14
|
||||
netmakers::execute_static::signature execute_static;
|
||||
netmakers::start_execution_static::signature start_execution_static;
|
||||
netmakers::read_resultset_head_static::signature read_resultset_head_static;
|
||||
netmakers::read_some_rows_static_1::signature read_some_rows_static_1;
|
||||
netmakers::read_some_rows_static_2::signature read_some_rows_static_2;
|
||||
#endif
|
||||
netmakers::connect_stream::signature connect_stream;
|
||||
netmakers::handshake::signature handshake;
|
||||
netmakers::connect::signature connect;
|
||||
netmakers::quit::signature quit;
|
||||
|
||||
static std::vector<network_functions_connection> all();
|
||||
static std::vector<network_functions_connection> sync_and_async();
|
||||
};
|
||||
std::ostream& operator<<(std::ostream& os, const network_functions_connection& v);
|
||||
|
||||
struct network_functions_any
|
||||
{
|
||||
using netmakers = netmakers_any;
|
||||
|
||||
string_view name;
|
||||
netmakers::prepare_statement::signature prepare_statement;
|
||||
netmakers::execute_query::signature execute_query;
|
||||
netmakers::execute_statement::signature execute_statement;
|
||||
netmakers::start_execution::signature start_execution;
|
||||
netmakers::close_statement::signature close_statement;
|
||||
netmakers::read_resultset_head::signature read_resultset_head;
|
||||
netmakers::read_some_rows::signature read_some_rows;
|
||||
netmakers::ping::signature ping;
|
||||
netmakers::reset_connection::signature reset_connection;
|
||||
netmakers::close::signature close;
|
||||
#ifdef BOOST_MYSQL_CXX14
|
||||
netmakers::execute_static::signature execute_static;
|
||||
netmakers::start_execution_static::signature start_execution_static;
|
||||
netmakers::read_resultset_head_static::signature read_resultset_head_static;
|
||||
netmakers::read_some_rows_static_1::signature read_some_rows_static_1;
|
||||
netmakers::read_some_rows_static_2::signature read_some_rows_static_2;
|
||||
#endif
|
||||
netmakers::connect::signature connect;
|
||||
netmakers::set_character_set::signature set_character_set;
|
||||
netmakers::run_pipeline::signature run_pipeline;
|
||||
|
||||
static std::vector<network_functions_any> all();
|
||||
static std::vector<network_functions_any> sync_and_async();
|
||||
};
|
||||
std::ostream& operator<<(std::ostream& os, const network_functions_any& v);
|
||||
|
||||
// Fixtures. Like tcp_connection_fixture and any_connection_fixture,
|
||||
// but using a network_functions value
|
||||
struct netfn_fixture_connection
|
||||
{
|
||||
tcp_connection conn{global_context_executor()};
|
||||
network_functions_connection net;
|
||||
|
||||
netfn_fixture_connection(network_functions_connection n) : net(std::move(n))
|
||||
{
|
||||
conn.set_meta_mode(metadata_mode::full);
|
||||
}
|
||||
~netfn_fixture_connection() { net.close(conn).validate_no_error(); }
|
||||
|
||||
void connect(connect_params_builder conn_params = {})
|
||||
{
|
||||
net.connect(conn, get_tcp_endpoint(), conn_params.build_hparams()).validate_no_error();
|
||||
}
|
||||
};
|
||||
|
||||
struct netfn_fixture_any
|
||||
{
|
||||
any_connection conn{global_context_executor()};
|
||||
network_functions_any net;
|
||||
|
||||
netfn_fixture_any(network_functions_any n) : net(std::move(n))
|
||||
{
|
||||
conn.set_meta_mode(metadata_mode::full);
|
||||
}
|
||||
~netfn_fixture_any() { net.close(conn).validate_no_error(); }
|
||||
|
||||
void connect(connect_params_builder conn_params = {})
|
||||
{
|
||||
net.connect(conn, conn_params.build()).validate_no_error();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -1,107 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_STREAMS_HPP
|
||||
#define BOOST_MYSQL_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_STREAMS_HPP
|
||||
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/local/stream_protocol.hpp>
|
||||
#include <boost/asio/ssl/stream.hpp>
|
||||
#include <boost/asio/use_future.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
// The actual streams we will be using to test
|
||||
using tcp_socket = boost::asio::ip::tcp::socket;
|
||||
using tcp_ssl_socket = boost::asio::ssl::stream<tcp_socket>;
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
|
||||
using unix_socket = boost::asio::local::stream_protocol::socket;
|
||||
using unix_ssl_socket = boost::asio::ssl::stream<unix_socket>;
|
||||
#endif
|
||||
|
||||
// Stream names
|
||||
template <class Stream>
|
||||
constexpr const char* get_stream_name();
|
||||
template <>
|
||||
constexpr const char* get_stream_name<tcp_socket>()
|
||||
{
|
||||
return "tcp";
|
||||
}
|
||||
template <>
|
||||
constexpr const char* get_stream_name<tcp_ssl_socket>()
|
||||
{
|
||||
return "tcp_ssl";
|
||||
}
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
|
||||
template <>
|
||||
constexpr const char* get_stream_name<unix_socket>()
|
||||
{
|
||||
return "unix";
|
||||
}
|
||||
template <>
|
||||
constexpr const char* get_stream_name<unix_ssl_socket>()
|
||||
{
|
||||
return "unix_ssl";
|
||||
}
|
||||
#endif
|
||||
|
||||
// Supports SSL (doesn't use the lib's type trait for test independance)
|
||||
template <class Stream>
|
||||
constexpr bool supports_ssl();
|
||||
template <>
|
||||
constexpr bool supports_ssl<tcp_socket>()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
template <>
|
||||
constexpr bool supports_ssl<tcp_ssl_socket>()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
|
||||
template <>
|
||||
constexpr bool supports_ssl<unix_socket>()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
template <>
|
||||
constexpr bool supports_ssl<unix_ssl_socket>()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// is_unix_socket
|
||||
template <class Stream>
|
||||
constexpr bool is_unix_socket()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
|
||||
template <>
|
||||
constexpr bool is_unix_socket<unix_socket>()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
template <>
|
||||
constexpr bool is_unix_socket<unix_ssl_socket>()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -0,0 +1,39 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_TCP_CONNECTION_FIXTURE_HPP
|
||||
#define BOOST_MYSQL_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_TCP_CONNECTION_FIXTURE_HPP
|
||||
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
#include <boost/mysql/tcp.hpp>
|
||||
|
||||
#include <boost/assert/source_location.hpp>
|
||||
|
||||
#include "test_common/source_location.hpp"
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
struct tcp_connection_fixture
|
||||
{
|
||||
tcp_connection conn;
|
||||
|
||||
tcp_connection_fixture();
|
||||
~tcp_connection_fixture();
|
||||
|
||||
void connect(source_location loc = BOOST_MYSQL_CURRENT_LOCATION);
|
||||
void connect(const handshake_params&, source_location loc = BOOST_MYSQL_CURRENT_LOCATION);
|
||||
};
|
||||
|
||||
asio::ip::tcp::endpoint get_tcp_endpoint();
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -1,56 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_TCP_NETWORK_FIXTURE_HPP
|
||||
#define BOOST_MYSQL_TEST_INTEGRATION_INCLUDE_TEST_INTEGRATION_TCP_NETWORK_FIXTURE_HPP
|
||||
|
||||
#include <boost/mysql/diagnostics.hpp>
|
||||
#include <boost/mysql/error_code.hpp>
|
||||
#include <boost/mysql/tcp.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
#include "test_integration/common.hpp"
|
||||
#include "test_integration/get_endpoint.hpp"
|
||||
#include "test_integration/streams.hpp"
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
struct tcp_network_fixture : network_fixture_base
|
||||
{
|
||||
boost::mysql::tcp_connection conn;
|
||||
|
||||
tcp_network_fixture() : conn(ctx.get_executor()) { conn.set_meta_mode(metadata_mode::full); }
|
||||
~tcp_network_fixture()
|
||||
{
|
||||
try
|
||||
{
|
||||
error_code ec;
|
||||
diagnostics diag;
|
||||
conn.close(ec, diag);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void connect() { conn.connect(get_endpoint<tcp_socket>(), params); }
|
||||
|
||||
void start_transaction()
|
||||
{
|
||||
results result;
|
||||
conn.execute("START TRANSACTION", result);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -12,6 +12,7 @@
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/ssl/stream.hpp>
|
||||
#include <boost/assert/source_location.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/core/detail/string_view.hpp>
|
||||
#include <boost/core/span.hpp>
|
||||
|
@ -1,115 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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/any_address.hpp>
|
||||
#include <boost/mysql/connection.hpp>
|
||||
#include <boost/mysql/diagnostics.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
#include <boost/mysql/statement.hpp>
|
||||
|
||||
#include <boost/asio/bind_executor.hpp>
|
||||
|
||||
#include "er_impl_common.hpp"
|
||||
#include "test_common/netfun_helpers.hpp"
|
||||
#include "test_common/tracker_executor.hpp"
|
||||
#include "test_integration/er_connection.hpp"
|
||||
#include "test_integration/server_features.hpp"
|
||||
#include "test_integration/streams.hpp"
|
||||
|
||||
using namespace boost::mysql::test;
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
template <class Base>
|
||||
class async_callback_base : public Base
|
||||
{
|
||||
protected:
|
||||
using conn_type = typename Base::conn_type;
|
||||
using base_type = Base;
|
||||
|
||||
template <class R, class... Args>
|
||||
using pmem_t = void (conn_type::*)(Args..., diagnostics&, as_network_result<R>&&);
|
||||
|
||||
template <class R, class... Args>
|
||||
network_result<R> fn_impl(pmem_t<R, Args...> p, Args... args)
|
||||
{
|
||||
// Get the object's I/O executor
|
||||
auto ex = this->conn().get_executor();
|
||||
|
||||
// Create the initial result
|
||||
auto res = create_initial_netresult<R>();
|
||||
|
||||
{
|
||||
// Mark ourselves as initiating
|
||||
initiation_guard guard;
|
||||
|
||||
// Invoke the initiation function
|
||||
invoke_polyfill(
|
||||
p,
|
||||
this->conn(),
|
||||
std::forward<Args>(args)...,
|
||||
*res.diag,
|
||||
as_network_result<R>(res, ex)
|
||||
);
|
||||
}
|
||||
|
||||
// Run
|
||||
run_until_completion(ex);
|
||||
|
||||
// Done
|
||||
return res;
|
||||
}
|
||||
|
||||
public:
|
||||
using Base::Base;
|
||||
static constexpr const char* name() noexcept { return "async_callback"; }
|
||||
};
|
||||
|
||||
template <class Stream>
|
||||
class async_callback_connection : public async_callback_base<connection_base<Stream>>
|
||||
{
|
||||
using base_type = async_callback_base<connection_base<Stream>>;
|
||||
|
||||
public:
|
||||
BOOST_MYSQL_TEST_IMPLEMENT_ASYNC()
|
||||
};
|
||||
|
||||
class any_async_callback_connection : public async_callback_base<any_connection_base>
|
||||
{
|
||||
using base_type = async_callback_base<any_connection_base>;
|
||||
|
||||
public:
|
||||
BOOST_MYSQL_TEST_IMPLEMENT_ASYNC_ANY()
|
||||
};
|
||||
|
||||
template <class Stream>
|
||||
void add_async_callback_variant(std::vector<std::reference_wrapper<er_network_variant>>& output)
|
||||
{
|
||||
add_variant<async_callback_connection<Stream>>(output);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
void boost::mysql::test::add_async_callback(std::vector<std::reference_wrapper<er_network_variant>>& output)
|
||||
{
|
||||
// Spotcheck for both streams
|
||||
add_async_callback_variant<tcp_socket>(output);
|
||||
add_async_callback_variant<tcp_ssl_socket>(output);
|
||||
add_variant_any<address_type::host_and_port, any_async_callback_connection>(output);
|
||||
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
|
||||
if (get_server_features().unix_sockets)
|
||||
{
|
||||
add_async_callback_variant<unix_socket>(output);
|
||||
add_variant_any<address_type::unix_path, any_async_callback_connection>(output);
|
||||
}
|
||||
#endif
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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/any_address.hpp>
|
||||
|
||||
#include <boost/asio/error.hpp>
|
||||
#include <boost/asio/spawn.hpp>
|
||||
|
||||
#include <exception>
|
||||
|
||||
#include "er_impl_common.hpp"
|
||||
#include "test_common/netfun_helpers.hpp"
|
||||
#include "test_integration/streams.hpp"
|
||||
|
||||
// Coroutines test async without diagnostics overloads
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
template <class Base>
|
||||
class async_coroutine_base : public Base
|
||||
{
|
||||
protected:
|
||||
using conn_type = typename Base::conn_type;
|
||||
using base_type = Base;
|
||||
|
||||
template <class R, class... Args>
|
||||
using pmem_t = R (conn_type::*)(Args..., boost::asio::yield_context&&);
|
||||
|
||||
template <class R, class... Args>
|
||||
network_result<R> fn_impl(pmem_t<R, Args...> p, Args... args)
|
||||
{
|
||||
auto res = create_initial_netresult<R>(false);
|
||||
boost::asio::spawn(
|
||||
this->conn().get_executor(),
|
||||
[&](boost::asio::yield_context yield) {
|
||||
invoke_and_assign(res, p, this->conn(), std::forward<Args>(args)..., yield[res.err]);
|
||||
},
|
||||
&rethrow_on_failure
|
||||
);
|
||||
run_until_completion(this->conn().get_executor());
|
||||
return res;
|
||||
}
|
||||
|
||||
public:
|
||||
using Base::Base;
|
||||
static constexpr const char* name() noexcept { return "async_coroutines"; }
|
||||
};
|
||||
|
||||
template <class Stream>
|
||||
class async_coroutine_connection : public async_coroutine_base<connection_base<Stream>>
|
||||
{
|
||||
using base_type = async_coroutine_base<connection_base<Stream>>;
|
||||
|
||||
public:
|
||||
BOOST_MYSQL_TEST_IMPLEMENT_ASYNC()
|
||||
};
|
||||
|
||||
class any_async_coroutine_connection : public async_coroutine_base<any_connection_base>
|
||||
{
|
||||
using base_type = async_coroutine_base<any_connection_base>;
|
||||
|
||||
public:
|
||||
BOOST_MYSQL_TEST_IMPLEMENT_ASYNC_ANY()
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
void boost::mysql::test::add_async_coroutines(std::vector<std::reference_wrapper<er_network_variant>>& output)
|
||||
{
|
||||
add_variant<async_coroutine_connection<tcp_socket>>(output);
|
||||
add_variant_any<address_type::host_and_port, any_async_coroutine_connection>(output);
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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/any_address.hpp>
|
||||
|
||||
#include <boost/asio/as_tuple.hpp>
|
||||
#include <boost/asio/awaitable.hpp>
|
||||
#include <boost/asio/co_spawn.hpp>
|
||||
#include <boost/asio/use_awaitable.hpp>
|
||||
|
||||
#include <exception>
|
||||
|
||||
#include "er_impl_common.hpp"
|
||||
#include "test_common/netfun_helpers.hpp"
|
||||
#include "test_integration/streams.hpp"
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_CO_AWAIT
|
||||
|
||||
// C++20 coroutines test async with diagnostics overloads & deferred tokens
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
template <class R>
|
||||
using result_tuple = std::
|
||||
conditional_t<std::is_same_v<R, void>, std::tuple<error_code>, std::tuple<error_code, R>>;
|
||||
|
||||
using token_t = boost::asio::as_tuple_t<boost::asio::use_awaitable_t<>>;
|
||||
|
||||
template <class R>
|
||||
void to_network_result(const std::tuple<error_code, R>& tup, network_result<R>& netresult)
|
||||
{
|
||||
netresult.err = std::get<0>(tup);
|
||||
netresult.value = std::get<1>(tup);
|
||||
}
|
||||
|
||||
void to_network_result(const std::tuple<error_code>& tup, network_result<void>& netresult)
|
||||
{
|
||||
netresult.err = std::get<0>(tup);
|
||||
}
|
||||
|
||||
template <class Base>
|
||||
class async_coroutinecpp20_base : public Base
|
||||
{
|
||||
protected:
|
||||
using conn_type = typename Base::conn_type;
|
||||
using base_type = Base;
|
||||
|
||||
template <class R, class... Args>
|
||||
using pmem_t = boost::asio::awaitable<result_tuple<R>> (conn_type::*)(Args..., diagnostics&, token_t&&);
|
||||
|
||||
template <class R, class... Args>
|
||||
network_result<R> fn_impl(pmem_t<R, Args...> p, Args... args)
|
||||
{
|
||||
auto res = create_initial_netresult<R>();
|
||||
|
||||
boost::asio::co_spawn(
|
||||
this->conn().get_executor(),
|
||||
[&]() -> boost::asio::awaitable<void> {
|
||||
// Create the task
|
||||
auto aw = (this->conn().*p)(std::forward<Args>(args)..., *res.diag, token_t());
|
||||
|
||||
// Verify that initiation didn't have side effects
|
||||
BOOST_TEST(res.diag->server_message() == "diagnostics not cleared properly");
|
||||
|
||||
// Run the task
|
||||
auto tup = co_await std::move(aw);
|
||||
to_network_result(tup, res);
|
||||
},
|
||||
// Regular errors are handled via error_code's. This will just
|
||||
// propagate unexpected ones.
|
||||
&rethrow_on_failure
|
||||
);
|
||||
|
||||
run_until_completion(this->conn().get_executor());
|
||||
return res;
|
||||
}
|
||||
|
||||
public:
|
||||
using Base::Base;
|
||||
static constexpr const char* name() noexcept { return "async_coroutinescpp20"; }
|
||||
};
|
||||
|
||||
template <class Stream>
|
||||
class async_coroutinecpp20_connection : public async_coroutinecpp20_base<connection_base<Stream>>
|
||||
{
|
||||
using base_type = async_coroutinecpp20_base<connection_base<Stream>>;
|
||||
|
||||
public:
|
||||
BOOST_MYSQL_TEST_IMPLEMENT_ASYNC()
|
||||
};
|
||||
|
||||
class any_async_coroutinecpp20_connection : public async_coroutinecpp20_base<any_connection_base>
|
||||
{
|
||||
using base_type = async_coroutinecpp20_base<any_connection_base>;
|
||||
|
||||
public:
|
||||
BOOST_MYSQL_TEST_IMPLEMENT_ASYNC_ANY()
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
void boost::mysql::test::add_async_coroutinescpp20(
|
||||
std::vector<std::reference_wrapper<er_network_variant>>& output
|
||||
)
|
||||
{
|
||||
add_variant<async_coroutinecpp20_connection<tcp_ssl_socket>>(output);
|
||||
add_variant_any<address_type::host_and_port, any_async_coroutinecpp20_connection>(output);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void boost::mysql::test::add_async_coroutinescpp20(std::vector<std::reference_wrapper<er_network_variant>>&)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
@ -1,448 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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_TEST_INTEGRATION_SRC_ER_IMPL_COMMON_HPP
|
||||
#define BOOST_MYSQL_TEST_INTEGRATION_SRC_ER_IMPL_COMMON_HPP
|
||||
|
||||
#include <boost/mysql/any_address.hpp>
|
||||
#include <boost/mysql/any_connection.hpp>
|
||||
#include <boost/mysql/connect_params.hpp>
|
||||
#include <boost/mysql/connection.hpp>
|
||||
#include <boost/mysql/error_code.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
#include <boost/mysql/metadata_mode.hpp>
|
||||
#include <boost/mysql/results.hpp>
|
||||
#include <boost/mysql/rows_view.hpp>
|
||||
#include <boost/mysql/statement.hpp>
|
||||
#include <boost/mysql/string_view.hpp>
|
||||
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/asio/error.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "test_common/ci_server.hpp"
|
||||
#include "test_common/network_result.hpp"
|
||||
#include "test_integration/er_connection.hpp"
|
||||
#include "test_integration/er_network_variant.hpp"
|
||||
#include "test_integration/get_endpoint.hpp"
|
||||
#include "test_integration/streams.hpp"
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
// Variants
|
||||
void add_sync_errc(std::vector<std::reference_wrapper<er_network_variant>>&);
|
||||
void add_sync_exc(std::vector<std::reference_wrapper<er_network_variant>>&);
|
||||
void add_async_callback(std::vector<std::reference_wrapper<er_network_variant>>&);
|
||||
void add_async_coroutines(std::vector<std::reference_wrapper<er_network_variant>>&);
|
||||
void add_async_coroutinescpp20(std::vector<std::reference_wrapper<er_network_variant>>&);
|
||||
|
||||
// Helpers
|
||||
template <class Stream>
|
||||
connection<Stream> create_connection_impl(
|
||||
boost::asio::any_io_executor executor,
|
||||
boost::asio::ssl::context& ssl_ctx,
|
||||
std::true_type // is ssl stream
|
||||
)
|
||||
{
|
||||
return connection<Stream>(executor, ssl_ctx);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
connection<Stream> create_connection_impl(
|
||||
boost::asio::any_io_executor executor,
|
||||
boost::asio::ssl::context&,
|
||||
std::false_type // is ssl stream
|
||||
)
|
||||
{
|
||||
return connection<Stream>(executor);
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
connection<Stream> create_connection(
|
||||
boost::asio::any_io_executor executor,
|
||||
boost::asio::ssl::context& ssl_ctx
|
||||
)
|
||||
{
|
||||
return create_connection_impl<Stream>(
|
||||
executor,
|
||||
ssl_ctx,
|
||||
std::integral_constant<bool, supports_ssl<Stream>()>()
|
||||
);
|
||||
}
|
||||
|
||||
// Base for implementing er_connection
|
||||
template <class Stream>
|
||||
class connection_base : public er_connection
|
||||
{
|
||||
public:
|
||||
using stream_type = Stream;
|
||||
using conn_type = connection<Stream>;
|
||||
using stmt_tuple = bound_statement_tuple<std::tuple<field_view, field_view>>;
|
||||
using stmt_it = bound_statement_iterator_range<fv_list_it>;
|
||||
|
||||
connection_base(
|
||||
boost::asio::any_io_executor executor,
|
||||
boost::asio::ssl::context& ssl_ctx,
|
||||
er_network_variant& var
|
||||
)
|
||||
: conn_(create_connection<Stream>(executor, ssl_ctx)), var_(var)
|
||||
{
|
||||
}
|
||||
|
||||
conn_type& conn() noexcept { return conn_; }
|
||||
|
||||
bool uses_ssl() const override { return conn_.uses_ssl(); }
|
||||
bool is_open() const override { return conn_.stream().lowest_layer().is_open(); }
|
||||
void set_metadata_mode(metadata_mode v) override { conn_.set_meta_mode(v); }
|
||||
void physical_connect() override { conn_.stream().lowest_layer().connect(get_endpoint<Stream>()); }
|
||||
void sync_close() noexcept override
|
||||
{
|
||||
try
|
||||
{
|
||||
conn_.close();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
er_network_variant& variant() const override { return var_; }
|
||||
|
||||
private:
|
||||
conn_type conn_;
|
||||
er_network_variant& var_;
|
||||
};
|
||||
|
||||
class any_connection_base : public er_connection
|
||||
{
|
||||
static any_connection_params make_ctor_params(asio::ssl::context& ctx) noexcept
|
||||
{
|
||||
any_connection_params res;
|
||||
res.ssl_context = &ctx;
|
||||
return res;
|
||||
}
|
||||
|
||||
public:
|
||||
using conn_type = any_connection;
|
||||
using base_type = any_connection_base;
|
||||
using stmt_tuple = bound_statement_tuple<std::tuple<field_view, field_view>>;
|
||||
using stmt_it = bound_statement_iterator_range<fv_list_it>;
|
||||
|
||||
connect_params get_connect_params(const handshake_params& input) const noexcept
|
||||
{
|
||||
connect_params res;
|
||||
if (addr_type_ == address_type::host_and_port)
|
||||
res.server_address.emplace_host_and_port(get_hostname());
|
||||
else if (addr_type_ == address_type::unix_path)
|
||||
res.server_address.emplace_unix_path(default_unix_path);
|
||||
res.username = input.username();
|
||||
res.password = input.password();
|
||||
res.database = input.database();
|
||||
res.multi_queries = input.multi_queries();
|
||||
return res;
|
||||
}
|
||||
|
||||
any_connection_base(
|
||||
boost::asio::any_io_executor executor,
|
||||
boost::asio::ssl::context& ssl_ctx,
|
||||
er_network_variant& var,
|
||||
address_type addr
|
||||
)
|
||||
: conn_(executor, make_ctor_params(ssl_ctx)), var_(var), addr_type_(addr)
|
||||
{
|
||||
}
|
||||
|
||||
any_connection& conn() noexcept { return conn_; }
|
||||
|
||||
bool uses_ssl() const override { return conn_.uses_ssl(); }
|
||||
bool is_open() const override { return true; } // TODO
|
||||
void set_metadata_mode(metadata_mode v) override { conn_.set_meta_mode(v); }
|
||||
void physical_connect() override final { assert(false); } // not allowed in v2
|
||||
network_result<void> handshake(const handshake_params&) override final
|
||||
{
|
||||
return error_code(asio::error::operation_not_supported);
|
||||
}
|
||||
network_result<void> quit() override final { return error_code(asio::error::operation_not_supported); }
|
||||
|
||||
void sync_close() noexcept override
|
||||
{
|
||||
try
|
||||
{
|
||||
conn_.close();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
er_network_variant& variant() const override { return var_; }
|
||||
|
||||
private:
|
||||
any_connection conn_;
|
||||
er_network_variant& var_;
|
||||
address_type addr_type_;
|
||||
};
|
||||
|
||||
// Macros to help implementing er_connection
|
||||
#ifdef BOOST_MYSQL_CXX14
|
||||
#define BOOST_MYSQL_TEST_IMPLEMENT_GENERIC_CXX14(prefix) \
|
||||
network_result<void> execute(string_view q, er_connection::static_results_t& result) override \
|
||||
{ \
|
||||
return this->template fn_impl<void, const string_view&, er_connection::static_results_t&>( \
|
||||
&conn_type::prefix##execute, \
|
||||
q, \
|
||||
result \
|
||||
); \
|
||||
} \
|
||||
network_result<void> start_execution(string_view q, er_connection::static_state_t& st) override \
|
||||
{ \
|
||||
return this->template fn_impl<void, const string_view&, er_connection::static_state_t&>( \
|
||||
&conn_type::prefix##start_execution, \
|
||||
q, \
|
||||
st \
|
||||
); \
|
||||
} \
|
||||
network_result<void> read_resultset_head(er_connection::static_state_t& st) override \
|
||||
{ \
|
||||
return this->template fn_impl<void, er_connection::static_state_t&>( \
|
||||
&conn_type::prefix##read_resultset_head, \
|
||||
st \
|
||||
); \
|
||||
} \
|
||||
network_result<std::size_t> read_some_rows( \
|
||||
er_connection::static_state_t& st, \
|
||||
boost::span<row_multifield> storage \
|
||||
) override \
|
||||
{ \
|
||||
return this \
|
||||
->template fn_impl<std::size_t, er_connection::static_state_t&, boost::span<row_multifield>>( \
|
||||
&conn_type::prefix##read_some_rows, \
|
||||
st, \
|
||||
storage \
|
||||
); \
|
||||
} \
|
||||
network_result<std::size_t> read_some_rows( \
|
||||
er_connection::static_state_t& st, \
|
||||
boost::span<row_2fields> storage \
|
||||
) override \
|
||||
{ \
|
||||
return this \
|
||||
->template fn_impl<std::size_t, er_connection::static_state_t&, boost::span<row_2fields>>( \
|
||||
&conn_type::prefix##read_some_rows, \
|
||||
st, \
|
||||
storage \
|
||||
); \
|
||||
}
|
||||
#else
|
||||
#define BOOST_MYSQL_TEST_IMPLEMENT_GENERIC_CXX14(prefix)
|
||||
#endif
|
||||
|
||||
#define BOOST_MYSQL_TEST_IMPLEMENT_GENERIC_COMMON(prefix) \
|
||||
using conn_type = typename base_type::conn_type; \
|
||||
using base_type::base_type; \
|
||||
network_result<statement> prepare_statement(string_view stmt_sql) override \
|
||||
{ \
|
||||
return this->template fn_impl<statement, string_view>( \
|
||||
&conn_type::prefix##prepare_statement, \
|
||||
stmt_sql \
|
||||
); \
|
||||
} \
|
||||
network_result<void> execute(string_view query, results& result) override \
|
||||
{ \
|
||||
return this->template fn_impl<void, const string_view&, results&>( \
|
||||
&conn_type::prefix##execute, \
|
||||
query, \
|
||||
result \
|
||||
); \
|
||||
} \
|
||||
network_result<void> execute( \
|
||||
bound_statement_tuple<std::tuple<field_view, field_view>> req, \
|
||||
results& result \
|
||||
) override \
|
||||
{ \
|
||||
return this->template fn_impl<void, const typename base_type::stmt_tuple&, results&>( \
|
||||
&conn_type::prefix##execute, \
|
||||
req, \
|
||||
result \
|
||||
); \
|
||||
} \
|
||||
network_result<void> execute(bound_statement_iterator_range<fv_list_it> req, results& result) override \
|
||||
{ \
|
||||
return this->template fn_impl<void, const typename base_type::stmt_it&, results&>( \
|
||||
&conn_type::prefix##execute, \
|
||||
req, \
|
||||
result \
|
||||
); \
|
||||
} \
|
||||
network_result<void> start_execution(string_view query, execution_state& st) override \
|
||||
{ \
|
||||
return this->template fn_impl<void, const string_view&, execution_state&>( \
|
||||
&conn_type::prefix##start_execution, \
|
||||
query, \
|
||||
st \
|
||||
); \
|
||||
} \
|
||||
network_result<void> start_execution( \
|
||||
bound_statement_tuple<std::tuple<field_view, field_view>> req, \
|
||||
execution_state& st \
|
||||
) override \
|
||||
{ \
|
||||
return this->template fn_impl<void, const typename base_type::stmt_tuple&, execution_state&>( \
|
||||
&conn_type::prefix##start_execution, \
|
||||
req, \
|
||||
st \
|
||||
); \
|
||||
} \
|
||||
network_result<void> start_execution( \
|
||||
bound_statement_iterator_range<fv_list_it> req, \
|
||||
execution_state& st \
|
||||
) override \
|
||||
{ \
|
||||
return this->template fn_impl<void, const typename base_type::stmt_it&, execution_state&>( \
|
||||
&conn_type::prefix##start_execution, \
|
||||
req, \
|
||||
st \
|
||||
); \
|
||||
} \
|
||||
network_result<void> close_statement(statement& stmt) override \
|
||||
{ \
|
||||
return this->template fn_impl<void, const statement&>(&conn_type::prefix##close_statement, stmt); \
|
||||
} \
|
||||
network_result<void> read_resultset_head(execution_state& st) override \
|
||||
{ \
|
||||
return this->template fn_impl<void, execution_state&>(&conn_type::prefix##read_resultset_head, st); \
|
||||
} \
|
||||
network_result<rows_view> read_some_rows(execution_state& st) override \
|
||||
{ \
|
||||
return this->template fn_impl<rows_view, execution_state&>(&conn_type::prefix##read_some_rows, st); \
|
||||
} \
|
||||
network_result<void> ping() override { return this->template fn_impl<void>(&conn_type::prefix##ping); } \
|
||||
network_result<void> reset_connection() override \
|
||||
{ \
|
||||
return this->template fn_impl<void>(&conn_type::prefix##reset_connection); \
|
||||
} \
|
||||
network_result<void> close() override \
|
||||
{ \
|
||||
return this->template fn_impl<void>(&conn_type::prefix##close); \
|
||||
} \
|
||||
BOOST_MYSQL_TEST_IMPLEMENT_GENERIC_CXX14(prefix)
|
||||
|
||||
#define BOOST_MYSQL_TEST_IMPLEMENT_GENERIC(prefix) \
|
||||
network_result<void> connect(const handshake_params& params) override \
|
||||
{ \
|
||||
return this->template fn_impl< \
|
||||
void, \
|
||||
const typename Stream::lowest_layer_type::endpoint_type&, \
|
||||
const handshake_params&>(&conn_type::prefix##connect, get_endpoint<Stream>(), params); \
|
||||
} \
|
||||
network_result<void> handshake(const handshake_params& params) override \
|
||||
{ \
|
||||
return this->template fn_impl<void, const handshake_params&>(&conn_type::prefix##handshake, params); \
|
||||
} \
|
||||
network_result<void> quit() override { return this->template fn_impl<void>(&conn_type::prefix##quit); } \
|
||||
BOOST_MYSQL_TEST_IMPLEMENT_GENERIC_COMMON(prefix)
|
||||
|
||||
#define BOOST_MYSQL_TEST_IMPLEMENT_GENERIC_ANY(prefix) \
|
||||
network_result<void> connect(const handshake_params& params) override \
|
||||
{ \
|
||||
return this->template fn_impl<void, const connect_params&>( \
|
||||
&conn_type::prefix##connect, \
|
||||
get_connect_params(params) \
|
||||
); \
|
||||
} \
|
||||
BOOST_MYSQL_TEST_IMPLEMENT_GENERIC_COMMON(prefix)
|
||||
|
||||
// Use these
|
||||
#define BOOST_MYSQL_TEST_IMPLEMENT_SYNC() BOOST_MYSQL_TEST_IMPLEMENT_GENERIC()
|
||||
#define BOOST_MYSQL_TEST_IMPLEMENT_ASYNC() BOOST_MYSQL_TEST_IMPLEMENT_GENERIC(async_)
|
||||
#define BOOST_MYSQL_TEST_IMPLEMENT_SYNC_ANY() BOOST_MYSQL_TEST_IMPLEMENT_GENERIC_ANY()
|
||||
#define BOOST_MYSQL_TEST_IMPLEMENT_ASYNC_ANY() BOOST_MYSQL_TEST_IMPLEMENT_GENERIC_ANY(async_)
|
||||
|
||||
// Implementation for er_network_variant
|
||||
template <class ErConnection>
|
||||
class er_network_variant_impl : public er_network_variant
|
||||
{
|
||||
using stream_type = typename ErConnection::stream_type;
|
||||
|
||||
public:
|
||||
bool supports_ssl() const override { return ::boost::mysql::test::supports_ssl<stream_type>(); }
|
||||
bool is_unix_socket() const override { return ::boost::mysql::test::is_unix_socket<stream_type>(); }
|
||||
bool supports_handshake() const override { return true; }
|
||||
const char* stream_name() const override { return ::boost::mysql::test::get_stream_name<stream_type>(); }
|
||||
const char* variant_name() const override { return ErConnection::name(); }
|
||||
er_connection_ptr create_connection(boost::asio::any_io_executor ex, boost::asio::ssl::context& ssl_ctx)
|
||||
override
|
||||
{
|
||||
return er_connection_ptr(new ErConnection(std::move(ex), ssl_ctx, *this));
|
||||
}
|
||||
};
|
||||
|
||||
template <address_type addr_type>
|
||||
constexpr const char* stream_name_from_type() noexcept;
|
||||
|
||||
template <>
|
||||
constexpr const char* stream_name_from_type<address_type::host_and_port>() noexcept
|
||||
{
|
||||
return "any_tcp";
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr const char* stream_name_from_type<address_type::unix_path>() noexcept
|
||||
{
|
||||
return "any_unix";
|
||||
}
|
||||
|
||||
template <address_type addr_type, class ErConnection>
|
||||
class er_network_variant_any_impl : public er_network_variant
|
||||
{
|
||||
public:
|
||||
bool supports_ssl() const override { return addr_type == address_type::host_and_port; }
|
||||
bool is_unix_socket() const override { return addr_type == address_type::unix_path; }
|
||||
bool supports_handshake() const override { return false; }
|
||||
const char* stream_name() const override { return stream_name_from_type<addr_type>(); }
|
||||
const char* variant_name() const override { return ErConnection::name(); }
|
||||
er_connection_ptr create_connection(boost::asio::any_io_executor ex, boost::asio::ssl::context& ssl_ctx)
|
||||
override
|
||||
{
|
||||
return er_connection_ptr(new ErConnection(std::move(ex), ssl_ctx, *this, addr_type));
|
||||
}
|
||||
};
|
||||
|
||||
// Helpers
|
||||
inline void rethrow_on_failure(std::exception_ptr ptr)
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
std::rethrow_exception(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
template <class ErConnection>
|
||||
void add_variant(std::vector<std::reference_wrapper<er_network_variant>>& output)
|
||||
{
|
||||
static er_network_variant_impl<ErConnection> obj;
|
||||
output.push_back(obj);
|
||||
}
|
||||
|
||||
template <address_type addr_type, class ErConnection>
|
||||
void add_variant_any(std::vector<std::reference_wrapper<er_network_variant>>& output)
|
||||
{
|
||||
static er_network_variant_any_impl<addr_type, ErConnection> obj;
|
||||
output.push_back(obj);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -1,71 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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 <functional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "er_impl_common.hpp"
|
||||
#include "test_integration/er_network_variant.hpp"
|
||||
|
||||
using namespace boost::mysql::test;
|
||||
using boost::mysql::error_code;
|
||||
|
||||
static std::unordered_map<std::string, er_network_variant*> make_variants_map()
|
||||
{
|
||||
std::unordered_map<std::string, er_network_variant*> res;
|
||||
for (er_network_variant& var : all_variants())
|
||||
res[var.name()] = &var;
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<std::reference_wrapper<er_network_variant>> boost::mysql::test::all_variants()
|
||||
{
|
||||
std::vector<std::reference_wrapper<er_network_variant>> res;
|
||||
add_sync_errc(res);
|
||||
add_sync_exc(res);
|
||||
add_async_callback(res);
|
||||
add_async_coroutines(res);
|
||||
add_async_coroutinescpp20(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<std::reference_wrapper<er_network_variant>> boost::mysql::test::all_variants_with_handshake()
|
||||
{
|
||||
std::vector<std::reference_wrapper<er_network_variant>> res;
|
||||
for (er_network_variant& var : all_variants())
|
||||
{
|
||||
if (var.supports_handshake())
|
||||
res.push_back(var);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
er_network_variant& boost::mysql::test::get_network_variant(string_view name)
|
||||
{
|
||||
static auto by_name = make_variants_map();
|
||||
std::string name_str(name);
|
||||
auto it = by_name.find(name_str);
|
||||
if (it == by_name.end())
|
||||
throw std::out_of_range("Unknown network variant: " + name_str);
|
||||
return *it->second;
|
||||
}
|
||||
|
||||
std::vector<std::reference_wrapper<er_network_variant>> boost::mysql::test::get_network_variants(
|
||||
boost::span<const string_view> names
|
||||
)
|
||||
{
|
||||
std::vector<std::reference_wrapper<er_network_variant>> res;
|
||||
for (auto name : names)
|
||||
res.push_back(get_network_variant(name));
|
||||
return res;
|
||||
}
|
||||
|
||||
std::ostream& boost::mysql::test::operator<<(std::ostream& os, const er_network_variant& value)
|
||||
{
|
||||
return os << value.stream_name() << "_" << value.variant_name();
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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 "test_integration/get_endpoint.hpp"
|
||||
|
||||
#include <boost/mysql/connection.hpp>
|
||||
#include <boost/mysql/string_view.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ip/address_v4.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "test_common/ci_server.hpp"
|
||||
|
||||
// Get the endpoint to use for TCP from an environment variable.
|
||||
// Required as CI MySQL doesn't run on loocalhost
|
||||
static boost::asio::ip::tcp::endpoint get_tcp_valid_endpoint()
|
||||
{
|
||||
boost::asio::io_context ctx;
|
||||
boost::asio::ip::tcp::resolver resolver(ctx.get_executor());
|
||||
auto results = resolver.resolve(boost::mysql::test::get_hostname(), boost::mysql::default_port_string);
|
||||
return *results.begin();
|
||||
}
|
||||
|
||||
boost::asio::ip::tcp::endpoint boost::mysql::test::endpoint_getter<boost::asio::ip::tcp>::operator()()
|
||||
{
|
||||
static auto res = get_tcp_valid_endpoint();
|
||||
return res;
|
||||
}
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
|
||||
boost::asio::local::stream_protocol::endpoint boost::mysql::test::endpoint_getter<
|
||||
boost::asio::local::stream_protocol>::operator()()
|
||||
{
|
||||
return boost::asio::local::stream_protocol::endpoint(default_unix_path);
|
||||
}
|
||||
#endif
|
@ -5,13 +5,13 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include "test_integration/metadata_validator.hpp"
|
||||
|
||||
#include <boost/mysql/metadata.hpp>
|
||||
#include <boost/mysql/metadata_collection_view.hpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "test_integration/metadata_validator.hpp"
|
||||
|
||||
using namespace boost::mysql::test;
|
||||
|
||||
#define MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(getter) \
|
||||
@ -32,7 +32,8 @@ static struct flag_entry
|
||||
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_zerofill),
|
||||
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_auto_increment),
|
||||
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(has_no_default_value),
|
||||
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_set_to_now_on_update)};
|
||||
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_set_to_now_on_update)
|
||||
};
|
||||
|
||||
BOOST_TEST_DONT_PRINT_LOG_VALUE(std::vector<flag_entry>::iterator)
|
||||
|
||||
@ -90,3 +91,12 @@ void boost::mysql::test::validate_meta(
|
||||
expected[i].validate(actual[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void boost::mysql::test::validate_2fields_meta(metadata_collection_view fields, string_view table)
|
||||
{
|
||||
validate_meta(
|
||||
fields,
|
||||
{meta_validator(table, "id", column_type::int_),
|
||||
meta_validator(table, "field_varchar", column_type::varchar)}
|
||||
);
|
||||
}
|
||||
|
123
test/integration/src/spotchecks_helpers.cpp
Normal file
123
test/integration/src/spotchecks_helpers.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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/asio/ip/tcp.hpp>
|
||||
|
||||
#include <ostream>
|
||||
#include <utility>
|
||||
|
||||
#include "test_integration/spotchecks_helpers.hpp"
|
||||
|
||||
using namespace boost::mysql::test;
|
||||
|
||||
// Network functions
|
||||
#define BOOST_MYSQL_MAKE_NETFN(conn, netm, fn, i) \
|
||||
(i == 0 ? netmakers::netm::sync_errc(&conn::fn) \
|
||||
: i == 1 ? netmakers::netm::sync_exc(&conn::fn) \
|
||||
: i == 2 ? netmakers::netm::async_diag(&conn::async_##fn) \
|
||||
: netmakers::netm::async_nodiag(&conn::async_##fn))
|
||||
|
||||
static constexpr const char* fn_names[] = {"sync_errc", "sync_exc", "async_diag", "async_nodiag"};
|
||||
|
||||
std::vector<network_functions_connection> boost::mysql::test::network_functions_connection::all()
|
||||
{
|
||||
std::vector<network_functions_connection> res;
|
||||
|
||||
for (std::size_t i = 0; i < 4; ++i)
|
||||
{
|
||||
// connect_stream doesn't involve diagnostics
|
||||
auto fn_connect_stream = (i == 0 || i == 1) ? netmakers::connect_stream::sync_errc_nodiag(
|
||||
&asio::ip::tcp::socket::connect
|
||||
)
|
||||
: netmakers::connect_stream::async_nodiag(
|
||||
&asio::ip::tcp::socket::async_connect
|
||||
);
|
||||
|
||||
res.push_back({
|
||||
fn_names[i],
|
||||
BOOST_MYSQL_MAKE_NETFN(tcp_connection, prepare_statement, prepare_statement, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(tcp_connection, execute_query, execute, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(tcp_connection, execute_statement, execute, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(tcp_connection, start_execution, start_execution, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(tcp_connection, close_statement, close_statement, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(tcp_connection, read_resultset_head, read_resultset_head, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(tcp_connection, read_some_rows, read_some_rows, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(tcp_connection, ping, ping, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(tcp_connection, reset_connection, reset_connection, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(tcp_connection, close, close, i),
|
||||
#ifdef BOOST_MYSQL_CXX14
|
||||
BOOST_MYSQL_MAKE_NETFN(tcp_connection, execute_static, execute, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(tcp_connection, start_execution_static, start_execution, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(tcp_connection, read_resultset_head_static, read_resultset_head, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(tcp_connection, read_some_rows_static_1, read_some_rows, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(tcp_connection, read_some_rows_static_2, read_some_rows, i),
|
||||
#endif
|
||||
std::move(fn_connect_stream),
|
||||
BOOST_MYSQL_MAKE_NETFN(tcp_connection, handshake, handshake, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(tcp_connection, connect, connect, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(tcp_connection, quit, quit, i),
|
||||
});
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<network_functions_connection> boost::mysql::test::network_functions_connection::sync_and_async()
|
||||
{
|
||||
auto all_fns = all();
|
||||
return {std::move(all_fns[0]), std::move(all_fns[2])};
|
||||
}
|
||||
|
||||
std::ostream& boost::mysql::test::operator<<(std::ostream& os, const network_functions_connection& v)
|
||||
{
|
||||
return os << v.name;
|
||||
}
|
||||
|
||||
std::vector<network_functions_any> boost::mysql::test::network_functions_any::all()
|
||||
{
|
||||
std::vector<network_functions_any> res;
|
||||
|
||||
for (std::size_t i = 0; i < 4; ++i)
|
||||
{
|
||||
res.push_back({
|
||||
fn_names[i],
|
||||
BOOST_MYSQL_MAKE_NETFN(any_connection, prepare_statement, prepare_statement, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(any_connection, execute_query, execute, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(any_connection, execute_statement, execute, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(any_connection, start_execution, start_execution, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(any_connection, close_statement, close_statement, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(any_connection, read_resultset_head, read_resultset_head, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(any_connection, read_some_rows, read_some_rows, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(any_connection, ping, ping, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(any_connection, reset_connection, reset_connection, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(any_connection, close, close, i),
|
||||
#ifdef BOOST_MYSQL_CXX14
|
||||
BOOST_MYSQL_MAKE_NETFN(any_connection, execute_static, execute, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(any_connection, start_execution_static, start_execution, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(any_connection, read_resultset_head_static, read_resultset_head, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(any_connection, read_some_rows_static_1, read_some_rows, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(any_connection, read_some_rows_static_2, read_some_rows, i),
|
||||
#endif
|
||||
BOOST_MYSQL_MAKE_NETFN(any_connection, connect, connect, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(any_connection, set_character_set, set_character_set, i),
|
||||
BOOST_MYSQL_MAKE_NETFN(any_connection, run_pipeline, run_pipeline, i),
|
||||
});
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<network_functions_any> boost::mysql::test::network_functions_any::sync_and_async()
|
||||
{
|
||||
auto all_fns = all();
|
||||
return {std::move(all_fns[0]), std::move(all_fns[2])};
|
||||
}
|
||||
|
||||
std::ostream& boost::mysql::test::operator<<(std::ostream& os, const network_functions_any& v)
|
||||
{
|
||||
return os << v.name;
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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/any_address.hpp>
|
||||
|
||||
#include "er_impl_common.hpp"
|
||||
#include "test_common/netfun_helpers.hpp"
|
||||
#include "test_integration/server_features.hpp"
|
||||
#include "test_integration/streams.hpp"
|
||||
|
||||
using namespace boost::mysql::test;
|
||||
using boost::mysql::error_code;
|
||||
|
||||
// MSVC complains about passing empty tokens, which is valid C++
|
||||
#ifdef BOOST_MSVC
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4003)
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
template <class Base>
|
||||
class sync_errc_base : public Base
|
||||
{
|
||||
protected:
|
||||
using conn_type = typename Base::conn_type;
|
||||
using base_type = Base;
|
||||
|
||||
// workaround for gcc5
|
||||
template <class R, class... Args>
|
||||
struct pmem
|
||||
{
|
||||
using type = R (conn_type::*)(Args..., error_code&, diagnostics&);
|
||||
};
|
||||
|
||||
template <class R, class... Args>
|
||||
network_result<R> fn_impl(typename pmem<R, Args...>::type p, Args... args)
|
||||
{
|
||||
auto res = create_initial_netresult<R>();
|
||||
invoke_and_assign(res, p, this->conn(), std::forward<Args>(args)..., res.err, *res.diag);
|
||||
return res;
|
||||
}
|
||||
|
||||
public:
|
||||
using Base::Base;
|
||||
static constexpr const char* name() noexcept { return "sync_errc"; }
|
||||
};
|
||||
|
||||
template <class Stream>
|
||||
class sync_errc_connection : public sync_errc_base<connection_base<Stream>>
|
||||
{
|
||||
using base_type = sync_errc_base<connection_base<Stream>>;
|
||||
|
||||
public:
|
||||
BOOST_MYSQL_TEST_IMPLEMENT_SYNC()
|
||||
};
|
||||
|
||||
class any_sync_errc_connection final : public sync_errc_base<any_connection_base>
|
||||
{
|
||||
using base_type = sync_errc_base<any_connection_base>;
|
||||
|
||||
public:
|
||||
BOOST_MYSQL_TEST_IMPLEMENT_SYNC_ANY()
|
||||
};
|
||||
|
||||
template <class Stream>
|
||||
void add_sync_errc_variant(std::vector<std::reference_wrapper<er_network_variant>>& output)
|
||||
{
|
||||
add_variant<sync_errc_connection<Stream>>(output);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
void boost::mysql::test::add_sync_errc(std::vector<std::reference_wrapper<er_network_variant>>& output)
|
||||
{
|
||||
// Verify that all streams work
|
||||
add_sync_errc_variant<tcp_socket>(output);
|
||||
add_sync_errc_variant<tcp_ssl_socket>(output);
|
||||
add_variant_any<address_type::host_and_port, any_sync_errc_connection>(output);
|
||||
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
|
||||
if (get_server_features().unix_sockets)
|
||||
{
|
||||
add_sync_errc_variant<unix_socket>(output);
|
||||
add_sync_errc_variant<unix_ssl_socket>(output);
|
||||
add_variant_any<address_type::unix_path, any_sync_errc_connection>(output);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef BOOST_MSVC
|
||||
#pragma warning(pop)
|
||||
#endif
|
@ -1,91 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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/any_address.hpp>
|
||||
|
||||
#include "er_impl_common.hpp"
|
||||
#include "test_common/netfun_helpers.hpp"
|
||||
#include "test_integration/streams.hpp"
|
||||
|
||||
using namespace boost::mysql::test;
|
||||
|
||||
// MSVC complains about passing empty tokens, which is valid C++
|
||||
#ifdef BOOST_MSVC
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4003)
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
template <class Base>
|
||||
class sync_exc_base : public Base
|
||||
{
|
||||
protected:
|
||||
using conn_type = typename Base::conn_type;
|
||||
using base_type = Base;
|
||||
|
||||
template <class R, class... Args>
|
||||
using pmem_t = R (conn_type::*)(Args...);
|
||||
|
||||
template <class R, class... Args>
|
||||
network_result<R> fn_impl(pmem_t<R, Args...> p, Args... args)
|
||||
{
|
||||
network_result<R> res;
|
||||
try
|
||||
{
|
||||
invoke_and_assign(res, p, this->conn(), std::forward<Args>(args)...);
|
||||
}
|
||||
catch (const boost::mysql::error_with_diagnostics& err)
|
||||
{
|
||||
res.err = err.code();
|
||||
res.diag = err.get_diagnostics();
|
||||
}
|
||||
catch (const boost::system::system_error& err)
|
||||
{
|
||||
res.err = err.code();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public:
|
||||
using Base::Base;
|
||||
static constexpr const char* name() noexcept { return "sync_exc"; }
|
||||
};
|
||||
|
||||
template <class Stream>
|
||||
class sync_exc_connection : public sync_exc_base<connection_base<Stream>>
|
||||
{
|
||||
using base_type = sync_exc_base<connection_base<Stream>>;
|
||||
|
||||
public:
|
||||
BOOST_MYSQL_TEST_IMPLEMENT_SYNC()
|
||||
};
|
||||
|
||||
class any_sync_exc_connection final : public sync_exc_base<any_connection_base>
|
||||
{
|
||||
using base_type = sync_exc_base<any_connection_base>;
|
||||
|
||||
public:
|
||||
BOOST_MYSQL_TEST_IMPLEMENT_SYNC_ANY()
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
void boost::mysql::test::add_sync_exc(std::vector<std::reference_wrapper<er_network_variant>>& output)
|
||||
{
|
||||
// Spotcheck
|
||||
add_variant<sync_exc_connection<tcp_socket>>(output);
|
||||
add_variant_any<address_type::host_and_port, any_sync_exc_connection>(output);
|
||||
}
|
||||
|
||||
#ifdef BOOST_MSVC
|
||||
#pragma warning(pop)
|
||||
#endif
|
105
test/integration/src/utils.cpp
Normal file
105
test/integration/src/utils.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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/defaults.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
|
||||
#include <boost/assert/source_location.hpp>
|
||||
|
||||
#include "test_common/ci_server.hpp"
|
||||
#include "test_common/network_result.hpp"
|
||||
#include "test_common/tracker_executor.hpp"
|
||||
#include "test_integration/any_connection_fixture.hpp"
|
||||
#include "test_integration/connect_params_builder.hpp"
|
||||
#include "test_integration/tcp_connection_fixture.hpp"
|
||||
|
||||
using namespace boost::mysql;
|
||||
using namespace boost::mysql::test;
|
||||
namespace asio = boost::asio;
|
||||
|
||||
static any_connection_params make_params(asio::ssl::context& ssl_ctx)
|
||||
{
|
||||
any_connection_params res;
|
||||
res.ssl_context = &ssl_ctx;
|
||||
return res;
|
||||
}
|
||||
|
||||
// any_connection_fixture
|
||||
any_connection_fixture::any_connection_fixture(any_connection_params params)
|
||||
: conn(global_context_executor(), params)
|
||||
{
|
||||
conn.set_meta_mode(metadata_mode::full);
|
||||
}
|
||||
|
||||
any_connection_fixture::any_connection_fixture(asio::ssl::context& ssl_ctx)
|
||||
: any_connection_fixture(make_params(ssl_ctx))
|
||||
{
|
||||
}
|
||||
|
||||
any_connection_fixture::~any_connection_fixture() { conn.async_close(as_netresult).validate_no_error(); }
|
||||
|
||||
void any_connection_fixture::connect(const connect_params& params, boost::source_location loc)
|
||||
{
|
||||
conn.async_connect(params, as_netresult).validate_no_error(loc);
|
||||
}
|
||||
|
||||
void any_connection_fixture::connect(boost::source_location loc)
|
||||
{
|
||||
connect(connect_params_builder().ssl(ssl_mode::disable).build(), loc);
|
||||
}
|
||||
|
||||
void any_connection_fixture::start_transaction(boost::source_location loc)
|
||||
{
|
||||
results r;
|
||||
conn.async_execute("START TRANSACTION", r, as_netresult).validate_no_error(loc);
|
||||
}
|
||||
|
||||
// tcp_connection_fixture
|
||||
static asio::ip::tcp::endpoint resolve_server_endpoint()
|
||||
{
|
||||
asio::io_context ctx;
|
||||
asio::ip::tcp::resolver resolver(ctx);
|
||||
auto results = resolver.resolve(get_hostname(), default_port_string);
|
||||
return *results.begin();
|
||||
}
|
||||
|
||||
tcp_connection_fixture::tcp_connection_fixture() : conn(global_context_executor())
|
||||
{
|
||||
conn.set_meta_mode(metadata_mode::full);
|
||||
}
|
||||
|
||||
tcp_connection_fixture::~tcp_connection_fixture() { conn.async_close(as_netresult).validate_no_error(); }
|
||||
|
||||
void tcp_connection_fixture::connect(boost::source_location loc)
|
||||
{
|
||||
connect(connect_params_builder().build_hparams(), loc);
|
||||
}
|
||||
|
||||
void tcp_connection_fixture::connect(const handshake_params& params, boost::source_location loc)
|
||||
{
|
||||
conn.async_connect(get_tcp_endpoint(), params, as_netresult).validate_no_error(loc);
|
||||
}
|
||||
|
||||
asio::ip::tcp::endpoint boost::mysql::test::get_tcp_endpoint()
|
||||
{
|
||||
static auto res = resolve_server_endpoint();
|
||||
return res;
|
||||
}
|
||||
|
||||
// connect_params_builder
|
||||
connect_params connect_params_builder::build()
|
||||
{
|
||||
connect_params res;
|
||||
res.server_address = std::move(addr_);
|
||||
res.username = res_.username();
|
||||
res.password = res_.password();
|
||||
res.database = res_.database();
|
||||
res.multi_queries = res_.multi_queries();
|
||||
res.ssl = res_.ssl();
|
||||
res.connection_collation = res_.connection_collation();
|
||||
return res;
|
||||
}
|
@ -7,7 +7,6 @@
|
||||
|
||||
#include <boost/mysql/any_connection.hpp>
|
||||
#include <boost/mysql/client_errc.hpp>
|
||||
#include <boost/mysql/common_server_errc.hpp>
|
||||
#include <boost/mysql/connect_params.hpp>
|
||||
#include <boost/mysql/error_code.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
@ -21,23 +20,18 @@
|
||||
|
||||
#include <boost/mysql/impl/internal/variant_stream.hpp>
|
||||
|
||||
#include <boost/asio/deferred.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
#include <boost/asio/ssl/host_name_verification.hpp>
|
||||
#include <boost/test/tree/decorator.hpp>
|
||||
#include <boost/asio/local/basic_endpoint.hpp>
|
||||
#include <boost/test/data/test_case.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "test_common/create_basic.hpp"
|
||||
#include "test_common/create_diagnostics.hpp"
|
||||
#include "test_common/netfun_maker.hpp"
|
||||
#include "test_common/network_result.hpp"
|
||||
#include "test_common/printing.hpp"
|
||||
#include "test_integration/common.hpp"
|
||||
#include "test_integration/server_ca.hpp"
|
||||
#include "test_integration/any_connection_fixture.hpp"
|
||||
#include "test_integration/connect_params_builder.hpp"
|
||||
#include "test_integration/server_features.hpp"
|
||||
#include "test_integration/spotchecks_helpers.hpp"
|
||||
|
||||
// Additional spotchecks for any_connection
|
||||
|
||||
@ -48,192 +42,72 @@ using boost::test_tools::per_element;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(test_any_connection)
|
||||
|
||||
using netmaker_connect = netfun_maker_mem<void, any_connection, const connect_params&>;
|
||||
using netmaker_execute = netfun_maker_mem<void, any_connection, const string_view&, results&>;
|
||||
|
||||
// Don't validate executor info, since our I/O objects don't use tracker executors
|
||||
const auto connect_fn = netmaker_connect::async_errinfo(&any_connection::async_connect);
|
||||
const auto execute_fn = netmaker_execute::async_errinfo(&any_connection::async_execute);
|
||||
|
||||
// Passing no SSL context to the constructor and using SSL works.
|
||||
// ssl_mode::require works
|
||||
BOOST_AUTO_TEST_CASE(default_ssl_context)
|
||||
{
|
||||
// Create the connection
|
||||
boost::asio::io_context ctx;
|
||||
any_connection conn(ctx);
|
||||
|
||||
// Call the function
|
||||
connect_fn(conn, default_connect_params(ssl_mode::require)).validate_no_error();
|
||||
|
||||
// uses_ssl reports the right value
|
||||
BOOST_TEST(conn.uses_ssl());
|
||||
}
|
||||
|
||||
// Passing a custom SSL context works
|
||||
BOOST_AUTO_TEST_CASE(custom_ssl_context)
|
||||
{
|
||||
// Set up a SSL context
|
||||
asio::ssl::context ssl_ctx(asio::ssl::context::tlsv12_client);
|
||||
ssl_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
|
||||
ssl_ctx.add_certificate_authority(boost::asio::buffer(CA_PEM));
|
||||
ssl_ctx.set_verify_callback(boost::asio::ssl::host_name_verification("bad.host.name"));
|
||||
|
||||
// Create the connection
|
||||
asio::io_context ctx;
|
||||
any_connection_params ctor_params;
|
||||
ctor_params.ssl_context = &ssl_ctx;
|
||||
any_connection conn(ctx, ctor_params);
|
||||
|
||||
// Certificate validation fails
|
||||
auto result = connect_fn(conn, default_connect_params(ssl_mode::require));
|
||||
BOOST_TEST(result.err.message().find("certificate verify failed") != std::string::npos);
|
||||
}
|
||||
|
||||
// Disabling SSL works with TCP connections
|
||||
BOOST_AUTO_TEST_CASE(tcp_ssl_mode_disable)
|
||||
{
|
||||
// Create the connection
|
||||
boost::asio::io_context ctx;
|
||||
any_connection conn(ctx);
|
||||
|
||||
// Call the function
|
||||
connect_fn(conn, default_connect_params(ssl_mode::disable)).validate_no_error();
|
||||
|
||||
// uses_ssl reports the right value
|
||||
BOOST_TEST(!conn.uses_ssl());
|
||||
}
|
||||
|
||||
// SSL mode enable woks with TCP connections
|
||||
BOOST_AUTO_TEST_CASE(tcp_ssl_mode_enable)
|
||||
{
|
||||
// Create the connection
|
||||
boost::asio::io_context ctx;
|
||||
any_connection conn(ctx);
|
||||
|
||||
// Call the function
|
||||
connect_fn(conn, default_connect_params(ssl_mode::enable)).validate_no_error();
|
||||
|
||||
// All our CIs support SSL
|
||||
BOOST_TEST(conn.uses_ssl());
|
||||
}
|
||||
|
||||
// any_connection can be used with UNIX sockets
|
||||
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
|
||||
// UNIX connections never use SSL
|
||||
BOOST_TEST_DECORATOR(*run_if(&server_features::unix_sockets))
|
||||
BOOST_AUTO_TEST_CASE(unix_ssl)
|
||||
BOOST_FIXTURE_TEST_CASE(unix_sockets, any_connection_fixture)
|
||||
{
|
||||
// Create the connection
|
||||
boost::asio::io_context ctx;
|
||||
any_connection conn(ctx);
|
||||
|
||||
// Connect params
|
||||
auto params = default_connect_params(ssl_mode::require);
|
||||
params.server_address.emplace_unix_path(default_unix_path);
|
||||
|
||||
// Call the function
|
||||
connect_fn(conn, params).validate_no_error();
|
||||
|
||||
// SSL is not enabled even if we specified require, since there's
|
||||
// no point in using SSL with UNIX sockets
|
||||
// Connect
|
||||
connect(connect_params_builder().set_unix().build());
|
||||
BOOST_TEST(!conn.uses_ssl());
|
||||
}
|
||||
#endif
|
||||
|
||||
// Spotcheck: users can log-in using the caching_sha2_password auth plugin
|
||||
BOOST_TEST_DECORATOR(*run_if(&server_features::sha256))
|
||||
BOOST_AUTO_TEST_CASE(tcp_caching_sha2_password)
|
||||
{
|
||||
// Create the connection
|
||||
boost::asio::io_context ctx;
|
||||
any_connection conn(ctx);
|
||||
// We can prepare statements
|
||||
auto stmt = conn.async_prepare_statement("SELECT ?", as_netresult).get();
|
||||
BOOST_TEST(stmt.num_params() == 1u);
|
||||
|
||||
// Connect params
|
||||
auto params = default_connect_params(ssl_mode::require);
|
||||
params.username = "csha2p_user";
|
||||
params.password = "csha2p_password";
|
||||
|
||||
// Call the function
|
||||
connect_fn(conn, params).validate_no_error();
|
||||
BOOST_TEST(conn.uses_ssl());
|
||||
}
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
|
||||
// Users can log-in using the caching_sha2_password auth plugin
|
||||
// even if they're using UNIX sockets.
|
||||
BOOST_TEST_DECORATOR(*run_if(&server_features::sha256, &server_features::unix_sockets))
|
||||
BOOST_AUTO_TEST_CASE(unix_caching_sha2_password)
|
||||
{
|
||||
// Setup
|
||||
boost::asio::io_context ctx;
|
||||
any_connection conn(ctx);
|
||||
any_connection root_conn(ctx);
|
||||
|
||||
// Clear the sha256 cache. This forces us send the password using plain text.
|
||||
// TODO: we could create a dedicated user - this can make the test more reliable
|
||||
auto root_params = default_connect_params();
|
||||
// We can execute queries
|
||||
results r;
|
||||
root_params.username = "root";
|
||||
root_params.password = "";
|
||||
root_conn.connect(root_params);
|
||||
root_conn.execute("FLUSH PRIVILEGES", r);
|
||||
conn.async_execute("SELECT 'abc'", r, as_netresult).validate_no_error();
|
||||
BOOST_TEST(r.rows() == makerows(1, "abc"), per_element());
|
||||
|
||||
// Connect params
|
||||
auto params = default_connect_params(ssl_mode::require);
|
||||
params.server_address.emplace_unix_path(default_unix_path);
|
||||
params.username = "csha2p_user";
|
||||
params.password = "csha2p_password";
|
||||
// We can execute statements
|
||||
conn.async_execute(stmt.bind(42), r, as_netresult).validate_no_error();
|
||||
BOOST_TEST(r.rows() == makerows(1, 42), per_element());
|
||||
|
||||
// Call the function
|
||||
connect_fn(conn, params).validate_no_error();
|
||||
BOOST_TEST(!conn.uses_ssl());
|
||||
// We can get errors
|
||||
conn.async_execute("SELECT * FROM bad_table", r, as_netresult)
|
||||
.validate_error(
|
||||
common_server_errc::er_no_such_table,
|
||||
"Table 'boost_mysql_integtests.bad_table' doesn't exist"
|
||||
);
|
||||
|
||||
// We can terminate the connection
|
||||
conn.async_close(as_netresult).validate_no_error();
|
||||
}
|
||||
#endif
|
||||
|
||||
network_result<void> create_net_result()
|
||||
{
|
||||
return network_result<void>(
|
||||
common_server_errc::er_aborting_connection,
|
||||
create_server_diag("diagnostics not cleared")
|
||||
);
|
||||
}
|
||||
|
||||
// Backslash escapes
|
||||
BOOST_AUTO_TEST_CASE(backslash_escapes)
|
||||
BOOST_FIXTURE_TEST_CASE(backslash_escapes, any_connection_fixture)
|
||||
{
|
||||
// Create the connection
|
||||
boost::asio::io_context ctx;
|
||||
any_connection conn(ctx);
|
||||
|
||||
// Backslash escapes enabled by default
|
||||
BOOST_TEST(conn.backslash_escapes());
|
||||
|
||||
// Connect doesn't change the value
|
||||
connect_fn(conn, default_connect_params(ssl_mode::disable)).validate_no_error();
|
||||
conn.async_connect(connect_params_builder().disable_ssl().build(), as_netresult).validate_no_error();
|
||||
BOOST_TEST(conn.backslash_escapes());
|
||||
BOOST_TEST(conn.format_opts()->backslash_escapes);
|
||||
|
||||
// Setting the SQL mode to NO_BACKSLASH_ESCAPES updates the value
|
||||
results r;
|
||||
execute_fn(conn, "SET sql_mode = 'NO_BACKSLASH_ESCAPES'", r).validate_no_error();
|
||||
conn.async_execute("SET sql_mode = 'NO_BACKSLASH_ESCAPES'", r, as_netresult).validate_no_error();
|
||||
BOOST_TEST(!conn.backslash_escapes());
|
||||
BOOST_TEST(!conn.format_opts()->backslash_escapes);
|
||||
|
||||
// Executing a different statement doesn't change the value
|
||||
execute_fn(conn, "SELECT 1", r).validate_no_error();
|
||||
conn.async_execute("SELECT 1", r, as_netresult).validate_no_error();
|
||||
BOOST_TEST(!conn.backslash_escapes());
|
||||
BOOST_TEST(!conn.format_opts()->backslash_escapes);
|
||||
|
||||
// Clearing the SQL mode updates the value
|
||||
execute_fn(conn, "SET sql_mode = ''", r).validate_no_error();
|
||||
conn.async_execute("SET sql_mode = ''", r, as_netresult).validate_no_error();
|
||||
BOOST_TEST(conn.backslash_escapes());
|
||||
BOOST_TEST(conn.format_opts()->backslash_escapes);
|
||||
|
||||
// Reconnecting clears the value
|
||||
execute_fn(conn, "SET sql_mode = 'NO_BACKSLASH_ESCAPES'", r).validate_no_error();
|
||||
conn.async_execute("SET sql_mode = 'NO_BACKSLASH_ESCAPES'", r, as_netresult).validate_no_error();
|
||||
BOOST_TEST(!conn.backslash_escapes());
|
||||
BOOST_TEST(!conn.format_opts()->backslash_escapes);
|
||||
connect_fn(conn, default_connect_params(ssl_mode::disable)).validate_no_error();
|
||||
conn.async_connect(connect_params_builder().disable_ssl().build(), as_netresult).validate_no_error();
|
||||
BOOST_TEST(conn.backslash_escapes());
|
||||
BOOST_TEST(conn.format_opts()->backslash_escapes);
|
||||
}
|
||||
@ -242,94 +116,67 @@ BOOST_AUTO_TEST_CASE(backslash_escapes)
|
||||
BOOST_AUTO_TEST_CASE(max_buffer_size)
|
||||
{
|
||||
// Create the connection
|
||||
boost::asio::io_context ctx;
|
||||
any_connection_params params;
|
||||
params.initial_buffer_size = 512u;
|
||||
params.max_buffer_size = 512u;
|
||||
any_connection conn(ctx, params);
|
||||
any_connection_fixture fix(params);
|
||||
|
||||
// Connect
|
||||
connect_fn(conn, default_connect_params(ssl_mode::disable)).validate_no_error();
|
||||
fix.conn.async_connect(connect_params_builder().disable_ssl().build(), as_netresult).validate_no_error();
|
||||
|
||||
// Reading and writing almost 512 bytes works
|
||||
results r;
|
||||
auto q = format_sql(conn.format_opts().value(), "SELECT {}", std::string(450, 'a'));
|
||||
execute_fn(conn, q, r).validate_no_error();
|
||||
auto q = format_sql(fix.conn.format_opts().value(), "SELECT {}", std::string(450, 'a'));
|
||||
fix.conn.async_execute(q, r, as_netresult).validate_no_error();
|
||||
BOOST_TEST(r.rows() == makerows(1, std::string(450, 'a')), per_element());
|
||||
|
||||
// Trying to write more than 512 bytes fails
|
||||
q = format_sql(conn.format_opts().value(), "SELECT LENGTH({})", std::string(512, 'a'));
|
||||
execute_fn(conn, q, r).validate_error_exact(client_errc::max_buffer_size_exceeded);
|
||||
q = format_sql(fix.conn.format_opts().value(), "SELECT LENGTH({})", std::string(512, 'a'));
|
||||
fix.conn.async_execute(q, r, as_netresult).validate_error(client_errc::max_buffer_size_exceeded);
|
||||
|
||||
// Trying to read more than 512 bytes fails
|
||||
execute_fn(conn, "SELECT REPEAT('a', 512)", r)
|
||||
.validate_error_exact(client_errc::max_buffer_size_exceeded);
|
||||
fix.conn.async_execute("SELECT REPEAT('a', 512)", r, as_netresult)
|
||||
.validate_error(client_errc::max_buffer_size_exceeded);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(default_max_buffer_size_success)
|
||||
BOOST_FIXTURE_TEST_CASE(default_max_buffer_size_success, any_connection_fixture)
|
||||
{
|
||||
// Create the connection
|
||||
boost::asio::io_context ctx;
|
||||
any_connection conn(ctx);
|
||||
|
||||
// Connect
|
||||
connect_fn(conn, default_connect_params(ssl_mode::disable)).validate_no_error();
|
||||
conn.async_connect(connect_params_builder().disable_ssl().build(), as_netresult).validate_no_error();
|
||||
|
||||
// Reading almost max_buffer_size works
|
||||
execution_state st;
|
||||
conn.start_execution("SELECT 1, REPEAT('a', 0x3f00000)", st);
|
||||
auto rws = conn.read_some_rows(st);
|
||||
conn.async_start_execution("SELECT 1, REPEAT('a', 0x3f00000)", st, as_netresult).validate_no_error();
|
||||
auto rws = conn.async_read_some_rows(st, as_netresult).get();
|
||||
BOOST_TEST(rws.at(0).at(1).as_string().size() == 0x3f00000u);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(default_max_buffer_size_error)
|
||||
BOOST_FIXTURE_TEST_CASE(default_max_buffer_size_error, any_connection_fixture)
|
||||
{
|
||||
// Create the connection
|
||||
boost::asio::io_context ctx;
|
||||
any_connection conn(ctx);
|
||||
|
||||
// Connect
|
||||
connect_fn(conn, default_connect_params(ssl_mode::disable)).validate_no_error();
|
||||
conn.async_connect(connect_params_builder().disable_ssl().build(), as_netresult).validate_no_error();
|
||||
|
||||
// Trying to read more than max_buffer_size bytes fails
|
||||
results r;
|
||||
execute_fn(conn, "SELECT 1, REPEAT('a', 0x4000000)", r)
|
||||
.validate_error_exact(client_errc::max_buffer_size_exceeded);
|
||||
conn.async_execute("SELECT 1, REPEAT('a', 0x4000000)", r, as_netresult)
|
||||
.validate_error(client_errc::max_buffer_size_exceeded);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(naggle_disabled)
|
||||
BOOST_DATA_TEST_CASE(naggle_disabled, network_functions_any::sync_and_async())
|
||||
{
|
||||
struct
|
||||
{
|
||||
string_view name;
|
||||
netmaker_connect::signature fn;
|
||||
} test_cases[] = {
|
||||
{"sync", netmaker_connect::sync_errc(&any_connection::connect)},
|
||||
{"async", connect_fn },
|
||||
};
|
||||
// Setup
|
||||
netfn_fixture_any fix(sample);
|
||||
|
||||
for (const auto& tc : test_cases)
|
||||
{
|
||||
BOOST_TEST_CONTEXT(tc.name)
|
||||
{
|
||||
// Create the connection
|
||||
boost::asio::io_context ctx;
|
||||
any_connection conn(ctx);
|
||||
// Connect
|
||||
fix.net.connect(fix.conn, connect_params_builder().disable_ssl().build()).validate_no_error();
|
||||
|
||||
// Connect
|
||||
tc.fn(conn, default_connect_params(ssl_mode::disable)).validate_no_error();
|
||||
|
||||
// Naggle's algorithm was disabled
|
||||
asio::ip::tcp::no_delay opt;
|
||||
static_cast<detail::engine_impl<detail::variant_stream>&>(
|
||||
detail::access::get_impl(conn).get_engine()
|
||||
)
|
||||
.stream()
|
||||
.tcp_socket()
|
||||
.get_option(opt);
|
||||
BOOST_TEST(opt.value() == true);
|
||||
}
|
||||
}
|
||||
// Naggle's algorithm was disabled
|
||||
asio::ip::tcp::no_delay opt;
|
||||
static_cast<detail::engine_impl<detail::variant_stream>&>(detail::access::get_impl(fix.conn).get_engine())
|
||||
.stream()
|
||||
.tcp_socket()
|
||||
.get_option(opt);
|
||||
BOOST_TEST(opt.value() == true);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
@ -9,37 +9,49 @@
|
||||
#include <boost/mysql/character_set.hpp>
|
||||
#include <boost/mysql/client_errc.hpp>
|
||||
#include <boost/mysql/mysql_collations.hpp>
|
||||
#include <boost/mysql/results.hpp>
|
||||
#include <boost/mysql/ssl_mode.hpp>
|
||||
#include <boost/mysql/string_view.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/assert/source_location.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "test_common/create_basic.hpp"
|
||||
#include "test_common/network_result.hpp"
|
||||
#include "test_common/printing.hpp"
|
||||
#include "test_integration/common.hpp"
|
||||
#include "test_common/source_location.hpp"
|
||||
#include "test_integration/any_connection_fixture.hpp"
|
||||
#include "test_integration/connect_params_builder.hpp"
|
||||
|
||||
using namespace boost::mysql;
|
||||
using namespace boost::mysql::test;
|
||||
using boost::test_tools::per_element;
|
||||
namespace asio = boost::asio;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(test_character_set_tracking)
|
||||
|
||||
static void validate_db_charset(any_connection& conn, string_view expected_charset)
|
||||
static void validate_db_charset(
|
||||
any_connection& conn,
|
||||
string_view expected_charset,
|
||||
boost::source_location loc = BOOST_MYSQL_CURRENT_LOCATION
|
||||
)
|
||||
{
|
||||
// Issue the query
|
||||
constexpr const char*
|
||||
query = "SELECT @@character_set_client, @@character_set_connection, @@character_set_results";
|
||||
results r;
|
||||
conn.execute("SELECT @@character_set_client, @@character_set_connection, @@character_set_results", r);
|
||||
const auto rw = r.rows().at(0);
|
||||
BOOST_TEST(rw.at(0).as_string() == expected_charset);
|
||||
BOOST_TEST(rw.at(1).as_string() == expected_charset);
|
||||
BOOST_TEST(rw.at(2).as_string() == expected_charset);
|
||||
conn.async_execute(query, r, as_netresult).validate_no_error(loc);
|
||||
|
||||
// Check
|
||||
BOOST_TEST_CONTEXT("Called from " << loc)
|
||||
{
|
||||
const auto expected = makerows(3, expected_charset, expected_charset, expected_charset);
|
||||
BOOST_TEST(r.rows() == expected, per_element());
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(charset_lifecycle)
|
||||
BOOST_FIXTURE_TEST_CASE(charset_lifecycle, any_connection_fixture)
|
||||
{
|
||||
// Setup
|
||||
asio::io_context ctx;
|
||||
any_connection conn(ctx);
|
||||
|
||||
// Non-connected connections have an unknown charset
|
||||
BOOST_TEST(conn.current_character_set().error() == client_errc::unknown_character_set);
|
||||
BOOST_TEST(conn.format_opts().error() == client_errc::unknown_character_set);
|
||||
@ -47,46 +59,40 @@ BOOST_AUTO_TEST_CASE(charset_lifecycle)
|
||||
// Connect with the default character set uses utf8mb4, both in the client
|
||||
// and in the server. This double-checks that all supported servers support the
|
||||
// collation we use by default.
|
||||
conn.connect(default_connect_params(ssl_mode::disable));
|
||||
connect();
|
||||
BOOST_TEST(conn.current_character_set()->name == "utf8mb4");
|
||||
BOOST_TEST(conn.format_opts()->charset.name == "utf8mb4");
|
||||
validate_db_charset(conn, "utf8mb4");
|
||||
|
||||
// Using set_character_set updates the character set everywhere
|
||||
character_set greek_charset{"greek", ascii_charset.next_char};
|
||||
conn.set_character_set(greek_charset);
|
||||
conn.async_set_character_set(greek_charset, as_netresult).validate_no_error();
|
||||
BOOST_TEST(conn.current_character_set()->name == "greek");
|
||||
BOOST_TEST(conn.format_opts()->charset.name == "greek");
|
||||
validate_db_charset(conn, "greek");
|
||||
|
||||
// Using reset_connection wipes out client-side character set information
|
||||
conn.reset_connection();
|
||||
conn.async_reset_connection(as_netresult).validate_no_error();
|
||||
BOOST_TEST(conn.current_character_set().error() == client_errc::unknown_character_set);
|
||||
BOOST_TEST(conn.format_opts().error() == client_errc::unknown_character_set);
|
||||
|
||||
// We can use set_character_set to recover from this
|
||||
conn.set_character_set(greek_charset);
|
||||
conn.async_set_character_set(greek_charset, as_netresult).validate_no_error();
|
||||
BOOST_TEST(conn.current_character_set()->name == "greek");
|
||||
BOOST_TEST(conn.format_opts()->charset.name == "greek");
|
||||
validate_db_charset(conn, "greek");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(connect_with_unknown_collation)
|
||||
BOOST_FIXTURE_TEST_CASE(connect_with_unknown_collation, any_connection_fixture)
|
||||
{
|
||||
// Setup
|
||||
asio::io_context ctx;
|
||||
any_connection conn(ctx);
|
||||
|
||||
// Connect with a collation that some servers may not support, or that we don't know of
|
||||
auto params = default_connect_params(ssl_mode::disable);
|
||||
params.connection_collation = mysql_collations::utf8mb4_0900_ai_ci; // not supported by MariaDB, triggers
|
||||
// fallback
|
||||
conn.connect(params);
|
||||
// utf8mb4_0900_ai_ci is not supported by MariaDB, triggers fallback
|
||||
connect(connect_params_builder().collation(mysql_collations::utf8mb4_0900_ai_ci).build());
|
||||
BOOST_TEST(conn.current_character_set().error() == client_errc::unknown_character_set);
|
||||
BOOST_TEST(conn.format_opts().error() == client_errc::unknown_character_set);
|
||||
|
||||
// Explicitly setting the character set solves the issue
|
||||
conn.set_character_set(ascii_charset);
|
||||
conn.async_set_character_set(ascii_charset, as_netresult).validate_no_error();
|
||||
BOOST_TEST(conn.current_character_set()->name == "ascii");
|
||||
validate_db_charset(conn, "ascii");
|
||||
}
|
||||
|
@ -6,137 +6,143 @@
|
||||
//
|
||||
|
||||
#include <boost/mysql/connection.hpp>
|
||||
#include <boost/mysql/date.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/results.hpp>
|
||||
#include <boost/mysql/tcp.hpp>
|
||||
|
||||
#include <boost/test/unit_test_suite.hpp>
|
||||
|
||||
#include "test_integration/common.hpp"
|
||||
#include "test_integration/tcp_network_fixture.hpp"
|
||||
#include "test_common/create_basic.hpp"
|
||||
#include "test_common/network_result.hpp"
|
||||
#include "test_integration/any_connection_fixture.hpp"
|
||||
#include "test_integration/metadata_validator.hpp"
|
||||
|
||||
using namespace boost::mysql::test;
|
||||
using boost::mysql::results;
|
||||
using namespace boost::mysql;
|
||||
using boost::test_tools::per_element;
|
||||
|
||||
namespace {
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(test_crud)
|
||||
|
||||
// Other SELECT statements are already covered
|
||||
BOOST_FIXTURE_TEST_CASE(query_empty_select, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(query_empty_select, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
// Issue query
|
||||
results result;
|
||||
conn.execute("SELECT * FROM empty_table", result);
|
||||
conn.async_execute("SELECT * FROM empty_table", result, as_netresult).validate_no_error();
|
||||
|
||||
// Verify results
|
||||
BOOST_TEST(result.size() == 1u);
|
||||
validate_2fields_meta(result.meta(), "empty_table");
|
||||
BOOST_TEST(result.rows().empty());
|
||||
BOOST_TEST(result.rows() == rows(), per_element());
|
||||
BOOST_TEST(result.affected_rows() == 0u);
|
||||
BOOST_TEST(result.warning_count() == 0u);
|
||||
BOOST_TEST(result.last_insert_id() == 0u);
|
||||
BOOST_TEST(result.info() == "");
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(query_empty_select_multifn, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(query_empty_select_multifn, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
// Issue query
|
||||
boost::mysql::execution_state st;
|
||||
conn.start_execution("SELECT * FROM empty_table", st);
|
||||
execution_state st;
|
||||
conn.async_start_execution("SELECT * FROM empty_table", st, as_netresult).validate_no_error();
|
||||
BOOST_TEST_REQUIRE(st.should_read_rows());
|
||||
validate_2fields_meta(st.meta(), "empty_table");
|
||||
|
||||
// Read eof
|
||||
auto rv = conn.read_some_rows(st);
|
||||
BOOST_TEST(rv.empty());
|
||||
auto rv = conn.async_read_some_rows(st, as_netresult).get();
|
||||
BOOST_TEST(rv == rows_view(), per_element());
|
||||
BOOST_TEST(st.affected_rows() == 0u);
|
||||
BOOST_TEST(st.warning_count() == 0u);
|
||||
BOOST_TEST(st.last_insert_id() == 0u);
|
||||
BOOST_TEST(st.info() == "");
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(query_insert, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(query_insert, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
start_transaction();
|
||||
|
||||
// Issue query
|
||||
constexpr const char*
|
||||
query = "INSERT INTO inserts_table (field_varchar, field_date) VALUES ('v0', '2010-10-11')";
|
||||
results result;
|
||||
conn.execute("INSERT INTO inserts_table (field_varchar, field_date) VALUES ('v0', '2010-10-11')", result);
|
||||
conn.async_execute(query, result, as_netresult).validate_no_error();
|
||||
|
||||
// Verify results
|
||||
BOOST_TEST(result.size() == 1u);
|
||||
BOOST_TEST(result.meta().empty());
|
||||
BOOST_TEST(result.rows().empty());
|
||||
BOOST_TEST(result.rows() == rows(), per_element());
|
||||
BOOST_TEST(result.affected_rows() == 1u);
|
||||
BOOST_TEST(result.warning_count() == 0u);
|
||||
BOOST_TEST(result.last_insert_id() > 0u);
|
||||
BOOST_TEST(result.info() == "");
|
||||
|
||||
// Verify insertion took place
|
||||
conn.execute("SELECT COUNT(*) FROM inserts_table", result);
|
||||
BOOST_TEST(result.rows().at(0).at(0).as_int64() == 1);
|
||||
conn.async_execute("SELECT COUNT(*) FROM inserts_table", result, as_netresult).validate_no_error();
|
||||
BOOST_TEST(result.rows() == makerows(1, 1), per_element());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(query_update, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(query_update, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
start_transaction();
|
||||
|
||||
// Issue the query
|
||||
results result;
|
||||
conn.execute("UPDATE updates_table SET field_int = field_int+10", result);
|
||||
conn.async_execute("UPDATE updates_table SET field_int = field_int+10", result, as_netresult)
|
||||
.validate_no_error();
|
||||
|
||||
// Validate results
|
||||
BOOST_TEST(result.size() == 1u);
|
||||
BOOST_TEST(result.meta().empty());
|
||||
BOOST_TEST(result.rows().empty());
|
||||
BOOST_TEST(result.rows() == rows(), per_element());
|
||||
BOOST_TEST(result.affected_rows() == 2u); // there are 3 rows, but 1 has field_int = NULL
|
||||
BOOST_TEST(result.warning_count() == 0u);
|
||||
BOOST_TEST(result.last_insert_id() == 0u);
|
||||
BOOST_TEST(result.info() == "Rows matched: 3 Changed: 2 Warnings: 0");
|
||||
|
||||
// Validate it took effect
|
||||
conn.execute("SELECT field_int FROM updates_table WHERE field_varchar = 'f0'", result);
|
||||
BOOST_TEST(result.rows().at(0).at(0).as_int64() == 52); // initial value was 42
|
||||
conn.async_execute("SELECT field_int FROM updates_table WHERE field_varchar = 'f0'", result, as_netresult)
|
||||
.validate_no_error();
|
||||
BOOST_TEST(result.rows() == makerows(1, 52), per_element()); // initial value was 42
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(query_delete, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(query_delete, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
start_transaction();
|
||||
|
||||
// Issue the query
|
||||
results result;
|
||||
conn.execute("DELETE FROM updates_table", result);
|
||||
conn.async_execute("DELETE FROM updates_table", result, as_netresult).validate_no_error();
|
||||
|
||||
// Validate results
|
||||
BOOST_TEST(result.size() == 1u);
|
||||
BOOST_TEST(result.meta().empty());
|
||||
BOOST_TEST(result.rows().empty());
|
||||
BOOST_TEST(result.rows() == rows(), per_element());
|
||||
BOOST_TEST(result.affected_rows() == 3u);
|
||||
BOOST_TEST(result.warning_count() == 0u);
|
||||
BOOST_TEST(result.last_insert_id() == 0u);
|
||||
BOOST_TEST(result.info() == "");
|
||||
|
||||
// Validate it took effect
|
||||
conn.execute("SELECT COUNT(*) FROM updates_table", result);
|
||||
BOOST_TEST(result.rows().at(0).at(0).as_int64() == 0);
|
||||
conn.async_execute("SELECT COUNT(*) FROM updates_table", result, as_netresult).validate_no_error();
|
||||
BOOST_TEST(result.rows() == makerows(1, 0), per_element());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(statement_update, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(statement_update, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
start_transaction();
|
||||
|
||||
// Prepare the statement
|
||||
auto stmt = conn.prepare_statement("UPDATE updates_table SET field_int = ? WHERE field_varchar = ?");
|
||||
constexpr const char* sql = "UPDATE updates_table SET field_int = ? WHERE field_varchar = ?";
|
||||
auto stmt = conn.async_prepare_statement(sql, as_netresult).get();
|
||||
BOOST_TEST(stmt.num_params() == 2u);
|
||||
|
||||
// Execute it
|
||||
@ -144,43 +150,45 @@ BOOST_FIXTURE_TEST_CASE(statement_update, tcp_network_fixture)
|
||||
conn.execute(stmt.bind(200, "f0"), result);
|
||||
BOOST_TEST(result.size() == 1u);
|
||||
BOOST_TEST(result.meta().empty());
|
||||
BOOST_TEST(result.rows().empty());
|
||||
BOOST_TEST(result.rows() == rows(), per_element());
|
||||
BOOST_TEST(result.affected_rows() == 1u);
|
||||
BOOST_TEST(result.warning_count() == 0u);
|
||||
BOOST_TEST(result.last_insert_id() == 0u);
|
||||
BOOST_TEST(result.info() == "Rows matched: 1 Changed: 1 Warnings: 0");
|
||||
|
||||
// Verify that it took effect
|
||||
conn.execute("SELECT field_int FROM updates_table WHERE field_varchar = 'f0'", result);
|
||||
BOOST_TEST(result.rows().at(0).at(0).as_int64() == 200);
|
||||
conn.async_execute("SELECT field_int FROM updates_table WHERE field_varchar = 'f0'", result, as_netresult)
|
||||
.validate_no_error();
|
||||
BOOST_TEST(result.rows() == makerows(1, 200), per_element());
|
||||
|
||||
// Close the statement
|
||||
conn.close_statement(stmt);
|
||||
conn.async_close_statement(stmt, as_netresult).validate_no_error();
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(statement_delete, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(statement_delete, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
start_transaction();
|
||||
|
||||
// Prepare the statement
|
||||
auto stmt = conn.prepare_statement("DELETE FROM updates_table WHERE field_varchar = ?");
|
||||
constexpr const char* sql = "DELETE FROM updates_table WHERE field_varchar = ?";
|
||||
auto stmt = conn.async_prepare_statement(sql, as_netresult).get();
|
||||
BOOST_TEST(stmt.num_params() == 1u);
|
||||
|
||||
// Execute it
|
||||
results result;
|
||||
conn.execute(stmt.bind("f0"), result);
|
||||
conn.async_execute(stmt.bind("f0"), result, as_netresult).validate_no_error();
|
||||
BOOST_TEST(result.size() == 1u);
|
||||
BOOST_TEST(result.meta().empty());
|
||||
BOOST_TEST(result.rows().empty());
|
||||
BOOST_TEST(result.rows() == rows(), per_element());
|
||||
BOOST_TEST(result.affected_rows() == 1u);
|
||||
BOOST_TEST(result.warning_count() == 0u);
|
||||
BOOST_TEST(result.last_insert_id() == 0u);
|
||||
BOOST_TEST(result.info() == "");
|
||||
|
||||
// Validate it took effect
|
||||
conn.execute("SELECT COUNT(*) FROM updates_table", result);
|
||||
BOOST_TEST(result.rows().at(0).at(0).as_int64() == 2);
|
||||
conn.async_execute("SELECT COUNT(*) FROM updates_table", result, as_netresult).validate_no_error();
|
||||
BOOST_TEST(result.rows() == makerows(1, 2), per_element());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // test_crud
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <boost/mysql/metadata_mode.hpp>
|
||||
#include <boost/mysql/results.hpp>
|
||||
#include <boost/mysql/row.hpp>
|
||||
#include <boost/mysql/row_view.hpp>
|
||||
#include <boost/mysql/rows_view.hpp>
|
||||
#include <boost/mysql/static_results.hpp>
|
||||
#include <boost/mysql/tcp.hpp>
|
||||
@ -37,20 +38,22 @@
|
||||
#include <boost/optional/optional.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <stdint.h>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "test_common/create_basic.hpp"
|
||||
#include "test_common/network_result.hpp"
|
||||
#include "test_common/printing.hpp"
|
||||
#include "test_integration/any_connection_fixture.hpp"
|
||||
#include "test_integration/connect_params_builder.hpp"
|
||||
#include "test_integration/metadata_validator.hpp"
|
||||
#include "test_integration/server_features.hpp"
|
||||
#include "test_integration/tcp_network_fixture.hpp"
|
||||
|
||||
using namespace boost::mysql::test;
|
||||
using namespace boost::mysql;
|
||||
@ -84,22 +87,23 @@ const flagsvec no_flags{};
|
||||
|
||||
constexpr format_options opts{utf8mb4_charset, true};
|
||||
|
||||
struct database_types_fixture : tcp_network_fixture
|
||||
struct database_types_fixture : any_connection_fixture
|
||||
{
|
||||
database_types_fixture()
|
||||
{
|
||||
// Connect
|
||||
params.set_multi_queries(true);
|
||||
connect();
|
||||
connect(connect_params_builder().multi_queries(true).build());
|
||||
|
||||
// Sets the time_zone to a well known value, so we can deterministically read TIMESTAMPs
|
||||
// Sets also sql_mode to allow invalid dates
|
||||
results result;
|
||||
conn.execute(
|
||||
"SET session time_zone = '+02:00'; "
|
||||
"SET session sql_mode = 'ALLOW_INVALID_DATES'",
|
||||
result
|
||||
);
|
||||
conn.async_execute(
|
||||
"SET session time_zone = '+02:00'; "
|
||||
"SET session sql_mode = 'ALLOW_INVALID_DATES'",
|
||||
result,
|
||||
as_netresult
|
||||
)
|
||||
.validate_no_error();
|
||||
}
|
||||
};
|
||||
|
||||
@ -123,7 +127,7 @@ struct table_base
|
||||
}
|
||||
|
||||
virtual ~table_base() {}
|
||||
virtual void select_static(tcp_connection& conn) = 0;
|
||||
virtual void select_static(any_connection& conn) = 0;
|
||||
|
||||
void add_meta(
|
||||
std::string field,
|
||||
@ -136,95 +140,30 @@ struct table_base
|
||||
metas.emplace_back(name, std::move(field), type, std::move(flags), decimals, std::move(ignore_flags));
|
||||
}
|
||||
|
||||
void validate_rows(boost::mysql::rows_view actual_matrix) const
|
||||
void validate_rows(rows_view actual) const
|
||||
{
|
||||
// The matrix size is correct
|
||||
BOOST_TEST_REQUIRE(metas.size() == actual_matrix.num_columns());
|
||||
// Sort the expected rows as the database retrieves it
|
||||
std::vector<row> expected{rws.begin(), rws.end()};
|
||||
std::sort(expected.begin(), expected.end(), [](row_view r1, row_view r2) {
|
||||
return r1.at(0).as_string() < r2.at(0).as_string();
|
||||
});
|
||||
|
||||
// Build a map with the received rows, by ID
|
||||
std::unordered_map<std::string, boost::mysql::row_view> actual;
|
||||
for (const auto& row : actual_matrix)
|
||||
actual[std::string(row.at(0).as_string())] = row;
|
||||
|
||||
// Verify that all expected rows are there and match
|
||||
for (const auto& expected_row : rws)
|
||||
{
|
||||
auto id = expected_row.at(0).as_string();
|
||||
BOOST_TEST_CONTEXT("row_id=" << id)
|
||||
{
|
||||
auto it = actual.find(std::string(id));
|
||||
if (it == actual.end())
|
||||
{
|
||||
BOOST_TEST(false, "Row not found in the actual table");
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_TEST(
|
||||
expected_row == it->second,
|
||||
"\nlhs: " << expected_row << "\nrhs: " << it->second
|
||||
);
|
||||
actual.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that there are no additional rows
|
||||
for (const auto& additional_row : actual)
|
||||
{
|
||||
BOOST_TEST_CONTEXT("row_id=" << additional_row.first)
|
||||
{
|
||||
BOOST_TEST(false, "Row was found in the table but not declared in database_types");
|
||||
}
|
||||
}
|
||||
// Compare
|
||||
BOOST_TEST(actual == expected, boost::test_tools::per_element());
|
||||
}
|
||||
|
||||
std::string select_sql() const { return format_sql(opts, "SELECT * FROM {:i} ORDER BY id", name); }
|
||||
|
||||
std::string insert_sql_stmt() const
|
||||
{
|
||||
format_context ctx(opts);
|
||||
format_sql_to(ctx, "INSERT INTO {:i} VALUES (", name);
|
||||
for (std::size_t i = 0; i < metas.size(); ++i)
|
||||
{
|
||||
if (i == 0)
|
||||
ctx.append_raw("?");
|
||||
else
|
||||
ctx.append_raw(", ?");
|
||||
}
|
||||
ctx.append_raw(")");
|
||||
return std::move(ctx).get().value();
|
||||
auto format_fn = [](const meta_validator&, format_context_base& ctx) { ctx.append_raw("?"); };
|
||||
return format_sql(opts, "INSERT INTO {:i} VALUES ({})", name, sequence(metas, format_fn));
|
||||
}
|
||||
|
||||
std::string insert_sql() const
|
||||
{
|
||||
format_context ctx(opts);
|
||||
format_sql_to(ctx, "INSERT INTO {:i} VALUES ", name);
|
||||
|
||||
bool is_first_row = true;
|
||||
for (const auto& r : rws)
|
||||
{
|
||||
// Comma separator between rows
|
||||
if (!is_first_row)
|
||||
ctx.append_raw(", ");
|
||||
is_first_row = false;
|
||||
|
||||
// Actual row
|
||||
ctx.append_raw("(");
|
||||
bool is_first_field = true;
|
||||
for (const field_view fv : r)
|
||||
{
|
||||
// Comma separator between fields
|
||||
if (!is_first_field)
|
||||
ctx.append_raw(", ");
|
||||
is_first_field = false;
|
||||
|
||||
// Actual field
|
||||
ctx.append_value(fv);
|
||||
}
|
||||
ctx.append_raw(")");
|
||||
}
|
||||
|
||||
return std::move(ctx).get().value();
|
||||
auto format_fn = [](row_view r, format_context_base& ctx) { format_sql_to(ctx, "({})", r); };
|
||||
return format_sql(opts, "INSERT INTO {:i} VALUES {}", name, sequence(rws, format_fn));
|
||||
}
|
||||
|
||||
std::string delete_sql() const { return format_sql(opts, "DELETE FROM {:i}", name); }
|
||||
@ -255,53 +194,24 @@ public:
|
||||
}
|
||||
|
||||
#ifdef BOOST_MYSQL_CXX14
|
||||
void select_static(tcp_connection& conn) override
|
||||
void select_static(any_connection& conn) override
|
||||
{
|
||||
// Execute the query
|
||||
static_results<StaticRow> result;
|
||||
conn.execute(select_sql(), result);
|
||||
conn.async_execute(select_sql(), result, as_netresult).validate_no_error();
|
||||
|
||||
// Validate metadata
|
||||
validate_meta(result.meta(), metas);
|
||||
|
||||
// Validate the rows
|
||||
// Build a map with the received rows, by ID
|
||||
std::unordered_map<std::string, StaticRow> actual;
|
||||
for (const auto& row : result.rows())
|
||||
actual[row.id] = row;
|
||||
|
||||
// Verify that all expected rows are there and match
|
||||
for (const auto& expected_row : static_rows_)
|
||||
{
|
||||
BOOST_TEST_CONTEXT("row_id=" << expected_row.id)
|
||||
{
|
||||
auto it = actual.find(expected_row.id);
|
||||
if (it == actual.end())
|
||||
{
|
||||
BOOST_TEST(false, "Row not found in the actual table");
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_TEST(
|
||||
expected_row == it->second,
|
||||
"\nlhs: " << expected_row << "\nrhs: " << it->second
|
||||
);
|
||||
actual.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that there are no additional rows
|
||||
for (const auto& additional_row : actual)
|
||||
{
|
||||
BOOST_TEST_CONTEXT("row_id=" << additional_row.first)
|
||||
{
|
||||
BOOST_TEST(false, "Row was found in the table but not declared in database_types");
|
||||
}
|
||||
}
|
||||
std::vector<StaticRow> expected{static_rows_};
|
||||
std::sort(expected.begin(), expected.end(), [](const StaticRow& r1, const StaticRow& r2) {
|
||||
return r1.id < r2.id;
|
||||
});
|
||||
BOOST_TEST(result.rows() == expected, boost::test_tools::per_element());
|
||||
}
|
||||
#else
|
||||
void select_static(tcp_connection&) override {}
|
||||
void select_static(any_connection&) override {}
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -1038,7 +948,7 @@ BOOST_FIXTURE_TEST_CASE(query_read, database_types_fixture)
|
||||
{
|
||||
// Execute the query
|
||||
results result;
|
||||
conn.execute(table->select_sql(), result);
|
||||
conn.async_execute(table->select_sql(), result, as_netresult).validate_no_error();
|
||||
|
||||
// Validate the received contents
|
||||
validate_meta(result.meta(), table->metas);
|
||||
@ -1057,13 +967,13 @@ BOOST_FIXTURE_TEST_CASE(sql_format_query_write, database_types_fixture)
|
||||
{
|
||||
// Remove all contents from the table
|
||||
results result;
|
||||
conn.execute(table->delete_sql(), result);
|
||||
conn.async_execute(table->delete_sql(), result, as_netresult).validate_no_error();
|
||||
|
||||
// Insert all the contents again
|
||||
conn.execute(table->insert_sql(), result);
|
||||
conn.async_execute(table->insert_sql(), result, as_netresult).validate_no_error();
|
||||
|
||||
// Query them again and verify the insertion was okay
|
||||
conn.execute(table->select_sql(), result);
|
||||
conn.async_execute(table->select_sql(), result, as_netresult).validate_no_error();
|
||||
validate_meta(result.meta(), table->metas);
|
||||
table->validate_rows(result.rows());
|
||||
}
|
||||
@ -1077,11 +987,11 @@ BOOST_FIXTURE_TEST_CASE(statement_read, database_types_fixture)
|
||||
BOOST_TEST_CONTEXT(table->name)
|
||||
{
|
||||
// Prepare the statement
|
||||
auto stmt = conn.prepare_statement(table->select_sql());
|
||||
auto stmt = conn.async_prepare_statement(table->select_sql(), as_netresult).get();
|
||||
|
||||
// Execute it with the provided parameters
|
||||
results result;
|
||||
conn.execute(stmt.bind(), result);
|
||||
conn.async_execute(stmt.bind(), result, as_netresult).validate_no_error();
|
||||
|
||||
// Validate the received contents
|
||||
validate_meta(result.meta(), table->metas);
|
||||
@ -1099,21 +1009,22 @@ BOOST_FIXTURE_TEST_CASE(statement_write, database_types_fixture)
|
||||
BOOST_TEST_CONTEXT(table->name)
|
||||
{
|
||||
// Prepare the statements
|
||||
auto insert_stmt = conn.prepare_statement(table->insert_sql_stmt());
|
||||
auto query_stmt = conn.prepare_statement(table->select_sql());
|
||||
auto insert_stmt = conn.async_prepare_statement(table->insert_sql_stmt(), as_netresult).get();
|
||||
auto query_stmt = conn.async_prepare_statement(table->select_sql(), as_netresult).get();
|
||||
|
||||
// Remove all contents from the table
|
||||
results result;
|
||||
conn.execute(table->delete_sql(), result);
|
||||
conn.async_execute(table->delete_sql(), result, as_netresult).validate_no_error();
|
||||
|
||||
// Insert all the contents again
|
||||
for (const auto& row : table->rws)
|
||||
{
|
||||
conn.execute(insert_stmt.bind(row.begin(), row.end()), result);
|
||||
conn.async_execute(insert_stmt.bind(row.begin(), row.end()), result, as_netresult)
|
||||
.validate_no_error();
|
||||
}
|
||||
|
||||
// Query them again and verify the insertion was okay
|
||||
conn.execute(query_stmt.bind(), result);
|
||||
conn.async_execute(query_stmt.bind(), result, as_netresult).validate_no_error();
|
||||
validate_meta(result.meta(), table->metas);
|
||||
table->validate_rows(result.rows());
|
||||
}
|
||||
|
@ -5,57 +5,46 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <boost/mysql/diagnostics.hpp>
|
||||
#include <boost/mysql/error_categories.hpp>
|
||||
#include <boost/mysql/error_code.hpp>
|
||||
#include <boost/mysql/mariadb_server_errc.hpp>
|
||||
#include <boost/mysql/mysql_server_errc.hpp>
|
||||
#include <boost/mysql/results.hpp>
|
||||
|
||||
#include "test_common/create_diagnostics.hpp"
|
||||
#include "test_common/network_result.hpp"
|
||||
#include "test_integration/any_connection_fixture.hpp"
|
||||
#include "test_integration/server_features.hpp"
|
||||
#include "test_integration/tcp_network_fixture.hpp"
|
||||
|
||||
using namespace boost::mysql::test;
|
||||
using boost::mysql::diagnostics;
|
||||
using boost::mysql::error_code;
|
||||
using boost::mysql::results;
|
||||
using namespace boost::mysql;
|
||||
|
||||
namespace {
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(test_db_specific)
|
||||
|
||||
BOOST_TEST_DECORATOR(*run_if(&server_features::regex_error_codes))
|
||||
BOOST_FIXTURE_TEST_CASE(mysql_specific_error_code, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(mysql_specific_error_code, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
error_code ec;
|
||||
diagnostics diag;
|
||||
results result;
|
||||
|
||||
// This is reported as a common, less descriptive error in MySQL5 and MariaDB
|
||||
conn.execute("select * from one_row_table where field_varchar regexp '(('", result, ec, diag);
|
||||
error_code expected_ec(
|
||||
boost::mysql::mysql_server_errc::er_regexp_mismatched_paren,
|
||||
boost::mysql::get_mysql_server_category()
|
||||
);
|
||||
BOOST_TEST(ec == expected_ec);
|
||||
error_code expected_ec(mysql_server_errc::er_regexp_mismatched_paren, get_mysql_server_category());
|
||||
conn.async_execute("select * from one_row_table where field_varchar regexp '(('", result, as_netresult)
|
||||
.validate_error(expected_ec, create_server_diag("Mismatched parenthesis in regular expression."));
|
||||
}
|
||||
|
||||
BOOST_TEST_DECORATOR(*run_if(&server_features::dup_query_error_codes))
|
||||
BOOST_FIXTURE_TEST_CASE(mariadb_specific_error_code, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(mariadb_specific_error_code, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
error_code ec;
|
||||
diagnostics diag;
|
||||
results result;
|
||||
|
||||
// This is reported as a common error in MySQL5 and MySQL8
|
||||
conn.execute("WITH abc AS (SELECT 1), abc as (SELECT 2) SELECT * FROM abc", result, ec, diag);
|
||||
error_code expected_ec(
|
||||
boost::mysql::mariadb_server_errc::er_dup_query_name,
|
||||
boost::mysql::get_mariadb_server_category()
|
||||
);
|
||||
BOOST_TEST(ec == expected_ec);
|
||||
error_code expected_ec(mariadb_server_errc::er_dup_query_name, get_mariadb_server_category());
|
||||
conn.async_execute("WITH abc AS (SELECT 1), abc as (SELECT 2) SELECT * FROM abc", result, as_netresult)
|
||||
.validate_error(expected_ec, create_server_diag("Duplicate query name `abc` in WITH clause"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
124
test/integration/test/execution_requests.cpp
Normal file
124
test/integration/test/execution_requests.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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/client_errc.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/field_view.hpp>
|
||||
#include <boost/mysql/results.hpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <forward_list>
|
||||
|
||||
#include "test_common/create_basic.hpp"
|
||||
#include "test_common/network_result.hpp"
|
||||
#include "test_integration/any_connection_fixture.hpp"
|
||||
|
||||
using namespace boost::mysql;
|
||||
using namespace boost::mysql::test;
|
||||
using boost::test_tools::per_element;
|
||||
|
||||
// Cover all possible execution requests for execute() and async_execute()
|
||||
|
||||
namespace {
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(test_execution_requests)
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(query, any_connection_fixture)
|
||||
{
|
||||
// Setup
|
||||
connect();
|
||||
results r;
|
||||
execution_state st;
|
||||
|
||||
// execute
|
||||
conn.async_execute("SELECT 1", r, as_netresult).validate_no_error();
|
||||
BOOST_TEST(r.rows() == makerows(1, 1), per_element());
|
||||
|
||||
// start execution
|
||||
conn.async_start_execution("SELECT 1", st, as_netresult).validate_no_error();
|
||||
auto rws = conn.async_read_some_rows(st, as_netresult).get();
|
||||
BOOST_TEST(rws == makerows(1, 1), per_element());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(stmt_tuple, any_connection_fixture)
|
||||
{
|
||||
// Setup
|
||||
connect();
|
||||
results r;
|
||||
execution_state st;
|
||||
auto stmt = conn.async_prepare_statement("SELECT ?", as_netresult).get();
|
||||
BOOST_TEST_REQUIRE(stmt.num_params() == 1u);
|
||||
|
||||
// execute
|
||||
conn.async_execute(stmt.bind("42"), r, as_netresult).validate_no_error();
|
||||
BOOST_TEST(r.rows() == makerows(1, "42"), per_element());
|
||||
|
||||
// start execution
|
||||
conn.async_start_execution(stmt.bind(90), st, as_netresult).validate_no_error();
|
||||
auto rws = conn.async_read_some_rows(st, as_netresult).get();
|
||||
BOOST_TEST(rws == makerows(1, 90), per_element());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(stmt_tuple_error, any_connection_fixture)
|
||||
{
|
||||
// Setup
|
||||
connect();
|
||||
results r;
|
||||
execution_state st;
|
||||
auto stmt = conn.async_prepare_statement("SELECT ?", as_netresult).get();
|
||||
BOOST_TEST_REQUIRE(stmt.num_params() == 1u);
|
||||
|
||||
// execute
|
||||
conn.async_execute(stmt.bind("42", 200), r, as_netresult).validate_error(client_errc::wrong_num_params);
|
||||
|
||||
// start execution
|
||||
conn.async_start_execution(stmt.bind(), st, as_netresult).validate_error(client_errc::wrong_num_params);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(stmt_range, any_connection_fixture)
|
||||
{
|
||||
// Setup
|
||||
connect();
|
||||
results r;
|
||||
execution_state st;
|
||||
std::forward_list<field_view> params{field_view(42), field_view("abc")};
|
||||
auto stmt = conn.async_prepare_statement("SELECT ?, ?", as_netresult).get();
|
||||
BOOST_TEST_REQUIRE(stmt.num_params() == 2u);
|
||||
|
||||
// execute
|
||||
conn.async_execute(stmt.bind(params.begin(), params.end()), r, as_netresult).validate_no_error();
|
||||
BOOST_TEST(r.rows() == makerows(2, 42, "abc"), per_element());
|
||||
|
||||
// start execution
|
||||
conn.async_start_execution(stmt.bind(params.begin(), params.end()), st, as_netresult).validate_no_error();
|
||||
auto rws = conn.async_read_some_rows(st, as_netresult).get();
|
||||
BOOST_TEST(rws == makerows(2, 42, "abc"), per_element());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(stmt_range_error, any_connection_fixture)
|
||||
{
|
||||
// Setup
|
||||
connect();
|
||||
results r;
|
||||
execution_state st;
|
||||
std::forward_list<field_view> params{field_view(42)};
|
||||
auto stmt = conn.async_prepare_statement("SELECT ?, ?", as_netresult).get();
|
||||
BOOST_TEST_REQUIRE(stmt.num_params() == 2u);
|
||||
|
||||
// execute
|
||||
conn.async_execute(stmt.bind(params.begin(), params.end()), r, as_netresult)
|
||||
.validate_error(client_errc::wrong_num_params);
|
||||
|
||||
// start execution
|
||||
conn.async_start_execution(stmt.bind(params.begin(), params.end()), st, as_netresult)
|
||||
.validate_error(client_errc::wrong_num_params);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
} // namespace
|
File diff suppressed because it is too large
Load Diff
@ -6,40 +6,39 @@
|
||||
//
|
||||
|
||||
#include <boost/mysql/common_server_errc.hpp>
|
||||
#include <boost/mysql/diagnostics.hpp>
|
||||
#include <boost/mysql/error_code.hpp>
|
||||
#include <boost/mysql/results.hpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "test_common/create_basic.hpp"
|
||||
#include "test_common/network_result.hpp"
|
||||
#include "test_common/printing.hpp"
|
||||
#include "test_common/validate_string_contains.hpp"
|
||||
#include "test_integration/common.hpp"
|
||||
#include "test_integration/tcp_network_fixture.hpp"
|
||||
#include "test_integration/any_connection_fixture.hpp"
|
||||
#include "test_integration/connect_params_builder.hpp"
|
||||
#include "test_integration/metadata_validator.hpp"
|
||||
#include "test_integration/tcp_connection_fixture.hpp"
|
||||
|
||||
using namespace boost::mysql::test;
|
||||
using namespace boost::mysql;
|
||||
using boost::test_tools::per_element;
|
||||
|
||||
namespace {
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(test_multi_queries)
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(empty_results, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(empty_results, any_connection_fixture)
|
||||
{
|
||||
params.set_multi_queries(true);
|
||||
connect();
|
||||
// Setup
|
||||
constexpr const char* query =
|
||||
"INSERT INTO inserts_table (field_varchar) VALUES ('abc');"
|
||||
"INSERT INTO inserts_table (field_varchar) VALUES ('def');"
|
||||
"DELETE FROM updates_table";
|
||||
connect(connect_params_builder().disable_ssl().multi_queries(true).build());
|
||||
start_transaction();
|
||||
|
||||
// Run the query
|
||||
results result;
|
||||
conn.execute(
|
||||
R"%(
|
||||
INSERT INTO inserts_table (field_varchar) VALUES ('abc');
|
||||
INSERT INTO inserts_table (field_varchar) VALUES ('def');
|
||||
DELETE FROM updates_table
|
||||
)%",
|
||||
result
|
||||
);
|
||||
conn.async_execute(query, result, as_netresult).validate_no_error();
|
||||
|
||||
// Validate results
|
||||
BOOST_TEST_REQUIRE(result.size() == 3u);
|
||||
@ -69,21 +68,19 @@ BOOST_FIXTURE_TEST_CASE(empty_results, tcp_network_fixture)
|
||||
BOOST_TEST(!result[2].is_out_params());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(data_results, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(data_results, any_connection_fixture)
|
||||
{
|
||||
params.set_multi_queries(true);
|
||||
connect();
|
||||
// Setup
|
||||
connect(connect_params_builder().disable_ssl().multi_queries(true).build());
|
||||
start_transaction();
|
||||
constexpr const char* query =
|
||||
"SELECT * FROM one_row_table;"
|
||||
"SELECT * FROM empty_table;"
|
||||
"DELETE FROM updates_table";
|
||||
|
||||
// Execute
|
||||
results result;
|
||||
conn.execute(
|
||||
R"%(
|
||||
SELECT * FROM one_row_table;
|
||||
SELECT * FROM empty_table;
|
||||
DELETE FROM updates_table
|
||||
)%",
|
||||
result
|
||||
);
|
||||
conn.async_execute(query, result, as_netresult).validate_no_error();
|
||||
|
||||
// Validate results
|
||||
BOOST_TEST_REQUIRE(result.size() == 3u);
|
||||
@ -113,19 +110,47 @@ BOOST_FIXTURE_TEST_CASE(data_results, tcp_network_fixture)
|
||||
BOOST_TEST(!result[2].is_out_params());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(error_not_enabled, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(error_not_enabled, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
// Setup
|
||||
connect(connect_params_builder().disable_ssl().build());
|
||||
|
||||
// Execute fails
|
||||
results result;
|
||||
error_code err;
|
||||
diagnostics diag;
|
||||
|
||||
conn.execute("SELECT 1; SELECT 2", result, err, diag);
|
||||
BOOST_TEST(err == common_server_errc::er_parse_error);
|
||||
validate_string_contains(diag.server_message(), {"you have an error in your sql syntax"});
|
||||
conn.async_execute("SELECT 1; SELECT 2", result, as_netresult)
|
||||
.validate_error_contains(
|
||||
common_server_errc::er_parse_error,
|
||||
{"you have an error in your sql syntax"}
|
||||
);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // test_crud
|
||||
// Spotcheck: old connection can do multi-queries
|
||||
BOOST_FIXTURE_TEST_CASE(tcp_connection_enable, tcp_connection_fixture)
|
||||
{
|
||||
connect(connect_params_builder().multi_queries(true).build_hparams());
|
||||
|
||||
// Execute succeeds
|
||||
results result;
|
||||
conn.async_execute("SELECT 1; SELECT 2", result, as_netresult).validate_no_error();
|
||||
BOOST_TEST_REQUIRE(result.size() == 2u);
|
||||
BOOST_TEST(result.at(0).rows() == makerows(1, 1), per_element());
|
||||
BOOST_TEST(result.at(1).rows() == makerows(1, 2), per_element());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(tcp_connection_disabled, tcp_connection_fixture)
|
||||
{
|
||||
// Setup
|
||||
connect();
|
||||
|
||||
// Execute succeeds
|
||||
results result;
|
||||
conn.async_execute("SELECT 1; SELECT 2", result, as_netresult)
|
||||
.validate_error_contains(
|
||||
common_server_errc::er_parse_error,
|
||||
{"you have an error in your sql syntax"}
|
||||
);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
} // namespace
|
||||
|
@ -5,26 +5,20 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <boost/mysql/any_connection.hpp>
|
||||
#include <boost/mysql/character_set.hpp>
|
||||
#include <boost/mysql/client_errc.hpp>
|
||||
#include <boost/mysql/common_server_errc.hpp>
|
||||
#include <boost/mysql/error_code.hpp>
|
||||
#include <boost/mysql/error_with_diagnostics.hpp>
|
||||
#include <boost/mysql/pipeline.hpp>
|
||||
#include <boost/mysql/ssl_mode.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "test_common/create_basic.hpp"
|
||||
#include "test_common/create_diagnostics.hpp"
|
||||
#include "test_common/netfun_helpers.hpp"
|
||||
#include "test_common/network_result.hpp"
|
||||
#include "test_common/printing.hpp"
|
||||
#include "test_integration/common.hpp"
|
||||
#include "test_integration/any_connection_fixture.hpp"
|
||||
|
||||
using namespace boost::mysql::test;
|
||||
using namespace boost::mysql;
|
||||
@ -35,17 +29,10 @@ namespace {
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(test_pipeline)
|
||||
|
||||
struct fixture
|
||||
{
|
||||
asio::io_context ctx;
|
||||
any_connection conn{ctx};
|
||||
|
||||
fixture() { conn.connect(default_connect_params(ssl_mode::disable)); }
|
||||
};
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(success, fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(success, any_connection_fixture)
|
||||
{
|
||||
// Setup
|
||||
connect();
|
||||
pipeline_request req;
|
||||
std::vector<stage_response> res;
|
||||
|
||||
@ -55,9 +42,7 @@ BOOST_FIXTURE_TEST_CASE(success, fixture)
|
||||
.add_prepare_statement("SELECT * FROM one_row_table WHERE id = ?");
|
||||
|
||||
// Run it
|
||||
auto result = create_initial_netresult<void>();
|
||||
conn.run_pipeline(req, res, result.err, *result.diag);
|
||||
result.validate_no_error();
|
||||
conn.async_run_pipeline(req, res, as_netresult).validate_no_error();
|
||||
|
||||
// Check results
|
||||
BOOST_TEST_REQUIRE(res.size() == 3u);
|
||||
@ -78,9 +63,7 @@ BOOST_FIXTURE_TEST_CASE(success, fixture)
|
||||
.add_close_statement(stmt);
|
||||
|
||||
// Run it
|
||||
result = create_initial_netresult<void>();
|
||||
conn.run_pipeline(req, res, result.err, *result.diag);
|
||||
result.validate_no_error();
|
||||
conn.async_run_pipeline(req, res, as_netresult).validate_no_error();
|
||||
|
||||
// Check results
|
||||
BOOST_TEST_REQUIRE(res.size() == 4u);
|
||||
@ -96,9 +79,10 @@ BOOST_FIXTURE_TEST_CASE(success, fixture)
|
||||
BOOST_TEST(!res[3].has_statement());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(errors, fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(errors, any_connection_fixture)
|
||||
{
|
||||
// Setup
|
||||
connect();
|
||||
pipeline_request req;
|
||||
std::vector<stage_response> res;
|
||||
|
||||
@ -111,12 +95,11 @@ BOOST_FIXTURE_TEST_CASE(errors, fixture)
|
||||
.add_execute("SELECT 'abc'"); // OK
|
||||
|
||||
// Run it. The result of the operation is the first encountered error
|
||||
auto result = create_initial_netresult<void>();
|
||||
conn.run_pipeline(req, res, result.err, *result.diag);
|
||||
result.validate_error_exact(
|
||||
common_server_errc::er_no_such_table,
|
||||
"Table 'boost_mysql_integtests.bad_table' doesn't exist"
|
||||
);
|
||||
conn.async_run_pipeline(req, res, as_netresult)
|
||||
.validate_error(
|
||||
common_server_errc::er_no_such_table,
|
||||
"Table 'boost_mysql_integtests.bad_table' doesn't exist"
|
||||
);
|
||||
|
||||
// Check results
|
||||
BOOST_TEST_REQUIRE(res.size() == 6u);
|
||||
|
@ -6,140 +6,133 @@
|
||||
//
|
||||
|
||||
#include <boost/mysql/client_errc.hpp>
|
||||
#include <boost/mysql/diagnostics.hpp>
|
||||
#include <boost/mysql/error_code.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/field.hpp>
|
||||
#include <boost/mysql/field_view.hpp>
|
||||
#include <boost/mysql/results.hpp>
|
||||
#include <boost/mysql/row.hpp>
|
||||
#include <boost/mysql/row_view.hpp>
|
||||
#include <boost/mysql/tcp.hpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "test_common/create_basic.hpp"
|
||||
#include "test_common/network_result.hpp"
|
||||
#include "test_common/printing.hpp"
|
||||
#include "test_integration/common.hpp"
|
||||
#include "test_integration/tcp_network_fixture.hpp"
|
||||
#include "test_integration/any_connection_fixture.hpp"
|
||||
#include "test_integration/metadata_validator.hpp"
|
||||
|
||||
using namespace boost::mysql::test;
|
||||
using namespace boost::mysql;
|
||||
using boost::test_tools::per_element;
|
||||
|
||||
namespace {
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(test_prepared_statements)
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(multiple_executions, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(multiple_executions, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
results result;
|
||||
|
||||
// Prepare a statement
|
||||
auto stmt = conn.prepare_statement("SELECT * FROM two_rows_table WHERE id = ? OR field_varchar = ?");
|
||||
constexpr const char* sql = "SELECT * FROM two_rows_table WHERE id = ? OR field_varchar = ?";
|
||||
auto stmt = conn.async_prepare_statement(sql, as_netresult).get();
|
||||
|
||||
// Execute it. Only one row will be returned (because of the id)
|
||||
results result;
|
||||
conn.execute(stmt.bind(1, "non_existent"), result);
|
||||
conn.async_execute(stmt.bind(1, "non_existent"), result, as_netresult).validate_no_error();
|
||||
validate_2fields_meta(result.meta(), "two_rows_table");
|
||||
BOOST_TEST_REQUIRE(result.rows().size() == 1u);
|
||||
BOOST_TEST((result.rows()[0] == makerow(1, "f0")));
|
||||
BOOST_TEST(result.rows() == makerows(2, 1, "f0"), per_element());
|
||||
|
||||
// Execute it again, but with different values. This time, two rows are returned
|
||||
conn.execute(stmt.bind(1, "f1"), result);
|
||||
conn.async_execute(stmt.bind(1, "f1"), result, as_netresult).validate_no_error();
|
||||
validate_2fields_meta(result.meta(), "two_rows_table");
|
||||
BOOST_TEST_REQUIRE(result.rows().size() == 2u);
|
||||
BOOST_TEST((result.rows()[0] == makerow(1, "f0")));
|
||||
BOOST_TEST((result.rows()[1] == makerow(2, "f1")));
|
||||
BOOST_TEST(result.rows() == makerows(2, 1, "f0", 2, "f1"), per_element());
|
||||
|
||||
// Close it
|
||||
conn.close_statement(stmt);
|
||||
conn.async_close_statement(stmt, as_netresult).validate_no_error();
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(multiple_statements, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(multiple_statements, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
start_transaction();
|
||||
results result;
|
||||
|
||||
// Prepare an update and a select
|
||||
results result;
|
||||
auto stmt_update = conn.prepare_statement("UPDATE updates_table SET field_int = ? WHERE field_varchar = ?"
|
||||
);
|
||||
auto stmt_select = conn.prepare_statement("SELECT field_int FROM updates_table WHERE field_varchar = ?");
|
||||
constexpr const char* update_sql = "UPDATE updates_table SET field_int = ? WHERE field_varchar = ?";
|
||||
constexpr const char* select_sql = "SELECT field_int FROM updates_table WHERE field_varchar = ?";
|
||||
auto stmt_update = conn.async_prepare_statement(update_sql, as_netresult).get();
|
||||
auto stmt_select = conn.async_prepare_statement(select_sql, as_netresult).get();
|
||||
BOOST_TEST(stmt_update.num_params() == 2u);
|
||||
BOOST_TEST(stmt_select.num_params() == 1u);
|
||||
BOOST_TEST(stmt_update.id() != stmt_select.id());
|
||||
|
||||
// Execute update
|
||||
conn.execute(stmt_update.bind(210, "f0"), result);
|
||||
conn.async_execute(stmt_update.bind(210, "f0"), result, as_netresult).validate_no_error();
|
||||
BOOST_TEST(result.rows() == rows(), per_element());
|
||||
BOOST_TEST(result.meta().size() == 0u);
|
||||
BOOST_TEST(result.affected_rows() == 1u);
|
||||
|
||||
// Execute select
|
||||
conn.execute(stmt_select.bind("f0"), result);
|
||||
BOOST_TEST(result.rows().size() == 1u);
|
||||
BOOST_TEST(result.rows()[0] == makerow(210));
|
||||
conn.async_execute(stmt_select.bind("f0"), result, as_netresult).validate_no_error();
|
||||
BOOST_TEST(result.rows() == makerows(1, 210), per_element());
|
||||
|
||||
// Execute update again
|
||||
conn.execute(stmt_update.bind(220, "f0"), result);
|
||||
conn.async_execute(stmt_update.bind(220, "f0"), result, as_netresult).validate_no_error();
|
||||
BOOST_TEST(result.rows() == rows(), per_element());
|
||||
BOOST_TEST(result.meta().size() == 0u);
|
||||
BOOST_TEST(result.affected_rows() == 1u);
|
||||
|
||||
// Update no longer needed, close it
|
||||
conn.close_statement(stmt_update);
|
||||
conn.async_close_statement(stmt_update, as_netresult).validate_no_error();
|
||||
|
||||
// Execute select again
|
||||
conn.execute(stmt_select.bind("f0"), result);
|
||||
BOOST_TEST(result.rows().size() == 1u);
|
||||
BOOST_TEST(result.rows()[0] == makerow(220));
|
||||
conn.async_execute(stmt_select.bind("f0"), result, as_netresult).validate_no_error();
|
||||
BOOST_TEST(result.rows() == makerows(1, 220), per_element());
|
||||
|
||||
// Close select
|
||||
conn.close_statement(stmt_select);
|
||||
conn.async_close_statement(stmt_select, as_netresult).validate_no_error();
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(statement_without_params, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(statement_without_params, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
// Prepare the statement
|
||||
auto stmt = conn.prepare_statement("SELECT * FROM empty_table");
|
||||
auto stmt = conn.async_prepare_statement("SELECT * FROM empty_table", as_netresult).get();
|
||||
BOOST_TEST(stmt.valid());
|
||||
BOOST_TEST(stmt.num_params() == 0u);
|
||||
|
||||
// Execute doesn't error
|
||||
results result;
|
||||
conn.execute(stmt.bind(), result);
|
||||
conn.async_execute(stmt.bind(), result, as_netresult).validate_no_error();
|
||||
validate_2fields_meta(result.meta(), "empty_table");
|
||||
BOOST_TEST(result.rows().size() == 0u);
|
||||
BOOST_TEST(result.rows() == rows(), per_element());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(wrong_num_params, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(wrong_num_params, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
// Prepare the statement
|
||||
auto stmt = conn.prepare_statement("SELECT * FROM empty_table");
|
||||
auto stmt = conn.async_prepare_statement("SELECT * FROM empty_table", as_netresult).get();
|
||||
BOOST_TEST(stmt.valid());
|
||||
BOOST_TEST(stmt.num_params() == 0u);
|
||||
|
||||
// Execute fails appropriately
|
||||
error_code ec;
|
||||
diagnostics diag;
|
||||
results result;
|
||||
conn.execute(stmt.bind(42), result, ec, diag);
|
||||
BOOST_TEST(ec == client_errc::wrong_num_params);
|
||||
conn.async_execute(stmt.bind(42), result, as_netresult).validate_error(client_errc::wrong_num_params);
|
||||
}
|
||||
|
||||
// Note: multifn query is already covered in spotchecks
|
||||
BOOST_FIXTURE_TEST_CASE(multifn, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(multifn, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
// Prepare the statement
|
||||
auto stmt = conn.prepare_statement("SELECT * FROM three_rows_table");
|
||||
auto stmt = conn.async_prepare_statement("SELECT * FROM three_rows_table", as_netresult).get();
|
||||
|
||||
// Execute it
|
||||
execution_state st;
|
||||
conn.start_execution(stmt.bind(), st);
|
||||
conn.async_start_execution(stmt.bind(), st, as_netresult).validate_no_error();
|
||||
BOOST_TEST_REQUIRE(st.should_read_rows());
|
||||
|
||||
// We don't know how many rows there will be in each batch,
|
||||
@ -149,23 +142,28 @@ BOOST_FIXTURE_TEST_CASE(multifn, tcp_network_fixture)
|
||||
while (st.should_read_rows() && call_count <= 4)
|
||||
{
|
||||
++call_count;
|
||||
for (row_view rv : conn.read_some_rows(st))
|
||||
for (row_view rv : conn.async_read_some_rows(st, as_netresult).get())
|
||||
all_rows.emplace_back(rv);
|
||||
}
|
||||
|
||||
// Verify rows
|
||||
BOOST_TEST_REQUIRE(all_rows.size() == 3u);
|
||||
BOOST_TEST(all_rows[0] == makerow(1, "f0"));
|
||||
BOOST_TEST(all_rows[1] == makerow(2, "f1"));
|
||||
BOOST_TEST(all_rows[2] == makerow(3, "f2"));
|
||||
BOOST_TEST(all_rows == makerows(2, 1, "f0", 2, "f1", 3, "f2"), per_element());
|
||||
|
||||
// Verify eof
|
||||
validate_eof(st);
|
||||
BOOST_TEST_REQUIRE(st.complete());
|
||||
BOOST_TEST(st.affected_rows() == 0u);
|
||||
BOOST_TEST(st.warning_count() == 0u);
|
||||
BOOST_TEST(st.last_insert_id() == 0u);
|
||||
BOOST_TEST(st.info() == "");
|
||||
|
||||
// Reading again does nothing
|
||||
auto rows = conn.read_some_rows(st);
|
||||
BOOST_TEST(rows.empty());
|
||||
validate_eof(st);
|
||||
auto rws = conn.async_read_some_rows(st, as_netresult).get();
|
||||
BOOST_TEST(rws == rows(), per_element());
|
||||
BOOST_TEST_REQUIRE(st.complete());
|
||||
BOOST_TEST(st.affected_rows() == 0u);
|
||||
BOOST_TEST(st.warning_count() == 0u);
|
||||
BOOST_TEST(st.last_insert_id() == 0u);
|
||||
BOOST_TEST(st.info() == "");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // test_prepared_statements
|
||||
|
@ -5,271 +5,219 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <boost/mysql/any_address.hpp>
|
||||
#include <boost/mysql/any_connection.hpp>
|
||||
#include <boost/mysql/common_server_errc.hpp>
|
||||
#include <boost/mysql/connect_params.hpp>
|
||||
#include <boost/mysql/connection.hpp>
|
||||
#include <boost/mysql/diagnostics.hpp>
|
||||
#include <boost/mysql/error_code.hpp>
|
||||
#include <boost/mysql/handshake_params.hpp>
|
||||
#include <boost/mysql/results.hpp>
|
||||
#include <boost/mysql/ssl_mode.hpp>
|
||||
#include <boost/mysql/string_view.hpp>
|
||||
#include <boost/mysql/throw_on_error.hpp>
|
||||
|
||||
#include <boost/asio/deferred.hpp>
|
||||
#include <boost/asio/bind_executor.hpp>
|
||||
#include <boost/asio/cancellation_signal.hpp>
|
||||
#include <boost/asio/cancellation_type.hpp>
|
||||
#include <boost/asio/error.hpp>
|
||||
#include <boost/asio/experimental/cancellation_condition.hpp>
|
||||
#include <boost/asio/experimental/parallel_group.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/core/span.hpp>
|
||||
#include <boost/test/data/monomorphic.hpp>
|
||||
#include <boost/test/data/test_case.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/test/unit_test_suite.hpp>
|
||||
|
||||
#include "test_common/netfun_maker.hpp"
|
||||
#include "test_integration/common.hpp"
|
||||
#include "test_integration/er_network_variant.hpp"
|
||||
#include "test_integration/network_samples.hpp"
|
||||
#include "test_integration/run_stackful_coro.hpp"
|
||||
#include "test_common/create_basic.hpp"
|
||||
#include "test_common/network_result.hpp"
|
||||
#include "test_integration/any_connection_fixture.hpp"
|
||||
#include "test_integration/connect_params_builder.hpp"
|
||||
#include "test_integration/server_features.hpp"
|
||||
#include "test_integration/spotchecks_helpers.hpp"
|
||||
#include "test_integration/tcp_connection_fixture.hpp"
|
||||
|
||||
using namespace boost::mysql::test;
|
||||
using namespace boost::mysql;
|
||||
using boost::asio::deferred;
|
||||
using boost::asio::experimental::make_parallel_group;
|
||||
using boost::asio::experimental::wait_for_one;
|
||||
namespace asio = boost::asio;
|
||||
using boost::test_tools::per_element;
|
||||
|
||||
namespace {
|
||||
|
||||
auto samples_with_reconnection = network_samples([]() {
|
||||
const string_view variant_names[] = {
|
||||
"tcp_sync_errc",
|
||||
"tcp_async_callback",
|
||||
"any_tcp_sync_errc",
|
||||
"any_tcp_async_callback",
|
||||
};
|
||||
|
||||
auto res = get_network_variants(variant_names);
|
||||
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
|
||||
if (get_server_features().unix_sockets)
|
||||
{
|
||||
res.push_back(get_network_variant("any_unix_sync_errc"));
|
||||
}
|
||||
#endif
|
||||
return res;
|
||||
});
|
||||
|
||||
auto samples_any = network_samples([]() {
|
||||
const string_view variant_names[] = {
|
||||
"any_tcp_sync_errc",
|
||||
"any_tcp_async_callback",
|
||||
};
|
||||
|
||||
auto res = get_network_variants(variant_names);
|
||||
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
|
||||
if (get_server_features().unix_sockets)
|
||||
{
|
||||
res.push_back(get_network_variant("any_unix_sync_errc"));
|
||||
}
|
||||
#endif
|
||||
return res;
|
||||
});
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(test_reconnect)
|
||||
|
||||
struct reconnect_fixture : network_fixture
|
||||
{
|
||||
void do_query_ok()
|
||||
{
|
||||
results result;
|
||||
conn->execute("SELECT * FROM empty_table", result).get();
|
||||
BOOST_TEST(result.rows().empty());
|
||||
}
|
||||
};
|
||||
auto connection_samples = network_functions_connection::sync_and_async();
|
||||
auto any_samples = network_functions_any::sync_and_async();
|
||||
auto any_samples_grid = boost::unit_test::data::make(any_samples) *
|
||||
boost::unit_test::data::make({ssl_mode::disable, ssl_mode::require});
|
||||
|
||||
BOOST_DATA_TEST_CASE_F(reconnect_fixture, reconnect_after_close, samples_with_reconnection)
|
||||
// Old connection can reconnect after close if the stream is not SSL
|
||||
BOOST_DATA_TEST_CASE(reconnect_after_close_connection, connection_samples)
|
||||
{
|
||||
setup(sample);
|
||||
netfn_fixture_connection fix(sample);
|
||||
|
||||
// Connect and use the connection
|
||||
connect();
|
||||
do_query_ok();
|
||||
results r;
|
||||
fix.net.connect(fix.conn, get_tcp_endpoint(), connect_params_builder().build_hparams())
|
||||
.validate_no_error();
|
||||
fix.net.execute_query(fix.conn, "SELECT * FROM empty_table", r).validate_no_error();
|
||||
|
||||
// Close
|
||||
conn->close().validate_no_error();
|
||||
fix.net.close(fix.conn).validate_no_error();
|
||||
|
||||
// Reopen and use the connection normally
|
||||
connect();
|
||||
do_query_ok();
|
||||
fix.net.connect(fix.conn, get_tcp_endpoint(), connect_params_builder().build_hparams())
|
||||
.validate_no_error();
|
||||
fix.net.execute_query(fix.conn, "SELECT * FROM empty_table", r).validate_no_error();
|
||||
}
|
||||
|
||||
BOOST_DATA_TEST_CASE_F(reconnect_fixture, reconnect_after_handshake_error, samples_with_reconnection)
|
||||
// any_connection can reconnect after close, even if the stream uses ssl
|
||||
BOOST_DATA_TEST_CASE(reconnect_after_close_any, any_samples_grid, p0, p1)
|
||||
{
|
||||
setup(sample);
|
||||
|
||||
// Error during server handshake
|
||||
params.set_database("bad_database");
|
||||
conn->connect(params).validate_error(
|
||||
common_server_errc::er_dbaccess_denied_error,
|
||||
{"database", "bad_database"}
|
||||
);
|
||||
|
||||
// Reopen with correct parameters and use the connection normally
|
||||
params.set_database("boost_mysql_integtests");
|
||||
connect();
|
||||
do_query_ok();
|
||||
}
|
||||
|
||||
BOOST_DATA_TEST_CASE_F(reconnect_fixture, reconnect_while_connected, samples_any)
|
||||
{
|
||||
setup(sample);
|
||||
netfn_fixture_any fix(p0);
|
||||
ssl_mode mode = p1;
|
||||
|
||||
// Connect and use the connection
|
||||
connect();
|
||||
do_query_ok();
|
||||
results r;
|
||||
fix.net.connect(fix.conn, connect_params_builder().ssl(mode).build()).validate_no_error();
|
||||
fix.net.execute_query(fix.conn, "SELECT * FROM empty_table", r).validate_no_error();
|
||||
|
||||
// Close
|
||||
fix.net.close(fix.conn).validate_no_error();
|
||||
|
||||
// Reopen and use the connection normally
|
||||
fix.net.connect(fix.conn, connect_params_builder().ssl(mode).build()).validate_no_error();
|
||||
fix.net.execute_query(fix.conn, "SELECT * FROM empty_table", r).validate_no_error();
|
||||
}
|
||||
|
||||
// Old connection can reconnect after handshake failure if the stream is not SSL
|
||||
BOOST_DATA_TEST_CASE(reconnect_after_handshake_error_connection, connection_samples)
|
||||
{
|
||||
netfn_fixture_connection fix(sample);
|
||||
|
||||
// Error during server handshake
|
||||
fix.net.connect(fix.conn, get_tcp_endpoint(), connect_params_builder().database("bad_db").build_hparams())
|
||||
.validate_error(
|
||||
common_server_errc::er_dbaccess_denied_error,
|
||||
"Access denied for user 'integ_user'@'%' to database 'bad_db'"
|
||||
);
|
||||
|
||||
// Reopen with correct parameters and use the connection normally
|
||||
results r;
|
||||
fix.net.connect(fix.conn, get_tcp_endpoint(), connect_params_builder().build_hparams())
|
||||
.validate_no_error();
|
||||
fix.net.execute_query(fix.conn, "SELECT * FROM empty_table", r).validate_no_error();
|
||||
}
|
||||
|
||||
// any_connection can reconnect after a handshake failure, even if SSL is used
|
||||
BOOST_DATA_TEST_CASE(reconnect_after_handshake_error_any, any_samples_grid, p0, p1)
|
||||
{
|
||||
netfn_fixture_any fix(p0);
|
||||
ssl_mode mode = p1;
|
||||
|
||||
// Error during server handshake
|
||||
fix.net.connect(fix.conn, connect_params_builder().ssl(mode).database("bad_db").build())
|
||||
.validate_error(
|
||||
common_server_errc::er_dbaccess_denied_error,
|
||||
"Access denied for user 'integ_user'@'%' to database 'bad_db'"
|
||||
);
|
||||
|
||||
// Reopen with correct parameters and use the connection normally
|
||||
results r;
|
||||
fix.net.connect(fix.conn, connect_params_builder().ssl(mode).build()).validate_no_error();
|
||||
fix.net.execute_query(fix.conn, "SELECT * FROM empty_table", r).validate_no_error();
|
||||
}
|
||||
|
||||
// any_connection can reconnect while it's connected
|
||||
BOOST_DATA_TEST_CASE(reconnect_while_connected, any_samples_grid, p0, p1)
|
||||
{
|
||||
netfn_fixture_any fix(p0);
|
||||
ssl_mode mode = p1;
|
||||
|
||||
// Connect and use the connection
|
||||
results r;
|
||||
fix.net.connect(fix.conn, connect_params_builder().ssl(mode).build()).validate_no_error();
|
||||
fix.net.execute_query(fix.conn, "SELECT * FROM empty_table", r).validate_no_error();
|
||||
|
||||
// We can safely connect again
|
||||
params.set_username("root");
|
||||
params.set_password("");
|
||||
fix.net.connect(fix.conn, connect_params_builder().ssl(mode).credentials("root", "").build())
|
||||
.validate_no_error();
|
||||
|
||||
// We've logged in as root. May be root@% or root@localhost
|
||||
fix.net.execute_query(fix.conn, "SELECT SUBSTRING_INDEX(CURRENT_USER(), '@', 1)", r).validate_no_error();
|
||||
BOOST_TEST(r.rows() == makerows(1, "root"), per_element());
|
||||
|
||||
// Close succeeds
|
||||
fix.net.close(fix.conn).validate_no_error();
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(reconnect_after_cancel, any_connection_fixture)
|
||||
{
|
||||
// Setup
|
||||
results r;
|
||||
connect();
|
||||
|
||||
// We've logged in as root
|
||||
results r;
|
||||
conn->execute("SELECT CURRENT_USER()", r).validate_no_error();
|
||||
BOOST_TEST(r.rows().at(0).at(0).as_string().starts_with("root"));
|
||||
// Kick an operation that ends up cancelled
|
||||
asio::cancellation_signal sig;
|
||||
auto netres = conn.async_execute("DO SLEEP(2)", r, as_netresult_t{sig.slot()});
|
||||
|
||||
// Return to the event loop and emit the signal
|
||||
asio::post(asio::bind_executor(global_context_executor(), [&]() {
|
||||
// Emit the signal
|
||||
sig.emit(asio::cancellation_type::terminal);
|
||||
}));
|
||||
|
||||
// Wait for the operation to finish
|
||||
std::move(netres).validate_error(asio::error::operation_aborted);
|
||||
|
||||
// We can connect again and use the connection
|
||||
connect();
|
||||
conn.async_execute("SELECT 42", r, as_netresult).validate_no_error();
|
||||
}
|
||||
|
||||
// parallel_group doesn't work with this macro. See https://github.com/chriskohlhoff/asio/issues/1398
|
||||
#ifndef BOOST_ASIO_USE_TS_EXECUTOR_AS_DEFAULT
|
||||
BOOST_AUTO_TEST_CASE(reconnect_after_cancel)
|
||||
// any_connection can change the stream type used by successive connect calls
|
||||
BOOST_AUTO_TEST_CASE(change_stream_type)
|
||||
{
|
||||
run_stackful_coro([](boost::asio::yield_context yield) {
|
||||
// Setup
|
||||
auto connect_prms = default_connect_params();
|
||||
any_connection conn(yield.get_executor());
|
||||
results r;
|
||||
boost::mysql::error_code ec;
|
||||
boost::mysql::diagnostics diag;
|
||||
// Helpers
|
||||
const auto tcp_params = connect_params_builder().ssl(ssl_mode::disable).build();
|
||||
const auto tcp_ssl_params = connect_params_builder().ssl(ssl_mode::require).build();
|
||||
const auto unix_params = connect_params_builder().set_unix().build();
|
||||
|
||||
// Connect
|
||||
conn.async_connect(connect_prms, diag, yield[ec]);
|
||||
boost::mysql::throw_on_error(ec, diag);
|
||||
|
||||
// Kick an operation that ends up cancelled
|
||||
auto wait_result = make_parallel_group(
|
||||
conn.async_execute("DO SLEEP(2)", r, deferred),
|
||||
boost::asio::post(yield.get_executor(), deferred)
|
||||
)
|
||||
.async_wait(wait_for_one(), yield);
|
||||
|
||||
// Verify this was the case
|
||||
BOOST_TEST(std::get<0>(wait_result)[1] == 0u); // post completed first
|
||||
BOOST_TEST(std::get<1>(wait_result) == boost::asio::error::operation_aborted);
|
||||
|
||||
// We can connect again
|
||||
conn.async_connect(connect_prms, diag, yield[ec]);
|
||||
boost::mysql::throw_on_error(ec, diag);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
// any_connection can change the stream type used by successive connect calls.
|
||||
// We need to split this test in two (TCP and UNIX), so UNIX cases don't run on Windows.
|
||||
struct change_stream_type_fixture : network_fixture_base
|
||||
{
|
||||
// Functions, to run sync and async with the same code
|
||||
using netmaker_connect = netfun_maker_mem<void, any_connection, const connect_params&>;
|
||||
using netmaker_ping = netfun_maker_mem<void, any_connection>;
|
||||
|
||||
struct functions_t
|
||||
{
|
||||
netmaker_connect::signature connect;
|
||||
netmaker_ping::signature ping;
|
||||
};
|
||||
|
||||
// A test case sample
|
||||
// Samples
|
||||
struct test_case_t
|
||||
{
|
||||
const char* name;
|
||||
functions_t fns;
|
||||
string_view name;
|
||||
const network_functions_any& fns;
|
||||
connect_params first_params;
|
||||
connect_params second_params;
|
||||
};
|
||||
|
||||
// Runs the actual test
|
||||
void run(boost::span<const test_case_t> test_cases)
|
||||
std::vector<test_case_t> test_cases{
|
||||
{"sync_tcp_tcpssl", any_samples[0], tcp_params, tcp_ssl_params},
|
||||
{"async_tcp_tcpssl", any_samples[1], tcp_params, tcp_ssl_params},
|
||||
{"async_tcpssl_tcp", any_samples[1], tcp_ssl_params, tcp_params },
|
||||
};
|
||||
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
|
||||
if (get_server_features().unix_sockets)
|
||||
{
|
||||
for (const auto& tc : test_cases)
|
||||
test_cases.push_back({"sync_unix_tcpssl", any_samples[0], unix_params, tcp_ssl_params});
|
||||
test_cases.push_back({"async_unix_tcpssl", any_samples[1], unix_params, tcp_ssl_params});
|
||||
test_cases.push_back({"async_tcpssl_unix", any_samples[1], tcp_ssl_params, unix_params});
|
||||
test_cases.push_back({"async_tcp_unix", any_samples[1], tcp_params, unix_params});
|
||||
}
|
||||
#endif
|
||||
|
||||
// Actual test
|
||||
for (const auto& tc : test_cases)
|
||||
{
|
||||
BOOST_TEST_CONTEXT(tc.name)
|
||||
{
|
||||
BOOST_TEST_CONTEXT(tc.name)
|
||||
{
|
||||
any_connection conn{ctx.get_executor()};
|
||||
// Setup
|
||||
netfn_fixture_any fix(tc.fns);
|
||||
|
||||
// Connect with the first stream type
|
||||
tc.fns.connect(conn, tc.first_params).validate_no_error();
|
||||
tc.fns.ping(conn).validate_no_error();
|
||||
// Connect with the first stream type
|
||||
fix.net.connect(fix.conn, tc.first_params).validate_no_error();
|
||||
fix.net.ping(fix.conn).validate_no_error();
|
||||
|
||||
// Connect with the second stream type
|
||||
tc.fns.connect(conn, tc.second_params).validate_no_error();
|
||||
tc.fns.ping(conn).validate_no_error();
|
||||
}
|
||||
// Connect with the second stream type
|
||||
fix.net.connect(fix.conn, tc.second_params).validate_no_error();
|
||||
fix.net.ping(fix.conn).validate_no_error();
|
||||
}
|
||||
}
|
||||
|
||||
functions_t sync_fns{
|
||||
netmaker_connect::sync_errc(&any_connection::connect),
|
||||
netmaker_ping::sync_errc(&any_connection::ping),
|
||||
};
|
||||
|
||||
functions_t async_fns{
|
||||
netmaker_connect::async_errinfo(&any_connection::async_connect),
|
||||
netmaker_ping::async_errinfo(&any_connection::async_ping),
|
||||
};
|
||||
|
||||
connect_params tcp_params;
|
||||
connect_params tcp_ssl_params;
|
||||
|
||||
change_stream_type_fixture()
|
||||
: tcp_params(default_connect_params(ssl_mode::disable)),
|
||||
tcp_ssl_params(default_connect_params(ssl_mode::require))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// TCP cases. Note that some sync cases are not included, to save testing time
|
||||
BOOST_FIXTURE_TEST_CASE(change_stream_type_tcp, change_stream_type_fixture)
|
||||
{
|
||||
test_case_t test_cases[] = {
|
||||
{"sync_tcp_tcpssl", sync_fns, tcp_params, tcp_ssl_params},
|
||||
{"async_tcp_tcpssl", async_fns, tcp_params, tcp_ssl_params},
|
||||
{"async_tcpssl_tcp", async_fns, tcp_ssl_params, tcp_params },
|
||||
};
|
||||
run(test_cases);
|
||||
}
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
|
||||
// UNIX cases. Note that some sync cases are not included, to save testing time
|
||||
BOOST_TEST_DECORATOR(*run_if(&server_features::unix_sockets))
|
||||
BOOST_FIXTURE_TEST_CASE(change_stream_type_unix, change_stream_type_fixture)
|
||||
{
|
||||
// UNIX connect params
|
||||
auto unix_params = default_connect_params();
|
||||
unix_params.server_address.emplace_unix_path(default_unix_path);
|
||||
|
||||
test_case_t test_cases[] = {
|
||||
{"sync_unix_tcpssl", sync_fns, unix_params, tcp_ssl_params},
|
||||
{"async_unix_tcpssl", async_fns, unix_params, tcp_ssl_params},
|
||||
{"async_tcpssl_unix", async_fns, tcp_ssl_params, unix_params },
|
||||
{"async_tcp_unix", async_fns, tcp_params, unix_params },
|
||||
};
|
||||
run(test_cases);
|
||||
}
|
||||
#endif
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // test_reconnect
|
||||
|
||||
} // namespace
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,12 +8,8 @@
|
||||
#include <boost/mysql/detail/config.hpp>
|
||||
|
||||
#ifdef BOOST_MYSQL_CXX14
|
||||
|
||||
#include <boost/mysql/column_type.hpp>
|
||||
#include <boost/mysql/common_server_errc.hpp>
|
||||
#include <boost/mysql/connection.hpp>
|
||||
#include <boost/mysql/diagnostics.hpp>
|
||||
#include <boost/mysql/error_code.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/metadata_collection_view.hpp>
|
||||
#include <boost/mysql/pfr.hpp>
|
||||
@ -28,17 +24,22 @@
|
||||
#include <boost/pfr/ops.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
|
||||
#include "test_common/check_meta.hpp"
|
||||
#include "test_common/network_result.hpp"
|
||||
#include "test_common/printing.hpp"
|
||||
#include "test_integration/common.hpp"
|
||||
#include "test_integration/any_connection_fixture.hpp"
|
||||
#include "test_integration/connect_params_builder.hpp"
|
||||
#include "test_integration/metadata_validator.hpp"
|
||||
#include "test_integration/static_rows.hpp"
|
||||
#include "test_integration/tcp_network_fixture.hpp"
|
||||
|
||||
using namespace boost::mysql;
|
||||
using namespace boost::mysql::test;
|
||||
using boost::span;
|
||||
using boost::test_tools::per_element;
|
||||
|
||||
// Note: the dynamic interface is already covered by stored_procedures, multi_queries, prepared_statements and
|
||||
// spotchecks
|
||||
@ -75,11 +76,14 @@ void validate_multifield_meta(metadata_collection_view meta)
|
||||
);
|
||||
}
|
||||
|
||||
void validate_multified_rows(boost::span<const row_multifield> r)
|
||||
std::array<row_multifield, 2> expected_multifield_rows()
|
||||
{
|
||||
BOOST_TEST_REQUIRE(r.size() == 2u);
|
||||
BOOST_TEST((r[0] == row_multifield{1.1f, 11, "aaa"}));
|
||||
BOOST_TEST((r[1] == row_multifield{{}, 22, "bbb"}));
|
||||
return {
|
||||
{
|
||||
{1.1f, 11, "aaa"},
|
||||
{{}, 22, "bbb"},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
constexpr const char* multifield_bad_msg =
|
||||
@ -90,29 +94,31 @@ constexpr const char* multifield_bad_msg =
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(singlefn)
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(describe_structs, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(describe_structs, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
static_results<row_multifield> result;
|
||||
conn.execute("SELECT * FROM multifield_table ORDER BY id", result);
|
||||
conn.async_execute("SELECT * FROM multifield_table ORDER BY id", result, as_netresult)
|
||||
.validate_no_error();
|
||||
|
||||
// Verify results
|
||||
validate_multifield_meta(result.meta());
|
||||
validate_multified_rows(result.rows());
|
||||
BOOST_TEST(result.rows() == expected_multifield_rows(), per_element());
|
||||
BOOST_TEST(result.affected_rows() == 0u);
|
||||
BOOST_TEST(result.warning_count() == 0u);
|
||||
BOOST_TEST(result.last_insert_id() == 0u);
|
||||
BOOST_TEST(result.info() == "");
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(tuples, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(tuples, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
using tuple_t = std::tuple<int, std::string, int, boost::optional<float>>; // trailing fields discarded
|
||||
static_results<tuple_t> result;
|
||||
conn.execute("SELECT * FROM multifield_table ORDER BY id", result);
|
||||
conn.async_execute("SELECT * FROM multifield_table ORDER BY id", result, as_netresult)
|
||||
.validate_no_error();
|
||||
|
||||
// Verify results
|
||||
validate_multifield_meta(result.meta());
|
||||
@ -126,12 +132,13 @@ BOOST_FIXTURE_TEST_CASE(tuples, tcp_network_fixture)
|
||||
}
|
||||
|
||||
#if BOOST_PFR_CORE_NAME_ENABLED
|
||||
BOOST_FIXTURE_TEST_CASE(pfr_structs_by_name, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(pfr_structs_by_name, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
static_results<pfr_by_name<row_multifield_pfr>> result;
|
||||
conn.execute("SELECT * FROM multifield_table ORDER BY id", result);
|
||||
conn.async_execute("SELECT * FROM multifield_table ORDER BY id", result, as_netresult)
|
||||
.validate_no_error();
|
||||
|
||||
// Verify results
|
||||
validate_multifield_meta(result.meta());
|
||||
@ -146,12 +153,17 @@ BOOST_FIXTURE_TEST_CASE(pfr_structs_by_name, tcp_network_fixture)
|
||||
#endif
|
||||
|
||||
#if BOOST_PFR_ENABLED && defined(BOOST_MYSQL_CXX14)
|
||||
BOOST_FIXTURE_TEST_CASE(pfr_structs_by_position, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(pfr_structs_by_position, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
static_results<pfr_by_position<row_multifield_pfr_literal>> result;
|
||||
conn.execute("SELECT id, field_int, field_double FROM multifield_table ORDER BY id", result);
|
||||
conn.async_execute(
|
||||
"SELECT id, field_int, field_double FROM multifield_table ORDER BY id",
|
||||
result,
|
||||
as_netresult
|
||||
)
|
||||
.validate_no_error();
|
||||
|
||||
// Verify results
|
||||
check_meta(result.meta(), {column_type::int_, column_type::int_, column_type::double_});
|
||||
@ -166,26 +178,22 @@ BOOST_FIXTURE_TEST_CASE(pfr_structs_by_position, tcp_network_fixture)
|
||||
#endif
|
||||
|
||||
// This spotchecks having a repeated empty row type, too
|
||||
BOOST_FIXTURE_TEST_CASE(multi_resultset, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(multi_resultset, any_connection_fixture)
|
||||
{
|
||||
params.set_multi_queries(true);
|
||||
connect();
|
||||
connect(connect_params_builder().multi_queries(true).build());
|
||||
start_transaction();
|
||||
|
||||
static_results<row_multifield, empty, row_2fields, empty> result;
|
||||
conn.execute(
|
||||
R"%(
|
||||
SELECT * FROM multifield_table;
|
||||
DELETE FROM updates_table;
|
||||
SELECT * FROM one_row_table;
|
||||
SET @v1 = 2
|
||||
)%",
|
||||
result
|
||||
);
|
||||
constexpr const char* query =
|
||||
"SELECT * FROM multifield_table;"
|
||||
"DELETE FROM updates_table;"
|
||||
"SELECT * FROM one_row_table;"
|
||||
"SET @v1 = 2";
|
||||
conn.async_execute(query, result, as_netresult).validate_no_error();
|
||||
|
||||
// Validate results
|
||||
validate_multifield_meta(result.meta<0>());
|
||||
validate_multified_rows(result.rows<0>());
|
||||
BOOST_TEST(result.rows<0>() == expected_multifield_rows(), per_element());
|
||||
BOOST_TEST(result.affected_rows<0>() == 0u);
|
||||
BOOST_TEST(result.warning_count<0>() == 0u);
|
||||
BOOST_TEST(result.last_insert_id<0>() == 0u);
|
||||
@ -198,9 +206,11 @@ BOOST_FIXTURE_TEST_CASE(multi_resultset, tcp_network_fixture)
|
||||
BOOST_TEST(result.last_insert_id<1>() == 0u);
|
||||
BOOST_TEST(result.info<1>() == "");
|
||||
|
||||
const row_2fields expected_2fields[]{
|
||||
{1, std::string("f0")}
|
||||
};
|
||||
validate_2fields_meta(result.meta<2>(), "one_row_table");
|
||||
BOOST_TEST_REQUIRE(result.rows<2>().size() == 1u);
|
||||
BOOST_TEST_REQUIRE((result.rows<2>()[0] == row_2fields{1, std::string("f0")}));
|
||||
BOOST_TEST(result.rows<2>() == expected_2fields, per_element());
|
||||
BOOST_TEST(result.affected_rows<2>() == 0u);
|
||||
BOOST_TEST(result.warning_count<2>() == 0u);
|
||||
BOOST_TEST(result.last_insert_id<2>() == 0u);
|
||||
@ -214,93 +224,74 @@ BOOST_FIXTURE_TEST_CASE(multi_resultset, tcp_network_fixture)
|
||||
BOOST_TEST(result.info<3>() == "");
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(metadata_check_failed, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(metadata_check_failed, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
error_code ec;
|
||||
diagnostics diag;
|
||||
static_results<row_multifield_bad> result;
|
||||
conn.execute("SELECT * FROM multifield_table ORDER BY id", result, ec, diag);
|
||||
|
||||
BOOST_TEST(ec == client_errc::metadata_check_failed);
|
||||
BOOST_TEST(diag.client_message() == multifield_bad_msg);
|
||||
conn.async_execute("SELECT * FROM multifield_table ORDER BY id", result, as_netresult)
|
||||
.validate_error(client_errc::metadata_check_failed, multifield_bad_msg);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(metadata_check_failed_empty_resultset, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(metadata_check_failed_empty_resultset, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
error_code ec;
|
||||
diagnostics diag;
|
||||
static_results<std::tuple<int>> result;
|
||||
conn.execute("SET @v1 = 2", result, ec, diag);
|
||||
|
||||
const char* expected_msg =
|
||||
"Field in position 0 can't be mapped: there are more fields in your C++ data type than in your query";
|
||||
|
||||
BOOST_TEST(ec == client_errc::metadata_check_failed);
|
||||
BOOST_TEST(diag.client_message() == expected_msg);
|
||||
static_results<std::tuple<int>> result;
|
||||
conn.async_execute("SET @v1 = 2", result, as_netresult)
|
||||
.validate_error(client_errc::metadata_check_failed, expected_msg);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(metadata_check_failed_subsequent_resultset, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(metadata_check_failed_subsequent_resultset, any_connection_fixture)
|
||||
{
|
||||
params.set_multi_queries(true);
|
||||
connect();
|
||||
connect(connect_params_builder().multi_queries(true).build());
|
||||
|
||||
error_code ec;
|
||||
diagnostics diag;
|
||||
static_results<empty, row_multifield_bad> result;
|
||||
conn.execute("SET @v1 = 2; SELECT * FROM multifield_table ORDER BY id", result, ec, diag);
|
||||
|
||||
BOOST_TEST(ec == client_errc::metadata_check_failed);
|
||||
BOOST_TEST(diag.client_message() == multifield_bad_msg);
|
||||
conn.async_execute("SET @v1 = 2; SELECT * FROM multifield_table ORDER BY id", result, as_netresult)
|
||||
.validate_error(client_errc::metadata_check_failed, multifield_bad_msg);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(num_resultsets_mismatch, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(num_resultsets_mismatch, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
error_code ec;
|
||||
diagnostics diag;
|
||||
static_results<row_2fields, empty> result;
|
||||
conn.execute("SELECT * FROM one_row_table", result, ec, diag);
|
||||
|
||||
BOOST_TEST(ec == client_errc::num_resultsets_mismatch);
|
||||
conn.async_execute("SELECT * FROM one_row_table", result, as_netresult)
|
||||
.validate_error(client_errc::num_resultsets_mismatch);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(num_resultsets_mismatch_empty_resultset, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(num_resultsets_mismatch_empty_resultset, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
error_code ec;
|
||||
diagnostics diag;
|
||||
static_results<empty, empty> result;
|
||||
conn.execute("SET @v1 = 2", result, ec, diag);
|
||||
|
||||
BOOST_TEST(ec == client_errc::num_resultsets_mismatch);
|
||||
conn.async_execute("SET @v1 = 2", result, as_netresult)
|
||||
.validate_error(client_errc::num_resultsets_mismatch);
|
||||
}
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(multifn)
|
||||
BOOST_FIXTURE_TEST_CASE(describe_structs, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(describe_structs, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
// Start
|
||||
static_execution_state<row_multifield> result;
|
||||
conn.start_execution("SELECT * FROM multifield_table WHERE id = 1", result);
|
||||
conn.async_start_execution("SELECT * FROM multifield_table WHERE id = 1", result, as_netresult)
|
||||
.validate_no_error();
|
||||
validate_multifield_meta(result.meta());
|
||||
BOOST_TEST(result.should_read_rows());
|
||||
|
||||
// Read rows
|
||||
std::array<row_multifield, 3> rws;
|
||||
std::size_t num_rows = conn.read_some_rows(result, boost::span<row_multifield>(rws));
|
||||
std::size_t num_rows = conn.async_read_some_rows(result, span<row_multifield>(rws), as_netresult).get();
|
||||
BOOST_TEST_REQUIRE(num_rows == 1u);
|
||||
BOOST_TEST((rws[0] == row_multifield{1.1f, 11, "aaa"}));
|
||||
|
||||
// Read again, in case the EOF came separately
|
||||
num_rows = conn.read_some_rows(result, boost::span<row_multifield>(rws));
|
||||
num_rows = conn.async_read_some_rows(result, span<row_multifield>(rws), as_netresult).get();
|
||||
BOOST_TEST_REQUIRE(num_rows == 0u);
|
||||
BOOST_TEST(result.complete());
|
||||
BOOST_TEST(result.affected_rows() == 0u);
|
||||
@ -309,7 +300,7 @@ BOOST_FIXTURE_TEST_CASE(describe_structs, tcp_network_fixture)
|
||||
BOOST_TEST(result.info() == "");
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(tuples, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(tuples, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
@ -317,18 +308,19 @@ BOOST_FIXTURE_TEST_CASE(tuples, tcp_network_fixture)
|
||||
|
||||
// Start
|
||||
static_execution_state<tuple_t> result;
|
||||
conn.start_execution("SELECT * FROM multifield_table WHERE id = 1", result);
|
||||
conn.async_start_execution("SELECT * FROM multifield_table WHERE id = 1", result, as_netresult)
|
||||
.validate_no_error();
|
||||
validate_multifield_meta(result.meta());
|
||||
BOOST_TEST(result.should_read_rows());
|
||||
|
||||
// Read rows
|
||||
std::array<tuple_t, 3> rws;
|
||||
std::size_t num_rows = conn.read_some_rows(result, boost::span<tuple_t>(rws));
|
||||
std::size_t num_rows = conn.async_read_some_rows(result, span<tuple_t>(rws), as_netresult).get();
|
||||
BOOST_TEST_REQUIRE(num_rows == 1u);
|
||||
BOOST_TEST((rws[0] == tuple_t{1, "aaa", 11}));
|
||||
|
||||
// Read again, in case the EOF came separately
|
||||
num_rows = conn.read_some_rows(result, boost::span<tuple_t>(rws));
|
||||
num_rows = conn.async_read_some_rows(result, span<tuple_t>(rws), as_netresult).get();
|
||||
BOOST_TEST_REQUIRE(num_rows == 0u);
|
||||
BOOST_TEST(result.complete());
|
||||
BOOST_TEST(result.affected_rows() == 0u);
|
||||
@ -338,24 +330,26 @@ BOOST_FIXTURE_TEST_CASE(tuples, tcp_network_fixture)
|
||||
}
|
||||
|
||||
#if BOOST_PFR_CORE_NAME_ENABLED
|
||||
BOOST_FIXTURE_TEST_CASE(pfr_structs_by_name, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(pfr_structs_by_name, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
// Start
|
||||
static_execution_state<pfr_by_name<row_multifield_pfr>> result;
|
||||
conn.start_execution("SELECT * FROM multifield_table WHERE id = 1", result);
|
||||
conn.async_start_execution("SELECT * FROM multifield_table WHERE id = 1", result, as_netresult)
|
||||
.validate_no_error();
|
||||
validate_multifield_meta(result.meta());
|
||||
BOOST_TEST(result.should_read_rows());
|
||||
|
||||
// Read rows
|
||||
std::array<row_multifield_pfr, 3> rws;
|
||||
std::size_t num_rows = conn.read_some_rows(result, boost::span<row_multifield_pfr>(rws));
|
||||
std::size_t num_rows = conn.async_read_some_rows(result, span<row_multifield_pfr>(rws), as_netresult)
|
||||
.get();
|
||||
BOOST_TEST_REQUIRE(num_rows == 1u);
|
||||
BOOST_TEST(boost::pfr::eq(rws[0], row_multifield_pfr{1.1f, 11, "aaa"}));
|
||||
|
||||
// Read again, in case the EOF came separately
|
||||
num_rows = conn.read_some_rows(result, boost::span<row_multifield_pfr>(rws));
|
||||
num_rows = conn.async_read_some_rows(result, span<row_multifield_pfr>(rws), as_netresult).get();
|
||||
BOOST_TEST_REQUIRE(num_rows == 0u);
|
||||
BOOST_TEST(result.complete());
|
||||
BOOST_TEST(result.affected_rows() == 0u);
|
||||
@ -366,24 +360,30 @@ BOOST_FIXTURE_TEST_CASE(pfr_structs_by_name, tcp_network_fixture)
|
||||
#endif
|
||||
|
||||
#if BOOST_PFR_ENABLED && defined(BOOST_MYSQL_CXX14)
|
||||
BOOST_FIXTURE_TEST_CASE(pfr_structs_by_position, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(pfr_structs_by_position, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
// Start
|
||||
static_execution_state<pfr_by_position<row_multifield_pfr_literal>> result;
|
||||
conn.start_execution("SELECT id, field_int, field_double FROM multifield_table WHERE id = 1", result);
|
||||
conn.async_start_execution(
|
||||
"SELECT id, field_int, field_double FROM multifield_table WHERE id = 1",
|
||||
result,
|
||||
as_netresult
|
||||
)
|
||||
.validate_no_error();
|
||||
check_meta(result.meta(), {column_type::int_, column_type::int_, column_type::double_});
|
||||
BOOST_TEST(result.should_read_rows());
|
||||
|
||||
// Read rows
|
||||
std::array<row_multifield_pfr_literal, 3> rws;
|
||||
std::size_t num_rows = conn.read_some_rows(result, boost::span<row_multifield_pfr_literal>(rws));
|
||||
auto num_rows = conn.async_read_some_rows(result, span<row_multifield_pfr_literal>(rws), as_netresult)
|
||||
.get();
|
||||
BOOST_TEST_REQUIRE(num_rows == 1u);
|
||||
BOOST_TEST(boost::pfr::eq(rws[0], row_multifield_pfr_literal{1, 11, 0.1}));
|
||||
|
||||
// Read again, in case the EOF came separately
|
||||
num_rows = conn.read_some_rows(result, boost::span<row_multifield_pfr_literal>(rws));
|
||||
num_rows = conn.async_read_some_rows(result, span<row_multifield_pfr_literal>(rws), as_netresult).get();
|
||||
BOOST_TEST_REQUIRE(num_rows == 0u);
|
||||
BOOST_TEST(result.complete());
|
||||
BOOST_TEST(result.affected_rows() == 0u);
|
||||
@ -394,34 +394,30 @@ BOOST_FIXTURE_TEST_CASE(pfr_structs_by_position, tcp_network_fixture)
|
||||
#endif
|
||||
|
||||
// This spotchecks having repeated empty row types, too
|
||||
BOOST_FIXTURE_TEST_CASE(multi_resultset, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(multi_resultset, any_connection_fixture)
|
||||
{
|
||||
params.set_multi_queries(true);
|
||||
connect();
|
||||
connect(connect_params_builder().multi_queries(true).build());
|
||||
start_transaction();
|
||||
|
||||
// Start
|
||||
constexpr const char* query =
|
||||
"SELECT * FROM multifield_table WHERE id = 1;"
|
||||
"DELETE FROM updates_table;"
|
||||
"SELECT * FROM one_row_table;"
|
||||
"SET @v1 = 2";
|
||||
static_execution_state<row_multifield, empty, row_2fields, empty> result;
|
||||
conn.start_execution(
|
||||
R"%(
|
||||
SELECT * FROM multifield_table WHERE id = 1;
|
||||
DELETE FROM updates_table;
|
||||
SELECT * FROM one_row_table;
|
||||
SET @v1 = 2
|
||||
)%",
|
||||
result
|
||||
);
|
||||
conn.async_start_execution(query, result, as_netresult).validate_no_error();
|
||||
validate_multifield_meta(result.meta());
|
||||
BOOST_TEST(result.should_read_rows());
|
||||
|
||||
// Read rows (r1)
|
||||
std::array<row_multifield, 3> rws;
|
||||
std::size_t num_rows = conn.read_some_rows(result, boost::span<row_multifield>(rws));
|
||||
std::size_t num_rows = conn.async_read_some_rows(result, span<row_multifield>(rws), as_netresult).get();
|
||||
BOOST_TEST_REQUIRE(num_rows == 1u);
|
||||
BOOST_TEST((rws[0] == row_multifield{1.1f, 11, "aaa"}));
|
||||
|
||||
// Read again, in case the EOF came separately (r1)
|
||||
num_rows = conn.read_some_rows(result, boost::span<row_multifield>(rws));
|
||||
num_rows = conn.async_read_some_rows(result, span<row_multifield>(rws), as_netresult).get();
|
||||
BOOST_TEST_REQUIRE(num_rows == 0u);
|
||||
BOOST_TEST(result.should_read_head());
|
||||
BOOST_TEST(result.affected_rows() == 0u);
|
||||
@ -430,7 +426,7 @@ BOOST_FIXTURE_TEST_CASE(multi_resultset, tcp_network_fixture)
|
||||
BOOST_TEST(result.info() == "");
|
||||
|
||||
// Next resultset (r2, empty)
|
||||
conn.read_resultset_head(result);
|
||||
conn.async_read_resultset_head(result, as_netresult).validate_no_error();
|
||||
BOOST_TEST(result.should_read_head());
|
||||
BOOST_TEST(result.meta().size() == 0u);
|
||||
BOOST_TEST(result.affected_rows() == 3u);
|
||||
@ -439,18 +435,18 @@ BOOST_FIXTURE_TEST_CASE(multi_resultset, tcp_network_fixture)
|
||||
BOOST_TEST(result.info() == "");
|
||||
|
||||
// Next resultset (r3)
|
||||
conn.read_resultset_head(result);
|
||||
conn.async_read_resultset_head(result, as_netresult).validate_no_error();
|
||||
BOOST_TEST(result.should_read_rows());
|
||||
validate_2fields_meta(result.meta(), "one_row_table");
|
||||
|
||||
// Read rows (r3)
|
||||
std::array<row_2fields, 3> rws2;
|
||||
num_rows = conn.read_some_rows(result, boost::span<row_2fields>(rws2));
|
||||
num_rows = conn.async_read_some_rows(result, span<row_2fields>(rws2), as_netresult).get();
|
||||
BOOST_TEST_REQUIRE(num_rows == 1u);
|
||||
BOOST_TEST((rws2[0] == row_2fields{1, std::string("f0")}));
|
||||
|
||||
// Read again, in case the EOF came separately (r3)
|
||||
num_rows = conn.read_some_rows(result, boost::span<row_2fields>(rws2));
|
||||
num_rows = conn.async_read_some_rows(result, span<row_2fields>(rws2), as_netresult).get();
|
||||
BOOST_TEST_REQUIRE(num_rows == 0u);
|
||||
BOOST_TEST(result.should_read_head());
|
||||
BOOST_TEST(result.affected_rows() == 0u);
|
||||
@ -459,7 +455,7 @@ BOOST_FIXTURE_TEST_CASE(multi_resultset, tcp_network_fixture)
|
||||
BOOST_TEST(result.info() == "");
|
||||
|
||||
// Next resultset (r4, empty)
|
||||
conn.read_resultset_head(result);
|
||||
conn.async_read_resultset_head(result, as_netresult).validate_no_error();
|
||||
BOOST_TEST(result.complete());
|
||||
BOOST_TEST(result.meta().size() == 0u);
|
||||
BOOST_TEST(result.affected_rows() == 0u);
|
||||
@ -468,83 +464,64 @@ BOOST_FIXTURE_TEST_CASE(multi_resultset, tcp_network_fixture)
|
||||
BOOST_TEST(result.info() == "");
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(metadata_check_failed, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(metadata_check_failed, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
error_code ec;
|
||||
diagnostics diag;
|
||||
static_execution_state<row_multifield_bad> result;
|
||||
conn.start_execution("SELECT * FROM multifield_table ORDER BY id", result, ec, diag);
|
||||
|
||||
BOOST_TEST(ec == client_errc::metadata_check_failed);
|
||||
BOOST_TEST(diag.client_message() == multifield_bad_msg);
|
||||
conn.async_start_execution("SELECT * FROM multifield_table ORDER BY id", result, as_netresult)
|
||||
.validate_error(client_errc::metadata_check_failed, multifield_bad_msg);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(metadata_check_failed_empty_resultset, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(metadata_check_failed_empty_resultset, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
error_code ec;
|
||||
diagnostics diag;
|
||||
static_execution_state<std::tuple<int>> result;
|
||||
conn.start_execution("SET @v1 = 2", result, ec, diag);
|
||||
|
||||
const char* expected_msg =
|
||||
"Field in position 0 can't be mapped: there are more fields in your C++ data type than in your query";
|
||||
|
||||
BOOST_TEST(ec == client_errc::metadata_check_failed);
|
||||
BOOST_TEST(diag.client_message() == expected_msg);
|
||||
static_execution_state<std::tuple<int>> result;
|
||||
conn.async_start_execution("SET @v1 = 2", result, as_netresult)
|
||||
.validate_error(client_errc::metadata_check_failed, expected_msg);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(num_resultsets_mismatch, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(num_resultsets_mismatch, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
error_code ec;
|
||||
diagnostics diag;
|
||||
static_execution_state<row_2fields, empty> result;
|
||||
|
||||
// Start execution
|
||||
conn.start_execution("SELECT * FROM empty_table", result);
|
||||
conn.async_start_execution("SELECT * FROM empty_table", result, as_netresult).validate_no_error();
|
||||
|
||||
// Error is detected when reading the OK packet in read_some_rows
|
||||
std::array<row_2fields, 3> storage;
|
||||
conn.read_some_rows(result, boost::span<row_2fields>(storage), ec, diag);
|
||||
BOOST_TEST(ec == client_errc::num_resultsets_mismatch);
|
||||
conn.async_read_some_rows(result, span<row_2fields>(storage), as_netresult)
|
||||
.validate_error(client_errc::num_resultsets_mismatch);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(num_resultsets_mismatch_empty_resultset, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(num_resultsets_mismatch_empty_resultset, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
error_code ec;
|
||||
diagnostics diag;
|
||||
|
||||
// Start
|
||||
static_execution_state<empty, empty> result;
|
||||
conn.start_execution("SET @v1 = 2", result, ec, diag);
|
||||
|
||||
BOOST_TEST(ec == client_errc::num_resultsets_mismatch);
|
||||
conn.async_start_execution("SET @v1 = 2", result, as_netresult)
|
||||
.validate_error(client_errc::num_resultsets_mismatch);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(metadata_check_failed_subsequent_resultset, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(metadata_check_failed_subsequent_resultset, any_connection_fixture)
|
||||
{
|
||||
params.set_multi_queries(true);
|
||||
connect();
|
||||
connect(connect_params_builder().multi_queries(true).build());
|
||||
|
||||
error_code ec;
|
||||
diagnostics diag;
|
||||
static_execution_state<empty, row_multifield_bad> result;
|
||||
|
||||
// Start execution goes OK
|
||||
conn.start_execution("SET @v1 = 2; SELECT * FROM multifield_table", result);
|
||||
conn.async_start_execution("SET @v1 = 2; SELECT * FROM multifield_table", result, as_netresult)
|
||||
.validate_no_error();
|
||||
|
||||
// Error is detected when reading next head
|
||||
conn.read_resultset_head(result, ec, diag);
|
||||
|
||||
BOOST_TEST(ec == client_errc::metadata_check_failed);
|
||||
BOOST_TEST(diag.client_message() == multifield_bad_msg);
|
||||
conn.async_read_resultset_head(result, as_netresult)
|
||||
.validate_error(client_errc::metadata_check_failed, multifield_bad_msg);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
@ -7,41 +7,42 @@
|
||||
|
||||
#include <boost/mysql/column_type.hpp>
|
||||
#include <boost/mysql/common_server_errc.hpp>
|
||||
#include <boost/mysql/connection.hpp>
|
||||
#include <boost/mysql/diagnostics.hpp>
|
||||
#include <boost/mysql/execution_state.hpp>
|
||||
#include <boost/mysql/results.hpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "test_common/check_meta.hpp"
|
||||
#include "test_common/create_basic.hpp"
|
||||
#include "test_common/network_result.hpp"
|
||||
#include "test_common/printing.hpp"
|
||||
#include "test_integration/common.hpp"
|
||||
#include "test_integration/tcp_network_fixture.hpp"
|
||||
#include "test_integration/any_connection_fixture.hpp"
|
||||
#include "test_integration/metadata_validator.hpp"
|
||||
|
||||
using namespace boost::mysql;
|
||||
using namespace boost::mysql::test;
|
||||
using boost::test_tools::per_element;
|
||||
|
||||
namespace {
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(test_stored_procedures)
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(without_selects, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(without_selects, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
start_transaction();
|
||||
|
||||
// Statement
|
||||
auto stmt = conn.prepare_statement("CALL sp_insert(?)");
|
||||
auto stmt = conn.async_prepare_statement("CALL sp_insert(?)", as_netresult).get();
|
||||
|
||||
// Call the procedure
|
||||
results result;
|
||||
conn.execute(stmt.bind("abc"), result);
|
||||
conn.async_execute(stmt.bind("abc"), result, as_netresult).validate_no_error();
|
||||
|
||||
// Verify results
|
||||
BOOST_TEST_REQUIRE(result.size() == 1u);
|
||||
BOOST_TEST(result.meta().size() == 0u);
|
||||
BOOST_TEST(result.rows().empty());
|
||||
BOOST_TEST(result.rows() == rows(), per_element());
|
||||
BOOST_TEST(result.affected_rows() == 1u);
|
||||
BOOST_TEST(result.warning_count() == 0u);
|
||||
BOOST_TEST(result.last_insert_id() == 0u); // this refers to the CALL, not to the INSERT
|
||||
@ -49,31 +50,31 @@ BOOST_FIXTURE_TEST_CASE(without_selects, tcp_network_fixture)
|
||||
BOOST_TEST(result.out_params() == row_view());
|
||||
|
||||
// Verify it took place
|
||||
conn.execute("SELECT field_varchar FROM inserts_table", result);
|
||||
BOOST_TEST(result.rows().at(0).at(0).as_string() == "abc");
|
||||
conn.async_execute("SELECT field_varchar FROM inserts_table", result, as_netresult).validate_no_error();
|
||||
BOOST_TEST(result.rows() == makerows(1, "abc"), per_element());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(with_one_select, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(with_one_select, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
// Statement
|
||||
auto stmt = conn.prepare_statement("CALL sp_select_1(?)");
|
||||
auto stmt = conn.async_prepare_statement("CALL sp_select_1(?)", as_netresult).get();
|
||||
|
||||
// Call the procedure
|
||||
results result;
|
||||
conn.execute(stmt.bind("abc"), result);
|
||||
conn.async_execute(stmt.bind("abc"), result, as_netresult).validate_no_error();
|
||||
|
||||
// Verify results
|
||||
BOOST_TEST_REQUIRE(result.size() == 2u);
|
||||
validate_2fields_meta(result[0].meta(), "one_row_table");
|
||||
BOOST_TEST(result[0].rows() == makerows(2, 1, "f0"));
|
||||
BOOST_TEST(result[0].rows() == makerows(2, 1, "f0"), per_element());
|
||||
BOOST_TEST(result[0].affected_rows() == 0u);
|
||||
BOOST_TEST(result[0].warning_count() == 0u);
|
||||
BOOST_TEST(result[0].last_insert_id() == 0u);
|
||||
BOOST_TEST(result[0].info() == "");
|
||||
BOOST_TEST(result[1].meta().size() == 0u);
|
||||
BOOST_TEST(result[1].rows().size() == 0u);
|
||||
BOOST_TEST(result[1].rows() == rows(), per_element());
|
||||
BOOST_TEST(result[1].affected_rows() == 0u);
|
||||
BOOST_TEST(result[1].warning_count() == 0u);
|
||||
BOOST_TEST(result[1].last_insert_id() == 0u);
|
||||
@ -81,33 +82,33 @@ BOOST_FIXTURE_TEST_CASE(with_one_select, tcp_network_fixture)
|
||||
BOOST_TEST(result.out_params() == row_view());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(with_two_selects, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(with_two_selects, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
// Statement
|
||||
auto stmt = conn.prepare_statement("CALL sp_select_2(?, ?)");
|
||||
auto stmt = conn.async_prepare_statement("CALL sp_select_2(?, ?)", as_netresult).get();
|
||||
|
||||
// Call the procedure
|
||||
results result;
|
||||
conn.execute(stmt.bind("abc", 42), result);
|
||||
conn.async_execute(stmt.bind("abc", 42), result, as_netresult).validate_no_error();
|
||||
|
||||
// Verify results
|
||||
BOOST_TEST_REQUIRE(result.size() == 3u);
|
||||
validate_2fields_meta(result[0].meta(), "one_row_table");
|
||||
BOOST_TEST(result[0].rows() == makerows(2, 1, "f0"));
|
||||
BOOST_TEST(result[0].rows() == makerows(2, 1, "f0"), per_element());
|
||||
BOOST_TEST(result[0].affected_rows() == 0u);
|
||||
BOOST_TEST(result[0].warning_count() == 0u);
|
||||
BOOST_TEST(result[0].last_insert_id() == 0u);
|
||||
BOOST_TEST(result[0].info() == "");
|
||||
check_meta(result[1].meta(), {column_type::varchar, column_type::int_});
|
||||
BOOST_TEST(result[1].rows() == makerows(2, "abc", 42));
|
||||
BOOST_TEST(result[1].rows() == makerows(2, "abc", 42), per_element());
|
||||
BOOST_TEST(result[1].affected_rows() == 0u);
|
||||
BOOST_TEST(result[1].warning_count() == 0u);
|
||||
BOOST_TEST(result[1].last_insert_id() == 0u);
|
||||
BOOST_TEST(result[1].info() == "");
|
||||
BOOST_TEST(result[2].meta().size() == 0u);
|
||||
BOOST_TEST(result[2].rows().size() == 0u);
|
||||
BOOST_TEST(result[2].rows() == rows(), per_element());
|
||||
BOOST_TEST(result[2].affected_rows() == 0u);
|
||||
BOOST_TEST(result[2].warning_count() == 0u);
|
||||
BOOST_TEST(result[2].last_insert_id() == 0u);
|
||||
@ -115,23 +116,23 @@ BOOST_FIXTURE_TEST_CASE(with_two_selects, tcp_network_fixture)
|
||||
BOOST_TEST(result.out_params() == row_view());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(with_two_selects_multifn, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(with_two_selects_multifn, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
// Statement
|
||||
auto stmt = conn.prepare_statement("CALL sp_select_2(?, ?)");
|
||||
auto stmt = conn.async_prepare_statement("CALL sp_select_2(?, ?)", as_netresult).get();
|
||||
|
||||
// Call the procedure
|
||||
execution_state st;
|
||||
conn.start_execution(stmt.bind("abc", 42), st);
|
||||
conn.async_start_execution(stmt.bind("abc", 42), st, as_netresult).validate_no_error();
|
||||
BOOST_TEST_REQUIRE(st.should_read_rows());
|
||||
validate_2fields_meta(st.meta(), "one_row_table");
|
||||
|
||||
// Read rows for the 1st select
|
||||
auto rv = conn.read_some_rows(st);
|
||||
auto rv = conn.async_read_some_rows(st, as_netresult).get();
|
||||
BOOST_TEST_REQUIRE(st.should_read_head());
|
||||
BOOST_TEST(rv == makerows(2, 1, "f0"));
|
||||
BOOST_TEST(rv == makerows(2, 1, "f0"), per_element());
|
||||
BOOST_TEST(st.affected_rows() == 0u);
|
||||
BOOST_TEST(st.warning_count() == 0u);
|
||||
BOOST_TEST(st.last_insert_id() == 0u);
|
||||
@ -139,14 +140,14 @@ BOOST_FIXTURE_TEST_CASE(with_two_selects_multifn, tcp_network_fixture)
|
||||
BOOST_TEST(!st.is_out_params());
|
||||
|
||||
// Read 2nd resultset's head
|
||||
conn.read_resultset_head(st);
|
||||
conn.async_read_resultset_head(st, as_netresult).validate_no_error();
|
||||
BOOST_TEST_REQUIRE(st.should_read_rows());
|
||||
check_meta(st.meta(), {column_type::varchar, column_type::int_});
|
||||
|
||||
// Read 2nd resultset's rows
|
||||
rv = conn.read_some_rows(st);
|
||||
rv = conn.async_read_some_rows(st, as_netresult).get();
|
||||
BOOST_TEST_REQUIRE(st.should_read_head());
|
||||
BOOST_TEST(rv == makerows(2, "abc", 42));
|
||||
BOOST_TEST(rv == makerows(2, "abc", 42), per_element());
|
||||
BOOST_TEST(st.affected_rows() == 0u);
|
||||
BOOST_TEST(st.warning_count() == 0u);
|
||||
BOOST_TEST(st.last_insert_id() == 0u);
|
||||
@ -154,7 +155,7 @@ BOOST_FIXTURE_TEST_CASE(with_two_selects_multifn, tcp_network_fixture)
|
||||
BOOST_TEST(!st.is_out_params());
|
||||
|
||||
// Read final resultset
|
||||
conn.read_resultset_head(st);
|
||||
conn.async_read_resultset_head(st, as_netresult).validate_no_error();
|
||||
BOOST_TEST_REQUIRE(st.complete());
|
||||
BOOST_TEST(st.meta().size() == 0u);
|
||||
BOOST_TEST(st.affected_rows() == 0u);
|
||||
@ -164,23 +165,23 @@ BOOST_FIXTURE_TEST_CASE(with_two_selects_multifn, tcp_network_fixture)
|
||||
BOOST_TEST(!st.is_out_params());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(output_params_not_bound, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(output_params_not_bound, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
// Statement
|
||||
auto stmt = conn.prepare_statement("CALL sp_outparams(?, @var1, @var2)");
|
||||
auto stmt = conn.async_prepare_statement("CALL sp_outparams(?, @var1, @var2)", as_netresult).get();
|
||||
|
||||
// Call the procedure
|
||||
results result;
|
||||
conn.execute(stmt.bind(10), result);
|
||||
conn.async_execute(stmt.bind(10), result, as_netresult).validate_no_error();
|
||||
|
||||
// Verify results
|
||||
BOOST_TEST_REQUIRE(result.size() == 2u);
|
||||
validate_2fields_meta(result[0].meta(), "one_row_table");
|
||||
BOOST_TEST(result[0].rows() == makerows(2, 1, "f0"));
|
||||
BOOST_TEST(result[0].rows() == makerows(2, 1, "f0"), per_element());
|
||||
BOOST_TEST(result[1].meta().size() == 0u);
|
||||
BOOST_TEST(result[1].rows().size() == 0u);
|
||||
BOOST_TEST(result[1].rows() == rows(), per_element());
|
||||
BOOST_TEST(result[1].affected_rows() == 0u);
|
||||
BOOST_TEST(result[1].warning_count() == 0u);
|
||||
BOOST_TEST(result[1].last_insert_id() == 0u);
|
||||
@ -188,31 +189,31 @@ BOOST_FIXTURE_TEST_CASE(output_params_not_bound, tcp_network_fixture)
|
||||
BOOST_TEST(result.out_params() == row_view());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(output_params_bound, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(output_params_bound, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
// Statement
|
||||
auto stmt = conn.prepare_statement("CALL sp_outparams(?, ?, ?)");
|
||||
auto stmt = conn.async_prepare_statement("CALL sp_outparams(?, ?, ?)", as_netresult).get();
|
||||
|
||||
// Call the procedure
|
||||
results result;
|
||||
conn.execute(stmt.bind(10, nullptr, 30), result);
|
||||
conn.async_execute(stmt.bind(10, nullptr, 30), result, as_netresult).validate_no_error();
|
||||
|
||||
// Verify results
|
||||
BOOST_TEST_REQUIRE(result.size() == 3u);
|
||||
validate_2fields_meta(result[0].meta(), "one_row_table");
|
||||
BOOST_TEST(result[0].rows() == makerows(2, 1, "f0"));
|
||||
BOOST_TEST(result[0].rows() == makerows(2, 1, "f0"), per_element());
|
||||
BOOST_TEST(!result[0].is_out_params());
|
||||
check_meta(result[1].meta(), {column_type::int_, column_type::int_});
|
||||
BOOST_TEST(result[1].rows() == makerows(2, 10, 31));
|
||||
BOOST_TEST(result[1].rows() == makerows(2, 10, 31), per_element());
|
||||
BOOST_TEST(result[1].affected_rows() == 0u);
|
||||
BOOST_TEST(result[1].warning_count() == 0u);
|
||||
BOOST_TEST(result[1].last_insert_id() == 0u);
|
||||
BOOST_TEST(result[1].info() == "");
|
||||
BOOST_TEST(result[1].is_out_params());
|
||||
BOOST_TEST(result[2].meta().size() == 0u);
|
||||
BOOST_TEST(result[2].rows().size() == 0u);
|
||||
BOOST_TEST(result[2].rows() == rows(), per_element());
|
||||
BOOST_TEST(result[2].affected_rows() == 0u);
|
||||
BOOST_TEST(result[2].warning_count() == 0u);
|
||||
BOOST_TEST(result[2].last_insert_id() == 0u);
|
||||
@ -221,34 +222,34 @@ BOOST_FIXTURE_TEST_CASE(output_params_bound, tcp_network_fixture)
|
||||
BOOST_TEST(result.out_params() == makerow(10, 31));
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(output_params_bound_multifn, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(output_params_bound_multifn, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
// Statement
|
||||
auto stmt = conn.prepare_statement("CALL sp_outparams(?, ?, ?)");
|
||||
auto stmt = conn.async_prepare_statement("CALL sp_outparams(?, ?, ?)", as_netresult).get();
|
||||
|
||||
// Call the procedure
|
||||
execution_state st;
|
||||
conn.start_execution(stmt.bind(10, nullptr, 30), st);
|
||||
conn.async_start_execution(stmt.bind(10, nullptr, 30), st, as_netresult).validate_no_error();
|
||||
BOOST_TEST_REQUIRE(st.should_read_rows());
|
||||
validate_2fields_meta(st.meta(), "one_row_table");
|
||||
|
||||
// 1st resultset, rows
|
||||
auto rv = conn.read_some_rows(st);
|
||||
BOOST_TEST(rv == makerows(2, 1, "f0"));
|
||||
auto rv = conn.async_read_some_rows(st, as_netresult).get();
|
||||
BOOST_TEST(rv == makerows(2, 1, "f0"), per_element());
|
||||
BOOST_TEST_REQUIRE(st.should_read_head());
|
||||
BOOST_TEST(!st.is_out_params());
|
||||
|
||||
// out params, head
|
||||
conn.read_resultset_head(st);
|
||||
conn.async_read_resultset_head(st, as_netresult).validate_no_error();
|
||||
BOOST_TEST_REQUIRE(st.should_read_rows());
|
||||
check_meta(st.meta(), {column_type::int_, column_type::int_});
|
||||
|
||||
// out params, rows and eof
|
||||
rv = conn.read_some_rows(st);
|
||||
rv = conn.async_read_some_rows(st, as_netresult).get();
|
||||
BOOST_TEST_REQUIRE(st.should_read_head());
|
||||
BOOST_TEST(rv == makerows(2, 10, 31));
|
||||
BOOST_TEST(rv == makerows(2, 10, 31), per_element());
|
||||
BOOST_TEST(st.affected_rows() == 0u);
|
||||
BOOST_TEST(st.warning_count() == 0u);
|
||||
BOOST_TEST(st.last_insert_id() == 0u);
|
||||
@ -256,7 +257,7 @@ BOOST_FIXTURE_TEST_CASE(output_params_bound_multifn, tcp_network_fixture)
|
||||
BOOST_TEST(st.is_out_params());
|
||||
|
||||
// final eof
|
||||
conn.read_resultset_head(st);
|
||||
conn.async_read_resultset_head(st, as_netresult).validate_no_error();
|
||||
BOOST_TEST_REQUIRE(st.complete());
|
||||
BOOST_TEST(st.meta().size() == 0u);
|
||||
BOOST_TEST(st.affected_rows() == 0u);
|
||||
@ -266,38 +267,33 @@ BOOST_FIXTURE_TEST_CASE(output_params_bound_multifn, tcp_network_fixture)
|
||||
BOOST_TEST(!st.is_out_params());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(with_signal, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(with_signal, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
// Statement
|
||||
auto stmt = conn.prepare_statement("CALL sp_signal()");
|
||||
auto stmt = conn.async_prepare_statement("CALL sp_signal()", as_netresult).get();
|
||||
|
||||
// Call the procedure. It should fail, since we're invoking SIGNAL
|
||||
results result;
|
||||
error_code err;
|
||||
diagnostics diag;
|
||||
conn.execute(stmt.bind(), result, err, diag);
|
||||
|
||||
// Verify results
|
||||
BOOST_TEST(err == common_server_errc::er_no);
|
||||
BOOST_TEST(diag.server_message() == "An error occurred");
|
||||
conn.async_execute(stmt.bind(), result, as_netresult)
|
||||
.validate_error(common_server_errc::er_no, "An error occurred");
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(with_query, tcp_network_fixture)
|
||||
BOOST_FIXTURE_TEST_CASE(with_query, any_connection_fixture)
|
||||
{
|
||||
connect();
|
||||
|
||||
// Call the procedure
|
||||
results result;
|
||||
conn.execute("CALL sp_outparams(42, @var1, @var2)", result);
|
||||
conn.async_execute("CALL sp_outparams(42, @var1, @var2)", result, as_netresult).validate_no_error();
|
||||
|
||||
// Verify results
|
||||
BOOST_TEST_REQUIRE(result.size() == 2u);
|
||||
validate_2fields_meta(result[0].meta(), "one_row_table");
|
||||
BOOST_TEST(result[0].rows() == makerows(2, 1, "f0"));
|
||||
BOOST_TEST(result[1].meta().size() == 0u);
|
||||
BOOST_TEST(result[1].rows().size() == 0u);
|
||||
BOOST_TEST(result[1].rows() == rows(), per_element());
|
||||
BOOST_TEST(result[1].affected_rows() == 0u);
|
||||
BOOST_TEST(result[1].warning_count() == 0u);
|
||||
BOOST_TEST(result[1].last_insert_id() == 0u);
|
||||
|
@ -1,37 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019-2024 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_TEST_UNIT_INCLUDE_TEST_UNIT_RUN_COROUTINE_HPP
|
||||
#define BOOST_MYSQL_TEST_UNIT_INCLUDE_TEST_UNIT_RUN_COROUTINE_HPP
|
||||
|
||||
#include <boost/asio/use_awaitable.hpp>
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_CO_AWAIT
|
||||
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/asio/co_spawn.hpp>
|
||||
#include <boost/asio/use_future.hpp>
|
||||
|
||||
#include "test_common/netfun_helpers.hpp"
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace test {
|
||||
|
||||
inline void run_coroutine(asio::any_io_executor ex, std::function<asio::awaitable<void>(void)> coro)
|
||||
{
|
||||
auto fut = boost::asio::co_spawn(ex, std::move(coro), boost::asio::use_future);
|
||||
run_until_completion(ex);
|
||||
fut.get();
|
||||
}
|
||||
} // namespace test
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_ASIO_HAS_CO_AWAIT
|
||||
|
||||
#endif
|
@ -11,7 +11,6 @@
|
||||
#include <boost/asio/compose.hpp>
|
||||
#include <boost/asio/coroutine.hpp>
|
||||
#include <boost/asio/error.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
@ -23,13 +22,12 @@
|
||||
#include <vector>
|
||||
|
||||
#include "test_common/buffer_concat.hpp"
|
||||
#include "test_common/tracker_executor.hpp"
|
||||
#include "test_unit/test_stream.hpp"
|
||||
|
||||
using namespace boost::mysql::test;
|
||||
using boost::mysql::error_code;
|
||||
|
||||
static boost::asio::io_context ctx;
|
||||
|
||||
std::size_t boost::mysql::test::test_stream::get_size_to_read(std::size_t buffer_size) const
|
||||
{
|
||||
auto it = read_break_offsets_.upper_bound(num_bytes_read_);
|
||||
@ -142,7 +140,7 @@ struct boost::mysql::test::test_stream::write_op : boost::asio::coroutine
|
||||
|
||||
boost::mysql::test::test_stream::executor_type boost::mysql::test::test_stream::get_executor()
|
||||
{
|
||||
return ctx.get_executor();
|
||||
return global_context_executor();
|
||||
}
|
||||
|
||||
// Reading
|
||||
|
@ -6,18 +6,20 @@
|
||||
//
|
||||
|
||||
#include <boost/mysql/client_errc.hpp>
|
||||
#include <boost/mysql/diagnostics.hpp>
|
||||
#include <boost/mysql/error_code.hpp>
|
||||
|
||||
#include <boost/mysql/detail/any_resumable_ref.hpp>
|
||||
#include <boost/mysql/detail/engine_impl.hpp>
|
||||
#include <boost/mysql/detail/next_action.hpp>
|
||||
|
||||
#include <boost/asio/any_completion_handler.hpp>
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/asio/coroutine.hpp>
|
||||
#include <boost/asio/deferred.hpp>
|
||||
#include <boost/asio/error.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
@ -26,7 +28,10 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#include "test_common/create_diagnostics.hpp"
|
||||
#include "test_common/netfun_maker.hpp"
|
||||
#include "test_common/network_result.hpp"
|
||||
#include "test_common/tracker_executor.hpp"
|
||||
#include "test_unit/printing.hpp"
|
||||
|
||||
using namespace boost::mysql::test;
|
||||
@ -169,21 +174,49 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
using test_engine = engine_impl<mock_engine_stream>;
|
||||
|
||||
// Helpers to run the sync and async versions uniformly.
|
||||
// engine_impl uses any_completion_handler, so it needs a wrapper.
|
||||
template <class CompletionToken>
|
||||
void do_async_run(test_engine& eng, any_resumable_ref resumable, CompletionToken&& token)
|
||||
// engine_impl must be adapted because async_run() takes a completion handler,
|
||||
// rather than a generic completion token
|
||||
struct test_engine
|
||||
{
|
||||
eng.async_run(resumable, std::forward<CompletionToken>(token));
|
||||
}
|
||||
engine_impl<mock_engine_stream> value;
|
||||
|
||||
const auto sync_fn = netfun_maker_mem<void, test_engine, any_resumable_ref>::sync_errc_noerrinfo(
|
||||
&test_engine::run
|
||||
);
|
||||
const auto async_fn = netfun_maker_fn<void, test_engine&, any_resumable_ref>::async_noerrinfo(&do_async_run);
|
||||
using signature_t = decltype(sync_fn);
|
||||
struct initiation_t
|
||||
{
|
||||
void operator()(
|
||||
asio::any_completion_handler<void(error_code)> handler,
|
||||
engine* eng,
|
||||
any_resumable_ref resumable
|
||||
) const
|
||||
{
|
||||
eng->async_run(resumable, std::move(handler));
|
||||
}
|
||||
};
|
||||
|
||||
void run(any_resumable_ref resumable, error_code& err) { value.run(resumable, err); }
|
||||
|
||||
template <class CompletionToken>
|
||||
auto async_run(any_resumable_ref resumable, CompletionToken&& token)
|
||||
-> decltype(asio::async_initiate<CompletionToken, void(error_code)>(
|
||||
initiation_t{},
|
||||
token,
|
||||
&value,
|
||||
resumable
|
||||
))
|
||||
{
|
||||
return asio::async_initiate<CompletionToken, void(error_code)>(
|
||||
initiation_t{},
|
||||
token,
|
||||
&value,
|
||||
resumable
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
using netmaker_t = netfun_maker<void, test_engine, any_resumable_ref>;
|
||||
const auto sync_fn = netmaker_t::sync_errc_nodiag(&test_engine::run);
|
||||
const auto async_fn = netmaker_t::async_nodiag(&test_engine::async_run);
|
||||
using signature_t = netmaker_t::signature;
|
||||
|
||||
// A mock for a sans-io algorithm. Can be converted to any_resumable_ref
|
||||
struct mock_algo
|
||||
@ -229,15 +262,14 @@ BOOST_AUTO_TEST_CASE(next_action_read)
|
||||
// Setup
|
||||
std::array<std::uint8_t, 8> buff{};
|
||||
mock_algo algo(next_action::read({buff, tc.ssl_active}));
|
||||
asio::io_context ctx;
|
||||
test_engine eng(ctx.get_executor());
|
||||
test_engine eng{global_context_executor()};
|
||||
|
||||
tc.fn(eng, any_resumable_ref(algo)).validate_no_error();
|
||||
BOOST_TEST(eng.stream().calls.size() == 1u);
|
||||
BOOST_TEST(eng.stream().calls[0].type() == next_action_type::read);
|
||||
BOOST_TEST(eng.stream().calls[0].read_args().use_ssl == tc.ssl_active);
|
||||
BOOST_TEST(eng.stream().calls[0].read_args().buffer.data() == buff.data());
|
||||
BOOST_TEST(eng.stream().calls[0].read_args().buffer.size() == buff.size());
|
||||
tc.fn(eng, any_resumable_ref(algo)).validate_no_error_nodiag();
|
||||
BOOST_TEST(eng.value.stream().calls.size() == 1u);
|
||||
BOOST_TEST(eng.value.stream().calls[0].type() == next_action_type::read);
|
||||
BOOST_TEST(eng.value.stream().calls[0].read_args().use_ssl == tc.ssl_active);
|
||||
BOOST_TEST(eng.value.stream().calls[0].read_args().buffer.data() == buff.data());
|
||||
BOOST_TEST(eng.value.stream().calls[0].read_args().buffer.size() == buff.size());
|
||||
algo.check_calls({
|
||||
{error_code(), 0u},
|
||||
{error_code(), 8u}
|
||||
@ -269,15 +301,14 @@ BOOST_AUTO_TEST_CASE(next_action_write)
|
||||
// Setup
|
||||
const std::array<std::uint8_t, 4> buff{};
|
||||
mock_algo algo(next_action::write({buff, tc.ssl_active}));
|
||||
asio::io_context ctx;
|
||||
test_engine eng(ctx.get_executor());
|
||||
test_engine eng{global_context_executor()};
|
||||
|
||||
tc.fn(eng, any_resumable_ref(algo)).validate_no_error();
|
||||
BOOST_TEST(eng.stream().calls.size() == 1u);
|
||||
BOOST_TEST(eng.stream().calls[0].type() == next_action_type::write);
|
||||
BOOST_TEST(eng.stream().calls[0].write_args().use_ssl == tc.ssl_active);
|
||||
BOOST_TEST(eng.stream().calls[0].write_args().buffer.data() == buff.data());
|
||||
BOOST_TEST(eng.stream().calls[0].write_args().buffer.size() == buff.size());
|
||||
tc.fn(eng, any_resumable_ref(algo)).validate_no_error_nodiag();
|
||||
BOOST_TEST(eng.value.stream().calls.size() == 1u);
|
||||
BOOST_TEST(eng.value.stream().calls[0].type() == next_action_type::write);
|
||||
BOOST_TEST(eng.value.stream().calls[0].write_args().use_ssl == tc.ssl_active);
|
||||
BOOST_TEST(eng.value.stream().calls[0].write_args().buffer.data() == buff.data());
|
||||
BOOST_TEST(eng.value.stream().calls[0].write_args().buffer.size() == buff.size());
|
||||
algo.check_calls({
|
||||
{error_code(), 0u},
|
||||
{error_code(), 4u}
|
||||
@ -312,12 +343,11 @@ BOOST_AUTO_TEST_CASE(next_action_other)
|
||||
{
|
||||
// Setup
|
||||
mock_algo algo(tc.act);
|
||||
asio::io_context ctx;
|
||||
test_engine eng(ctx.get_executor());
|
||||
test_engine eng{global_context_executor()};
|
||||
|
||||
tc.fn(eng, any_resumable_ref(algo)).validate_no_error();
|
||||
BOOST_TEST(eng.stream().calls.size() == 1u);
|
||||
BOOST_TEST(eng.stream().calls[0].type() == tc.act.type());
|
||||
tc.fn(eng, any_resumable_ref(algo)).validate_no_error_nodiag();
|
||||
BOOST_TEST(eng.value.stream().calls.size() == 1u);
|
||||
BOOST_TEST(eng.value.stream().calls[0].type() == tc.act.type());
|
||||
algo.check_calls({
|
||||
{error_code(), 0u},
|
||||
{error_code(), 0u}
|
||||
@ -359,12 +389,14 @@ BOOST_AUTO_TEST_CASE(stream_errors)
|
||||
{
|
||||
// Setup
|
||||
mock_algo algo(tc.act);
|
||||
asio::io_context ctx;
|
||||
test_engine eng(ctx.get_executor(), asio::error::already_open);
|
||||
test_engine eng{
|
||||
{global_context_executor(), asio::error::already_open}
|
||||
};
|
||||
|
||||
tc.fn(eng, any_resumable_ref(algo)).validate_no_error(); // Error gets swallowed by the algo
|
||||
BOOST_TEST(eng.stream().calls.size() == 1u);
|
||||
BOOST_TEST(eng.stream().calls[0].type() == tc.act.type());
|
||||
tc.fn(eng, any_resumable_ref(algo))
|
||||
.validate_no_error_nodiag(); // Error gets swallowed by the algo
|
||||
BOOST_TEST(eng.value.stream().calls.size() == 1u);
|
||||
BOOST_TEST(eng.value.stream().calls[0].type() == tc.act.type());
|
||||
algo.check_calls({
|
||||
{error_code(), 0u},
|
||||
{asio::error::already_open, 0u}
|
||||
@ -395,11 +427,11 @@ BOOST_AUTO_TEST_CASE(resume_error_immediate)
|
||||
{
|
||||
// Setup
|
||||
mock_algo algo(next_action(tc.ec));
|
||||
asio::io_context ctx;
|
||||
test_engine eng(ctx.get_executor());
|
||||
test_engine eng{global_context_executor()};
|
||||
|
||||
tc.fn(eng, any_resumable_ref(algo)).validate_error_exact(tc.ec);
|
||||
BOOST_TEST(eng.stream().calls.size() == 0u);
|
||||
tc.fn(eng, any_resumable_ref(algo))
|
||||
.validate_error(tc.ec, create_server_diag("<diagnostics unavailable>"));
|
||||
BOOST_TEST(eng.value.stream().calls.size() == 0u);
|
||||
algo.check_calls({
|
||||
{error_code(), 0u}
|
||||
});
|
||||
@ -429,17 +461,16 @@ BOOST_AUTO_TEST_CASE(resume_error_successive_calls)
|
||||
{
|
||||
// Setup
|
||||
mock_algo algo(next_action::connect(), next_action(tc.ec));
|
||||
asio::io_context ctx;
|
||||
test_engine eng(ctx.get_executor());
|
||||
test_engine eng{global_context_executor()};
|
||||
|
||||
tc.fn(eng, any_resumable_ref(algo)).validate_error_exact(tc.ec);
|
||||
BOOST_TEST(eng.stream().calls.size() == 1u);
|
||||
BOOST_TEST(eng.stream().calls[0].type() == next_action_type::connect);
|
||||
tc.fn(eng, any_resumable_ref(algo))
|
||||
.validate_error(tc.ec, create_server_diag("<diagnostics unavailable>"));
|
||||
BOOST_TEST(eng.value.stream().calls.size() == 1u);
|
||||
BOOST_TEST(eng.value.stream().calls[0].type() == next_action_type::connect);
|
||||
algo.check_calls({
|
||||
{error_code(), 0u},
|
||||
{error_code(), 0u}
|
||||
});
|
||||
// Note: the testing infrastructure checks that we don't do extra posts in the async functions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,15 +23,15 @@ BOOST_AUTO_TEST_SUITE(test_connection_use_after_move)
|
||||
|
||||
using test_connection = connection<test_stream>;
|
||||
|
||||
using query_netfun_maker = netfun_maker_mem<void, test_connection, const string_view&, results&>;
|
||||
using query_netfun_maker = netfun_maker<void, test_connection, const string_view&, results&>;
|
||||
|
||||
struct
|
||||
{
|
||||
query_netfun_maker::signature query;
|
||||
const char* name;
|
||||
} all_fns[] = {
|
||||
{query_netfun_maker::sync_errc(&test_connection::execute), "sync" },
|
||||
{query_netfun_maker::async_errinfo(&test_connection::async_execute), "async"},
|
||||
{query_netfun_maker::sync_errc(&test_connection::execute), "sync" },
|
||||
{query_netfun_maker::async_diag(&test_connection::async_execute), "async"},
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_CASE(use_move_constructed_connection)
|
||||
|
@ -22,16 +22,16 @@
|
||||
#include <boost/mysql/detail/engine_impl.hpp>
|
||||
#include <boost/mysql/detail/engine_stream_adaptor.hpp>
|
||||
|
||||
#include <boost/asio/deferred.hpp>
|
||||
#include <boost/asio/error.hpp>
|
||||
#include <boost/asio/use_awaitable.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "test_common/assert_buffer_equals.hpp"
|
||||
#include "test_common/buffer_concat.hpp"
|
||||
#include "test_common/netfun_maker.hpp"
|
||||
#include "test_common/network_result.hpp"
|
||||
#include "test_common/printing.hpp"
|
||||
#include "test_unit/create_coldef_frame.hpp"
|
||||
#include "test_unit/create_execution_processor.hpp"
|
||||
@ -42,11 +42,11 @@
|
||||
#include "test_unit/create_row_message.hpp"
|
||||
#include "test_unit/create_statement.hpp"
|
||||
#include "test_unit/fail_count.hpp"
|
||||
#include "test_unit/run_coroutine.hpp"
|
||||
#include "test_unit/test_stream.hpp"
|
||||
|
||||
using namespace boost::mysql::test;
|
||||
using namespace boost::mysql;
|
||||
namespace asio = boost::asio;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(test_misc)
|
||||
|
||||
@ -54,7 +54,6 @@ using test_connection = connection<test_stream>;
|
||||
|
||||
// Make sure async_execute() and friends don't cause side
|
||||
// effects in the initiation
|
||||
#ifdef BOOST_ASIO_HAS_CO_AWAIT
|
||||
BOOST_AUTO_TEST_CASE(async_execute_side_effects_in_initiation)
|
||||
{
|
||||
test_connection conn;
|
||||
@ -65,16 +64,13 @@ BOOST_AUTO_TEST_CASE(async_execute_side_effects_in_initiation)
|
||||
.add_bytes(create_ok_frame(1, ok_builder().affected_rows(2).build()))
|
||||
.add_bytes(create_ok_frame(1, ok_builder().affected_rows(1).build()));
|
||||
|
||||
// Launch coroutine and wait for completion
|
||||
run_coroutine(conn.get_executor(), [&]() -> boost::asio::awaitable<void> {
|
||||
// Call both queries but don't wait on them yet, so they don't initiate
|
||||
auto aw1 = conn.async_execute("Q1", result1, boost::asio::use_awaitable);
|
||||
auto aw2 = conn.async_execute("Q2", result2, boost::asio::use_awaitable);
|
||||
// Create two queries as deferred objects, but don't run them yet
|
||||
auto q1 = conn.async_execute("Q1", result1, asio::deferred);
|
||||
auto q2 = conn.async_execute("Q2", result2, asio::deferred);
|
||||
|
||||
// Run them in reverse order
|
||||
co_await std::move(aw2);
|
||||
co_await std::move(aw1);
|
||||
});
|
||||
// Run them in reverse order
|
||||
std::move(q2)(as_netresult).validate_no_error();
|
||||
std::move(q1)(as_netresult).validate_no_error();
|
||||
|
||||
// Check that we wrote Q2's message first, then Q1's
|
||||
auto expected = concat_copy(
|
||||
@ -87,7 +83,6 @@ BOOST_AUTO_TEST_CASE(async_execute_side_effects_in_initiation)
|
||||
BOOST_TEST(result2.affected_rows() == 2u);
|
||||
BOOST_TEST(result1.affected_rows() == 1u);
|
||||
}
|
||||
#endif // BOOST_ASIO_HAS_CO_AWAIT
|
||||
|
||||
// spotcheck for the dynamic interface
|
||||
// Verifies that execute (dynamic interface) works when rows come in separate batches
|
||||
@ -148,7 +143,7 @@ BOOST_AUTO_TEST_CASE(execute_multiple_batches)
|
||||
|
||||
// Regression check: execute statement with iterator range with a reference type that is convertible to
|
||||
// field_view, but not equal to field_view
|
||||
BOOST_AUTO_TEST_CASE(execute_stmt_iterator_reference_not_field_view)
|
||||
BOOST_AUTO_TEST_CASE(stmt_iterator_reference_not_field_view)
|
||||
{
|
||||
results result;
|
||||
auto stmt = statement_builder().id(1).num_params(2).build();
|
||||
@ -173,8 +168,6 @@ BOOST_AUTO_TEST_CASE(execute_stmt_iterator_reference_not_field_view)
|
||||
BOOST_TEST(result.info() == "1st");
|
||||
}
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_CO_AWAIT
|
||||
|
||||
// The serialized form of executing a statement with ID=1, params=("test", nullptr)
|
||||
constexpr std::uint8_t execute_stmt_msg[] = {
|
||||
0x15, 0x00, 0x00, 0x00, 0x17, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
|
||||
@ -187,52 +180,48 @@ BOOST_AUTO_TEST_CASE(async_execute_deferred_lifetimes_rvalues)
|
||||
{
|
||||
test_connection conn;
|
||||
|
||||
run_coroutine(conn.get_executor(), [&]() -> boost::asio::awaitable<void> {
|
||||
results result;
|
||||
conn.stream().add_bytes(create_ok_frame(1, ok_builder().info("1st").build()));
|
||||
results result;
|
||||
conn.stream().add_bytes(create_ok_frame(1, ok_builder().info("1st").build()));
|
||||
|
||||
// Deferred op. Execution request is a temporary
|
||||
auto aw = conn.async_execute(
|
||||
statement_builder().id(1).num_params(2).build().bind(std::string("test"), nullptr),
|
||||
result,
|
||||
boost::asio::use_awaitable
|
||||
);
|
||||
co_await std::move(aw);
|
||||
// Deferred op. Execution request is a temporary
|
||||
auto op = conn.async_execute(
|
||||
statement_builder().id(1).num_params(2).build().bind(std::string("test"), nullptr),
|
||||
result,
|
||||
asio::deferred
|
||||
);
|
||||
std::move(op)(as_netresult).validate_no_error();
|
||||
|
||||
// verify that the op had the intended effects
|
||||
BOOST_MYSQL_ASSERT_BUFFER_EQUALS(conn.stream().bytes_written(), execute_stmt_msg);
|
||||
BOOST_TEST(result.info() == "1st");
|
||||
});
|
||||
// verify that the op had the intended effects
|
||||
BOOST_MYSQL_ASSERT_BUFFER_EQUALS(conn.stream().bytes_written(), execute_stmt_msg);
|
||||
BOOST_TEST(result.info() == "1st");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_execute_deferred_lifetimes_lvalues)
|
||||
{
|
||||
test_connection conn;
|
||||
|
||||
run_coroutine(conn.get_executor(), [&]() -> boost::asio::awaitable<void> {
|
||||
results result;
|
||||
results result;
|
||||
|
||||
// Create a bound statement on the heap. This helps tooling detect memory errors
|
||||
using bound_stmt_t = bound_statement_tuple<std::tuple<std::string, std::nullptr_t>>;
|
||||
auto stmt = statement_builder().id(1).num_params(2).build();
|
||||
std::unique_ptr<bound_stmt_t> stmt_ptr{new bound_stmt_t{stmt.bind(std::string("test"), nullptr)}};
|
||||
// Create a bound statement on the heap. This helps tooling detect memory errors
|
||||
using bound_stmt_t = bound_statement_tuple<std::tuple<std::string, std::nullptr_t>>;
|
||||
auto stmt = statement_builder().id(1).num_params(2).build();
|
||||
std::unique_ptr<bound_stmt_t> stmt_ptr{new bound_stmt_t{stmt.bind(std::string("test"), nullptr)}};
|
||||
|
||||
// Messages
|
||||
conn.stream().add_bytes(create_ok_frame(1, ok_builder().info("1st").build()));
|
||||
// Messages
|
||||
conn.stream().add_bytes(create_ok_frame(1, ok_builder().info("1st").build()));
|
||||
|
||||
// Deferred op
|
||||
auto aw = conn.async_execute(*stmt_ptr, result, boost::asio::use_awaitable);
|
||||
// Deferred op
|
||||
auto op = conn.async_execute(*stmt_ptr, result, asio::deferred);
|
||||
|
||||
// Free the statement
|
||||
stmt_ptr.reset();
|
||||
// Free the statement
|
||||
stmt_ptr.reset();
|
||||
|
||||
// Actually run the op
|
||||
co_await std::move(aw);
|
||||
// Actually run the op
|
||||
std::move(op)(as_netresult).validate_no_error();
|
||||
|
||||
// verify that the op had the intended effects
|
||||
BOOST_MYSQL_ASSERT_BUFFER_EQUALS(conn.stream().bytes_written(), execute_stmt_msg);
|
||||
BOOST_TEST(result.info() == "1st");
|
||||
});
|
||||
// verify that the op had the intended effects
|
||||
BOOST_MYSQL_ASSERT_BUFFER_EQUALS(conn.stream().bytes_written(), execute_stmt_msg);
|
||||
BOOST_TEST(result.info() == "1st");
|
||||
}
|
||||
|
||||
// Verify that we correctly perform a decay-copy of the parameters and the
|
||||
@ -241,50 +230,45 @@ BOOST_AUTO_TEST_CASE(async_start_execution_deferred_lifetimes_rvalues)
|
||||
{
|
||||
test_connection conn;
|
||||
|
||||
run_coroutine(conn.get_executor(), [&]() -> boost::asio::awaitable<void> {
|
||||
execution_state st;
|
||||
conn.stream().add_bytes(create_ok_frame(1, ok_builder().info("1st").build()));
|
||||
execution_state st;
|
||||
conn.stream().add_bytes(create_ok_frame(1, ok_builder().info("1st").build()));
|
||||
|
||||
// Deferred op. Execution request is a temporary
|
||||
auto aw = conn.async_start_execution(
|
||||
statement_builder().id(1).num_params(2).build().bind(std::string("test"), nullptr),
|
||||
st,
|
||||
boost::asio::use_awaitable
|
||||
);
|
||||
co_await std::move(aw);
|
||||
// Deferred op. Execution request is a temporary
|
||||
auto op = conn.async_start_execution(
|
||||
statement_builder().id(1).num_params(2).build().bind(std::string("test"), nullptr),
|
||||
st,
|
||||
asio::deferred
|
||||
);
|
||||
std::move(op)(as_netresult).validate_no_error();
|
||||
|
||||
// verify that the op had the intended effects
|
||||
BOOST_MYSQL_ASSERT_BUFFER_EQUALS(conn.stream().bytes_written(), execute_stmt_msg);
|
||||
BOOST_TEST(st.info() == "1st");
|
||||
});
|
||||
// verify that the op had the intended effects
|
||||
BOOST_MYSQL_ASSERT_BUFFER_EQUALS(conn.stream().bytes_written(), execute_stmt_msg);
|
||||
BOOST_TEST(st.info() == "1st");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(deferred_lifetimes_lvalues)
|
||||
BOOST_AUTO_TEST_CASE(deferred_lifetimes_statement)
|
||||
{
|
||||
test_connection conn;
|
||||
execution_state st;
|
||||
conn.stream().add_bytes(create_ok_frame(1, ok_builder().info("1st").build()));
|
||||
|
||||
run_coroutine(conn.get_executor(), [&]() -> boost::asio::awaitable<void> {
|
||||
execution_state st;
|
||||
conn.stream().add_bytes(create_ok_frame(1, ok_builder().info("1st").build()));
|
||||
// Create a bound statement on the heap. This helps tooling detect memory errors
|
||||
using bound_stmt_t = bound_statement_tuple<std::tuple<std::string, std::nullptr_t>>;
|
||||
auto stmt = statement_builder().id(1).num_params(2).build();
|
||||
std::unique_ptr<bound_stmt_t> stmt_ptr{new bound_stmt_t{stmt.bind(std::string("test"), nullptr)}};
|
||||
|
||||
// Create a bound statement on the heap. This helps tooling detect memory errors
|
||||
using bound_stmt_t = bound_statement_tuple<std::tuple<std::string, std::nullptr_t>>;
|
||||
auto stmt = statement_builder().id(1).num_params(2).build();
|
||||
std::unique_ptr<bound_stmt_t> stmt_ptr{new bound_stmt_t{stmt.bind(std::string("test"), nullptr)}};
|
||||
// Deferred op
|
||||
auto op = conn.async_start_execution(*stmt_ptr, st, asio::deferred);
|
||||
|
||||
// Deferred op
|
||||
auto aw = conn.async_start_execution(*stmt_ptr, st, boost::asio::use_awaitable);
|
||||
// Free the statement
|
||||
stmt_ptr.reset();
|
||||
|
||||
// Free the statement
|
||||
stmt_ptr.reset();
|
||||
// Actually run the op
|
||||
std::move(op)(as_netresult).validate_no_error();
|
||||
|
||||
// Actually run the op
|
||||
co_await std::move(aw);
|
||||
|
||||
// verify that the op had the intended effects
|
||||
BOOST_MYSQL_ASSERT_BUFFER_EQUALS(conn.stream().bytes_written(), execute_stmt_msg);
|
||||
BOOST_TEST(st.info() == "1st");
|
||||
});
|
||||
// verify that the op had the intended effects
|
||||
BOOST_MYSQL_ASSERT_BUFFER_EQUALS(conn.stream().bytes_written(), execute_stmt_msg);
|
||||
BOOST_TEST(st.info() == "1st");
|
||||
}
|
||||
|
||||
// Verify that async_close_statement doesn't require the passed-in statement to be alive. Only
|
||||
@ -293,58 +277,38 @@ BOOST_AUTO_TEST_CASE(async_close_statement_handle_deferred_tokens)
|
||||
{
|
||||
test_connection conn;
|
||||
|
||||
run_coroutine(conn.get_executor(), [&]() -> boost::asio::awaitable<void> {
|
||||
auto stmt = statement_builder().id(3).build();
|
||||
conn.stream().add_bytes(create_ok_frame(1, ok_builder().build()));
|
||||
auto stmt = statement_builder().id(3).build();
|
||||
conn.stream().add_bytes(create_ok_frame(1, ok_builder().build()));
|
||||
|
||||
// Deferred op
|
||||
auto aw = conn.async_close_statement(stmt, boost::asio::use_awaitable);
|
||||
// Deferred op
|
||||
auto op = conn.async_close_statement(stmt, asio::deferred);
|
||||
|
||||
// Invalidate the original variable
|
||||
stmt = statement_builder().id(42).build();
|
||||
// Invalidate the original variable
|
||||
stmt = statement_builder().id(42).build();
|
||||
|
||||
// Run the operation
|
||||
co_await std::move(aw);
|
||||
// Run the operation
|
||||
std::move(op)(as_netresult).validate_no_error();
|
||||
|
||||
// verify that the op had the intended effects
|
||||
const auto expected_message = concat_copy(
|
||||
create_frame(0, {0x19, 0x03, 0x00, 0x00, 0x00}),
|
||||
create_frame(0, {0x0e})
|
||||
);
|
||||
BOOST_MYSQL_ASSERT_BUFFER_EQUALS(conn.stream().bytes_written(), expected_message);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
// Verify that async functions with signature void(error_code, T)
|
||||
// get their executor propagated correctly
|
||||
BOOST_AUTO_TEST_CASE(nonvoid_signature_executor_propagation)
|
||||
{
|
||||
// Setup
|
||||
test_connection conn;
|
||||
execution_state st;
|
||||
|
||||
// Function wrapper. This takes care of validating executor propagation
|
||||
auto fn = netfun_maker_mem<rows_view, test_connection, execution_state&>::async_errinfo(
|
||||
&test_connection::async_read_some_rows
|
||||
// verify that the op had the intended effects
|
||||
const auto expected_message = concat_copy(
|
||||
create_frame(0, {0x19, 0x03, 0x00, 0x00, 0x00}),
|
||||
create_frame(0, {0x0e})
|
||||
);
|
||||
|
||||
// Call it
|
||||
fn(conn, st).validate_no_error();
|
||||
BOOST_MYSQL_ASSERT_BUFFER_EQUALS(conn.stream().bytes_written(), expected_message);
|
||||
}
|
||||
|
||||
// Regression check: when there is a network error, sync functions
|
||||
// returning a value fail with an assertion
|
||||
BOOST_AUTO_TEST_CASE(net_error_prepare_statement)
|
||||
{
|
||||
using netmaker_stmt = netfun_maker_mem<statement, test_connection, string_view>;
|
||||
using netmaker_stmt = netfun_maker<statement, test_connection, string_view>;
|
||||
struct
|
||||
{
|
||||
const char* name;
|
||||
netmaker_stmt::signature prepare_statement;
|
||||
} fns[] = {
|
||||
{"sync", netmaker_stmt::sync_errc(&test_connection::prepare_statement) },
|
||||
{"async", netmaker_stmt::async_errinfo(&test_connection::async_prepare_statement)},
|
||||
{"sync", netmaker_stmt::sync_errc(&test_connection::prepare_statement) },
|
||||
{"async", netmaker_stmt::async_diag(&test_connection::async_prepare_statement)},
|
||||
};
|
||||
|
||||
for (const auto& fn : fns)
|
||||
@ -355,21 +319,21 @@ BOOST_AUTO_TEST_CASE(net_error_prepare_statement)
|
||||
test_connection conn;
|
||||
conn.stream().set_fail_count(fail_count(0, boost::asio::error::connection_reset));
|
||||
|
||||
fn.prepare_statement(conn, "SELECT 1").validate_error_exact(boost::asio::error::connection_reset);
|
||||
fn.prepare_statement(conn, "SELECT 1").validate_error(boost::asio::error::connection_reset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(net_error_read_some_rows)
|
||||
{
|
||||
using netmaker_stmt = netfun_maker_mem<rows_view, test_connection, execution_state&>;
|
||||
using netmaker_stmt = netfun_maker<rows_view, test_connection, execution_state&>;
|
||||
struct
|
||||
{
|
||||
const char* name;
|
||||
netmaker_stmt::signature read_some_rows;
|
||||
} fns[] = {
|
||||
{"sync", netmaker_stmt::sync_errc(&test_connection::read_some_rows) },
|
||||
{"async", netmaker_stmt::async_errinfo(&test_connection::async_read_some_rows)},
|
||||
{"sync", netmaker_stmt::sync_errc(&test_connection::read_some_rows) },
|
||||
{"async", netmaker_stmt::async_diag(&test_connection::async_read_some_rows)},
|
||||
};
|
||||
|
||||
for (const auto& fn : fns)
|
||||
@ -382,21 +346,21 @@ BOOST_AUTO_TEST_CASE(net_error_read_some_rows)
|
||||
execution_state st;
|
||||
add_meta(get_iface(st), {column_type::bigint});
|
||||
|
||||
fn.read_some_rows(conn, st).validate_error_exact(boost::asio::error::connection_reset);
|
||||
fn.read_some_rows(conn, st).validate_error(boost::asio::error::connection_reset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(net_error_void_signature)
|
||||
{
|
||||
using netmaker_execute = netfun_maker_mem<void, test_connection, const string_view&, results&>;
|
||||
using netmaker_execute = netfun_maker<void, test_connection, const string_view&, results&>;
|
||||
struct
|
||||
{
|
||||
const char* name;
|
||||
netmaker_execute::signature execute;
|
||||
} fns[] = {
|
||||
{"sync", netmaker_execute::sync_errc(&test_connection::execute) },
|
||||
{"async", netmaker_execute::async_errinfo(&test_connection::async_execute)},
|
||||
{"sync", netmaker_execute::sync_errc(&test_connection::execute) },
|
||||
{"async", netmaker_execute::async_diag(&test_connection::async_execute)},
|
||||
};
|
||||
|
||||
for (const auto& fn : fns)
|
||||
@ -408,7 +372,7 @@ BOOST_AUTO_TEST_CASE(net_error_void_signature)
|
||||
conn.stream().set_fail_count(fail_count(0, boost::asio::error::connection_reset));
|
||||
results r;
|
||||
|
||||
fn.execute(conn, "SELECT 1", r).validate_error_exact(boost::asio::error::connection_reset);
|
||||
fn.execute(conn, "SELECT 1", r).validate_error(boost::asio::error::connection_reset);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -452,11 +416,8 @@ struct test_any_connection_fixture
|
||||
}
|
||||
};
|
||||
|
||||
auto pipeline_fn = netfun_maker_mem<
|
||||
void,
|
||||
any_connection,
|
||||
const pipeline_request&,
|
||||
std::vector<stage_response>&>::async_errinfo(&any_connection::async_run_pipeline);
|
||||
auto pipeline_fn = netfun_maker<void, any_connection, const pipeline_request&, std::vector<stage_response>&>::
|
||||
async_diag(&any_connection::async_run_pipeline);
|
||||
|
||||
// empty pipelines complete immediately, posting adequately
|
||||
BOOST_FIXTURE_TEST_CASE(empty_pipeline, test_any_connection_fixture)
|
||||
@ -483,7 +444,7 @@ BOOST_FIXTURE_TEST_CASE(pipeline_fatal_error, test_any_connection_fixture)
|
||||
stream().set_fail_count(fail_count(1, boost::asio::error::network_reset));
|
||||
|
||||
// Run it
|
||||
pipeline_fn(conn, req, res).validate_error_exact(boost::asio::error::network_reset);
|
||||
pipeline_fn(conn, req, res).validate_error(boost::asio::error::network_reset);
|
||||
|
||||
// Validate the results
|
||||
BOOST_TEST(res.size() == 2u);
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "test_common/assert_buffer_equals.hpp"
|
||||
#include "test_common/check_meta.hpp"
|
||||
#include "test_common/netfun_maker.hpp"
|
||||
#include "test_unit/create_coldef_frame.hpp"
|
||||
@ -33,9 +32,9 @@ BOOST_AUTO_TEST_SUITE(test_multifn)
|
||||
|
||||
using test_connection = connection<test_stream>;
|
||||
|
||||
using start_query_netm = netfun_maker_mem<void, test_connection, const string_view&, execution_state&>;
|
||||
using read_resultset_head_netm = netfun_maker_mem<void, test_connection, execution_state&>;
|
||||
using read_some_rows_netm = netfun_maker_mem<rows_view, test_connection, execution_state&>;
|
||||
using start_query_netm = netfun_maker<void, test_connection, const string_view&, execution_state&>;
|
||||
using read_resultset_head_netm = netfun_maker<void, test_connection, execution_state&>;
|
||||
using read_some_rows_netm = netfun_maker<rows_view, test_connection, execution_state&>;
|
||||
|
||||
struct
|
||||
{
|
||||
@ -48,9 +47,9 @@ struct
|
||||
read_resultset_head_netm::sync_errc(&test_connection::read_resultset_head),
|
||||
read_some_rows_netm::sync_errc(&test_connection::read_some_rows),
|
||||
"sync" },
|
||||
{start_query_netm::async_errinfo(&test_connection::async_start_execution),
|
||||
read_resultset_head_netm::async_errinfo(&test_connection::async_read_resultset_head),
|
||||
read_some_rows_netm::async_errinfo(&test_connection::async_read_some_rows),
|
||||
{start_query_netm::async_diag(&test_connection::async_start_execution),
|
||||
read_resultset_head_netm::async_diag(&test_connection::async_read_resultset_head),
|
||||
read_some_rows_netm::async_diag(&test_connection::async_read_some_rows),
|
||||
"async"},
|
||||
};
|
||||
|
||||
|
@ -37,8 +37,8 @@ using row1 = std::tuple<int, float>;
|
||||
using row2 = std::tuple<double>;
|
||||
|
||||
using state_t = static_execution_state<row1, row1, row2, row1, row2>;
|
||||
using netfun_maker_row1 = netfun_maker_mem<std::size_t, test_connection, state_t&, span<row1> >;
|
||||
using netfun_maker_row2 = netfun_maker_mem<std::size_t, test_connection, state_t&, span<row2> >;
|
||||
using netfun_maker_row1 = netfun_maker<std::size_t, test_connection, state_t&, span<row1> >;
|
||||
using netfun_maker_row2 = netfun_maker<std::size_t, test_connection, state_t&, span<row2> >;
|
||||
|
||||
struct
|
||||
{
|
||||
@ -49,8 +49,8 @@ struct
|
||||
{netfun_maker_row1::sync_errc(&test_connection::read_some_rows),
|
||||
netfun_maker_row2::sync_errc(&test_connection::read_some_rows),
|
||||
"sync" },
|
||||
{netfun_maker_row1::async_errinfo(&test_connection::async_read_some_rows),
|
||||
netfun_maker_row2::async_errinfo(&test_connection::async_read_some_rows),
|
||||
{netfun_maker_row1::async_diag(&test_connection::async_read_some_rows),
|
||||
netfun_maker_row2::async_diag(&test_connection::async_read_some_rows),
|
||||
"async"},
|
||||
};
|
||||
|
||||
@ -165,7 +165,7 @@ BOOST_AUTO_TEST_CASE(error_row_type_mismatch)
|
||||
// 1st resultset: row1. Note that this will consume the message
|
||||
fix.stream().add_bytes(create_text_row_message(0, 10, 4.2f));
|
||||
fns.read_some_rows_row2(fix.conn, fix.st, fix.storage2)
|
||||
.validate_error_exact(client_errc::row_type_mismatch);
|
||||
.validate_error(client_errc::row_type_mismatch);
|
||||
|
||||
// Advance resultset
|
||||
fix.add_ok();
|
||||
@ -177,7 +177,7 @@ BOOST_AUTO_TEST_CASE(error_row_type_mismatch)
|
||||
// 3rd resultset: row2
|
||||
fix.stream().add_bytes(create_text_row_message(1, 9.1));
|
||||
fns.read_some_rows_row1(fix.conn, fix.st, fix.storage1)
|
||||
.validate_error_exact(client_errc::row_type_mismatch);
|
||||
.validate_error(client_errc::row_type_mismatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user