2
0
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:
Ruben Perez
2022-07-29 17:37:24 +02:00
parent b49893421f
commit d7833401a0
5 changed files with 228 additions and 225 deletions

View File

@@ -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_ */

View File

@@ -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_ */

View File

@@ -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>

View File

@@ -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_; }

View File

@@ -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();
}
}
}
};