mirror of
https://github.com/boostorg/mysql.git
synced 2026-02-15 01:02:17 +00:00
read_one_row algorithm
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
//
|
||||
// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_READ_ROW_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_READ_ROW_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/mysql/resultset.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/read_one_row.hpp>
|
||||
#include <boost/mysql/detail/protocol/text_deserialization.hpp>
|
||||
#include <boost/mysql/detail/protocol/common_messages.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
inline bool process_read_message(
|
||||
boost::asio::const_buffer read_message,
|
||||
capabilities current_capabilities,
|
||||
resultset& resultset,
|
||||
row& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
assert(resultset.valid());
|
||||
|
||||
// Message type: row, error or eof?
|
||||
std::uint8_t msg_type = 0;
|
||||
deserialization_context ctx (read_message, current_capabilities);
|
||||
err = make_error_code(deserialize(ctx, msg_type));
|
||||
if (err)
|
||||
return false;
|
||||
if (msg_type == eof_packet_header)
|
||||
{
|
||||
// end of resultset => this is a ok_packet, not a row
|
||||
ok_packet ok_pack;
|
||||
err = deserialize_message(ctx, ok_pack);
|
||||
if (err)
|
||||
return false;
|
||||
resultset.complete(ok_pack);
|
||||
output.clear();
|
||||
return false;
|
||||
}
|
||||
else if (msg_type == error_packet_header)
|
||||
{
|
||||
// An error occurred during the generation of the rows
|
||||
err = process_error_packet(ctx, info);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// An actual row
|
||||
output.clear();
|
||||
ctx.rewind(1); // keep the 'message type' byte, as it is part of the actual message
|
||||
err = resultset.deserialize_row(ctx, output.values());
|
||||
if (err)
|
||||
return false;
|
||||
output.copy_strings();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
template<class Stream>
|
||||
struct read_one_row_op : boost::asio::coroutine
|
||||
{
|
||||
channel<Stream>& chan_;
|
||||
error_info& output_info_;
|
||||
resultset& resultset_;
|
||||
row& output_;
|
||||
|
||||
read_one_row_op(
|
||||
channel<Stream>& chan,
|
||||
error_info& output_info,
|
||||
resultset& resultset,
|
||||
row& output
|
||||
) noexcept :
|
||||
chan_(chan),
|
||||
output_info_(output_info),
|
||||
resultset_(resultset),
|
||||
output_(output)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Self>
|
||||
void operator()(
|
||||
Self& self,
|
||||
error_code err = {},
|
||||
boost::asio::const_buffer read_message = {}
|
||||
)
|
||||
{
|
||||
// Error checking
|
||||
if (err)
|
||||
{
|
||||
self.complete(err, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal path
|
||||
bool result;
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
// Read the message
|
||||
BOOST_ASIO_CORO_YIELD chan_.async_read_one(resultset_.sequence_number(), std::move(self));
|
||||
|
||||
// Process it
|
||||
result = process_read_message(
|
||||
read_message,
|
||||
chan_.current_capabilities(),
|
||||
resultset_,
|
||||
output_,
|
||||
err,
|
||||
output_info_
|
||||
);
|
||||
self.complete(err, result);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // mysql
|
||||
} // boost
|
||||
|
||||
|
||||
template <class Stream>
|
||||
bool boost::mysql::detail::read_one_row(
|
||||
channel<Stream>& channel,
|
||||
resultset& resultset,
|
||||
row& output,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
// Read a packet
|
||||
auto read_message = channel.read_one(resultset.sequence_number(), err);
|
||||
if (err)
|
||||
return false;
|
||||
|
||||
return process_read_message(
|
||||
read_message,
|
||||
channel.current_capabilities(),
|
||||
resultset,
|
||||
output,
|
||||
err,
|
||||
info
|
||||
);
|
||||
}
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
|
||||
CompletionToken,
|
||||
void(boost::mysql::error_code, bool)
|
||||
)
|
||||
boost::mysql::detail::async_read_one_row(
|
||||
channel<Stream>& channel,
|
||||
resultset& resultset,
|
||||
row& output,
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
)
|
||||
{
|
||||
return boost::asio::async_compose<CompletionToken, void(error_code, bool)> (
|
||||
read_one_row_op<Stream>(
|
||||
channel,
|
||||
output_info,
|
||||
resultset,
|
||||
output
|
||||
),
|
||||
token,
|
||||
channel
|
||||
);
|
||||
}
|
||||
|
||||
#endif /* INCLUDE_MYSQL_IMPL_NETWORK_ALGORITHMS_READ_TEXT_ROW_IPP_ */
|
||||
@@ -1,199 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_READ_ROW_HPP
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_READ_ROW_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/mysql/detail/network_algorithms/read_row.hpp>
|
||||
#include <boost/mysql/detail/protocol/text_deserialization.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
inline read_row_result process_read_message(
|
||||
deserialize_row_fn deserializer,
|
||||
capabilities current_capabilities,
|
||||
const std::vector<field_metadata>& meta,
|
||||
row& output,
|
||||
bytestring& ok_packet_buffer,
|
||||
ok_packet& output_ok_packet,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
assert(deserializer);
|
||||
|
||||
// Message type: row, error or eof?
|
||||
std::uint8_t msg_type = 0;
|
||||
deserialization_context ctx (boost::asio::buffer(output.buffer()), current_capabilities);
|
||||
err = make_error_code(deserialize(ctx, msg_type));
|
||||
if (err)
|
||||
return read_row_result::error;
|
||||
if (msg_type == eof_packet_header)
|
||||
{
|
||||
// end of resultset => we read the packet against the row, but it is the ok_packet, instead
|
||||
err = deserialize_message(ctx, output_ok_packet);
|
||||
if (err)
|
||||
return read_row_result::error;
|
||||
std::swap(output.buffer(), ok_packet_buffer);
|
||||
output.buffer().clear();
|
||||
output.values().clear();
|
||||
return read_row_result::eof;
|
||||
}
|
||||
else if (msg_type == error_packet_header)
|
||||
{
|
||||
// An error occurred during the generation of the rows
|
||||
err = process_error_packet(ctx, info);
|
||||
return read_row_result::error;
|
||||
}
|
||||
else
|
||||
{
|
||||
// An actual row
|
||||
ctx.rewind(1); // keep the 'message type' byte, as it is part of the actual message
|
||||
err = deserializer(ctx, meta, output.values());
|
||||
if (err)
|
||||
return read_row_result::error;
|
||||
return read_row_result::row;
|
||||
}
|
||||
}
|
||||
|
||||
template<class Stream>
|
||||
struct read_row_op : boost::asio::coroutine
|
||||
{
|
||||
channel<Stream>& chan_;
|
||||
error_info& output_info_;
|
||||
deserialize_row_fn deserializer_;
|
||||
const std::vector<field_metadata>& meta_;
|
||||
row& output_;
|
||||
bytestring& ok_packet_buffer_;
|
||||
ok_packet& output_ok_packet_;
|
||||
|
||||
read_row_op(
|
||||
channel<Stream>& chan,
|
||||
error_info& output_info,
|
||||
deserialize_row_fn deserializer,
|
||||
const std::vector<field_metadata>& meta,
|
||||
row& output,
|
||||
bytestring& ok_packet_buffer,
|
||||
ok_packet& output_ok_packet
|
||||
) :
|
||||
chan_(chan),
|
||||
output_info_(output_info),
|
||||
deserializer_(deserializer),
|
||||
meta_(meta),
|
||||
output_(output),
|
||||
ok_packet_buffer_(ok_packet_buffer),
|
||||
output_ok_packet_(output_ok_packet)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Self>
|
||||
void operator()(
|
||||
Self& self,
|
||||
error_code err = {}
|
||||
)
|
||||
{
|
||||
read_row_result result = read_row_result::error;
|
||||
|
||||
// Error checking
|
||||
if (err)
|
||||
{
|
||||
self.complete(err, result);
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal path
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
// Read the message
|
||||
BOOST_ASIO_CORO_YIELD chan_.async_read(output_.buffer(), std::move(self));
|
||||
|
||||
// Process it
|
||||
result = process_read_message(
|
||||
deserializer_,
|
||||
chan_.current_capabilities(),
|
||||
meta_,
|
||||
output_,
|
||||
ok_packet_buffer_,
|
||||
output_ok_packet_,
|
||||
err,
|
||||
output_info_
|
||||
);
|
||||
self.complete(err, result);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // mysql
|
||||
} // boost
|
||||
|
||||
|
||||
template <class Stream>
|
||||
boost::mysql::detail::read_row_result boost::mysql::detail::read_row(
|
||||
deserialize_row_fn deserializer,
|
||||
channel<Stream>& channel,
|
||||
const std::vector<field_metadata>& meta,
|
||||
row& output,
|
||||
bytestring& ok_packet_buffer,
|
||||
ok_packet& output_ok_packet,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
)
|
||||
{
|
||||
// Read a packet
|
||||
channel.read(output.buffer(), err);
|
||||
if (err)
|
||||
return read_row_result::error;
|
||||
|
||||
return process_read_message(
|
||||
deserializer,
|
||||
channel.current_capabilities(),
|
||||
meta,
|
||||
output,
|
||||
ok_packet_buffer,
|
||||
output_ok_packet,
|
||||
err,
|
||||
info
|
||||
);
|
||||
}
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
|
||||
CompletionToken,
|
||||
void(boost::mysql::error_code, boost::mysql::detail::read_row_result)
|
||||
)
|
||||
boost::mysql::detail::async_read_row(
|
||||
deserialize_row_fn deserializer,
|
||||
channel<Stream>& chan,
|
||||
const std::vector<field_metadata>& meta,
|
||||
row& output,
|
||||
bytestring& ok_packet_buffer,
|
||||
ok_packet& output_ok_packet,
|
||||
CompletionToken&& token,
|
||||
error_info& output_info
|
||||
)
|
||||
{
|
||||
return boost::asio::async_compose<CompletionToken, void(error_code, read_row_result)> (
|
||||
read_row_op<Stream>(
|
||||
chan,
|
||||
output_info,
|
||||
deserializer,
|
||||
meta,
|
||||
output,
|
||||
ok_packet_buffer,
|
||||
output_ok_packet
|
||||
),
|
||||
token,
|
||||
chan
|
||||
);
|
||||
}
|
||||
|
||||
#endif /* INCLUDE_MYSQL_IMPL_NETWORK_ALGORITHMS_READ_TEXT_ROW_IPP_ */
|
||||
@@ -9,6 +9,7 @@
|
||||
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_READ_ROW_HPP
|
||||
|
||||
#include <boost/mysql/detail/network_algorithms/common.hpp>
|
||||
#include <boost/mysql/resultset.hpp>
|
||||
#include <boost/mysql/metadata.hpp>
|
||||
#include <boost/mysql/row.hpp>
|
||||
#include <boost/utility/string_view.hpp>
|
||||
@@ -18,36 +19,23 @@ namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
enum class read_row_result
|
||||
{
|
||||
error,
|
||||
row,
|
||||
eof
|
||||
};
|
||||
|
||||
template <class Stream>
|
||||
read_row_result read_row(
|
||||
deserialize_row_fn deserializer,
|
||||
bool read_one_row(
|
||||
channel<Stream>& channel,
|
||||
const std::vector<field_metadata>& meta,
|
||||
resultset& resultset,
|
||||
row& output,
|
||||
bytestring& ok_packet_buffer,
|
||||
ok_packet& output_ok_packet,
|
||||
error_code& err,
|
||||
error_info& info
|
||||
);
|
||||
|
||||
template <class Stream, class CompletionToken>
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, read_row_result))
|
||||
async_read_row(
|
||||
deserialize_row_fn deserializer,
|
||||
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, bool))
|
||||
async_read_one_row(
|
||||
channel<Stream>& channel,
|
||||
const std::vector<field_metadata>& meta,
|
||||
resultset& resultset,
|
||||
row& output,
|
||||
bytestring& ok_packet_buffer,
|
||||
ok_packet& output_ok_packet,
|
||||
CompletionToken&& token,
|
||||
error_info& output_info
|
||||
error_info& output_info,
|
||||
CompletionToken&& token
|
||||
);
|
||||
|
||||
|
||||
@@ -55,7 +43,7 @@ async_read_row(
|
||||
} // mysql
|
||||
} // boost
|
||||
|
||||
#include <boost/mysql/detail/network_algorithms/impl/read_row.hpp>
|
||||
#include <boost/mysql/detail/network_algorithms/impl/read_one_row.hpp>
|
||||
|
||||
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
#ifndef BOOST_MYSQL_RESULTSET_HPP
|
||||
#define BOOST_MYSQL_RESULTSET_HPP
|
||||
|
||||
#include <boost/mysql/error.hpp>
|
||||
#include <boost/mysql/row.hpp>
|
||||
#include <boost/mysql/metadata.hpp>
|
||||
#include <boost/mysql/detail/protocol/deserialization_context.hpp>
|
||||
#include <boost/mysql/detail/protocol/common_messages.hpp>
|
||||
#include <boost/mysql/detail/channel/channel.hpp>
|
||||
#include <boost/mysql/detail/auxiliar/bytestring.hpp>
|
||||
@@ -122,6 +124,11 @@ public:
|
||||
meta_.emplace_back(pack, true);
|
||||
}
|
||||
|
||||
error_code deserialize_row(detail::deserialization_context& ctx, std::vector<value>& output)
|
||||
{
|
||||
return deserializer_(ctx, meta_, output);
|
||||
}
|
||||
|
||||
std::uint8_t& sequence_number() noexcept { return seqnum_; }
|
||||
|
||||
std::vector<field_metadata>& meta() noexcept { return meta_; }
|
||||
|
||||
@@ -14,8 +14,10 @@
|
||||
#include <boost/mysql/metadata.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
@@ -46,11 +48,9 @@ namespace mysql {
|
||||
class row
|
||||
{
|
||||
std::vector<value> values_;
|
||||
detail::bytestring buffer_;
|
||||
std::vector<char> buffer_;
|
||||
public:
|
||||
row() = default;
|
||||
row(std::vector<value>&& values, detail::bytestring&& buffer) noexcept :
|
||||
values_(std::move(values)), buffer_(std::move(buffer)) {}; // TODO: hide this
|
||||
row(const row&) = delete; // TODO
|
||||
row(row&&) = default;
|
||||
row& operator=(const row&) = delete; // TODO
|
||||
@@ -104,8 +104,35 @@ public:
|
||||
// TODO: hide these
|
||||
const std::vector<value>& values() const noexcept { return values_; }
|
||||
std::vector<value>& values() noexcept { return values_; }
|
||||
const detail::bytestring& buffer() const noexcept { return buffer_; }
|
||||
detail::bytestring& buffer() noexcept { return buffer_; }
|
||||
void copy_strings()
|
||||
{
|
||||
// Calculate size
|
||||
std::size_t size = 0;
|
||||
for (const auto& v: values_)
|
||||
{
|
||||
auto typed_value = v.get_optional<boost::string_view>();
|
||||
if (typed_value.has_value())
|
||||
{
|
||||
size += typed_value->size();
|
||||
}
|
||||
}
|
||||
|
||||
// Make space
|
||||
buffer_.resize(size);
|
||||
|
||||
// Copy the strings
|
||||
std::size_t offset = 0;
|
||||
for (auto& v: values_)
|
||||
{
|
||||
auto typed_value = v.get_optional<boost::string_view>();
|
||||
if (typed_value.has_value())
|
||||
{
|
||||
std::memcpy(buffer_.data() + offset, typed_value->data(), typed_value->size());
|
||||
v = value(boost::string_view(buffer_.data() + offset, typed_value->size()));
|
||||
offset += typed_value->size();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user