2
0
mirror of https://github.com/boostorg/mysql.git synced 2026-02-14 00:42:53 +00:00

Added error_info

This commit is contained in:
ruben
2020-01-18 20:05:21 +00:00
parent 034ab38f05
commit a65e86af1d
21 changed files with 469 additions and 203 deletions

View File

@@ -8,6 +8,53 @@ matrix:
compiler: gcc
services:
- mysql
env:
- CMAKE_OPTIONS=-DCMAKE_BUILD_TYPE=Debug
addons:
apt:
update: true
sources:
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- boost1.70
- os: linux
dist: bionic
sudo: true
compiler: gcc
services:
- mysql
env:
- CMAKE_OPTIONS=-DCMAKE_BUILD_TYPE=Release
addons:
apt:
update: true
sources:
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- boost1.70
- os: linux
dist: bionic
sudo: true
compiler: gcc
services:
- mysql
env:
- CMAKE_OPTIONS=-DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
addons:
apt:
update: true
sources:
- sourceline: 'ppa:mhier/libboost-latest'
packages:
- boost1.70
- os: linux
dist: bionic
sudo: true
compiler: gcc
services:
- mysql
env:
- CMAKE_OPTIONS=-DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
addons:
apt:
update: true
@@ -27,12 +74,24 @@ matrix:
before_install:
- mysql.server start
env:
- CMAKE_OPTIONS=-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl
- CMAKE_OPTIONS=-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DCMAKE_BUILD_TYPE=Debug
- os: osx
osx_image: xcode11.3
sudo: true
compiler: clang
addons:
homebrew:
packages:
- boost
- mysql
before_install:
- mysql.server start
env:
- CMAKE_OPTIONS=-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DCMAKE_BUILD_TYPE=Release
script:
- mkdir -p build
- cd build
- cmake -DCMAKE_BUILD_TYPE=Release $CMAKE_OPTIONS ..
- cmake $CMAKE_OPTIONS ..
- make -j CTEST_OUTPUT_ON_FAILURE=1 all test

View File

@@ -1,3 +1,14 @@
error_info
Add error_info to all existing API
Document
Add error_info to examples
Add error_info to integ tests
Make error_info optional for synchronous (?)
Use error_info in sync exc
Add validation of exception messages
Error code descriptions
Review async testing with futures (it no longer tests the error_info parameter)
Verify that error_codes are cleared in sync errc stuff
Prepared statements
Multiresultset
Text protocol
@@ -9,9 +20,6 @@ Handshake
Usability
Incomplete query reads: how does this affect further queries?
Metadata in rows: being able to index by name
Errors
Error code descriptions
Error text is lost in the protocol
Iterators for sync resultset iteration
Better interface for connection_params data structure
Consideration of timezones

View File

@@ -27,11 +27,11 @@ void print_employee(const mysql::row& employee)
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
}
void die_on_error(const mysql::error_code& err)
void die_on_error(const mysql::error_code& err, const mysql::error_info& info = mysql::error_info())
{
if (err)
{
std::cerr << "Error: " << err << std::endl;
std::cerr << "Error: " << err << ": " << info.message() << std::endl;
exit(1);
}
}
@@ -55,10 +55,10 @@ public:
void connect()
{
connection.next_level().async_connect(ep, [this](const mysql::error_code& err) {
connection.next_level().async_connect(ep, [this](mysql::error_code err) {
die_on_error(err);
connection.async_handshake(conn_params, [this](const mysql::error_code& err) {
die_on_error(err);
connection.async_handshake(conn_params, [this](mysql::error_code err, const mysql::error_info& info) {
die_on_error(err, info);
query_employees();
});
});
@@ -67,11 +67,13 @@ public:
void query_employees()
{
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
connection.async_query(sql, [this](const mysql::error_code& err, mysql::tcp_resultset&& result) {
die_on_error(err);
connection.async_query(sql, [this](mysql::error_code err, const mysql::error_info& info,
mysql::tcp_resultset&& result
) {
die_on_error(err, info);
resultset = std::move(result);
resultset.async_fetch_all([this](const mysql::error_code& err, const auto& rows) {
die_on_error(err);
resultset.async_fetch_all([this](mysql::error_code err, const mysql::error_info& info, const auto& rows) {
die_on_error(err, info);
for (const auto& employee: rows)
{
print_employee(employee);
@@ -84,8 +86,9 @@ public:
void update_slacker()
{
const char* sql = "UPDATE employee SET salary = 15000 WHERE last_name = 'Slacker'";
connection.async_query(sql, [this](const mysql::error_code& err, [[maybe_unused]] mysql::tcp_resultset&& result) {
die_on_error(err);
connection.async_query(sql, [this](mysql::error_code err, const mysql::error_info& info,
[[maybe_unused]] mysql::tcp_resultset&& result) {
die_on_error(err, info);
assert(result.fields().size() == 0);
query_intern();
});
@@ -94,11 +97,12 @@ public:
void query_intern()
{
const char* sql = "SELECT salary FROM employee WHERE last_name = 'Slacker'";
connection.async_query(sql, [this](const mysql::error_code& err, mysql::tcp_resultset&& result) {
die_on_error(err);
connection.async_query(sql, [this](const mysql::error_code& err, mysql::error_info info,
mysql::tcp_resultset&& result) {
die_on_error(err, info);
resultset = std::move(result);
resultset.async_fetch_all([](const mysql::error_code& err, const auto& rows) {
die_on_error(err);
resultset.async_fetch_all([](const mysql::error_code& err, mysql::error_info info, const auto& rows) {
die_on_error(err, info);
assert(rows.size() == 1);
[[maybe_unused]] auto salary = std::get<double>(rows[0].values()[0]);
assert(salary == 15000);

View File

@@ -85,7 +85,7 @@ public:
const Stream& next_level() const { return next_level_; }
/// Performs the MySQL-level handshake (synchronous with error code version).
void handshake(const connection_params& params, error_code& ec);
void handshake(const connection_params& params, error_code& ec, error_info& info);
/// Performs the MySQL-level handshake (synchronous with exceptions version).
void handshake(const connection_params& params);
@@ -96,7 +96,7 @@ public:
* until the operation completes, as no copy is made by the library.
*/
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code))
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, error_info))
async_handshake(const connection_params& params, CompletionToken&& token);
/**
@@ -111,14 +111,14 @@ public:
* \warning After query() has returned, you should read the entire resultset
* before calling query() again. Otherwise, the results are undefined.
*/
resultset<Stream> query(std::string_view query_string, error_code&);
resultset<Stream> query(std::string_view query_string, error_code&, error_info&);
/// Executes a SQL text query (sync with exceptions version).
resultset<Stream> query(std::string_view query_string);
/// Executes a SQL text query (async version).
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, resultset<Stream>))
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, error_info, resultset<Stream>))
async_query(std::string_view query_string, CompletionToken&& token);
};

View File

@@ -2,6 +2,7 @@
#define MYSQL_ASIO_ERROR_HPP
#include <boost/system/error_code.hpp>
#include <string>
namespace mysql
{
@@ -27,6 +28,20 @@ enum class Error : int
/// An alias for boost::system error codes.
using error_code = boost::system::error_code;
/// Additional information about error conditions
class error_info
{
std::string msg_;
public:
error_info(std::string&& err = {}): msg_(std::move(err)) {}
const std::string& message() const noexcept { return msg_; }
void set_message(std::string&& err) { msg_ = std::move(err); }
void clear() noexcept { msg_.clear(); }
};
inline bool operator==(const error_info& lhs, const error_info& rhs) noexcept { return lhs.message() == rhs.message(); }
inline bool operator!=(const error_info& lhs, const error_info& rhs) noexcept { return !(lhs==rhs); }
inline std::ostream& operator<<(std::ostream& os, const error_info& v) { return os << v.message(); }
}
#include "mysql/impl/error.hpp"

View File

@@ -28,10 +28,19 @@ inline handshake_params to_handshake_params(
template <typename Stream>
void mysql::connection<Stream>::handshake(
const connection_params& params,
error_code& errc
error_code& errc,
error_info& info
)
{
detail::hanshake(channel_, detail::to_handshake_params(params), buffer_, errc);
errc.clear();
info.clear();
detail::hanshake(
channel_,
detail::to_handshake_params(params),
buffer_,
errc,
info
);
// TODO: should we close() the stream in case of error?
}
@@ -41,13 +50,14 @@ void mysql::connection<Stream>::handshake(
)
{
error_code errc;
handshake(params, errc);
detail::check_error_code(errc);
error_info info;
handshake(params, errc, info);
detail::check_error_code(errc, info);
}
template <typename Stream>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(mysql::error_code))
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(mysql::error_code, mysql::error_info))
mysql::connection<Stream>::async_handshake(
const connection_params& params,
CompletionToken&& token
@@ -65,11 +75,14 @@ mysql::connection<Stream>::async_handshake(
template <typename Stream>
mysql::resultset<Stream> mysql::connection<Stream>::query(
std::string_view query_string,
error_code& err
error_code& err,
error_info& info
)
{
err.clear();
info.clear();
resultset<Stream> res;
detail::execute_query(channel_, query_string, res, err);
detail::execute_query(channel_, query_string, res, err, info);
return res;
}
@@ -80,14 +93,18 @@ mysql::resultset<Stream> mysql::connection<Stream>::query(
{
resultset<Stream> res;
error_code err;
detail::execute_query(channel_, query_string, res, err);
detail::check_error_code(err);
error_info info;
detail::execute_query(channel_, query_string, res, err, info);
detail::check_error_code(err, info);
return res;
}
template <typename Stream>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(mysql::error_code, mysql::resultset<Stream>))
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
void(mysql::error_code, mysql::error_info, mysql::resultset<Stream>)
)
mysql::connection<Stream>::async_query(
std::string_view query_string,
CompletionToken&& token

View File

@@ -64,11 +64,11 @@ inline boost::system::error_code make_error_code(Error error)
);
}
inline void check_error_code(const error_code& errc)
inline void check_error_code(const error_code& errc, const error_info& info)
{
if (errc)
{
throw boost::system::system_error(errc);
throw boost::system::system_error(errc, info.message());
}
}
@@ -78,4 +78,4 @@ inline void check_error_code(const error_code& errc)
#endif
#endif

View File

@@ -26,11 +26,12 @@ void hanshake(
ChannelType& channel,
const handshake_params& params,
bytestring& buffer,
error_code& err
error_code& err,
error_info& info
);
template <typename ChannelType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code))
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, error_info))
async_handshake(
ChannelType& channel,
const handshake_params& params,
@@ -44,4 +45,4 @@ async_handshake(
#include "mysql/impl/handshake.ipp"
#endif
#endif

View File

@@ -21,7 +21,8 @@ inline std::uint8_t get_collation_first_byte(collation value)
inline error_code deserialize_handshake(
boost::asio::const_buffer buffer,
handshake_packet& output
handshake_packet& output,
error_info& info
)
{
DeserializationContext ctx (boost::asio::buffer(buffer), capabilities());
@@ -33,7 +34,7 @@ inline error_code deserialize_handshake(
}
else if (msg_type == error_packet_header)
{
return process_error_packet(ctx);
return process_error_packet(ctx, info);
}
else if (msg_type != handshake_protocol_version_10)
{
@@ -127,11 +128,11 @@ public:
);
}
error_code process_handshake(bytestring& buffer)
error_code process_handshake(bytestring& buffer, error_info& info)
{
// Deserialize server greeting
handshake_packet handshake;
auto err = deserialize_handshake(boost::asio::buffer(buffer), handshake);
auto err = deserialize_handshake(boost::asio::buffer(buffer), handshake, info);
if (err) return err;
// Check capabilities
@@ -159,7 +160,8 @@ public:
error_code process_handshake_server_response(
bytestring& buffer,
bool& auth_complete
bool& auth_complete,
error_info& info
)
{
DeserializationContext ctx (boost::asio::buffer(buffer), negotiated_caps_);
@@ -174,7 +176,7 @@ public:
}
else if (msg_type == error_packet_header)
{
return process_error_packet(ctx);
return process_error_packet(ctx, info);
}
else if (msg_type != auth_switch_request_header)
{
@@ -200,7 +202,8 @@ public:
}
error_code process_auth_switch_response(
boost::asio::const_buffer buffer
boost::asio::const_buffer buffer,
error_info& info
)
{
DeserializationContext ctx (boost::asio::buffer(buffer), negotiated_caps_);
@@ -208,7 +211,7 @@ public:
if (err) return err;
if (msg_type == error_packet_header)
{
return process_error_packet(ctx);
return process_error_packet(ctx, info);
}
else if (msg_type != ok_packet_header)
{
@@ -228,9 +231,12 @@ void mysql::detail::hanshake(
ChannelType& channel,
const handshake_params& params,
bytestring& buffer,
error_code& err
error_code& err,
error_info& info
)
{
info.clear();
// Set up processor
handshake_processor processor (params);
@@ -239,7 +245,7 @@ void mysql::detail::hanshake(
if (err) return;
// Process server greeting
err = processor.process_handshake(buffer);
err = processor.process_handshake(buffer, info);
if (err) return;
// Send
@@ -252,7 +258,7 @@ void mysql::detail::hanshake(
// Process it
bool auth_complete = false;
err = processor.process_handshake_server_response(buffer, auth_complete);
err = processor.process_handshake_server_response(buffer, auth_complete, info);
if (err) return;
if (auth_complete)
{
@@ -269,14 +275,14 @@ void mysql::detail::hanshake(
if (err) return;
// Process it
err = processor.process_auth_switch_response(boost::asio::buffer(buffer));
err = processor.process_auth_switch_response(boost::asio::buffer(buffer), info);
if (err) return;
channel.set_current_capabilities(processor.negotiated_capabilities());
}
template <typename ChannelType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(mysql::error_code))
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(mysql::error_code, mysql::error_info))
mysql::detail::async_handshake(
ChannelType& channel,
const handshake_params& params,
@@ -284,7 +290,7 @@ mysql::detail::async_handshake(
CompletionToken&& token
)
{
using HandlerSignature = void(mysql::error_code);
using HandlerSignature = void(error_code, error_info);
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using StreamType = typename ChannelType::stream_type;
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
@@ -296,6 +302,7 @@ mysql::detail::async_handshake(
ChannelType& channel_;
bytestring& buffer_;
handshake_processor processor_;
error_info info_;
Op(
HandlerType&& handler,
@@ -313,7 +320,7 @@ mysql::detail::async_handshake(
void complete(bool cont, error_code errc)
{
channel_.set_current_capabilities(processor_.negotiated_capabilities());
BaseType::complete(cont, errc);
BaseType::complete(cont, errc, std::move(info_));
}
void operator()(
@@ -333,7 +340,7 @@ mysql::detail::async_handshake(
}
// Process server greeting
err = processor_.process_handshake(buffer_);
err = processor_.process_handshake(buffer_, info_);
if (err)
{
complete(cont, err);
@@ -357,7 +364,7 @@ mysql::detail::async_handshake(
}
// Process it
err = processor_.process_handshake_server_response(buffer_, auth_complete);
err = processor_.process_handshake_server_response(buffer_, auth_complete, info_);
if (auth_complete) err.clear();
if (err || auth_complete)
{
@@ -382,7 +389,7 @@ mysql::detail::async_handshake(
}
// Process it
err = processor_.process_auth_switch_response(boost::asio::buffer(buffer_));
err = processor_.process_auth_switch_response(boost::asio::buffer(buffer_), info_);
if (err)
{
complete(cont, err);

View File

@@ -232,7 +232,7 @@ inline std::pair<error_code, std::uint8_t> deserialize_message_type(
DeserializationContext& ctx
);
inline error_code process_error_packet(DeserializationContext& ctx);
inline error_code process_error_packet(DeserializationContext& ctx, error_info& info);
/*struct StmtPrepare

View File

@@ -214,12 +214,14 @@ inline std::pair<mysql::error_code, std::uint8_t> mysql::detail::deserialize_mes
}
inline mysql::error_code mysql::detail::process_error_packet(
DeserializationContext& ctx
DeserializationContext& ctx,
error_info& info
)
{
err_packet error_packet;
auto errc = deserialize_message(error_packet, ctx);
if (errc) return errc;
info.set_message(std::string(error_packet.error_message.value));
return make_error_code(static_cast<Error>(error_packet.error_code.value));
}

View File

@@ -2,6 +2,7 @@
#define MYSQL_ASIO_IMPL_QUERY_HPP
#include "mysql/resultset.hpp"
#include "mysql/error.hpp"
#include "mysql/impl/capabilities.hpp"
#include <string_view>
@@ -28,11 +29,12 @@ void execute_query(
ChannelType& channel,
std::string_view query,
channel_resultset_type<ChannelType>& output,
error_code& err
error_code& err,
error_info& info
);
template <typename ChannelType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, channel_resultset_type<ChannelType>))
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, error_info, channel_resultset_type<ChannelType>))
async_execute_query(
ChannelType& channel,
std::string_view query,
@@ -47,11 +49,12 @@ fetch_result fetch_text_row(
bytestring& buffer,
std::vector<value>& output_values,
ok_packet& output_ok_packet,
error_code& err
error_code& err,
error_info& info
);
template <typename ChannelType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, fetch_result))
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, error_info, fetch_result))
async_fetch_text_row(
ChannelType& channel,
const std::vector<field_metadata>& meta,

View File

@@ -41,7 +41,8 @@ public:
std::optional<std::uint64_t> // has value if there are fields in the response
process_query_response(
channel_resultset_type<ChannelType>& output,
error_code& err
error_code& err,
error_info& info
)
{
// Response may be: ok_packet, err_packet, local infile request (not implemented)
@@ -62,7 +63,7 @@ public:
}
else if (msg_type == error_packet_header)
{
err = process_error_packet(ctx);
err = process_error_packet(ctx, info);
return {};
}
else
@@ -117,7 +118,8 @@ inline fetch_result process_fetch_message(
const bytestring& buffer,
std::vector<value>& output_values,
ok_packet& output_ok_packet,
error_code& err
error_code& err,
error_info& info
)
{
// Message type: row, error or eof?
@@ -135,7 +137,7 @@ inline fetch_result process_fetch_message(
else if (msg_type == error_packet_header)
{
// An error occurred during the generation of the rows
err = process_error_packet(ctx);
err = process_error_packet(ctx, info);
return fetch_result::error;
}
else
@@ -156,7 +158,8 @@ void mysql::detail::execute_query(
ChannelType& channel,
std::string_view query,
resultset<channel_stream_type<ChannelType>>& output,
error_code& err
error_code& err,
error_info& info
)
{
// Compose a com_query message, reset seq num
@@ -172,7 +175,7 @@ void mysql::detail::execute_query(
if (err) return;
// Response may be: ok_packet, err_packet, local infile request (not implemented), or response with fields
auto num_fields = processor.process_query_response(output, err);
auto num_fields = processor.process_query_response(output, err, info);
if (!num_fields) // ok or err
{
return;
@@ -199,7 +202,7 @@ void mysql::detail::execute_query(
template <typename ChannelType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
void(mysql::error_code, mysql::detail::channel_resultset_type<ChannelType>)
void(mysql::error_code, mysql::error_info, mysql::detail::channel_resultset_type<ChannelType>)
)
mysql::detail::async_execute_query(
ChannelType& channel,
@@ -207,7 +210,7 @@ mysql::detail::async_execute_query(
CompletionToken&& token
)
{
using HandlerSignature = void(error_code, channel_resultset_type<ChannelType>);
using HandlerSignature = void(error_code, error_info, channel_resultset_type<ChannelType>);
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using StreamType = typename ChannelType::stream_type;
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
@@ -236,10 +239,11 @@ mysql::detail::async_execute_query(
{
ResultsetType resultset;
error_code err;
auto num_fields = processor_->process_query_response(resultset, err);
error_info info;
auto num_fields = processor_->process_query_response(resultset, err, info);
if (!num_fields) // ok or err
{
this->complete(cont, err, std::move(resultset));
this->complete(cont, err, std::move(info), std::move(resultset));
return false;
}
else
@@ -253,7 +257,7 @@ mysql::detail::async_execute_query(
{
ResultsetType resultset;
std::move(*processor_).create_resultset(resultset);
this->complete(cont, error_code(), std::move(resultset));
this->complete(cont, error_code(), error_info(), std::move(resultset));
}
void operator()(
@@ -270,7 +274,7 @@ mysql::detail::async_execute_query(
);
if (err)
{
this->complete(cont, err, ResultsetType());
this->complete(cont, err, error_info(), ResultsetType());
yield break;
}
@@ -281,7 +285,7 @@ mysql::detail::async_execute_query(
);
if (err)
{
this->complete(cont, err, ResultsetType());
this->complete(cont, err, error_info(), ResultsetType());
yield break;
}
@@ -308,7 +312,7 @@ mysql::detail::async_execute_query(
if (err)
{
this->complete(cont, err, ResultsetType());
this->complete(cont, err, error_info(), ResultsetType());
yield break;
}
@@ -339,7 +343,8 @@ mysql::detail::fetch_result mysql::detail::fetch_text_row(
bytestring& buffer,
std::vector<value>& output_values,
ok_packet& output_ok_packet,
error_code& err
error_code& err,
error_info& info
)
{
// Read a packet
@@ -352,13 +357,17 @@ mysql::detail::fetch_result mysql::detail::fetch_text_row(
buffer,
output_values,
output_ok_packet,
err
err,
info
);
}
template <typename ChannelType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(mysql::error_code, mysql::detail::fetch_result))
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
void(mysql::error_code, mysql::error_info, mysql::detail::fetch_result)
)
mysql::detail::async_fetch_text_row(
ChannelType& channel,
const std::vector<field_metadata>& meta,
@@ -401,15 +410,17 @@ mysql::detail::async_fetch_text_row(
void process_result(bool cont)
{
error_code err;
error_info info;
auto result = process_fetch_message(
channel_.current_capabilities(),
meta_,
buffer_,
output_values_,
output_ok_packet_,
err
err,
info
);
this->complete(cont, err, result);
this->complete(cont, err, info, result);
}
void operator()(
@@ -422,7 +433,7 @@ mysql::detail::async_fetch_text_row(
yield channel_.async_read(buffer_, std::move(*this));
if (err)
{
this->complete(cont, err, fetch_result::error);
this->complete(cont, err, error_info(), fetch_result::error);
yield break;
}
process_result(cont);

View File

@@ -9,13 +9,17 @@
template <typename StreamType>
const mysql::row* mysql::resultset<StreamType>::fetch_one(
error_code& err
error_code& err,
error_info& info
)
{
assert(valid());
err.clear();
info.clear();
if (complete())
{
err.clear();
return nullptr;
}
auto result = detail::fetch_text_row(
@@ -24,7 +28,8 @@ const mysql::row* mysql::resultset<StreamType>::fetch_one(
buffer_,
current_row_.values(),
ok_packet_,
err
err,
info
);
eof_received_ = result == detail::fetch_result::eof;
return result == detail::fetch_result::row ? &current_row_ : nullptr;
@@ -34,20 +39,24 @@ template <typename StreamType>
const mysql::row* mysql::resultset<StreamType>::fetch_one()
{
error_code errc;
const row* res = fetch_one(errc);
detail::check_error_code(errc);
error_info info;
const row* res = fetch_one(errc, info);
detail::check_error_code(errc, info);
return res;
}
template <typename StreamType>
std::vector<mysql::owning_row> mysql::resultset<StreamType>::fetch_many(
std::size_t count,
error_code& err
error_code& err,
error_info& info
)
{
assert(valid());
err.clear();
info.clear();
std::vector<mysql::owning_row> res;
if (!complete()) // support calling fetch on already exhausted resultset
@@ -63,7 +72,8 @@ std::vector<mysql::owning_row> mysql::resultset<StreamType>::fetch_many(
buff,
values,
ok_packet_,
err
err,
info
);
eof_received_ = result == detail::fetch_result::eof;
if (result == detail::fetch_result::row)
@@ -86,17 +96,19 @@ std::vector<mysql::owning_row> mysql::resultset<StreamType>::fetch_many(
)
{
error_code errc;
auto res = fetch_many(count, errc);
detail::check_error_code(errc);
error_info info;
auto res = fetch_many(count, errc, info);
detail::check_error_code(errc, info);
return res;
}
template <typename StreamType>
std::vector<mysql::owning_row> mysql::resultset<StreamType>::fetch_all(
error_code& err
error_code& err,
error_info& info
)
{
return fetch_many(std::numeric_limits<std::size_t>::max(), err);
return fetch_many(std::numeric_limits<std::size_t>::max(), err, info);
}
template <typename StreamType>
@@ -107,12 +119,15 @@ std::vector<mysql::owning_row> mysql::resultset<StreamType>::fetch_all()
template <typename StreamType>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(mysql::error_code, const mysql::row*))
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
void(mysql::error_code, mysql::error_info, const mysql::row*)
)
mysql::resultset<StreamType>::async_fetch_one(
CompletionToken&& token
)
{
using HandlerSignature = void(error_code, const row*);
using HandlerSignature = void(error_code, error_info, const row*);
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
@@ -129,6 +144,7 @@ mysql::resultset<StreamType>::async_fetch_one(
void operator()(
error_code err,
error_info info,
detail::fetch_result result,
bool cont=true
)
@@ -137,7 +153,7 @@ mysql::resultset<StreamType>::async_fetch_one(
{
if (resultset_.complete())
{
this->complete(cont, error_code(), nullptr);
this->complete(cont, error_code(), error_info(), nullptr);
}
else
{
@@ -153,6 +169,7 @@ mysql::resultset<StreamType>::async_fetch_one(
this->complete(
cont,
err,
std::move(info),
result == detail::fetch_result::row ? &resultset_.current_row_ : nullptr
);
}
@@ -167,19 +184,22 @@ mysql::resultset<StreamType>::async_fetch_one(
Op(
std::move(initiator.completion_handler),
*this
)(error_code(), detail::fetch_result::error, false);
)(error_code(), error_info(), detail::fetch_result::error, false);
return initiator.result.get();
}
template <typename StreamType>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(mysql::error_code, std::vector<mysql::owning_row>))
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
void(mysql::error_code, mysql::error_info, std::vector<mysql::owning_row>)
)
mysql::resultset<StreamType>::async_fetch_many(
std::size_t count,
CompletionToken&& token
)
{
using HandlerSignature = void(error_code, std::vector<owning_row>);
using HandlerSignature = void(error_code, error_info, std::vector<owning_row>);
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
@@ -218,6 +238,7 @@ mysql::resultset<StreamType>::async_fetch_many(
void operator()(
error_code err,
error_info info,
detail::fetch_result result,
bool cont=true
)
@@ -236,7 +257,7 @@ mysql::resultset<StreamType>::async_fetch_many(
);
if (result == detail::fetch_result::error)
{
this->complete(cont, err, std::move(impl_->rows));
this->complete(cont, err, std::move(info), std::move(impl_->rows));
yield break;
}
else if (result == detail::fetch_result::eof)
@@ -248,7 +269,7 @@ mysql::resultset<StreamType>::async_fetch_many(
impl_->row_received();
}
}
this->complete(cont, err, std::move(impl_->rows));
this->complete(cont, err, error_info(), std::move(impl_->rows));
}
}
};
@@ -260,13 +281,16 @@ mysql::resultset<StreamType>::async_fetch_many(
Op(
std::move(initiator.completion_handler),
std::make_shared<OpImpl>(*this, count)
)(error_code(), detail::fetch_result::error, false);
)(error_code(), error_info(), detail::fetch_result::error, false);
return initiator.result.get();
}
template <typename StreamType>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(mysql::error_code, std::vector<mysql::owning_row>))
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
void(mysql::error_code, mysql::error_info, std::vector<mysql::owning_row>)
)
mysql::resultset<StreamType>::async_fetch_all(
CompletionToken&& token
)
@@ -281,4 +305,4 @@ mysql::resultset<StreamType>::async_fetch_all(
#include <boost/asio/unyield.hpp>
#endif
#endif

View File

@@ -77,7 +77,7 @@ public:
* fetch_one is the fetch method that performs the less memory allocations
* of the three.
*/
const row* fetch_one(error_code& err);
const row* fetch_one(error_code& err, error_info& info);
/// Fetches a single row (sync with exceptions version).
const row* fetch_one();
@@ -91,7 +91,7 @@ public:
* Only if count is **greater** than the available number of rows,
* the resultset will be completed.
*/
std::vector<owning_row> fetch_many(std::size_t count, error_code& err);
std::vector<owning_row> fetch_many(std::size_t count, error_code& err, error_info& info);
/// Fetches at most count rows (sync with exceptions version).
std::vector<owning_row> fetch_many(std::size_t count);
@@ -104,24 +104,24 @@ public:
*
* The resultset is guaranteed to be complete() after this call returns.
*/
std::vector<owning_row> fetch_all(error_code& err);
std::vector<owning_row> fetch_all(error_code& err, error_info& info);
/// Fetches all available rows (sync with exceptions version).
std::vector<owning_row> fetch_all();
/// Fetchs a single row (async version).
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, const row*))
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, error_info, const row*))
async_fetch_one(CompletionToken&& token);
/// Fetches at most count rows (async version).
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, std::vector<owning_row>))
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, error_info, std::vector<owning_row>))
async_fetch_many(std::size_t count, CompletionToken&& token);
/// Fetches all available rows (async version).
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, std::vector<owning_row>))
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, error_info, std::vector<owning_row>))
async_fetch_all(CompletionToken&& token);
/**
@@ -177,6 +177,6 @@ using tcp_resultset = resultset<boost::asio::ip::tcp::socket>;
}
#include "mysql/impl/resultset.hpp"
#include "mysql/impl/resultset.ipp"
#endif

View File

@@ -25,9 +25,9 @@ add_executable(
unit/row.cpp
)
# A codegen issue in MSVC C++17 makes gmock expectations not work
if (NOT MSVC)
target_sources(mysql_unittests PRIVATE unit/channel.cpp)
endif()
#if (NOT MSVC)
# target_sources(mysql_unittests PRIVATE unit/channel.cpp)
#endif()
target_include_directories(
mysql_unittests

View File

@@ -3,6 +3,7 @@
#include "mysql/value.hpp"
#include "mysql/row.hpp"
#include <gmock/gmock.h>
#include <vector>
namespace mysql
@@ -62,6 +63,16 @@ inline std::string_view makesv(const std::uint8_t (&value) [N])
return std::string_view(reinterpret_cast<const char*>(value), N);
}
inline void validate_error_info(const mysql::error_info& value, const std::vector<std::string>& to_check)
{
std::string msg_lower = value.message();
std::transform(msg_lower.begin(), msg_lower.end(), msg_lower.begin(), &tolower);
for (const auto& elm: to_check)
{
EXPECT_THAT(msg_lower, testing::HasSubstr(elm));
}
}
}
}

View File

@@ -7,12 +7,15 @@
#include "mysql/connection.hpp"
#include "integration_test_common.hpp"
#include "test_common.hpp"
#include <boost/asio/use_future.hpp>
namespace net = boost::asio;
using namespace testing;
using namespace mysql::test;
using mysql::detail::make_error_code;
using mysql::error_info;
namespace
{
@@ -24,8 +27,9 @@ struct HandshakeTest : public mysql::test::IntegTest
// Sync with error codes
TEST_F(HandshakeTest, SyncErrc_FastAuthSuccessfulLogin)
{
conn.handshake(connection_params, errc);
conn.handshake(connection_params, errc, info);
EXPECT_EQ(errc, mysql::error_code());
EXPECT_EQ(info, error_info());
}
// TODO: review failure in Mac
@@ -40,15 +44,17 @@ TEST_F(HandshakeTest, SyncErrc_FastAuthSuccessfulLogin)
TEST_F(HandshakeTest, SyncErrc_FastAuthSuccessfulLoginNoDatabase)
{
connection_params.database = "";
conn.handshake(connection_params, errc);
conn.handshake(connection_params, errc, info);
EXPECT_EQ(errc, mysql::error_code());
EXPECT_EQ(info, error_info());
}
TEST_F(HandshakeTest, SyncErrc_FastAuthBadUser)
{
connection_params.username = "non_existing_user";
conn.handshake(connection_params, errc);
conn.handshake(connection_params, errc, info);
EXPECT_NE(errc, mysql::error_code());
EXPECT_NE(info, error_info());
// TODO: if default auth plugin is unknown, unknown auth plugin is returned instead of access denied
// EXPECT_EQ(errc, make_error_code(mysql::Error::access_denied_error));
}
@@ -56,15 +62,17 @@ TEST_F(HandshakeTest, SyncErrc_FastAuthBadUser)
TEST_F(HandshakeTest, SyncErrc_FastAuthBadPassword)
{
connection_params.password = "bad_password";
conn.handshake(connection_params, errc);
conn.handshake(connection_params, errc, info);
EXPECT_EQ(errc, make_error_code(mysql::Error::access_denied_error));
validate_error_info(info, {"access denied", "integ_user"});
}
TEST_F(HandshakeTest, SyncErrc_FastAuthBadDatabase)
{
connection_params.database = "bad_database";
conn.handshake(connection_params, errc);
conn.handshake(connection_params, errc, info);
EXPECT_EQ(errc, make_error_code(mysql::Error::bad_db_error));
validate_error_info(info, {"unknown database", "bad_database"});
}
// Sync with exceptions
@@ -83,7 +91,7 @@ TEST_F(HandshakeTest, SyncExc_FastAuthBadPassword)
TEST_F(HandshakeTest, Async_FastAuthSuccessfulLogin)
{
auto fut = conn.async_handshake(connection_params, boost::asio::use_future);
EXPECT_NO_THROW(fut.get());
EXPECT_EQ(fut.get(), error_info());
}
// TODO: review failure in Mac
@@ -92,21 +100,20 @@ TEST_F(HandshakeTest, Async_FastAuthSuccessfulLogin)
connection_params.username = "empty_password_user";
connection_params.password = "";
auto fut = conn.async_handshake(connection_params, boost::asio::use_future);
EXPECT_NO_THROW(fut.get());
EXPECT_EQ(fut.get(), error_info());
}*/
TEST_F(HandshakeTest, Async_FastAuthSuccessfulLoginNoDatabase)
{
connection_params.database = "";
auto fut = conn.async_handshake(connection_params, boost::asio::use_future);
EXPECT_NO_THROW(fut.get());
EXPECT_EQ(fut.get(), error_info());
}
TEST_F(HandshakeTest, Async_FastAuthBadUser)
{
connection_params.username = "non_existing_user";
auto fut = conn.async_handshake(connection_params, boost::asio::use_future);
EXPECT_THROW(fut.get(), boost::system::system_error);
// TODO: if default auth plugin is unknown, unknown auth plugin is returned instead of access denied
// validate_future_exception(fut, make_error_code(mysql::Error::access_denied_error));
@@ -115,15 +122,17 @@ TEST_F(HandshakeTest, Async_FastAuthBadUser)
TEST_F(HandshakeTest, Async_FastAuthBadPassword)
{
connection_params.password = "bad_password";
auto fut = conn.async_handshake(connection_params, boost::asio::use_future);
validate_future_exception(fut, make_error_code(mysql::Error::access_denied_error));
validate_async_fail([&](auto&& cb) {
conn.async_handshake(connection_params, std::move(cb));
}, mysql::Error::access_denied_error, {"access denied", "integ_user"});
}
TEST_F(HandshakeTest, Async_FastAuthBadDatabase)
{
connection_params.database = "bad_db";
auto fut = conn.async_handshake(connection_params, boost::asio::use_future);
validate_future_exception(fut, make_error_code(mysql::Error::bad_db_error));
validate_async_fail([&](auto&& cb) {
conn.async_handshake(connection_params, std::move(cb));
}, mysql::Error::bad_db_error, {"unknown database", "bad_db"});
}
} // anon namespace

View File

@@ -7,6 +7,7 @@
#include <boost/asio/ip/tcp.hpp>
#include <future>
#include <thread>
#include "test_common.hpp"
namespace mysql
{
@@ -19,6 +20,7 @@ struct IntegTest : testing::Test
boost::asio::io_context ctx;
mysql::connection<boost::asio::ip::tcp::socket> conn {ctx};
mysql::error_code errc;
mysql::error_info info;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> guard { ctx.get_executor() };
std::thread runner {[this]{ ctx.run(); } };
@@ -26,6 +28,7 @@ struct IntegTest : testing::Test
{
boost::asio::ip::tcp::endpoint endpoint (boost::asio::ip::address_v4::loopback(), 3306);
conn.next_level().connect(endpoint);
reset_errors();
}
~IntegTest()
@@ -35,24 +38,46 @@ struct IntegTest : testing::Test
runner.join();
}
template <typename T>
void validate_future_exception(std::future<T>& fut, mysql::error_code expected_errc)
template <typename Callable>
void validate_async_fail(
Callable&& initiator,
error_code expected_errc,
const std::vector<std::string>& expected_info
)
{
try
{
fut.get();
FAIL() << "Expected asynchronous operation to fail";
}
catch (const boost::system::system_error& exc)
{
EXPECT_EQ(exc.code(), expected_errc);
}
std::promise<std::pair<error_code, error_info>> prom;
initiator([&prom](error_code errc, error_info info, auto&&...) {
prom.set_value(std::make_pair(errc, std::move(info)));
});
auto [actual_errc, actual_info] = prom.get_future().get();
EXPECT_EQ(actual_errc, expected_errc);
validate_error_info(actual_info, expected_info);
}
template <typename Callable>
void validate_async_fail(
Callable&& initiator,
Error expected_errc,
const std::vector<std::string>& expected_info
)
{
validate_async_fail(
std::forward<Callable>(initiator),
::mysql::detail::make_error_code(expected_errc),
expected_info
);
}
void handshake()
{
conn.handshake(connection_params);
}
void reset_errors()
{
// TODO: set errc to something not null to verify we clear stuff
info.set_message("Previous error message was not cleared correctly");
}
};
}

View File

@@ -21,6 +21,7 @@ using mysql::test::validate_meta;
using mysql::field_metadata;
using mysql::field_type;
using mysql::error_code;
using mysql::error_info;
namespace
{
@@ -68,14 +69,22 @@ struct QueryTest : public mysql::test::IntegTest
{
validate_2fields_meta(result.fields(), table);
}
auto make_query_initiator(const char* sql)
{
return [this, sql](auto&& cb) {
conn.async_query(sql, cb);
};
}
};
// Query, sync errc
TEST_F(QueryTest, QuerySyncErrc_InsertQueryOk)
{
auto result = conn.query(
"INSERT INTO inserts_table (field_varchar, field_date) VALUES ('v0', '2010-10-11')", errc);
ASSERT_EQ(errc, mysql::error_code());
const char* sql = "INSERT INTO inserts_table (field_varchar, field_date) VALUES ('v0', '2010-10-11')";
auto result = conn.query(sql, errc, info);
ASSERT_EQ(errc, error_code());
EXPECT_EQ(info, error_info());
EXPECT_TRUE(result.fields().empty());
EXPECT_TRUE(result.valid());
EXPECT_TRUE(result.complete());
@@ -87,17 +96,19 @@ TEST_F(QueryTest, QuerySyncErrc_InsertQueryOk)
TEST_F(QueryTest, QuerySyncErrc_InsertQueryFailed)
{
auto result = conn.query(
"INSERT INTO bad_table (field_varchar, field_date) VALUES ('v0', '2010-10-11')", errc);
const char* sql = "INSERT INTO bad_table (field_varchar, field_date) VALUES ('v0', '2010-10-11')";
auto result = conn.query(sql, errc, info);
ASSERT_EQ(errc, make_error_code(mysql::Error::no_such_table));
validate_error_info(info, {"table", "doesn't exist", "bad_table"});
EXPECT_FALSE(result.valid());
}
TEST_F(QueryTest, QuerySyncErrc_UpdateQueryOk)
{
auto result = conn.query(
"UPDATE updates_table SET field_int = field_int+1", errc);
const char* sql = "UPDATE updates_table SET field_int = field_int+1";
auto result = conn.query(sql, errc, info);
ASSERT_EQ(errc, mysql::error_code());
EXPECT_EQ(info, error_info());
EXPECT_TRUE(result.fields().empty());
EXPECT_TRUE(result.valid());
EXPECT_TRUE(result.complete());
@@ -109,8 +120,9 @@ TEST_F(QueryTest, QuerySyncErrc_UpdateQueryOk)
TEST_F(QueryTest, QuerySyncErrc_SelectOk)
{
auto result = conn.query("SELECT * FROM empty_table", errc);
auto result = conn.query("SELECT * FROM empty_table", errc, info);
ASSERT_EQ(errc, mysql::error_code());
EXPECT_EQ(info, error_info());
EXPECT_TRUE(result.valid());
EXPECT_FALSE(result.complete());
validate_2fields_meta(result, "empty_table");
@@ -118,8 +130,9 @@ TEST_F(QueryTest, QuerySyncErrc_SelectOk)
TEST_F(QueryTest, QuerySyncErrc_SelectQueryFailed)
{
auto result = conn.query("SELECT field_varchar, field_bad FROM one_row_table", errc);
auto result = conn.query("SELECT field_varchar, field_bad FROM one_row_table", errc, info);
ASSERT_EQ(errc, make_error_code(mysql::Error::bad_field_error));
validate_error_info(info, {"unknown column", "field_bad"});
EXPECT_FALSE(result.valid());
}
@@ -148,10 +161,11 @@ TEST_F(QueryTest, QuerySyncExc_Error)
// Query, async
TEST_F(QueryTest, QueryAsync_InsertQueryOk)
{
auto result = conn.async_query(
auto [info, result] = conn.async_query(
"INSERT INTO inserts_table (field_varchar, field_date) VALUES ('v0', '2010-10-11')",
net::use_future
).get();
EXPECT_EQ(info, error_info());
EXPECT_TRUE(result.fields().empty());
EXPECT_TRUE(result.valid());
EXPECT_TRUE(result.complete());
@@ -163,19 +177,20 @@ TEST_F(QueryTest, QueryAsync_InsertQueryOk)
TEST_F(QueryTest, QueryAsync_InsertQueryFailed)
{
auto fut = conn.async_query(
"INSERT INTO bad_table (field_varchar, field_date) VALUES ('v0', '2010-10-11')",
net::use_future
validate_async_fail(
make_query_initiator("INSERT INTO bad_table (field_varchar, field_date) VALUES ('v0', '2010-10-11')"),
mysql::Error::no_such_table,
{"table", "doesn't exist", "bad_table"}
);
validate_future_exception(fut, make_error_code(mysql::Error::no_such_table));
}
TEST_F(QueryTest, QueryAsync_UpdateQueryOk)
{
auto result = conn.async_query(
auto [info, result] = conn.async_query(
"UPDATE updates_table SET field_int = field_int+1",
net::use_future
).get();
EXPECT_EQ(info, error_info());
EXPECT_TRUE(result.fields().empty());
EXPECT_TRUE(result.valid());
EXPECT_TRUE(result.complete());
@@ -187,7 +202,8 @@ TEST_F(QueryTest, QueryAsync_UpdateQueryOk)
TEST_F(QueryTest, QueryAsync_SelectOk)
{
auto result = conn.async_query("SELECT * FROM empty_table", net::use_future).get();
auto [info, result] = conn.async_query("SELECT * FROM empty_table", net::use_future).get();
EXPECT_EQ(info, error_info());
EXPECT_TRUE(result.valid());
EXPECT_FALSE(result.complete());
validate_2fields_meta(result, "empty_table");
@@ -195,8 +211,11 @@ TEST_F(QueryTest, QueryAsync_SelectOk)
TEST_F(QueryTest, QueryAsync_SelectQueryFailed)
{
auto fut = conn.async_query("SELECT field_varchar, field_bad FROM one_row_table", net::use_future);
validate_future_exception(fut, make_error_code(mysql::Error::bad_field_error));
validate_async_fail(
make_query_initiator("SELECT field_varchar, field_bad FROM one_row_table"),
mysql::Error::bad_field_error,
{"unknown column", "field_bad"}
);
}
@@ -209,14 +228,17 @@ TEST_F(QueryTest, FetchOneSyncErrc_NoResults)
EXPECT_EQ(result.fields().size(), 2);
// Already in the end of the resultset, we receive the EOF
const mysql::row* row = result.fetch_one(errc);
const mysql::row* row = result.fetch_one(errc, info);
EXPECT_EQ(errc, mysql::error_code());
EXPECT_EQ(info, error_info());
EXPECT_EQ(row, nullptr);
validate_eof(result);
// Fetching again just returns null
row = result.fetch_one(errc);
reset_errors();
row = result.fetch_one(errc, info);
EXPECT_EQ(errc, mysql::error_code());
EXPECT_EQ(info, error_info());
EXPECT_EQ(row, nullptr);
validate_eof(result);
}
@@ -229,16 +251,19 @@ TEST_F(QueryTest, FetchOneSyncErrc_OneRow)
EXPECT_EQ(result.fields().size(), 2);
// Fetch only row
const mysql::row* row = result.fetch_one(errc);
const mysql::row* row = result.fetch_one(errc, info);
ASSERT_EQ(errc, mysql::error_code());
EXPECT_EQ(info, error_info());
ASSERT_NE(row, nullptr);
validate_2fields_meta(result, "one_row_table");
EXPECT_EQ(row->values(), makevalues(1, "f0"));
EXPECT_FALSE(result.complete());
// Fetch next: end of resultset
row = result.fetch_one(errc);
reset_errors();
row = result.fetch_one(errc, info);
ASSERT_EQ(errc, mysql::error_code());
EXPECT_EQ(info, error_info());
ASSERT_EQ(row, nullptr);
validate_eof(result);
}
@@ -251,24 +276,29 @@ TEST_F(QueryTest, FetchOneSyncErrc_TwoRows)
EXPECT_EQ(result.fields().size(), 2);
// Fetch first row
const mysql::row* row = result.fetch_one(errc);
const mysql::row* row = result.fetch_one(errc, info);
ASSERT_EQ(errc, mysql::error_code());
EXPECT_EQ(info, error_info());
ASSERT_NE(row, nullptr);
validate_2fields_meta(result, "two_rows_table");
EXPECT_EQ(row->values(), makevalues(1, "f0"));
EXPECT_FALSE(result.complete());
// Fetch next row
row = result.fetch_one(errc);
reset_errors();
row = result.fetch_one(errc, info);
ASSERT_EQ(errc, mysql::error_code());
EXPECT_EQ(info, error_info());
ASSERT_NE(row, nullptr);
validate_2fields_meta(result, "two_rows_table");
EXPECT_EQ(row->values(), makevalues(2, "f1"));
EXPECT_FALSE(result.complete());
// Fetch next: end of resultset
row = result.fetch_one(errc);
reset_errors();
row = result.fetch_one(errc, info);
ASSERT_EQ(errc, mysql::error_code());
EXPECT_EQ(info, error_info());
ASSERT_EQ(row, nullptr);
validate_eof(result);
}
@@ -309,12 +339,14 @@ TEST_F(QueryTest, FetchOneAsync_NoResults)
auto result = conn.query("SELECT * FROM empty_table");
// Already in the end of the resultset, we receive the EOF
const auto* row = result.async_fetch_one(net::use_future).get();
auto [info, row] = result.async_fetch_one(net::use_future).get();
EXPECT_EQ(info, error_info());
EXPECT_EQ(row, nullptr);
validate_eof(result);
// Fetching again just returns null
row = result.async_fetch_one(net::use_future).get();
std::tie(info, row) = result.async_fetch_one(net::use_future).get();
EXPECT_EQ(info, error_info());
EXPECT_EQ(row, nullptr);
validate_eof(result);
}
@@ -324,13 +356,16 @@ TEST_F(QueryTest, FetchOneAsync_OneRow)
auto result = conn.query("SELECT * FROM one_row_table");
// Fetch only row
const auto* row = result.async_fetch_one(net::use_future).get();
auto [info, row] = result.async_fetch_one(net::use_future).get();
EXPECT_EQ(info, error_info());
ASSERT_NE(row, nullptr);
EXPECT_EQ(row->values(), makevalues(1, "f0"));
EXPECT_FALSE(result.complete());
// Fetch next: end of resultset
row = result.async_fetch_one(net::use_future).get();
reset_errors();
std::tie(info, row) = result.async_fetch_one(net::use_future).get();
EXPECT_EQ(info, error_info());
ASSERT_EQ(row, nullptr);
validate_eof(result);
}
@@ -340,19 +375,24 @@ TEST_F(QueryTest, FetchOneAsync_TwoRows)
auto result = conn.query("SELECT * FROM two_rows_table");
// Fetch first row
const auto* row = result.async_fetch_one(net::use_future).get();
auto [info, row] = result.async_fetch_one(net::use_future).get();
EXPECT_EQ(info, error_info());
ASSERT_NE(row, nullptr);
EXPECT_EQ(row->values(), makevalues(1, "f0"));
EXPECT_FALSE(result.complete());
// Fetch next row
row = result.async_fetch_one(net::use_future).get();
reset_errors();
std::tie(info, row) = result.async_fetch_one(net::use_future).get();
EXPECT_EQ(info, error_info());
ASSERT_NE(row, nullptr);
EXPECT_EQ(row->values(), makevalues(2, "f1"));
EXPECT_FALSE(result.complete());
// Fetch next: end of resultset
row = result.async_fetch_one(net::use_future).get();
reset_errors();
std::tie(info, row) = result.async_fetch_one(net::use_future).get();
EXPECT_EQ(info, error_info());
ASSERT_EQ(row, nullptr);
validate_eof(result);
}
@@ -363,15 +403,18 @@ TEST_F(QueryTest, FetchManySyncErrc_NoResults)
auto result = conn.query("SELECT * FROM empty_table");
// Fetch many, but there are no results
auto rows = result.fetch_many(10, errc);
auto rows = result.fetch_many(10, errc, info);
ASSERT_EQ(errc, error_code());
EXPECT_EQ(info, error_info());
EXPECT_TRUE(rows.empty());
EXPECT_TRUE(result.complete());
validate_eof(result);
// Fetch again, should return OK and empty
rows = result.fetch_many(10, errc);
reset_errors();
rows = result.fetch_many(10, errc, info);
ASSERT_EQ(errc, error_code());
EXPECT_EQ(info, error_info());
EXPECT_TRUE(rows.empty());
EXPECT_TRUE(result.complete());
validate_eof(result);
@@ -382,14 +425,17 @@ TEST_F(QueryTest, FetchManySyncErrc_MoreRowsThanCount)
auto result = conn.query("SELECT * FROM three_rows_table");
// Fetch 2, one remaining
auto rows = result.fetch_many(2, errc);
auto rows = result.fetch_many(2, errc, info);
ASSERT_EQ(errc, error_code());
EXPECT_EQ(info, error_info());
EXPECT_FALSE(result.complete());
EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1")));
// Fetch another two (completes the resultset)
rows = result.fetch_many(2, errc);
reset_errors();
rows = result.fetch_many(2, errc, info);
ASSERT_EQ(errc, error_code());
EXPECT_EQ(info, error_info());
EXPECT_TRUE(result.complete());
validate_eof(result);
EXPECT_EQ(rows, (makerows(2, 3, "f2")));
@@ -400,8 +446,9 @@ TEST_F(QueryTest, FetchManySyncErrc_LessRowsThanCount)
auto result = conn.query("SELECT * FROM two_rows_table");
// Fetch 3, resultset exhausted
auto rows = result.fetch_many(3, errc);
auto rows = result.fetch_many(3, errc, info);
ASSERT_EQ(errc, error_code());
EXPECT_EQ(info, error_info());
EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1")));
validate_eof(result);
}
@@ -411,14 +458,17 @@ TEST_F(QueryTest, FetchManySyncErrc_SameRowsAsCount)
auto result = conn.query("SELECT * FROM two_rows_table");
// Fetch 2, 0 remaining but resultset not exhausted
auto rows = result.fetch_many(2, errc);
auto rows = result.fetch_many(2, errc, info);
ASSERT_EQ(errc, error_code());
EXPECT_EQ(info, error_info());
EXPECT_FALSE(result.complete());
EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1")));
// Fetch again, exhausts the resultset
rows = result.fetch_many(2, errc);
reset_errors();
rows = result.fetch_many(2, errc, info);
ASSERT_EQ(errc, error_code());
EXPECT_EQ(info, error_info());
EXPECT_EQ(rows.size(), 0);
validate_eof(result);
}
@@ -428,8 +478,9 @@ TEST_F(QueryTest, FetchManySyncErrc_CountEqualsOne)
auto result = conn.query("SELECT * FROM one_row_table");
// Fetch 1, 1 remaining
auto rows = result.fetch_many(1, errc);
auto rows = result.fetch_many(1, errc, info);
ASSERT_EQ(errc, error_code());
EXPECT_EQ(info, error_info());
EXPECT_FALSE(result.complete());
EXPECT_EQ(rows, (makerows(2, 1, "f0")));
}
@@ -460,13 +511,16 @@ TEST_F(QueryTest, FetchManyAsync_NoResults)
auto result = conn.query("SELECT * FROM empty_table");
// Fetch many, but there are no results
auto rows = result.async_fetch_many(10, net::use_future).get();
auto [info, rows] = result.async_fetch_many(10, net::use_future).get();
EXPECT_EQ(info, error_info());
EXPECT_TRUE(rows.empty());
EXPECT_TRUE(result.complete());
validate_eof(result);
// Fetch again, should return OK and empty
rows = result.async_fetch_many(10, net::use_future).get();
reset_errors();
std::tie(info, rows) = result.async_fetch_many(10, net::use_future).get();
EXPECT_EQ(info, error_info());
EXPECT_TRUE(rows.empty());
EXPECT_TRUE(result.complete());
validate_eof(result);
@@ -477,12 +531,14 @@ TEST_F(QueryTest, FetchManyAsync_MoreRowsThanCount)
auto result = conn.query("SELECT * FROM three_rows_table");
// Fetch 2, one remaining
auto rows = result.async_fetch_many(2, net::use_future).get();
auto [info, rows] = result.async_fetch_many(2, net::use_future).get();
EXPECT_EQ(info, error_info());
EXPECT_FALSE(result.complete());
EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1")));
// Fetch another two (completes the resultset)
rows = result.async_fetch_many(2, net::use_future).get();
std::tie(info, rows) = result.async_fetch_many(2, net::use_future).get();
EXPECT_EQ(info, error_info());
EXPECT_TRUE(result.complete());
validate_eof(result);
EXPECT_EQ(rows, (makerows(2, 3, "f2")));
@@ -493,7 +549,8 @@ TEST_F(QueryTest, FetchManyAsync_LessRowsThanCount)
auto result = conn.query("SELECT * FROM two_rows_table");
// Fetch 3, resultset exhausted
auto rows = result.async_fetch_many(3, net::use_future).get();
auto [info, rows] = result.async_fetch_many(3, net::use_future).get();
EXPECT_EQ(info, error_info());
EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1")));
validate_eof(result);
}
@@ -503,12 +560,15 @@ TEST_F(QueryTest, FetchManyAsync_SameRowsAsCount)
auto result = conn.query("SELECT * FROM two_rows_table");
// Fetch 2, 0 remaining but resultset not exhausted
auto rows = result.async_fetch_many(2, net::use_future).get();
auto [info, rows] = result.async_fetch_many(2, net::use_future).get();
EXPECT_EQ(info, error_info());
EXPECT_FALSE(result.complete());
EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1")));
// Fetch again, exhausts the resultset
rows = result.async_fetch_many(2, net::use_future).get();
reset_errors();
std::tie(info, rows) = result.async_fetch_many(2, net::use_future).get();
EXPECT_EQ(info, error_info());
EXPECT_EQ(rows.size(), 0);
validate_eof(result);
}
@@ -518,7 +578,8 @@ TEST_F(QueryTest, FetchManyAsync_CountEqualsOne)
auto result = conn.query("SELECT * FROM one_row_table");
// Fetch 1, 1 remaining
auto rows = result.async_fetch_many(1, net::use_future).get();
auto [info, rows] = result.async_fetch_many(1, net::use_future).get();
EXPECT_EQ(info, error_info());
EXPECT_FALSE(result.complete());
EXPECT_EQ(rows, (makerows(2, 1, "f0")));
}
@@ -529,14 +590,17 @@ TEST_F(QueryTest, FetchAllSyncErrc_NoResults)
auto result = conn.query("SELECT * FROM empty_table");
// Fetch many, but there are no results
auto rows = result.fetch_all(errc);
auto rows = result.fetch_all(errc, info);
ASSERT_EQ(errc, error_code());
EXPECT_EQ(info, error_info());
EXPECT_TRUE(rows.empty());
EXPECT_TRUE(result.complete());
// Fetch again, should return OK and empty
rows = result.fetch_all(errc);
reset_errors();
rows = result.fetch_all(errc, info);
ASSERT_EQ(errc, error_code());
EXPECT_EQ(info, error_info());
EXPECT_TRUE(rows.empty());
validate_eof(result);
}
@@ -545,8 +609,9 @@ TEST_F(QueryTest, FetchAllSyncErrc_OneRow)
{
auto result = conn.query("SELECT * FROM one_row_table");
auto rows = result.fetch_all(errc);
auto rows = result.fetch_all(errc, info);
ASSERT_EQ(errc, error_code());
EXPECT_EQ(info, error_info());
EXPECT_TRUE(result.complete());
EXPECT_EQ(rows, (makerows(2, 1, "f0")));
}
@@ -555,8 +620,9 @@ TEST_F(QueryTest, FetchAllSyncErrc_SeveralRows)
{
auto result = conn.query("SELECT * FROM two_rows_table");
auto rows = result.fetch_all(errc);
auto rows = result.fetch_all(errc, info);
ASSERT_EQ(errc, error_code());
EXPECT_EQ(info, error_info());
validate_eof(result);
EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1")));
}
@@ -578,12 +644,15 @@ TEST_F(QueryTest, FetchAllAsync_NoResults)
auto result = conn.query("SELECT * FROM empty_table");
// Fetch many, but there are no results
auto rows = result.async_fetch_all(net::use_future).get();
auto [info, rows] = result.async_fetch_all(net::use_future).get();
EXPECT_EQ(info, error_info());
EXPECT_TRUE(rows.empty());
EXPECT_TRUE(result.complete());
// Fetch again, should return OK and empty
rows = result.async_fetch_all(net::use_future).get();
reset_errors();
std::tie(info, rows) = result.async_fetch_all(net::use_future).get();
EXPECT_EQ(info, error_info());
EXPECT_TRUE(rows.empty());
validate_eof(result);
}
@@ -592,7 +661,8 @@ TEST_F(QueryTest, FetchAllAsync_OneRow)
{
auto result = conn.query("SELECT * FROM one_row_table");
auto rows = result.async_fetch_all(net::use_future).get();
auto [info, rows] = result.async_fetch_all(net::use_future).get();
EXPECT_EQ(info, error_info());
EXPECT_TRUE(result.complete());
EXPECT_EQ(rows, (makerows(2, 1, "f0")));
}
@@ -601,7 +671,8 @@ TEST_F(QueryTest, FetchAllAsync_SeveralRows)
{
auto result = conn.query("SELECT * FROM two_rows_table");
auto rows = result.async_fetch_all(net::use_future).get();
auto [info, rows] = result.async_fetch_all(net::use_future).get();
EXPECT_EQ(info, error_info());
validate_eof(result);
EXPECT_EQ(rows, (makerows(2, 1, "f0", 2, "f1")));
}
@@ -616,8 +687,3 @@ TEST_F(QueryTest, QueryAndFetch_AliasedTableAndField_MetadataCorrect)
}
} // anon namespace

View File

@@ -4,5 +4,9 @@ set -e
SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )"
mysql -u root < $SCRIPTPATH/db_setup.sql
if [ "$MYSQL_SKIP_DB_SETUP" != "1" ]
then
mysql -u root < $SCRIPTPATH/db_setup.sql
fi
./mysql_integrationtests